2323import android .content .Intent ;
2424import android .content .IntentFilter ;
2525import android .content .SharedPreferences ;
26+ import android .content .pm .PackageManager ;
2627import android .location .Address ;
28+ import android .location .LocationManager ;
2729import android .util .Log ;
2830import android .view .LayoutInflater ;
2931import android .view .View ;
5658import org .json .JSONException ;
5759import org .json .JSONObject ;
5860
59- import java .io .BufferedInputStream ;
60- import java .io .BufferedReader ;
61- import java .io .InputStream ;
62- import java .io .InputStreamReader ;
6361import java .net .HttpURLConnection ;
6462import java .net .URL ;
6563import java .util .ArrayList ;
6664import java .util .Calendar ;
6765import java .util .Date ;
6866import java .util .HashMap ;
67+ import java .util .List ;
68+ import java .util .Locale ;
69+ import java .util .concurrent .CopyOnWriteArrayList ;
6970import java .util .concurrent .Future ;
7071
7172public class Weather extends GlanceDialogExtension {
7273 public static final String ACTION_REQUEST_UPDATE = "com.stario.REQUEST_UPDATE" ;
74+ public static final String PRECISE_LOCATION = "com.stario.PRECISE_LOCATION" ;
7375 public static final String FORECAST_KEY = "com.stario.WEATHER_FORECAST" ;
7476 public static final String IMPERIAL_KEY = "com.stario.IMPERIAL" ;
7577 public static final String LOCATION_NAME = "com.stario.LOCATION" ;
@@ -79,7 +81,8 @@ public class Weather extends GlanceDialogExtension {
7981 private static final String TAG = "com.stario.launcher.Weather" ;
8082
8183 private static final int FORECAST_MAX_ENTRIES = 20 ;
82- private static final int UPDATE_INTERVAL = 3_600_000 ;
84+ private static final int DEFAULT_UPDATE_INTERVAL = 3_600_000 ;
85+ private static final int FALLBACK_UPDATE_INTERVAL = 300_000 ;
8386 private static final int REQUEST_TIMEOUT = 10_000 ;
8487 private static final int DAY = 0 ;
8588 private static final int NIGHT = 1 ;
@@ -337,46 +340,50 @@ void assign(double value) {
337340 }});
338341 }};
339342
340- private static double lat = Double .MAX_VALUE ;
341- private static double lon = Double .MAX_VALUE ;
343+ private static volatile double lat = Double .MAX_VALUE ;
344+ private static volatile double lon = Double .MAX_VALUE ;
342345
343346 private final BroadcastReceiver receiver ;
344347 private final WeatherPreview preview ;
345348 private final DateParser dateParser ;
346349
347350 private SharedPreferences weatherPreferences ;
348- private ArrayList <Data > weatherData ;
349351 private SharedPreferences settings ;
352+ private GeocoderFallback geocoder ;
353+ private volatile Address address ;
354+ private volatile long lastUpdate ;
355+ private List <Data > weatherData ;
350356 private Future <?> runningTask ;
351357 private RecyclerView recycler ;
352358 private TextView temperature ;
353- private GeocoderFallback geocoder ;
354359 private TextView location ;
355360 private TextView summary ;
356- private Address address ;
357- private long lastUpdate ;
358361 private View direction ;
359362 private TextView speed ;
360363 private View container ;
361364 private ImageView icon ;
362365
363366 public Weather () {
364- this .weatherData = new ArrayList <>();
367+ this .weatherData = new CopyOnWriteArrayList <>();
365368 this .dateParser = DateParser .newBuilder ().build ();
366369 this .runningTask = null ;
370+ this .address = null ;
367371 this .lastUpdate = 0 ;
368372
369373 this .preview = new WeatherPreview ();
370374 this .receiver = new BroadcastReceiver () {
371375 @ Override
372376 public void onReceive (Context context , Intent intent ) {
373377 lastUpdate = 0 ;
378+ address = null ;
374379 weatherData .clear ();
375380 preview .update (null );
376381
377- if (runningTask != null &&
378- !runningTask .isDone ()) {
379- runningTask .cancel (true );
382+ synchronized (Weather .this ) {
383+ if (runningTask != null &&
384+ !runningTask .isDone ()) {
385+ runningTask .cancel (true );
386+ }
380387 }
381388
382389 update ();
@@ -468,22 +475,44 @@ protected void updateScaling(@FloatRange(from = 0f, to = 1f) float fraction,
468475 public HashMap <String , Object > data = new HashMap <>();
469476
470477 @ Override
471- public void update () {
478+ public synchronized void update () {
472479 if (!weatherPreferences .getBoolean (FORECAST_KEY , true ) ||
473480 (runningTask != null && !runningTask .isDone ())) {
474481 return ;
475482 }
476483
477484 runningTask = Utils .submitTask (() -> {
478- if (Math .abs (System .currentTimeMillis () - lastUpdate ) > UPDATE_INTERVAL ) {
479- if (weatherPreferences .contains (LATITUDE_KEY ) &&
480- weatherPreferences .contains (LONGITUDE_KEY )) {
481- lat = Double .longBitsToDouble (
482- weatherPreferences .getLong (LATITUDE_KEY , Long .MAX_VALUE ));
483- lon = Double .longBitsToDouble (
484- weatherPreferences .getLong (LONGITUDE_KEY , Long .MAX_VALUE ));
485- } else {
486- updateLocation (Utils .getPublicIPAddress ());
485+ if (Math .abs (System .currentTimeMillis () - lastUpdate ) > DEFAULT_UPDATE_INTERVAL ) {
486+ boolean prefersPreciseLocation = weatherPreferences .getBoolean (PRECISE_LOCATION , false );
487+ boolean fetchedPreciseLocation = false ;
488+
489+ if (prefersPreciseLocation ) {
490+ if (activity .checkSelfPermission (android .Manifest .permission .ACCESS_COARSE_LOCATION )
491+ == PackageManager .PERMISSION_GRANTED ) {
492+ LocationManager locationManager = (LocationManager )
493+ activity .getSystemService (Context .LOCATION_SERVICE );
494+ android .location .Location location =
495+ locationManager .getLastKnownLocation (LocationManager .NETWORK_PROVIDER );
496+
497+ if (location != null ) {
498+ lat = location .getLatitude ();
499+ lon = location .getLongitude ();
500+
501+ fetchedPreciseLocation = true ;
502+ }
503+ }
504+ }
505+
506+ if (!fetchedPreciseLocation ) {
507+ if (weatherPreferences .contains (LATITUDE_KEY ) &&
508+ weatherPreferences .contains (LONGITUDE_KEY )) {
509+ lat = Double .longBitsToDouble (
510+ weatherPreferences .getLong (LATITUDE_KEY , Long .MAX_VALUE ));
511+ lon = Double .longBitsToDouble (
512+ weatherPreferences .getLong (LONGITUDE_KEY , Long .MAX_VALUE ));
513+ } else {
514+ loadApproximatedLocation (Utils .getPublicIPAddress ());
515+ }
487516 }
488517
489518 if (lat < -90 || lat > 90 ||
@@ -537,11 +566,24 @@ public void update() {
537566 int index = getFirstIndexInTime ();
538567
539568 if (index > 0 ) {
540- UiUtils .runOnUIThread (() ->
541- preview .update (this .weatherData .get (index )));
569+ UiUtils .runOnUIThread (() -> preview .update (this .weatherData .get (index )));
570+ }
571+
572+ String addressName = weatherPreferences .getString (LOCATION_NAME , null );
573+ if (addressName == null ) {
574+ address = geocoder .getFromLocation (lat , lon );
575+ } else {
576+ address = new Address (Locale .ENGLISH );
577+ address .setLocality (addressName );
542578 }
543579
544- lastUpdate = System .currentTimeMillis ();
580+ // Artificially change the update interval if we want precise location data
581+ // But location is not accessible
582+ if (!fetchedPreciseLocation && prefersPreciseLocation ) {
583+ lastUpdate = System .currentTimeMillis () - DEFAULT_UPDATE_INTERVAL + FALLBACK_UPDATE_INTERVAL ;
584+ } else {
585+ lastUpdate = System .currentTimeMillis ();
586+ }
545587 } catch (JSONException exception ) {
546588 Log .e (TAG , "updateWeather: " , exception );
547589 }
@@ -566,17 +608,25 @@ private void updateData() {
566608
567609 summary .setText (getSummary (data .iconCode ));
568610
569- String locationString = weatherPreferences .getString (LOCATION_NAME , null );
570- if (locationString != null ) {
571- location .setText (locationString );
572- } else if (address != null ) {
573- locationString = address .getSubLocality ();
611+ if (address != null ) {
612+ String subLocality = address .getSubLocality ();
613+ String locality = address .getLocality ();
614+
615+ if (locality == null ) {
616+ locality = address .getSubAdminArea ();
617+ }
618+
619+ if (locality == null ) {
620+ locality = address .getAdminArea ();
621+ }
574622
575- if (locationString == null ) {
576- locationString = address .getLocality ();
623+ if (locality == null ) {
624+ locality = subLocality ;
625+ } else if (subLocality != null ) {
626+ locality = subLocality + ", " + locality ;
577627 }
578628
579- location .setText (locationString );
629+ location .setText (locality );
580630 } else {
581631 location .setText (null );
582632 }
@@ -653,9 +703,8 @@ public static int getSummary(String iconCode) {
653703 return summary != null ? summary : 0 ;
654704 }
655705
656- private void updateLocation (String ip ) {
706+ private void loadApproximatedLocation (String ip ) {
657707 for (IpApiEntry entry : LOCATION_APIS ) {
658- StringBuilder response = new StringBuilder ();
659708 HttpURLConnection connection = null ;
660709
661710 try {
@@ -671,22 +720,11 @@ private void updateLocation(String ip) {
671720 connection .connect ();
672721
673722 int responseCode = connection .getResponseCode ();
674- if (connection .getResponseCode () == HttpURLConnection .HTTP_OK ) {
675- InputStream inputStream = new BufferedInputStream (connection .getInputStream ());
676- BufferedReader reader = new BufferedReader (new InputStreamReader (inputStream ));
677-
678- String line ;
679-
680- while ((line = reader .readLine ()) != null ) {
681- response .append (line );
682- }
683-
684- JSONObject jsonObject = new JSONObject (response .toString ());
723+ if (responseCode == HttpURLConnection .HTTP_OK ) {
724+ JSONObject jsonObject = new JSONObject (Utils .readStream (connection .getInputStream ()));
685725 for (IpApiEntry .Callback callback : entry .callback ) {
686726 callback .assign (jsonObject .getDouble (callback .field ));
687727 }
688-
689- address = geocoder .getFromLocation (lat , lon );
690728 } else {
691729 Log .w (TAG , "getWeatherInfo: Server returned non-OK status: " + responseCode );
692730 }
@@ -701,7 +739,6 @@ private void updateLocation(String ip) {
701739 }
702740
703741 private JSONObject getWeatherInfo () {
704- StringBuilder response = new StringBuilder ();
705742 HttpURLConnection connection = null ;
706743
707744 try {
@@ -726,7 +763,7 @@ private JSONObject getWeatherInfo() {
726763
727764 return null ;
728765 } catch (Exception exception ) {
729- Log .e (TAG , "getWeatherInfo: " + exception . getMessage () );
766+ Log .e (TAG , "getWeatherInfo: " , exception );
730767
731768 return null ;
732769 } finally {
0 commit comments