remove button when data is not present but being re-loaded; add icon to indicate that uvhi "clear sky" is enabled, fix a crash when manually refreshing data caused by warnings with no expiration date

This commit is contained in:
Starfish
2025-06-01 17:05:34 +02:00
parent 9096129179
commit 7b77f6bde0
14 changed files with 168 additions and 62 deletions

View File

@@ -1,4 +1,10 @@
# Changelog
## Version 0.63.0
- fixed a crash when manually refreshing data caused by warnings with no expiration date
- the button to change to the nearest station is not displayed when data is not present but being re-loaded
- when the uv hazard index is set to "clear sky", the uv hazard index icon in the main view now indicates this by displaying a tiny "C" in the upper-right corner of the icon
## Version 0.62.9
- added an option to allow the overview chart to start above 0°
- modified the possible values for a fixed scale to allow some values above 0°

View File

@@ -743,6 +743,8 @@ public class APIReaders {
*/
public void onPositiveResult(){
// remove the flag that data was removed previously, if set
WeatherSettings.removeWeatherUpdatedFlag(context,WeatherSettings.UpdateType.DATA_CLEARED);
}
public void onPositiveResult(ArrayList<RawWeatherInfo> rawWeatherInfos){

View File

@@ -247,6 +247,7 @@ private static class ViewHolder {
ImageView symbolRH;
View endofday_bar;
TextView uvHazardIndex;
TextView uvHazardIndexType;
public ViewHolder() {
symbols = new ImageView[6];
@@ -308,6 +309,7 @@ public View getView(int i, View view, ViewGroup viewGroup) {
ImageView biocular = null;
ImageView symbolRH = null;
TextView textView_uvHazardIndex = null;
TextView textView_uvHazardIndexType = null;
if (view == null) {
// view is not available from cache
newView = true;
@@ -355,6 +357,7 @@ public View getView(int i, View view, ViewGroup viewGroup) {
biocular = viewHolder.biocular;
symbolRH = viewHolder.symbolRH;
textView_uvHazardIndex = viewHolder.uvHazardIndex;
textView_uvHazardIndexType = viewHolder.uvHazardIndexType;
}
// init forecastIcons on 1st call after we inflated the imageview size
if (forecastIcons==null){
@@ -822,6 +825,10 @@ public View getView(int i, View view, ViewGroup viewGroup) {
textView_uvHazardIndex = (TextView) view.findViewById(R.id.fcitem_uvHazardIndex);
viewHolder.uvHazardIndex = textView_uvHazardIndex;
}
if (textView_uvHazardIndexType==null){
textView_uvHazardIndexType = (TextView) view.findViewById(R.id.fcitem_uvHazardIndexType);
viewHolder.uvHazardIndexType = textView_uvHazardIndexType;
}
if (textView_uvHazardIndex!=null) {
/*
int uvHazardPosition = getHourlyPosition(i);
@@ -834,17 +841,18 @@ public View getView(int i, View view, ViewGroup viewGroup) {
if (WeatherSettings.UVHImainDisplay(context) && weatherInfo.hasUvHazardIndex()){
if (weatherInfo.isDaytime(weatherLocation)) {
textView_uvHazardIndex.setVisibility(View.VISIBLE);
/*
textView_uvHazardIndex.setBackground(ForecastBitmap.getColoredBox(context,UVHazardIndex.UVIndexColors[weatherForecasts_hourly.get(uvHazardPosition).getUvHazardIndex()]));
textView_uvHazardIndex.setText(String.valueOf(weatherForecasts_hourly.get(uvHazardPosition).getUvHazardIndex()));
*/
if (WeatherSettings.getPrefUVHIClearSky(context)){
textView_uvHazardIndexType.setVisibility(View.VISIBLE);
}
textView_uvHazardIndex.setBackground(ForecastBitmap.getColoredBox(context,UVHazardIndex.UVIndexColors[weatherInfo.getUvHazardIndex()]));
textView_uvHazardIndex.setText(String.valueOf(weatherInfo.getUvHazardIndex()));
} else {
textView_uvHazardIndex.setVisibility(View.GONE);
textView_uvHazardIndexType.setVisibility(View.GONE);
}
} else {
textView_uvHazardIndex.setVisibility(View.GONE);
textView_uvHazardIndexType.setVisibility(View.GONE);
}
}
// hourly forecast bar, display only if forecast is 6h

View File

@@ -156,17 +156,13 @@ public class MainActivity extends Activity {
ThemePicker.tintAlertDialogButtons(context,alertDialog);
}
}
// action DEPRECATED, currently not used
if (intent.getAction().equals(MainActivity.MAINAPP_SHOW_PROGRESS)){
ProgressBar progressBar = (ProgressBar) findViewById(R.id.main_progressbar);
if (progressBar!=null){
progressBar.setVisibility(View.VISIBLE);
}
showMainappProgress();
}
// action DEPRECATED, currently not used
if (intent.getAction().equals(MainActivity.MAINAPP_HIDE_PROGRESS)){
ProgressBar progressBar = (ProgressBar) findViewById(R.id.main_progressbar);
if (progressBar!=null){
progressBar.setVisibility(View.INVISIBLE);
}
hideMainappProgress();
forceWeatherUpdateFlag = false;
}
if (intent.getAction().equals(WeatherWarningActivity.WEATHER_WARNINGS_UPDATE)){
@@ -359,29 +355,29 @@ public class MainActivity extends Activity {
@Override
protected void onResume(){
PrivateLog.log(getApplicationContext(),PrivateLog.MAIN, PrivateLog.INFO,"app resumed.");
/*
// we need to check here, too since warnings may have been activated in settings again
try {
if (prepareAreaDatabase(context)){
Intent serviceStartIntent = new Intent(context,CreateAreasDatabaseService.class);
startService(serviceStartIntent);
}
} catch (Exception e){
// ignore
}
*/
long estimatedAdapterLayoutTimeMillis = getEstimatedAdapterLayoutTimeInMillis(context);
PrivateLog.log(getApplicationContext(),PrivateLog.MAIN, PrivateLog.INFO,"Estimated weather adapter layout time is: "+timerDecimalFormat.format(estimatedAdapterLayoutTimeMillis/1000f)+ " sec");
//PrivateLog.log(context,PrivateLog.MAIN,PrivateLog.INFO,"Entering onResume at "+timerDecimalFormat.format((Calendar.getInstance().getTimeInMillis()-launchTimer)/1000f)+" sec from app launch.");
boolean newLocation = false;
boolean syncWeatherUpdate = false;
// check if a new station was set in the background
if (WeatherSettings.useBackgroundLocation(context)){
newLocation = WeatherLocationManager.checkForBackgroundLocation(context);
syncWeatherUpdate = WeatherLocationManager.checkForBackgroundLocation(context);
}
// check if station has changed
if (WeatherSettings.hasWeatherUpdatedFlag(context,WeatherSettings.UpdateType.STATION)){
newLocation = true;
syncWeatherUpdate = true;
}
if ((DataStorage.Updates.isSyncDue(context,WeatherSettings.Updates.Category.WEATHER)) || (newLocation)) {
PrivateLog.log(context,PrivateLog.MAIN,PrivateLog.INFO,"Weather data is outdated, getting new weather data.");
// check if data was cleared (e.g. by changing the UV setting)
if (WeatherSettings.hasWeatherUpdatedFlag(context,WeatherSettings.UpdateType.DATA_CLEARED)){
// we need to invalidate the data in the memory, since this data is not accurate any more
weatherCard = null;
// need to force empty screen; the sync process will be triggered from inside displayWeatherForecast()
displayWeatherForecast();
} else {
// do nothing
}
if ((DataStorage.Updates.isSyncDue(context,WeatherSettings.Updates.Category.WEATHER)) || (syncWeatherUpdate)) {
PrivateLog.log(context,PrivateLog.MAIN,PrivateLog.INFO,"Weather data is outdated or not present, getting new weather data.");
weatherForecastRunnable.setWeatherLocations(null);
executor.execute(weatherForecastRunnable);
} else {
@@ -500,6 +496,8 @@ public class MainActivity extends Activity {
updateAppViews(null);
PrivateLog.log(context, PrivateLog.MAIN, PrivateLog.INFO, "Weather update: success");
super.onPositiveResult();
// remove the flag that data was removed
WeatherSettings.removeWeatherUpdatedFlag(context,WeatherSettings.UpdateType.DATA_CLEARED);
DataStorage.Updates.setLastUpdate(context,WeatherSettings.Updates.Category.WEATHER,Calendar.getInstance().getTimeInMillis());
// finally, do a sync of other conditions. Weather will not by updated, since setLastUpdate was called.
// If no sync is due, nothing will happen.
@@ -509,7 +507,7 @@ public class MainActivity extends Activity {
public void run() {
ContentResolver.requestSync(getManualSyncRequest(context, WeatherSyncAdapter.UpdateFlags.FLAG_UPDATE_DEFAULT));
}
});;
});
loadCurrentWeather();
// also update the widgets & Gadgetbridge with new data. This will take place with a delay.
updateAppViews(null);
@@ -1165,11 +1163,13 @@ public class MainActivity extends Activity {
// force a re-load of weather data using the station from settings
weatherCard = null;
// set back the flag as update was done.
WeatherSettings.setWeatherUpdatedFlag(context,WeatherSettings.UpdateType.NONE);
//WeatherSettings.setWeatherUpdatedFlag(context,WeatherSettings.UpdateType.NONE);
WeatherSettings.removeWeatherUpdatedFlag(context,WeatherSettings.UpdateType.STATION);
}
if (WeatherSettings.hasWeatherUpdatedFlag(context,WeatherSettings.UpdateType.VIEWS)){
// set back the flag before recreate occurs
WeatherSettings.setWeatherUpdatedFlag(context,WeatherSettings.UpdateType.NONE);
//WeatherSettings.setWeatherUpdatedFlag(context,WeatherSettings.UpdateType.NONE);
WeatherSettings.removeWeatherUpdatedFlag(context,WeatherSettings.UpdateType.VIEWS);
// notify widgets (& Gadgetbridge), since such view changes may also affect widgets, e.g. overview chart
updateAppViews(weatherCard);
// recreate the whole view
@@ -1250,19 +1250,7 @@ public class MainActivity extends Activity {
displayUpdateTime(weatherCard);
forecastAdapter = new ForecastAdapter(getApplicationContext(),getCustomForecastWeatherInfoArray(weatherCard),weatherCard.forecast1hourly,weatherCard.weatherLocation);
forecastAdapter.setWarnings(localWarnings);
// weatherList.setFastScrollEnabled(true);
weatherList.setAdapter(forecastAdapter);
/*
weatherList.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
long time = Calendar.getInstance().getTimeInMillis()-launchTimer;
launchItemsCounter++;
//PrivateLog.log(context,PrivateLog.MAIN,PrivateLog.INFO,"Adapter item #"+launchItemsCounter+" finished layout at "+timerDecimalFormat.format(time/1000f)+" sec from app launch.");
//WeatherSettings.LaunchTimer.updateLaunchTimes(context,time,launchItemsCounter);
}
});
*/
forecastAdapter.notifyDataSetChanged();
if (WeatherSettings.loggingEnabled(this)){
float time = (Calendar.getInstance().getTimeInMillis()-launchTimer)/1000f;
@@ -2432,7 +2420,7 @@ public class MainActivity extends Activity {
Button nextStationButton = (Button) findViewById(R.id.main_nodata_button);
boolean hasNetwork = Weather.suitableNetworkAvailable(context);
if (nextStationButton!=null){
if (hasNetwork){
if ((hasNetwork) && (!WeatherSettings.hasWeatherUpdatedFlag(context,WeatherSettings.UpdateType.DATA_CLEARED))){
nextStationButton.setVisibility(View.VISIBLE);
nextStationButton.setOnClickListener(new View.OnClickListener() {
@Override

View File

@@ -228,7 +228,7 @@ public class Settings extends PreferenceActivity implements SharedPreferences.On
WeatherSettings.setWeatherUpdatedFlag(context,WeatherSettings.UpdateType.VIEWS);
}
if (s.equals(WeatherSettings.PREF_UVHI_CLEAR_SKY)){
WeatherSettings.setWeatherUpdatedFlag(context,WeatherSettings.UpdateType.VIEWS);
WeatherSettings.setWeatherUpdatedFlag(context,WeatherSettings.UpdateType.VIEWS | WeatherSettings.UpdateType.DATA_CLEARED);
Weather.removeForecastsFromDatabase(context);
}
if (s.equals(WeatherSettings.PREF_WIDGET_CLOCKSIZE)){

View File

@@ -1385,28 +1385,25 @@ public class WeatherSettings {
public final static int NONE = 0;
public final static int VIEWS = 2;
public final static int STATION = 4;
public final static int DATA_CLEARED = 8;
}
public static boolean hasWeatherUpdatedFlag(Context context, int flag) {
/*SharedPreferences sharedPreferences = getSharedPreferences(context);
int savedFlag = sharedPreferences.getInt(PREF_WEATHERUPDATEDFLAG,PREF_WEATHERUPDATEDFLAG_DEFAULT);
*/
int savedFlag = DataStorage.getInt(context,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG_DEFAULT);
int result = savedFlag & flag;
return (result>0);
}
public static void setWeatherUpdatedFlag(Context context, int flag) {
/*SharedPreferences sharedPreferences = getSharedPreferences(context);
int oldFlag = sharedPreferences.getInt(PREF_WEATHERUPDATEDFLAG,PREF_WEATHERUPDATEDFLAG_DEFAULT);
SharedPreferences.Editor pref_editor = sharedPreferences.edit();
if (flag==UpdateType.NONE){
pref_editor.putInt(PREF_WEATHERUPDATEDFLAG, UpdateType.NONE);
} else {
pref_editor.putInt(PREF_WEATHERUPDATEDFLAG, oldFlag|flag);
public static void removeWeatherUpdatedFlag(Context context, int flag) {
int savedFlag = DataStorage.getInt(context,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG_DEFAULT);
// if flag is set, xor stored flag with flag to remove flag
if (hasWeatherUpdatedFlag(context,flag)){
int result = savedFlag ^ flag;
DataStorage.setInt(context,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG, result);
}
pref_editor.apply();
*/
}
public static void setWeatherUpdatedFlag(Context context, int flag) {
int oldFlag = DataStorage.getInt(context,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG_DEFAULT);
if (flag==UpdateType.NONE){
DataStorage.setInt(context,DataStorage.DATASTORAGE_WEATHERUPDATEDFLAG, UpdateType.NONE);

View File

@@ -165,9 +165,12 @@ public class WeatherSyncAdapter extends AbstractThreadedSyncAdapter {
Bitmap iconMutable = warningIconBitmap.copy(Bitmap.Config.ARGB_8888, true);
ThemePicker.applyColor(iconMutable, weatherWarning.getWarningColor());
String notificationBody = weatherWarning.description;
notificationBody = WeatherSettings.getSetStationLocation(context).getDescription(context).toUpperCase(Locale.getDefault()) + ": " + notificationBody;
String expires = WeatherWarnings.getExpiresMiniString(context, weatherWarning);
expires = expires.replaceFirst(String.valueOf(expires.charAt(0)), String.valueOf(expires.charAt(0)).toUpperCase());
notificationBody = WeatherSettings.getSetStationLocation(context).getDescription(context).toUpperCase(Locale.getDefault()) + ": " + notificationBody + " (" + expires + ".)";
if (expires!=null){
expires = expires.replaceFirst(String.valueOf(expires.charAt(0)), String.valueOf(expires.charAt(0)).toUpperCase());
notificationBody = notificationBody + " (" + expires + ".)";
}
// construct pending intent for sharing
PendingIntent shareWarningPendingIntent = getWarningPendingIntent(context, weatherWarning, uniqueNotificationID);
Notification n;

View File

@@ -19,6 +19,7 @@
package de.kaffeemitkoffein.tinyweatherforecastgermany;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -28,6 +29,7 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import java.text.SimpleDateFormat;
import java.util.*;
@@ -193,7 +195,7 @@ public class WeatherWarnings {
} else {
return context.getResources().getString(R.string.ends)+" "+getTimeMiniString(weatherWarning.expires);
}
} else return "";
} else return null;
}
public static SpannableStringBuilder getMiniWarningsString(Context context, ArrayList<WeatherWarning> applicableWarnings, long itemStartTime, long itemStopTime, boolean multiLine, int textType){
@@ -216,7 +218,10 @@ public class WeatherWarnings {
}
if (applicableWarnings.get(i).expires!=0){
if (applicableWarnings.get(i).expires<itemStopTime){
text = text + " (" + getExpiresMiniString(context,applicableWarnings.get(i)) + ")";
String expiresMiniString = getExpiresMiniString(context,applicableWarnings.get(i));
if (expiresMiniString!=null){
text = text + " (" + expiresMiniString + ")";
}
}
}
if ((!multiLine) && (applicableWarnings.size()>1)){
@@ -302,6 +307,7 @@ public class WeatherWarnings {
return addToNotified(context,warningNotification);
}
@SuppressLint("Range")
public static ArrayList<WarningNotification> getNotificationElements(Context context){
ArrayList<WarningNotification> result = new ArrayList<WarningNotification>();
NotificationListDbHelper notificationListDbHelper = new NotificationListDbHelper(context);

View File

@@ -155,6 +155,22 @@
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView
android:id="@+id/fcitem_uvHazardIndexType"
android:layout_width="@dimen/fcmain_mediumdevice_textsize_small"
android:layout_height="@dimen/fcmain_mediumdevice_textsize_small"
android:visibility="visible"
android:layout_alignTop="@id/fcitem_uvHazardIndex"
android:layout_alignRight="@id/fcitem_uvHazardIndex"
android:gravity="top|right"
android:textStyle="bold"
android:layout_marginTop="1dp"
android:layout_marginLeft="1dp"
android:text="C"
android:textSize="@dimen/fcmain_mediumdevice_textsize_small"
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<ImageView android:id="@+id/fcitem_forecastbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -171,6 +171,22 @@
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView
android:id="@+id/fcitem_uvHazardIndexType"
android:layout_width="@dimen/fcmain_mediumdevice_textsize_small"
android:layout_height="@dimen/fcmain_mediumdevice_textsize_small"
android:visibility="visible"
android:layout_alignTop="@id/fcitem_uvHazardIndex"
android:layout_alignRight="@id/fcitem_uvHazardIndex"
android:gravity="top|right"
android:textStyle="bold"
android:layout_marginTop="1dp"
android:layout_marginLeft="1dp"
android:text="C"
android:textSize="@dimen/fcmain_mediumdevice_textsize_small"
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView android:id="@+id/fcitem_weatherconditiontext"
android:textSize="@dimen/fcmain_narrowdevice_textsize_large"
android:textColor="?attr/colorTextLight"

View File

@@ -144,6 +144,22 @@
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView
android:id="@+id/fcitem_uvHazardIndexType"
android:layout_width="@dimen/fcmain_textsize_small"
android:layout_height="@dimen/fcmain_textsize_small"
android:visibility="visible"
android:layout_alignTop="@id/fcitem_uvHazardIndex"
android:layout_alignRight="@id/fcitem_uvHazardIndex"
android:gravity="top|right"
android:textStyle="bold"
android:layout_marginTop="1dp"
android:layout_marginLeft="1dp"
android:text="C"
android:textSize="@dimen/fcmain_textsize_small"
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<ImageView android:id="@+id/fcitem_forecastbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -169,6 +169,22 @@
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView
android:id="@+id/fcitem_uvHazardIndexType"
android:layout_width="@dimen/fcmain_textsize_small"
android:layout_height="@dimen/fcmain_textsize_small"
android:visibility="visible"
android:layout_alignTop="@id/fcitem_uvHazardIndex"
android:layout_alignRight="@id/fcitem_uvHazardIndex"
android:gravity="top|right"
android:textStyle="bold"
android:layout_marginTop="1dp"
android:layout_marginLeft="1dp"
android:text="C"
android:textSize="@dimen/fcmain_textsize_small"
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView android:id="@+id/fcitem_weatherconditiontext"
android:textSize="@dimen/fcmain_narrowdevice_textsize_large"
android:textColor="?attr/colorTextLight"

View File

@@ -144,6 +144,22 @@
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView
android:id="@+id/fcitem_uvHazardIndexType"
android:layout_width="@dimen/fcmain_narrowdevice_textsize_small"
android:layout_height="@dimen/fcmain_narrowdevice_textsize_small"
android:visibility="visible"
android:layout_alignTop="@id/fcitem_uvHazardIndex"
android:layout_alignRight="@id/fcitem_uvHazardIndex"
android:gravity="top|right"
android:textStyle="bold"
android:layout_marginTop="1dp"
android:layout_marginLeft="1dp"
android:text="C"
android:textSize="@dimen/fcmain_narrowdevice_textsize_small"
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<ImageView android:id="@+id/fcitem_forecastbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -169,6 +169,22 @@
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView
android:id="@+id/fcitem_uvHazardIndexType"
android:layout_width="@dimen/fcmain_narrowdevice_textsize_small"
android:layout_height="@dimen/fcmain_narrowdevice_textsize_small"
android:visibility="visible"
android:layout_alignTop="@id/fcitem_uvHazardIndex"
android:layout_alignRight="@id/fcitem_uvHazardIndex"
android:gravity="top|right"
android:textStyle="bold"
android:layout_marginTop="1dp"
android:layout_marginLeft="1dp"
android:text="C"
android:textSize="@dimen/fcmain_narrowdevice_textsize_small"
android:shadowColor="?attr/colorTextLight"
android:textColor="?attr/colorText"/>
<TextView android:id="@+id/fcitem_weatherconditiontext"
android:textSize="@dimen/fcmain_narrowdevice_textsize_large"
android:textColor="?attr/colorTextLight"