Git Product home page Git Product logo

stravazpot-android's Introduction

StravaZpot Build Status

A fluent API to integrate with Strava on Android apps.

Usage

This document explains how to use StravaZpot in your Android app. For additional questions, you may want to check Strava official documentation here.

Authentication

Login button

StravaZpot includes a custom view to include a login button according to Strava guidelines. To do that, you can add the following code to your XML layout:

<com.sweetzpot.stravazpot.authenticaton.ui.StravaLoginButton
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/login_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    app:type="orange"/>

The custom attribute type accepts two different values: orange and light (default). StravaLoginButton makes use of vector drawables in the support library. Thus, if you are targeting version prior to Android API 21, you would need to add the following code to your Activity:

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

Login Activity

Strava uses OAuth 2.0 to authenticate users, and then request them to grant permission to your app. To get a Client ID and Secret from Strava for your app, follow this link.

Once you have your app credentials, you can get an Intent to launch the login activity for your app. You can easily do it with:

Intent intent = StravaLogin.withContext(this)
                  .withClientID(<YOUR_CLIENT_ID>)
                  .withRedirectURI(<YOUR_REDIRECT_URL>)
                  .withApprovalPrompt(ApprovalPrompt.AUTO)
                  .withAccessScope(AccessScope.VIEW_PRIVATE_WRITE)
                  .makeIntent();
startActivityForResult(intent, RQ_LOGIN);

You need to notice several things with this call:

  • <YOUR_CLIENT_ID> must be replaced with the Client ID provided by Strava when you registered your application.
  • <YOUR_REDIRECT_URL> must be in the domain you specified when you registered your app in Strava.
  • Refer to ApprovalPrompt enum to get more options for this parameter.
  • Refer to AccessScope enum to get more options for this parameter.
  • You need to launch the intent with startActivityForResult since the login activity will return a value that you will need later to obtain a token. If login to Strava was successful and the user granted permission to your app, you will receive a code that you can retrieve with:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode == RQ_LOGIN && resultCode == RESULT_OK && data != null) {
        String code = data.getStringExtra(StravaLoginActivity.RESULT_CODE);
        // Use code to obtain token
    }
}

Finally, you need to add the login activity to your manifest:

<activity
    android:name="com.sweetzpot.stravazpot.authenticaton.ui.StravaLoginActivity"
    android:label="@string/login_strava" />

Obtain a Token

Every Strava API call needs a token to prove the user is authenticated and the app has permission to access the API. After you have obtained the code from user login, you need to exchange it with Strava to get a token. You can do it with the following code:

AuthenticationConfig config = AuthenticationConfig.create()
                                                  .debug()
                                                  .build();
AuthenticationAPI api = new AuthenticationAPI(config);
LoginResult result = api.getTokenForApp(AppCredentials.with(CLIENT_ID, CLIENT_SECRET))
                        .withCode(CODE)
                        .execute();

Notice that in this call you must provide the Client ID and Secret provided by Strava when you registered your application, and the code obtained during the login process. Also, the execution of the previous code involves a network request; you are responsible for calling this code in a suitable thread, outside the UI thread. Otherwise, you will get an exception.

If the previous request is successful, you will get a LoginResult, which has a Token that you can use in your subsequent API calls, and an Athlete instance, representing the authenticated user.

Deauthorize

AuthenticationAPI api = new AuthenticationAPI(config);
authenticationAPI.deauthorize()
                 .execute();

Athlete API

StravaConfig

Before introducing the Athlete API, we have to talk about StravaConfig. StravaConfig is a class required by all the APIs in StravaZpot to configure the way it is going to interact with Strava. You can create a single instance of StravaConfig as soon as you obtain a token, and reuse it during your app lifecycle. To create an instance of StravaConfig:

StravaConfig config = StravaConfig.withToken(TOKEN)
                                  .debug()
                                  .build();

You must provide the token obtained during the authentication process. The call to debug() method will show in the Android Monitor what is going on when you do the network requests.

Once you have the configuration object, you can proceed to use all the APIs.

Create the Athlete API object

AthleteAPI athleteAPI = new AthleteAPI(config);

Retrieve current athlete

Athlete athlete = athleteAPI.retrieveCurrentAthlete()
                            .execute();

Retrieve another athlete

Athlete athlete = athleteAPI.retrieveAthlete(ATHLETE_ID)
                            .execute();

Update an athlete

Athlete athlete = athleteAPI.updateAthlete()
                            .newCity(CITY)
                            .newState(STATE)
                            .newCountry(COUNTRY)
                            .newSex(Gender.FEMALE)
                            .newWeight(WEIGHT)
                            .execute();

Retrieve athlete's zones

Zones zones = athleteAPI.getAthleteZones()
                        .execute();

Retrieve athlete's totals and stats

Stats stats = athleteAPI.getAthleteTotalsAndStats(ATHLETE_ID)
                        .execute();

List athlete K/QOMs/CRs

List<SegmentEffort> koms = athleteAPI.listAthleteKOMS(ATHLETE_ID)
                                     .inPage(PAGE)
                                     .perPage(ITEMS_PER_PAGE)
                                     .execute();

Friend API

Create the Friend API object

FriendAPI friendAPI = new FriendAPI(config);

List user's friends

List<Athlete> friends = friendAPI.getMyFriends()
                                 .inPage(PAGE)
                                 .perPage(ITEMS_PER_PAGE)
                                 .execute();

List another athlete's friends

List<Athlete> friends = friendAPI.getAthleteFriends(ATHLETE_ID)
                                 .inPage(PAGE)
                                 .perPage(ITEMS_PER_PAGE)
                                 .execute();

List user's followers

List<Athlete> followers = friendAPI.getMyFollowers()
                                   .inPage(PAGE)
                                   .perPage(ITEMS_PER_PAGE)
                                   .execute();

List another athlete's followers

List<Athlete> followers = friendAPI.getAthleteFollowers(123456)
                                   .inPage(2)
                                   .perPage(10)
                                   .execute();

List common following athletes between two users

List<Athlete> followers = friendAPI.getBothFollowing(ATHLETE_ID)
                                   .inPage(PAGE)
                                   .perPage(PER_PAGE)
                                   .execute();

Activity API

Create the Activity API object

ActivityAPI activityAPI = new ActivityAPI(config);

Create an activity

Activity activity = activityAPI.createActivity(ACTIVITY_NAME)
                               .ofType(ActivityType.RUN)
                               .startingOn(START_DATE)
                               .withElapsedTime(Time.seconds(SECONDS))
                               .withDescription(ACTIVITY_DESCRIPTION)
                               .withDistance(Distance.meters(METERS))
                               .isPrivate(false)
                               .withTrainer(true)
                               .withCommute(false)
                               .execute();

Retrieve an activity

Activity activity = activityAPI.getActivity(ACTIVITY_ID)
                               .includeAllEfforts(true)
                               .execute();

Update an activity

Activity activity = activityAPI.updateActivity(ACTIVITY_ID)
                               .changeName(ACTIVITY_NAME)
                               .changeType(ActivityType.RIDE)
                               .changePrivate(true)
                               .changeCommute(true)
                               .changeTrainer(true)
                               .changeGearID(GEAR_ID)
                               .changeDescription(ACTIVITY_DESCRIPTION)
                               .execute();

Delete an activity

activityAPI.deleteActivity(321934)
           .execute();

List user's activities

List<Activity> activities = activityAPI.listMyActivities()
                                       .before(Time.seconds(BEFORE_SECONDS))
                                       .after(Time.seconds(AFTER_SECONDS))
                                       .inPage(PAGE)
                                       .perPage(ITEMS_PER_PAGE)
                                       .execute();

List user's friends' activities

List<Activity> activities = activityAPI.listFriendActivities()
                                       .before(Time.seconds(BEFORE_SECONDS))
                                       .inPage(PAGE)
                                       .perPage(ITEMS_PER_PAGE)
                                       .execute();

List related activities

List<Activity> activities = activityAPI.listRelatedActivities(ACTIVITY_ID)
                                       .inPage(PAGE)
                                       .perPage(ITEMS_PER_PAGE)
                                       .execute();

List activity zones

List<ActivityZone> activityZones = activityAPI.listActivityZones(ACTIVITY_ID)
                                              .execute();

List activity laps

List<ActivityLap> laps = activityAPI.listActivityLaps(ACTIVITY_ID)
                                    .execute();

Comment API

Create the Comment API object

CommentAPI commentAPI = new CommentAPI(config);

List activity comments

List<Comment> comments = commentAPI.listActivityComments(ACTIVITY_ID)
                                   .inPage(PAGE)
                                   .perPage(ITEMS_PER_PAGE)
                                   .execute();

Kudos API

Create the Kudos API object

KudosAPI kudosAPI = new KudosAPI(config);

List activity kudoers

List<Athlete> athletes = kudosAPI.listActivityKudoers(ACTIVITY_ID)
                                 .inPage(PAGE)
                                 .perPage(ITEMS_PER_PAGE)
                                 .execute();

Photo API

Create the Photo API object

PhotoAPI photoAPI = new PhotoAPI(config);

List activity photos

List<Photo> photos = photoAPI.listAcivityPhotos(ACTIVITY_ID)
                             .execute();

Club API

Create the Club API object

ClubAPI clubAPI = new ClubAPI(config);

Retrieve a club

Club club = clubAPI.getClub(CLUB_ID)
                   .execute();

List club announcements

List<Announcement> announcements = clubAPI.listClubAnnouncements(CLUB_ID)
                                          .execute();

List club group events

List<Event> events = clubAPI.listClubGroupEvents(CLUB_ID)
                            .execute();

List user's clubs

List<Club> clubs = clubAPI.listMyClubs()
                          .execute();

List club members

List<Athlete> athletes = clubAPI.listClubMembers(CLUB_ID)
                                .inPage(PAGE)
                                .perPage(ITEMS_PER_PAGE)
                                .execute();

List club admins

List<Athlete> athletes = clubAPI.listClubAdmins(CLUB_ID)
                                .inPage(PAGE)
                                .perPage(ITEMS_PER_PAGE)
                                .execute();

List club activities

List<Activity> activities = clubAPI.listClubActivities(CLUB_ID)
                                   .before(BEFORE)
                                   .inPage(PAGE)
                                   .perPage(PER_PAGE)
                                   .execute();

Join a club

JoinResult joinResult = clubAPI.joinClub(123456)
                               .execute();

Leave a club

LeaveResult leaveResult = clubAPI.leaveClub(123456)
                                 .execute();

Gear API

Create the Gear API object

GearAPI gearAPI = new GearAPI(config);

Retrieve gear

Gear gear = gearAPI.getGear(GEAR_ID)
                   .execute();

Route API

Create the Route API

RouteAPI routeAPI = new RouteAPI(config);

Retrieve a route

Route route = routeAPI.getRoute(ROUTE_ID)
                      .execute();

List athlete's routes

List<Route> routes = routeAPI.listRoutes(ATHLETE_ID)
                             .execute();

Segment API

Create the Segment API object

SegmentAPI segmentAPI = new SegmentAPI(config);

Retrieve a segment

Segment segment = segmentAPI.getSegment(SEGMENT_ID)
                            .execute();

List user's starred segments

List<Segment> segments = segmentAPI.listMyStarredSegments()
                                   .inPage(PAGE)
                                   .perPage(ITEMS_PER_PAGE)
                                   .execute();

List another athlete's starred segments

List<Segment> segments = segmentAPI.listStarredSegmentsByAthlete(ATHLETE_ID)
                                   .inPage(PAGE)
                                   .perPage(PER_PAGE)
                                   .execute();

Star a segment

Segment segment = segmentAPI.starSegment(SEGMENT_ID)
                            .execute();

Unstar a segment

Segment segment = segmentAPI.unstarSegment(SEGMENT_ID)
                            .execute();

List segment efforts

List<SegmentEffort> efforts = segmentAPI.listSegmentEfforts(SEGMENT_ID)
                                        .forAthlete(ATHLETE_ID)
                                        .startingOn(START_DATE)
                                        .endingOn(END_DATE)
                                        .inPage(PAGE)
                                        .perPage(ITEMS_PER_PAGE)
                                        .execute();

Retrieve segment leaderboard

Leaderboard leaderboard = segmentAPI.getLeaderboardForSegment(SEGMENT_ID)
                                    .withGender(Gender.FEMALE)
                                    .inAgeGroup(AgeGroup.AGE_25_34)
                                    .inWeightClass(WeightClass.KG_75_84)
                                    .following(true)
                                    .inClub(CLUB_ID)
                                    .inDateRange(DateRange.THIS_WEEK)
                                    .withContextEntries(CONTEXT_ENTRIES)
                                    .inPage(PAGE)
                                    .perPage(ITEMS_PER_PAGE)
                                    .execute();

Explore segments

List<Segment> segments = segmentAPI.exploreSegmentsInRegion(Bounds.with(Coordinates.at(SW_LAT, SW_LONG), Coordinates.at(NE_LAT, NE_LONG)))
                                   .forActivityType(ExploreType.RUNNING)
                                   .withMinimumClimbCategory(MIN_CLIMB_CATEGORY)
                                   .withMaximumClimbCategory(MAX_CLIMB_CATEGORY)
                                   .execute();

Segment Effort API

Create the Segment Effort API object

SegmentEffortAPI segmentEffortAPI = new SegmentEffortAPI(config);

Retrieve a segment effort

SegmentEffort segmentEffort = segmentEffortAPI.getSegmentEffort(SEGMENT_EFFORT_ID)
                                              .execute();

Stream API

Create the Stream API object

StreamAPI streamAPI = new StreamAPI(config);

Retrieve activity streams

List<Stream> streams = streamAPI.getActivityStreams(ACTIVITY_ID)
                                .forTypes(StreamType.LATLNG, StreamType.DISTANCE)
                                .withResolution(Resolution.LOW)
                                .withSeriesType(SeriesType.DISTANCE)
                                .execute();

Retrieve segment effort streams

List<Stream> streams = streamAPI.getSegmentEffortStreams(SEGMENT_EFFORT_ID)
                                .forTypes(StreamType.LATLNG, StreamType.DISTANCE)
                                .withResolution(Resolution.LOW)
                                .withSeriesType(SeriesType.DISTANCE)
                                .execute();

Retrieve segment streams

List<Stream> streams = streamAPI.getSegmentStreams(SEGMENT_ID)
                                .forTypes(StreamType.LATLNG, StreamType.DISTANCE)
                                .withResolution(Resoulution.LOW)
                                .withSeriesType(SeriesType.DISTANCE)
                                .execute();

Retrieve route streams

List<Stream> streams = streamAPI.getRouteStreams(ROUTE_ID)
                                .execute();

Upload API

Create the Upload API object

UploadAPI uploadAPI = new UploadAPI(config);

Upload a file

Strava allows you to upload files with formats GPX, FIT or TCX. We recommend to use TCXZpot in order to generate TCX files that can be uploaded to Strava.

UploadStatus uploadStatus = uploadAPI.uploadFile(new File(<path_to_file>))
                                     .withDataType(DataType.FIT)
                                     .withActivityType(UploadActivityType.RIDE)
                                     .withName("A complete ride around the city")
                                     .withDescription("No description")
                                     .isPrivate(false)
                                     .hasTrainer(false)
                                     .isCommute(false)
                                     .withExternalID("test.fit")
                                     .execute();

Check upload status

UploadStatus uploadStatus = uploadAPI.checkUploadStatus(16486788)
                                     .execute();

Threading

All the APIs in StravaZpot perform network requests in a synchronous manner and without switching to a new thread. Therefore, it is up to the user of the library to invoke the API in a suitable thread, and outside the Android UI thread in order to avoid NetworkOnMainThreadException.

Exceptions

StravaZpot methods do not have any checked exceptions, but users of the library should be prepared for them to happen. In particular, the following scenarios can arise:

  • Strava may return a 401 Unauthorized response code. In that case, the network request will throw a StravaUnauthorizedException. It is up to the user of the library to reuthenticate with Strava to get a new token and retry the request.
  • If any other network error happen, or the request is not successful, it will throw a StravaAPIException.

Download

You can get StravaZpot from JCenter using Gradle. Just add this line to your build file:

compile 'com.sweetzpot.stravazpot:lib:1.3.1'

License

Copyright 2018 SweetZpot AS

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

stravazpot-android's People

Contributors

alessionunzi avatar binghammer avatar truizlop avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stravazpot-android's Issues

What to put in redirect uri and CODE

Hello.I would like you to help me with the redirect uri.I put the same url that i put in the domain callback in stravas page(which is localhost). but i get this error.Can you please help by telling me which url to put?Also the .withCode(CODE) what should i write?Sorry for this but i'm totally new

untitled

ERR_TOO_MANY_REDIRECTS

When I try to signIn to Strava with this code :

val intent = StravaLogin.withContext(getContext())
                                .withClientID(CLIENT_ID.toInt())
                                .withRedirectURI("some URL")
                                .withAccessScope(AccessScope.VIEW_PRIVATE_WRITE)
                                .makeIntent()
                        startActivityForResult(intent, 1)

this page is showing
screenshot_1543814409

then I clicked to "Log in using Facebook" and next page is

screenshot_1543814070

"Log in using Google" and by email Password is working perfect this error is only when I try to log in using facebook.

Error with OAuth

Not sure why it's difficult to do but I followed the instructions and getting error.

Intent intent = StravaLogin.withContext(this)
.withClientID(STRAVA_CLIENT_ID)
.withRedirectURI("localhost")
.withApprovalPrompt(ApprovalPrompt.AUTO)
.withAccessScope(AccessScope.VIEW_PRIVATE_WRITE)
.makeIntent();
startActivityForResult(intent, REQUEST_STRAVA_LOGIN);

Got this error:

{"message": "Bad request","errors":[{resource":"Application","field":"redirect_uri", "code":"invalid"}]}

Edit: fixed my problem. It was the uri

Question abut token

In application I should store token and use it in future for calls to Strava, or I should store code, and obtain token at lest on every new start of app? With sore token is problem, tat you not allow to access it via your class;-) I can remove "Bearer" from string representation of token but...

Edit: like I see for example in Strava library for python, they suggest to store token. So maybe getTokien will be good idea?;-) I can also serialize Token object:)

Logout?

Is there any way to logout so that we can sign in to different account?

Segment.getMap does not have polyline field.

Hello,
Was trying to use your library to retrieve favorite segments with their polylines, but...

I have the following response and You can find that map does not have summary_polyline, but just polyline (strava names it full polyline). But, unfortunately Map class do not parse this field.

Question: is it possible to add it?

{"id":3579232,"resource_state":3,"name":"Martha's Chapel - East","activity_type":"Ride","distance":4316.06,"average_grade":0.6,"maximum_grade":3.8,"elevation_high":97.6,"elevation_low":71.2,"start_latlng":[35.78940027393401,-79.00851692073047],"end_latlng":[35.790341310203075,-78.96480194292963],"start_latitude":35.78940027393401,"start_longitude":-79.00851692073047,"end_latitude":35.790341310203075,"end_longitude":-78.96480194292963,"climb_category":0,"city":"Apex","state":"NC","country":"United States","private":false,"hazardous":false,"starred":true,"created_at":"2013-03-09T20:31:02Z","updated_at":"2017-07-23T08:05:38Z","total_elevation_gain":36.2302,"map":{"id":"s3579232","polyline":"wbmyEfjfaN@kLGqM@eEDcCAiKLoQA_B@aEE_BQgBMw@_@mBy@_CoA_DyBcG[q@u@wA}@cCa@oAWi@k@aB]w@uAuEkE}MaAcCw@gCcAsDsAsFa@oCU{DA{CBiDLqGDs@Fs@Js@ZgBb@aBp@qBd@s@vBeCxEcFzCoDx@mAt@yAfByDh@_B\\aBRaBDq@DaB@aEB}CH{C?eB","resource_state":3},"effort_count":24497,"athlete_count":2098,"star_count":16,"athlete_segment_stats":{"pr_elapsed_time":368,"pr_date":"2017-07-13","effort_count":15}}

Thank you,

State field missing

Hello,
could you please add state field in Request access?
I use backend to get and store STRAVA token and it's not a good way to pass state param into RedirectURI cause then SRAVA add one empty state param into redirect url

dependencies aren't downloading in version 1.3.1

When trying to obtain a Token using the example code in version 1.3.1

AuthenticationConfig config = AuthenticationConfig.create()
                                                  .debug()
                                                  .build();
AuthenticationAPI api = new AuthenticationAPI(config);
LoginResult result = api.getTokenForApp(AppCredentials.with(CLIENT_ID, CLIENT_SECRET))
                        .withCode(CODE)
                        .execute();

the retrofit and okhttp dependencies are not found and several classNotFoundExceptions are thrown.

such as:

E/AndroidRuntime: FATAL EXCEPTION: Thread-6
                  Process: bentest.com.strava_upload_andriod, PID: 12124
                  java.lang.NoClassDefFoundError: Failed resolution of: [Lokhttp3/Interceptor;
                      at com.sweetzpot.stravazpot.common.api.AuthenticationConfig$Builder.build(AuthenticationConfig.java:32)
                      at bentest.com.strava_upload_andriod.AuthThread.run(AuthThread.java:21)
                   Caused by: java.lang.ClassNotFoundException: Didn't find class "okhttp3.Interceptor" on path: DexPathList[[zip file "/data/app/bentest.com.strava_upload_andriod-2/base.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_dependencies_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_0_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_1_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_2_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_3_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_4_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_5_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_6_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_7_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_8_apk.apk", zip file "/data/app/bentest.com.strava_upload_andriod-2/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/bentest.com.strava_upload_andriod-2/lib/arm, /system/lib, /vendor/lib]]
                      at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
                      at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
                      at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
                      at com.sweetzpot.stravazpot.common.api.AuthenticationConfig$Builder.build(AuthenticationConfig.java:32) 
                      at bentest.com.strava_upload_andriod.AuthThread.run(AuthThread.java:21) 
E/chromium: [ERROR:gl_context_virtual.cc(39)] Trying to make virtual context current without decoder.

However, this issue doesn't exist in version 1.3

I think this might have something to do with changing compile to 'implementation' instead of 'api'

Support for virtual runs

hi there, it seems virtual runs are not supported (the type is not listed in the model folder of the library, under activitytype). When reading a virtual run, it is reported as virtual ride from the library. Any chance this can be extended? Thank you.

Upload API: com.google.gson.stream.MalformedJsonException

Hi,

I plan to use the upload API for my app and I have made sure the returned token is working correctly by using curl to upload the same GPX file.

I am getting MalformedJsonException with this call to UploadAPI:

UploadStatus uploadStatus = uploadAPI.uploadFile(new File(gpxDir, filename))
                    .withDataType(DataType.GPX)
                    .withActivityType(UploadActivityType.RIDE) 
                    .withName("test gpx")
                    .withDescription("")
                    .isPrivate(false)
                    .hasTrainer(false)
                    .isCommute(false)
                    .withExternalID(filename)
                    .execute();

Exception stack:

java.lang.RuntimeException: An error occurred while executing doInBackground()
    at android.os.AsyncTask$3.done(AsyncTask.java:353)
    at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
    at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
    at java.util.concurrent.FutureTask.run(FutureTask.java:271)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
    at java.lang.Thread.run(Thread.java:764)
Caused by: com.sweetzpot.stravazpot.common.api.exception.StravaAPIException: A network error happened contacting Strava API
    at com.sweetzpot.stravazpot.common.api.StravaAPI.execute(StravaAPI.java:28)
    at com.sweetzpot.stravazpot.upload.request.UploadFileRequest.execute(UploadFileRequest.java:91)
    at idv.markkuo.ambitsync.MoveInfoActivity$StravaUploadAsyncTask.doInBackground(MoveInfoActivity.java:243)
    at idv.markkuo.ambitsync.MoveInfoActivity$StravaUploadAsyncTask.doInBackground(MoveInfoActivity.java:223)
    at android.os.AsyncTask$2.call(AsyncTask.java:333)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    ... 4 more
Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
    at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1559)
    at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1401)
    at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:593)
    at com.google.gson.stream.JsonReader.peek(JsonReader.java:425)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:205)
    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
    at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117)
    at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211)
    at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
    at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:89)
    at com.sweetzpot.stravazpot.common.api.StravaAPI.execute(StravaAPI.java:26)
    ... 9 more

I am new to Retrofit API. I can't seem to enable the logging so I can't check what's returning from strava. Could you give me a hint? Thanks so much.

Crash in an strava account recent created

This is the error log:

12-17 11:43:56.401 31885-32148/com.cajanegra.stam W/System.err: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a name but was NULL at line 1 column 30 path $.biggest_ride_distance
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:89)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at com.sweetzpot.stravazpot.common.api.StravaAPI.execute(StravaAPI.java:26)
12-17 11:43:56.402 31885-32148/com.cajanegra.stam W/System.err:     at com.sweetzpot.stravazpot.athlete.request.GetTotalsAndStatsRequest.execute(GetTotalsAndStatsRequest.java:23)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at com.cajanegra.stam.PerfilActivity$5$1$1.doInBackground(PerfilActivity.java:477)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at com.cajanegra.stam.PerfilActivity$5$1$1.doInBackground(PerfilActivity.java:468)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at android.os.AsyncTask$2.call(AsyncTask.java:295)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at java.lang.Thread.run(Thread.java:818)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err: Caused by: java.lang.IllegalStateException: Expected a name but was NULL at line 1 column 30 path $.biggest_ride_distance
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at com.google.gson.stream.JsonReader.nextName(JsonReader.java:789)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:215)
12-17 11:43:56.403 31885-32148/com.cajanegra.stam W/System.err: 	... 16 more

This is the json that the lib is trying to parse

{
"biggest_ride_distance": null,
"biggest_climb_elevation_gain": null,
"recent_ride_totals": {
"count": 0,
"distance": 0,
"moving_time": 0,
"elapsed_time": 0,
"elevation_gain": 0,
"achievement_count": 0
},
"recent_run_totals": {
"count": 1,
"distance": 7366.60009765625,
"moving_time": 2788,
"elapsed_time": 2828,
"elevation_gain": 52.69230651855469,
"achievement_count": 0
},
"recent_swim_totals": {
"count": 0,
"distance": 0,
"moving_time": 0,
"elapsed_time": 0,
"elevation_gain": 0,
"achievement_count": 0
},
"ytd_ride_totals": {
"count": 0,
"distance": 0,
"moving_time": 0,
"elapsed_time": 0,
"elevation_gain": 0
},
"ytd_run_totals": {
"count": 1,
"distance": 7367,
"moving_time": 2788,
"elapsed_time": 2828,
"elevation_gain": 53
},
"ytd_swim_totals": {
"count": 0,
"distance": 0,
"moving_time": 0,
"elapsed_time": 0,
"elevation_gain": 0
},
"all_ride_totals": {
"count": 0,
"distance": 0,
"moving_time": 0,
"elapsed_time": 0,
"elevation_gain": 0
},
"all_run_totals": {
"count": 1,
"distance": 7367,
"moving_time": 2788,
"elapsed_time": 2828,
"elevation_gain": 53
},
"all_swim_totals": {
"count": 0,
"distance": 0,
"moving_time": 0,
"elapsed_time": 0,
"elevation_gain": 0
}
}

Please fix it. I know It is just a validation.

Issues with Getting Segments

I am trying to access the Segments part of the API, specifically the exploreSegementsInRegion, and I believe I have it set up correctly, but I am getting an error as such:

java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

As far as I know, it is not an issue with my code. Really all I have so far is what is shown in the Readme(which is well done by the way).

I know that I get back a response because I can see it in Android Monitor, but I think when it is trying to convert it to the List it is throwing an error.

The boyd of my function looks like this

      try {
            AuthenticationConfig config = AuthenticationConfig.create()
                    .debug()
                    .build();
            AuthenticationAPI api = new AuthenticationAPI(config);
            TOKEN = api.getTokenForApp(AppCredentials.with(Integer.parseInt(getString(R.string.clientID)), getString(R.string.clientSecret)))
                    .withCode(CODE)
                    .execute();

            CONFIG = StravaConfig.withToken(TOKEN.getToken())
                    .debug()
                    .build();

            SegmentAPI segmentAPI = new SegmentAPI(CONFIG);
            segments = segmentAPI.exploreSegmentsInRegion(Bounds.with(Coordinates.at(SW_LAT, SW_LONG), Coordinates.at(NE_LAT, NE_LONG)))
                    .forActivityType(ExploreType.RIDING)
                    .execute();
            System.out.println("here");

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

That code is in the doInBackground section of an AsyncTask, but I again I believe that is all correct to my knowledge.

The first three lines of the traceback are:

at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)

Let me know if you need anymore information and thank you.

Activity - Temperature

When downloading activities generated via a Garmin, i.e. fully populated, the Temperature field of Activity causes an error in JSON - OBJECT expected, number received.

I tried fully populating the Temperature class which seems to be a stub using Distance as a model, but the error still occurs.

Then spotted the type adapters - Temperature missing.

Fixes:

Temperature.java

public class Temperature {
private float celsiusDegrees;
public static Temperature celsiusDegrees(float celsiusDegrees) {
return new Temperature(celsiusDegrees);
}
public Temperature(float celsiusDegrees) {
this.celsiusDegrees = celsiusDegrees;
}

public float getCelsiusDegrees() {
    return celsiusDegrees;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Temperature temperature = (Temperature) o;

    return Float.compare(temperature.celsiusDegrees, celsiusDegrees) == 0;

}

@Override
public int hashCode() {
    return (celsiusDegrees != +0.0f ? Float.floatToIntBits(celsiusDegrees) : 0);
}

@Override
public String toString() {
    return String.valueOf(celsiusDegrees);
}

}

TemperatureTypeAdapter

public class TemperatureTypeAdapter extends TypeAdapter {

@Override
public void write(JsonWriter out, Temperature value) throws IOException {
    out.value(value.getCelsiusDegrees());
}

@Override
public Temperature read(JsonReader in) throws IOException {
    if(!in.peek().equals(JsonToken.NULL)) {
        return new Temperature((float) (in.nextDouble()));
    } else {
        return null;
    }
}

}

and register the typeadapter in config.java

Issue sending TXC file

I have an issue sending TCX file.
At the end of transmission I have following error:
java.lang.NumberFormatException: Expected an int but was 2179799777 at line 1 column 17 path $.id

Debugging I found that happens when Strava anwer to upload with code 201 "Your activity is still being processed." that is considered error and there is an exception during parsing the answer.

Looking at:
https://developers.strava.com/docs/uploads/
this code should interpreted as success?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.