se.toxbee.sleepfighter.activity.AlarmActivity.java Source code

Java tutorial

Introduction

Here is the source code for se.toxbee.sleepfighter.activity.AlarmActivity.java

Source

/*******************************************************************************
 * Copyright (c) 2013 See AUTHORS file.
 * 
 * This file is part of SleepFighter.
 * 
 * SleepFighter 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.
 * 
 * SleepFighter 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 SleepFighter. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package se.toxbee.sleepfighter.activity;

import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import org.joda.time.DateTime;

import se.toxbee.sleepfighter.R;
import se.toxbee.sleepfighter.SFApplication;
import se.toxbee.sleepfighter.android.power.WakeLocker;
import se.toxbee.sleepfighter.android.utils.DialogUtils;
import se.toxbee.sleepfighter.audio.VibrationManager;
import se.toxbee.sleepfighter.helper.AlarmIntentHelper;
import se.toxbee.sleepfighter.helper.NotificationHelper;
import se.toxbee.sleepfighter.model.Alarm;
import se.toxbee.sleepfighter.model.AlarmTimestamp;
import se.toxbee.sleepfighter.model.challenge.ChallengeType;
import se.toxbee.sleepfighter.preference.GlobalPreferencesManager;
import se.toxbee.sleepfighter.service.AlarmPlannerService;
import se.toxbee.sleepfighter.service.AlarmPlannerService.Command;
import se.toxbee.sleepfighter.text.MetaTextUtils;
import se.toxbee.sleepfighter.utils.debug.Debug;
import se.toxbee.sleepfighter.utils.string.StringUtils;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.TextView;

/**
 * The activity for when an alarm rings/occurs.
 * 
 * @author Centril<twingoow@gmail.com> / Mazdak Farrokhzad.
 * @author Lam(m)<dannylam@gmail.com> / Danny Lam
 * @version 1.0
 * @since Sep 20, 2013
 */
public class AlarmActivity extends Activity {

    public static final String EXTRA_ALARM_ID = "alarm_id";

    public static final int CHALLENGE_REQUEST_CODE = 1;

    private static final int WINDOW_FLAGS_SCREEN_ON = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
            | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;

    private static final int WINDOW_FLAGS_LOCKSCREEN = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;

    private static final int EMERGENCY_COST = 100;
    private static final int EMERGENCY_PERCENTAGE_COST = 20;
    private static final int SNOOZE_COST = 10;
    private static final int SNOOZE_PERCENTAGE_COST = 5;
    private static final int CHALLENGE_POINTS_GET = 5;

    private Parameters p;
    private TextView tvName, tvTime;
    private Button btnStop, btnSnooze;
    private Alarm alarm;
    private Timer timer;
    private Camera camera;
    private boolean turnScreenOn = true;
    private boolean bypassLockscreen = true;

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

        // Turn and/or keep screen on.
        this.setScreenFlags();
        this.setContentView(R.layout.activity_alarm);
        SFApplication app = SFApplication.get();

        // Fetch alarm Id.
        int alarmId = new AlarmIntentHelper(this.getIntent()).getAlarmId();
        this.alarm = app.getPersister().fetchAlarmById(alarmId);

        // Get the name and time of the current ringing alarm
        tvName = (TextView) findViewById(R.id.tvAlarmName);
        tvName.setText(MetaTextUtils.printAlarmName(this, alarm));
        tvTime = (TextView) findViewById(R.id.tvAlarmTime);

        setupStopButton();
        setupSnoozeButton();
        setupFooter();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.alarm_activity_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_emergency_stop:
            handleEmergencyStop();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private void setupStopButton() {
        // Connect the challenge button with XML
        btnStop = (Button) findViewById(R.id.btnStop);
        btnStop.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                onStopClick();
            }
        });
    }

    private void setupSnoozeButton() {
        btnSnooze = (Button) findViewById(R.id.btnSnooze);

        if (alarm.getSnoozeConfig().isSnoozeEnabled()) {
            btnSnooze.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    startSnooze();
                }
            });
        } else {
            btnSnooze.setVisibility(View.GONE);
        }
    }

    private void setupFooter() {
        boolean usingChallenge = useChallenges();
        if (usingChallenge) {
            TextView pointText = (TextView) findViewById(R.id.challenge_points_text);

            String challengePointsStr = this.getResources().getString(R.string.challenge_points);
            pointText.setText(SFApplication.get().getPrefs().getChallengePoints() + " " + challengePointsStr);
        } else {
            findViewById(R.id.footer).setVisibility(View.INVISIBLE);
        }
    }

    /**
     * Handle what happens when the user presses the emergency stop.
     */
    private void handleEmergencyStop() {
        boolean skippingChallenges = useChallenges();

        if (skippingChallenges) {
            skipChallengeConfirm();
        } else {
            stopAlarm();
            performRescheduling();
        }
    }

    /**
     * Handles if the user uses emergency stop so that a challenge would be
     * skipped by showing confirmation dialog.
     */
    private void skipChallengeConfirm() {
        final int emergencyCost = Math.max(EMERGENCY_COST,
                SFApplication.get().getPrefs().getChallengePoints() / (100 / EMERGENCY_PERCENTAGE_COST));

        // Show confirmation dialog where the user has to confirm skipping the
        // challenge, and in turn lose a lot of points
        DialogInterface.OnClickListener yesAction = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                SFApplication.get().getPrefs().addChallengePoints(-emergencyCost);
                stopAlarm();
                performRescheduling();
            }
        };
        Resources res = getResources();

        // Get the correct string with the correct value inserted.
        DialogUtils.showConfirmationDialog(
                String.format(res.getString(R.string.alarm_emergency_dialog),
                        res.getQuantityString(R.plurals.alarm_emergency_cost, emergencyCost, emergencyCost)),
                this, yesAction);

    }

    private void onStopClick() {
        boolean showChallenge = useChallenges();

        if (showChallenge) {
            startChallenge();
        } else {
            stopAlarm();
            performRescheduling();
        }
    }

    /**
     * Launch ChallengeActivity to start alarm.
     */
    private void startChallenge() {
        // The vibration stops whenever you start the challenge
        VibrationManager.getInstance().stopVibrate(getApplicationContext());

        // Send user to ChallengeActivity.
        Intent i = new Intent(this, ChallengeActivity.class);
        new AlarmIntentHelper(i).setAlarmId(this.alarm);
        startActivityForResult(i, CHALLENGE_REQUEST_CODE);
    }

    /**
     * Stops alarm temporarily and sends a snooze command to the server.
     */
    private void startSnooze() {
        stopAlarm();

        // Send snooze command to service
        AlarmPlannerService.call(this, Command.SNOOZE, alarm.getId());

        // Remove some challenge points if skipping challenge
        boolean skippingChallenge = useChallenges();
        if (skippingChallenge) {
            GlobalPreferencesManager prefs = SFApplication.get().getPrefs();

            int snoozeCost = Math.max(SNOOZE_COST, prefs.getChallengePoints() / (100 / SNOOZE_PERCENTAGE_COST));
            prefs.addChallengePoints(-snoozeCost);
        }
    }

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

        // Release the wake-lock acquired in AlarmReceiver!
        WakeLocker.release();
    }

    private void performRescheduling() {
        SFApplication app = SFApplication.get();

        // Disable alarm if not repeating.
        if (!this.alarm.isRepeating()) {
            if (this.alarm.getMessageBus() == null) {
                this.alarm.setMessageBus(app.getBus());
            }

            this.alarm.setActivated(false);
        } else {
            // Reschedule earliest alarm (if any).
            AlarmTimestamp at = app.getAlarms().getEarliestAlarm(new DateTime().getMillis());
            if (at != AlarmTimestamp.INVALID) {
                AlarmPlannerService.call(app, Command.CREATE, at.getAlarm().getId());
            }
        }
    }

    /**
     * Sets screen related flags, reads from preferences.
     */
    private void setScreenFlags() {
        int flags = this.computeScreenFlags();

        if (flags == 0) {
            return;
        }

        this.getWindow().addFlags(flags);
    }

    private void readPreferences() {
        GlobalPreferencesManager prefs = SFApplication.get().getPrefs();

        this.turnScreenOn = prefs.turnScreenOn();
        this.bypassLockscreen = prefs.bypassLockscreen();
    }

    /**
     * Computes screen flags based on preferences.
     * 
     * @return screen flags.
     */
    private int computeScreenFlags() {
        readPreferences();

        int flags = 0;

        if (this.turnScreenOn) {
            flags |= WINDOW_FLAGS_SCREEN_ON;
        }

        if (this.bypassLockscreen) {
            flags |= WINDOW_FLAGS_LOCKSCREEN;
        }

        return flags;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Check if result is from a challenge
        if (requestCode == CHALLENGE_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                Debug.d("done with challenge");

                // If completed, stop the alarm
                stopAlarm();
                performRescheduling();

                // Add points
                SFApplication.get().getPrefs().addChallengePoints(CHALLENGE_POINTS_GET);
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    /**
     * Stop the current alarm sound and vibration
     */
    public void stopAlarm() {
        stopAudio();
        SFApplication.get().getTts().stop();

        VibrationManager.getInstance().stopVibrate(getApplicationContext());

        // Remove notification saying alarm is ringing
        NotificationHelper.getInstance().removeNotification(getApplicationContext());

        // Removes the globally set ringing alarm
        SFApplication.get().setRingingAlarm(null);

        finish();
    }

    private void stopAudio() {
        SFApplication.get().setAudioDriver(null);

        TextToSpeech tts = SFApplication.get().getTts();
        if (tts != null) {
            tts.stop();
        }
    }

    /**
     * Start flash, animation and show the current time on display
     */
    @Override
    protected void onStart() {
        super.onStart();

        // Start animation and flash
        startAnimate();

        if (alarm.isFlashEnabled()) {
            startFlash();
        }

        // Cancel previously started timers
        cancelTimer();

        timer = new Timer("SFTimer");

        final Runnable updateTask = new Runnable() {
            public void run() {
                // Set the current time on the text view
                tvTime.setText(getCurrentTime());
            }
        };

        // Update the user interface
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(updateTask);
            }
        };

        DateTime dt = DateTime.now();
        timer.scheduleAtFixedRate(timerTask, dt.getMillisOfSecond(), 1000);
    }

    private void cancelTimer() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

    /**
     * Checks various settings to determine if a challenge should be shown when
     * trying to stop.
     * 
     * @return true if a challenge should be shown
     */
    private boolean useChallenges() {
        boolean challengeEnabled = this.alarm.getChallengeSet().isEnabled();

        boolean globallyEnabled = SFApplication.get().getPrefs().isChallengesActivated();

        // Checks if any of the individual challenges are enabled
        Set<ChallengeType> enabledChallenges = this.alarm.getChallengeSet().getEnabledTypes();
        boolean anyChallengeEnabled = !enabledChallenges.isEmpty();

        return challengeEnabled && globallyEnabled && anyChallengeEnabled;
    }

    /*
     * Use to stop the timer (non-Javadoc)
     * 
     * @see android.app.Activity#onStop()
     */
    @Override
    protected void onStop() {
        super.onStop();

        if (camera != null) {
            camera.release();
        }

        cancelTimer();
    }

    // Get the current time with the Calendar
    public String getCurrentTime() {
        DateTime time = new DateTime();
        return StringUtils.joinTime(time.getHourOfDay(), time.getMinuteOfHour());
    }

    /**
     * Start the camera's flashlight if found
     */
    private void startFlash() {

        // Check if there is any camera. If not found, return nothing.
        // If found, flash!
        Context context = this;
        PackageManager pm = context.getPackageManager();

        if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
            Log.e("err", "No flashlight detected!");
            return;
        } else {
            camera = Camera.open();
            Log.i("info", "The flashlight is on.");
            p = camera.getParameters();
            p.setFlashMode(Parameters.FLASH_MODE_TORCH);
            camera.setParameters(p);
        }
    }

    /**
     * Declaring new animations and sets to components
     */
    private void startAnimate() {

        // Setting animation

        Animation fadeShort = new AlphaAnimation(1, 0);
        fadeShort.setDuration(200);
        fadeShort.setInterpolator(new LinearInterpolator());
        fadeShort.setRepeatCount(Animation.INFINITE);
        fadeShort.setRepeatMode(Animation.REVERSE);

        // Set the components with animation
        tvTime.startAnimation(fadeShort);
    }
}