Spotify Android SDK - how to refresh the Player object after an hour - java

I am using the Spotify Android SDK version spotifysdk-1.0.0-beta8.aar. I have a valid OAuth token which I am refreshing each hour successfully. This is how I am getting the Spotify Player Object initially
mPlayer = Spotify.getPlayer(playerConfig, this, new Player.InitializationObserver() {
#Override
public void onInitialized(Player player) {
player.addConnectionStateCallback(JukeSpotDashboardActivity.this);
player.addPlayerNotificationCallback(JukeSpotDashboardActivity.this);
player.login(utils.getPreferenceValue(getString(R.string.spotify_access_token)));
}
#Override
public void onError(Throwable throwable) {
Log.e(JukeSpotDashboardActivity.class.getName(),
"Could not initialize player: " + throwable.getMessage());
}
});
At the end of each hour the access token gets refreshed and stored in the shared preferences. How do you guys suggest I use the new access token in the same mPlayer object so that which ever song is being streamed currently won't get stopped and I won't need to recreate this Spotify player object?
I know there is the
mPlayer.login(accessToken);
function, but it did not work when I got the new accessToken and relogged in. Any idea what I am doing wrong?

Once logged in, your application will be able to stream until the application is stopped, the network connection is lost, or the user's premium account has ended. (There may be other events as well.) This means that you don't need to update the Player object with the refreshed access token to continue streaming.
Note that if you're making any other requests using an access token such as adding a track to a playlist, the access token must be updated in whatever tool you're using to perform the request. For example, if you're using kaaes' excellent Spotify Web API client for Android, you'd need to get a new SpotifyService instance based on the refreshed access token.

Related

Java Custom Google Analytics 4 Server-Side Event User-IP

In my current Java project, it's easy to track server-side user events in the "old" Google Analytics Universal Project with simple REST calls to Google Analytics. So that location tracking was working, i could override the server ip with the user ip, according to the parameter "&uip=1.2.3.4" (https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters?hl=de#uip).
As upgrading to GA4 is recommended, I was able to change all the REST parameters in my project and show my events in the new dashboard, except for the user location. I can't find any information about such a parameter. I tried using still "uip" but now all my requests are located to the country of my server.
Unfortunately it's not possible to track the event client side, because my project is a simple REST API, returning only JSON data.
Does anyone have an idea, if there's such a parameter like "uip" for ga4 or if this isn't possible anymore?
In the following way I setup my parameters:
private String getQueryParameters(MeasurementEvent event) {
StringBuilder body = new StringBuilder();
body.append("?v=").append(version);
body.append("&tid=").append(trackingId);
body.append("&cid=").append(event.getClientId());
body.append("&en=").append(eventName);
body.append("&aip=1");
if (StringUtils.hasText(event.getAction())) {
body.append("&ep.useraction=").append(event.getAction());
}
if (StringUtils.hasText(event.getCategory())) {
body.append("&ep.awsregion=").append(event.getCategory());
}
if (StringUtils.hasText(event.getLabel())) {
body.append("&ep.softwarename=").append(event.getLabel());
}
if (StringUtils.hasText(event.getRemoteAddress())) {
body.append("&uip=").append(event.getRemoteAddress());
}
if (StringUtils.hasText(event.getUrl())) {
body.append("&dl=").append(event.getUrl());
}
return body.toString();
}

Android Wear Watchface Engine Never Calls DataApi Update

I have code like this for a watchface in Android Studio. I'm putting a random integer in the datamap so that the receiving side on the phone app can detect it as having been updated. I set this code in onConnected with the intent that every time the watchface is installed or 'chosen' by the user again, it will update data on the watchface - the main app receives this 'installed' status and then sends back updated information to the watch.
public class MyWatchFace extends CanvasWatchFaceService {
///
private class Engine extends CanvasWatchFaceService.Engine implements
DataApi.DataListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener{
#Override
public void onConnected(#Nullable Bundle bundle) {
Wearable.DataApi.addListener(mGoogleApiClient, this);
PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/installed");
putDataMapReq.getDataMap().putInt(INSTALLED, new Random().nextInt());
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
putDataReq.setUrgent();
Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq)
.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
#Override
public void onResult(DataApi.DataItemResult dataItemResult) {
Log.d(TAG, "Sending Install Status was successful: " + dataItemResult.getStatus()
.isSuccess());
}
});
}
The problem is that this section of code
Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq)
.setResultCallback
never seems to be called when I debug it, while installing or choosing the watchface again. I have to go to the phone app and update information on there for the information to be sent back to the watch.
I originally put the callback in the onCreate method of the Engine, but that was never called either. This is also where I create and connect the mGoogleApiClient, which IS successfully called.
Is there a more appropriate method to put this installed update code in? Why is it never being called in these 2 methods?
Everything else about the watch works fine - it successfully retrieves data from the phone app when the phone app data changes. PS, I use the same mGoogleApiClient to retrieve this phone data to also send the install status, in case you think that might be a problem. Do I have to create two separate clients?

Java + RestFB API: Getting fresh Page Access Token without messing with AppID, appSecret

What I want to do:
I am trying to make a simple program that posts 5-10 statuses, at a time, on a page's wall. The post to the page will have to be done under the name of the page.
I've read tons of badly written Facebook Developers documentation and I'm reaching the point of confusion where I don't even know what questions to ask. So her I am.
My code so far:
I manually got the Page Access token manually, by this method:
Go to https://developers.facebook.com/tools/explorer
At the GET request form, down there, fill in me/accounts
You'll get a Javascript representation of your basic user data. Find the page you want.
Note the access_token and id fields, we're going to use them in the code below.
Thus, after getting the page Access token manually (And the ID of the page, of course)
import com.restfb.DefaultFacebookClient;
import com.restfb.FacebookClient;
import com.restfb.Parameter;
import com.restfb.exception.FacebookException;
import com.restfb.types.FacebookType;
import com.restfb.types.Page;
import com.restfb.types.User;
/**
*
* #author dsfounis
*/
public class FacebookConnector {
/* Variables */
private final String pageAccessToken = "GOT_THIS_FROM_THE_METHOD_ABOVE";
private final String pageID = "THIS_TOO";
private FacebookClient fbClient;
private User myuser = null; //Store references to myr user and page
private Page mypage = null; //for later use. In this question's context, these
//references are useless.
private int counter = 0;
public FacebookConnector() {
try {
fbClient = new DefaultFacebookClient(pageAccessToken);
myuser = fbClient.fetchObject("me", User.class);
mypage = fbClient.fetchObject(pageID, Page.class);
counter = 0;
} catch (FacebookException ex) { //So that you can see what went wrong
ex.printStackTrace(System.err); //in case you did anything incorrectly
}
}
public void makeTestPost() {
fbClient.publish(pageID + "/feed", FacebookType.class, Parameter.with("message", Integer.toString(counter) + ": Hello, facebook World!"));
counter++;
}
}
The problem:
The code above works. The thing is, it works temporarily. The page access token that I get has an expiration time of one hour, and I need to manually go through the process of obtaining it, every time that I run the program. What is the point of automating a process if I keep some aspects of it manual?
So I have to ask you: Can I do the process above programmatically, and obtain a fresh page access token at program launch?
Can I, maybe, use a better API to do something as simple as just post a couple of things on a Page's wall, every day?
My application is a console one, and I would like to stay away from implementing needless Logins, even though if you tell me that it is needed, it's going to be a bother I'll have to go through.
As a note: I've got the application registered in Facebook Developers, albeit only as a basic app. To get more permissions, I need to show proof of Facebook Login implementation, and as I say in the title, it's something I'll have to avoid.
There is no automatic process to obtain an access token. If there was, it will defeat the whole purpose of the OAuth flow. For pet projects and tests it's okay to use the Graph API Explorer but for public applications involving users it is mandatory that the user manually selects the login dialog.
Under your current scenario you can extend the user token using the method mentioned here https://developers.facebook.com/docs/roadmap/completed-changes/offline-access-removal/
Scenario 5: Page Access Tokens
When a user grants an app the manage_pages permission, the app is able
to obtain page access tokens for pages that the user administers by
querying the [User ID]/accounts Graph API endpoint. With the migration
enabled, when using a short-lived user access token to query this
endpoint, the page access tokens obtained are short-lived as well.
Exchange the short-lived user access token for a long-lived access
token using the endpoint and steps explained earlier.
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
By using a
long-lived user access token, querying the [User ID]/accounts endpoint
will now provide page access tokens that do not expire for pages that
a user manages. This will also apply when querying with a non-expiring
user access token obtained through the deprecated offline_access
permission.
A simple program used only by the owner of the application does not need approval from Facebook.
e.g. https://www.facebook.com/phwdbot

FB SDK not working on OS 7

I am implementing FB in one of my app.I am using jar 0.8.25. Its working fine on all simulators from 5 to 7.1.And for devices works only for OS 5 and 6 but not working on device 7 and 7.1.For OS 7 after log in success it remains on FB page it doesn't redirect back. and when i press back button, i get error encountered unable to refresh access token with try again button.
When analyzing on console it never finds access token single time for OS 7.while for 5 and 6 its working perfectly.
Please tell what may cause the issue.
Thanks,
This isn't a solution to your specific problem. I mentioned in the comments that I'm using an interface. So I'm posting here as its too much for the comment section. It is also not the COMPLETE solution, you will need to handle the flow and expired tokens, this is just to show you the logic of how I did this.
For my interface I open a browserfield to the Oauth url:
https://www.facebook.com/dialog/oauth?client_id=<APP_ID>&response_type=token&redirect_uri=http://www.facebook.com/connect/login_success.html&scope=publish_actions
And I add a listener to this browser to listen for the redirects after login. Once you have the access token, you should persist it and close the browserfield.
private class OAuthScreen extends MainScreen
{
BrowserField browser_field;
LoadingDialog loading_dialog;
public OAuthScreen(final Command task)
{
super(VERTICAL_SCROLL | HORIZONTAL_SCROLL);
BrowserFieldConfig browserConfig = new BrowserFieldConfig();
browserConfig.setProperty(BrowserFieldConfig.VIEWPORT_WIDTH, new Integer(Display.getWidth()));
browser_field = new BrowserField(browserConfig);
browser_field.addListener(new BrowserFieldListener()
{
public void documentCreated(BrowserField browserField, ScriptEngine scriptEngine, Document document) throws Exception
{
int index = browserField.getDocumentUrl().indexOf("#access_token=");
if (index == -1)
{
super.documentCreated(browserField, scriptEngine, document);
}
else
{
access_token = browserField.getDocumentUrl().substring(index + "#access_token=".length(), browserField.getDocumentUrl().indexOf("&"));
PersistentObject store = PersistentStore.getPersistentObject(STORE_KEY);
FacebookTokens store_tokens = new FacebookTokens();
store_tokens.access_token = access_token;
store.setContents(store_tokens);
store.commit();
if (task != null) task.execute();
OAuthScreen.this.close();
}
}
public void documentLoaded(BrowserField browserField, Document document) throws Exception
{
super.documentLoaded(browserField, document);
loading_dialog.close();
}
});
// whatever loading dialog you want, this sometimes takes a while to open
loading_dialog = LoadingDialog.push(loading_field);
add(browser_field);
browser_field.requestContent("https://www.facebook.com/dialog/oauth?client_id="+APP_ID+"&response_type=token&redirect_uri=http://www.facebook.com/connect/login_success.html&scope=publish_actions");
}
}
The callback task is just for if I want to perform a call directly after login.
Now just perform API calls as you need them. API methods here https://developers.facebook.com/docs/graph-api/reference/v2.0/
Methods that require the access token, should have it appended to the url such as, https://graph.facebook.com/me/feed?access_token=" + access_token
Be aware that clearing your access token won't clear the token stored in the browser field. And will mean that you can't login next time (because the browser is still logged in).
So if you want to logout you need to open this link in a browserfield before clearing your local access token "https://www.facebook.com/logout.php?next=http://www.facebook.com/connect/login_success.html&access_token=" + access_token
Clearing the cookies of the browser should suffice, but I haven't found a way to do this.

In GoogleCloudMessaging API, how to handle the renewal or expiration of registration ID?

As the question says that How to find out when does the registration ID has become invalid in GoogleCloudMessaging API?
I already read the answers on few questions on similar topic: Do GCM registration id's expire? and Google Coud Mesaging (GCM) and registration_id expiry, how will I know? . The issue with those question is that the answers there are for C2DMor old GCM API which used GCMRegistrar instead of GoogleCloudMessaging API. The previous two methods have been depreciated.
I'll try to break my confusion/question stepwise:
1) Under the heading Enable GCM, in the second point it says:
Google may periodically refresh the registration ID, so you should design your Android application with the understanding that the com.google.android.c2dm.intent.REGISTRATION intent may be called multiple times. Your Android application needs to be able to respond accordingly.
The registration ID lasts until the Android application explicitly unregisters itself, or until Google refreshes the registration ID for your Android application. Whenever the application receives a com.google.android.c2dm.intent.REGISTRATION intent with a registration_id extra, it should save the ID for future use, pass it to the 3rd-party server to complete the registration, and keep track of whether the server completed the registration. If the server fails to complete the registration, it should try again or unregister from GCM.
2) Now, if that's the case then I should handle the intent in a BroadcastReceiver and send the register() request again to get a new registration ID. But the issue is that on the same page under heading ERROR_MAIN_THREAD, it says that:
GCM methods are blocking. You should not run them in the main thread or in broadcast receivers.
3) I also understand that there are other two scenarios when the registration ID changes( as mentioned under Advanced Topics under heading Keeping the Registration State in Sync):
Application update and Backup&restore. I am already handling them on opening of the app.
4) In GCMRegistrar API, inside GCMBaseIntentService, there used to be a callback onRegistered() method, which got called when the device got registered. Here I used to persist the registration ID and send to 3rd party servers.
But, now How should I handle the updation or renewal of the registration ID, persist it and send it to 3rd party server?
It might be that either I am getting confused by reading all of it or I am missing something. I would be really thankful for your help.
Update
Even on Handling registration ID changes in Google Cloud Messaging on Android thread, there is no mentioning of how to handle the periodic refreshing of ID by Google?
I am giving a way as What I implemented in my application
#Override
protected void onRegistered(Context context, String registrationId) {
Log.i(TAG, "Device registered: regId = " + registrationId);
//displayMessage(context, getString(R.string.gcm_registered));
//ServerUtilities.register(context, registrationId);
//1. Store this id to application Prefs on each request of device registration
//2. Clear this id from app prefs on each request of device un-registration
//3. Now add an if check for new registartion id to server, you can write a method on server side to check if this reg-id matching for this device or not (and you need an unique identification of device to be stored on server)
//4. That method will clear that if id is matching it meanse this is existing reg-id, and if not matching this is updated reg-id.
//5. If this is updated reg-id, update on server and update into application prefs.
}
You can do like this also
if reg_id exists_into prefrences then
if stored_id equals_to new_reg_id then
do nothing
else
say server to reg_id updated
update prefrences with new id
end if
else
update this id to application prefs
say server that your device is registered
end if
But problem arises when, user clears the application data and you will loose the current reg-id.
Update for new API example Credits goes to Eran and His Answer Handling registration ID changes in Google Cloud Messaging on Android
Google changed their Demo App to use the new interface. They refresh the registration ID by setting an expiration date on the value persisted locally by the app. When the app starts, they load their locally stored registration id. If it is "expired" (which in the demo means it was received from GCM over 7 days ago), they call gcm.register(senderID) again.
This doesn't handle the hypothetical scenario in which a registration ID is refreshed by Google for an app that hasn't been launched for a long time. In that case, the app won't be aware of the change, and neither will the 3rd party server.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mDisplay = (TextView) findViewById(R.id.display);
context = getApplicationContext();
regid = getRegistrationId(context);
if (regid.length() == 0) {
registerBackground();
}
gcm = GoogleCloudMessaging.getInstance(this);
}
/**
* Gets the current registration id for application on GCM service.
* <p>
* If result is empty, the registration has failed.
*
* #return registration id, or empty string if the registration is not
* complete.
*/
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.length() == 0) {
Log.v(TAG, "Registration not found.");
return "";
}
// check if app was updated; if so, it must clear registration id to
// avoid a race condition if GCM sends a message
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion || isRegistrationExpired()) {
Log.v(TAG, "App version changed or registration expired.");
return "";
}
return registrationId;
}
/**
* Checks if the registration has expired.
*
* <p>To avoid the scenario where the device sends the registration to the
* server but the server loses it, the app developer may choose to re-register
* after REGISTRATION_EXPIRY_TIME_MS.
*
* #return true if the registration has expired.
*/
private boolean isRegistrationExpired() {
final SharedPreferences prefs = getGCMPreferences(context);
// checks if the information is not stale
long expirationTime =
prefs.getLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);
return System.currentTimeMillis() > expirationTime;
}
Just to add to Pankaj's answer:
This(the example on getting started documents by Google) doesn't handle the hypothetical scenario in which a registration ID is
refreshed by Google for an app that hasn't been launched for a long
time. In that case, the app won't be aware of the change, and neither
will the 3rd party server.
Its true that the example on Getting
started documentation does not handle that case. So the developer
need to handle himself.
Also the answer says that They refresh the registration ID by setting an expiration date on the value persisted locally by the app. When the app starts, they load their locally stored registration id. If it is "expired" they call gcm.register(senderID) again.
The issue is that the seven days local expiry of the registration ID in the sample is to avoid the scenario where the device sends the
registration to the 3rd party server but the server loses it. It does
not handle the refreshing of the ID from Google servers.
The second point under the heading Enable GCM on Architectural
Overview page, it says:
Note that Google may periodically refresh the registration ID, so
you should design your Android application with the understanding
that the com.google.android.c2dm.intent.REGISTRATION intent may be
called multiple times. Your Android application needs to be able to
respond accordingly.
So, for handling that you should have a Broadcast Listener which
could handle com.google.android.c2dm.intent.REGISTRATION intent,
which Google send to the app when it has to refresh the registration
ID.
There is another part of the question which states about the problem is that inside the Broadcast Listener I cannot call register the for Push ID again. This is because the
documentation says:
GCM methods are blocking. You should not run them in the main thread or in broadcast receiver.
I think that issue is completely different from the statement. When you register a broadcast receiver, it will have an Intent which will contain the new registration ID from Google. I DON'T need to call gcm.register() method again in the Broadcast listener.
Hope this helps someone understand how to handle the renewal of registration ID.

Categories

Resources