@@ -47,6 +48,6 @@
-
+
diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmAlertActivity.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmAlertActivity.java
index d9f1390..925d026 100644
--- a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmAlertActivity.java
+++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmAlertActivity.java
@@ -8,13 +8,13 @@ import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Vibrator;
import android.util.Log;
-import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
+import com.triggertrap.seekarc.SeekArc;
+
// TODO See GlowPad.
// TODO sound audible alarm -- see AlarmKlaxon.java
@@ -22,16 +22,17 @@ import android.widget.Button;
// TODO Snooze? set another alarm for the next half-hour (or grace_period / 2)?
public class AlarmAlertActivity extends Activity {
- // TODO correct alert lifetime
- private static final int ALERT_LIFE = 1000*10; //1000*60*2; // 2 minutes
- private static final long[] vPattern = {500, 500};
- private static Boolean userCancelled;
- private static Vibrator vibrator;
private static Intent notifyIntent;
+ public static Boolean alertFinished, userCancelled;
+ public static Activity alertActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // Self-reference so we can run finish() from outside.
+ alertActivity = this;
+
requestWindowFeature(Window.FEATURE_NO_TITLE);
Window window = getWindow();
// Set to use the full screen
@@ -47,32 +48,15 @@ public class AlarmAlertActivity extends Activity {
setContentView(R.layout.alarm_alert);
notifyIntent = new Intent(getApplicationContext(), AlarmNotify.class);
- // Disable any current notifications
+ // Disable any current notifications (if we're snoozing)
stopService(notifyIntent);
- // Turn off the alert activity, and switch to a notification
- new Handler().postDelayed(new Runnable() {
- public void run() {
- // Close the dialogue and switch to notification
- // if the Activity has not been closed by the user
- if (!userCancelled) {
- startService(notifyIntent);
- finish();
- }
- }
- }, ALERT_LIFE);
}
@Override
public void onStart() {
super.onStart();
- userCancelled = false;
-
- vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
- vibrator.vibrate(vPattern, 0);
-
- // TODO change button to slide...
- // TODO change slide to circle slide? https://github.com/JesusM/HoloCircleSeekBar
+ /*
Button cancelButton = (Button) findViewById(R.id.cancel_dialog_button);
cancelButton.setOnClickListener (new View.OnClickListener() {
@Override
@@ -80,39 +64,38 @@ public class AlarmAlertActivity extends Activity {
cancelGraceAlarm();
}
});
+ */
+ SeekArc cancelArc = (SeekArc) findViewById(R.id.cancel_dialog_seekArc);
+ cancelArc.setOnSeekArcChangeListener(new SeekArc.OnSeekArcChangeListener() {
+ volatile Boolean seekFinished = false;
+ @Override
+ public void onProgressChanged(SeekArc seekArc, int progress, boolean fromUser) {
+ if (progress > 98 && !seekFinished && fromUser) {
+ AlarmReceiver.dismissAlarm(alertActivity);
+ seekFinished = true;
+ }
+ }
+ @Override
+ public void onStartTrackingTouch(SeekArc seekArc) {
+ }
+ @Override
+ public void onStopTrackingTouch(SeekArc seekArc) {
+ }
+ });
+
}
/**
- * Handle the user pressing the back/return button
- * TODO Do we want this to cancel the alarm and grace period?
- * TODO Or do we trigger the notification and finish() right away?
- * TODO probably most intuitive to cancel the alarms...
+ * Handle the user pressing the back/return and home buttons
*/
@Override
public void onBackPressed() {
- cancelGraceAlarm();
- /* OR:
- // Ensure that the we don't trigger the notification a second time
- userCancelled = true;
- startService(notifyIntent);
- finish();
- */
+ AlarmReceiver.setAlarmStatus(AlarmReceiver.ALARM_IGNORED);
+ AlarmReceiver.snoozeAlarm(this);
}
- public void onStop() {
- vibrator.cancel();
- super.onStop();
- }
-
- private void cancelGraceAlarm() {
- AlarmManager graceManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent graceIntent = new Intent(getApplicationContext(), GraceReceiver.class);
- PendingIntent gracePendingIntent = PendingIntent.getBroadcast(getApplicationContext(), MainActivity.GRACE_REQUEST, graceIntent, 0);
- graceManager.cancel(gracePendingIntent);
- Log.d("AlarmAlertActivity", "Cancelled grace alarm.");
- // Ensure we don't load a notification now
- userCancelled = true;
- // Close the dialogue (stop vibration &c)
- finish();
+ public void onUserLeaveHint() {
+ AlarmReceiver.setAlarmStatus(AlarmReceiver.ALARM_IGNORED);
+ AlarmReceiver.snoozeAlarm(this);
}
}
\ No newline at end of file
diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmKlaxon.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmKlaxon.java
new file mode 100644
index 0000000..af21e54
--- /dev/null
+++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmKlaxon.java
@@ -0,0 +1,130 @@
+package za.org.treehouse.hypoalarm;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Vibrator;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class AlarmKlaxon {
+ private static final long[] vPattern = {500, 500};
+ // Volume modification for alarms while a phone call is active, from com.android.deskclock.alarms
+ private static final float IN_CALL_VOLUME = 0.125f;
+ private static MediaPlayer mediaPlayer = null;
+ private static TelephonyManager telephonyManager;
+ private static Vibrator vibrator;
+
+ public static void start(final Context context) {
+
+ /**
+ *
+ * TODO add raw ring tone to use as fallback
+ * TODO add in-call ring tone
+ * TODO lower volume if in a call
+ * TODO cancel noise if a call comes in (add TelephonyManager listener which cancels the alert but calls the notification)
+ *
+ * TODO start telephony listener
+ *
+ * TODO snooze 5 minutes on press back button or home button (new runnable)
+ * TODO remove back/home button and most top icons
+ * TODO fix glowpad/seekarc
+ */
+ vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ vibrator.cancel();
+ vibrator.vibrate(vPattern, 0);
+
+ if (true)
+ return; // TODO remove after testing -- is mediaplayer responsible for delays?
+
+ telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ // TODO select alarm tone?
+ // Use the default alarm tone...
+ Uri alarmNoise = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
+ stopAudio(context);
+ mediaPlayer = new MediaPlayer();
+ mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.e("AlarmAlertActivity", "Error occurred while playing audio. Stopping alarm.");
+ stopAudio(context);
+ return true;
+ }
+ });
+
+ try {
+ /*
+ * TODO find out if we're in a call
+ if (inTelephoneCall) {
+ Log.v("Using the in-call alarm");
+ sMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
+ setDataSourceFromResource(context, sMediaPlayer, R.raw.in_call_alarm);
+ } else {
+ */
+ mediaPlayer.setDataSource(context, alarmNoise);
+ startAudio(context);
+ //}
+ } catch (Exception ex) {
+ // The alarmNoise may be on the sd card which could be busy right
+ // now. Use the fallback ringtone.
+ try {
+ // Reset the media player to clear the error state.
+ mediaPlayer.reset();
+ //setDataSourceFromResource(this, mediaPlayer, R.raw.fallbackring);
+ startAudio(context);
+ } catch (Exception ex2) {
+ // At this point we just don't play anything.
+ Log.e("AlarmAlertActivity", "Failed to play fallback ringtone", ex2);
+ }
+ }
+
+ }
+
+ public static void stop(final Context context) {
+ vibrator.cancel();
+ if (true)
+ return; // TODO remove after testing
+ stopAudio(context);
+ }
+
+ private static void startAudio(final Context context) throws IOException {
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ // do not play alarms if stream volume is 0 (typically because ringer mode is silent).
+ if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
+ mediaPlayer.setLooping(true);
+ try {
+ mediaPlayer.prepare();
+ } catch (Exception e) {
+ Log.e("AlarmAlertActivity", "Prepare failed. Exiting", e);
+ return;
+ }
+ audioManager.requestAudioFocus(null,
+ AudioManager.STREAM_ALARM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ mediaPlayer.start();
+ }
+ }
+
+ private static void stopAudio(final Context context) {
+ if (mediaPlayer != null) {
+ mediaPlayer.stop();
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ audioManager.abandonAudioFocus(null);
+ mediaPlayer.release();
+ mediaPlayer = null;
+ }
+ }
+ // Load ringtone from a resource
+ private static void setDataSourceFromResource(Context context, MediaPlayer player, int res) throws IOException {
+ AssetFileDescriptor afd = context.getResources().openRawResourceFd(res);
+ if (afd != null) {
+ player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ afd.close();
+ }
+ }
+}
diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmNotify.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmNotify.java
index 3460bf9..b1232dc 100644
--- a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmNotify.java
+++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmNotify.java
@@ -27,10 +27,10 @@ public class AlarmNotify extends Service {
public void onDestroy() {
// If the notification is cancelled, stop updating.
notificationRunning = false;
- Log.d("AlarmNotify", "1: Setting notificationRunning to false");
// Remove the notification in the notification bar
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancel(notifyID);
+ Log.d("AlarmNotify", "Notification stopped.");
}
@Override
@@ -38,10 +38,10 @@ public class AlarmNotify extends Service {
final int UPDATE_INTERVAL = 10*1000; // Timer is updated six times a minute
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ Log.d("AlarmNotify", "Notification started.");
+
//final String phoneNumber = sharedPref.getString(getString(R.string.PhoneNumberPref), null);
final int gracePeriod = sharedPref.getInt(getString(R.string.GracePeriodPref), 60);
- // convert gracePeriod to milliseconds and calculate when it'll fire
- final long endTime = System.currentTimeMillis() + (gracePeriod * 60 * 1000);
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_grey);
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
@@ -55,6 +55,8 @@ public class AlarmNotify extends Service {
.setAutoCancel(false)
.setPriority(Notification.PRIORITY_HIGH);
+
+// TODO if alarm alert is snoozing and we cancel, cancel the snooze.
// Set up dismiss action
Intent cancellerIntent = new Intent(getBaseContext(), CancelGraceReceiver.class);
PendingIntent cancellerPendingIntent = PendingIntent.getBroadcast(getBaseContext(), MainActivity.CANCEL_GRACE_REQUEST, cancellerIntent, PendingIntent.FLAG_CANCEL_CURRENT);
@@ -69,12 +71,13 @@ public class AlarmNotify extends Service {
/**
* TODO load alert activity (without sound or vibration) on select?
* TODO This would allow the user to test competence
- Intent alertActivityIntent = new Intent(this, AlarmAlertActivity.class);
- alertActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- notification.setContentIntent(alertActivityIntent);
+ * something like:
+ Intent alarmAlertIntent = new Intent(this, AlarmAlertActivity.class);
+ PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(this, 0, alarmAlertIntent, 0);
+ notification.setContentIntent(alarmPen`dingIntent);
*/
+
nm.cancel(notifyID);
nm.notify(notifyID, notification.build());
@@ -82,21 +85,22 @@ public class AlarmNotify extends Service {
@Override
public void run() {
notificationRunning = true;
- Log.d("AlarmNotify", "2: Setting notificationRunning to true");
int max = 1000;
- // Convert endTime from milliseconds to seconds, and translate to time remaining
- int secondsLeft = (int) ((endTime - System.currentTimeMillis())) / (1000);
- int gracePeriodSeconds = gracePeriod * 60;
- // Multiply each int by 1000 for greater progress resolution
- int progress = (((gracePeriodSeconds * 1000) - (secondsLeft * 1000)) * max) / (gracePeriodSeconds * 1000);
+ /* TODO check that graceEndTime is always set.
+ if (AlarmReceiver.graceEndTime == 0) {
+ AlarmReceiver.graceEndTime = System.currentTimeMillis() + (gracePeriod * 60 * 1000);
+ }*/
+ // Count in milliseconds for greater progress resolution
+ int milliSecondsLeft = (int) ((AlarmReceiver.graceEndTime - System.currentTimeMillis()));
+ int gracePeriodMilliSeconds = gracePeriod * 60 * 1000;
+ int progress = ((gracePeriodMilliSeconds - milliSecondsLeft) * max) / gracePeriodMilliSeconds;
while (progress < max) {
- // Stop the thread if cancelled elsewhere
- Log.d("AlarmNotify", "notificationRunning is " + notificationRunning);
+ // Stop the thread if the notification is cancelled elsewhere
if (!notificationRunning) {
return;
}
- int minutesLeft = secondsLeft / 60;
+ int minutesLeft = (milliSecondsLeft / 1000) / 60;
if (Build.VERSION.SDK_INT >= 11) {
notification.setContentText(String.format(getString(R.string.notificationText), MainActivity.MinutesToGracePeriodStr(minutesLeft)));
notification.setProgress(max, progress, false);
@@ -104,12 +108,12 @@ public class AlarmNotify extends Service {
nm.notify(notifyID, notification.build());
}
// Prepare secondsLeft and progress for the next loop
- secondsLeft = secondsLeft - (UPDATE_INTERVAL / 1000);
+ milliSecondsLeft = milliSecondsLeft - UPDATE_INTERVAL;
// Multiply each int by 1000 for greater progress resolution
- progress = (((gracePeriodSeconds * 1000) - (secondsLeft * 1000)) * max) / (gracePeriodSeconds * 1000);
- Log.d("AlarmNotify", "secondsLeft is " + secondsLeft + " and progress is " + progress + " (gracePeriodSeconds is " + gracePeriodSeconds + ")");
- // Sleeps the thread, simulating an operation
- // that takes time
+ progress = ((gracePeriodMilliSeconds - milliSecondsLeft) * max) / gracePeriodMilliSeconds;
+ //Log.d("AlarmNotify", "milliSecondsLeft is " + milliSecondsLeft + " and progress is " + progress + " (gracePeriodMilliSeconds is " + gracePeriodMilliSeconds + ")");
+
+ // Sleep until we need to update again
try {
Thread.sleep(UPDATE_INTERVAL);
} catch (InterruptedException e) {
diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java
index 50af2eb..232cd56 100644
--- a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java
+++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/AlarmReceiver.java
@@ -1,34 +1,55 @@
package za.org.treehouse.hypoalarm;
import android.app.AlarmManager;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
+import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
-import android.view.WindowManager;
+import android.widget.Toast;
import java.util.Calendar;
+/**
+ * TODO change alarm state if a phone call comes in
+ *
+ * TODO display notification if alarm is about to go off (and allow user to cancel it before alarm goes off)
+ *
+ * TODO allow snooze state
+ */
+
public class AlarmReceiver extends BroadcastReceiver {
+ private static final int SNOOZE_TIME = 1000*20; //1000*60*5; // Snooze for 5 minutes if need be
+ private static final int ALERT_LIFE = 1000*10; //1000*60*2; // 2 minutes
private static SharedPreferences sharedPref;
private static AlarmManager alarmManager, graceManager;
private static PendingIntent alarmPendingIntent, gracePendingIntent;
+ private static Intent alertActivityIntent, notifyIntent;
+ public static volatile String alarmStatus; // Register ALARM_DISMISSED and its brethren here
+ public static final String ALARM_RUNNING = "ALARM_RUNNING";
+ public static final String ALARM_DISMISSED = "ALARM_DISMISSED";
+ public static final String ALARM_IGNORED = "ALARM_IGNORED";
+ public static final String ALARM_SNOOZED = "ALARM_SNOOZED";
+ public static final String ALARM_SNOOZE_RUNNING = "ALARM_SNOOZE_RUNNING";
+ public static long graceEndTime;
@Override
- public void onReceive(Context context, Intent intent) {
+ public void onReceive(final Context context, Intent intent) {
sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
Boolean alarmActive = sharedPref.getBoolean(context.getString(R.string.AlarmActivePref), true);
+ int gracePeriod = sharedPref.getInt(context.getString(R.string.GracePeriodPref), 60);
+ String alarmTimeStr = sharedPref.getString(context.getString(R.string.AlarmTimePref), null);
if (alarmActive) {
- // Set a grace period alarm to send SMS
- int gracePeriod = sharedPref.getInt(context.getString(R.string.GracePeriodPref), 60);
+ // if nothing else happens, assume the alert was ignored.
+ alarmStatus = ALARM_RUNNING;
+ // Set a grace period alarm to send SMS
Calendar graceCal = Calendar.getInstance();
graceCal.set(Calendar.SECOND, 0);
graceCal.add(Calendar.MINUTE, gracePeriod);
@@ -43,16 +64,20 @@ public class AlarmReceiver extends BroadcastReceiver {
}
Log.d("AlarmReceiver", "Setting grace alarm for " + MainActivity.debugDate(graceCal));
- // Allow user to acknowledge alarm and cancel grace alarm
- Intent alertActivityIntent = new Intent(context, AlarmAlertActivity.class);
+ // Calculate when the grace period ends
+ graceEndTime = System.currentTimeMillis() + (gracePeriod * 60 * 1000);
+
+ // Set up intents for later use
+ notifyIntent = new Intent(context, AlarmNotify.class);
+ alertActivityIntent = new Intent(context, AlarmAlertActivity.class);
alertActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- context.startActivity(alertActivityIntent);
+ // Allow user to acknowledge alarm and cancel grace alarm
+ startAlert(context);
// Reset for tomorrow; as of API 19, setRepeating() is inexact, so we use setExact()
- String alarmTimeStr = sharedPref.getString(context.getString(R.string.AlarmTimePref), null);
- // Calendar automatically advances the day since alarmTimeStr is now in the past.
+ // (Calendar will automatically advance the day since today's alarmTimeStr is now in the past.)
Calendar cal = MainActivity.TimeStringToCalendar(alarmTimeStr);
alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmPendingIntent = PendingIntent.getBroadcast(context, MainActivity.ALARM_REQUEST, intent, 0);
@@ -65,4 +90,88 @@ public class AlarmReceiver extends BroadcastReceiver {
Log.d("AlarmReceiver", "Resetting alarm for " + MainActivity.debugDate(cal));
}
}
+
+ public static void startAlert(final Context context) {
+ Log.d("AlarmReceiver", "Starting alarm; status is " + alarmStatus);
+ // Turn off any notifications first
+ context.stopService(notifyIntent);
+
+ context.startActivity(alertActivityIntent);
+ AlarmKlaxon.start(context);
+
+ // Turn off the alert activity after a period, and switch to a notification
+ new Handler().postDelayed(new Runnable() {
+ public void run() {
+ // Close the dialogue and switch to notification
+ // if the Activity has not been closed by the user
+ // (that is, snoozeAlert and dismissAlert have not been called)
+ // TODO don't run if we've just snoozed from home/back button, but do run if
+ // TODO we want to finish the snooze alert activity...
+ if (alarmStatus.contentEquals(ALARM_DISMISSED) ||
+ alarmStatus.contentEquals(ALARM_SNOOZED)) {
+ return;
+ }
+ // Stop if we're running the snooze alert
+ if (alarmStatus.contentEquals(ALARM_SNOOZE_RUNNING)) {
+ stopAlert(context);
+ } else {
+ alarmStatus = ALARM_IGNORED; // This is true, although we are about to switch to ALARM_SNOOZED
+ snoozeAlarm(context);
+ }
+ }
+ }, ALERT_LIFE);
+ }
+ public static void stopAlert(final Context context) {
+ Log.d("AlarmReceiver", "Stopping alarm; status is " + alarmStatus);
+ AlarmKlaxon.stop(context);
+ AlarmAlertActivity.alertActivity.finish();
+ // Display a notification if the alarm hasn't been dismissed
+ if (!alarmStatus.contentEquals(ALARM_DISMISSED)) {
+ context.startService(notifyIntent);
+ }
+ }
+
+ public static void dismissAlarm(final Context context) {
+ Log.d("AlarmReceiver", "Dismissing alarm");
+ alarmStatus = ALARM_DISMISSED;
+ // Close the alert and all notifications
+ stopAlert(context);
+
+ // Cancel the graceAlarm
+ AlarmManager graceManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ Intent graceIntent = new Intent(context, GraceReceiver.class);
+ PendingIntent gracePendingIntent = PendingIntent.getBroadcast(context, MainActivity.GRACE_REQUEST, graceIntent, 0);
+ graceManager.cancel(gracePendingIntent);
+ }
+
+ // TODO should the snooze reset the time at which the grace period alarm fires?
+ public static void snoozeAlarm(final Context context) {
+ Log.d("AlarmReceiver", "Snoozing alarm");
+ stopAlert(context);
+ // Close the alert, stop the klaxon, and start the notification,
+ // but only if there's time left before the gracePeriod triggers,
+ // and we haven't snoozed before
+ if (((System.currentTimeMillis() + SNOOZE_TIME) < graceEndTime) &&
+ (!alarmStatus.contentEquals(ALARM_SNOOZED)) &&
+ (!alarmStatus.contentEquals(ALARM_DISMISSED))) {
+ new Handler().postDelayed(new Runnable() {
+ public void run() {
+ Log.d("AlarmReceiver", "Resuming after snooze; status is " + alarmStatus);
+ // Don't run if the alarm was dismissed before the timer ran out
+ // (because a notification was acknowledged)
+ if (!alarmStatus.contentEquals(ALARM_DISMISSED)) {
+ alarmStatus = ALARM_SNOOZE_RUNNING;
+ startAlert(context);
+ }
+ }
+ }, SNOOZE_TIME);
+ // Change alarm status from ignored to snoozed
+ alarmStatus = ALARM_SNOOZED;
+ }
+ }
+
+ public static void setAlarmStatus (String status) {
+ Log.d("AlarmReceiver", "Setting alarm status to " + status);
+ alarmStatus = status;
+ }
}
diff --git a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/CancelGraceReceiver.java b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/CancelGraceReceiver.java
index 5f23424..d2f5d60 100644
--- a/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/CancelGraceReceiver.java
+++ b/HypoAlarm/src/main/java/za/org/treehouse/hypoalarm/CancelGraceReceiver.java
@@ -18,6 +18,9 @@ public class CancelGraceReceiver extends BroadcastReceiver {
graceManager.cancel(gracePendingIntent);
Log.d("CancelGraceReceiver", "Cancelled grace alarm");
+ // Ensure that any snoozes that are pending never happen.
+ AlarmReceiver.setAlarmStatus(AlarmReceiver.ALARM_DISMISSED);
+
// Display toast
Toast.makeText(context, context.getString(R.string.alarmCancelToast), Toast.LENGTH_LONG).show();
diff --git a/HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png
index b21af85..1d371ad 100644
Binary files a/HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png and b/HypoAlarm/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png
index 015adbe..178ed19 100644
Binary files a/HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png and b/HypoAlarm/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png
index 0982477..fb1f16f 100644
Binary files a/HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png and b/HypoAlarm/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png b/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png
index e887989..592272f 100644
Binary files a/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png and b/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher_grey.png b/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher_grey.png
new file mode 100644
index 0000000..12a2c1b
Binary files /dev/null and b/HypoAlarm/src/main/res/drawable-xxhdpi/ic_launcher_grey.png differ
diff --git a/HypoAlarm/src/main/res/drawable/hypoalarm.png b/HypoAlarm/src/main/res/drawable/hypoalarm.png
index e1f444c..e091112 100644
Binary files a/HypoAlarm/src/main/res/drawable/hypoalarm.png and b/HypoAlarm/src/main/res/drawable/hypoalarm.png differ
diff --git a/HypoAlarm/src/main/res/layout/alarm_alert.xml b/HypoAlarm/src/main/res/layout/alarm_alert.xml
index a2e1222..124eb91 100644
--- a/HypoAlarm/src/main/res/layout/alarm_alert.xml
+++ b/HypoAlarm/src/main/res/layout/alarm_alert.xml
@@ -2,6 +2,8 @@
@@ -9,6 +11,34 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HypoAlarm/src/main/res/values/dimens.xml b/HypoAlarm/src/main/res/values/dimens.xml
index 48963b2..35c33e2 100644
--- a/HypoAlarm/src/main/res/values/dimens.xml
+++ b/HypoAlarm/src/main/res/values/dimens.xml
@@ -3,4 +3,30 @@
16dp
16dp
+
+
+
+ 135dip
+
+
+ 75dip
+
+
+ 40dip
+
+
+ 15dip
+
+
+ 270dp
+
+
+ 94dp
+
+
+ 28dp
+
\ No newline at end of file
diff --git a/HypoAlarm/src/main/res/values/strings.xml b/HypoAlarm/src/main/res/values/strings.xml
index c76093b..ca70a12 100644
--- a/HypoAlarm/src/main/res/values/strings.xml
+++ b/HypoAlarm/src/main/res/values/strings.xml
@@ -57,4 +57,11 @@
All HypoAlarms cancelled
+
+ - @drawable/ic_lockscreen_answer
+ - @null
+ - @drawable/ic_lockscreen_decline
+ - @null
"
+
+
diff --git a/HypoAlarm/src/main/res/values/styles.xml b/HypoAlarm/src/main/res/values/styles.xml
index 00a7ff8..1162a3e 100644
--- a/HypoAlarm/src/main/res/values/styles.xml
+++ b/HypoAlarm/src/main/res/values/styles.xml
@@ -5,4 +5,13 @@
+
+
+
+
+
diff --git a/SeekArc/SeekArc.iml b/SeekArc/SeekArc.iml
new file mode 100644
index 0000000..2ee141d
--- /dev/null
+++ b/SeekArc/SeekArc.iml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SeekArc/build.gradle b/SeekArc/build.gradle
new file mode 100644
index 0000000..f7e10f4
--- /dev/null
+++ b/SeekArc/build.gradle
@@ -0,0 +1,19 @@
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion "19.0.1"
+
+ defaultConfig {
+ minSdkVersion 8
+ targetSdkVersion 17
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ runProguard false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
\ No newline at end of file
diff --git a/SeekArc/build/source/buildConfig/debug/com/triggertrap/seekarc/BuildConfig.java b/SeekArc/build/source/buildConfig/debug/com/triggertrap/seekarc/BuildConfig.java
new file mode 100644
index 0000000..ddbe975
--- /dev/null
+++ b/SeekArc/build/source/buildConfig/debug/com/triggertrap/seekarc/BuildConfig.java
@@ -0,0 +1,13 @@
+/**
+ * Automatically generated file. DO NOT MODIFY
+ */
+package com.triggertrap.seekarc;
+
+public final class BuildConfig {
+ public static final boolean DEBUG = Boolean.parseBoolean("true");
+ public static final String PACKAGE_NAME = "com.triggertrap.seekarc";
+ public static final String BUILD_TYPE = "debug";
+ public static final String FLAVOR = "";
+ public static final int VERSION_CODE = 1;
+ public static final String VERSION_NAME = "1.0";
+}
diff --git a/SeekArc/build/source/buildConfig/release/com/triggertrap/seekarc/BuildConfig.java b/SeekArc/build/source/buildConfig/release/com/triggertrap/seekarc/BuildConfig.java
new file mode 100644
index 0000000..ba2f829
--- /dev/null
+++ b/SeekArc/build/source/buildConfig/release/com/triggertrap/seekarc/BuildConfig.java
@@ -0,0 +1,13 @@
+/**
+ * Automatically generated file. DO NOT MODIFY
+ */
+package com.triggertrap.seekarc;
+
+public final class BuildConfig {
+ public static final boolean DEBUG = false;
+ public static final String PACKAGE_NAME = "com.triggertrap.seekarc";
+ public static final String BUILD_TYPE = "release";
+ public static final String FLAVOR = "";
+ public static final int VERSION_CODE = 1;
+ public static final String VERSION_NAME = "1.0";
+}
diff --git a/SeekArc/build/source/r/debug/com/triggertrap/seekarc/R.java b/SeekArc/build/source/r/debug/com/triggertrap/seekarc/R.java
new file mode 100644
index 0000000..ff2b5eb
--- /dev/null
+++ b/SeekArc/build/source/r/debug/com/triggertrap/seekarc/R.java
@@ -0,0 +1,419 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package com.triggertrap.seekarc;
+
+public final class R {
+ public static final class attr {
+ /** Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int arcColor=0x7f010009;
+ /**
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int arcWidth=0x7f010004;
+ /**
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int clockwise=0x7f01000d;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int max=0x7f010002;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int progress=0x7f010005;
+ /**
Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int progressColor=0x7f01000a;
+ /**
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int progressWidth=0x7f010003;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int rotation=0x7f010006;
+ /**
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int roundEdges=0x7f01000b;
+ /**
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ */
+ public static int seekArcStyle=0x7f01000e;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int startAngle=0x7f010007;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int sweepAngle=0x7f010008;
+ /**
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ */
+ public static int thumb=0x7f010000;
+ /**
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int thumbOffset=0x7f010001;
+ /**
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int touchInside=0x7f01000c;
+ }
+ public static final class color {
+ public static int progress_gray=0x7f030000;
+ public static int progress_gray_dark=0x7f030001;
+ }
+ public static final class drawable {
+ public static int ic_launcher=0x7f020000;
+ public static int scrubber_control_disabled_holo=0x7f020001;
+ public static int scrubber_control_focused_holo=0x7f020002;
+ public static int scrubber_control_normal_holo=0x7f020003;
+ public static int scrubber_control_pressed_holo=0x7f020004;
+ public static int seek_arc_control_selector=0x7f020005;
+ }
+ public static final class string {
+ public static int app_name=0x7f040000;
+ }
+ public static final class style {
+ public static int SeekArc=0x7f050000;
+ public static int SeekArcLight=0x7f050001;
+ }
+ public static final class styleable {
+ /** Attributes that can be used with a SeekArc.
+
Includes the following attributes:
+
+
+
+ | Attribute | Description |
+ {@link #SeekArc_arcColor com.triggertrap.seekarc:arcColor} | |
+ {@link #SeekArc_arcWidth com.triggertrap.seekarc:arcWidth} | |
+ {@link #SeekArc_clockwise com.triggertrap.seekarc:clockwise} | |
+ {@link #SeekArc_max com.triggertrap.seekarc:max} | |
+ {@link #SeekArc_progress com.triggertrap.seekarc:progress} | |
+ {@link #SeekArc_progressColor com.triggertrap.seekarc:progressColor} | |
+ {@link #SeekArc_progressWidth com.triggertrap.seekarc:progressWidth} | |
+ {@link #SeekArc_rotation com.triggertrap.seekarc:rotation} | |
+ {@link #SeekArc_roundEdges com.triggertrap.seekarc:roundEdges} | |
+ {@link #SeekArc_startAngle com.triggertrap.seekarc:startAngle} | |
+ {@link #SeekArc_sweepAngle com.triggertrap.seekarc:sweepAngle} | |
+ {@link #SeekArc_thumb com.triggertrap.seekarc:thumb} | |
+ {@link #SeekArc_thumbOffset com.triggertrap.seekarc:thumbOffset} | |
+ {@link #SeekArc_touchInside com.triggertrap.seekarc:touchInside} | |
+
+ @see #SeekArc_arcColor
+ @see #SeekArc_arcWidth
+ @see #SeekArc_clockwise
+ @see #SeekArc_max
+ @see #SeekArc_progress
+ @see #SeekArc_progressColor
+ @see #SeekArc_progressWidth
+ @see #SeekArc_rotation
+ @see #SeekArc_roundEdges
+ @see #SeekArc_startAngle
+ @see #SeekArc_sweepAngle
+ @see #SeekArc_thumb
+ @see #SeekArc_thumbOffset
+ @see #SeekArc_touchInside
+ */
+ public static final int[] SeekArc = {
+ 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003,
+ 0x7f010004, 0x7f010005, 0x7f010006, 0x7f010007,
+ 0x7f010008, 0x7f010009, 0x7f01000a, 0x7f01000b,
+ 0x7f01000c, 0x7f01000d
+ };
+ /**
+ This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#arcColor}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:arcColor
+ */
+ public static final int SeekArc_arcColor = 9;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#arcWidth}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:arcWidth
+ */
+ public static final int SeekArc_arcWidth = 4;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#clockwise}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:clockwise
+ */
+ public static final int SeekArc_clockwise = 13;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#max}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:max
+ */
+ public static final int SeekArc_max = 2;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#progress}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:progress
+ */
+ public static final int SeekArc_progress = 5;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#progressColor}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:progressColor
+ */
+ public static final int SeekArc_progressColor = 10;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#progressWidth}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:progressWidth
+ */
+ public static final int SeekArc_progressWidth = 3;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#rotation}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:rotation
+ */
+ public static final int SeekArc_rotation = 6;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#roundEdges}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:roundEdges
+ */
+ public static final int SeekArc_roundEdges = 11;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#startAngle}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:startAngle
+ */
+ public static final int SeekArc_startAngle = 7;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#sweepAngle}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:sweepAngle
+ */
+ public static final int SeekArc_sweepAngle = 8;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#thumb}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ @attr name com.triggertrap.seekarc:thumb
+ */
+ public static final int SeekArc_thumb = 0;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#thumbOffset}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:thumbOffset
+ */
+ public static final int SeekArc_thumbOffset = 1;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#touchInside}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:touchInside
+ */
+ public static final int SeekArc_touchInside = 12;
+ /** Attributes that can be used with a SeekArcTheme.
+
Includes the following attributes:
+
+
+
+ | Attribute | Description |
+ {@link #SeekArcTheme_seekArcStyle com.triggertrap.seekarc:seekArcStyle} | |
+
+ @see #SeekArcTheme_seekArcStyle
+ */
+ public static final int[] SeekArcTheme = {
+ 0x7f01000e
+ };
+ /**
+ This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#seekArcStyle}
+ attribute's value can be found in the {@link #SeekArcTheme} array.
+
+
+
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ @attr name com.triggertrap.seekarc:seekArcStyle
+ */
+ public static final int SeekArcTheme_seekArcStyle = 0;
+ };
+}
diff --git a/SeekArc/build/source/r/release/com/triggertrap/seekarc/R.java b/SeekArc/build/source/r/release/com/triggertrap/seekarc/R.java
new file mode 100644
index 0000000..b7b12f4
--- /dev/null
+++ b/SeekArc/build/source/r/release/com/triggertrap/seekarc/R.java
@@ -0,0 +1,418 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package com.triggertrap.seekarc;
+
+public final class R {
+ public static final class attr {
+ /**
Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int arcColor=0x7f010009;
+ /**
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int arcWidth=0x7f010004;
+ /**
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int clockwise=0x7f01000d;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int max=0x7f010002;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int progress=0x7f010005;
+ /**
Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int progressColor=0x7f01000a;
+ /**
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int progressWidth=0x7f010003;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int rotation=0x7f010006;
+ /**
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int roundEdges=0x7f01000b;
+ /**
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ */
+ public static int seekArcStyle=0x7f01000e;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int startAngle=0x7f010007;
+ /**
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int sweepAngle=0x7f010008;
+ /**
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ */
+ public static int thumb=0x7f010000;
+ /**
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int thumbOffset=0x7f010001;
+ /**
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ */
+ public static int touchInside=0x7f01000c;
+ }
+ public static final class color {
+ public static int progress_gray=0x7f030000;
+ public static int progress_gray_dark=0x7f030001;
+ }
+ public static final class drawable {
+ public static int scrubber_control_disabled_holo=0x7f020000;
+ public static int scrubber_control_focused_holo=0x7f020001;
+ public static int scrubber_control_normal_holo=0x7f020002;
+ public static int scrubber_control_pressed_holo=0x7f020003;
+ public static int seek_arc_control_selector=0x7f020004;
+ }
+ public static final class string {
+ public static int app_name=0x7f040000;
+ }
+ public static final class style {
+ public static int SeekArc=0x7f050000;
+ public static int SeekArcLight=0x7f050001;
+ }
+ public static final class styleable {
+ /** Attributes that can be used with a SeekArc.
+
Includes the following attributes:
+
+
+
+ | Attribute | Description |
+ {@link #SeekArc_arcColor com.triggertrap.seekarc:arcColor} | |
+ {@link #SeekArc_arcWidth com.triggertrap.seekarc:arcWidth} | |
+ {@link #SeekArc_clockwise com.triggertrap.seekarc:clockwise} | |
+ {@link #SeekArc_max com.triggertrap.seekarc:max} | |
+ {@link #SeekArc_progress com.triggertrap.seekarc:progress} | |
+ {@link #SeekArc_progressColor com.triggertrap.seekarc:progressColor} | |
+ {@link #SeekArc_progressWidth com.triggertrap.seekarc:progressWidth} | |
+ {@link #SeekArc_rotation com.triggertrap.seekarc:rotation} | |
+ {@link #SeekArc_roundEdges com.triggertrap.seekarc:roundEdges} | |
+ {@link #SeekArc_startAngle com.triggertrap.seekarc:startAngle} | |
+ {@link #SeekArc_sweepAngle com.triggertrap.seekarc:sweepAngle} | |
+ {@link #SeekArc_thumb com.triggertrap.seekarc:thumb} | |
+ {@link #SeekArc_thumbOffset com.triggertrap.seekarc:thumbOffset} | |
+ {@link #SeekArc_touchInside com.triggertrap.seekarc:touchInside} | |
+
+ @see #SeekArc_arcColor
+ @see #SeekArc_arcWidth
+ @see #SeekArc_clockwise
+ @see #SeekArc_max
+ @see #SeekArc_progress
+ @see #SeekArc_progressColor
+ @see #SeekArc_progressWidth
+ @see #SeekArc_rotation
+ @see #SeekArc_roundEdges
+ @see #SeekArc_startAngle
+ @see #SeekArc_sweepAngle
+ @see #SeekArc_thumb
+ @see #SeekArc_thumbOffset
+ @see #SeekArc_touchInside
+ */
+ public static final int[] SeekArc = {
+ 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003,
+ 0x7f010004, 0x7f010005, 0x7f010006, 0x7f010007,
+ 0x7f010008, 0x7f010009, 0x7f01000a, 0x7f01000b,
+ 0x7f01000c, 0x7f01000d
+ };
+ /**
+ This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#arcColor}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:arcColor
+ */
+ public static final int SeekArc_arcColor = 9;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#arcWidth}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:arcWidth
+ */
+ public static final int SeekArc_arcWidth = 4;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#clockwise}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:clockwise
+ */
+ public static final int SeekArc_clockwise = 13;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#max}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:max
+ */
+ public static final int SeekArc_max = 2;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#progress}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:progress
+ */
+ public static final int SeekArc_progress = 5;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#progressColor}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a color value, in the form of "#rgb", "#argb",
+"#rrggbb", or "#aarrggbb".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:progressColor
+ */
+ public static final int SeekArc_progressColor = 10;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#progressWidth}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:progressWidth
+ */
+ public static final int SeekArc_progressWidth = 3;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#rotation}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:rotation
+ */
+ public static final int SeekArc_rotation = 6;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#roundEdges}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:roundEdges
+ */
+ public static final int SeekArc_roundEdges = 11;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#startAngle}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:startAngle
+ */
+ public static final int SeekArc_startAngle = 7;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#sweepAngle}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be an integer value, such as "100".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:sweepAngle
+ */
+ public static final int SeekArc_sweepAngle = 8;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#thumb}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ @attr name com.triggertrap.seekarc:thumb
+ */
+ public static final int SeekArc_thumb = 0;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#thumbOffset}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a dimension value, which is a floating point number appended with a unit such as "14.5sp".
+Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),
+in (inches), mm (millimeters).
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:thumbOffset
+ */
+ public static final int SeekArc_thumbOffset = 1;
+ /**
+
This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#touchInside}
+ attribute's value can be found in the {@link #SeekArc} array.
+
+
+
Must be a boolean value, either "true" or "false".
+
This may also be a reference to a resource (in the form
+"@[package:]type:name") or
+theme attribute (in the form
+"?[package:][type:]name")
+containing a value of this type.
+ @attr name com.triggertrap.seekarc:touchInside
+ */
+ public static final int SeekArc_touchInside = 12;
+ /** Attributes that can be used with a SeekArcTheme.
+
Includes the following attributes:
+
+
+
+ | Attribute | Description |
+ {@link #SeekArcTheme_seekArcStyle com.triggertrap.seekarc:seekArcStyle} | |
+
+ @see #SeekArcTheme_seekArcStyle
+ */
+ public static final int[] SeekArcTheme = {
+ 0x7f01000e
+ };
+ /**
+ This symbol is the offset where the {@link com.triggertrap.seekarc.R.attr#seekArcStyle}
+ attribute's value can be found in the {@link #SeekArcTheme} array.
+
+
+
Must be a reference to another resource, in the form "@[+][package:]type:name"
+or to a theme attribute in the form "?[package:][type:]name".
+ @attr name com.triggertrap.seekarc:seekArcStyle
+ */
+ public static final int SeekArcTheme_seekArcStyle = 0;
+ };
+}
diff --git a/SeekArc/libs/android-support-v4.jar b/SeekArc/libs/android-support-v4.jar
new file mode 100644
index 0000000..cf12d28
Binary files /dev/null and b/SeekArc/libs/android-support-v4.jar differ
diff --git a/SeekArc/licence.txt b/SeekArc/licence.txt
new file mode 100644
index 0000000..d52685c
--- /dev/null
+++ b/SeekArc/licence.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Triggertrap Ltd
+Author Neil Davies
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/SeekArc/proguard-project.txt b/SeekArc/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/SeekArc/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/SeekArc/project.properties b/SeekArc/project.properties
new file mode 100644
index 0000000..484dab0
--- /dev/null
+++ b/SeekArc/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
+android.library=true
diff --git a/SeekArc/src/main/AndroidManifest.xml b/SeekArc/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c2ddadd
--- /dev/null
+++ b/SeekArc/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/SeekArc/src/main/java/com/triggertrap/seekarc/SeekArc.java b/SeekArc/src/main/java/com/triggertrap/seekarc/SeekArc.java
new file mode 100644
index 0000000..3317523
--- /dev/null
+++ b/SeekArc/src/main/java/com/triggertrap/seekarc/SeekArc.java
@@ -0,0 +1,528 @@
+/*******************************************************************************
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013 Triggertrap Ltd
+ * Author Neil Davies
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ******************************************************************************/
+package com.triggertrap.seekarc;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ *
+ * SeekArc.java
+ *
+ * This is a class that functions much like a SeekBar but
+ * follows a circle path instead of a straight line.
+ *
+ * @author Neil Davies
+ *
+ */
+public class SeekArc extends View {
+
+ private static final String TAG = SeekArc.class.getSimpleName();
+ private static int INVALID_PROGRESS_VALUE = -1;
+ // The initial rotational offset -90 means we start at 12 o'clock
+ private final int mAngleOffset = -90;
+
+ /**
+ * The Drawable for the seek arc thumbnail
+ */
+ private Drawable mThumb;
+
+ /**
+ * The Maximum value that this SeekArc can be set to
+ */
+ private int mMax = 100;
+
+ /**
+ * The Current value that the SeekArc is set to
+ */
+ private int mProgress = 0;
+
+ /**
+ * The width of the progress line for this SeekArc
+ */
+ private int mProgressWidth = 4;
+
+ /**
+ * The Width of the background arc for the SeekArc
+ */
+ private int mArcWidth = 2;
+
+ /**
+ * The Angle to start drawing this Arc from
+ */
+ private int mStartAngle = 0;
+
+ /**
+ * The Angle through which to draw the arc (Max is 360)
+ */
+ private int mSweepAngle = 360;
+
+ /**
+ * The rotation of the SeekArc- 0 is twelve o'clock
+ */
+ private int mRotation = 0;
+
+ /**
+ * Give the SeekArc rounded edges
+ */
+ private boolean mRoundedEdges = false;
+
+ /**
+ * Enable touch inside the SeekArc
+ */
+ private boolean mTouchInside = true;
+
+ /**
+ * Will the progress increase clockwise or anti-clockwise
+ */
+ private boolean mClockwise = true;
+
+ // Internal variables
+ private int mArcRadius = 0;
+ private float mProgressSweep = 0;
+ private RectF mArcRect = new RectF();
+ private Paint mArcPaint;
+ private Paint mProgressPaint;
+ private int mTranslateX;
+ private int mTranslateY;
+ private int mThumbXPos;
+ private int mThumbYPos;
+ private double mTouchAngle;
+ private float mTouchIgnoreRadius;
+ private OnSeekArcChangeListener mOnSeekArcChangeListener;
+
+ public interface OnSeekArcChangeListener {
+
+ /**
+ * Notification that the progress level has changed. Clients can use the
+ * fromUser parameter to distinguish user-initiated changes from those
+ * that occurred programmatically.
+ *
+ * @param seekArc
+ * The SeekArc whose progress has changed
+ * @param progress
+ * The current progress level. This will be in the range
+ * 0..max where max was set by
+ * {@link ProgressArc#setMax(int)}. (The default value for
+ * max is 100.)
+ * @param fromUser
+ * True if the progress change was initiated by the user.
+ */
+ void onProgressChanged(SeekArc seekArc, int progress, boolean fromUser);
+
+ /**
+ * Notification that the user has started a touch gesture. Clients may
+ * want to use this to disable advancing the seekbar.
+ *
+ * @param seekArc
+ * The SeekArc in which the touch gesture began
+ */
+ void onStartTrackingTouch(SeekArc seekArc);
+
+ /**
+ * Notification that the user has finished a touch gesture. Clients may
+ * want to use this to re-enable advancing the seekarc.
+ *
+ * @param seekArc
+ * The SeekArc in which the touch gesture began
+ */
+ void onStopTrackingTouch(SeekArc seekArc);
+ }
+
+ public SeekArc(Context context) {
+ super(context);
+ init(context, null, 0);
+ }
+
+ public SeekArc(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, R.attr.seekArcStyle);
+ }
+
+ public SeekArc(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs, defStyle);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyle) {
+
+ Log.d(TAG, "Initialising SeekArc");
+ final Resources res = getResources();
+ float density = context.getResources().getDisplayMetrics().density;
+
+ // Defaults, may need to link this into theme settings
+ int arcColor = res.getColor(R.color.progress_gray);
+ int progressColor = res.getColor(android.R.color.holo_blue_light);
+ int thumbHalfheight = 0;
+ int thumbHalfWidth = 0;
+ mThumb = res.getDrawable(R.drawable.seek_arc_control_selector);
+ // Convert progress width to pixels for current density
+ mProgressWidth = (int) (mProgressWidth * density);
+
+
+ if (attrs != null) {
+ // Attribute initialization
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SeekArc, defStyle, 0);
+
+ Drawable thumb = a.getDrawable(R.styleable.SeekArc_thumb);
+ if (thumb != null) {
+ mThumb = thumb;
+ }
+
+
+
+ thumbHalfheight = (int) mThumb.getIntrinsicHeight() / 2;
+ thumbHalfWidth = (int) mThumb.getIntrinsicWidth() / 2;
+ mThumb.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth,
+ thumbHalfheight);
+
+ mMax = a.getInteger(R.styleable.SeekArc_max, mMax);
+ mProgress = a.getInteger(R.styleable.SeekArc_progress, mProgress);
+ mProgressWidth = (int) a.getDimension(
+ R.styleable.SeekArc_progressWidth, mProgressWidth);
+ mArcWidth = (int) a.getDimension(R.styleable.SeekArc_arcWidth,
+ mArcWidth);
+ mStartAngle = a.getInt(R.styleable.SeekArc_startAngle, mStartAngle);
+ mSweepAngle = a.getInt(R.styleable.SeekArc_sweepAngle, mSweepAngle);
+ mRotation = a.getInt(R.styleable.SeekArc_rotation, mRotation);
+ mRoundedEdges = a.getBoolean(R.styleable.SeekArc_roundEdges,
+ mRoundedEdges);
+ mTouchInside = a.getBoolean(R.styleable.SeekArc_touchInside,
+ mTouchInside);
+ mClockwise = a.getBoolean(R.styleable.SeekArc_clockwise,
+ mClockwise);
+
+ arcColor = a.getColor(R.styleable.SeekArc_arcColor, arcColor);
+ progressColor = a.getColor(R.styleable.SeekArc_progressColor,
+ progressColor);
+
+ a.recycle();
+ }
+
+ mProgress = (mProgress > mMax) ? mMax : mProgress;
+ mProgress = (mProgress < 0) ? 0 : mProgress;
+
+ mSweepAngle = (mSweepAngle > 360) ? 360 : mSweepAngle;
+ mSweepAngle = (mSweepAngle < 0) ? 0 : mSweepAngle;
+
+ mStartAngle = (mStartAngle > 360) ? 0 : mStartAngle;
+ mStartAngle = (mStartAngle < 0) ? 0 : mStartAngle;
+
+ mArcPaint = new Paint();
+ mArcPaint.setColor(arcColor);
+ mArcPaint.setAntiAlias(true);
+ mArcPaint.setStyle(Paint.Style.STROKE);
+ mArcPaint.setStrokeWidth(mArcWidth);
+ //mArcPaint.setAlpha(45);
+
+ mProgressPaint = new Paint();
+ mProgressPaint.setColor(progressColor);
+ mProgressPaint.setAntiAlias(true);
+ mProgressPaint.setStyle(Paint.Style.STROKE);
+ mProgressPaint.setStrokeWidth(mProgressWidth);
+
+ if (mRoundedEdges) {
+ mArcPaint.setStrokeCap(Paint.Cap.ROUND);
+ mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if(!mClockwise) {
+ canvas.scale(-1, 1, mArcRect.centerX(), mArcRect.centerY() );
+ }
+
+ // Draw the arcs
+ final int arcStart = mStartAngle + mAngleOffset + mRotation;
+ final int arcSweep = mSweepAngle;
+ canvas.drawArc(mArcRect, arcStart, arcSweep, false, mArcPaint);
+ canvas.drawArc(mArcRect, arcStart, mProgressSweep, false,
+ mProgressPaint);
+
+ // Draw the thumb nail
+ canvas.translate(mTranslateX -mThumbXPos, mTranslateY -mThumbYPos);
+ mThumb.draw(canvas);
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ final int height = getDefaultSize(getSuggestedMinimumHeight(),
+ heightMeasureSpec);
+ final int width = getDefaultSize(getSuggestedMinimumWidth(),
+ widthMeasureSpec);
+ final int min = Math.min(width, height);
+ float top = 0;
+ float left = 0;
+ int arcDiameter = 0;
+
+ mTranslateX = (int) (width * 0.5f);
+ mTranslateY = (int) (height * 0.5f);
+
+ arcDiameter = min - getPaddingLeft();
+ mArcRadius = arcDiameter / 2;
+ top = height / 2 - (arcDiameter / 2);
+ left = width / 2 - (arcDiameter / 2);
+ mArcRect.set(left, top, left + arcDiameter, top + arcDiameter);
+
+ int arcStart = (int)mProgressSweep + mStartAngle + mRotation + 90;
+ mThumbXPos = (int) (mArcRadius * Math.cos(Math.toRadians(arcStart)));
+ mThumbYPos = (int) (mArcRadius * Math.sin(Math.toRadians(arcStart)));
+
+ setTouchInSide(mTouchInside);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ onStartTrackingTouch();
+ updateOnTouch(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ updateOnTouch(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ onStopTrackingTouch();
+ setPressed(false);
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ onStopTrackingTouch();
+ setPressed(false);
+
+ break;
+ }
+
+ return true;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ if (mThumb != null && mThumb.isStateful()) {
+ int[] state = getDrawableState();
+ mThumb.setState(state);
+ }
+ invalidate();
+ }
+
+ private void onStartTrackingTouch() {
+ if (mOnSeekArcChangeListener != null) {
+ mOnSeekArcChangeListener.onStartTrackingTouch(this);
+ }
+ }
+
+ private void onStopTrackingTouch() {
+ if (mOnSeekArcChangeListener != null) {
+ mOnSeekArcChangeListener.onStopTrackingTouch(this);
+ }
+ }
+
+ private void updateOnTouch(MotionEvent event) {
+ boolean ignoreTouch = ignoreTouch(event.getX(), event.getY());
+ if (ignoreTouch) {
+ return;
+ }
+ setPressed(true);
+ mTouchAngle = getTouchDegrees(event.getX(), event.getY());
+ int progress = getProgressForAngle(mTouchAngle);
+ onProgressRefresh(progress, true);
+ }
+
+ private boolean ignoreTouch(float xPos, float yPos) {
+ boolean ignore = false;
+ float x = xPos - mTranslateX;
+ float y = yPos - mTranslateY;
+
+ float touchRadius = (float) Math.sqrt(((x * x) + (y * y)));
+ if (touchRadius < mTouchIgnoreRadius) {
+ ignore = true;
+ }
+ return ignore;
+ }
+
+ private double getTouchDegrees(float xPos, float yPos) {
+ float x = xPos - mTranslateX;
+ float y = yPos - mTranslateY;
+ //invert the x-coord if we are rotating anti-clockwise
+ x= (mClockwise) ? x:-x;
+ // convert to arc Angle
+ double angle = Math.toDegrees(Math.atan2(y, x) + (Math.PI / 2)
+ - Math.toRadians(mRotation));
+ if (angle < 0) {
+ angle = 360 + angle;
+ }
+ angle -= mStartAngle;
+ return angle;
+ }
+
+ private int getProgressForAngle(double angle) {
+ int touchProgress = (int) Math.round(valuePerDegree() * angle);
+
+ touchProgress = (touchProgress < 0) ? INVALID_PROGRESS_VALUE
+ : touchProgress;
+ touchProgress = (touchProgress > mMax) ? INVALID_PROGRESS_VALUE
+ : touchProgress;
+ return touchProgress;
+ }
+
+ private float valuePerDegree() {
+ return (float) mMax / mSweepAngle;
+ }
+
+ private void onProgressRefresh(int progress, boolean fromUser) {
+ updateProgress(progress, fromUser);
+ }
+
+ private void updateThumbPosition() {
+ int thumbAngle = (int) (mStartAngle + mProgressSweep + mRotation + 90);
+ mThumbXPos = (int) (mArcRadius * Math.cos(Math.toRadians(thumbAngle)));
+ mThumbYPos = (int) (mArcRadius * Math.sin(Math.toRadians(thumbAngle)));
+ }
+
+ private void updateProgress(int progress, boolean fromUser) {
+
+ if (progress == INVALID_PROGRESS_VALUE) {
+ return;
+ }
+
+ if (mOnSeekArcChangeListener != null) {
+ mOnSeekArcChangeListener
+ .onProgressChanged(this, progress, fromUser);
+ }
+
+ progress = (progress > mMax) ? mMax : progress;
+ progress = (mProgress < 0) ? 0 : progress;
+
+ mProgress = progress;
+ mProgressSweep = (float) progress / mMax * mSweepAngle;
+
+ updateThumbPosition();
+
+ invalidate();
+ }
+
+ /**
+ * Sets a listener to receive notifications of changes to the SeekArc's
+ * progress level. Also provides notifications of when the user starts and
+ * stops a touch gesture within the SeekArc.
+ *
+ * @param l
+ * The seek bar notification listener
+ *
+ * @see SeekArc.OnSeekBarChangeListener
+ */
+ public void setOnSeekArcChangeListener(OnSeekArcChangeListener l) {
+ mOnSeekArcChangeListener = l;
+ }
+
+ public void setProgress(int progress) {
+ updateProgress(progress, false);
+ }
+
+ public int getProgressWidth() {
+ return mProgressWidth;
+ }
+
+ public void setProgressWidth(int mProgressWidth) {
+ this.mProgressWidth = mProgressWidth;
+ mProgressPaint.setStrokeWidth(mProgressWidth);
+ }
+
+ public int getArcWidth() {
+ return mArcWidth;
+ }
+
+ public void setArcWidth(int mArcWidth) {
+ this.mArcWidth = mArcWidth;
+ mArcPaint.setStrokeWidth(mArcWidth);
+ }
+ public int getArcRotation() {
+ return mRotation;
+ }
+
+ public void setArcRotation(int mRotation) {
+ this.mRotation = mRotation;
+ updateThumbPosition();
+ }
+
+ public int getStartAngle() {
+ return mStartAngle;
+ }
+
+ public void setStartAngle(int mStartAngle) {
+ this.mStartAngle = mStartAngle;
+ updateThumbPosition();
+ }
+
+ public int getSweepAngle() {
+ return mSweepAngle;
+ }
+
+ public void setSweepAngle(int mSweepAngle) {
+ this.mSweepAngle = mSweepAngle;
+ updateThumbPosition();
+ }
+
+ public void setRoundedEdges(boolean isEnabled) {
+ mRoundedEdges = isEnabled;
+ if (mRoundedEdges) {
+ mArcPaint.setStrokeCap(Paint.Cap.ROUND);
+ mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+ } else {
+ mArcPaint.setStrokeCap(Paint.Cap.SQUARE);
+ mProgressPaint.setStrokeCap(Paint.Cap.SQUARE);
+ }
+ }
+
+ public void setTouchInSide(boolean isEnabled) {
+ int thumbHalfheight = (int) mThumb.getIntrinsicHeight() / 2;
+ int thumbHalfWidth = (int) mThumb.getIntrinsicWidth() / 2;
+ mTouchInside = isEnabled;
+ if (mTouchInside) {
+ mTouchIgnoreRadius = (float) mArcRadius / 4;
+ } else {
+ // Don't use the exact radius makes interaction too tricky
+ mTouchIgnoreRadius = mArcRadius
+ - Math.min(thumbHalfWidth, thumbHalfheight);
+ }
+ }
+
+ public void setClockwise(boolean isClockwise) {
+ mClockwise = isClockwise;
+ }
+}
diff --git a/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_disabled_holo.png b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_disabled_holo.png
new file mode 100644
index 0000000..62be77c
Binary files /dev/null and b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_disabled_holo.png differ
diff --git a/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_focused_holo.png b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_focused_holo.png
new file mode 100644
index 0000000..754dd2f
Binary files /dev/null and b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_focused_holo.png differ
diff --git a/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_normal_holo.png b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_normal_holo.png
new file mode 100644
index 0000000..d546a73
Binary files /dev/null and b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_normal_holo.png differ
diff --git a/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo.png b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo.png
new file mode 100644
index 0000000..0b62072
Binary files /dev/null and b/SeekArc/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo.png differ
diff --git a/SeekArc/src/main/res/drawable/seek_arc_control_selector.xml b/SeekArc/src/main/res/drawable/seek_arc_control_selector.xml
new file mode 100644
index 0000000..3414842
--- /dev/null
+++ b/SeekArc/src/main/res/drawable/seek_arc_control_selector.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/SeekArc/src/main/res/values/attrs.xml b/SeekArc/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..a2bac7c
--- /dev/null
+++ b/SeekArc/src/main/res/values/attrs.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SeekArc/src/main/res/values/colors.xml b/SeekArc/src/main/res/values/colors.xml
new file mode 100644
index 0000000..9e86417
--- /dev/null
+++ b/SeekArc/src/main/res/values/colors.xml
@@ -0,0 +1,28 @@
+
+
+
+ #FFD8D8D8
+ #FF383838
+
diff --git a/SeekArc/src/main/res/values/strings.xml b/SeekArc/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cf0db82
--- /dev/null
+++ b/SeekArc/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+ Seekarc_library
+
+
diff --git a/SeekArc/src/main/res/values/styles.xml b/SeekArc/src/main/res/values/styles.xml
new file mode 100644
index 0000000..38838de
--- /dev/null
+++ b/SeekArc/src/main/res/values/styles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/settings.gradle b/settings.gradle
index cef31ed..d090641 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':HypoAlarm', ':GlowPadBackport'
+include ':HypoAlarm', ':GlowPadBackport', ':SeekArc'