Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ dependencies {
exclude group: 'net.sf.kxml', module: 'kxml2'
exclude group: 'xmlpull', module: 'xmlpull'
}
// For upload notes to osm server
implementation ('de.westnordost:osmapi-notes:3.1'){
// Already included in Android
exclude group: 'net.sf.kxml', module: 'kxml2'
exclude group: 'xmlpull', module: 'xmlpull'
}
// App intro
implementation 'com.github.AppIntro:AppIntro:6.3.1'

Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
</intent-filter>
</activity>
<activity android:name=".activity.AvailableLayouts" />
<activity android:name=".activity.OpenStreetMapNotesUpload"/>

<service
android:name=".service.gps.GPSLogger"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package net.osmtracker.activity;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import net.openid.appauth.AuthorizationException;
import net.openid.appauth.AuthorizationRequest;
import net.openid.appauth.AuthorizationResponse;
import net.openid.appauth.AuthorizationService;
import net.openid.appauth.AuthorizationServiceConfiguration;
import net.openid.appauth.ResponseTypeValues;
import net.openid.appauth.TokenResponse;
import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.osm.OpenStreetMapConstants;
import net.osmtracker.osm.UploadToOpenStreetMapNotesTask;

/**
* <p>Uploads a note on OSM using the API and
* OAuth authentication.</p>
*
* <p>This activity may be called twice during a single
* upload cycle: First to start the upload, then a second
* time when the user has authenticated using the browser.</p>
*
* @author Most of the code was made by Nicolas Guillaumin, adapted by Jose Andrés Vargas Serrano
*/
public class OpenStreetMapNotesUpload extends Activity {

private static final String TAG = OpenStreetMapNotesUpload.class.getSimpleName();

private double latitude;
private double longitude;

private TextView noteContentView;
private TextView noteFooterView;

/** URL that the browser will call once the user is authenticated */
public final static String OAUTH2_CALLBACK_URL = "osmtracker://osm-upload/oath2-completed/";
public final static int RC_AUTH = 7;

private AuthorizationService authService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View uploadNoteView = getLayoutInflater().inflate(R.layout.osm_note_upload, null);
setContentView(uploadNoteView);
setTitle(R.string.osm_note_upload);

noteContentView = uploadNoteView.findViewById(R.id.wplist_item_name);
noteFooterView = uploadNoteView.findViewById(R.id.osm_note_footer);

// Read and cache extras
Bundle extras = getIntent().getExtras();
if (extras == null) {
Log.e(TAG, "Missing extras for note upload.");
finish();
return;
}

String initialNoteText = extras.getString("noteContent", "");
String appName = extras.getString("appName", getString(R.string.app_name));
String version = extras.getString("version", "");

if (extras.containsKey("latitude")) latitude = extras.getDouble("latitude");
if (extras.containsKey("longitude")) longitude = extras.getDouble("longitude");

// fill UI with note content and note footer
noteContentView.setText(initialNoteText);
noteFooterView.setText(getString(R.string.osm_note_footer, appName, version));

final Button btnOk = (Button) findViewById(R.id.osm_note_upload_button_ok);
btnOk.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startUpload();
}
});
final Button btnCancel = (Button) findViewById(R.id.osm_note_upload_button_cancel);
btnCancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});

}


/**
* Either starts uploading directly if we are authenticated against OpenStreetMap,
* or ask the user to authenticate via the browser.
*/
private void startUpload() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if ( prefs.contains(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN) ) {
// Re-use saved token
uploadToOsm(prefs.getString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN, ""));
} else {
// Open browser and request token
requestOsmAuth();
}
}
/*
* Init Authorization request workflow.
*/
public void requestOsmAuth() {
// Authorization service configuration
AuthorizationServiceConfiguration serviceConfig =
new AuthorizationServiceConfiguration(
Uri.parse(OpenStreetMapConstants.OAuth2.Urls.AUTHORIZATION_ENDPOINT),
Uri.parse(OpenStreetMapConstants.OAuth2.Urls.TOKEN_ENDPOINT));

// Obtaining an authorization code
Uri redirectURI = Uri.parse(OAUTH2_CALLBACK_URL);
AuthorizationRequest.Builder authRequestBuilder =
new AuthorizationRequest.Builder(
serviceConfig, OpenStreetMapConstants.OAuth2.CLIENT_ID,
ResponseTypeValues.CODE, redirectURI);
AuthorizationRequest authRequest = authRequestBuilder
.setScope(OpenStreetMapConstants.OAuth2.SCOPE)
.build();

// Start activity.
authService = new AuthorizationService(this);
Intent authIntent = authService.getAuthorizationRequestIntent(authRequest);
startActivityForResult(authIntent, RC_AUTH); //when done onActivityResult will be called.
}


protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// User is returning from authentication
if (requestCode == RC_AUTH) {
// Handling the authorization response
AuthorizationResponse resp = AuthorizationResponse.fromIntent(data);
AuthorizationException ex = AuthorizationException.fromIntent(data);
// ... process the response or exception ...
if (ex != null) {
Log.e(TAG, "Authorization Error. Exception received from server.");
Log.e(TAG, ex.getMessage());
} else if (resp == null) {
Log.e(TAG, "Authorization Error. Null response from server.");
} else {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

//Exchanging the authorization code
authService.performTokenRequest(
resp.createTokenExchangeRequest(),
new AuthorizationService.TokenResponseCallback() {
@Override public void onTokenRequestCompleted(
TokenResponse resp, AuthorizationException ex) {
if (resp != null) {
// exchange succeeded
SharedPreferences.Editor editor = prefs.edit();
editor.putString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN, resp.accessToken);
editor.apply();
//continue with the note Upload.
uploadToOsm(resp.accessToken);
} else {
// authorization failed, check ex for more details
Log.e(TAG, "OAuth failed.");
}
}
});
}
} else {
Log.e(TAG, "Unexpected requestCode:" + requestCode + ".");
}
}

/**
* Uploads notes to OSM.
*/
public void uploadToOsm(String accessToken) {
String noteText = noteContentView.getText().toString();
String footer = noteFooterView.getText().toString();
if (!footer.isEmpty()) {
noteText = noteText + "\n\n" + footer;
}
new UploadToOpenStreetMapNotesTask(
OpenStreetMapNotesUpload.this,
accessToken,
noteText,
latitude,
longitude
).execute();
}


}
53 changes: 48 additions & 5 deletions app/src/main/java/net/osmtracker/activity/WaypointList.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.*;
import android.widget.AdapterView.AdapterContextMenuInfo;
import androidx.core.content.FileProvider;
import net.osmtracker.R;
import net.osmtracker.db.DataHelper;
Expand All @@ -43,6 +44,8 @@ protected void onCreate(Bundle savedInstanceState) {
listView.setFitsSystemWindows(true);
listView.setClipToPadding(false);
listView.setPadding(0, 48, 0, 0);

registerForContextMenu(listView);
}

@Override
Expand Down Expand Up @@ -224,4 +227,44 @@ private boolean isAudioFile(String path) {
return path.endsWith(DataHelper.EXTENSION_3GPP);
}

// Where the menu items get defined
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.waypoint_contextmenu, menu);
}

// What happens when a menu item is selected
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
final Cursor cursor = ((CursorAdapter) getListAdapter()).getCursor();
if (!cursor.moveToPosition(info.position)) return super.onContextItemSelected(item);

// Menu options when you long press on a waypoint
switch (item.getItemId()) {
case R.id.wplist_contextmenu_osm_note_upload:
String noteText = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_NAME));
String appName = getString(R.string.app_name);
double lat = cursor.getDouble(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LATITUDE));
double lon = cursor.getDouble(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LONGITUDE));

Intent intent = new Intent(this, OpenStreetMapNotesUpload.class);
intent.putExtra("noteContent", noteText);
intent.putExtra("appName", appName);
// Retrieve app. version number
try {
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
String version = pi.versionName;
intent.putExtra("version", version);
} catch (PackageManager.NameNotFoundException nnfe) {
// Should not occur
}
intent.putExtra("latitude", lat);
intent.putExtra("longitude", lon);
startActivity(intent);
return true;
}
return super.onContextItemSelected(item);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ public static class Api {
}

public static class OAuth2 {
public static final String CLIENT_ID_PROD = "6s8TuIQoPeq89ZWUFOXU7EZ-ZaCUVtUoNZFIKCMdU-E";
// Client ID prod was changed to test notes uploading with new scope, must be changed back
public static final String CLIENT_ID_PROD = "2gBvqUryethglDBRXIZvXA-ijLMp--r6NUHV19NyRz4";
public static final String CLIENT_ID_DEV = "94Ht-oVBJ2spydzfk18s1RV2z7NS98SBwMfzSCqLQLE"; // DEV

public static final String CLIENT_ID = (DEV_MODE) ? CLIENT_ID_DEV : CLIENT_ID_PROD;

public static final String SCOPE = "write_gpx";
public static final String SCOPE = "write_gpx write_notes";
public static final String USER_AGENT = "OSMTracker for Android™";

public static class Urls {
Expand Down
Loading