/*
* Copyright 2013 Google Inc.
*
* 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.google.android.apps.dashclock.weather;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Loader;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.Preference;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import net.nurik.roman.dashclock.R;
import java.util.ArrayList;
import java.util.List;
import static com.google.android.apps.dashclock.weather.YahooWeatherApiClient.LocationSearchResult;
/**
* A preference that allows the user to choose a location, using the Yahoo! GeoPlanet API.
*/
public class WeatherLocationPreference extends Preference {
public WeatherLocationPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WeatherLocationPreference(Context context) {
super(context);
}
public WeatherLocationPreference(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public void setValue(String value) {
if (value == null) {
value = "";
}
if (callChangeListener(value)) {
persistString(value);
notifyChanged();
}
}
public static CharSequence getDisplayValue(Context context, String value) {
if (TextUtils.isEmpty(value) || value.indexOf(',') < 0) {
return context.getString(R.string.pref_weather_location_automatic);
}
String[] woeidAndDisplayName = value.split(",", 2);
return woeidAndDisplayName[1];
}
public static String getWoeidFromValue(String value) {
if (TextUtils.isEmpty(value) || value.indexOf(',') < 0) {
return null;
}
String[] woeidAndDisplayName = value.split(",", 2);
return woeidAndDisplayName[0];
}
@Override
protected void onClick() {
super.onClick();
LocationChooserDialogFragment fragment = LocationChooserDialogFragment.newInstance();
fragment.setPreference(this);
Activity activity = (Activity) getContext();
activity.getFragmentManager().beginTransaction()
.add(fragment, getFragmentTag())
.commit();
}
@Override
protected void onAttachedToActivity() {
super.onAttachedToActivity();
Activity activity = (Activity) getContext();
LocationChooserDialogFragment fragment = (LocationChooserDialogFragment) activity
.getFragmentManager().findFragmentByTag(getFragmentTag());
if (fragment != null) {
// re-bind preference to fragment
fragment.setPreference(this);
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedString("") : (String) defaultValue);
}
public String getFragmentTag() {
return "location_chooser_" + getKey();
}
/**
* Dialog fragment that pops up when touching the preference.
*/
public static class LocationChooserDialogFragment extends DialogFragment implements
TextWatcher,
LoaderManager.LoaderCallbacks<List<LocationSearchResult>> {
/**
* Time between search queries while typing.
*/
private static final int QUERY_DELAY_MILLIS = 500;
private WeatherLocationPreference mPreference;
private SearchResultsListAdapter mSearchResultsAdapter;
private ListView mSearchResultsList;
public LocationChooserDialogFragment() {
}
public static LocationChooserDialogFragment newInstance() {
return new LocationChooserDialogFragment();
}
public void setPreference(WeatherLocationPreference preference) {
mPreference = preference;
tryBindList();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
tryBindList();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Force Holo Light since ?android:actionBarXX would use dark action bar
Context layoutContext = new ContextThemeWrapper(getActivity(),
R.style.Theme_Dialog);
LayoutInflater layoutInflater = LayoutInflater.from(layoutContext);
View rootView = layoutInflater.inflate(R.layout.dialog_weather_location_chooser, null);
TextView searchView = (TextView) rootView.findViewById(R.id.location_query);
searchView.addTextChangedListener(this);
// Set up apps
mSearchResultsList = (ListView) rootView.findViewById(android.R.id.list);
mSearchResultsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long itemId) {
String value = mSearchResultsAdapter.getPrefValueAt(position);
mPreference.setValue(value);
dismiss();
}
});
tryBindList();
AlertDialog dialog = new AlertDialog.Builder(getActivity())
.setView(rootView)
.create();
dialog.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
return dialog;
}
private void tryBindList() {
if (mPreference == null) {
return;
}
if (isAdded() && mSearchResultsAdapter == null) {
mSearchResultsAdapter = new SearchResultsListAdapter();
}
if (mSearchResultsAdapter != null && mSearchResultsList != null) {
mSearchResultsList.setAdapter(mSearchResultsAdapter);
}
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
mQuery = charSequence.toString();
if (mRestartLoaderHandler.hasMessages(0)) {
return;
}
mRestartLoaderHandler.sendMessageDelayed(
mRestartLoaderHandler.obtainMessage(0),
QUERY_DELAY_MILLIS);
}
@Override
public void afterTextChanged(Editable editable) {
}
private String mQuery;
private Handler mRestartLoaderHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bundle args = new Bundle();
args.putString("query", mQuery);
getLoaderManager().restartLoader(0, args, LocationChooserDialogFragment.this);
}
};
@Override
public Loader<List<LocationSearchResult>> onCreateLoader(int id, Bundle args) {
final String query = args.getString("query");
return new ResultsLoader(query, getActivity());
}
@Override
public void onLoadFinished(Loader<List<LocationSearchResult>> loader,
List<LocationSearchResult> results) {
mSearchResultsAdapter.changeArray(results);
}
@Override
public void onLoaderReset(Loader<List<LocationSearchResult>> loader) {
mSearchResultsAdapter.changeArray(null);
}
private class SearchResultsListAdapter extends BaseAdapter {
private List<LocationSearchResult> mResults;
private SearchResultsListAdapter() {
mResults = new ArrayList<LocationSearchResult>();
}
public void changeArray(List<LocationSearchResult> results) {
if (results == null) {
results = new ArrayList<LocationSearchResult>();
}
mResults = results;
notifyDataSetChanged();
}
@Override
public int getCount() {
return Math.max(1, mResults.size());
}
@Override
public Object getItem(int position) {
if (position == 0 && mResults.size() == 0) {
return null;
}
return mResults.get(position);
}
public String getPrefValueAt(int position) {
if (position == 0 && mResults.size() == 0) {
return "";
}
LocationSearchResult result = mResults.get(position);
return result.woeid + "," + result.displayName;
}
@Override
public long getItemId(int position) {
if (position == 0 && mResults.size() == 0) {
return -1;
}
return mResults.get(position).woeid.hashCode();
}
@Override
public View getView(int position, View convertView, ViewGroup container) {
if (convertView == null) {
convertView = LayoutInflater.from(getActivity())
.inflate(R.layout.list_item_weather_location_result, container, false);
}
if (position == 0 && mResults.size() == 0) {
((TextView) convertView.findViewById(android.R.id.text1))
.setText(R.string.pref_weather_location_automatic);
((TextView) convertView.findViewById(android.R.id.text2))
.setText(R.string.pref_weather_location_automatic_description);
} else {
LocationSearchResult result = mResults.get(position);
((TextView) convertView.findViewById(android.R.id.text1))
.setText(result.displayName);
((TextView) convertView.findViewById(android.R.id.text2))
.setText(result.country);
}
return convertView;
}
}
}
/**
* Loader that fetches location search results from {@link YahooWeatherApiClient}.
*/
private static class ResultsLoader extends AsyncTaskLoader<List<LocationSearchResult>> {
private String mQuery;
private List<LocationSearchResult> mResults;
public ResultsLoader(String query, Context context) {
super(context);
mQuery = query;
}
@Override
public List<LocationSearchResult> loadInBackground() {
return YahooWeatherApiClient.findLocationsAutocomplete(mQuery);
}
@Override
public void deliverResult(List<LocationSearchResult> apps) {
mResults = apps;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(apps);
}
}
@Override
protected void onStartLoading() {
if (mResults != null) {
deliverResult(mResults);
}
if (takeContentChanged() || mResults == null) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
}
}
}