show weather icon

This commit is contained in:
danijoo
2015-12-27 19:58:07 +01:00
parent 9f1bb7c4d2
commit c6af85ddd5
10 changed files with 282 additions and 46 deletions

View File

@@ -18,7 +18,7 @@ android {
}
}
buildTypes.each {
it.buildConfigField 'String', 'OPEN_WEATHER_MAP_API_KEY', '""'
it.buildConfigField 'String', 'OPEN_WEATHER_MAP_API_KEY', '"f230a5958a6234052143e3983e295d58"'
}
}
@@ -33,4 +33,5 @@ dependencies {
compile 'com.android.support:recyclerview-v7:23.1.1'
compile 'com.google.android.gms:play-services-gcm:7.8.0'
compile 'com.google.android.apps.muzei:muzei-api:2.0'
compile 'com.google.android.gms:play-services-wearable:7.8.0'
}

View File

@@ -36,11 +36,13 @@ import android.view.View;
import com.example.android.sunshine.app.data.WeatherContract;
import com.example.android.sunshine.app.sync.SunshineSyncAdapter;
import com.example.android.sunshine.app.sync.SunshineWearSyncHelper;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import java.io.IOException;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements ForecastFragment.Callback {
@@ -145,6 +147,11 @@ public class MainActivity extends AppCompatActivity implements ForecastFragment.
if (id == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class));
return true;
} else if(id == R.id.syncWear) {
// TODO remove
SunshineWearSyncHelper helper = new SunshineWearSyncHelper();
Random rnd = new Random();
helper.updateWear(this, rnd.nextInt(100), rnd.nextInt(100), R.drawable.art_fog);
}
return super.onOptionsItemSelected(item);
}

View File

@@ -59,12 +59,12 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
// Interval at which to sync with the weather, in seconds.
// 60 seconds (1 minute) * 180 = 3 hours
public static final int SYNC_INTERVAL = 60 * 180;
public static final int SYNC_FLEXTIME = SYNC_INTERVAL/3;
public static final int SYNC_FLEXTIME = SYNC_INTERVAL / 3;
private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
private static final int WEATHER_NOTIFICATION_ID = 3004;
private static final String[] NOTIFY_WEATHER_PROJECTION = new String[] {
private static final String[] NOTIFY_WEATHER_PROJECTION = new String[]{
WeatherContract.WeatherEntry.COLUMN_WEATHER_ID,
WeatherContract.WeatherEntry.COLUMN_MAX_TEMP,
WeatherContract.WeatherEntry.COLUMN_MIN_TEMP,
@@ -78,8 +78,9 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
private static final int INDEX_SHORT_DESC = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({LOCATION_STATUS_OK, LOCATION_STATUS_SERVER_DOWN, LOCATION_STATUS_SERVER_INVALID, LOCATION_STATUS_UNKNOWN, LOCATION_STATUS_INVALID})
public @interface LocationStatus {}
@IntDef({LOCATION_STATUS_OK, LOCATION_STATUS_SERVER_DOWN, LOCATION_STATUS_SERVER_INVALID, LOCATION_STATUS_UNKNOWN, LOCATION_STATUS_INVALID})
public @interface LocationStatus {
}
public static final int LOCATION_STATUS_OK = 0;
public static final int LOCATION_STATUS_SERVER_DOWN = 1;
@@ -186,7 +187,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
/**
* Take the String representing the complete forecast in JSON Format and
* pull out the data we need to construct the Strings needed for the wireframes.
*
* <p/>
* Fortunately parsing is easy: constructor takes the JSON string and converts it
* into an Object hierarchy for us.
*/
@@ -232,7 +233,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
JSONObject forecastJson = new JSONObject(forecastJsonStr);
// do we have an error?
if ( forecastJson.has(OWM_MESSAGE_CODE) ) {
if (forecastJson.has(OWM_MESSAGE_CODE)) {
int errorCode = forecastJson.getInt(OWM_MESSAGE_CODE);
switch (errorCode) {
@@ -278,7 +279,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
// now we work exclusively in UTC
dayTime = new Time();
for(int i = 0; i < weatherArray.length(); i++) {
for (int i = 0; i < weatherArray.length(); i++) {
// These are the values that will be collected.
long dateTime;
double pressure;
@@ -296,7 +297,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
JSONObject dayForecast = weatherArray.getJSONObject(i);
// Cheating to convert this to UTC time, which is what we want anyhow
dateTime = dayTime.setJulianDay(julianStartDay+i);
dateTime = dayTime.setJulianDay(julianStartDay + i);
pressure = dayForecast.getDouble(OWM_PRESSURE);
humidity = dayForecast.getInt(OWM_HUMIDITY);
@@ -334,7 +335,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
int inserted = 0;
// add to database
if ( cVVector.size() > 0 ) {
if (cVVector.size() > 0) {
ContentValues[] cvArray = new ContentValues[cVVector.size()];
cVVector.toArray(cvArray);
getContext().getContentResolver().bulkInsert(WeatherContract.WeatherEntry.CONTENT_URI, cvArray);
@@ -342,11 +343,12 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
// delete old data so we don't build up an endless history
getContext().getContentResolver().delete(WeatherContract.WeatherEntry.CONTENT_URI,
WeatherContract.WeatherEntry.COLUMN_DATE + " <= ?",
new String[] {Long.toString(dayTime.setJulianDay(julianStartDay-1))});
new String[]{Long.toString(dayTime.setJulianDay(julianStartDay - 1))});
updateWidgets();
updateMuzei();
notifyWeather();
syncWearable();
}
Log.d(LOG_TAG, "Sync Complete. " + cVVector.size() + " Inserted");
setLocationStatus(getContext(), LOCATION_STATUS_OK);
@@ -376,6 +378,24 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
}
}
private void syncWearable() {
String locationQuery = Utility.getPreferredLocation(getContext());
Uri weatherUri = WeatherContract.WeatherEntry.buildWeatherLocationWithDate(locationQuery, System.currentTimeMillis());
Cursor cursor = getContext().getContentResolver().query(weatherUri, NOTIFY_WEATHER_PROJECTION, null, null, null);
try {
if (cursor.moveToFirst()) {
// we only show ints on the wear
int highestTemp = (int) cursor.getDouble(INDEX_MAX_TEMP);
int lowestTemp = (int) cursor.getDouble(INDEX_MIN_TEMP);
int weatherIconId = Utility.getArtResourceForWeatherCondition(cursor.getInt(INDEX_WEATHER_ID));
new SunshineWearSyncHelper().updateWear(getContext(), highestTemp, lowestTemp, weatherIconId);
}
} finally {
cursor.close();
}
}
private void notifyWeather() {
Context context = getContext();
//checking the last update and notify if it' the first of the day
@@ -384,7 +404,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
boolean displayNotifications = prefs.getBoolean(displayNotificationsKey,
Boolean.parseBoolean(context.getString(R.string.pref_enable_notifications_default)));
if ( displayNotifications ) {
if (displayNotifications) {
String lastNotificationKey = context.getString(R.string.pref_last_notification);
long lastSync = prefs.getLong(lastNotificationKey, 0);
@@ -487,9 +507,9 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
* Helper method to handle insertion of a new location in the weather database.
*
* @param locationSetting The location string used to request updates from the server.
* @param cityName A human-readable city name, e.g "Mountain View"
* @param lat the latitude of the city
* @param lon the longitude of the city
* @param cityName A human-readable city name, e.g "Mountain View"
* @param lat the latitude of the city
* @param lon the longitude of the city
* @return the row ID of the added location.
*/
long addLocation(String locationSetting, String cityName, double lat, double lon) {
@@ -554,6 +574,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
/**
* Helper method to have the sync adapter sync immediately
*
* @param context The context used to access the account service
*/
public static void syncImmediately(Context context) {
@@ -582,7 +603,7 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
context.getString(R.string.app_name), context.getString(R.string.sync_account_type));
// If the password doesn't exist, the account doesn't exist
if ( null == accountManager.getPassword(newAccount) ) {
if (null == accountManager.getPassword(newAccount)) {
/*
* Add the account and account type, no password or user data
@@ -627,10 +648,11 @@ public class SunshineSyncAdapter extends AbstractThreadedSyncAdapter {
/**
* Sets the location status into shared preference. This function should not be called from
* the UI thread because it uses commit to write to the shared preferences.
* @param c Context to get the PreferenceManager from.
*
* @param c Context to get the PreferenceManager from.
* @param locationStatus The IntDef value to set
*/
static private void setLocationStatus(Context c, @LocationStatus int locationStatus){
static private void setLocationStatus(Context c, @LocationStatus int locationStatus) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(c);
SharedPreferences.Editor spe = sp.edit();
spe.putInt(c.getString(R.string.pref_location_status_key), locationStatus);

View File

@@ -0,0 +1,89 @@
package com.example.android.sunshine.app.sync;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.DataMap;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class SunshineWearSyncHelper implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
public static final String KEY_HIGHEST_TEMP = "highestTemp";
public static final String KEY_LOWEST_TEMP = "lowestTemp";
public static final String KEY_WEATHER_ICON = "weatherIcon";
private GoogleApiClient mApiClient;
private PutDataRequest mDataRequest;
public void updateWear(Context context, int highestTemp, int lowestTemp, int weatherIconResId) {
mDataRequest = buildRequest(context, highestTemp, lowestTemp, weatherIconResId);
mApiClient = new GoogleApiClient.Builder(context.getApplicationContext())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Wearable.API)
.build();
mApiClient.connect();
}
/**
* Build the DataRequest
* @param highestTemp daily highest temperature
* @param lowestTemp daily lowest Temperature
* @return PutDataRequest with the given values in it
*/
private PutDataRequest buildRequest(Context context, int highestTemp, int lowestTemp, int weatherIconResId) {
PutDataMapRequest mapRequest = PutDataMapRequest.create("/dailyTemp");
DataMap dMap = mapRequest.getDataMap();
dMap.putInt(KEY_HIGHEST_TEMP, highestTemp);
dMap.putInt(KEY_LOWEST_TEMP, lowestTemp);
try {
Bitmap weatherIcon = BitmapFactory.decodeResource(context.getResources(), weatherIconResId);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
weatherIcon.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.flush();
byte[] byteArray = stream.toByteArray();
Asset asset = Asset.createFromBytes(byteArray);
dMap.putAsset(KEY_WEATHER_ICON, asset);
} catch (IOException e) {
// should now happen
}
return mapRequest.asPutDataRequest();
}
@Override
public void onConnected(Bundle bundle) {
Log.d("SunshineWearSyncHelper", "Api connected.");
Wearable.DataApi.putDataItem(mApiClient, mDataRequest).setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
@Override
public void onResult(DataApi.DataItemResult dataItemResult) {
Log.d("SunshineWearSyncHelper", "Data item synced: " + dataItemResult.getDataItem().getUri());
}
});
}
@Override
public void onConnectionSuspended(int i) {
Log.d("SunshineWearSyncHelper", "Api connection suspended. " + i );
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("SunshineWearSyncHelper", "Api connection failed. " + connectionResult);
}
}

View File

@@ -21,4 +21,9 @@
android:title="@string/action_settings"
android:orderInCategory="100"
app:showAsAction="never" />
<item android:id="@+id/syncWear"
android:title="Sync wear"
android:orderInCategory="100"
app:showAsAction="never"
/>
</menu>