8000 8.03 place picker implementation answer by DAGalpin · Pull Request #49 · udacity/Advanced_Android_Development · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ dependencies {
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:design:22.2.0'
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'
}
14 changes: 14 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
android:protectionLevel="signature" />
<uses-permission android:name="com.example.android.sunshine.app.permission.C2D_MESSAGE" />

<!-- Permissions required to use the Place Picker -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down Expand Up @@ -113,6 +116,16 @@
<category android:name="com.example.android.sunshine.app" />
</intent-filter>
</receiver>
<!-- Muzei Extension -->
<service android:name=".muzei.WeatherMuzeiSource"
android:icon="@drawable/ic_muzei"
android:label="@string/app_name"
android:description="@string/muzei_description" >
<intent-filter>
<action android:name="com.google.android.apps.muzei.api.MuzeiArtSource" />
</intent-filter>
<meta-data android:name="color" android:value="@color/primary" />
</service>
<!-- Today Widget -->
<receiver
android:name=".widget.TodayWidgetProvider"
Expand All @@ -125,6 +138,7 @@
android:resource="@xml/widget_info_today" />
</receiver>
<service android:name=".widget.TodayWidgetIntentService" />
<!-- Detail Widget -->
<receiver
android:name=".widget.DetailWidgetProvider"
android:label="@string/title_widget_detail"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.example.android.sunshine.app;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
Expand All @@ -24,9 +25,17 @@
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 com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.location.places.ui.PlacePicker;

public class LocationEditTextPreference extends EditTextPreference {
static final private int DEFAULT_MINIMUM_LOCATION_LENGTH = 2;
private int mMinLength;
Expand All @@ -42,8 +51,57 @@ 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) {
Context context = getContext();

// Launch the Place Picker so that the user can specify their location, and then
// return the result to SettingsActivity.
PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();


// We are in a view right now, not an activity. So we need to get ourselves
// an activity that we can use to start our Place Picker intent. By using
// SettingsActivity in this way, we can ensure the result of the Place Picker
// intent comes to the right place for us to process it.
Activity settingsActivity = (SettingsActivity) context;
try {
settingsActivity.startActivityForResult(
builder.build(context), SettingsActivity.PLACE_PICKER_REQUEST);

} catch (GooglePlayServicesNotAvailableException
| GooglePlayServicesRepairableException e) {
// What did you do?? This is why we check Google Play services in onResume!!!
// The difference in these exception types is the difference between pausing
// for a moment to prompt the user to update/install/enable Play services vs
// complete and utter failure.
// If you prefer to manage Google Play services dynamically, then you can do so
// by responding to these exceptions in the right moment. But I prefer a cleaner
// user experience, which is why you check all of this when the app resumes,
// and then disable/enable features based on that availability.
}
}
});

return view;
}

@Override
protected void showDialog(Bundle state) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.util.Log;

import com.example.android.sunshine.app.data.WeatherContract;
import com.example.android.sunshine.app.sync.SunshineSyncAdapter;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlacePicker;

/**
* A {@link PreferenceActivity} that presents a set of application settings.
Expand All @@ -38,6 +41,7 @@
*/
public class SettingsActivity extends PreferenceActivity
implements Preference.OnPreferenceChangeListener, SharedPreferences.OnSharedPreferenceChangeListener {
protected final static int PLACE_PICKER_REQUEST = 9090;

@Override
public void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -154,4 +158,24 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
public Intent getParentActivityIntent() {
return super.getParentActivityIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check to see if the result is from our Place Picker intent
if (requestCode == PLACE_PICKER_REQUEST) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
Place place = PlacePicker.getPlace(data, this);
String address = place.getAddress().toString();

SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(getString(R.string.pref_location_key), address);
editor.commit();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ public static String getStringForWeatherCondition(Context context, int weatherId
stringId = R.string.condition_2xx;
} else if (weatherId >= 300 && weatherId <= 321) {
stringId = R.string.condition_3xx;
} else switch(weatherId) {
} else switch (weatherI 684D d) {
case 500:
stringId = R.string.condition_500;
break;
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-nodpi/ic_muzei.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions app/src/main/res/layout/pref_current_location.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/current_location"
android:layout_width="48dp"
android:layout_height="match_parent">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_current_location"
android:contentDescription="@string/pref_current_location_desc"
android:layout_gravity="end|center_vertical"
android:tint="@color/material_deep_teal_500"/>

</FrameLayout>
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
<!-- Default postal code for location preference [CHAR LIMIT=NONE] -->
<string name="pref_location_default" translatable="false">94043</string>

<!-- Content description for the get current location button in the location preference -->
<string name="pref_current_location_desc">Use my location</string>

<!-- Strings used when displaying the state of the Location in settings -->
<string name="pref_location_error_description">Invalid Location (<xliff:g id="location_setting">%1$s</xliff:g>)"</string>
<string name="pref_location_unknown_description">Validating Location... (<xliff:g id="location_setting">%1$s</xliff:g>)"</string>
Expand Down Expand Up @@ -140,6 +143,9 @@
<string name="title_widget_today">Sunshine Today</string>
<string name="title_widget_detail">Sunshine Details</string>

&l 384B t;!-- Strings related to Muzei Source -->
<string name="muzei_description">Today\'s weather</string>

<!-- Empty Weather Database -->
<string name="empty_forecast_list">No Weather Information Available</string>
<string name="empty_forecast_list_no_network">No weather information available. The network is not available to fetch weather data.</string>
Expand Down
0