Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue-657: Forecast Widget Type B #704

Merged
merged 17 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0a0dd48
issue-657: First draft of large widget layout
stembitf Nov 25, 2024
93917bd
Merge branch 'feature/issue-660-Forecast-widget-Type-A' into feature/…
stembitf Nov 25, 2024
f395e03
issue-657: Needed files added for large widget.
stembitf Nov 25, 2024
9c712b4
issue-657: Large widget basically implemented.
stembitf Nov 25, 2024
4116b1d
issue-657: Some code cleanup.
stembitf Nov 25, 2024
05c8cf4
issue-657: Large widget updates periodically now. Layout changes. Bac…
stembitf Nov 26, 2024
e405e9a
issue-657: Small layout updates. Unused layout file removed.
stembitf Nov 26, 2024
a5c914c
Merge branch 'android-main' into feature/issue-657-Forecast-widget-Ty…
stembitf Nov 26, 2024
0f90b2e
issue-657: Translations added. Widget update period set to 30 minutes…
stembitf Nov 26, 2024
5bcbf77
issue-657: Time at selected destination shown now for weather.
stembitf Nov 28, 2024
53da96b
issue-657: Now checked that weather forecasts have a future timestamp.
stembitf Nov 28, 2024
459414d
issue-657: onPostExecute methods simplified
stembitf Nov 28, 2024
f3370f2
issue-657: Color handling added for large widget.
stembitf Nov 28, 2024
da267a2
issue-657: Update period set to 15 minutes
stembitf Nov 28, 2024
6f0bf66
issue-624: Some restructuring in base widget provider. Some comments …
stembitf Nov 28, 2024
0226560
issue-657: new setWidgetData method after initWidget. Some variables …
stembitf Nov 29, 2024
6fe4bc5
issue-657: Widget update repeat interval time taken from widget confi…
stembitf Nov 29, 2024
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
15 changes: 15 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@
</intent-filter>
</activity>

<receiver android:name=".LargeWidgetProvider" android:label="@string/large_widget" android:exported="false">
geosaaga marked this conversation as resolved.
Show resolved Hide resolved
<intent-filter>
<action android:name="fi.fmi.mobileweather.AUTO_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/large_widget_provider_info" />
</receiver>
<activity android:name=".LargeWidgetConfigurationActivity" android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>

</application>

</manifest>
438 changes: 281 additions & 157 deletions android/app/src/main/java/fi/fmi/mobileweather/BaseWidgetProvider.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package fi.fmi.mobileweather;

public class LargeWidgetConfigurationActivity extends BaseWidgetConfigurationActivity {
@Override
protected Class<?> getWidgetProviderClass() {
return LargeWidgetProvider.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package fi.fmi.mobileweather;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.widget.RemoteViews;

import org.json.JSONArray;
import org.json.JSONObject;

import java.text.DateFormat;
import java.util.Date;
import java.util.Iterator;

public class LargeWidgetProvider extends BaseWidgetProvider {
// set the widget layout here
@Override
protected int getLayoutResourceId() {
return R.layout.large_widget_layout;
}

// set the widget UI date like colors, texts, icons etc.
@Override
protected void onPostExecute(JSONObject forecastJson, JSONArray announcementsJson, RemoteViews main, SharedPreferencesHelper pref) {

// init widget
WidgetInitResult widgetInitResult = initWidget(forecastJson, main, pref);

// populate widget with data
setWidgetData(announcementsJson, pref, widgetInitResult);
}

@Override
protected void setWidgetData(JSONArray announcementsJson, SharedPreferencesHelper pref, WidgetInitResult widgetInitResult) {
JSONObject forecastJson = widgetInitResult.forecastJson();
RemoteViews widgetRemoteViews = widgetInitResult.widgetRemoteViews();
String background = widgetInitResult.background();

// set colors for views which are specific for large widget
// (not set in the initWidget)
setLargeWidgetSpecificColors(widgetRemoteViews, background);

try {
// Get the keys of the JSONObject
Iterator<String> keys = forecastJson.keys();

// Retrieve the first key
if (!keys.hasNext()) {
return;
}
String firstKey = keys.next();
Log.d("Download forecastJson", "First key (geoid): " + firstKey);

// Extract the JSONArray associated with the first key
JSONArray data = forecastJson.getJSONArray(firstKey);

// find first epoch time which is in future
int firstFutureTimeIndex = getFirstFutureTimeIndex(data);
// if no future time found or less than 5 future times available, do not continue
if (firstFutureTimeIndex == -1 || data.length() < (firstFutureTimeIndex + 5)) {
// throw new Exception("No future time found or less than 5 future times available");
throw new Exception("No future time found or less than 5 future times available");
}

// handle the first 5 JsonObjects with future time
for (int i = firstFutureTimeIndex; i < (firstFutureTimeIndex + 5); i++) {
JSONObject forecast = data.getJSONObject(i);

// if first future index
if (i == firstFutureTimeIndex) {
// set the location name and region
String name = forecast.getString("name");
String region = forecast.getString("region");
widgetRemoteViews.setTextViewText(R.id.locationNameTextView, name + ", ");
widgetRemoteViews.setTextViewText(R.id.locationRegionTextView, region);
}


// time at the selected location
String localTime = forecast.getString("localtime");
String temperature = forecast.getString("temperature");
String weathersymbol = forecast.getString("smartSymbol");

// get timeTextView0 or timeTextView1 etc. based on i from widgetRemoteViews
int timeTextViewId = context.getResources().getIdentifier("timeTextView" + i, "id", context.getPackageName());
int temperatureTextViewId = context.getResources().getIdentifier("temperatureTextView" + i, "id", context.getPackageName());
int weatherIconImageViewId = context.getResources().getIdentifier("weatherIconImageView" + i, "id", context.getPackageName());

// ** set the time, temperature and weather icon

String formattedTime = getFormattedWeatherTime(localTime);

widgetRemoteViews.setTextViewText(timeTextViewId, formattedTime);

temperature = addPlusIfNeeded(temperature);
widgetRemoteViews.setTextViewText(temperatureTextViewId, temperature + "°");

Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
context.getResources().getIdentifier("s" + weathersymbol + (background.equals("light") ? "_light" : "_dark"), "drawable", context.getPackageName()));
widgetRemoteViews.setImageViewBitmap(weatherIconImageViewId, icon);
}

// Update time TODO: should be hidden for release
widgetRemoteViews.setTextViewText(R.id.updateTimeTextView, DateFormat.getTimeInstance().format(new Date()));

// Crisis view
showCrisisViewIfNeeded(announcementsJson, widgetRemoteViews, pref);

appWidgetManager.updateAppWidget(appWidgetId, widgetRemoteViews);
return;

} catch (final Exception e) {
Log.e("Download json", "Exception Json parsing error: " + e.getMessage());
showErrorView(
context,
pref,
"(parsing error) " + context.getResources().getString(R.string.update_failed),
context.getResources().getString(R.string.check_internet_connection)
);
}

appWidgetManager.updateAppWidget(appWidgetId, widgetRemoteViews);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ protected int getLayoutResourceId() {
}
}

// define here what happens when the user changes the widget size
@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
Expand All @@ -33,7 +34,7 @@ public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidge
int minHeight = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);

// Determine the layout resource ID based on the new size
int layoutId = getLayoutResourceId(minWidth, minHeight);
int layoutId = getLayoutResourceIdForResize(minWidth, minHeight);

// Store the layout resource ID in shared preferences
saveLayoutResourceId(context, appWidgetId, layoutId);
Expand All @@ -44,7 +45,7 @@ public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidge
}

// TODO: needs to be tested well with all kind of devices:
private int getLayoutResourceId(int minWidth, int minHeight) {
private int getLayoutResourceIdForResize(int minWidth, int minHeight) {
if (minWidth > 100 && minWidth < 200 && minHeight > 120) {
Log.d("Widget Update", "Small widget " + minWidth + "x" + minHeight);
return R.layout.small_widget_layout;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ public static void scheduleWidgetUpdate(Context context, Class<? extends AppWidg
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();

// get the update interval time from the widget setup
int repeatInterval = WidgetSetupManager.getWidgetSetup().getWeather().getInterval();

PeriodicWorkRequest updateRequest =
new PeriodicWorkRequest.Builder(WidgetUpdateWorker.class,
30, TimeUnit.MINUTES)
repeatInterval, TimeUnit.MINUTES)
.setConstraints(constraints)
.addTag("WidgetUpdate")
.build();
Expand Down
10 changes: 8 additions & 2 deletions android/app/src/main/java/fi/fmi/mobileweather/WidgetSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,21 +139,27 @@ public void setTimezone(String timezone) {

public static class Weather {
private String apiUrl;
private int interval;
private boolean useCardinalsForWindDirection;

// Getters and setters
public String getApiUrl() {
return apiUrl;
}

public void setApiUrl(String apiUrl) {
this.apiUrl = apiUrl;
}

public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}

public boolean isUseCardinalsForWindDirection() {
return useCardinalsForWindDirection;
}

public void setUseCardinalsForWindDirection(boolean useCardinalsForWindDirection) {
this.useCardinalsForWindDirection = useCardinalsForWindDirection;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,19 @@ public Result doWork() {

private void broadcastUpdate() {
Log.d("Widget Update", "Broadcasting widget update");
Intent intent = new Intent(getApplicationContext(), SmallWidgetProvider.class)

// ** broadcast to all widget providers which can receive ACTION_APPWIDGET_AUTO_UPDATE
// (SmallWidgetProvider and LargeWidgetProvider)

// Create intents for each widget provider class
Intent smallWidgetIntent = new Intent(getApplicationContext(), SmallWidgetProvider.class)
.setAction(ACTION_APPWIDGET_AUTO_UPDATE);
getApplicationContext().sendBroadcast(intent);
Intent largeWidgetIntent = new Intent(getApplicationContext(), LargeWidgetProvider.class)
.setAction(ACTION_APPWIDGET_AUTO_UPDATE);

// Send broadcasts
getApplicationContext().sendBroadcast(smallWidgetIntent);
getApplicationContext().sendBroadcast(largeWidgetIntent);
}

}
Expand Down
Loading