From 089e1a7cc72c82d8a8d49a91e3633b3458c33688 Mon Sep 17 00:00:00 2001 From: Dan Galpin Date: Mon, 25 May 2015 06:03:14 -0700 Subject: [PATCH 1/4] Add a Muzei extension, changing the wallpaper with the weather Requires installation of Muzei: https://play.google.com/store/apps/details?id=net.nurik.roman.muzei --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 12 +++ .../example/android/sunshine/app/Utility.java | 38 ++++++++- .../app/muzei/WeatherMuzeiSource.java | 79 ++++++++++++++++++ .../app/sync/SunshineSyncAdapter.java | 12 +++ app/src/main/res/drawable-nodpi/ic_muzei.png | Bin 0 -> 22644 bytes app/src/main/res/values/strings.xml | 3 + 7 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/example/android/sunshine/app/muzei/WeatherMuzeiSource.java create mode 100755 app/src/main/res/drawable-nodpi/ic_muzei.png diff --git a/app/build.gradle b/app/build.gradle index dbf29c76..e97a2342 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,4 +29,5 @@ dependencies { compile 'com.android.support:design:22.2.0' compile 'com.android.support:recyclerview-v7:22.2.0' compile 'com.google.android.gms:play-services-gcm:7.0.0' + compile 'com.google.android.apps.muzei:muzei-api:2.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 925588ff..029a49d4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,6 +110,17 @@ + + + + + + + + + = 300 && weatherId <= 321) { stringId = R.string.condition_3xx; - } else switch(weatherId) { + } else switch (weatherId) { case 500: stringId = R.string.condition_500; break; @@ -502,6 +502,42 @@ public static String getStringForWeatherCondition(Context context, int weatherId return context.getString(stringId); } + /* + * Helper method to provide the correct image according to the weather condition id returned + * by the OpenWeatherMap call. + * + * @param weatherId from OpenWeatherMap API response + * @return A string URL to an appropriate image or null if no mapping is found + */ + public static String getImageUrlForWeatherCondition(int weatherId) { + // Based on weather code data found at: + // http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes + if (weatherId >= 200 && weatherId <= 232) { + return "http://upload.wikimedia.org/wikipedia/commons/2/28/Thunderstorm_in_Annemasse,_France.jpg"; + } else if (weatherId >= 300 && weatherId <= 321) { + return "http://upload.wikimedia.org/wikipedia/commons/a/a0/Rain_on_leaf_504605006.jpg"; + } else if (weatherId >= 500 && weatherId <= 504) { + return "http://upload.wikimedia.org/wikipedia/commons/6/6c/Rain-on-Thassos.jpg"; + } else if (weatherId == 511) { + return "http://upload.wikimedia.org/wikipedia/commons/b/b8/Fresh_snow.JPG"; + } else if (weatherId >= 520 && weatherId <= 531) { + return "http://upload.wikimedia.org/wikipedia/commons/6/6c/Rain-on-Thassos.jpg"; + } else if (weatherId >= 600 && weatherId <= 622) { + return "http://upload.wikimedia.org/wikipedia/commons/b/b8/Fresh_snow.JPG"; + } else if (weatherId >= 701 && weatherId <= 761) { + return "http://upload.wikimedia.org/wikipedia/commons/e/e6/Westminster_fog_-_London_-_UK.jpg"; + } else if (weatherId == 761 || weatherId == 781) { + return "http://upload.wikimedia.org/wikipedia/commons/d/dc/Raised_dust_ahead_of_a_severe_thunderstorm_1.jpg"; + } else if (weatherId == 800) { + return "http://upload.wikimedia.org/wikipedia/commons/7/7e/A_few_trees_and_the_sun_(6009964513).jpg"; + } else if (weatherId == 801) { + return "http://upload.wikimedia.org/wikipedia/commons/e/e7/Cloudy_Blue_Sky_(5031259890).jpg"; + } else if (weatherId >= 802 && weatherId <= 804) { + return "http://upload.wikimedia.org/wikipedia/commons/5/54/Cloudy_hills_in_Elis,_Greece_2.jpg"; + } + return null; + } + /** * Returns true if the network is available or about to become available. * diff --git a/app/src/main/java/com/example/android/sunshine/app/muzei/WeatherMuzeiSource.java b/app/src/main/java/com/example/android/sunshine/app/muzei/WeatherMuzeiSource.java new file mode 100644 index 00000000..dcfc24cb --- /dev/null +++ b/app/src/main/java/com/example/android/sunshine/app/muzei/WeatherMuzeiSource.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.sunshine.app.muzei; + +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; + +import com.example.android.sunshine.app.MainActivity; +import com.example.android.sunshine.app.Utility; +import com.example.android.sunshine.app.data.WeatherContract; +import com.example.android.sunshine.app.sync.SunshineSyncAdapter; +import com.google.android.apps.muzei.api.Artwork; +import com.google.android.apps.muzei.api.MuzeiArtSource; + +/** + * Muzei source that changes your background based on the current weather conditions + */ +public class WeatherMuzeiSource extends MuzeiArtSource { + private static final String[] FORECAST_COLUMNS = new String[]{ + WeatherContract.WeatherEntry.COLUMN_WEATHER_ID, + WeatherContract.WeatherEntry.COLUMN_SHORT_DESC + }; + // these indices must match the projection + private static final int INDEX_WEATHER_ID = 0; + private static final int INDEX_SHORT_DESC = 1; + + public WeatherMuzeiSource() { + super("WeatherMuzeiSource"); + } + + @Override + protected void onHandleIntent(Intent intent) { + super.onHandleIntent(intent); + boolean dataUpdated = intent != null && + SunshineSyncAdapter.ACTION_DATA_UPDATED.equals(intent.getAction()); + if (dataUpdated && isEnabled()) { + onUpdate(UPDATE_REASON_OTHER); + } + } + + @Override + protected void onUpdate(int reason) { + String location = Utility.getPreferredLocation(this); + Uri weatherForLocationUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate( + location, System.currentTimeMillis()); + Cursor cursor = getContentResolver().query(weatherForLocationUri, FORECAST_COLUMNS, null, + null, WeatherContract.WeatherEntry.COLUMN_DATE + " ASC"); + if (cursor.moveToFirst()) { + int weatherId = cursor.getInt(INDEX_WEATHER_ID); + String desc = cursor.getString(INDEX_SHORT_DESC); + + String imageUrl = Utility.getImageUrlForWeatherCondition(weatherId); + // Only publish a new wallpaper if we have a valid image + if (imageUrl != null) { + publishArtwork(new Artwork.Builder() + .imageUri(Uri.parse(imageUrl)) + .title(desc) + .byline(location) + .viewIntent(new Intent(this, MainActivity.class)) + .build()); + } + } + cursor.close(); + } +} diff --git a/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java b/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java index c9275d0e..5968effe 100644 --- a/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java +++ b/app/src/main/java/com/example/android/sunshine/app/sync/SunshineSyncAdapter.java @@ -34,6 +34,7 @@ import com.example.android.sunshine.app.R; import com.example.android.sunshine.app.Utility; import com.example.android.sunshine.app.data.WeatherContract; +import com.example.android.sunshine.app.muzei.WeatherMuzeiSource; import org.json.JSONArray; import org.json.JSONException; @@ -341,6 +342,7 @@ private void getWeatherDataFromJson(String forecastJsonStr, new String[] {Long.toString(dayTime.setJulianDay(julianStartDay-1))}); updateWidgets(); + updateMuzei(); notifyWeather(); } Log.d(LOG_TAG, "Sync Complete. " + cVVector.size() + " Inserted"); @@ -361,6 +363,16 @@ private void updateWidgets() { context.sendBroadcast(dataUpdatedIntent); } + private void updateMuzei() { + // Muzei is only compatible with Jelly Bean MR1+ devices, so there's no need to update the + // Muzei background on lower API level devices + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + Context context = getContext(); + context.startService(new Intent(ACTION_DATA_UPDATED) + .setClass(context, WeatherMuzeiSource.class)); + } + } + private void notifyWeather() { Context context = getContext(); //checking the last update and notify if it' the first of the day diff --git a/app/src/main/res/drawable-nodpi/ic_muzei.png b/app/src/main/res/drawable-nodpi/ic_muzei.png new file mode 100755 index 0000000000000000000000000000000000000000..b0fe3c498636ce7c0c66f15c7764537c95a0ddf1 GIT binary patch literal 22644 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 52c512f8..a94d15e6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -140,6 +140,9 @@ Sunshine Today Sunshine Details + + Today\'s weather + No Weather Information Available No weather information available. The network is not available to fetch weather data. From 6752c12310ff82e59976ffa468b17be250b0ede6 Mon Sep 17 00:00:00 2001 From: Joanna Smith Date: Tue, 16 Jun 2015 00:03:04 -0700 Subject: [PATCH 2/4] Creating a new widget layout for the location text preference --- app/build.gradle | 1 + .../app/LocationEditTextPreference.java | 30 ++++++++++++++++++ .../res/drawable-hdpi/ic_current_location.png | Bin 0 -> 549 bytes .../res/drawable-mdpi/ic_current_location.png | Bin 0 -> 341 bytes .../drawable-xhdpi/ic_current_location.png | Bin 0 -> 660 bytes .../drawable-xxhdpi/ic_current_location.png | Bin 0 -> 976 bytes .../main/res/layout/pref_current_location.xml | 27 ++++++++++++++++ app/src/main/res/values/strings.xml | 3 ++ 8 files changed, 61 insertions(+) create mode 100644 app/src/main/res/drawable-hdpi/ic_current_location.png create mode 100644 app/src/main/res/drawable-mdpi/ic_current_location.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_current_location.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_current_location.png create mode 100644 app/src/main/res/layout/pref_current_location.xml diff --git a/app/build.gradle b/app/build.gradle index 0f98c48a..9618e3f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,4 +31,5 @@ dependencies { compile 'com.android.support:recyclerview-v7:22.2.0' compile 'com.google.android.apps.muzei:muzei-api:2.0' compile 'com.google.android.gms:play-services-gcm:7.5.0' + compile 'com.google.android.gms:play-services-location:7.5.0' } diff --git a/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java b/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java index eb84c05e..381c0a25 100644 --- a/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java +++ b/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java @@ -24,8 +24,14 @@ import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; +import android.widget.Toast; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; public class LocationEditTextPreference extends EditTextPreference { static final private int DEFAULT_MINIMUM_LOCATION_LENGTH = 2; @@ -42,8 +48,32 @@ public LocationEditTextPreference(Context context, AttributeSet attrs) { } finally { a.recycle(); } + + // Check to see if Google Play services is available. The Place Picker API is available + // through Google Play services, so if this is false, we'll just carry on as though this + // feature does not exist. If it is true, however, we can add a widget to our preference. + GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); + int resultCode = apiAvailability.isGooglePlayServicesAvailable(getContext()); + if (resultCode == ConnectionResult.SUCCESS) { + // Add the get current location widget to our location preference + setWidgetLayoutResource(R.layout.pref_current_location); + } } + @Override + protected View onCreateView(ViewGroup parent) { + View view = super.onCreateView(parent); + View currentLocation = view.findViewById(R.id.current_location); + currentLocation.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // We'll use a toast for now so that we can test our new preference widget. + Toast.makeText(getContext(), "Woo!", Toast.LENGTH_LONG).show(); + } + }); + + return view; + } @Override protected void showDialog(Bundle state) { diff --git a/app/src/main/res/drawable-hdpi/ic_current_location.png b/app/src/main/res/drawable-hdpi/ic_current_location.png new file mode 100644 index 0000000000000000000000000000000000000000..85e38726dd4240d78e9f802db94a6987b027d0ef GIT binary patch literal 549 zcmV+=0^0qFP)q+FWCCT}XAvHG*iR93pm_1S5Hp zGX{g3?7{FW1~WTE1v&0B)$aWAV|TJUEMI4Vh=>I~Eg~f)`bUbsvBa4vI%APZJ|NE; zS0tp?c;%8+^1MTZHfhFaF~^AZ80bfR8dQmisnWF0?lOWOivMAs61I>7o-}(5etQc3 zW(t=Q*GlZ*QPJi9WQ;5UCnl3HONKmcUCkIi0XHVsB!|$`+Jp$PIdkEDiW5@$l+e}KHK>XL2GeFWdrUB7$wV6n8>Ei0iSS^K z=Z7{5y;D_$J7-ZHp=ORx?M)`Kf-yqJS=6;x2@N7&(Tl%`L%Ya#YYk`7z30|sQ4g80 z*Lne78Wk85Q(Xo|0QUIf@zkeKuOA|f-32DEnDS;fBGv_$1n#m_CUSyLx zUAo+}P64FMv&k;wl-GS<6W|kYt7j@lhB^8sazfy#`K4`S29I5>#|uP4vFFe9;gv>NR1ZS{7KkmM6)zWGe(^m-XX^d7xb_zdYrRNjt`h% no>RS36~_OkuMzu0>#O_)RZ4aRmaPDG00000NkvXXu0mjf7M<-G literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_current_location.png b/app/src/main/res/drawable-mdpi/ic_current_location.png new file mode 100644 index 0000000000000000000000000000000000000000..5684aa7dc5d4dd20ffac6d3191a20b810fa3b56c GIT binary patch literal 341 zcmV-b0jmCqP)%+^Dn#{hi&+J@;JCoEaj|OrYZL7kDW!#0DpPlLFTqv(5ld zAjt|ff|$5qnIwEe*UG7d=7p)6Mf5+2YK8}7}aF5bzPJQLg7;+9r@&{7z|1J zU}*a)K}1wSB}VX7?ptDQsMOEs$JcXc0 zV2FJLGxFJJ)tW{hf?H#O8w9!5yYU4a1oy@Q4Fv9gR3M9>ZY*UlM_nxwYhB(^tOj)t-4}Irl~@CEvS9>iDv{ft<02T9Penfl z7b>}gE={qeQ9gUP=*sff8!fA4O(qD-v(F6;8hGq7M%bhrU=gDvo?2KZgRNKG$#1>{ zZ7fI|PDx?I;Y=DghlK;JM8@LuXZl-VBh96x)S19!k~>-Z6$eYbc#_uSkP)JcQr2ec zCvUJv54F%xR@r8T94;<7X4z4%m7Jc}bjYPZU==>QI6TNaKiY18%^Z)AV2LVz%*iV( ulHduvV~rziwk|2NO8>wAC-9}f^Y90NP?&Wf5nE#b0000126oEP)hbF_M~pKs-ng5!67dvJ&uMT#<#ewipzY&>qA?Z$U`K zdMF~c|3D-m8bNHSG5r-vDk3(RT)dc=YC_ki*N10clJ}k2?4j=Gdz=|~;GLOwHsM{= zAgbDF4AdB?F;I$b%8YOqQQc*PGCPT=qlg}^@d8<+7hGjeJ;vHZnZFRvW;sok4HI#k zzmdS6(MQAv*v2FhvkBU%u_DVzf|fW;%|v{SIC{eb7dSu%IWlC((ZNA3a)&pFqsv5) zTod0R*nZ{&n*q1?>E|JW?K)`^iX7ui(Swh@Oe3amNyskNGG$WuN;AM~1l=VPC?e?o zWCx+Tm_^WiOibI%lXagK;@H9iM72m8F+@z7ao!`YW`0CeW5jUWELICiY%vR0B+w@G zCb7&8lKKdJdp{*ZkTRkw18(h{XN-APS>-unoMS8C_9>z&5g_6*`n{9FrHvn`Af^hp zXvHPXFY3G*0`wrN9$fm?*xg=n43~X~YB%1l=}iKcL1dji$7M>LaRpB){WZu5s`Vir z6{%KI#~1L_t#@=bW3_pHiY!sUYGFm4u|50=o1ohFh-WvcHW~h;ji^g5V6{_0Jgd-( zHKdLY<3<0lIe>K@31|?jR~;S44G?#9V2vRG-Nx!f-1!ZZ!i9g~uByu(;_?NuPM_iO zDEtlr*n_C{;!L*B+XX@gA=pw0Z^q;YwkRgcuke?+-5_ujYs-pypm_Sqy zXeO>^ezH4U9PKQbk#fY5H{-mdKum`bvv6I6>Nbn@mO~_P8A11&0n+$t;tcjLvR{!% z#C60p%|3kW=U2q^4T+^4BMDQKXaU@EoZ=DU7$-$dT*CdmJjoEfbdo1amOP#GGQ>US zclTSqq=r6W5lPTX4pC!mj3Y4{rND+L@)QZ|2}jvL8A{9`p8Y{T8R}p+S6D#SXn|pN yQD@sJF>LOvafUfb2X)Z+zkOq%#z60ghW`K~?beCrQMK^^0000 + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a5ab50de..cd9580a9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,6 +48,9 @@ 94043 + + Use my location + Invalid Location (%1$s)" Validating Location... (%1$s)" From 5ac9eb20648a1085e26dc114adf7d1f46018975b Mon Sep 17 00:00:00 2001 From: Joanna Smith Date: Tue, 16 Jun 2015 00:20:27 -0700 Subject: [PATCH 3/4] Setting up the required framework for the PlacePicker integration --- app/src/main/AndroidManifest.xml | 3 ++ .../app/LocationEditTextPreference.java | 35 +++++++++++++++++-- .../sunshine/app/SettingsActivity.java | 24 +++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 03b126d2..a8893d32 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,6 +39,9 @@ android:protectionLevel="signature" /> + + + Date: Tue, 16 Jun 2015 21:39:26 -0700 Subject: [PATCH 4/4] Providing the answer for the PlacePicker implementation quiz --- .../android/sunshine/app/LocationEditTextPreference.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java b/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java index 3b663df4..b91398c8 100644 --- a/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java +++ b/app/src/main/java/com/example/android/sunshine/app/LocationEditTextPreference.java @@ -74,7 +74,7 @@ public void onClick(View v) { // Launch the Place Picker so that the user can specify their location, and then // return the result to SettingsActivity. - // TODO(student): Create a PlacePicker.IntentBuilder object here. + PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder(); // We are in a view right now, not an activity. So we need to get ourselves @@ -83,9 +83,8 @@ public void onClick(View v) { // intent comes to the right place for us to process it. Activity settingsActivity = (SettingsActivity) context; try { - // TODO(student): Launch the intent using your settingsActivity object to access - // startActivityForResult(). You'll need to build your builder object and use - // the request code we declared in SettingsActivity. + settingsActivity.startActivityForResult( + builder.build(context), SettingsActivity.PLACE_PICKER_REQUEST); } catch (GooglePlayServicesNotAvailableException | GooglePlayServicesRepairableException e) {