From 1caca1439a699b5a5ca0da48b9758175df6050a9 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Wed, 24 Jun 2015 20:14:08 +0200 Subject: [PATCH] Initial implementation of setting alarms to the Mi Band. The code basically works, but there a lot of things to fix / improve. * The alarms are stored to and read from the Shared Preferences, but there is no persistence within the app (basically they are read and stored at every access) * The alarm list is not updated when coming back from the alarm detail view (probably related to the point above), but the actual alarm is * The alarms preference names is sometimes built by concatenating strings, which is not really safe * There is no check in the alarm constructor whether the stored string is a valid alarm representation * Even though only 3 alarms can be stored on the device, we could have more in the app and let the user choose which to sync * In the alarm detail view XML some material* drawables are used, it's possible that these break on android version < 5 * ... --- app/src/main/AndroidManifest.xml | 16 ++ .../BluetoothCommunicationService.java | 4 + .../gadgetbridge/ControlCenter.java | 7 + .../gadgetbridge/EventHandler.java | 2 + .../freeyourgadget/gadgetbridge/GBAlarm.java | 138 +++++++++++++++++ .../gadgetbridge/ServiceDeviceSupport.java | 8 + .../gadgetbridge/activities/AlarmDetails.java | 140 ++++++++++++++++++ .../activities/ConfigureAlarms.java | 55 +++++++ .../adapter/GBAlarmListAdapter.java | 99 +++++++++++++ .../gadgetbridge/miband/MiBandConst.java | 4 + .../gadgetbridge/miband/MiBandService.java | 5 +- .../gadgetbridge/miband/MiBandSupport.java | 38 +++++ .../gadgetbridge/pebble/PebbleSupport.java | 5 + .../res/layout/activity_alarm_details.xml | 123 +++++++++++++++ .../res/layout/activity_configure_alarms.xml | 16 ++ app/src/main/res/layout/alarm_item.xml | 109 ++++++++++++++ .../main/res/menu/controlcenter_context.xml | 3 + app/src/main/res/menu/menu_alarm_details.xml | 7 + app/src/main/res/menu/menu_set_alarm.xml | 7 + app/src/main/res/values/strings.xml | 12 +- 20 files changed, 795 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBAlarm.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java create mode 100644 app/src/main/res/layout/activity_alarm_details.xml create mode 100644 app/src/main/res/layout/activity_configure_alarms.xml create mode 100644 app/src/main/res/layout/alarm_item.xml create mode 100644 app/src/main/res/menu/menu_alarm_details.xml create mode 100644 app/src/main/res/menu/menu_set_alarm.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7df4110df..563016f89 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -194,6 +194,22 @@ android:name="android.support.PARENT_ACTIVITY" android:value="nodomain.freeyourgadget.gadgetbridge.ControlCenter" /> + + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java index e4d39b00e..74562d351 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java @@ -60,6 +60,7 @@ public class BluetoothCommunicationService extends Service { public static final String ACTION_FETCH_ACTIVITY_DATA = "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.fetch_activity_data"; public static final String ACTION_DISCONNECT = "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.disconnect"; public static final String ACTION_FIND_DEVICE = "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.find_device"; + public static final String ACTION_SET_ALARMS = "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.set_alarms"; public static final String EXTRA_PERFORM_PAIR = "perform_pair"; @@ -265,6 +266,9 @@ public class BluetoothCommunicationService extends Service { startForeground(GB.NOTIFICATION_ID, GB.createNotification(getString(R.string.gadgetbridge_running), this)); mStarted = true; break; + case ACTION_SET_ALARMS: + mDeviceSupport.onSetAlarms(); + } return START_STICKY; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java index c2f332782..22ca6ede9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms; import nodomain.freeyourgadget.gadgetbridge.activities.ChartsActivity; import nodomain.freeyourgadget.gadgetbridge.activities.SleepChartActivity; import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAdapter; @@ -230,6 +231,12 @@ public class ControlCenter extends Activity { } }); } + case R.id.controlcenter_configure_alarms: + if (selectedDevice != null) { + Intent startIntent; + startIntent = new Intent(ControlCenter.this, ConfigureAlarms.class); + startActivity(startIntent); + } return true; case R.id.controlcenter_take_screenshot: if (selectedDevice != null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java index 3f4e76533..29d7b1cce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/EventHandler.java @@ -13,6 +13,8 @@ public interface EventHandler { void onSetTime(long ts); + void onSetAlarms(); + void onSetCallState(String number, String name, GBCommand command); void onSetMusicInfo(String artist, String album, String track); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBAlarm.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBAlarm.java new file mode 100644 index 000000000..8232423e6 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBAlarm.java @@ -0,0 +1,138 @@ +package nodomain.freeyourgadget.gadgetbridge; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import java.util.Calendar; + +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM_PREFIX; + +public class GBAlarm { + private int index; + private boolean enabled; + private boolean smartWakeup; + private int repetition; + private int hour; + private int minute; + + public static final byte ALARM_ONCE = 0; + public static final byte ALARM_MON = 1; + public static final byte ALARM_TUE = 2; + public static final byte ALARM_WED = 4; + public static final byte ALARM_THU = 8; + public static final byte ALARM_FRI = 16; + public static final byte ALARM_SAT = 32; + public static final byte ALARM_SUN = 64; + + public static final String DEFAULT_ALARM1 = "0,false,true,31,7,30"; + public static final String DEFAULT_ALARM2 = "1,false,false,96,8,00"; + public static final String DEFAULT_ALARM3 = "2,false,false,0,15,30"; + + public GBAlarm(int index, boolean enabled, boolean smartWakeup, byte repetition, int hour, int minute) { + this.index = index; + this.enabled = enabled; + this.smartWakeup = smartWakeup; + this.repetition = repetition; + this.hour = hour; + this.minute = minute; + store(); + } + + public GBAlarm(String fromPreferences){ + String[] tokens = fromPreferences.split(","); + //TODO: sanify the string! + this.index = Integer.parseInt(tokens[0]); + this.enabled = Boolean.parseBoolean(tokens[1]); + this.smartWakeup = Boolean.parseBoolean(tokens[2]); + this.repetition = Integer.parseInt(tokens[3]); + this.hour = Integer.parseInt(tokens[4]); + this.minute = Integer.parseInt(tokens[5]); + store(); + } + + public int getIndex() { + return this.index; + } + + public String getTime() { + return String.format("%02d",this.hour) + ":" + String.format("%02d",this.minute); + } + + public int getHour(){ + return this.hour; + } + + public int getMinute(){ + return this.minute; + } + public Calendar getAlarmCal() { + Calendar alarm = Calendar.getInstance(); + alarm.set(Calendar.HOUR_OF_DAY, this.hour); + alarm.set(Calendar.MINUTE, this.minute); + return alarm; + } + + public boolean isEnabled() { + return this.enabled; + } + + public boolean isSmartWakeup() { + return this.smartWakeup; + } + + public boolean getRepetition(int dow) { + return (this.repetition & dow) > 0; + } + + public int getRepetitionMask() { + return this.repetition; + } + + public String toPreferences() { + return String.valueOf(this.index)+','+ + String.valueOf(this.enabled)+','+ + String.valueOf(this.smartWakeup)+','+ + String.valueOf(this.repetition)+','+ + String.valueOf(this.hour)+','+ + String.valueOf(this.minute); + } + + public void setSmartWakeup(boolean smartWakeup) { + this.smartWakeup = smartWakeup; + store(); + } + + public void setRepetition(boolean mon, boolean tue, boolean wed, boolean thu, boolean fri, boolean sat, boolean sun) { + this.repetition = ALARM_ONCE | + (mon ? ALARM_MON : 0) | + (tue ? ALARM_TUE : 0) | + (wed ? ALARM_WED : 0) | + (thu ? ALARM_THU : 0) | + (fri ? ALARM_FRI : 0) | + (sat ? ALARM_SAT : 0) | + (sun ? ALARM_SUN : 0); + store(); + } + + public void setHour(int hour) { + this.hour = hour; + store(); + } + + public void setMinute(int minute) { + this.minute = minute; + store(); + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + store(); // TODO: if we have many setters, this may become a bottleneck + } + + private void store() { + //TODO: I don't like to have the alarm index both in the preference name and in the value + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + String pref = PREF_MIBAND_ALARM_PREFIX +(this.index+1); + sharedPrefs.edit().putString(pref, this.toPreferences()).apply(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ServiceDeviceSupport.java index 9a17893b8..e3a63afe0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ServiceDeviceSupport.java @@ -226,4 +226,12 @@ public class ServiceDeviceSupport implements DeviceSupport { } delegate.onScreenshotReq(); } + + @Override + public void onSetAlarms() { + if (checkBusy("set alarms")) { + return; + } + delegate.onSetAlarms(); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java new file mode 100644 index 000000000..a9c4d709a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java @@ -0,0 +1,140 @@ +package nodomain.freeyourgadget.gadgetbridge.activities; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.CheckedTextView; +import android.widget.TimePicker; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; + +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM_PREFIX; + +public class AlarmDetails extends Activity { + + private static final Logger LOG = LoggerFactory.getLogger(AlarmDetails.class); + + + private GBAlarm alarm; + private TimePicker timePicker; + //using CheckedTextView allows for vertically aligned text + private CheckedTextView ctvSmartWakeup; + private CheckedTextView ctvMonday; + private CheckedTextView ctvTuesday; + private CheckedTextView ctvWednesday; + private CheckedTextView ctvThursday; + private CheckedTextView ctvFriday; + private CheckedTextView ctvSaturday; + private CheckedTextView ctvSunday; + + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_alarm_details); + + int index = getIntent().getExtras().getInt("alarm_index"); + if (index <0 || index > 2) { + finish(); + }else { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + String pref = PREF_MIBAND_ALARM_PREFIX +(index+1); + alarm = new GBAlarm(sharedPrefs.getString(pref, "")); + //TODO: this is horrible and error-prone + + timePicker = (TimePicker) findViewById(R.id.alarm_time_picker); + ctvSmartWakeup = (CheckedTextView) findViewById(R.id.alarm_ctv_smart_wakeup); + ctvMonday = (CheckedTextView) findViewById(R.id.alarm_ctv_mon); + ctvTuesday = (CheckedTextView) findViewById(R.id.alarm_ctv_tue); + ctvWednesday = (CheckedTextView) findViewById(R.id.alarm_ctv_wed); + ctvThursday = (CheckedTextView) findViewById(R.id.alarm_ctv_thu); + ctvFriday = (CheckedTextView) findViewById(R.id.alarm_ctv_fri); + ctvSaturday = (CheckedTextView) findViewById(R.id.alarm_ctv_sat); + ctvSunday = (CheckedTextView) findViewById(R.id.alarm_ctv_sun); + + timePicker.setIs24HourView(DateFormat.is24HourFormat(GBApplication.getContext())); + timePicker.setCurrentHour(alarm.getHour()); + timePicker.setCurrentMinute(alarm.getMinute()); + + ctvSmartWakeup.setChecked(alarm.isSmartWakeup()); + ctvSmartWakeup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + + ctvMonday.setChecked(alarm.getRepetition(GBAlarm.ALARM_MON)); + ctvTuesday.setChecked(alarm.getRepetition(GBAlarm.ALARM_TUE)); + ctvWednesday.setChecked(alarm.getRepetition(GBAlarm.ALARM_WED)); + ctvThursday.setChecked(alarm.getRepetition(GBAlarm.ALARM_THU)); + ctvFriday.setChecked(alarm.getRepetition(GBAlarm.ALARM_FRI)); + ctvSaturday.setChecked(alarm.getRepetition(GBAlarm.ALARM_SAT)); + ctvSunday.setChecked(alarm.getRepetition(GBAlarm.ALARM_SUN)); + + ctvMonday.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + ctvTuesday.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + ctvWednesday.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + ctvThursday.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + ctvFriday.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + ctvSaturday.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + ctvSunday.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((CheckedTextView) v).toggle(); + } + }); + + } + + } + + + @Override + protected void onDestroy() { + super.onDestroy(); + alarm.setSmartWakeup(ctvSmartWakeup.isChecked()); + alarm.setRepetition(ctvMonday.isChecked(),ctvTuesday.isChecked(),ctvWednesday.isChecked(),ctvThursday.isChecked(),ctvFriday.isChecked(),ctvSaturday.isChecked(),ctvSunday.isChecked()); + alarm.setHour(timePicker.getCurrentHour()); + alarm.setMinute(timePicker.getCurrentMinute()); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java new file mode 100644 index 000000000..c3176d6c2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -0,0 +1,55 @@ +package nodomain.freeyourgadget.gadgetbridge.activities; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.app.Activity; +import android.preference.PreferenceManager; +import android.widget.ListView; + + +import nodomain.freeyourgadget.gadgetbridge.BluetoothCommunicationService; +import nodomain.freeyourgadget.gadgetbridge.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.adapter.GBAlarmListAdapter; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM1; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM2; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM3; + + +import java.util.ArrayList; +import java.util.List; + + +public class ConfigureAlarms extends Activity { + + ListView alarmListView; + private GBAlarmListAdapter mGBAlarmListAdapter; + + final List alarmList = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_configure_alarms); + getActionBar().setDisplayHomeAsUpEnabled(true); + + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + //The GBAlarm class initializes the sharedPrefs values if they're missing, no need to handle it here + alarmList.add(new GBAlarm(sharedPrefs.getString(PREF_MIBAND_ALARM1, GBAlarm.DEFAULT_ALARM1))); + alarmList.add(new GBAlarm(sharedPrefs.getString(PREF_MIBAND_ALARM2, GBAlarm.DEFAULT_ALARM2))); + alarmList.add(new GBAlarm(sharedPrefs.getString(PREF_MIBAND_ALARM3, GBAlarm.DEFAULT_ALARM3))); + + alarmListView = (ListView) findViewById(R.id.alarmListView); + mGBAlarmListAdapter = new GBAlarmListAdapter(this, alarmList); + alarmListView.setAdapter(this.mGBAlarmListAdapter); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + Intent startIntent = new Intent(ConfigureAlarms.this, BluetoothCommunicationService.class); + startIntent.setAction(BluetoothCommunicationService.ACTION_SET_ALARMS); + startService(startIntent); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java new file mode 100644 index 000000000..c5e6ff97a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java @@ -0,0 +1,99 @@ +package nodomain.freeyourgadget.gadgetbridge.adapter; + + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; +import android.content.Intent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CompoundButton; +import android.widget.Switch; +import android.widget.TextView; + +import nodomain.freeyourgadget.gadgetbridge.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.AlarmDetails; + + +import java.util.List; + +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM1; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM2; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM3; + + +public class GBAlarmListAdapter extends ArrayAdapter { + private final Context mContext; + + private List alarmList; + + public GBAlarmListAdapter(Context context, List alarmList) { + super(context, 0, alarmList); + + this.mContext = context; + this.alarmList = alarmList; + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + + final GBAlarm alarm = getItem(position); + + if (view == null) { + LayoutInflater inflater = (LayoutInflater) mContext + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = inflater.inflate(R.layout.alarm_item, parent, false); + } + + TextView alarmTime = (TextView) view.findViewById(R.id.alarm_item_time); + Switch isEnabled = (Switch) view.findViewById(R.id.alarm_item_toggle); + TextView isSmartWakeup = (TextView) view.findViewById(R.id.alarm_smart_wakeup); + + highlightDay((TextView) view.findViewById(R.id.alarm_item_sunday), alarm.getRepetition(GBAlarm.ALARM_SUN)); + highlightDay((TextView) view.findViewById(R.id.alarm_item_monday), alarm.getRepetition(GBAlarm.ALARM_MON)); + highlightDay((TextView) view.findViewById(R.id.alarm_item_tuesday), alarm.getRepetition(GBAlarm.ALARM_TUE)); + highlightDay((TextView) view.findViewById(R.id.alarm_item_wednesday), alarm.getRepetition(GBAlarm.ALARM_WED)); + highlightDay((TextView) view.findViewById(R.id.alarm_item_thursday), alarm.getRepetition(GBAlarm.ALARM_THU)); + highlightDay((TextView) view.findViewById(R.id.alarm_item_friday), alarm.getRepetition(GBAlarm.ALARM_FRI)); + highlightDay((TextView) view.findViewById(R.id.alarm_item_saturday), alarm.getRepetition(GBAlarm.ALARM_SAT)); + + isEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + alarm.setEnabled(isChecked); + } + }); + + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent startIntent; + startIntent = new Intent(mContext, AlarmDetails.class); + startIntent.putExtra("alarm_index", alarm.getIndex()); + mContext.startActivity(startIntent); + } + }); + alarmTime.setText(alarm.getTime()); + isEnabled.setChecked(alarm.isEnabled()); + if(alarm.isSmartWakeup()) { + isSmartWakeup.setVisibility(TextView.VISIBLE); + } else { + isSmartWakeup.setVisibility(TextView.GONE); + } + + return view; + } + + private void highlightDay(TextView view, boolean isOn) { + if (isOn) { + view.setTextColor(Color.BLUE); + } else { + view.setTextColor(Color.BLACK); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java index 9754a5fa0..7d26e07e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandConst.java @@ -15,6 +15,10 @@ public final class MiBandConst { public static final String PREF_USER_WEIGHT_KG = "mi_user_weight_kg"; public static final String PREF_MIBAND_WEARSIDE = "mi_wearside"; public static final String PREF_MIBAND_ADDRESS = "development_miaddr"; // FIXME: should be prefixed mi_ + public static final String PREF_MIBAND_ALARM_PREFIX = "mi_alarm"; + public static final String PREF_MIBAND_ALARM1 = PREF_MIBAND_ALARM_PREFIX +"1"; + public static final String PREF_MIBAND_ALARM2 = PREF_MIBAND_ALARM_PREFIX +"2"; + public static final String PREF_MIBAND_ALARM3 = PREF_MIBAND_ALARM_PREFIX +"3"; public static final String ORIGIN_SMS = "sms"; public static final String ORIGIN_INCOMING_CALL = "incoming_call"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java index ff53ae32a..e9500f365 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandService.java @@ -134,6 +134,9 @@ public class MiBandService { public static final byte COMMAND_CONFIRM_ACTIVITY_DATA_TRANSFER_COMPLETE = 0xa; public static final byte COMMAND_FETCH_DATA = 0x6; + + public static final byte COMMAND_SET_TIMER = 0x4; + /* @@ -152,8 +155,6 @@ public class MiBandService { public static final COMMAND_SET_REALTIME_STEPS_NOTIFICATION = 0x3t - public static final COMMAND_SET_TIMER = 0x4t - public static final COMMAND_SET_WEAR_LOCATION = 0xft public static final COMMAND_STOP_SYNC_DATA = 0x11t diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java index 40a03fb27..ef195b0b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/miband/MiBandSupport.java @@ -18,6 +18,7 @@ import java.util.GregorianCalendar; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBActivitySample; +import nodomain.freeyourgadget.gadgetbridge.GBAlarm; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBCommand; import nodomain.freeyourgadget.gadgetbridge.GBDevice.State; @@ -44,6 +45,9 @@ import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.FLASH_ORIG import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_GENERIC; import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_K9MAIL; import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.ORIGIN_SMS; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM1; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM2; +import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.PREF_MIBAND_ALARM3; import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_COUNT; import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_DURATION; import static nodomain.freeyourgadget.gadgetbridge.miband.MiBandConst.VIBRATION_PAUSE; @@ -323,6 +327,21 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return VibrationProfile.getProfile(profileId, (byte) (repeat & 0xfff)); } + @Override + public void onSetAlarms() { + try { + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + TransactionBuilder builder = performInitialized("Set alarm"); + queueAlarm(new GBAlarm(sharedPrefs.getString(PREF_MIBAND_ALARM1, GBAlarm.DEFAULT_ALARM1)), builder, characteristic); + queueAlarm(new GBAlarm(sharedPrefs.getString(PREF_MIBAND_ALARM2, GBAlarm.DEFAULT_ALARM2)), builder, characteristic); + queueAlarm(new GBAlarm(sharedPrefs.getString(PREF_MIBAND_ALARM3, GBAlarm.DEFAULT_ALARM3)), builder, characteristic); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to set alarms on MI device", ex); + } + } + @Override public void onSMS(String from, String body) { performPreferredNotification("sms received", ORIGIN_SMS, null); @@ -544,6 +563,25 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + + private void queueAlarm(GBAlarm alarm, TransactionBuilder builder, BluetoothGattCharacteristic characteristic) { + Calendar alarmCal = alarm.getAlarmCal(); + byte[] alarmMessage = new byte[] { + (byte) MiBandService.COMMAND_SET_TIMER, + (byte) alarm.getIndex(), + (byte) (alarm.isEnabled() ? 1: 0), + (byte) (alarmCal.get(Calendar.YEAR) - 2000), + (byte) alarmCal.get(Calendar.MONTH), + (byte) alarmCal.get(Calendar.DATE), + (byte) alarmCal.get(Calendar.HOUR_OF_DAY), + (byte) alarmCal.get(Calendar.MINUTE), + (byte) alarmCal.get(Calendar.SECOND), + (byte) (alarm.isSmartWakeup() ? 30 : 0), + (byte) alarm.getRepetitionMask() + }; + builder.write(characteristic, alarmMessage); + } + private void handleActivityNotif(byte[] value) { if (value.length == 11) { // byte 0 is the data type: 1 means that each minute is represented by a triplet of bytes diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleSupport.java index c868adc0a..4d6fad6ea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/pebble/PebbleSupport.java @@ -38,4 +38,9 @@ public class PebbleSupport extends AbstractBTDeviceSupport { public synchronized PebbleIoThread getDeviceIOThread() { return (PebbleIoThread) super.getDeviceIOThread(); } + + @Override + public void onSetAlarms() { + //nothing to do ATM + } } diff --git a/app/src/main/res/layout/activity_alarm_details.xml b/app/src/main/res/layout/activity_alarm_details.xml new file mode 100644 index 000000000..a84c7d129 --- /dev/null +++ b/app/src/main/res/layout/activity_alarm_details.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_configure_alarms.xml b/app/src/main/res/layout/activity_configure_alarms.xml new file mode 100644 index 000000000..83e4af074 --- /dev/null +++ b/app/src/main/res/layout/activity_configure_alarms.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/layout/alarm_item.xml b/app/src/main/res/layout/alarm_item.xml new file mode 100644 index 000000000..6bfa02f7d --- /dev/null +++ b/app/src/main/res/layout/alarm_item.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/controlcenter_context.xml b/app/src/main/res/menu/controlcenter_context.xml index 4002869d4..c9c07d963 100644 --- a/app/src/main/res/menu/controlcenter_context.xml +++ b/app/src/main/res/menu/controlcenter_context.xml @@ -6,6 +6,9 @@ + diff --git a/app/src/main/res/menu/menu_alarm_details.xml b/app/src/main/res/menu/menu_alarm_details.xml new file mode 100644 index 000000000..14e9fe768 --- /dev/null +++ b/app/src/main/res/menu/menu_alarm_details.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/menu/menu_set_alarm.xml b/app/src/main/res/menu/menu_set_alarm.xml new file mode 100644 index 000000000..835ed8d52 --- /dev/null +++ b/app/src/main/res/menu/menu_set_alarm.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e9bfccd75..864df02b6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -138,5 +138,15 @@ Find lost Device Cancel to stop vibration. ChartsActivity - + Configure Alarms + Configure alarms + Alarm Details + Sun + Mon + Tue + Wed + Thu + Fri + Sat + smart wakeup