net.frakbot.FWeather.activity.LocationChooserDialog.java Source code

Java tutorial

Introduction

Here is the source code for net.frakbot.FWeather.activity.LocationChooserDialog.java

Source

/*
 * Copyright 2014 Sebastiano Poggi and Francesco Pontillo
 *
 * 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 net.frakbot.FWeather.activity;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.text.Editable;
import android.text.TextWatcher;
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 com.actionbarsherlock.app.SherlockFragmentActivity;
import net.frakbot.FWeather.R;
import net.frakbot.FWeather.updater.weather.YahooWeatherApiClient;
import net.frakbot.FWeather.util.WeatherLocationPreference;
import net.frakbot.util.log.FLog;

import java.util.ArrayList;
import java.util.List;

import static net.frakbot.FWeather.updater.weather.YahooWeatherApiClient.LocationSearchResult;

/**
 * Dialog that pops up when touching the Location preference.
 */
public class LocationChooserDialog extends SherlockFragmentActivity
        implements TextWatcher, LoaderManager.LoaderCallbacks<List<LocationSearchResult>> {

    /**
     * Time between search queries while typing.
     */
    private static final int QUERY_DELAY_MILLIS = 500;
    private static final String TAG = LocationChooserDialog.class.getSimpleName();

    private SearchResultsListAdapter mSearchResultsAdapter;
    private ListView mSearchResultsList;
    private boolean mSelectedValue = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // This sets the window size, while working around the IllegalStateException thrown by ActionBarView
        getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);

        setContentView(R.layout.dialog_weather_location_chooser);

        TextView searchView = (TextView) findViewById(R.id.location_query);
        searchView.addTextChangedListener(this);

        // Set up apps
        mSearchResultsList = (ListView) 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);
                FLog.d(TAG, "User has selected an item: " + value);
                try {
                    sendBroadcast(new Intent(WeatherLocationPreference.ACTION_SET_VALUE)
                            .putExtra(WeatherLocationPreference.EXTRA_VALUE, value).setPackage(getPackageName()));
                } catch (Exception e) {
                    FLog.w(TAG, "Unable to send the SET_VALUE broadcast", e);
                }
                mSelectedValue = true;
                finish();
            }
        });

        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

        tryBindList();
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (!mSelectedValue) {
            // If no value was selected, this has been canceled
            try {
                sendBroadcast(new Intent(WeatherLocationPreference.ACTION_CANCELED).setPackage(getPackageName()));
            } catch (Exception e) {
                FLog.w(TAG, "Unable to send the CANCELED broadcast", e);
            }
        }
    }

    private void tryBindList() {
        if (mSearchResultsAdapter == null) {
            mSearchResultsAdapter = new SearchResultsListAdapter();
        }

        if (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);
            getSupportLoaderManager().restartLoader(0, args, LocationChooserDialog.this);
        }
    };

    @Override
    public Loader<List<LocationSearchResult>> onCreateLoader(int id, Bundle args) {
        final String query = args.getString("query");
        return new ResultsLoader(query, this);
    }

    @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(LocationChooserDialog.this)
                        .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 net.frakbot.FWeather.updater.weather
     * .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();
        }
    }
}