public class SupportUtils {

    /**
     * Shared preferences key to store support action
     */
    public static final String SUPPORT_SHARED_KEY = "shared_preferences_support";

    /**
     * Shared preferences value for fresh install
     */
    public static final int SUPPORT_UNSET = 0x00000000;

    /**
     * Shared preferences value for supporter
     */
    public static final int SUPPORT_DONATE = 0x00000001;

    /**
     * Shared preferences value for future supporter
     */
    public static final int SUPPORT_NOT_YET = 0x00000002;

    /**
     * id for espresso product
     */
    public static final String SKU_ESPRESSO = "espresso";

    /**
     * id for cappuccino product
     */
    public static final String SKU_CAPPUCCINO = "cappuccino";

    /**
     * id for iced coffee product
     */
    public static final String SKU_ICED_COFFEE = "iced_coffee";

    /**
     * id for earl grey product
     */
    public static final String SKU_EARL_GREY = "earl_grey";

    /**
     * arbitrary request code for the purchase flow
     */
    public static final int REQUEST_CODE_SUPPORT_DEV = 42;

    /**
     * state for a purchased purchase
     * http://developer.android.com/google/play/billing/v2/billing_reference.html
     */
    public static final int SUPPORT_STATE_PURCHASED = 0;

    /**
     * check if user has already supported us
     *
     * @param i user inventory
     * @return true if already purchased
     */
    private static boolean hasPurchased(final Inventory i) {
        if (i.hasPurchase(SKU_ESPRESSO) && i.getPurchase(SKU_ESPRESSO).getPurchaseState() == SUPPORT_STATE_PURCHASED) {
            return true;
        }

        if (i.hasPurchase(SKU_CAPPUCCINO) && i.getPurchase(SKU_CAPPUCCINO).getPurchaseState() == SUPPORT_STATE_PURCHASED) {
            return true;
        }

        if (i.hasPurchase(SKU_ICED_COFFEE) && i.getPurchase(SKU_ICED_COFFEE).getPurchaseState() == SUPPORT_STATE_PURCHASED) {
            return true;
        }

        if (i.hasPurchase(SKU_EARL_GREY) && i.getPurchase(SKU_EARL_GREY).getPurchaseState() == SUPPORT_STATE_PURCHASED) {
            return true;
        }

        return false;
    }


    /**
     * Check is a user is supporting us.
     *
     * @param context  a {@link android.content.Context}
     * @param listener a {@link fr.tvbarthel.apps.billing.utils.SupportUtils.OnCheckSupportListener} used to deliver the response.
     */
    public static void checkSupport(Context context, final OnCheckSupportListener listener) {
        final SharedPreferences sharedPreferences =
                PreferenceManager.getDefaultSharedPreferences(context);
        final int donateState = sharedPreferences.getInt(SupportUtils.SUPPORT_SHARED_KEY,
                SupportUtils.SUPPORT_UNSET);
        switch (donateState) {
            case SupportUtils.SUPPORT_UNSET:
                //retrieve info
                final IabHelper helper = new IabHelper(context,
                        context.getResources().getString(R.string.support_key));
                helper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                    @Override
                    public void onIabSetupFinished(IabResult result) {
                        if (result.isSuccess()) {
                            helper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
                                @Override
                                public void onQueryInventoryFinished(IabResult result, Inventory inv) {
                                    if (result.isSuccess()) {
                                        if (SupportUtils.hasPurchased(inv)) {
                                            if(listener != null) {
                                                listener.onCheckSupport(true);
                                            }

                                            //save it
                                            final SharedPreferences.Editor editor = sharedPreferences.edit();
                                            editor.putInt(SupportUtils.SUPPORT_SHARED_KEY, SupportUtils.SUPPORT_DONATE);
                                            editor.commit();

                                            //release resources
                                            helper.dispose();
                                        }
                                    }
                                }
                            });
                        }
                    }
                });
                break;
            case SupportUtils.SUPPORT_DONATE:
                //user already support us
                if(listener != null) {
                    listener.onCheckSupport(true);
                }
                break;
            case SupportUtils.SUPPORT_NOT_YET:
                if(listener != null) {
                    listener.onCheckSupport(false);
                }
                break;
            default:
                if(listener != null) {
                    listener.onCheckSupport(false);
                }
        }
    }

    public static interface OnCheckSupportListener {
        /**
         * Called after we know if the user is supporting us or not.
         *
         * @param supporting true if the user is supporting us, false otherwise.
         */
        void onCheckSupport(boolean supporting);
    }

} public class AboutDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        final LayoutInflater inflater = getActivity().getLayoutInflater();
        final View dialogView = inflater.inflate(R.layout.dialog_about, null);
        final TextView textViewContent = (TextView) dialogView.findViewById(R.id.dialog_about_content);
        if (textViewContent != null) {
            textViewContent.setMovementMethod(LinkMovementMethod.getInstance());
            Linkify.addLinks(textViewContent, Linkify.WEB_URLS);
        }

        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setCancelable(true)
                .setPositiveButton(android.R.string.ok, null)
                .setTitle(R.string.dialog_about_title)
                .setView(dialogView)
                .setInverseBackgroundForced(true);

        return builder.create();
    }
} public class ForecastFragment extends Fragment {

    public static String ARGUMENT_MODEL = "ForecastFragment.DailyForecastModel";
    public static String ARGUMENT_TEMPERATURE_UNIT = "ForecastFragment.TemperatureUnit";

    public static ForecastFragment newInstance(DailyForecastModel model, String temperatureUnit) {
        final ForecastFragment instance = new ForecastFragment();
        final Bundle arguments = new Bundle();
        arguments.putParcelable(ARGUMENT_MODEL, model);
        arguments.putString(ARGUMENT_TEMPERATURE_UNIT, temperatureUnit);
        instance.setArguments(arguments);
        return instance;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View v = inflater.inflate(R.layout.fragment_forecast, container, false);
        final Bundle arguments = getArguments();
        final DailyForecastModel dailyForecastModel = arguments.getParcelable(ARGUMENT_MODEL);
        final String temperatureUnit = arguments.getString(ARGUMENT_TEMPERATURE_UNIT);
        if (dailyForecastModel != null) {
            final long temperature = TemperatureUtils.convertTemperature(getActivity(),
                    dailyForecastModel.getTemperature(), temperatureUnit);
            final long minTemperature = TemperatureUtils.convertTemperature(getActivity(),
                    dailyForecastModel.getMinTemperature(), temperatureUnit);
            final long maxTemperature = TemperatureUtils.convertTemperature(getActivity(),
                    dailyForecastModel.getMaxTemperature(), temperatureUnit);

            ((TextView) v.findViewById(R.id.fragment_forecast_day_temperature))
                    .setText(temperature + temperatureUnit);

            ((TextView) v.findViewById(R.id.fragment_forecast_min_max)).setText(getString(R.string.forecast_fragment_min_max_temperature, minTemperature, maxTemperature));

            ((TextView) v.findViewById(R.id.fragment_forecast_weather_description))
                    .setText(dailyForecastModel.getDescription());
        }
        return v;
    }


} public class LicenseDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final LayoutInflater inflater = getActivity().getLayoutInflater();

        final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
        final View dialogView = inflater.inflate(R.layout.dialog_license, null);

        if (dialogView != null) {
            final TextView textViewContent = (TextView) dialogView.findViewById(R.id.dialog_license_content);
            if (textViewContent != null) {
                textViewContent.setMovementMethod(LinkMovementMethod.getInstance());
                Linkify.addLinks(textViewContent, Linkify.WEB_URLS);
            }
        }

        dialogBuilder.setCancelable(true)
                .setView(dialogView)
                .setPositiveButton(android.R.string.ok, null)
                .setTitle(R.string.dialog_license_title)
                .setInverseBackgroundForced(true);

        return dialogBuilder.create();
    }
} public class MoreAppsDialogFragment extends DialogFragment {

    private static final String URI_ROOT_MARKET = "market://details?id=";
    private static final String URI_ROOT_PLAY_STORE = "http://play.google.com/store/apps/details?id=";

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final LayoutInflater inflater = getActivity().getLayoutInflater();
        final ListView listView = (ListView) inflater.inflate(R.layout.dialog_more_apps, null);

        final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
        dialogBuilder.setPositiveButton(android.R.string.ok, null);
        dialogBuilder.setTitle(R.string.dialog_more_apps_title);
        dialogBuilder.setView(listView);
        dialogBuilder.setInverseBackgroundForced(true);

        App chaseWhisply = new App();
        chaseWhisply.setLogoResourceId(R.drawable.ic_chase_whisply);
        chaseWhisply.setNameResourceId(R.string.dialog_more_apps_chase_whisply_app_name);
        chaseWhisply.setPackageNameResourceId(R.string.dialog_more_apps_chase_whisply_package_name);

        App googlyZoo = new App();
        googlyZoo.setLogoResourceId(R.drawable.ic_googly_zoo);
        googlyZoo.setNameResourceId(R.string.dialog_more_apps_googly_zoo_app_name);
        googlyZoo.setPackageNameResourceId(R.string.dialog_more_apps_googly_zoo_package_name);

        App simpleThermometer = new App();
        simpleThermometer.setLogoResourceId(R.drawable.ic_simple_thermometer);
        simpleThermometer.setNameResourceId(R.string.dialog_more_apps_simplethermometer_app_name);
        simpleThermometer.setPackageNameResourceId(R.string.dialog_more_apps_simplethermometer_package_name);

        final ArrayList<App> apps = new ArrayList<App>();
        apps.add(chaseWhisply);
        apps.add(googlyZoo);
        apps.add(simpleThermometer);

        listView.setAdapter(new MoreAppsAdapter(getActivity(), apps));
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                App appClicked = apps.get(position);
                launchPlayStoreDetails(getResources().getString(appClicked.getPackageNameResourceId()));
            }
        });

        return dialogBuilder.create();
    }

    private void launchPlayStoreDetails(String appPackageName) {
        try {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(URI_ROOT_MARKET + appPackageName)));
        } catch (android.content.ActivityNotFoundException activityNotFoundException) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(URI_ROOT_PLAY_STORE + appPackageName)));
        }
    }
} public class TemperatureUnitPickerDialogFragment extends DialogFragment {

    private static final String BUNDLE_TEMPERATURE_UNIT_NAMES = "BundleTemperatureUnitNames";
    private static final String BUNDLE_TEMPERATURE_UNIT_SYMBOLS = "BundleTemperatureUnitSymbols";

    public static TemperatureUnitPickerDialogFragment newInstance(String[] temperatureUnitNames, String[] temperatureUnitSymbols) {
        final TemperatureUnitPickerDialogFragment instance = new TemperatureUnitPickerDialogFragment();
        final Bundle arguments = new Bundle();
        arguments.putStringArray(BUNDLE_TEMPERATURE_UNIT_NAMES, temperatureUnitNames);
        arguments.putStringArray(BUNDLE_TEMPERATURE_UNIT_SYMBOLS, temperatureUnitSymbols);
        instance.setArguments(arguments);
        return instance;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final Bundle arguments = getArguments();

        final String[] temperatureUnitNames = arguments.getStringArray(BUNDLE_TEMPERATURE_UNIT_NAMES);
        final String[] temperatureUnitSymbols = arguments.getStringArray(BUNDLE_TEMPERATURE_UNIT_SYMBOLS);

        final ArrayAdapter<String> temperatureUnitNameAdapter = new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_expandable_list_item_1, temperatureUnitNames);

        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle(R.string.dialog_temperature_unit_picker_title);
        builder.setAdapter(temperatureUnitNameAdapter, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                SharedPreferenceUtils.storeTemperatureUnitSymbol(getActivity(), temperatureUnitSymbols[which]);
            }
        });
        builder.setCancelable(true);
        builder.setNegativeButton(android.R.string.cancel, null);
        builder.setInverseBackgroundForced(true);
        return builder.create();
    }
} public class DailyForecastJsonParser extends AsyncTask<String, Void, ArrayList<DailyForecastModel>> {

    private static final String TAG_WEATHER_LIST = "list";
    private static final String TAG_HUMIDITY = "humidity";
    private static final String TAG_WEATHER = "weather";
    private static final String TAG_WEATHER_DESCRIPTION = "description";
    private static final String TAG_TEMPERATURE = "temp";
    private static final String TAG_TEMPERATURE_DAY = "day";
    private static final String TAG_TEMPERATURE_MIN = "min";
    private static final String TAG_TEMPERATURE_MAX = "max";
    private static final String TAG_DATE_TIME = "dt";

    @Override
    protected ArrayList<DailyForecastModel> doInBackground(String... params) {
        return parse(params[0]);
    }

    public static ArrayList<DailyForecastModel> parse(final String json) {
        final ArrayList<DailyForecastModel> result = new ArrayList<DailyForecastModel>();
        if (json != null) {
            try {
                JSONObject root = new JSONObject(json);
                JSONArray weatherList = root.getJSONArray(TAG_WEATHER_LIST);
                for (int i = 0; i < weatherList.length(); i++) {
                    result.add(parseDailyForecast(weatherList.getJSONObject(i)));
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    private static DailyForecastModel parseDailyForecast(JSONObject jsonDailyForecast) {
        final DailyForecastModel model = new DailyForecastModel();
        model.setDateTime(parseDateTime(jsonDailyForecast));
        model.setHumidity(parseHumidity(jsonDailyForecast));
        model.setDescription(parseWeatherDescription(jsonDailyForecast));
        parseTemperature(model, jsonDailyForecast);
        return model;
    }

    private static void parseTemperature(DailyForecastModel model, JSONObject dailyForecast) {
        try {
            JSONObject jsonTemperature = dailyForecast.getJSONObject(TAG_TEMPERATURE);
            model.setTemperature(parseDayTemperature(jsonTemperature));
            model.setMinTemperature(parseMinTemperature(jsonTemperature));
            model.setMaxTemperature(parseMaxTemperature(jsonTemperature));
        } catch (JSONException exception) {
            exception.printStackTrace();
            model.setTemperature(0d);
            model.setMinTemperature(0d);
            model.setMaxTemperature(0d);
        }
    }

    private static Double parseDayTemperature(JSONObject temperature) {
        return parseDouble(temperature, TAG_TEMPERATURE_DAY, 0d);
    }

    private static Double parseMinTemperature(JSONObject temperature) {
        return parseDouble(temperature, TAG_TEMPERATURE_MIN, 0d);
    }

    private static Double parseMaxTemperature(JSONObject temperature) {
        return parseDouble(temperature, TAG_TEMPERATURE_MAX, 0d);
    }


    private static Double parseDouble(JSONObject object, String tag, Double defaultValue) {
        try {
            return object.getDouble(tag);
        } catch (JSONException exception) {
            exception.printStackTrace();
            return defaultValue;
        }
    }

    private static String parseWeatherDescription(JSONObject dailyForecast) {
        String weatherDescription = "";
        try {
            weatherDescription = dailyForecast.getJSONArray(TAG_WEATHER).getJSONObject(0).getString(TAG_WEATHER_DESCRIPTION);
        } catch (JSONException exception) {
            exception.printStackTrace();
        }
        return weatherDescription;
    }

    private static int parseHumidity(JSONObject dailyForecast) {
        int humidity = 0;
        try {
            humidity = dailyForecast.getInt(TAG_HUMIDITY);
        } catch (JSONException exception) {
            exception.printStackTrace();
        }
        return humidity;
    }

    private static long parseDateTime(JSONObject dailyForecast) {
        long dateTime = 0;
        try {
            dateTime = dailyForecast.getLong(TAG_DATE_TIME);
        } catch (JSONException exception) {
            exception.printStackTrace();
        }
        return dateTime;
    }

} public class DailyForecastModel implements Parcelable {

    private long mDateTime;
    private String mDescription;
    private Double mTemperature;
    private Double mMinTemperature;
    private Double mMaxTemperature;
    private int mHumidity;

    public DailyForecastModel() {
    }

    public DailyForecastModel(Parcel in) {
        readFromParcel(in);
    }

    public String getDescription() {
        return mDescription;
    }

    public void setDescription(String description) {
        mDescription = description;
    }

    public int getHumidity() {
        return mHumidity;
    }

    public void setHumidity(int humidity) {
        mHumidity = humidity;
    }

    public long getDateTime() {
        return mDateTime;
    }

    public void setDateTime(long dateTime) {
        mDateTime = dateTime;
    }

    public Double getTemperature() {
        return mTemperature;
    }

    public void setTemperature(Double temperature) {
        mTemperature = temperature;
    }

    public Double getMinTemperature() {
        return mMinTemperature;
    }

    public void setMinTemperature(Double minTemperature) {
        mMinTemperature = minTemperature;
    }

    public Double getMaxTemperature() {
        return mMaxTemperature;
    }

    public void setMaxTemperature(Double maxTemperature) {
        mMaxTemperature = maxTemperature;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(mDateTime);
        dest.writeString(mDescription);
        dest.writeInt(mHumidity);
        dest.writeDouble(mTemperature);
        dest.writeDouble(mMinTemperature);
        dest.writeDouble(mMaxTemperature);
    }

    private void readFromParcel(Parcel in) {
        mDateTime = in.readLong();
        mDescription = in.readString();
        mHumidity = in.readInt();
        mTemperature = in.readDouble();
        mMinTemperature = in.readDouble();
        mMaxTemperature = in.readDouble();
    }

    public static final Parcelable.Creator<DailyForecastModel> CREATOR = new Parcelable.Creator<DailyForecastModel>() {
        @Override
        public DailyForecastModel createFromParcel(Parcel source) {
            return new DailyForecastModel(source);
        }

        @Override
        public DailyForecastModel[] newArray(int size) {
            return new DailyForecastModel[size];
        }
    };


} @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class WeatherWidgetReceiver extends AppWidgetProvider {

    //An intent Action used to notify a data change: text color, temperature value
    // temperature unit etc.
    public static final String APPWIDGET_DATA_CHANGED =
            "fr.tvbarthel.apps.simpleweatherforcast.receivers.WeatherWidgetReceiver.DataChanged";

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        if (SharedPreferenceUtils.isWeatherOutdated(context, false)) {
            DailyForecastUpdateService.startForUpdate(context);
        } else {
            updateWidget(context, appWidgetIds);
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        final String action = intent.getAction();
        if (APPWIDGET_DATA_CHANGED.equals(action)) {
            notifyWidgetDataChanged(context);
        } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)
                && ConnectivityUtils.isConnected(context)
                && SharedPreferenceUtils.isWeatherOutdated(context, false)) {
            DailyForecastUpdateService.startForUpdate(context);
        }
    }


    private RemoteViews buildLayout(Context context, int appWidgetId) {
        // Specify the service to provide data for the collection widget.  Note that we need to
        // embed the appWidgetId via the data otherwise it will be ignored.
        final Intent intent = new Intent(context, AppWidgetService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        final RemoteViews remoteViews;
        remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget);
        remoteViews.setRemoteAdapter(appWidgetId, R.id.app_widget_list_view, intent);
        remoteViews.setEmptyView(R.id.app_widget_list_view, R.id.app_widget_empty_view);

        // Bind a click listener template for the contents of the weather list.
        final Intent onClickIntent = new Intent(context, MainActivity.class);
        final PendingIntent onClickPendingIntent = PendingIntent.getActivity(context, 0, onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setPendingIntentTemplate(R.id.app_widget_list_view, onClickPendingIntent);

        return remoteViews;
    }

    private void notifyWidgetDataChanged(Context context) {
        final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        final ComponentName thisWidget = new ComponentName(context, WeatherWidgetReceiver.class);
        final int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
        for (int appWidgetId : appWidgetIds) {
            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.app_widget_list_view);
        }
    }

    private void updateWidget(Context context, int[] appWidgetIds) {
        final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        // Update each of the widgets with the remote adapter
        for (int appWidgetId : appWidgetIds) {
            RemoteViews layout = buildLayout(context, appWidgetId);
            appWidgetManager.updateAppWidget(appWidgetId, layout);
        }
    }
} public class DailyForecastUpdateService extends Service implements LocationListener {

    // update action
    public static final String ACTION_UPDATE = "DailyForecastUpdateService.Actions.Update";
    // update error action
    public static final String ACTION_UPDATE_ERROR = "DailyForecastUpdateService.Actions.UpdateError";
    // update error extra
    public static final String EXTRA_UPDATE_ERROR = "DailyForecastUpdateService.Extra.UpdateError";

    private LocationManager mLocationManager;
    private Looper mServiceLooper;
    private Handler mServiceHandler;
    private Boolean mIsUpdatingTemperature;

    public static void startForUpdate(Context context) {
        final Intent intent = new Intent(context, DailyForecastUpdateService.class);
        intent.setAction(ACTION_UPDATE);
        context.startService(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        HandlerThread thread = new HandlerThread("DailyForecastUpdateService", android.os.Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new Handler(mServiceLooper);
        mIsUpdatingTemperature = false;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // We don't support binding.
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null && ACTION_UPDATE.equals(intent.getAction()) && !mIsUpdatingTemperature) {
            if (ConnectivityUtils.isConnected(getApplicationContext())) {
                mIsUpdatingTemperature = true;
                // First get a new location
                getNewLocation();
            } else {
                broadcastErrorAndStop(R.string.toast_no_connection_available);
            }
        }
        return START_STICKY;
    }

    private void updateForecast(final Location newLocation) {
        mServiceHandler.post(new Runnable() {
            @Override
            public void run() {
                try {
                    final String JSON = getForecastAsJSON(newLocation);
                    SharedPreferenceUtils.storeWeather(DailyForecastUpdateService.this, JSON);

                    // Broadcast change to the widget provider
                    Intent intent = new Intent(DailyForecastUpdateService.this, WeatherWidgetReceiver.class);
                    intent.setAction(WeatherWidgetReceiver.APPWIDGET_DATA_CHANGED);
                    sendBroadcast(intent);
                    mIsUpdatingTemperature = false;
                    DailyForecastUpdateService.this.stopSelf();
                } catch (SocketTimeoutException e) {
                    broadcastErrorAndStop(R.string.error_message_server_not_available);
                } catch (MalformedURLException e) {
                    broadcastErrorAndStop(R.string.error_message_malformed_url);
                } catch (IOException e) {
                    broadcastErrorAndStop(R.string.error_message_io_exception);
                }
            }
        });
    }

    private void getNewLocation() {
        final String provider = LocationUtils.getBestCoarseProvider(this);
        if (provider == null) {
            broadcastErrorAndStop(R.string.toast_not_allowed_to_access_location);
        } else {
            mLocationManager.requestSingleUpdate(provider, this, null);
        }
    }

    private void broadcastErrorAndStop(int resourceId) {
        final String message = getString(resourceId);
        final Intent intent = new Intent(ACTION_UPDATE_ERROR);
        intent.putExtra(EXTRA_UPDATE_ERROR, message);
        sendBroadcast(intent);
        stopSelf();
    }

    private String getForecastAsJSON(Location location) throws IOException {
        final String lang = Locale.getDefault().getLanguage();
        //Forge the url for the open weather map API.
        final String url = getString(R.string.url_open_weather_map_api, lang, location.getLatitude(), location.getLongitude());
        return URLUtils.getAsString(url);
    }

    @Override
    public void onLocationChanged(Location location) {
        updateForecast(location);
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }
} public class AlphaForegroundColorSpan extends ForegroundColorSpan {

    private float mAlpha;

    public AlphaForegroundColorSpan(int color) {
        super(color);
    }

    public AlphaForegroundColorSpan(Parcel src) {
        super(src);
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        ds.setColor(getAlphaColor());
    }

    public void setAlpha(float alpha) {
        mAlpha = alpha;
    }

    public float getAlpha() {
        return mAlpha;
    }

    private int getAlphaColor() {
        int foregroundColor = getForegroundColor();
        return Color.argb((int) (mAlpha * 255), Color.red(foregroundColor), Color.green(foregroundColor),
                Color.blue(foregroundColor));
    }
} public class SharedPreferenceUtils {

    public static final long REFRESH_TIME_AUTO = 1000 * 60 * 60 * 2; // 2 hours in millis.
    public static final long REFRESH_TIME_MANUAL = 1000 * 60 * 10; // 10 minutes.

    public static String KEY_LAST_UPDATE = "SharedPreferenceUtils.Key.LastUpdate";
    public static String KEY_LAST_KNOWN_JSON_WEATHER = "SharedPreferenceUtils.Key.LastKnownJsonWeather";
    public static String KEY_TEMPERATURE_UNIT_SYMBOL = "SharedPreferenceUtils.Key.TemperatureUnitSymbol";

    private static SharedPreferences getDefaultSharedPreferences(final Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context);
    }

    public static String getLastKnownWeather(final Context context) {
        return getDefaultSharedPreferences(context).getString(KEY_LAST_KNOWN_JSON_WEATHER, null);
    }

    public static long getLastUpdate(final Context context) {
        return getDefaultSharedPreferences(context).getLong(KEY_LAST_UPDATE, 0);
    }

    public static void storeWeather(final Context context, final String jsonWeather) {
        final Editor editor = getDefaultSharedPreferences(context).edit();
        editor.putString(KEY_LAST_KNOWN_JSON_WEATHER, jsonWeather);
        editor.putLong(KEY_LAST_UPDATE, System.currentTimeMillis());
        editor.apply();
    }

    public static void storeTemperatureUnitSymbol(final Context context, final String unitSymbol) {
        final Editor editor = getDefaultSharedPreferences(context).edit();
        editor.putString(KEY_TEMPERATURE_UNIT_SYMBOL, unitSymbol);
        editor.apply();
    }

    public static String getTemperatureUnitSymbol(final Context context) {
        return getDefaultSharedPreferences(context).getString(KEY_TEMPERATURE_UNIT_SYMBOL,
                context.getString(R.string.temperature_unit_celsius_symbol));
    }

    public static void registerOnSharedPreferenceChangeListener(final Context context, SharedPreferences.OnSharedPreferenceChangeListener listener) {
        getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(listener);
    }

    public static void unregisterOnSharedPreferenceChangeListener(final Context context, SharedPreferences.OnSharedPreferenceChangeListener listener) {
        getDefaultSharedPreferences(context).unregisterOnSharedPreferenceChangeListener(listener);
    }

    public static boolean isWeatherOutdated(Context context, boolean isManualRefresh) {
        final long refreshTimeInMillis = isManualRefresh ? REFRESH_TIME_MANUAL : REFRESH_TIME_AUTO;
        final long lastUpdate = SharedPreferenceUtils.getLastUpdate(context);
        return System.currentTimeMillis() - lastUpdate > refreshTimeInMillis;
    }

}
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.PageTransformer;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.TypefaceSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.nineoldandroids.view.ViewHelper;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Locale;
import fr.tvbarthel.apps.billing.utils.SupportUtils;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.AboutDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.ForecastFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.LicenseDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.MoreAppsDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.fragments.TemperatureUnitPickerDialogFragment;
import fr.tvbarthel.apps.simpleweatherforcast.openweathermap.DailyForecastJsonParser;
import fr.tvbarthel.apps.simpleweatherforcast.openweathermap.DailyForecastModel;
import fr.tvbarthel.apps.simpleweatherforcast.receivers.WeatherWidgetReceiver;
import fr.tvbarthel.apps.simpleweatherforcast.services.DailyForecastUpdateService;
import fr.tvbarthel.apps.simpleweatherforcast.ui.AlphaForegroundColorSpan;
import fr.tvbarthel.apps.simpleweatherforcast.utils.SharedPreferenceUtils;
                    final Intent intent = getIntent();
                    if (intent != null && intent.hasExtra(EXTRA_PAGE_POSITION)) {
                        final int position = intent.getIntExtra(EXTRA_PAGE_POSITION, 0);
                        mViewPager.setCurrentItem(position);
                        intent.removeExtra(EXTRA_PAGE_POSITION);
                    } else {
                        invalidatePageTransformer();
                    }
                }
            }.execute(jsonDailyForecast);
        }
    }

    /**
     * Trick to notify the pageTransformer of a data set change.
     */
    private void invalidatePageTransformer() {
        if (mViewPager.getAdapter().getCount() > 0) {
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    if (mViewPager.beginFakeDrag()) {
                        mViewPager.fakeDragBy(0f);
                        mViewPager.endFakeDrag();
                    }
                }
            });
        }
    }

    @SuppressLint("NewApi")
    private void setGradientBackgroundColor(int currentPosition, float positionOffset) {
        final GradientDrawable g = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
                new int[]{getColor(currentPosition, positionOffset), getColor(currentPosition, (float) Math.pow(positionOffset, 0.40))});
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
            mRootView.setBackgroundDrawable(g);
        } else {
            mRootView.setBackground(g);
        }
    }

    private void setActionBarAlpha(float alpha) {
        mAlphaForegroundColorSpan.setAlpha(alpha);
        mActionBarSpannableTitle.setSpan(mAlphaForegroundColorSpan, 0, mActionBarSpannableTitle.length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        getSupportActionBar().setTitle(mActionBarSpannableTitle);
    }

    private void updateActionBarTitle(int currentPosition, float positionOffset) {
        float alpha = 1 - positionOffset * 2;
        if (positionOffset >= 0.5) {
            currentPosition++;
            alpha = (positionOffset - 0.5f) * 2;
        }
        setActionBarTitle(currentPosition);
        setActionBarAlpha(alpha);
    }

    private void setActionBarTitle(int position) {
        String newTitle = getActionBarTitle(position);
        if (mActionBarSpannableTitle == null || !mActionBarSpannableTitle.toString().equals(newTitle)) {
            mActionBarSpannableTitle = new SpannableString(newTitle);
            mActionBarSpannableTitle.setSpan(mTypefaceSpanLight, 0, mActionBarSpannableTitle.length(),
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

    private String getActionBarTitle(int currentPosition) {
        final DailyForecastModel currentModel = mSectionsPagerAdapter.getModel(currentPosition);
        return mActionBarTitleDateFormat.format(currentModel.getDateTime() * 1000);
    }


    private int getColor(int currentPosition, float positionOffset) {
        //retrieve current color and next color relative to the current position.
        final int colorSize = mBackgroundColors.length;
        final int currentColor = mBackgroundColors[(currentPosition) % colorSize];
        final int nextColor = mBackgroundColors[(currentPosition + 1) % colorSize];

        //Compute the deltas relative to the current position offset.
        final int deltaR = (int) ((Color.red(nextColor) - Color.red(currentColor)) * positionOffset);
        final int deltaG = (int) ((Color.green(nextColor) - Color.green(currentColor)) * positionOffset);
        final int deltaB = (int) ((Color.blue(nextColor) - Color.blue(currentColor)) * positionOffset);

        return Color.argb(255, Color.red(currentColor) + deltaR, Color.green(currentColor) + deltaG, Color.blue(currentColor) + deltaB);
    }


    /**
     * A {@link FragmentStatePagerAdapter} used for {@link DailyForecastModel}
     */
    public class ForecastPagerAdapter extends FragmentStatePagerAdapter {

        private final ArrayList<DailyForecastModel> mDailyForecastModels;

        public ForecastPagerAdapter(FragmentManager fm) {
            super(fm);
            mDailyForecastModels = new ArrayList<DailyForecastModel>();
        }

        public void updateModels(ArrayList<DailyForecastModel> newModels) {
            mDailyForecastModels.clear();
            mDailyForecastModels.addAll(newModels);
            notifyDataSetChanged();
        }

        public void clear() {
            mDailyForecastModels.clear();
            notifyDataSetChanged();
        }

        public DailyForecastModel getModel(int position) {
            if (position >= mDailyForecastModels.size()) {
                return new DailyForecastModel();
            }
            return mDailyForecastModels.get(position);
        }

        @Override
        public Fragment getItem(int position) {
            return ForecastFragment.newInstance(mDailyForecastModels.get(position), mTemperatureUnit);