Skip to content

Commit b9ee005

Browse files
author
Patrick FINKELSTEIN
committed
Upload Activity added
1 parent af12298 commit b9ee005

7 files changed

Lines changed: 144 additions & 84 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ Thanks to Joe Birch, I used his code to better understand Oauth process
4949
https://github.com/hitherejoe/FlutterOAuth
5050

5151
And Javier for https://javiercbk.github.io/json_to_dart/
52+
53+
54+
License:
55+
strava-flutter is provided under a MIT License. Copyright (c) 2019 Patrick FINKELSTEIN

example/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ A few resources to get you started if this is your first Flutter project:
1414
For help getting started with Flutter, view our
1515
[online documentation](https://flutter.io/docs), which offers tutorials,
1616
samples, guidance on mobile development, and a full API reference.
17+

example/lib/main.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import 'package:strava_flutter/Models/summaryAthlete.dart';
1313
import 'package:strava_flutter/Models/summaryActivity.dart';
1414

1515

16-
1716
Strava strava;
1817

1918
void main() => runApp(MyApp());

lib/API/Oauth.dart

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,15 @@ import 'dart:convert';
88
import 'constants.dart';
99
import 'token.dart';
1010

11-
1211
import 'package:url_launcher/url_launcher.dart';
1312
import 'package:shared_preferences/shared_preferences.dart';
1413

15-
16-
1714
//===========================================
1815
// Code related to Authorization processs
1916
//===========================================
2017

2118
abstract class Auth {
22-
23-
24-
StreamController<String> onCodeReceived = StreamController();
19+
StreamController<String> onCodeReceived = StreamController();
2520

2621
// Save the token and the expiry date
2722
void _saveToken(String token, int expire, String scope) async {
@@ -41,7 +36,6 @@ StreamController<String> onCodeReceived = StreamController();
4136
localToken.accessToken = prefs.getString('token').toString();
4237
localToken.expiresAt = prefs.getInt('expire');
4338
localToken.scope = prefs.getString('scope');
44-
4539
} catch (error) {
4640
print('Error getting the key');
4741
localToken.accessToken = null;
@@ -61,14 +55,11 @@ StreamController<String> onCodeReceived = StreamController();
6155
return (localToken);
6256
}
6357

64-
65-
6658
Map<String, String> createHeader(String token) {
67-
return {'Authorization': 'Bearer $token'};
68-
59+
return {'Authorization': 'Bearer $token'};
6960
}
7061

71-
// Get the code from Strava server
62+
// Get the code from Strava server
7263
Future<void> _getStravaCode(
7364
String clientID, String redirectUrl, String scope) async {
7465
print('Welcome to getStravaCode');
@@ -114,7 +105,8 @@ StreamController<String> onCodeReceived = StreamController();
114105
print('End of getStravaCode');
115106
}
116107

117-
Future<Token> _getStravaToken(String clientID, String secret, String code) async {
108+
Future<Token> _getStravaToken(
109+
String clientID, String secret, String code) async {
118110
Token _answer = Token();
119111

120112
print('---> Entering getStravaToken!!');
@@ -151,7 +143,7 @@ StreamController<String> onCodeReceived = StreamController();
151143
_answer.refreshToken = refreshToken;
152144
_answer.expiresAt = expiresAt;
153145
}
154-
146+
155147
return (_answer);
156148
// });
157149
}
@@ -162,50 +154,64 @@ StreamController<String> onCodeReceived = StreamController();
162154
return (_expiryDate.isBefore(DateTime.now()));
163155
}
164156

165-
Future<bool> OAuth(String clientID, String redirectUrl, String scope, String secret) async {
157+
Future<bool> OAuth(
158+
String clientID, String redirectUrl, String scope, String secret) async {
166159
bool isExpired = true;
167-
bool returnValue = false;
160+
bool isAuthOK = false;
168161

169162
final Token tokenStored = await getStoredToken();
170163
final String _token = tokenStored.accessToken;
171164

165+
172166
// Check if the token is not expired
173167
if (_token != "null") {
174168
print('----> token has been stored before! ${tokenStored.accessToken}');
175169

176170
isExpired = _isTokenExpired(tokenStored);
177171
}
178172

179-
// Check if access token has been stored previously or expired
180-
if ((_token == "null") || (isExpired)) {
173+
// Check if the scope has changed
174+
if ((tokenStored.scope != scope) || (_token == "null") || isExpired) {
175+
// Ask for a new authorization
176+
isAuthOK = await newAuthorization(clientID, redirectUrl, secret, scope);
177+
}
181178

182-
// todo: if accessToken is expired use the refresh token
183-
// to get a new access code
184-
await _getStravaCode(clientID, redirectUrl, scope);
179+
if (tokenStored.scope == scope) {
180+
isAuthOK = true;
181+
}
185182

186-
onCodeReceived.stream.listen((stravaCode) async {
187-
if (stravaCode != null) {
188-
var answer = await _getStravaToken(clientID, secret, stravaCode);
183+
return isAuthOK;
189184

190-
print('---> answer ${answer.expiresAt} , ${answer.accessToken}');
185+
}
191186

192-
// Save the token information
193-
if (answer.accessToken != null && answer.expiresAt != null) {
194-
_saveToken(answer.accessToken, answer.expiresAt, scope);
195-
}
187+
Future<bool> newAuthorization(
188+
String clientID, String redirectUrl, String secret, String scope) async {
189+
// todo: if accessToken is expired use the refresh token
190+
// to get a new access code
196191

197-
returnValue = true;
198-
} else {
199-
print('----> code is still null');
200-
returnValue = false;
192+
bool returnValue = false;
193+
194+
await _getStravaCode(clientID, redirectUrl, scope);
195+
196+
onCodeReceived.stream.listen((stravaCode) async {
197+
if (stravaCode != null) {
198+
var answer = await _getStravaToken(clientID, secret, stravaCode);
199+
200+
print('---> answer ${answer.expiresAt} , ${answer.accessToken}');
201+
202+
// Save the token information
203+
if (answer.accessToken != null && answer.expiresAt != null) {
204+
_saveToken(answer.accessToken, answer.expiresAt, scope);
201205
}
202-
});
203-
} else {
204-
205-
returnValue = true;
206-
}
207206

208-
return (returnValue);
207+
returnValue = true;
208+
} else {
209+
print('----> code is still null');
210+
returnValue = false;
211+
}
212+
});
213+
214+
return returnValue;
209215
}
210216

211217
Future<void> deAuthorize() async {
@@ -226,7 +232,4 @@ StreamController<String> onCodeReceived = StreamController();
226232
}
227233
}
228234
}
229-
230-
231-
232-
}
235+
}

lib/API/constants.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,14 @@ enum ErrorCode {
1010

1111

1212
final tokenEndpoint = "https://www.strava.com/oauth/token";
13-
final authorizationEndpoint = "https://www.strava.com/oauth/authorize";
13+
final authorizationEndpoint = "https://www.strava.com/oauth/authorize";
14+
15+
16+
class Fault {
17+
int statusCode;
18+
String message;
19+
20+
Fault(this.statusCode, this.message);
21+
}
22+
23+

lib/API/upload.dart

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,25 @@ import 'package:http/http.dart' as http;
1212
import 'package:path_provider/path_provider.dart';
1313

1414

15+
import 'constants.dart';
16+
import '../Models/uploadActivity.dart';
1517
import 'strava.dart'; // To remove when removing test1 and test2
1618

19+
// To check if the activity has been uploaded successfully
20+
// No numeric error code for the moment given by Strava
21+
class StatusUpload {
22+
String ready = "Your activity is ready.";
23+
String deleted = "The created activity has been deleted.";
24+
String error = "There was an error processing your activity.";
25+
String processed = "Your activity is still being processed.";
26+
String notFound = 'Not Found';
27+
28+
} // End of enum statusUpload
29+
30+
1731

1832
abstract class Upload {
19-
// Upload();
33+
2034

2135
Future<String> get _localPath async {
2236
final directory = await getApplicationDocumentsDirectory();
@@ -51,10 +65,12 @@ abstract class Upload {
5165
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
5266
}
5367

54-
void uploadActivity(String name, String description, String fileUrl,
68+
Future<Fault> uploadActivity(String name, String description, String fileUrl,
5569
String fileType, String accessToken) async {
5670
final postUri = Uri.parse('https://www.strava.com/api/v3/uploads');
5771

72+
var fault = Fault(888, '');
73+
5874
var request = http.MultipartRequest("POST", postUri);
5975
request.fields['data_type'] = fileType; // tested with gpx
6076
request.fields['trainer'] = 'false';
@@ -68,38 +84,60 @@ abstract class Upload {
6884

6985
request.files.add(await http.MultipartFile.fromPath('file', fileUrl));
7086

71-
// var response = await request.send();
87+
String _body = '';
7288

73-
// Not working
74-
// var response = await request.send();
75-
// print('Response: ${response.statusCode} ${response.reasonPhrase}');
76-
// String _body = await response.stream.transform(utf8.decoder).join();
89+
var response = await request.send();
7790

78-
// Working
79-
String _body = '';
80-
request.send().then((response) {
91+
// request.send().then((response) {
8192
print('Response: ${response.statusCode} ${response.reasonPhrase}');
93+
94+
fault.statusCode =response.statusCode;
95+
fault.message =response.reasonPhrase;
96+
8297
if (response.statusCode == 201) {
8398
print('Created!!!');
99+
} else {
100+
print('Error while uploading the activity');
101+
print('---> ${response.reasonPhrase}');
102+
84103
}
85104

86-
// if statusCode == 400 and "Bad Request" Most probbaly the activity has been alreacdy created
105+
int idUpload;
106+
107+
// Upload is processed by the server
108+
if (fault.statusCode == 201) {
87109

88110
response.stream.transform(utf8.decoder).listen((value) {
89111
print(value);
90-
_body = _body + value;
112+
var _body = json.decode(value);
113+
ResponseUploadActivity _response = ResponseUploadActivity.fromJson(_body);
114+
115+
print('id ${_response.id}');
116+
idUpload =_response.id;
117+
91118
});
92-
});
93119

94-
// Not working yet
95-
/****
96-
final jsonResponse = json.decode(_body);
97-
final UploadActivity _uploadActivity =
98-
UploadActivity.fromJson(jsonResponse);
99-
var _activityID = _uploadActivity.id;
100-
print('activity $_activityID');
101-
***/
120+
121+
// Todo: Every second or two check if upload has been susccesful
122+
123+
final reqCheckUpgrade = 'https://www.strava.com/api/v3/uploads/' + idUpload.toString();
124+
125+
var resp = await http.get(reqCheckUpgrade, headers: header);
126+
print('check status ${resp.reasonPhrase}');
127+
128+
129+
130+
}
131+
132+
return fault;
133+
134+
102135
}
136+
137+
138+
139+
140+
//// test2()
103141
/// This is a test function
104142
/// to debug Strava upload activity
105143
void test2(String secret) async {
@@ -108,9 +146,6 @@ abstract class Upload {
108146
"32212", secret, "http://localhost:8080", 'auto', 'activity:write');
109147

110148
await strava.OAuth("32212", "http://localhost:8080", 'activity:write', secret);
111-
// var token = Token();
112-
// var _token = await token.getStoredToken();
113-
114149
var _token = await strava.getStoredToken();
115150

116151
// Use the asset file to test without having to create internally a ride
@@ -133,8 +168,6 @@ abstract class Upload {
133168
var text = await readFile();
134169
print('text ?? $text');
135170

136-
// var currentDir = Directory.current;
137-
138171
String dir = (await getApplicationDocumentsDirectory()).path;
139172

140173
var newFile = File('$dir/tototo.txt');
@@ -153,8 +186,6 @@ abstract class Upload {
153186
var newAct = File('$dir/myActivity.gpx');
154187
print(newAct.lengthSync());
155188

156-
// Will not work for asset file
157-
// var fileActivity = File('assets/test.JPG');
158189

159190
final strava = Strava(
160191
"32212", secret, "http://localhost:8080", 'auto', 'activity:write');
@@ -189,17 +220,6 @@ abstract class Upload {
189220
}
190221
});
191222

192-
/****
193-
var documentsDir = await getApplicationDocumentsDirectory();
194-
print('documents directory path ${documentsDir.path}');
195-
196223

197-
198-
documentsDir
199-
.list(recursive: false, followLinks: false)
200-
.listen((FileSystemEntity entity) {
201-
print(entity.path);
202-
});
203-
****/
204224
}
205225
}

lib/Models/uploadActivity.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,26 @@ class UploadActivity {
2828
return data;
2929
}
3030
}
31+
32+
33+
class ResponseUploadActivity {
34+
int id;
35+
String externalId;
36+
String error;
37+
String status;
38+
int activityId;
39+
40+
41+
ResponseUploadActivity(this.id, this.externalId, this.error, this.status, this.activityId);
42+
43+
ResponseUploadActivity.fromJson(Map<String, dynamic> json) {
44+
id = json['id'];
45+
externalId = json['external_id'];
46+
error = json['error'];
47+
status = json['status'];
48+
activityId = json['activity_id'];
49+
}
50+
51+
52+
53+
}

0 commit comments

Comments
 (0)