name.gumartinm.weather.information.widget.WidgetIntentService.java Source code

Java tutorial

Introduction

Here is the source code for name.gumartinm.weather.information.widget.WidgetIntentService.java

Source

/**
 * Copyright 2014 Gustavo Martin Morcuende
 *
 * 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 name.gumartinm.weather.information.widget;

import android.app.IntentService;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.net.http.AndroidHttpClient;
import android.preference.PreferenceManager;
import android.support.v4.app.TaskStackBuilder;
import android.view.View;
import android.widget.RemoteViews;

import com.fasterxml.jackson.core.JsonParseException;
import name.gumartinm.weather.information.R;
import name.gumartinm.weather.information.httpclient.CustomHTTPClient;
import name.gumartinm.weather.information.model.DatabaseQueries;
import name.gumartinm.weather.information.model.WeatherLocation;
import name.gumartinm.weather.information.model.currentweather.Current;
import name.gumartinm.weather.information.parser.JPOSCurrentParser;
import name.gumartinm.weather.information.service.IconsList;
import name.gumartinm.weather.information.service.PermanentStorage;
import name.gumartinm.weather.information.service.ServiceCurrentParser;
import timber.log.Timber;

import org.apache.http.client.ClientProtocolException;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;

public class WidgetIntentService extends IntentService {
    private static final String WIDGET_PREFERENCES_NAME = "WIDGET_PREFERENCES";

    public WidgetIntentService() {
        super("WIS-Thread");
    }

    @Override
    protected void onHandleIntent(final Intent intent) {
        final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
        final boolean isRefreshAppWidget = intent.getBooleanExtra("refreshAppWidget", false);

        if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            // Nothing to do. Something went wrong.
            return;
        }

        final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
        final WeatherLocation weatherLocation = query.queryDataBase();

        if (weatherLocation == null) {
            // Nothing to do. Show error.
            final RemoteViews view = this.makeViewOnError(appWidgetId);
            this.updateWidget(view, appWidgetId);
            return;
        }

        if (!isRefreshAppWidget) {
            final RemoteViews view = this.makeViewOnNotRefresh(weatherLocation, appWidgetId);
            this.updateWidget(view, appWidgetId);
        } else {
            final RemoteViews view = this.makeViewOnRefresh(weatherLocation, appWidgetId);
            this.updateWidget(view, appWidgetId);
        }
    }

    public static void deleteWidgetCurrentData(final Context context, final int appWidgetId) {
        final PermanentStorage store = new PermanentStorage(context.getApplicationContext());

        store.removeWidgetCurrentData(appWidgetId);
    }

    private RemoteViews makeViewOnError(final int appWidgetId) {
        final RemoteViews view = this.makeErrorView(appWidgetId);

        final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
        store.removeWidgetCurrentData(appWidgetId);

        return view;
    }

    private RemoteViews makeViewOnNotRefresh(final WeatherLocation weatherLocation, final int appWidgetId) {
        RemoteViews view;

        final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
        final Current current = store.getWidgetCurrentData(appWidgetId);
        if (current != null) {
            // Update UI.
            view = this.makeView(current, weatherLocation, appWidgetId);
        } else {
            // Show error.
            view = this.makeErrorView(appWidgetId);

            store.removeWidgetCurrentData(appWidgetId);
        }

        return view;
    }

    private RemoteViews makeViewOnRefresh(final WeatherLocation weatherLocation, final int appWidgetId) {
        RemoteViews view;

        final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
        final Current current = this.getRemoteCurrent(weatherLocation);
        if (current != null) {
            // Update UI.
            view = this.makeView(current, weatherLocation, appWidgetId);

            store.saveWidgetCurrentData(current, appWidgetId);
        } else {
            // Show error.
            view = this.makeErrorView(appWidgetId);

            store.removeWidgetCurrentData(appWidgetId);
        }

        return view;
    }

    private Current getRemoteCurrent(final WeatherLocation weatherLocation) {

        final ServiceCurrentParser weatherService = new ServiceCurrentParser(new JPOSCurrentParser());
        final CustomHTTPClient HTTPClient = new CustomHTTPClient(
                AndroidHttpClient.newInstance(this.getString(R.string.http_client_agent)));

        try {
            return this.getRemoteCurrentThrowable(weatherLocation, HTTPClient, weatherService);

        } catch (final JsonParseException e) {
            Timber.e(e, "doInBackground exception: ");
        } catch (final ClientProtocolException e) {
            Timber.e(e, "doInBackground exception: ");
        } catch (final MalformedURLException e) {
            Timber.e(e, "doInBackground exception: ");
        } catch (final URISyntaxException e) {
            Timber.e(e, "doInBackground exception: ");
        } catch (final IOException e) {
            // logger infrastructure swallows UnknownHostException :/
            Timber.e(e, "doInBackground exception: " + e.getMessage());
        } finally {
            HTTPClient.close();
        }

        return null;
    }

    private Current getRemoteCurrentThrowable(final WeatherLocation weatherLocation,
            final CustomHTTPClient HTTPClient, final ServiceCurrentParser weatherService)
            throws ClientProtocolException, MalformedURLException, URISyntaxException, JsonParseException,
            IOException {
        final SharedPreferences sharedPreferences = PreferenceManager
                .getDefaultSharedPreferences(this.getApplicationContext());
        final String APPID = sharedPreferences.getString(this.getString(R.string.weather_preferences_app_id_key),
                "");

        final String APIVersion = this.getResources().getString(R.string.api_version);

        final String urlAPI = this.getResources().getString(R.string.uri_api_weather_today);
        String url = weatherService.createURIAPICurrent(urlAPI, APIVersion, weatherLocation.getLatitude(),
                weatherLocation.getLongitude());
        if (!APPID.isEmpty()) {
            url = url.concat("&APPID=" + APPID);
        }
        final String jsonData = HTTPClient.retrieveDataAsString(new URL(url));

        return weatherService.retrieveCurrentFromJPOS(jsonData);
    }

    private interface UnitsConversor {

        public double doConversion(final double value);
    }

    private RemoteViews makeView(final Current current, final WeatherLocation weatherLocation,
            final int appWidgetId) {

        // 1. Update units of measurement.

        UnitsConversor tempUnitsConversor;
        String keyPreference = this.getApplicationContext()
                .getString(R.string.widget_preferences_temperature_units_key);
        String realKeyPreference = keyPreference + "_" + appWidgetId;
        // What was saved to permanent storage (or default values if it is the first time)
        final int tempValue = this.getSharedPreferences(WIDGET_PREFERENCES_NAME, Context.MODE_PRIVATE)
                .getInt(realKeyPreference, 0);
        final String tempSymbol = this.getResources()
                .getStringArray(R.array.weather_preferences_temperature)[tempValue];
        if (tempValue == 0) {
            tempUnitsConversor = new UnitsConversor() {

                @Override
                public double doConversion(final double value) {
                    return value - 273.15;
                }

            };
        } else if (tempValue == 1) {
            tempUnitsConversor = new UnitsConversor() {

                @Override
                public double doConversion(final double value) {
                    return (value * 1.8) - 459.67;
                }

            };
        } else {
            tempUnitsConversor = new UnitsConversor() {

                @Override
                public double doConversion(final double value) {
                    return value;
                }

            };
        }

        // 2. Update country.
        keyPreference = this.getApplicationContext().getString(R.string.widget_preferences_country_switch_key);
        realKeyPreference = keyPreference + "_" + appWidgetId;
        // What was saved to permanent storage (or default values if it is the first time)
        final boolean isCountry = this.getSharedPreferences(WIDGET_PREFERENCES_NAME, Context.MODE_PRIVATE)
                .getBoolean(realKeyPreference, false);

        // 3. Formatters
        final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
        tempFormatter.applyPattern("###.##");

        // 4. Prepare data for RemoteViews.
        String tempMax = "";
        if (current.getMain().getTemp_max() != null) {
            double conversion = (Double) current.getMain().getTemp_max();
            conversion = tempUnitsConversor.doConversion(conversion);
            tempMax = tempFormatter.format(conversion) + tempSymbol;
        }
        String tempMin = "";
        if (current.getMain().getTemp_min() != null) {
            double conversion = (Double) current.getMain().getTemp_min();
            conversion = tempUnitsConversor.doConversion(conversion);
            tempMin = tempFormatter.format(conversion) + tempSymbol;
        }
        Bitmap picture;
        if ((current.getWeather().size() > 0) && (current.getWeather().get(0).getIcon() != null)
                && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
            final String icon = current.getWeather().get(0).getIcon();
            picture = BitmapFactory.decodeResource(this.getResources(),
                    IconsList.getIcon(icon).getResourceDrawable());
        } else {
            picture = BitmapFactory.decodeResource(this.getResources(), R.drawable.weather_severe_alert);
        }
        final String city = weatherLocation.getCity();
        final String country = weatherLocation.getCountry();

        // 5. Insert data in RemoteViews.
        final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(),
                R.layout.appwidget);
        remoteView.setImageViewBitmap(R.id.weather_appwidget_image, picture);
        remoteView.setTextViewText(R.id.weather_appwidget_temperature_max, tempMax);
        remoteView.setTextViewText(R.id.weather_appwidget_temperature_min, tempMin);
        remoteView.setTextViewText(R.id.weather_appwidget_city, city);
        if (!isCountry) {
            remoteView.setViewVisibility(R.id.weather_appwidget_country, View.GONE);
        } else {
            // TODO: It is as if Android had a view cache. If I did not set VISIBLE value,
            // the country field would be gone forever... :/
            remoteView.setViewVisibility(R.id.weather_appwidget_country, View.VISIBLE);
            remoteView.setTextViewText(R.id.weather_appwidget_country, country);
        }

        // 6. Activity launcher.
        final Intent resultIntent = new Intent(this.getApplicationContext(), WidgetConfigure.class);
        resultIntent.putExtra("actionFromUser", true);
        resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        // From: http://stackoverflow.com/questions/4011178/multiple-instances-of-widget-only-updating-last-widget
        final Uri data = Uri.withAppendedPath(Uri.parse("PAIN" + "://widget/id/"), String.valueOf(appWidgetId));
        resultIntent.setData(data);

        final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
        // Adds the back stack for the Intent (but not the Intent itself)
        stackBuilder.addParentStack(WidgetConfigure.class);
        // Adds the Intent that starts the Activity to the top of the stack
        stackBuilder.addNextIntent(resultIntent);
        final PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
                PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);

        return remoteView;
    }

    private RemoteViews makeErrorView(final int appWidgetId) {
        final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(),
                R.layout.appwidget_error);

        // 6. Activity launcher.
        final Intent resultIntent = new Intent(this.getApplicationContext(), WidgetConfigure.class);
        resultIntent.putExtra("actionFromUser", true);
        resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        // From: http://stackoverflow.com/questions/4011178/multiple-instances-of-widget-only-updating-last-widget
        final Uri data = Uri.withAppendedPath(Uri.parse("PAIN" + "://widget/id/"), String.valueOf(appWidgetId));
        resultIntent.setData(data);

        final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
        // Adds the back stack for the Intent (but not the Intent itself)
        stackBuilder.addParentStack(WidgetConfigure.class);
        // Adds the Intent that starts the Activity to the top of the stack
        stackBuilder.addNextIntent(resultIntent);
        final PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
                PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.weather_appwidget_error, resultPendingIntent);

        return remoteView;
    }

    private void updateWidget(final RemoteViews remoteView, final int appWidgetId) {

        final AppWidgetManager manager = AppWidgetManager.getInstance(this.getApplicationContext());
        manager.updateAppWidget(appWidgetId, remoteView);
    }
}