11package net .osmtracker .activity ;
22
3- import android .app .Activity ;
43import android .content .Intent ;
54import android .content .SharedPreferences ;
6- import android .content .pm .PackageInfo ;
7- import android .content .pm .PackageManager ;
85import android .net .Uri ;
96import android .os .Bundle ;
10- import android .preference .PreferenceManager ;
117import android .util .Log ;
12- import android .view .LayoutInflater ;
13- import android .view .View ;
14- import android .view .View .OnClickListener ;
158import android .widget .Button ;
169import android .widget .TextView ;
10+ import android .widget .Toast ;
11+
12+ import androidx .activity .result .ActivityResultLauncher ;
13+ import androidx .activity .result .contract .ActivityResultContracts ;
14+ import androidx .appcompat .app .AppCompatActivity ;
15+ import androidx .preference .PreferenceManager ;
1716
1817import net .openid .appauth .AuthorizationException ;
1918import net .openid .appauth .AuthorizationRequest ;
2019import net .openid .appauth .AuthorizationResponse ;
2120import net .openid .appauth .AuthorizationService ;
2221import net .openid .appauth .AuthorizationServiceConfiguration ;
2322import net .openid .appauth .ResponseTypeValues ;
24- import net . openid . appauth . TokenResponse ;
23+
2524import net .osmtracker .OSMTracker ;
2625import net .osmtracker .R ;
2726import net .osmtracker .osm .OpenStreetMapConstants ;
2827import net .osmtracker .osm .UploadToOpenStreetMapNotesTask ;
2928
29+ import java .util .concurrent .ExecutorService ;
30+ import java .util .concurrent .Executors ;
31+
3032/**
31- * <p>Uploads a note on OSM using the API and
32- * OAuth authentication.</p>
33+ * <p>Uploads a note on OSM using the API and OAuth authentication.</p>
3334 *
3435 * <p>This activity may be called twice during a single
3536 * upload cycle: First to start the upload, then a second
3637 * time when the user has authenticated using the browser.</p>
3738 *
3839 * @author Most of the code was made by Nicolas Guillaumin, adapted by Jose Andrés Vargas Serrano
3940 */
40- public class OpenStreetMapNotesUpload extends Activity {
41+ public class OpenStreetMapNotesUpload extends AppCompatActivity {
4142
4243 private static final String TAG = OpenStreetMapNotesUpload .class .getSimpleName ();
4344
@@ -51,19 +52,39 @@ public class OpenStreetMapNotesUpload extends Activity {
5152
5253 /** URL that the browser will call once the user is authenticated */
5354 public final static String OAUTH2_CALLBACK_URL = "osmtracker://osm-upload/oath2-completed/" ;
54- public final static int RC_AUTH = 7 ;
55-
5655 private AuthorizationService authService ;
56+ private ActivityResultLauncher <Intent > authLauncher ;
5757
5858 @ Override
5959 protected void onCreate (Bundle savedInstanceState ) {
60- super .onCreate (savedInstanceState );
61- View uploadNoteView = getLayoutInflater ().inflate (R .layout .osm_note_upload , null );
62- setContentView (uploadNoteView );
63- setTitle (R .string .osm_note_upload );
64-
65- noteContentView = uploadNoteView .findViewById (R .id .wplist_item_name );
66- noteFooterView = uploadNoteView .findViewById (R .id .osm_note_footer );
60+ super .onCreate (savedInstanceState );
61+
62+ // Register the launcher
63+ authLauncher = registerForActivityResult (
64+ new ActivityResultContracts .StartActivityForResult (),
65+ result -> {
66+ // This replaces the logic previously in onActivityResult
67+ Intent data = result .getData ();
68+ // RC_AUTH logic
69+ if (data != null ) {
70+ AuthorizationResponse resp = AuthorizationResponse .fromIntent (data );
71+ AuthorizationException ex = AuthorizationException .fromIntent (data );
72+
73+ if (resp != null ) {
74+ exchangeAuthorizationCode (resp );
75+ } else {
76+ Log .e (TAG , "Authorization failed: " + (ex != null ? ex .getMessage () : "Unknown error" ));
77+ Toast .makeText (this , R .string .osm_upload_oauth_failed , Toast .LENGTH_SHORT ).show ();
78+ }
79+ }
80+ }
81+ );
82+
83+
84+ setContentView (R .layout .osm_note_upload );
85+ setTitle (R .string .osm_note_upload );
86+ noteContentView = findViewById (R .id .wplist_item_name );
87+ noteFooterView = findViewById (R .id .osm_note_footer );
6788
6889 // Read and cache extras
6990 Bundle extras = getIntent ().getExtras ();
@@ -85,20 +106,10 @@ protected void onCreate(Bundle savedInstanceState) {
85106 noteContentView .setText (initialNoteText );
86107 noteFooterView .setText (getString (R .string .osm_note_footer , appName , version ));
87108
88- final Button btnOk = (Button ) findViewById (R .id .osm_note_upload_button_ok );
89- btnOk .setOnClickListener (new OnClickListener () {
90- @ Override
91- public void onClick (View v ) {
92- startUpload (noteId );
93- }
94- });
95- final Button btnCancel = (Button ) findViewById (R .id .osm_note_upload_button_cancel );
96- btnCancel .setOnClickListener (new OnClickListener () {
97- @ Override
98- public void onClick (View v ) {
99- finish ();
100- }
101- });
109+ final Button btnOk = findViewById (R .id .osm_note_upload_button_ok );
110+ btnOk .setOnClickListener (v -> startUpload (noteId ));
111+ final Button btnCancel = findViewById (R .id .osm_note_upload_button_cancel );
112+ btnCancel .setOnClickListener (v -> finish ());
102113
103114 }
104115
@@ -109,100 +120,96 @@ public void onClick(View v) {
109120 */
110121 private void startUpload (long noteId ) {
111122 SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences (this );
112- if ( prefs .contains (OSMTracker .Preferences .KEY_OSM_OAUTH2_ACCESSTOKEN ) ) {
113- // Re-use saved token
114- uploadToOsm (prefs .getString (OSMTracker .Preferences .KEY_OSM_OAUTH2_ACCESSTOKEN , "" ), noteId );
115- } else {
116- // Open browser and request token
123+ String accessToken = prefs .getString (OSMTracker .Preferences .KEY_OSM_OAUTH2_ACCESSTOKEN , null );
124+
125+ if (accessToken != null && !accessToken .isEmpty ()) {
126+ // STATE: AUTHORIZED. Re-use saved token
127+ Log .d (TAG , "Token found, proceeding to upload note to OSM." );
128+ uploadToOsm (accessToken , noteId );
129+ } else {
130+ // STATE: UNAUTHORIZED. Open browser and request token
131+ Log .d (TAG , "No token found, requesting authorization." );
117132 requestOsmAuth ();
118133 }
119134 }
120135 /*
121- * Init Authorization request workflow.
136+ * Init Authorization request workflow. Launches browser to request authorization.
122137 */
123138 public void requestOsmAuth () {
124139 // Authorization service configuration
125- AuthorizationServiceConfiguration serviceConfig =
126- new AuthorizationServiceConfiguration (
140+ AuthorizationServiceConfiguration serviceConfig = new AuthorizationServiceConfiguration (
127141 Uri .parse (OpenStreetMapConstants .OAuth2 .Urls .AUTHORIZATION_ENDPOINT ),
128142 Uri .parse (OpenStreetMapConstants .OAuth2 .Urls .TOKEN_ENDPOINT ));
129143
130- // Obtaining an authorization code
131- Uri redirectURI = Uri .parse (OAUTH2_CALLBACK_URL );
132- AuthorizationRequest .Builder authRequestBuilder =
133- new AuthorizationRequest .Builder (
134- serviceConfig , OpenStreetMapConstants .OAuth2 .CLIENT_ID ,
135- ResponseTypeValues .CODE , redirectURI );
136- AuthorizationRequest authRequest = authRequestBuilder
137- .setScope (OpenStreetMapConstants .OAuth2 .SCOPE )
138- .build ();
139-
140- // Start activity.
144+ // Obtaining an authorization code
145+ AuthorizationRequest authRequest = new AuthorizationRequest .Builder (
146+ serviceConfig ,
147+ OpenStreetMapConstants .OAuth2 .CLIENT_ID ,
148+ ResponseTypeValues .CODE ,
149+ Uri .parse (OAUTH2_CALLBACK_URL ))
150+ .setScope (OpenStreetMapConstants .OAuth2 .SCOPE )
151+ .build ();
152+
153+ // Start activity.
141154 authService = new AuthorizationService (this );
142155 Intent authIntent = authService .getAuthorizationRequestIntent (authRequest );
143- startActivityForResult (authIntent , RC_AUTH ); //when done onActivityResult will be called.
156+ //when done onActivityResult will be called.
157+ // Use the launcher instead of startActivityForResult
158+ authLauncher .launch (authIntent );
144159 }
145160
146-
147- protected void onActivityResult (int requestCode , int resultCode , Intent data ) {
148- super .onActivityResult (requestCode , resultCode , data );
149- // User is returning from authentication
150- if (requestCode == RC_AUTH ) {
151- // Handling the authorization response
152- AuthorizationResponse resp = AuthorizationResponse .fromIntent (data );
153- AuthorizationException ex = AuthorizationException .fromIntent (data );
154- // ... process the response or exception ...
155- if (ex != null ) {
156- Log .e (TAG , "Authorization Error. Exception received from server." );
157- Log .e (TAG , ex .getMessage ());
158- } else if (resp == null ) {
159- Log .e (TAG , "Authorization Error. Null response from server." );
160- } else {
161- SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences (this );
162-
163- //Exchanging the authorization code
164- authService .performTokenRequest (
165- resp .createTokenExchangeRequest (),
166- new AuthorizationService .TokenResponseCallback () {
167- @ Override public void onTokenRequestCompleted (
168- TokenResponse resp , AuthorizationException ex ) {
169- if (resp != null ) {
170- // exchange succeeded
171- SharedPreferences .Editor editor = prefs .edit ();
172- editor .putString (OSMTracker .Preferences .KEY_OSM_OAUTH2_ACCESSTOKEN , resp .accessToken );
173- editor .apply ();
174- //continue with the note Upload.
175- uploadToOsm (resp .accessToken , noteId );
176- } else {
177- // authorization failed, check ex for more details
178- Log .e (TAG , "OAuth failed." );
179- }
180- }
181- });
182- }
183- } else {
184- Log .e (TAG , "Unexpected requestCode:" + requestCode + "." );
185- }
186- }
161+ private void exchangeAuthorizationCode (AuthorizationResponse resp ) {
162+ authService .performTokenRequest (resp .createTokenExchangeRequest (), (tokenResp , tokenEx ) -> {
163+ if (tokenResp != null && tokenResp .accessToken != null ) {
164+ // STATE: TRANSITION TO AUTHORIZED
165+ persistToken (tokenResp .accessToken );
166+ uploadToOsm (tokenResp .accessToken , noteId );
167+ } else {
168+ Log .e (TAG , "Token exchange failed" );
169+ }
170+ });
171+ }
172+
173+ private void persistToken (String token ) {
174+ PreferenceManager .getDefaultSharedPreferences (this ).edit ()
175+ .putString (OSMTracker .Preferences .KEY_OSM_OAUTH2_ACCESSTOKEN , token )
176+ .apply ();
177+ }
187178
188179 /**
189180 * Uploads notes to OSM.
190181 */
191182 public void uploadToOsm (String accessToken , long noteId ) {
192- String noteText = noteContentView .getText ().toString ();
193- String footer = noteFooterView .getText ().toString ();
194- if (!footer .isEmpty ()) {
195- noteText = noteText + "\n \n " + footer ;
196- }
197- new UploadToOpenStreetMapNotesTask (
198- OpenStreetMapNotesUpload .this ,
199- accessToken ,
200- noteId ,
201- noteText ,
202- latitude ,
203- longitude
204- ).execute ();
205- }
206-
183+ String noteText = noteContentView .getText ().toString ();
184+ String footer = noteFooterView .getText ().toString ();
185+ if (!footer .isEmpty ()) {
186+ noteText = noteText + "\n \n " + footer ;
187+ }
188+
189+ // Final variables for the background thread
190+ final String finalNoteText = noteText ;
191+
192+ // This replaces the deprecated AsyncTask.execute()
193+ ExecutorService executor = Executors .newSingleThreadExecutor ();
194+ executor .execute (() -> {
195+ try {
196+ new UploadToOpenStreetMapNotesTask (
197+ OpenStreetMapNotesUpload .this ,
198+ accessToken ,
199+ noteId ,
200+ finalNoteText ,
201+ latitude ,
202+ longitude
203+ ).run ();
204+ } catch (Exception e ) {
205+ Log .e (TAG , "Error during OSM Note upload" , e );
206+ runOnUiThread (() ->
207+ Toast .makeText (this , R .string .osm_upload_error , Toast .LENGTH_SHORT ).show ()
208+ );
209+ } finally {
210+ executor .shutdown ();
211+ }
212+ });
213+ }
207214
208215}
0 commit comments