ApplySettings.java :  » Timer » timeriffic » com » alfray » timeriffic » core » app » Android Open Source

Android Open Source » Timer » timeriffic 
timeriffic » com » alfray » timeriffic » core » app » ApplySettings.java
/*
 * Project: Timeriffic
 * Copyright (C) 2009 ralfoide gmail com,
 *
 *  This program 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.
 *
 *  This program 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 com.alfray.timeriffic.core.app;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.util.Log;
import android.util.SparseArray;
import android.widget.Toast;

import com.alfray.timeriffic.R;
import com.alfray.timeriffic.app.TimerifficApp;
import com.alfray.timeriffic.app.UpdateReceiver;
import com.alfray.timeriffic.app.UpdateService;
import com.alfray.timeriffic.core.actions.TimedActionUtils;
import com.alfray.timeriffic.core.error.ExceptionHandler;
import com.alfray.timeriffic.core.prefs.PrefsValues;
import com.alfray.timeriffic.core.profiles1.Columns;
import com.alfray.timeriffic.core.profiles1.ProfilesDB;
import com.alfray.timeriffic.core.profiles1.ProfilesDB.ActionInfo;
import com.alfray.timeriffic.core.settings.ISetting;
import com.alfray.timeriffic.core.settings.SettingFactory;
import com.alfray.timeriffic.core.utils.SettingsHelper;
import com.alfray.timeriffic.core.utils.SettingsHelper.RingerMode;
import com.alfray.timeriffic.core.utils.SettingsHelper.VibrateRingerMode;


public class ApplySettings {

    private final static boolean DEBUG = true;
    public final static String TAG = ApplySettings.class.getSimpleName();

    private final Context mContext;
    private final PrefsValues mPrefs;
    private final SimpleDateFormat mUiDateFormat;
    private final SimpleDateFormat mDebugDateFormat;

    public ApplySettings(Context context, PrefsValues prefs) {
        mContext = context;
        mPrefs = prefs;
        mDebugDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss Z");
        String format = null;
        SimpleDateFormat dt = null;
        try {
            format = context.getString(R.string.globalstatus_nextlast_date_time);
            dt  = new SimpleDateFormat(format);
        } catch (Exception e) {
            Log.e(TAG, "Invalid R.string.globalstatus_nextlast_date_time: " +
                    (format == null ? "null" : format));
        }
        mUiDateFormat = dt == null ? mDebugDateFormat : dt;
    }

    private void showToast(String s, int duration) {
        try {
            Toast.makeText(mContext, s, duration).show();
        } catch (Throwable t) {
            ExceptionHandler.addToLog(mPrefs, t);
            Log.w(TAG, "Toast.show crashed", t);
        }
    }

    public void apply(boolean applyState, int displayToast) {
        if (DEBUG) Log.d(TAG, "Checking enabled");

        checkProfiles(applyState, displayToast);
        notifyDataChanged();
    }

    public void retryActions(String actions) {
        if (DEBUG) Log.d(TAG, "Retry actions: " + actions);

        SettingsHelper settings = new SettingsHelper(mContext);
        if (performAction(settings, actions, null /*failedActions*/)) {
            showToast("Timeriffic retried the actions", Toast.LENGTH_LONG);
            if (DEBUG) Log.d(TAG, "Retry succeed");
        } else {
            showToast("Timeriffic found no action to retry", Toast.LENGTH_LONG);
            if (DEBUG) Log.d(TAG, "Retry no-op");
        }
    }

    private void checkProfiles(boolean applyState, int displayToast) {
        ProfilesDB profilesDb = new ProfilesDB();
        try {
            profilesDb.onCreate(mContext);

            profilesDb.removeAllActionExecFlags();

            // Only do something if at least one profile is enabled.
            long[] prof_indexes = profilesDb.getEnabledProfiles();
            if (prof_indexes != null && prof_indexes.length != 0) {

                Calendar c = new GregorianCalendar();
                c.setTimeInMillis(System.currentTimeMillis());
                int hourMin = c.get(Calendar.HOUR_OF_DAY) * 60 + c.get(Calendar.MINUTE);
                int day = TimedActionUtils.calendarDayToActionDay(c);

                if (applyState) {
                    ActionInfo[] actions =
                        profilesDb.getWeekActivableActions(hourMin, day, prof_indexes);

                    if (actions != null && actions.length > 0) {
                        performActions(actions);
                        profilesDb.markActionsEnabled(actions, Columns.ACTION_MARK_PREV);
                    }
                }

                // Compute next event and set an alarm for it
                ActionInfo[] nextActions = new ActionInfo[] { null };
                int nextEventMin = profilesDb.getWeekNextEvent(hourMin, day, prof_indexes, nextActions);
                if (nextEventMin > 0) {
                    scheduleAlarm(c, nextEventMin, nextActions[0], displayToast);
                    if (applyState) {
                        profilesDb.markActionsEnabled(nextActions, Columns.ACTION_MARK_NEXT);
                    }
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "checkProfiles failed", e);
            ExceptionHandler.addToLog(mPrefs, e);

        } finally {
            profilesDb.onDestroy();
        }
    }

    private void performActions(ActionInfo[] actions) {

        String logActions = null;
        String lastAction = null;
        SettingsHelper settings = null;
        SparseArray<String> failedActions = new SparseArray<String>();

        for (ActionInfo info : actions) {
            try {
                if (settings == null) settings = new SettingsHelper(mContext);

                if (performAction(settings, info.mActions, failedActions)) {
                    lastAction = info.mActions;
                    if (logActions == null) {
                        logActions = lastAction;
                    } else {
                        logActions += " | " + lastAction;
                    }
                }
            } catch (Exception e) {
                Log.w(TAG, "Failed to apply setting", e);
                ExceptionHandler.addToLog(mPrefs, e);
            }
        }

        if (lastAction != null) {
            // Format the timestamp of the last action to be "now"
            String time = mUiDateFormat.format(new Date(System.currentTimeMillis()));

            // Format the action description
            String a = TimedActionUtils.computeLabels(mContext, lastAction);

            synchronized (mPrefs.editLock()) {
                Editor e = mPrefs.startEdit();
                try {
                    mPrefs.editStatusLastTS(e, time);
                    mPrefs.editStatusNextAction(e, a);

                } finally {
                    mPrefs.endEdit(e, TAG);
                }
            }

            addToDebugLog(logActions);
        }

        if (failedActions.size() > 0) {
            notifyFailedActions(failedActions);
        }
    }

    private boolean performAction(
            SettingsHelper settings,
            String actions,
            SparseArray<String> failedActions) {
        if (actions == null) return false;
        boolean didSomething = false;

        RingerMode ringerMode = null;
        VibrateRingerMode vibRingerMode = null;

        for (String action : actions.split(",")) {
            if (action.length() > 1) {
                char code = action.charAt(0);
                char v = action.charAt(1);

                switch(code) {
                case Columns.ACTION_RINGER:
                    for (RingerMode mode : RingerMode.values()) {
                        if (mode.getActionLetter() == v) {
                            ringerMode = mode;
                            break;
                        }
                    }
                    break;
                case Columns.ACTION_VIBRATE:
                    for (VibrateRingerMode mode : VibrateRingerMode.values()) {
                        if (mode.getActionLetter() == v) {
                            vibRingerMode = mode;
                            break;
                        }
                    }
                    break;

                default:
                    ISetting s = SettingFactory.getInstance().getSetting(code);
                    if (s != null && s.isSupported(mContext)) {
                        if (!s.performAction(mContext, action)) {
                            // Record failed action
                            if (failedActions != null) {
                                failedActions.put(code, action);
                            }
                        } else {
                            didSomething = true;
                        }
                    }
                    break;
                }
            }
        }

        if (ringerMode != null || vibRingerMode != null) {
            didSomething = true;
            settings.changeRingerVibrate(ringerMode, vibRingerMode);
        }

        return didSomething;
    }

    /** Notify UI to update */
    private void notifyDataChanged() {
        Context c = mContext.getApplicationContext();
        if (c instanceof TimerifficApp) {
            ((TimerifficApp) c).invokeDataListener();
        }
    }

    /**
     * Schedule an alarm to happen at nextEventMin minutes from now.
     *
     * @param now The time that was used at the beginning of the update.
     * @param nextEventMin The number of minutes ( > 0) after "now" where to set the alarm.
     * @param nextActions
     * @param displayToast One of {@link UpdateReceiver#TOAST_NONE},
     *             {@link UpdateReceiver#TOAST_IF_CHANGED} or {@link UpdateReceiver#TOAST_ALWAYS}
     */
    private void scheduleAlarm(
            Calendar now,
            int nextEventMin,
            ActionInfo nextActions,
            int displayToast) {
        AlarmManager manager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(mContext, UpdateReceiver.class);
        intent.setAction(UpdateReceiver.ACTION_APPLY_STATE);
        PendingIntent op = PendingIntent.getBroadcast(
                        mContext,
                        0 /*requestCode*/,
                        intent,
                        PendingIntent.FLAG_UPDATE_CURRENT);

        now.set(Calendar.SECOND, 0);
        now.set(Calendar.MILLISECOND, 0);
        now.add(Calendar.MINUTE, nextEventMin);

        long timeMs = now.getTimeInMillis();

        manager.set(AlarmManager.RTC_WAKEUP, timeMs, op);

        boolean shouldDisplayToast = displayToast != UpdateReceiver.TOAST_NONE;
        if (displayToast == UpdateReceiver.TOAST_IF_CHANGED) {
            shouldDisplayToast = timeMs != mPrefs.getLastScheduledAlarm();
        }


        SimpleDateFormat sdf = null;
        String format = null;
        try {
            format = mContext.getString(R.string.toast_next_alarm_date_time);
            sdf = new SimpleDateFormat(format);
        } catch (Exception e) {
            Log.e(TAG, "Invalid R.string.toast_next_alarm_date_time: " +
                    (format == null ? "null" : format));
        }
        if (sdf == null) sdf = mDebugDateFormat;

        sdf.setCalendar(now);
        String s2 = sdf.format(now.getTime());

        synchronized (mPrefs.editLock()) {
            Editor e = mPrefs.startEdit();
            try {
                mPrefs.editLastScheduledAlarm(e, timeMs);
                mPrefs.editStatusNextTS(e, s2);
                mPrefs.editStatusNextAction(e,
                        TimedActionUtils.computeLabels(mContext, nextActions.mActions));
            } finally {
                mPrefs.endEdit(e, TAG);
            }
        }

        s2 = mContext.getString(R.string.toast_next_change_at_datetime, s2);

        if (shouldDisplayToast) showToast(s2, Toast.LENGTH_LONG);
        if (DEBUG) Log.d(TAG, s2);
        addToDebugLog(s2);
    }


    protected static final String SEP_START = " [ ";
    protected static final String SEP_END = " ]\n";

    /** Add debug log for now. */
    public /* package */ void addToDebugLog(String message) {
        String time = mDebugDateFormat.format(new Date(System.currentTimeMillis()));
        addToDebugLog(time, message);
    }

    /** Add time:action specific log. */
    protected synchronized void addToDebugLog(String time, String logActions) {

        logActions = time + SEP_START + logActions + SEP_END;
        int len = logActions.length();

        // We try to keep only 4k in the buffer
        int max = 4096;

        String a = null;

        if (logActions.length() < max) {
            a = mPrefs.getLastActions();
            if (a != null) {
                int lena = a.length();
                if (lena + len > max) {
                    int extra = lena + len - max;
                    int pos = -1;
                    int p = -1;
                    do {
                        pos = a.indexOf(SEP_END, pos + 1);
                        p = pos + SEP_END.length();
                    } while (pos >= 0 && p < extra);

                    if (pos < 0 || p >= lena) {
                        a = null;
                    } else {
                        a = a.substring(p);
                    }
                }
            }
        }

        if (a == null) {
            mPrefs.setLastActions(logActions);
        } else {
            a += logActions;
            mPrefs.setLastActions(a);
        }

    }

    public static synchronized int getNumActionsInLog(Context context) {
        try {
            PrefsValues pv = new PrefsValues(context);
            String curr = pv.getLastActions();

            if (curr == null) {
                return 0;
            }

            int count = -1;
            int pos = -1;
            do {
                count++;
                pos = curr.indexOf(" ]", pos + 1);
            } while (pos >= 0);

            return count;

        } catch (Exception e) {
            Log.w(TAG, "getNumActionsInLog failed", e);
            ExceptionHandler.addToLog(new PrefsValues(context), e);
        }

        return 0;
    }


    /**
     * Send a notification indicating the given actions have failed.
     */
    private void notifyFailedActions(SparseArray<String> failedActions) {
        StringBuilder sb = new StringBuilder();
        for (int n = failedActions.size(), i = 0; i < n; i++) {
            if (i > 0) sb.append(',');
            sb.append(failedActions.valueAt(i));
        }
        String actions = sb.toString();
        String details = TimedActionUtils.computeLabels(mContext, actions);

        UpdateService.createRetryNotification(mContext, mPrefs, actions, details);
    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.