StravaZpot


Source link: https://github.com/SweetzpotAS/StravaZpot-Android

StravaZpot

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.

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.2'

License

Copyright 2016 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. 

Resources

NoseyExplorer is a simple explorer that lets you see the data that Realm.io has saved for your models.

Simply register your models and your ready to start browsing your data from within your app.

Simplify getting user's location for Android.

A helper class to make any view rotatable.

Track activities, getCurrentActivity, check whether application is running or not, finish all activities, and so on.

Parallax effect on every item of your RecyclerView.

ProgressedView provides you to show progress when user clicks to any view, and then you're done with the task reverse it back to view again. This view extends RelativeLayout, so it is easy to implement in xml or by code.

Topics


2D Engines   3D Engines   9-Patch   Action Bars   Activities   ADB   Advertisements   Analytics   Animations   ANR   AOP   API   APK   APT   Architecture   Audio   Autocomplete   Background Processing   Backward Compatibility   Badges   Bar Codes   Benchmarking   Bitmaps   Bluetooth   Blur Effects   Bread Crumbs   BRMS   Browser Extensions   Build Systems   Bundles   Buttons   Caching   Camera   Canvas   Cards   Carousels   Changelog   Checkboxes   Cloud Storages   Color Analysis   Color Pickers   Colors   Comet/Push   Compass Sensors   Conferences   Content Providers   Continuous Integration   Crash Reports   Credit Cards   Credits   CSV   Curl/Flip   Data Binding   Data Generators   Data Structures   Database   Database Browsers   Date &   Debugging   Decompilers   Deep Links   Dependency Injections   Design   Design Patterns   Dex   Dialogs   Distributed Computing   Distribution Platforms   Download Managers   Drawables   Emoji   Emulators   EPUB   Equalizers &   Event Buses   Exception Handling   Face Recognition   Feedback &   File System   File/Directory   Fingerprint   Floating Action   Fonts   Forms   Fragments   FRP   FSM   Functional Programming   Gamepads   Games   Geocaching   Gestures   GIF   Glow Pad   Gradle Plugins   Graphics   Grid Views   Highlighting   HTML   HTTP Mocking   Icons   IDE   IDE Plugins   Image Croppers   Image Loaders   Image Pickers   Image Processing   Image Views   Instrumentation   Intents   Job Schedulers   JSON   Keyboard   Kotlin   Layouts   Library Demos   List View   List Views   Localization   Location   Lock Patterns   Logcat   Logging   Mails   Maps   Markdown   Mathematics   Maven Plugins   MBaaS   Media   Menus   Messaging   MIME   Mobile Web   Native Image   Navigation   NDK   Networking   NFC   NoSQL   Number Pickers   OAuth   Object Mocking   OCR Engines   OpenGL   ORM   Other Pickers   Parallax List   Parcelables   Particle Systems   Password Inputs   PDF   Permissions   Physics Engines   Platforms   Plugin Frameworks   Preferences   Progress Indicators   ProGuard   Properties   Protocol Buffer   Pull To   Purchases   Push/Pull   QR Codes   Quick Return   Radio Buttons   Range Bars   Ratings   Recycler Views   Resources   REST   Ripple Effects   RSS   Screenshots   Scripting   Scroll Views   SDK   Search Inputs   Security   Sensors   Services   Showcase Views   Signatures   Sliding Panels   Snackbars   SOAP   Social Networks   Spannable   Spinners   Splash Screens   SSH   Static Analysis   Status Bars   Styling   SVG   System   Tags   Task Managers   TDD &   Template Engines   Testing   Testing Tools   Text Formatting   Text Views   Text Watchers   Text-to   Toasts   Toolkits For   Tools   Tooltips   Trainings   TV   Twitter   Updaters   USB   User Stories   Utils   Validation   Video   View Adapters   View Pagers   Views   Watch Face   Wearable Data   Wearables   Weather   Web Tools   Web Views   WebRTC   WebSockets   Wheel Widgets   Wi-Fi   Widgets   Windows   Wizards   XML   XMPP   YAML   ZIP Codes