03-14-2018 03:03 - edited 03-14-2018 03:05
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-14-2018 03:03 - edited 03-14-2018 03:05
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
Hello, I have an issue with the redirect_url on Android..
User is directed to the Authorization Page in Chrome Custom Tabs, when I click "agree", user is not redirected back to application..
What are the rules and expectancies regarding redirect_url for FitBit API?
I find the documentation on the website very unclear..
I have already set callback_uri in many different ways, with different outcomes:
Local Page with Auth Code, no redirect because not specified:
Error Not Found:
- http://locallhost.com/callback
- http://locallhost.com/redirect
What should I use to test locally?
Oops it it not you, it is us..
Setting in the Application Page are correct.
Settings in the Manifest are the following, adapted per redirect..
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="www.locallhost.com/redirect"
android:scheme="http"/>
</intent-filter>
Answered! Go to the Best Answer.
03-15-2018 02:51
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 02:51
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
No, nothing..

03-15-2018 02:59
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 02:59
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
Since you're not even getting an error its like onNewIntent() isn't even getting called, which is also strange.
Could you send me your whole <activity></activity> block and the associated public class? - PM me it if you dont want it public

03-15-2018 03:12
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 03:12
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
The Manifest, Callback will be to the MainActivity.. This is a demo for OAuth2 purposes, so MainActivity is the only activity in here. For now, I have not included anything regarding getting the Token or Refreshing the Token yet. This will be covered by Scribe Library.
The Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.gebruiker.scribetest">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="fitbitcallback"
android:scheme="niels"/>
</intent-filter>
</activity>
</application>
</manifest>
The MainActivity
public class MainActivity extends AppCompatActivity {
private String clientid = "xxxxx";
private String client_secret = "xxxxxxxxxxxx";
private String AUTHCODE_URL = "https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=22CJ69&scope=activity%20heartrate%20location%20profile%20sleep";
private static final String TAG = "MyActivity";
String string;
Uri uri;
//here you create an instance of the FitBit20Service Object and of the FitBitApi that is used to initiate the authentication
private OAuth20Service service = new ServiceBuilder(clientid)
.apiSecret(client_secret)
.apiKey(clientid)
.scope("activity heartrate location profile sleep")
.build(FitBitApi.instance());
//create an instance of Scanner so you can read the output from the FitBit servers
private Scanner in = new Scanner(System.in);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//this code you use in the Sign Up Button/ Activity
LaunchTabs_getCode();
//retrieve parameter from the Intent
onNewIntent(getIntent());
uri = Uri.parse(string);
Set<String> args = uri.getQueryParameterNames();
String returnedCode = uri.getQueryParameter("code");
System.out.println(returnedCode);
//use this code as a parameter in retrieving the accesstoken and refreshtoken
//this needs to be done on a separate thread, not on the UI, otherwise you get an error
//store the accesstoken and the refreshtoken in FireBase for this user
//new OAuthTask(code).execute();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, intent.toString());
string = intent.getDataString();
}
public void LaunchTabs_getCode(){
String authorizationUrl = service.getAuthorizationUrl();
System.out.println(authorizationUrl);
final CustomTabsIntent intent = new CustomTabsIntent.Builder().build();
final String url = AUTHCODE_URL;
intent.launchUrl(this,Uri.parse(url));
}
}

03-15-2018 03:33
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 03:33
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
- Who Voted for this post?
Okay, can you try splitting up your classes? Instead of having one class that both gets and tries to process the returned code.
Instead, have one class that sends the user off to Fibit for authorisation and a second class that take the input from Chrome Tab. I'm thinking the problem is once your users are finished in the Chrome Tab android is reusing the MainActivity class and never calling onNewIntent() - since the class has already been initiated.
I'm thinking something like this:
The Manifest:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.gebruiker.scribetest"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:launchMode="singleInstance"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".RedirectActivity" android:label="RedirectActivity" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:host="fitbitcallback" android:scheme="niels"/> </intent-filter> </activity> </application> </manifest>
The MainActivity:
public class MainActivity extends AppCompatActivity { private String clientid = "xxxxx"; private String client_secret = "xxxxxxxxxxxx"; private String AUTHCODE_URL = "https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=22CJ69&scope=activity%20heartrate%20location%20profile%20sleep"; private static final String TAG = "MyActivity"; //here you create an instance of the FitBit20Service Object and of the FitBitApi that is used to initiate the authentication private OAuth20Service service = new ServiceBuilder(clientid) .apiSecret(client_secret) .apiKey(clientid) .scope("activity heartrate location profile sleep") .build(FitBitApi.instance()); //create an instance of Scanner so you can read the output from the FitBit servers private Scanner in = new Scanner(System.in); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //this code you use in the Sign Up Button/ Activity LaunchTabs_getCode(); } public void LaunchTabs_getCode(){ String authorizationUrl = service.getAuthorizationUrl(); System.out.println(authorizationUrl); final CustomTabsIntent intent = new CustomTabsIntent.Builder().build(); final String url = AUTHCODE_URL; intent.launchUrl(this,Uri.parse(url)); } }
The (new) RedirectActivity:
public class RedirectActivity extends AppCompatActivity { private static final String TAG = "MyActivity"; String string; Uri uri; @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.d(TAG, intent.toString()); string = intent.getDataString(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //retrieve parameter from the Intent onNewIntent(getIntent()); uri = Uri.parse(string); Set<String> args = uri.getQueryParameterNames(); String returnedCode = uri.getQueryParameter("code"); System.out.println(returnedCode); } }
03-15-2018 03:52
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 03:52
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
I get directed to the Authorization Page and when clicking "Agree" to the RedirectActivity.
Still no "returnedCode" though..
In LogCat I find "E/MultiWindowProxy: getServiceInstance failed!"
I don't know what it means, only thing in red I can find..

03-15-2018 04:13 - edited 03-15-2018 04:17
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 04:13 - edited 03-15-2018 04:17
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
- Who Voted for this post?
I just remembered how much I love debuging Java code. The compiler give you such detailed error messages!
I'm assuming your log level is set to DEBUG, so I've updated the RedirectActivity class with a bunch of log lines so we can trace whats going on. Can you run this and let me see what appears in the log message - See message bellow
03-15-2018 04:16
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 04:16
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
- Who Voted for this post?
I must be having a bad day!
public class RedirectActivity extends AppCompatActivity { private static final String TAG = "MyActivity"; @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "FINDLINE 1"); super.onCreate(savedInstanceState); Log.d(TAG, "FINDLINE 2"); setContentView(R.layout.activity_main); // For now this will work, but once everthing is fine change this to what ever layout your using once a user has completed authorisation Log.d(TAG, "FINDLINE 3"); //retrieve parameter from the Intent Log.d(TAG, "FINDLINE 4"); List<String> params = getIntent().getData().getPathSegments(); Log.d(TAG, "FINDLINE 5"); final int size = params.size(); Log.d(TAG, "FINDLINE 6 There are " + size + " segments"); for (int i = 0; i < size; i++) { object = params.get(i); Log.d(TAG, "FINDLINE 6." + i + " = " + object); } } }
03-15-2018 04:30
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 04:30
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
I don't really understand what you are doing here..
Have adapted a few things:
- changed layout to layout of activity we land on
- added "onNewIntent(getIntent()); to have something to work with
- declared object as a String
- changed "alist" to "params", I think you meant that..
For the moment this is not returning anything in the "LogCat Debug"
public class RedirectActivity extends AppCompatActivity { private static final String TAG = "MyActivity"; @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "FINDLINE 1"); super.onCreate(savedInstanceState); Log.d(TAG, "FINDLINE 2"); setContentView(R.layout.activity_redirect); // For now this will work, but once everthing is fine change this to what ever layout your using once a user has completed authorisation Log.d(TAG, "FINDLINE 3");
onNewIntent(getIntent()); //retrieve parameter from the Intent Log.d(TAG, "FINDLINE 4"); List<String> params = getIntent().getData().getPathSegments(); Log.d(TAG, "FINDLINE 5"); final int size = params.size(); Log.d(TAG, "FINDLINE 6 There are " + size + " segments"); for (int i = 0; i < size; i++) { String object = params.get(i); Log.d(TAG, "FINDLINE 6." + i + " = " + object); } } }

03-15-2018 04:33
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 04:33
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
Thank you so much for the help.. I don't understand why they make it so hard to get to an API..
The value for me is in using the API, making catching visualizations with good UX.. My functional design is ready, just this OAuth2.. Pff.. 🙂

03-15-2018 04:43
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 04:43
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
- Who Voted for this post?
Basically I'm trying to get it to print a each line into LogCat. That way if it does spit out an error we can see whats causing it by checking which FINDLINE's its coming between.
We shouldnt need the onNewIntent() function anymore since params directly from getIntent(), and all onNewIntent() was doing was storing the getIntent value as a string anyway.
Under FINDLINE 4 I'm trying to get all the parameters from the intent path and putting them in a List.
FINDLINE 6 should tell us how many parameters are being sent back by Chrome Tab and the for loop is supposed to just tell us what parameters are being returned.
I'm really not sure why nothing is bring printed in LogCat though, I wonder if its worth replacing all Log.d() function with System.out.println().
Your app is going to Fibit and authorising users, Chrome tab is then sending users back to the correct class. There is just something not right about how we're getting the returned code from the intent and I'm wondering if Chrome tab is even returning a code or not. So I'm trying to get everthing returned from the intent and print it out to logs so we can actually inspect whats being sent back
03-15-2018 04:54 - edited 03-15-2018 04:55
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 04:54 - edited 03-15-2018 04:55
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
I get nothing back in the Debug Log in Logcat. Not when using Log.d, not when using System.out.println(). Left onNewIntent() out..
My second activity now is//
public class RedirectActivity extends AppCompatActivity {
private static final String TAG = "MyActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "FINDLINE 1");
super.onCreate(savedInstanceState);
Log.d(TAG, "FINDLINE 2");
setContentView(R.layout.activity_main); // For now this will work, but once everthing is fine change this to what ever layout your using once a user has completed authorisation
Log.d(TAG, "FINDLINE 3");
//retrieve parameter from the Intent
Log.d(TAG, "FINDLINE 4");
List<String> params = getIntent().getData().getPathSegments();
Log.d(TAG, "FINDLINE 5");
final int size = params.size();
Log.d(TAG, "FINDLINE 6 There are " + size + " segments");
for (int i = 0; i < size; i++) {
String object = params.get(i);
Log.d(TAG, "FINDLINE 6." + i + " = " + object);
}
}
}

03-15-2018 05:38
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 05:38
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
- Who Voted for this post?
Okay, i'm now setting up my own Android build enviroment. this may take some time - I will be back...
03-15-2018 06:10
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 06:10
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
ok, great, thanks!

03-15-2018 08:10
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 08:10
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
- Who Voted for this post?
Sorry about the wait, I forgot just how long it takes to setup a working development enviroment on Windows - and how slow the emulator was!
It turns out I made two mistakes in my previous code.
- Uri.parse() - is not required as getIntent().getData() already returns a Uri object
- getPathSegments() - was also wrong, it should have been getQueryParameter() already
Here is a working example:
MainActivity:
public class MainActivity extends AppCompatActivity { private String clientid = "XXXXXX"; private static final String TAG = "MyActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //this code you use in the Sign Up Button/ Activity getApiCodeFromFitbit(); } public void getApiCodeFromFitbit() { String url = "https://www.fitbit.com/oauth2/authorize?" + "response_type=code" + "&client_id=" + clientid + "&scope=activity"+ "&redirect_uri=niels://fitbitcallback"; CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); builder.setToolbarColor(ContextCompat.getColor(this, R.color.colorAccent)); // set toolbar color and/or setting custom actions before invoking build() // Once ready, call CustomTabsIntent.Builder.build() to create a CustomTabsIntent CustomTabsIntent customTabsIntent = builder.build(); // and launch the desired Url with CustomTabsIntent.launchUrl() customTabsIntent.launchUrl(this, Uri.parse(url)); } }
RedirectActivity:
public class RedirectActivity extends AppCompatActivity { private static final String TAG = "MyActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // For now this will work, but once everthing is fine change this to what ever layout your using once a user has completed authorisation //retrieve parameter from the Intent Uri returnUrl = getIntent().getData(); if (returnUrl != null) { String apiCode = returnUrl.getQueryParameter("code"); Log.d(TAG, "API Code returned from Fitbit " + apiCode); } else { Log.d(TAG, "Something is wrong with the return value from Fitbit. getIntent().getData() is NULL?"); } } }
In all my testing apiCode is correctly populated with the OAuth2 code from Fitbit. I've uploaded the full project code to my Git server
Let me know how you get on with this code
03-15-2018 09:38 - edited 03-15-2018 10:03
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 09:38 - edited 03-15-2018 10:03
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
Hi man,
Thank you very much for your help..
This indeed functions and I get the code back in LogCat.
I see you use a very different approach from what was proposed in the Scribe library I used.
This is a much more straightforward way of doing it.
Seeing this I would want to lose the dependency on Scribe, and also use the direct approach for the token request, the refresh and the revoke flow. For these three requests, I need custom Headers though. Is it possible to add custom headers to a url?
Furthermore, two of them are POST requests..
Also, this does not involve going to an Authorization page anymore, they are direct requests.
So Chrome Custom Tabs is not necessary anymore, but can/ should I keep using it?
What would you do?

03-15-2018 10:02
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 10:02
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
- Who Voted for this post?
Yeah I tried setting up my example to use the Scribe library but in the end it was all getting too confusing.
I need to pop out for a bit, but let me have a think about how I would do it and I'll get back to you.
I think I might create a new class to handle the resquests, that how I would solve it in my web app so the principle should work for an Android app too but I'll have a play about with my example code and see if I can come up with something eligent
03-15-2018 10:04
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 10:04
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
Thanks for your time and patience..
You are of great help to me, you cannot imagine.. 🙂

03-15-2018 13:17
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 13:17
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
@NielsWearablesI just want to check, you said before you were writing to API level 17. Is that definitly the API level you want your app to be aimed at?
I'm running into a few things I'm trying to do that require higher API level, but if you need it to be 17 I can find other ways to do the same things - I just wanted to check your requirments

03-15-2018 16:02 - edited 03-15-2018 16:03
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-15-2018 16:02 - edited 03-15-2018 16:03
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
Hi,
I would say I would like to target at least 90% of available Android devices out there.. That means API 19 or less. That is the target I would like to have for percentage of devices.
With regards to the libraries used, it is a very positive thing to avoid library dependencies. The fact that you can find an approach that avoids Scribe is another plus.
@DavidSFitbit, I have been getting super advice from stuart since a few days. Maybe you can work out an integrated OAuth2 approach for Android and other platforms with him. I have to say, it has been a pleasure conversating with him, been a great help. I am into UX, Data Visualization, IoT and this sort of stuff, not a Backend guy.. It could be a great added value for FitBit to have a simplified onboarding procedure regarding OAuth2 for different platforms.
@stuartma, thank you again for all the help!

03-16-2018 04:34 - edited 03-16-2018 04:34
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post

03-16-2018 04:34 - edited 03-16-2018 04:34
- Mark as New
- Bookmark
- Subscribe
- Permalink
- Report this post
Right its done. In the end I ended up rethinking most of it and rewrote everything else. I’ve updated the Git repo for you to see a working example.
In the end I went back to ScribeJava, anything else was just reinventing the wheel, but this time I’ve wrapped it all in a helper class (NxFitbitHelper) tokens to make things simpler, this is where you setup your ClientID and access.
MainActivity.java – now just calls a static method of the helper class to get the users authenticated
RedirectActivity.java – creates a instance of the helper class and sets up the access token .requestAccessTokenFromIntent(). Everything bellow that line is just examples of how to get a query information. You’ll probably use the last example most by making direct calls to the API, but if you find your calling the same end point several times setting up helper methods like I have for Profile might be better for you so you dont hit any rate limits
NxFitbitHelper.java is the helper class but inorder to get Scribe to work you'll need to include FitbitApi20.java and Fitbit20ServiceImpl.java so we can setup the Authorization header - the non-standard OAuth element to the API.
You shoud be able to seriablise the helper class and pass it around but I've not tested that - there are limits to my android programming skills.
I think this is about the limit of help I can give you without actual seeing your source code! But if you need anything else added to the helper class, or help pulling data out of the returned JSON I should be able to do that.
Let me know how you get on, I'm intrigued to see what you come up with now

