1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-28 21:06:50 +01:00

Pebble: further improvements to the background webview

- the webview is not static anymore (but its holder is)
- the openweathermap calls of type weather and forecast are now supported (as properly as possible)
- assemble the main JSON messages in the weather receiver, as info are lost afterwards
- switch to Uri parsing instead of String (also unify handling of legacy and new requests)
- attempt to address most of the lints/warnings of AS
- remove printStackTrace in favor of LOG.warn
This commit is contained in:
Daniele Gobbetti 2017-07-26 18:12:12 +02:00
parent 79f3cad36d
commit 868593cfd3
2 changed files with 235 additions and 111 deletions

View File

@ -1,6 +1,7 @@
package nodomain.freeyourgadget.gadgetbridge.util;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@ -15,6 +16,7 @@ import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.SparseArray;
import android.webkit.ConsoleMessage;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
@ -47,6 +49,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.UUID;
@ -54,13 +57,12 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppMessage;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
public class WebViewSingleton {
private static final Logger LOG = LoggerFactory.getLogger(WebViewSingleton.class);
private static WebView instance = null;
private WebView instance = null;
private MutableContextWrapper contextWrapper;
private Looper mainLooper;
private static WebViewSingleton webViewSingleton = new WebViewSingleton();
@ -70,23 +72,23 @@ public class WebViewSingleton {
}
public static synchronized WebView getInstance(Activity context) {
if (instance == null) {
if (webViewSingleton.instance == null) {
webViewSingleton.contextWrapper = new MutableContextWrapper(context);
webViewSingleton.mainLooper = context.getMainLooper();
instance = new WebView(webViewSingleton.contextWrapper);
instance.setWillNotDraw(true);
instance.clearCache(true);
instance.setWebViewClient(new GBWebClient());
instance.setWebChromeClient(new GBChromeClient());
instance.setWebContentsDebuggingEnabled(true);
WebSettings webSettings = instance.getSettings();
webViewSingleton.instance = new WebView(webViewSingleton.contextWrapper);
WebView.setWebContentsDebuggingEnabled(true);
webViewSingleton.instance.setWillNotDraw(true);
webViewSingleton.instance.clearCache(true);
webViewSingleton.instance.setWebViewClient(new GBWebClient());
webViewSingleton.instance.setWebChromeClient(new GBChromeClient());
WebSettings webSettings = webViewSingleton.instance.getSettings();
webSettings.setJavaScriptEnabled(true);
//needed to access the DOM
webSettings.setDomStorageEnabled(true);
//needed for localstorage
webSettings.setDatabaseEnabled(true);
}
return instance;
return webViewSingleton.instance;
}
public static void updateActivityContext(Activity context) {
@ -97,7 +99,7 @@ public class WebViewSingleton {
@NonNull
public static WebView getWebView() {
return instance;
return webViewSingleton.instance;
}
public static void runJavascriptInterface(GBDevice device, UUID uuid) {
@ -110,9 +112,9 @@ public class WebViewSingleton {
new Handler(webViewSingleton.mainLooper).post(new Runnable() {
@Override
public void run() {
instance.removeJavascriptInterface("GBjs");
instance.addJavascriptInterface(jsInterface, "GBjs");
instance.loadUrl("file:///android_asset/app_config/configure.html?rand=" + Math.random() * 500);
webViewSingleton.instance.removeJavascriptInterface("GBjs");
webViewSingleton.instance.addJavascriptInterface(jsInterface, "GBjs");
webViewSingleton.instance.loadUrl("file:///android_asset/app_config/configure.html?rand=" + Math.random() * 500);
}
});
}
@ -121,7 +123,7 @@ public class WebViewSingleton {
public static void appMessage(GBDeviceEventAppMessage message) {
if (instance == null) {
if (webViewSingleton.instance == null) {
LOG.warn("WEBVIEW is not initialized, cannot send appMessages to it");
return;
}
@ -131,7 +133,7 @@ public class WebViewSingleton {
new Handler(webViewSingleton.mainLooper).post(new Runnable() {
@Override
public void run() {
instance.evaluateJavascript("Pebble.evaluate('appmessage',[" + appMessage + "]);", new ValueCallback<String>() {
webViewSingleton.instance.evaluateJavascript("Pebble.evaluate('appmessage',[" + appMessage + "]);", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
//TODO: the message should be acked here instead of in PebbleIoThread
@ -146,14 +148,14 @@ public class WebViewSingleton {
new Handler(webViewSingleton.mainLooper).post(new Runnable() {
@Override
public void run() {
if (instance != null) {
instance.setWebChromeClient(null);
instance.setWebViewClient(null);
instance.clearHistory();
instance.clearCache(true);
instance.loadUrl("about:blank");
instance.freeMemory();
instance.pauseTimers();
if (webViewSingleton.instance != null) {
webViewSingleton.instance.setWebChromeClient(null);
webViewSingleton.instance.setWebViewClient(null);
webViewSingleton.instance.clearHistory();
webViewSingleton.instance.clearCache(true);
webViewSingleton.instance.loadUrl("about:blank");
// webViewSingleton.instance.freeMemory();
webViewSingleton.instance.pauseTimers();
// instance.destroy();
// instance = null;
// contextWrapper = null;
@ -168,7 +170,7 @@ public class WebViewSingleton {
double altitude;
float latitude, longitude, accuracy, speed;
public CurrentPosition() {
private CurrentPosition() {
Prefs prefs = GBApplication.getPrefs();
this.latitude = prefs.getFloat("location_latitude", 0);
this.longitude = prefs.getFloat("location_longitude", 0);
@ -188,15 +190,12 @@ public class WebViewSingleton {
this.latitude = (float) lastKnownLocation.getLatitude();
this.longitude = (float) lastKnownLocation.getLongitude();
this.accuracy = (float) lastKnownLocation.getAccuracy();
this.accuracy = lastKnownLocation.getAccuracy();
this.altitude = (float) lastKnownLocation.getAltitude();
this.speed = (float) lastKnownLocation.getSpeed();
this.speed = lastKnownLocation.getSpeed();
}
}
}
}
}
@ -204,60 +203,79 @@ public class WebViewSingleton {
return null;
}
private static JSONObject coordObject(CurrentPosition currentPosition) throws JSONException {
JSONObject coord = new JSONObject();
coord.put("lat", currentPosition.latitude);
coord.put("lon", currentPosition.longitude);
return coord;
}
private static WebResourceResponse mimicOpenWeatherMapResponse(String origialRequestURL) {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
private static JSONObject sysObject(CurrentPosition currentPosition) throws JSONException {
GregorianCalendar[] sunrise = SPA.calculateSunriseTransitSet(new GregorianCalendar(), currentPosition.latitude, currentPosition.longitude, DeltaT.estimate(new GregorianCalendar()));
if (weatherSpec == null) {
JSONObject sys = new JSONObject();
sys.put("country", "World");
sys.put("sunrise", (sunrise[0].getTimeInMillis() / 1000));
sys.put("sunset", (sunrise[2].getTimeInMillis() / 1000));
return sys;
}
private static void convertTemps(JSONObject main, String units) throws JSONException {
if ("metric".equals(units)) {
main.put("temp", (int) main.get("temp") - 273);
main.put("temp_min", (int) main.get("temp_min") - 273);
main.put("temp_max", (int) main.get("temp_max") - 273);
} else if ("imperial".equals(units)) { //it's 2017... this is so sad
main.put("temp", ((int) (main.get("temp")) - 273.15f) * 1.8f + 32);
main.put("temp_min", ((int) (main.get("temp_min")) - 273.15f) * 1.8f + 32);
main.put("temp_max", ((int) (main.get("temp_max")) - 273.15f) * 1.8f + 32);
}
}
private static WebResourceResponse mimicOpenWeatherMapResponse(String type, String units) {
if (Weather.getInstance() == null || Weather.getInstance().getWeather2() == null) {
LOG.warn("WEBVIEW - Weather instance is null, cannot update weather");
return null;
}
//location block
CurrentPosition currentPosition = new CurrentPosition();
GregorianCalendar[] sunrise = SPA.calculateSunriseTransitSet(new GregorianCalendar(), currentPosition.latitude, currentPosition.longitude, DeltaT.estimate(new GregorianCalendar()));
JSONObject resp = new JSONObject();
JSONObject coord = new JSONObject();
JSONObject sys = new JSONObject();
JSONArray weather = new JSONArray();
JSONObject currCond = new JSONObject();
JSONObject main = new JSONObject();
try {
coord.put("lat", currentPosition.latitude);
coord.put("lon", currentPosition.longitude);
JSONObject resp;
sys.put("country", "World");
sys.put("sunrise", (sunrise[0].getTimeInMillis() / 1000));
sys.put("sunset", (sunrise[2].getTimeInMillis() / 1000));
if ("weather".equals(type) && Weather.getInstance().getWeather2().reconstructedWeather != null) {
resp = new JSONObject(Weather.getInstance().getWeather2().reconstructedWeather.toString());
currCond.put("id", weatherSpec.currentConditionCode);
currCond.put("main", weatherSpec.currentCondition);
currCond.put("icon", Weather.mapToOpenWeatherMapIcon(weatherSpec.currentConditionCode));
weather.put(currCond);
JSONObject main = resp.getJSONObject("main");
int currentTemp = weatherSpec.currentTemp;
int todayMinTemp = weatherSpec.todayMinTemp;
int todayMaxTemp = weatherSpec.todayMaxTemp;
convertTemps(main, units); //caller might want different units
if (origialRequestURL.contains("units=metric")) { // not nice but WebResourceRequest is not in KitKat
currentTemp -= 273;
todayMinTemp -= 273;
todayMaxTemp -= 273;
resp.put("cod", 200);
resp.put("coord", coordObject(currentPosition));
resp.put("sys", sysObject(currentPosition));
} else if ("forecast".equals(type) && Weather.getInstance().getWeather2().reconstructedForecast != null) {
resp = new JSONObject(Weather.getInstance().getWeather2().reconstructedForecast.toString());
JSONObject city = resp.getJSONObject("city");
city.put("coord", coordObject(currentPosition));
JSONArray list = resp.getJSONArray("list");
for (int i = 0, size = list.length(); i < size; i++) {
JSONObject item = list.getJSONObject(i);
JSONObject main = item.getJSONObject("main");
convertTemps(main, units); //caller might want different units
}
resp.put("cod", 200);
} else {
LOG.warn("WEBVIEW - cannot mimick request of type " + type + " (unsupported or lack of data)");
return null;
}
main.put("temp", currentTemp);
main.put("temp_min", todayMinTemp);
main.put("temp_max", todayMaxTemp);
main.put("name", weatherSpec.location);
resp.put("cod", 200);
resp.put("coord", coord);
resp.put("sys", sys);
resp.put("weather", weather);
resp.put("main", main);
LOG.info("WEBVIEW - mimic openweather response" + resp.toString());
HashMap headers = new HashMap<>();
Map<String, String> headers = new HashMap<>();
headers.put("Access-Control-Allow-Origin", "*");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@ -269,7 +287,7 @@ public class WebViewSingleton {
return new WebResourceResponse("application/json", "utf-8", new ByteArrayInputStream(resp.toString().getBytes()));
}
} catch (JSONException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
}
return null;
@ -297,39 +315,50 @@ public class WebViewSingleton {
}
private static class GBWebClient extends WebViewClient {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
LOG.debug("WEBVIEW shouldInterceptRequest URL" + request.getUrl());
if (request.getUrl().toString().startsWith("http://api.openweathermap.org") || request.getUrl().toString().startsWith("https://api.openweathermap.org")) {
return mimicOpenWeatherMapResponse(request.getUrl().toString());
} else {
LOG.debug("WEBVIEW request:" + request.getUrl().toString() + " not intercepted");
}
}
Uri parsedUri = request.getUrl();
LOG.debug("WEBVIEW shouldInterceptRequest URL: " + parsedUri.toString());
WebResourceResponse mimickedReply = mimicReply(parsedUri);
if (mimickedReply != null)
return mimickedReply;
return super.shouldInterceptRequest(view, request);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
LOG.debug("WEBVIEW shouldInterceptRequest URL (legacy)" + url);
if (url.startsWith("http://api.openweathermap.org") || url.startsWith("https://api.openweathermap.org")) {
return mimicOpenWeatherMapResponse(url);
} else {
LOG.debug("WEBVIEW request:" + url + " not intercepted");
}
LOG.debug("WEBVIEW shouldInterceptRequest URL (legacy): " + url);
Uri parsedUri = Uri.parse(url);
WebResourceResponse mimickedReply = mimicReply(parsedUri);
if (mimickedReply != null)
return mimickedReply;
return super.shouldInterceptRequest(view, url);
}
private WebResourceResponse mimicReply(Uri requestedUri) {
if (requestedUri.getHost().contains("openweathermap.org")) {
LOG.debug("WEBVIEW request to openweathermap.org detected of type: " + requestedUri.getLastPathSegment() + " params: " + requestedUri.getQuery());
return mimicOpenWeatherMapResponse(requestedUri.getLastPathSegment(), requestedUri.getQueryParameter("units"));
} else {
LOG.debug("WEBVIEW request:" + requestedUri.toString() + " not intercepted");
}
return null;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http://") || url.startsWith("https://")) {
Uri parsedUri = Uri.parse(url);
if (parsedUri.getScheme().startsWith("http")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
GBApplication.getContext().startActivity(i);
} else {
} else if (parsedUri.getScheme().startsWith("pebblejs")) {
url = url.replaceFirst("^pebblejs://close#", "file:///android_asset/app_config/configure.html?config=true&json=");
view.loadUrl(url);
} else {
LOG.debug("WEBVIEW Ignoring unhandled scheme: " + parsedUri.getScheme());
}
return true;
@ -341,21 +370,21 @@ public class WebViewSingleton {
File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache");
File configurationFile = new File(destDir, uuid.toString() + ".json");
if (configurationFile.exists()) {
String jsonstring = FileUtils.getStringFromFile(configurationFile);
JSONObject json = new JSONObject(jsonstring);
String jsonString = FileUtils.getStringFromFile(configurationFile);
JSONObject json = new JSONObject(jsonString);
return json.getJSONObject("appKeys");
}
} catch (IOException | JSONException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
}
return null;
}
public static String parseIncomingAppMessage(String msg, UUID uuid) {
private static String parseIncomingAppMessage(String msg, UUID uuid) {
JSONObject jsAppMessage = new JSONObject();
JSONObject knownKeys = getAppConfigurationKeys(uuid);
HashMap<Integer, String> appKeysMap = new HashMap<Integer, String>();
SparseArray<String> appKeysMap = new SparseArray<>();
String inKey, outKey;
//knownKeys contains "name"->"index", we need to reverse that
@ -388,7 +417,7 @@ public class WebViewSingleton {
jsAppMessage.put("payload", outgoing);
} catch (JSONException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
}
return jsAppMessage.toString();
}
@ -398,7 +427,7 @@ public class WebViewSingleton {
UUID mUuid;
GBDevice device;
public JSInterface(GBDevice device, UUID mUuid) {
private JSInterface(GBDevice device, UUID mUuid) {
LOG.debug("Creating JS interface for UUID: " + mUuid.toString());
this.device = device;
this.mUuid = mUuid;
@ -456,7 +485,7 @@ public class WebViewSingleton {
GBApplication.deviceService().onAppConfiguration(this.mUuid, out.toString());
} catch (JSONException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
}
}
@ -470,7 +499,7 @@ public class WebViewSingleton {
//TODO: use real info
wi.put("language", "en");
} catch (JSONException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
}
//Json not supported apparently, we need to cast back and forth
return wi.toString();
@ -486,7 +515,7 @@ public class WebViewSingleton {
return "file:///" + configurationFile.getAbsolutePath();
}
} catch (IOException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
}
return null;
}
@ -501,7 +530,7 @@ public class WebViewSingleton {
}
} catch (IOException e) {
GB.toast("Error reading presets", Toast.LENGTH_LONG, GB.ERROR);
e.printStackTrace();
LOG.warn(e.getMessage());
}
return null;
}
@ -519,7 +548,7 @@ public class WebViewSingleton {
GB.toast("Presets stored", Toast.LENGTH_SHORT, GB.INFO);
} catch (IOException e) {
GB.toast("Error storing presets", Toast.LENGTH_LONG, GB.ERROR);
e.printStackTrace();
LOG.warn(e.getMessage());
}
}
@ -537,12 +566,12 @@ public class WebViewSingleton {
digest.update(bytes, 0, bytes.length);
bytes = digest.digest();
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(String.format("%02X", bytes[i]));
for (byte aByte : bytes) {
sb.append(String.format("%02X", aByte));
}
return sb.toString().toLowerCase();
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
return prefix;
}
}
@ -577,7 +606,7 @@ public class WebViewSingleton {
geoPosition.put("coords", coords);
} catch (JSONException e) {
e.printStackTrace();
LOG.warn(e.getMessage());
}
LOG.info("WEBVIEW - geo position" + geoPosition.toString());
return geoPosition.toString();

View File

@ -20,9 +20,14 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
public class ParcelableWeather2 implements Parcelable {
private static final Logger LOG = LoggerFactory.getLogger(ParcelableWeather2.class);
@ -35,15 +40,17 @@ public class ParcelableWeather2 implements Parcelable {
public int currentTemp = 0;
public String currentCondition = "";
String[] currentConditionType = null;
private String[] currentConditionType = null;
public int currentConditionCode = 3200;
String[] forecastConditionType = null;
private String[] forecastConditionType = null;
public int forecastConditionCode = 3200;
public int todayLowTemp = 0;
public int todayHighTemp = 0;
public int forecastLowTemp = 0;
public int forecastHighTemp = 0;
public JSONObject reconstructedWeather = null;
public JSONObject reconstructedForecast = null;
private ParcelableWeather2(Parcel in) {
int version = in.readInt();
@ -59,6 +66,11 @@ public class ParcelableWeather2 implements Parcelable {
int conditions = bundle.getInt("weather_conditions");
if (conditions > 0) {
Bundle conditionBundle = in.readBundle();
reconstructedWeather = new JSONObject();
JSONArray weather = new JSONArray();
JSONObject condition = new JSONObject();
JSONObject main = new JSONObject();
currentCondition = conditionBundle.getString("weather_condition_text");
conditionBundle.getStringArray("weather_condition_types");
currentTemp = conditionBundle.getInt("weather_current_temp");
@ -67,22 +79,105 @@ public class ParcelableWeather2 implements Parcelable {
currentConditionCode = weatherConditionTypesToOpenWeatherMapIds(currentConditionType[0]);
todayLowTemp = conditionBundle.getInt("weather_low_temp");
todayHighTemp = conditionBundle.getInt("weather_high_temp");
try {
condition.put("id", currentConditionCode);
condition.put("main", currentCondition);
condition.put("icon", Weather.mapToOpenWeatherMapIcon(currentConditionCode));
weather.put(condition);
main.put("temp", currentTemp);
main.put("humidity", conditionBundle.getInt("weather_humidity_value"));
main.put("temp_min", todayLowTemp);
main.put("temp_max", todayHighTemp);
main.put("name", location);
reconstructedWeather.put("weather", weather);
reconstructedWeather.put("main", main);
} catch (JSONException e) {
e.printStackTrace();
}
LOG.debug("Weather JSON for WEBVIEW: " + reconstructedWeather.toString());
//fetch immediate next forecast
if (--conditions > 0) {
int timeOffset = 86400000; //manually determined
reconstructedForecast = new JSONObject();
JSONArray list = new JSONArray();
JSONObject city = new JSONObject();
JSONObject item = new JSONObject();
condition = new JSONObject();
main = new JSONObject();
weather = new JSONArray();
Bundle forecastBundle = in.readBundle();
forecastConditionType = forecastBundle.getStringArray("weather_condition_types");
forecastConditionCode = weatherConditionTypesToOpenWeatherMapIds(forecastConditionType[0]);
forecastLowTemp = forecastBundle.getInt("weather_low_temp");
forecastHighTemp = forecastBundle.getInt("weather_high_temp");
try {
condition.put("id", forecastConditionCode);
condition.put("main", forecastBundle.getString("weather_condition_text"));
condition.put("icon", Weather.mapToOpenWeatherMapIcon(forecastConditionCode));
weather.put(condition);
main.put("temp", forecastBundle.getInt("weather_current_temp"));
main.put("humidity", forecastBundle.getInt("weather_humidity_value"));
main.put("temp_min", forecastLowTemp);
main.put("temp_max", forecastHighTemp);
//forecast
//"city":{"id":3181913,"name":"Bolzano","coord":{"lat":46.4927,"lon":11.3336},"country":"IT"}
city.put("name", location);
city.put("country", "World");
reconstructedForecast.put("city", city);
item.put("dt", (time / 1000) + timeOffset);
item.put("main", main);
item.put("weather", weather);
list.put(item);
} catch (JSONException e) {
e.printStackTrace();
}
// get the rest
while (--conditions > 0) {
conditionBundle = in.readBundle();
conditionBundle.getString("weather_condition_text");
weatherConditionTypesToOpenWeatherMapIds(conditionBundle.getStringArray("weather_condition_types")[0]);
conditionBundle.getInt("weather_current_temp");
item = new JSONObject();
condition = new JSONObject();
main = new JSONObject();
weather = new JSONArray();
timeOffset += 86400000;
try {
condition.put("id", weatherConditionTypesToOpenWeatherMapIds(conditionBundle.getStringArray("weather_condition_types")[0]));
condition.put("main", conditionBundle.getString("weather_condition_text"));
condition.put("icon", Weather.mapToOpenWeatherMapIcon(weatherConditionTypesToOpenWeatherMapIds(conditionBundle.getStringArray("weather_condition_types")[0])));
weather.put(condition);
main.put("temp", conditionBundle.getInt("weather_current_temp"));
main.put("humidity", conditionBundle.getInt("weather_humidity_value"));
main.put("temp_min", conditionBundle.getInt("weather_low_temp"));
main.put("temp_max", conditionBundle.getInt("weather_high_temp"));
item.put("dt", (time / 1000) + timeOffset);
item.put("main", main);
item.put("weather", weather);
list.put(item);
} catch (JSONException e) {
e.printStackTrace();
}
}
try {
reconstructedForecast.put("cnt", list.length());
reconstructedForecast.put("list", list);
} catch (JSONException e) {
e.printStackTrace();
}
LOG.debug("Forecast JSON for WEBVIEW: " + reconstructedForecast.toString());
}
}
// get the rest
while (--conditions > 0) {
Bundle conditionBundle = in.readBundle();
conditionBundle.getString("weather_condition_text");
conditionBundle.getStringArray("weather_condition_types");
conditionBundle.getInt("weather_current_temp");
}
}
public static final Creator<ParcelableWeather2> CREATOR = new Creator<ParcelableWeather2>() {