mirror of
https://github.com/russok/FitoTrack.git
synced 2025-10-28 00:02:11 -07:00
Merge branch 'feature-upload-osm' into develop
This commit is contained in:
commit
05d43de1a7
20
NOTICE.md
20
NOTICE.md
@ -24,6 +24,7 @@
|
||||
Current owners and lead developers are: Ludwig Brinckmann, devemux86
|
||||
|
||||
<https://github.com/PhilJay/MPAndroidChart>
|
||||
|
||||
Copyright 2019 Philipp Jahoda
|
||||
|
||||
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
|
||||
@ -33,6 +34,7 @@
|
||||
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.
|
||||
|
||||
<https://github.com/Clans/FloatingActionButton>
|
||||
|
||||
Copyright 2015 Dmytro Tarianyk
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -47,6 +49,24 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
<https://github.com/westnordost/osmapi>
|
||||
|
||||
© 2016-2019 Tobias Zwick. This library is released under the terms of the GNU Lesser General Public License (LGPL).
|
||||
|
||||
<https://github.com/mttkay/signpost>
|
||||
|
||||
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.
|
||||
|
||||
<https://github.com/FasterXML/jackson-core>
|
||||
|
||||
This copy of Jackson JSON processor streaming parser/generator is licensed under the
|
||||
|
||||
@ -67,15 +67,24 @@ dependencies {
|
||||
implementation 'org.mapsforge:mapsforge-map-android:0.11.0'
|
||||
implementation 'com.caverock:androidsvg:1.3'
|
||||
|
||||
// Charts
|
||||
implementation 'net.sf.kxml:kxml2:2.3.0'
|
||||
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||
implementation 'com.github.clans:fab:1.6.4'
|
||||
|
||||
// XML
|
||||
implementation 'stax:stax-api:1.0.1'
|
||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
|
||||
|
||||
// Android Room
|
||||
def room_version = "2.2.0-beta01"
|
||||
// Upload to OSM
|
||||
implementation ('de.westnordost:osmapi-traces:1.0')
|
||||
configurations {
|
||||
compile.exclude group: 'net.sf.kxml', module: 'kxml2' // already included in Android
|
||||
}
|
||||
implementation 'oauth.signpost:signpost-commonshttp4:1.2.1.2'
|
||||
|
||||
// Android Room Database
|
||||
def room_version = "2.2.0"
|
||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
package de.tadris.fitness.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
@ -28,21 +30,31 @@ import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import de.tadris.fitness.Instance;
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.tadris.fitness.osm.OAuthAuthentication;
|
||||
import de.tadris.fitness.osm.OsmTraceUploader;
|
||||
import de.tadris.fitness.util.DialogUtils;
|
||||
import de.tadris.fitness.util.gpx.GpxExporter;
|
||||
import de.tadris.fitness.util.unit.UnitUtils;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
import de.westnordost.osmapi.traces.GpsTraceDetails;
|
||||
import oauth.signpost.OAuthConsumer;
|
||||
|
||||
public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.WorkoutDeleter {
|
||||
|
||||
@ -211,7 +223,55 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
}).start();
|
||||
}
|
||||
|
||||
private OAuthConsumer oAuthConsumer= null;
|
||||
private void prepareUpload(){
|
||||
OAuthAuthentication authentication= new OAuthAuthentication(mHandler, this, new OAuthAuthentication.OAuthAuthenticationListener() {
|
||||
@Override
|
||||
public void authenticationFailed() {
|
||||
new AlertDialog.Builder(ShowWorkoutActivity.this)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.authenticationFailed)
|
||||
.setPositiveButton(R.string.okay, null)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticationComplete(OAuthConsumer consumer) {
|
||||
oAuthConsumer= consumer;
|
||||
showUploadOptions();
|
||||
}
|
||||
});
|
||||
|
||||
authentication.authenticateIfNecessary();
|
||||
}
|
||||
|
||||
AlertDialog dialog= null;
|
||||
private void showUploadOptions(){
|
||||
dialog= new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.actionUploadToOSM)
|
||||
.setView(R.layout.dialog_upload_osm)
|
||||
.setPositiveButton(R.string.upload, (dialogInterface, i) -> {
|
||||
CheckBox checkBox= dialog.findViewById(R.id.uploadCutting);
|
||||
Spinner spinner= dialog.findViewById(R.id.uploadVisibility);
|
||||
EditText descriptionEdit= dialog.findViewById(R.id.uploadDescription);
|
||||
String description= descriptionEdit.getText().toString().trim();
|
||||
GpsTraceDetails.Visibility visibility;
|
||||
switch (spinner.getSelectedItemPosition()){
|
||||
case 0: visibility= GpsTraceDetails.Visibility.IDENTIFIABLE; break;
|
||||
default:
|
||||
case 1: visibility= GpsTraceDetails.Visibility.TRACKABLE; break;
|
||||
case 2: visibility= GpsTraceDetails.Visibility.PRIVATE; break;
|
||||
}
|
||||
uploadToOsm(checkBox.isChecked(), visibility, description);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void uploadToOsm(boolean cut, GpsTraceDetails.Visibility visibility, String description){
|
||||
List<WorkoutSample> samples = new ArrayList<>(this.samples);
|
||||
new OsmTraceUploader(this, mHandler, workout, samples, visibility, oAuthConsumer, cut, description).upload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
@ -223,6 +283,9 @@ public class ShowWorkoutActivity extends WorkoutActivity implements DialogUtils.
|
||||
case R.id.actionExportGpx:
|
||||
exportToGpx();
|
||||
return true;
|
||||
case R.id.actionUploadOSM:
|
||||
prepareUpload();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@ -80,9 +80,13 @@ public class Workout{
|
||||
if(comment.length() > 2){
|
||||
return comment;
|
||||
}else{
|
||||
return SimpleDateFormat.getDateTimeInstance().format(new Date(start));
|
||||
return getDateString();
|
||||
}
|
||||
}
|
||||
|
||||
public String getDateString(){
|
||||
return SimpleDateFormat.getDateTimeInstance().format(new Date(start));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
29
app/src/main/java/de/tadris/fitness/osm/GpsTraceLatLong.java
Normal file
29
app/src/main/java/de/tadris/fitness/osm/GpsTraceLatLong.java
Normal file
@ -0,0 +1,29 @@
|
||||
package de.tadris.fitness.osm;
|
||||
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.westnordost.osmapi.map.data.LatLon;
|
||||
|
||||
public class GpsTraceLatLong implements LatLon {
|
||||
|
||||
private final double latitude;
|
||||
private final double longitude;
|
||||
|
||||
public GpsTraceLatLong(double latitude, double longitude) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public GpsTraceLatLong(WorkoutSample sample) {
|
||||
this(sample.lat, sample.lon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
}
|
||||
132
app/src/main/java/de/tadris/fitness/osm/OAuthAuthentication.java
Normal file
132
app/src/main/java/de/tadris/fitness/osm/OAuthAuthentication.java
Normal file
@ -0,0 +1,132 @@
|
||||
package de.tadris.fitness.osm;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.provider.Browser;
|
||||
import android.text.InputType;
|
||||
import android.widget.EditText;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
import oauth.signpost.OAuth;
|
||||
import oauth.signpost.OAuthConsumer;
|
||||
import oauth.signpost.OAuthProvider;
|
||||
import oauth.signpost.exception.OAuthException;
|
||||
|
||||
public class OAuthAuthentication {
|
||||
|
||||
private OAuthConsumer oAuthConsumer= OAuthUrlProvider.getDefaultConsumer();
|
||||
private OAuthProvider oAuthProvider= OAuthUrlProvider.getDefaultProvider();
|
||||
|
||||
private Handler handler;
|
||||
private Activity activity;
|
||||
private ProgressDialogController dialogController;
|
||||
private SharedPreferences preferences;
|
||||
private OAuthAuthenticationListener listener;
|
||||
|
||||
public OAuthAuthentication(Handler handler, Activity activity, OAuthAuthenticationListener listener) {
|
||||
this.handler = handler;
|
||||
this.activity = activity;
|
||||
dialogController= new ProgressDialogController(activity, activity.getString(R.string.uploading));
|
||||
this.preferences= activity.getSharedPreferences("osm_oauth", Context.MODE_PRIVATE);
|
||||
this.listener= listener;
|
||||
}
|
||||
|
||||
public void authenticateIfNecessary(){
|
||||
if(isAuthenticated()){
|
||||
loadAccessToken();
|
||||
listener.authenticationComplete(oAuthConsumer);
|
||||
}else{
|
||||
retrieveRequestToken();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAuthenticated(){
|
||||
return preferences.getBoolean("authenticated", false);
|
||||
}
|
||||
|
||||
private void retrieveRequestToken(){
|
||||
dialogController.show();
|
||||
dialogController.setIndeterminate(true);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
String authUrl = oAuthProvider.retrieveRequestToken(oAuthConsumer, OAuth.OUT_OF_BAND);
|
||||
handler.post(() -> {
|
||||
Intent intent= new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl));
|
||||
intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
activity.startActivity(intent);
|
||||
showEnterVerificationCodeDialog();
|
||||
dialogController.cancel();
|
||||
});
|
||||
} catch (OAuthException e) {
|
||||
e.printStackTrace();
|
||||
handler.post(() -> {
|
||||
dialogController.cancel();
|
||||
listener.authenticationFailed();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void showEnterVerificationCodeDialog(){
|
||||
EditText editText= new EditText(activity);
|
||||
editText.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
editText.setSingleLine(true);
|
||||
|
||||
AlertDialog dialog= new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.enterVerificationCode).setView(editText).setPositiveButton(R.string.okay, (dialogInterface, i) -> {
|
||||
new Thread(() -> retrieveAccessToken(editText.getText().toString().trim())).start();
|
||||
dialogInterface.cancel();
|
||||
}).setNegativeButton(R.string.cancel, null).setCancelable(false).create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void loadAccessToken(){
|
||||
oAuthConsumer.setTokenWithSecret(preferences.getString("accessToken", ""),
|
||||
preferences.getString("tokenSecret", ""));
|
||||
}
|
||||
|
||||
private void saveAccessToken(){
|
||||
preferences.edit()
|
||||
.putString("accessToken", oAuthConsumer.getToken())
|
||||
.putString("tokenSecret", oAuthConsumer.getTokenSecret())
|
||||
.putBoolean("authenticated", true).apply();
|
||||
}
|
||||
|
||||
public void clearAccessToken(){
|
||||
preferences.edit().putBoolean("authenticated", false).apply();
|
||||
}
|
||||
|
||||
private void retrieveAccessToken(String verificationCode){
|
||||
handler.post(() -> dialogController.show());
|
||||
try{
|
||||
oAuthProvider.retrieveAccessToken(oAuthConsumer, verificationCode);
|
||||
handler.post(() -> {
|
||||
dialogController.cancel();
|
||||
saveAccessToken();
|
||||
listener.authenticationComplete(oAuthConsumer);
|
||||
});
|
||||
}catch (OAuthException e){
|
||||
handler.post(() -> {
|
||||
dialogController.cancel();
|
||||
listener.authenticationFailed();
|
||||
});
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public interface OAuthAuthenticationListener{
|
||||
|
||||
void authenticationFailed();
|
||||
|
||||
void authenticationComplete(OAuthConsumer consumer);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package de.tadris.fitness.osm;
|
||||
|
||||
import oauth.signpost.OAuthConsumer;
|
||||
import oauth.signpost.OAuthProvider;
|
||||
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
|
||||
import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;
|
||||
|
||||
public class OAuthUrlProvider {
|
||||
|
||||
static private final String CONSUMER_KEY= "jFL9grFmAo5ZS720YDDRXdSOb7F0IZQf9lnY1PHq";
|
||||
static private final String CONSUMER_SECRET= "oH969vYW60fZLco6E09UQl3uFXqjl4siQbOL0q9q";
|
||||
|
||||
static OAuthConsumer getDefaultConsumer(){
|
||||
return new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
|
||||
}
|
||||
|
||||
static OAuthProvider getDefaultProvider(){
|
||||
return new CommonsHttpOAuthProvider(URL_TOKEN_REQUEST, URL_TOKEN_ACCESS, URL_AUTHORIZE);
|
||||
}
|
||||
|
||||
static private final String URL_TOKEN_REQUEST= "https://www.openstreetmap.org/oauth/request_token";
|
||||
static private final String URL_TOKEN_ACCESS= "https://www.openstreetmap.org/oauth/access_token";
|
||||
static private final String URL_AUTHORIZE= "https://www.openstreetmap.org/oauth/authorize";
|
||||
|
||||
|
||||
}
|
||||
116
app/src/main/java/de/tadris/fitness/osm/OsmTraceUploader.java
Normal file
116
app/src/main/java/de/tadris/fitness/osm/OsmTraceUploader.java
Normal file
@ -0,0 +1,116 @@
|
||||
package de.tadris.fitness.osm;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import de.tadris.fitness.R;
|
||||
import de.tadris.fitness.data.Workout;
|
||||
import de.tadris.fitness.data.WorkoutSample;
|
||||
import de.tadris.fitness.view.ProgressDialogController;
|
||||
import de.westnordost.osmapi.OsmConnection;
|
||||
import de.westnordost.osmapi.traces.GpsTraceDetails;
|
||||
import de.westnordost.osmapi.traces.GpsTracesDao;
|
||||
import de.westnordost.osmapi.traces.GpsTrackpoint;
|
||||
import oauth.signpost.OAuthConsumer;
|
||||
|
||||
public class OsmTraceUploader {
|
||||
|
||||
private static final int CUT_DISTANCE= 300;
|
||||
|
||||
private Activity activity;
|
||||
private Handler handler;
|
||||
private Workout workout;
|
||||
private List<WorkoutSample> samples;
|
||||
private GpsTraceDetails.Visibility visibility;
|
||||
private OAuthConsumer consumer;
|
||||
private boolean cut;
|
||||
private ProgressDialogController dialogController;
|
||||
private String description;
|
||||
|
||||
public OsmTraceUploader(Activity activity, Handler handler, Workout workout, List<WorkoutSample> samples, GpsTraceDetails.Visibility visibility, OAuthConsumer consumer, boolean cut, String description) {
|
||||
this.activity = activity;
|
||||
this.handler = handler;
|
||||
this.workout = workout;
|
||||
this.samples = samples;
|
||||
this.visibility = visibility;
|
||||
this.consumer = consumer;
|
||||
this.cut = cut;
|
||||
this.description= description;
|
||||
this.dialogController= new ProgressDialogController(activity, activity.getString(R.string.uploading));
|
||||
}
|
||||
|
||||
private void cut(){
|
||||
cut(false);
|
||||
cut(true);
|
||||
}
|
||||
|
||||
private void cut(boolean last){
|
||||
double distance= 0;
|
||||
int count= 0;
|
||||
WorkoutSample lastSample= samples.remove(last ? samples.size()-1 : 0);
|
||||
while(distance < CUT_DISTANCE){
|
||||
WorkoutSample currentSample= samples.remove(last ? samples.size()-1 : 0);
|
||||
distance+= lastSample.toLatLong().sphericalDistance(currentSample.toLatLong());
|
||||
count++;
|
||||
lastSample= currentSample;
|
||||
}
|
||||
Log.d("Uploader", "Cutted " + (last ? "last" : "first") + " " + count + " Samples (" + distance + " meters)");
|
||||
}
|
||||
|
||||
public void upload(){
|
||||
new Thread(() -> {
|
||||
try{
|
||||
executeTask();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
handler.post(() -> {
|
||||
Toast.makeText(activity, R.string.uploadFailed, Toast.LENGTH_LONG).show();
|
||||
dialogController.cancel();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void executeTask(){
|
||||
handler.post(() -> dialogController.show());
|
||||
setProgress(0);
|
||||
if(cut){ cut(); }
|
||||
setProgress(20);
|
||||
OsmConnection osm = new OsmConnection(
|
||||
"https://api.openstreetmap.org/api/0.6/", "FitoTrack", consumer);
|
||||
|
||||
List<GpsTrackpoint> trackpoints= new ArrayList<>();
|
||||
|
||||
for(WorkoutSample sample : samples){
|
||||
GpsTrackpoint trackpoint= new GpsTrackpoint(new GpsTraceLatLong(sample));
|
||||
trackpoint.time= new Date(sample.absoluteTime);
|
||||
trackpoint.elevation= (float)sample.elevation;
|
||||
trackpoints.add(trackpoint);
|
||||
}
|
||||
setProgress(25);
|
||||
new GpsTracesDao(osm).create(workout.getDateString(), visibility, description, Collections.singletonList("FitoTrack"), trackpoints);
|
||||
setProgress(100);
|
||||
handler.post(() -> {
|
||||
Toast.makeText(activity, R.string.uploadSuccessful, Toast.LENGTH_LONG).show();
|
||||
dialogController.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
private void setProgress(int progress){
|
||||
handler.post(() -> dialogController.setProgress(progress));
|
||||
}
|
||||
|
||||
}
|
||||
64
app/src/main/res/layout/dialog_upload_osm.xml
Normal file
64
app/src/main/res/layout/dialog_upload_osm.xml
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/description" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/uploadDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ems="10"
|
||||
android:inputType="textShortMessage|textAutoComplete"
|
||||
android:singleLine="true" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/trackVisibilityPref" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/uploadVisibility"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:dropDownWidth="match_parent"
|
||||
android:entries="@array/osm_track_visibility"
|
||||
android:spinnerMode="dropdown" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</TableLayout>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/uploadCutting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/cut" />
|
||||
|
||||
</LinearLayout>
|
||||
@ -23,6 +23,9 @@
|
||||
<item
|
||||
android:id="@+id/actionExportGpx"
|
||||
android:title="@string/exportAsGpxFile" />
|
||||
<item
|
||||
android:id="@+id/actionUploadOSM"
|
||||
android:title="@string/actionUploadToOSM" />
|
||||
<item
|
||||
android:id="@+id/actionDeleteWorkout"
|
||||
android:title="@string/delete" />
|
||||
|
||||
8
app/src/main/res/values/osm_track_visibility.xml
Normal file
8
app/src/main/res/values/osm_track_visibility.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="osm_track_visibility">
|
||||
<item>Identifiable</item>
|
||||
<item>Trackable</item>
|
||||
<item>Private</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@ -62,6 +62,12 @@
|
||||
<string name="workoutBurnedEnergy">Burned Energy</string>
|
||||
<string name="workoutTotalEnergy">Total Energy</string>
|
||||
<string name="workoutEnergyConsumption">Energy Consumption</string>
|
||||
<string name="uploading">Uploading</string>
|
||||
<string name="enterVerificationCode">Enter Verification Code</string>
|
||||
<string name="authenticationFailed">Authentication failed.</string>
|
||||
<string name="upload">Upload</string>
|
||||
<string name="uploadSuccessful">Upload Successful</string>
|
||||
<string name="uploadFailed">Upload Failed</string>
|
||||
|
||||
<string name="workoutAscent">Ascent</string>
|
||||
<string name="workoutDescent">Descent</string>
|
||||
@ -115,4 +121,8 @@
|
||||
<string name="data">Data</string>
|
||||
<string name="mapStyle">Map Style</string>
|
||||
<string name="waiting_gps">Waiting for GPS</string>
|
||||
<string name="actionUploadToOSM">Upload to OSM</string>
|
||||
<string name="cut">Cut the first/last 300 Meters</string>
|
||||
<string name="trackVisibilityPref">Track Visibility</string>
|
||||
<string name="description">Description</string>
|
||||
</resources>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user