net.naonedbus.appwidget.HoraireWidgetProvider.java Source code

Java tutorial

Introduction

Here is the source code for net.naonedbus.appwidget.HoraireWidgetProvider.java

Source

/**
 * Copyright (C) 2013 Romain Guefveneu.
 *   
 *  This file is part of naonedbus.
 *   
 *  Naonedbus is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  Naonedbus is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.naonedbus.appwidget;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import net.naonedbus.BuildConfig;
import net.naonedbus.R;
import net.naonedbus.activity.impl.HorairesActivity;
import net.naonedbus.activity.impl.MainActivity;
import net.naonedbus.activity.widgetconfigure.WidgetConfigureActivity;
import net.naonedbus.bean.Arret;
import net.naonedbus.bean.Favori;
import net.naonedbus.bean.NextHoraireTask;
import net.naonedbus.bean.horaire.Horaire;
import net.naonedbus.intent.ParamIntent;
import net.naonedbus.manager.impl.FavorisViewManager;
import net.naonedbus.manager.impl.HoraireManager;
import net.naonedbus.utils.ColorUtils;
import net.naonedbus.utils.FormatUtils;

import org.joda.time.DateMidnight;
import org.joda.time.DateTime;

import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.TaskStackBuilder;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.View.MeasureSpec;
import android.widget.RemoteViews;
import android.widget.TextView;

/**
 * @author romain.guefveneu
 * 
 */
public abstract class HoraireWidgetProvider extends AppWidgetProvider {

    private static final String LOG_TAG = "HoraireWidgetProvider";
    private static final boolean DBG = BuildConfig.DEBUG;

    private static final String ACTION_APPWIDGET_UPDATE = "net.naonedbus.action.APPWIDGET_UPDATE";
    private static final String ACTION_APPWIDGET_ON_CLICK = "net.naonedbus.action.APPWIDGET_ON_CLICK";

    private static PendingIntent sPendingIntent;
    private static long sNextTimestamp = Long.MAX_VALUE;
    private static Integer sHoraireTextWidth = Integer.MIN_VALUE;
    private static Object sLock = new Object();

    private final int mLayoutId;
    private final int mHoraireLimitRes;
    private int mHoraireLimit = -1;

    protected HoraireWidgetProvider(final int layoutId, final int horaireLimitRes) {
        mLayoutId = layoutId;
        mHoraireLimitRes = horaireLimitRes;
    }

    @Override
    public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds) {
        final int count = appWidgetIds.length;

        if (DBG)
            Log.i(LOG_TAG, Integer.toHexString(hashCode()) + " - " + "onUpdate");

        final Thread update = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < count; i++) {
                    updateAppWidget(context, appWidgetManager, appWidgetIds[i]);
                }
            }
        });
        update.start();
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onAppWidgetOptionsChanged(final Context context, final AppWidgetManager appWidgetManager,
            final int appWidgetId, final Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
        updateAppWidget(context, appWidgetManager, appWidgetId);
    }

    /**
     * Programmer le rafraichissement des widgets.
     * 
     * @param context
     * @param timestamp
     */
    private synchronized void scheduleUpdate(final Context context, final long timestamp) {
        final long now = new DateTime().getMillis();
        final long triggerTimestamp = new DateTime(timestamp).plusMinutes(1).getMillis();

        if (sNextTimestamp < now || triggerTimestamp < sNextTimestamp) {
            sNextTimestamp = triggerTimestamp;

            final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            if (sPendingIntent != null) {
                m.cancel(sPendingIntent);

                if (DBG)
                    Log.i(LOG_TAG,
                            Integer.toHexString(hashCode()) + " - " + "\tAnnulation de la prcdente alarme.");
            }

            final Intent intent = new Intent(ACTION_APPWIDGET_UPDATE);
            sPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
            m.set(AlarmManager.RTC, sNextTimestamp, sPendingIntent);

            if (DBG)
                Log.i(LOG_TAG, Integer.toHexString(hashCode()) + " - " + "Programmation de l'alarme  : "
                        + new DateTime(sNextTimestamp).toString());
        }

    }

    /**
     * Update the widget.
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager,
            final int appWidgetId) {
        final RemoteViews views = new RemoteViews(context.getPackageName(), this.mLayoutId);
        final int idFavori = WidgetConfigureActivity.getFavoriIdFromWidget(context, appWidgetId);
        final Favori favori = FavorisViewManager.getInstance().getSingle(context.getContentResolver(), idFavori);

        if (DBG)
            Log.i(LOG_TAG, Integer.toHexString(hashCode()) + " - " + appWidgetId + " - updateAppWidget " + favori);

        // Initialisation du nombre d'horaires
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            final Bundle bundle = appWidgetManager.getAppWidgetOptions(appWidgetId);
            mHoraireLimit = getHorairesCount(context, bundle);
        } else {
            if (mHoraireLimit == -1) {
                mHoraireLimit = context.getResources().getInteger(mHoraireLimitRes);
            }
        }

        if (favori != null) {
            prepareWidgetView(context, views, favori, appWidgetId);
            appWidgetManager.updateAppWidget(appWidgetId, views);
        } else {
            WidgetConfigureActivity.removeWidgetId(context, appWidgetId);
        }
    }

    /**
     * Prparer le widget, le mettre  jour si ncessaire.
     */
    protected void prepareWidgetView(final Context context, final RemoteViews views, final Favori favori,
            final Integer appWidgetId) {
        if (DBG)
            Log.d(LOG_TAG,
                    Integer.toHexString(hashCode()) + " - " + appWidgetId + " - prepareWidgetView " + favori);

        final HoraireManager horaireManager = HoraireManager.getInstance();
        final DateMidnight today = new DateMidnight();

        // Initialisation du widget
        prepareWidgetViewInit(context, views, favori);
        setOnClickListener(context, views, favori);

        try {
            if (horaireManager.isInDB(context.getContentResolver(), favori, today, mHoraireLimit)) {

                // Les horaires sont en cache
                final List<Horaire> horaires = horaireManager.getNextSchedules(context.getContentResolver(), favori,
                        today, mHoraireLimit);
                prepareWidgetViewHoraires(context, views, favori, horaires);

            } else {

                // Charger les prochains horaires
                prepareWidgetAndloadHoraires(context, views, favori, appWidgetId);

            }
        } catch (final IOException e) {
            if (DBG)
                Log.e(LOG_TAG, Integer.toHexString(hashCode()) + " - " + appWidgetId
                        + " - Erreur lors du chargement des horaires", e);
        }
    }

    /**
     * Lancer le chargement des horaires.
     */
    protected void prepareWidgetAndloadHoraires(final Context context, final RemoteViews views, final Favori favori,
            final Integer appWidgetId) {
        final NextHoraireTask horaireTask = new NextHoraireTask();
        horaireTask.setContext(context);
        horaireTask.setArret(favori);
        horaireTask.setId(appWidgetId);
        horaireTask.setLimit(mHoraireLimit);
        horaireTask.setActionCallback(ACTION_APPWIDGET_UPDATE);

        // Lancer le chargement des horaires
        final HoraireManager horaireManager = HoraireManager.getInstance();
        horaireManager.schedule(horaireTask);

        views.setTextViewText(R.id.itemTime, null);
        views.setViewVisibility(R.id.widgetProgress, View.VISIBLE);
    }

    /**
     * Prparer le widget avec les lments fixes (nom, ligne, sens...)
     */
    protected void prepareWidgetViewInit(final Context context, final RemoteViews views, final Favori favori) {
        views.setTextViewText(R.id.itemSymbole, favori.getLettre());

        // Uniquement pour 2.2 et sup
        if (Build.VERSION.SDK_INT > 7) {
            views.setTextColor(R.id.itemSymbole, favori.getCouleurTexte());
            views.setTextColor(R.id.itemTitle, favori.getCouleurTexte());
            views.setTextColor(R.id.itemDescription, favori.getCouleurTexte());

            final GradientDrawable gradientDrawable = ColorUtils.getGradiant(favori.getCouleurBackground());
            final Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
            final Canvas canvas = new Canvas(bitmap);
            gradientDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            gradientDrawable.draw(canvas);
            views.setImageViewBitmap(R.id.widgetHeaderBackground, bitmap);
        }

        if (favori.getNomFavori() == null) {
            views.setTextViewText(R.id.itemTitle, favori.getNomArret());
            views.setTextViewText(R.id.itemDescription, FormatUtils.formatSens(favori.getNomSens()));
        } else {
            views.setTextViewText(R.id.itemTitle, favori.getNomArret());
            views.setTextViewText(R.id.itemDescription,
                    FormatUtils.formatSens(favori.getNomArret(), favori.getNomSens()));
        }

        views.setViewVisibility(R.id.widgetLoading, View.GONE);
    }

    /**
     * Set pending intent in remote views.
     * 
     * @param context
     * @param views
     *            Content remote views
     * @param viewId
     *            The view listening to click
     */
    private void setOnClickListener(final Context context, final RemoteViews views, final Favori favori) {
        final Intent intent = new Intent(context, this.getClass());
        intent.setAction(ACTION_APPWIDGET_ON_CLICK);
        intent.putExtra("favori", favori);

        final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, favori.getId(), intent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        views.setOnClickPendingIntent(R.id.horaireWidget, pendingIntent);
    }

    /**
     * Prparer le widget avec les horaires.
     * 
     * @param views
     * @param favori
     */
    protected void prepareWidgetViewHoraires(final Context context, final RemoteViews views, final Favori favori,
            final List<Horaire> nextHoraires) {

        final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(context);
        CharSequence content = "";

        if (nextHoraires.size() > 0) {

            // Programmer si besoin l'alarme
            scheduleUpdate(context, nextHoraires.get(0).getHoraire().getMillis());

            for (int i = 0; i < nextHoraires.size(); i++) {
                final Horaire horaire = nextHoraires.get(i);
                content = TextUtils.concat(content,
                        FormatUtils.formatTimeAmPm(context, timeFormat.format(horaire.getHoraire().toDate())));
                if (i < nextHoraires.size() - 1) {
                    content = TextUtils.concat(content, " \u2022 ");
                }
            }

        } else {
            content = context.getString(R.string.msg_aucun_depart_24h);
        }

        views.setTextViewText(R.id.itemTime, content);
        views.setViewVisibility(R.id.widgetProgress, View.GONE);
    }

    /**
     * Supprimer la configuration du widget.
     */
    @Override
    public void onDeleted(final Context context, final int[] appWidgetIds) {
        if (DBG)
            Log.i(LOG_TAG, Integer.toHexString(hashCode()) + " - " + "onDeleted " + Arrays.toString(appWidgetIds));

        final int count = appWidgetIds.length;
        for (int i = 0; i < count; i++) {
            WidgetConfigureActivity.removeWidgetId(context, appWidgetIds[i]);
        }
    }

    /**
     * Grer le signal de rafraichissement.
     */
    @Override
    public void onReceive(final Context context, final Intent intent) {
        final String action = intent.getAction();
        final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

        if (DBG)
            Log.i(LOG_TAG, Integer.toHexString(hashCode()) + " - " + "onReceive " + action);

        final boolean updateWidget = ACTION_APPWIDGET_UPDATE.equals(action)
                || Intent.ACTION_USER_PRESENT.equals(action);

        if (updateWidget) {

            final int idExtra = intent.getIntExtra("id", -1);
            int[] ids;

            if (DBG)
                Log.i(LOG_TAG, Integer.toHexString(hashCode()) + " - "
                        + "Rception de l'ordre de rafraichissement des widgets.");

            if (idExtra == -1) {
                ids = appWidgetManager.getAppWidgetIds(new ComponentName(context, this.getClass()));
            } else {
                ids = new int[] { idExtra };
            }

            onUpdate(context, appWidgetManager, ids);

        } else if (HoraireWidgetProvider.ACTION_APPWIDGET_ON_CLICK.equals(action)) {

            final Arret arret = intent.getParcelableExtra("favori");
            if (arret != null) {
                final ParamIntent startIntent = new ParamIntent(context, HorairesActivity.class);
                startIntent.putExtra(HorairesActivity.PARAM_ARRET, arret);
                startIntent.putExtra(HorairesActivity.PARAM_FROM_WIDGET, true);
                startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

                final TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
                stackBuilder.addParentStack(MainActivity.class);
                stackBuilder.addNextIntentWithParentStack(startIntent);

                stackBuilder.startActivities();
            }
        }

        super.onReceive(context, intent);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private int getHorairesCount(final Context context, final Bundle bundle) {

        synchronized (sLock) {
            if (sHoraireTextWidth == Integer.MIN_VALUE) {
                final TextView horaireTextView = new TextView(context);
                horaireTextView.setTextAppearance(horaireTextView.getContext(),
                        android.R.style.TextAppearance_Medium);

                final DateTime noon = new DateTime().withHourOfDay(12).withMinuteOfHour(00);
                final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(context);
                horaireTextView.setText(
                        FormatUtils.formatTimeAmPm(context, timeFormat.format(noon.toDate())) + " \u2022  ");
                final int specY = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                final int specX = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                horaireTextView.measure(specX, specY);
                sHoraireTextWidth = horaireTextView.getMeasuredWidth();
            }
        }

        final Resources r = context.getResources();
        final int padding = r.getDimensionPixelSize(R.dimen.padding_small);

        final int minWidth = bundle.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
        final int minWidthPixel = Math.round(
                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, minWidth, r.getDisplayMetrics())) - padding;

        return (minWidthPixel / sHoraireTextWidth);
    }

}