1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-24 02:46:50 +01:00

Amazfit GTR 4: Initial support

This commit is contained in:
José Rebelo 2022-10-22 20:53:45 +01:00 committed by Gitea
parent 7e7019886d
commit addf7ff6a6
83 changed files with 3753 additions and 1281 deletions

View File

@ -35,7 +35,7 @@ vendor's servers.
- [Bip](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip)
- [Bip Lite](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-Lite), [Bip S](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-S), [Bip U](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-U) [**\[!\]**](#special-pairing-procedures)
- [Cor](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor), [Cor 2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor-2)
- [GTR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR), [GTR 2/2e](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR), [GTR 3](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR-3) [**\[!\]**](#special-pairing-procedures)
- [GTR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR), [GTR 2/2e](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR), [GTR 3](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR-3), [GTR 4](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR-4) [**\[!\]**](#special-pairing-procedures)
- [GTS](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS), [GTS 2/2e](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS), [GTS 3](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS-3) [**\[!\]**](#special-pairing-procedures)
- [Neo](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Neo) [**\[!\]**](#special-pairing-procedures)
- T-Rex [**\[!\]**](#special-pairing-procedures)

View File

@ -28,14 +28,12 @@ import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.StatusPrinter;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
public abstract class Logging {
// Only used for tests
@ -54,7 +52,7 @@ public abstract class Logging {
} else {
stopFileLogger();
}
getLogger().info("Gadgetbridge version: {}", BuildConfig.VERSION_NAME);
getLogger().info("Gadgetbridge version: {}-{}", BuildConfig.VERSION_NAME, BuildConfig.GIT_HASH_SHORT);
} catch (Exception ex) {
Log.e("GBApplication", "External files dir not available, cannot log to file", ex);
stopFileLogger();

View File

@ -17,17 +17,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.os.Bundle;
import androidx.preference.Preference;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_ACTIVETIME_MINUTES;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_CALORIES_BURNT;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_DISTANCE_METERS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GENDER;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GOAL_FAT_BURN_TIME_MINUTES;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GOAL_STANDING_TIME_HOURS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GOAL_WEIGHT_KG;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_NAME;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_SLEEP_DURATION;
@ -36,6 +32,10 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_WEIGHT_KG;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_YEAR_OF_BIRTH;
import android.os.Bundle;
import nodomain.freeyourgadget.gadgetbridge.R;
public class AboutUserPreferencesActivity extends AbstractSettingsActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -47,12 +47,18 @@ public class AboutUserPreferencesActivity extends AbstractSettingsActivity {
addPreferenceHandlerFor(PREF_USER_WEIGHT_KG);
addPreferenceHandlerFor(PREF_USER_GENDER);
addPreferenceHandlerFor(PREF_USER_STEPS_GOAL);
addPreferenceHandlerFor(PREF_USER_GOAL_WEIGHT_KG);
addPreferenceHandlerFor(PREF_USER_GOAL_STANDING_TIME_HOURS);
addPreferenceHandlerFor(PREF_USER_GOAL_FAT_BURN_TIME_MINUTES);
addIntentNotificationListener(PREF_USER_STEPS_GOAL);
addIntentNotificationListener(PREF_USER_HEIGHT_CM);
addIntentNotificationListener(PREF_USER_SLEEP_DURATION);
addIntentNotificationListener(PREF_USER_STEP_LENGTH_CM);
addIntentNotificationListener(PREF_USER_DISTANCE_METERS);
addIntentNotificationListener(PREF_USER_GOAL_WEIGHT_KG);
addIntentNotificationListener(PREF_USER_GOAL_STANDING_TIME_HOURS);
addIntentNotificationListener(PREF_USER_GOAL_FAT_BURN_TIME_MINUTES);
}
@Override
@ -66,6 +72,10 @@ public class AboutUserPreferencesActivity extends AbstractSettingsActivity {
PREF_USER_STEP_LENGTH_CM,
PREF_USER_ACTIVETIME_MINUTES,
PREF_USER_CALORIES_BURNT,
PREF_USER_DISTANCE_METERS,};
PREF_USER_DISTANCE_METERS,
PREF_USER_GOAL_WEIGHT_KG,
PREF_USER_GOAL_STANDING_TIME_HOURS,
PREF_USER_GOAL_FAT_BURN_TIME_MINUTES
};
}
}

View File

@ -86,7 +86,7 @@ public class ConfigureReminders extends AbstractGBActivity {
final Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
int reservedSlots = prefs.getInt(DeviceSettingsPreferenceConst.PREF_RESERVER_REMINDERS_CALENDAR, coordinator.supportsCalendarEvents() ? 0 : 9);
int deviceSlots = coordinator.getReminderSlotCount() - reservedSlots;
int deviceSlots = coordinator.getReminderSlotCount(gbDevice) - reservedSlots;
if (mGBReminderListAdapter.getItemCount() >= deviceSlots) {
// No more free slots

View File

@ -18,7 +18,6 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@ -34,13 +33,11 @@ import android.os.Vibrator;
import android.view.View;
import android.widget.Button;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -50,18 +47,29 @@ import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
public class FindPhoneActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(FindPhoneActivity.class);
public static final String ACTION_FOUND
= "nodomain.freeyourgadget.gadgetbridge.findphone.action.reply";
public static final String ACTION_FOUND = "nodomain.freeyourgadget.gadgetbridge.findphone.action.reply";
public static final String ACTION_VIBRATE = "nodomain.freeyourgadget.gadgetbridge.findphone.action.vibrate";
public static final String ACTION_RING = "nodomain.freeyourgadget.gadgetbridge.findphone.action.ring";
public static final String EXTRA_RING = "extra_ring";
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
LOG.info("Got action: {}", action);
switch (action) {
case ACTION_FOUND: {
case ACTION_FOUND:
finish();
break;
}
case ACTION_VIBRATE:
stopSound();
break;
case ACTION_RING:
playRingtone();
break;
}
}
}
@ -77,8 +85,12 @@ public class FindPhoneActivity extends AbstractGBActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_phone);
final boolean ring = getIntent().getBooleanExtra(EXTRA_RING, true);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_FOUND);
filter.addAction(ACTION_VIBRATE);
filter.addAction(ACTION_RING);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
registerReceiver(mReceiver, filter); // for ACTION_FOUND
@ -93,8 +105,11 @@ public class FindPhoneActivity extends AbstractGBActivity {
GB.removeNotification(GB.NOTIFICATION_ID_PHONE_FIND, this);
vibrate();
if (ring) {
playRingtone();
}
GBApplication.deviceService().onFindPhone(true);
}
private void vibrate(){
mVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
@ -115,7 +130,12 @@ public class FindPhoneActivity extends AbstractGBActivity {
if (mAudioManager != null) {
userVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
}
if (mp != null && mp.isPlaying()) {
LOG.warn("Already playing");
return;
} else if (mp == null) {
mp = new MediaPlayer();
}
if (!playConfiguredRingtone()) {
playFallbackRingtone();
@ -172,19 +192,24 @@ public class FindPhoneActivity extends AbstractGBActivity {
}
private void stopSound() {
if (mAudioManager != null) {
mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, userVolume, AudioManager.FLAG_PLAY_SOUND);
}
if (mp != null) {
mp.stop();
mp.reset();
mp.release();
mp = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stopVibration();
stopSound();
GBApplication.deviceService().onPhoneFound();
GBApplication.deviceService().onFindPhone(false);
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
unregisterReceiver(mReceiver);

View File

@ -17,6 +17,25 @@
package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings;
public class DeviceSettingsPreferenceConst {
public static final String PREF_HEADER_TIME = "pref_header_time";
public static final String PREF_HEADER_DISPLAY = "pref_header_display";
public static final String PREF_HEADER_HEALTH = "pref_header_health";
public static final String PREF_HEADER_WORKOUT = "pref_header_workout";
public static final String PREF_HEADER_WORKOUT_DETECTION = "pref_header_workout_detection";
public static final String PREF_HEADER_GPS = "pref_header_gps";
public static final String PREF_HEADER_AGPS = "pref_header_agps";
public static final String PREF_SCREEN_NIGHT_MODE = "pref_screen_night_mode";
public static final String PREF_SCREEN_SLEEP_MODE = "pref_screen_sleep_mode";
public static final String PREF_SCREEN_LIFT_WRIST = "pref_screen_lift_wrist";
public static final String PREF_SCREEN_PASSWORD = "pref_screen_password";
public static final String PREF_SCREEN_ALWAYS_ON_DISPLAY = "pref_screen_always_on_display";
public static final String PREF_SCREEN_HEARTRATE_MONITORING = "pref_screen_heartrate_monitoring";
public static final String PREF_SCREEN_INACTIVITY_EXTENDED = "pref_screen_inactivity_extended";
public static final String PREF_SCREEN_SOUND_AND_VIBRATION = "pref_screen_sound_and_vibration";
public static final String PREF_SCREEN_DO_NOT_DISTURB = "pref_screen_do_not_disturb";
public static final String PREF_SCREEN_OFFLINE_VOICE = "pref_screen_offline_voice";
public static final String PREF_LANGUAGE = "language";
public static final String PREF_LANGUAGE_AUTO = "auto";
public static final String PREF_DATEFORMAT = "dateformat";
@ -25,9 +44,11 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_TIMEFORMAT_12H = "am/pm";
public static final String PREF_TIMEFORMAT_AUTO = "auto";
public static final String PREF_WEARLOCATION = "wearlocation";
public static final String PREF_WEARDIRECTION = "weardirection";
public static final String PREF_VIBRATION_ENABLE = "vibration_enable";
public static final String PREF_NOTIFICATION_ENABLE = "notification_enable";
public static final String PREF_SCREEN_BRIGHTNESS = "screen_brightness";
public static final String PREF_SCREEN_AUTO_BRIGHTNESS = "screen_auto_brightness";
public static final String PREF_SCREEN_ORIENTATION = "screen_orientation";
public static final String PREF_SCREEN_TIMEOUT = "screen_timeout";
public static final String PREF_RESERVER_ALARMS_CALENDAR = "reserve_alarms_calendar";
@ -44,6 +65,8 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_BUTTON_1_FUNCTION_DOUBLE = "button_1_function_double";
public static final String PREF_BUTTON_2_FUNCTION_DOUBLE = "button_2_function_double";
public static final String PREF_BUTTON_3_FUNCTION_DOUBLE = "button_3_function_double";
public static final String PREF_UPPER_BUTTON_LONG_PRESS = "pref_button_action_upper_long";
public static final String PREF_LOWER_BUTTON_SHORT_PRESS = "pref_button_action_lower_short";
public static final String PREF_VIBRATION_STRENGH_PERCENTAGE = "vibration_strength";
public static final String PREF_RELAX_FIRMWARE_CHECKS = "relax_firmware_checks";
@ -80,10 +103,26 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_ALWAYS_ON_DISPLAY_AUTOMATIC = "automatic";
public static final String PREF_ALWAYS_ON_DISPLAY_ALWAYS = "always";
public static final String PREF_ALWAYS_ON_DISPLAY_SCHEDULED = "scheduled";
public static final String PREF_ALWAYS_ON_DISPLAY_FOLLOW_WATCHFACE = "always_on_display_follow_watchface";
public static final String PREF_ALWAYS_ON_DISPLAY_STYLE = "always_on_display_style";
public static final String PREF_VOLUME = "volume";
public static final String PREF_CROWN_VIBRATION = "crown_vibration";
public static final String PREF_ALERT_TONE = "alert_tone";
public static final String PREF_COVER_TO_MUTE = "cover_to_mute";
public static final String PREF_VIBRATE_FOR_ALERT = "vibrate_for_alert";
public static final String PREF_TEXT_TO_SPEECH = "text_to_speech";
public static final String PREF_OFFLINE_VOICE_RESPOND_TURN_WRIST = "offline_voice_respond_turn_wrist";
public static final String PREF_OFFLINE_VOICE_RESPOND_SCREEN_ON = "offline_voice_respond_screen_on";
public static final String PREF_OFFLINE_VOICE_RESPONSE_DURING_SCREEN_LIGHTING = "offline_voice_response_during_screen_lighting";
public static final String PREF_OFFLINE_VOICE_LANGUAGE = "offline_voice_language";
public static final String PREF_SLEEP_TIME = "prefs_enable_sleep_time";
public static final String PREF_SLEEP_TIME_START = "prefs_sleep_time_start";
public static final String PREF_SLEEP_TIME_END = "prefs_sleep_time_end";
public static final String PREF_SLEEP_MODE_SLEEP_SCREEN = "pref_sleep_mode_sleep_screen";
public static final String PREF_SLEEP_MODE_SMART_ENABLE = "pref_sleep_mode_smart_enable";
public static final String PREF_LIFTWRIST_NOSHED = "activate_display_on_lift_wrist_noshed";
public static final String PREF_DISCONNECTNOTIF_NOSHED = "disconnect_notification_noshed";
@ -140,6 +179,16 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_WORKOUT_START_ON_PHONE = "workout_start_on_phone";
public static final String PREF_WORKOUT_SEND_GPS_TO_BAND = "workout_send_gps_to_band";
public static final String PREF_WORKOUT_DETECTION_CATEGORIES = "workout_detection_categories";
public static final String PREF_WORKOUT_DETECTION_ALERT = "workout_detection_alert";
public static final String PREF_WORKOUT_DETECTION_SENSITIVITY = "workout_detection_sensitivity";
public static final String PREF_GPS_MODE_PRESET = "pref_gps_mode_preset";
public static final String PREF_GPS_BAND = "pref_gps_band";
public static final String PREF_GPS_COMBINATION = "pref_gps_combination";
public static final String PREF_GPS_SATELLITE_SEARCH = "pref_gps_satellite_search";
public static final String PREF_AGPS_EXPIRY_REMINDER_ENABLED = "pref_agps_expiry_reminder_enabled";
public static final String PREF_AGPS_EXPIRY_REMINDER_TIME = "pref_agps_expiry_reminder_time";
public static final String PREF_FIND_PHONE = "prefs_find_phone";
public static final String PREF_FIND_PHONE_DURATION = "prefs_find_phone_duration";

View File

@ -66,6 +66,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.XTimePreference;
import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_CONTROL_CENTER_SORTABLE;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_SELECTION_BROADCAST;
@ -360,6 +361,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
addPreferenceHandlerFor(PREF_WORKOUT_ACTIVITY_TYPES_SORTABLE);
addPreferenceHandlerFor(PREF_SHORTCUTS);
addPreferenceHandlerFor(PREF_SHORTCUTS_SORTABLE);
addPreferenceHandlerFor(PREF_CONTROL_CENTER_SORTABLE);
addPreferenceHandlerFor(PREF_LANGUAGE);
addPreferenceHandlerFor(PREF_EXPOSE_HR_THIRDPARTY);
addPreferenceHandlerFor(PREF_BT_CONNECTED_ADVERTISEMENT);
@ -367,9 +369,12 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
addPreferenceHandlerFor(PREF_VIBRATION_ENABLE);
addPreferenceHandlerFor(PREF_NOTIFICATION_ENABLE);
addPreferenceHandlerFor(PREF_SCREEN_BRIGHTNESS);
addPreferenceHandlerFor(PREF_SCREEN_AUTO_BRIGHTNESS);
addPreferenceHandlerFor(PREF_SCREEN_ORIENTATION);
addPreferenceHandlerFor(PREF_SCREEN_TIMEOUT);
addPreferenceHandlerFor(PREF_TIMEFORMAT);
addPreferenceHandlerFor(PREF_UPPER_BUTTON_LONG_PRESS);
addPreferenceHandlerFor(PREF_LOWER_BUTTON_SHORT_PRESS);
addPreferenceHandlerFor(PREF_BUTTON_1_FUNCTION_SHORT);
addPreferenceHandlerFor(PREF_BUTTON_2_FUNCTION_SHORT);
addPreferenceHandlerFor(PREF_BUTTON_3_FUNCTION_SHORT);
@ -431,6 +436,9 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
addPreferenceHandlerFor(PREF_AMPM_ENABLED);
addPreferenceHandlerFor(PREF_SOUNDS);
addPreferenceHandlerFor(PREF_SLEEP_MODE_SLEEP_SCREEN);
addPreferenceHandlerFor(PREF_SLEEP_MODE_SMART_ENABLE);
addPreferenceHandlerFor(PREF_HYBRID_HR_DRAW_WIDGET_CIRCLES);
addPreferenceHandlerFor(PREF_HYBRID_HR_FORCE_WHITE_COLOR);
addPreferenceHandlerFor(PREF_HYBRID_HR_SAVE_RAW_ACTIVITY_FILES);
@ -511,6 +519,32 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
addPreferenceHandlerFor(PREF_HOURLY_CHIME_START);
addPreferenceHandlerFor(PREF_HOURLY_CHIME_END);
addPreferenceHandlerFor(PREF_WORKOUT_DETECTION_CATEGORIES);
addPreferenceHandlerFor(PREF_WORKOUT_DETECTION_ALERT);
addPreferenceHandlerFor(PREF_WORKOUT_DETECTION_SENSITIVITY);
addPreferenceHandlerFor(PREF_GPS_MODE_PRESET);
addPreferenceHandlerFor(PREF_GPS_BAND);
addPreferenceHandlerFor(PREF_GPS_COMBINATION);
addPreferenceHandlerFor(PREF_GPS_SATELLITE_SEARCH);
addPreferenceHandlerFor(PREF_AGPS_EXPIRY_REMINDER_ENABLED);
addPreferenceHandlerFor(PREF_AGPS_EXPIRY_REMINDER_TIME);
addPreferenceHandlerFor(PREF_ALWAYS_ON_DISPLAY_FOLLOW_WATCHFACE);
addPreferenceHandlerFor(PREF_ALWAYS_ON_DISPLAY_STYLE);
addPreferenceHandlerFor(PREF_WEARDIRECTION);
addPreferenceHandlerFor(PREF_VOLUME);
addPreferenceHandlerFor(PREF_CROWN_VIBRATION);
addPreferenceHandlerFor(PREF_ALERT_TONE);
addPreferenceHandlerFor(PREF_COVER_TO_MUTE);
addPreferenceHandlerFor(PREF_VIBRATE_FOR_ALERT);
addPreferenceHandlerFor(PREF_TEXT_TO_SPEECH);
addPreferenceHandlerFor(PREF_OFFLINE_VOICE_RESPOND_TURN_WRIST);
addPreferenceHandlerFor(PREF_OFFLINE_VOICE_RESPOND_SCREEN_ON);
addPreferenceHandlerFor(PREF_OFFLINE_VOICE_RESPONSE_DURING_SCREEN_LIGHTING);
addPreferenceHandlerFor(PREF_OFFLINE_VOICE_LANGUAGE);
String sleepTimeState = prefs.getString(PREF_SLEEP_TIME, PREF_DO_NOT_DISTURB_OFF);
boolean sleepTimeScheduled = sleepTimeState.equals(PREF_DO_NOT_DISTURB_SCHEDULED);

View File

@ -471,7 +471,7 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
);
//set reminders
holder.setRemindersView.setVisibility(coordinator.getReminderSlotCount() > 0 ? View.VISIBLE : View.GONE);
holder.setRemindersView.setVisibility(coordinator.getReminderSlotCount(device) > 0 ? View.VISIBLE : View.GONE);
holder.setRemindersView.setOnClickListener(new View.OnClickListener()
{

View File

@ -0,0 +1,45 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.capabilities;
public class GpsCapability {
public enum Preset {
ACCURACY,
BALANCED,
POWER_SAVING,
CUSTOM
}
public enum Band {
SINGLE_BAND,
DUAL_BAND
}
public enum Combination {
LOW_POWER_GPS,
GPS,
GPS_BDS,
GPS_GNOLASS,
GPS_GALILEO,
ALL_SATELLITES
}
public enum SatelliteSearch {
SPEED_FIRST,
ACCURACY_FIRST
}
}

View File

@ -0,0 +1,36 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.capabilities;
public class WorkoutDetectionCapability {
public enum Category {
WALKING,
INDOOR_WALKING,
OUTDOOR_RUNNING,
TREADMILL,
OUTDOOR_CYCLING,
POOL_SWIMMING,
ELLIPTICAL,
ROWING_MACHINE
}
public enum Sensitivity {
HIGH,
STANDARD,
LOW
}
}

View File

@ -624,7 +624,7 @@ public class DBHelper {
int reservedSlots = prefs.getInt(DeviceSettingsPreferenceConst.PREF_RESERVER_REMINDERS_CALENDAR, coordinator.supportsCalendarEvents() ? 0 : 9);
final int reminderSlots = coordinator.getReminderSlotCount();
final int reminderSlots = coordinator.getReminderSlotCount(gbDevice);
try (DBHandler db = GBApplication.acquireDB()) {
final DaoSession daoSession = db.getDaoSession();

View File

@ -23,6 +23,9 @@ public class GBDeviceEventFindPhone extends GBDeviceEvent {
public enum Event {
UNKNOWN,
START,
STOP
START_VIBRATE,
STOP,
VIBRATE,
RING,
}
}

View File

@ -70,6 +70,8 @@ public class GBDeviceEventUpdatePreferences extends GBDeviceEvent {
if (value == null) {
editor.remove(key);
} else if (value instanceof Short) {
editor.putInt(key, (Short) value);
} else if (value instanceof Integer) {
editor.putInt(key, (Integer) value);
} else if (value instanceof Boolean) {

View File

@ -250,7 +250,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
}
@Override
public int getReminderSlotCount() {
public int getReminderSlotCount(final GBDevice device) {
return 0;
}

View File

@ -385,7 +385,7 @@ public interface DeviceCoordinator {
/**
* Indicates the maximum number of reminder slots available in the device.
*/
int getReminderSlotCount();
int getReminderSlotCount(GBDevice device);
/**
* Indicates the maximum number of slots available for world clocks in the device.

View File

@ -92,7 +92,7 @@ public interface EventHandler {
void onFindDevice(boolean start);
void onPhoneFound();
void onFindPhone(boolean start);
void onSetConstantVibration(int integer);

View File

@ -18,6 +18,9 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huami;
import androidx.annotation.NonNull;
import org.apache.commons.lang3.ArrayUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -34,6 +37,9 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiLanguageType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType;
public abstract class Huami2021Coordinator extends HuamiCoordinator {
@Override
@ -119,23 +125,16 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
}
@Override
public int getReminderSlotCount() {
return 50;
public int getReminderSlotCount(final GBDevice device) {
return getPrefs(device).getInt(Huami2021Service.REMINDERS_PREF_CAPABILITY, 0);
}
@Override
public String[] getSupportedLanguageSettings(final GBDevice device) {
return new String[]{
"auto",
"de_DE",
"en_US",
"es_ES",
"fr_FR",
"it_IT",
"nl_NL",
"pt_PT",
"tr_TR",
};
// Return all known languages by default. Unsupported languages will be removed by Huami2021SettingsCustomizer
final List<String> allLanguages = new ArrayList<>(HuamiLanguageType.idLookup.keySet());
allLanguages.add(0, "auto");
return allLanguages.toArray(new String[0]);
}
@Override
@ -145,64 +144,111 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
@Override
public List<HeartRateCapability.MeasurementInterval> getHeartRateMeasurementIntervals() {
return Arrays.asList(
HeartRateCapability.MeasurementInterval.OFF,
HeartRateCapability.MeasurementInterval.SMART,
HeartRateCapability.MeasurementInterval.MINUTES_1,
HeartRateCapability.MeasurementInterval.MINUTES_10,
HeartRateCapability.MeasurementInterval.MINUTES_30
);
// Return all known by default. Unsupported will be removed by Huami2021SettingsCustomizer
return Arrays.asList(HeartRateCapability.MeasurementInterval.values());
}
/**
* Returns a superset of all settings supported by Zepp OS Devices. Unsupported settings are removed
* by {@link Huami2021SettingsCustomizer}.
*/
@Override
public int[] getSupportedDeviceSpecificSettings(final GBDevice device) {
return new int[]{
R.xml.devicesettings_header_time,
//R.xml.devicesettings_timeformat,
R.xml.devicesettings_dateformat_2,
// TODO R.xml.devicesettings_world_clocks,
final List<Integer> settings = new ArrayList<>();
R.xml.devicesettings_header_display,
R.xml.devicesettings_huami2021_displayitems,
R.xml.devicesettings_huami2021_shortcuts,
R.xml.devicesettings_nightmode,
R.xml.devicesettings_liftwrist_display_sensitivity,
R.xml.devicesettings_password,
R.xml.devicesettings_always_on_display,
R.xml.devicesettings_screen_timeout_5_to_15,
R.xml.devicesettings_screen_brightness,
//
// Time
//
settings.add(R.xml.devicesettings_header_time);
//settings.add(R.xml.devicesettings_timeformat);
settings.add(R.xml.devicesettings_dateformat_2);
// TODO settings.add(R.xml.devicesettings_world_clocks);
R.xml.devicesettings_header_health,
R.xml.devicesettings_heartrate_sleep_alert_activity_stress_spo2,
R.xml.devicesettings_inactivity_dnd_no_threshold,
R.xml.devicesettings_goal_notification,
//
// Display
//
settings.add(R.xml.devicesettings_header_display);
settings.add(R.xml.devicesettings_huami2021_displayitems);
settings.add(R.xml.devicesettings_huami2021_shortcuts);
if (supportsControlCenter()) {
settings.add(R.xml.devicesettings_huami2021_control_center);
}
settings.add(R.xml.devicesettings_nightmode);
settings.add(R.xml.devicesettings_sleep_mode);
settings.add(R.xml.devicesettings_liftwrist_display_sensitivity);
settings.add(R.xml.devicesettings_password);
settings.add(R.xml.devicesettings_always_on_display);
settings.add(R.xml.devicesettings_screen_timeout);
if (supportsAutoBrightness(device)) {
settings.add(R.xml.devicesettings_screen_brightness_withauto);
} else {
settings.add(R.xml.devicesettings_screen_brightness);
}
R.xml.devicesettings_header_workout,
R.xml.devicesettings_workout_start_on_phone,
R.xml.devicesettings_workout_send_gps_to_band,
//
// Health
//
settings.add(R.xml.devicesettings_header_health);
settings.add(R.xml.devicesettings_heartrate_sleep_alert_activity_stress_spo2);
settings.add(R.xml.devicesettings_inactivity_dnd_no_threshold);
settings.add(R.xml.devicesettings_goal_notification);
R.xml.devicesettings_header_notifications,
R.xml.devicesettings_vibrationpatterns,
R.xml.devicesettings_donotdisturb_withauto_and_always,
R.xml.devicesettings_screen_on_on_notifications,
R.xml.devicesettings_autoremove_notifications,
R.xml.devicesettings_canned_reply_16,
R.xml.devicesettings_transliteration,
//
// Workout
//
settings.add(R.xml.devicesettings_header_workout);
if (hasGps(device)) {
settings.add(R.xml.devicesettings_gps_agps);
} else {
// If the device has GPS, it doesn't report workout start/end to the phone
settings.add(R.xml.devicesettings_workout_start_on_phone);
settings.add(R.xml.devicesettings_workout_send_gps_to_band);
}
settings.add(R.xml.devicesettings_workout_detection);
R.xml.devicesettings_header_calendar,
R.xml.devicesettings_sync_calendar,
//
// Notifications
//
settings.add(R.xml.devicesettings_header_notifications);
settings.add(R.xml.devicesettings_sound_and_vibration);
settings.add(R.xml.devicesettings_vibrationpatterns);
settings.add(R.xml.devicesettings_donotdisturb_withauto_and_always);
settings.add(R.xml.devicesettings_screen_on_on_notifications);
settings.add(R.xml.devicesettings_autoremove_notifications);
settings.add(R.xml.devicesettings_canned_reply_16);
settings.add(R.xml.devicesettings_transliteration);
R.xml.devicesettings_header_other,
R.xml.devicesettings_device_actions_without_not_wear,
//
// Calendar
//
settings.add(R.xml.devicesettings_header_calendar);
settings.add(R.xml.devicesettings_sync_calendar);
R.xml.devicesettings_header_connection,
R.xml.devicesettings_expose_hr_thirdparty,
R.xml.devicesettings_bt_connected_advertisement,
R.xml.devicesettings_high_mtu,
//
// Other
//
settings.add(R.xml.devicesettings_header_other);
settings.add(R.xml.devicesettings_offline_voice);
settings.add(R.xml.devicesettings_device_actions_without_not_wear);
settings.add(R.xml.devicesettings_buttonactions_upper_long);
settings.add(R.xml.devicesettings_buttonactions_lower_short);
settings.add(R.xml.devicesettings_weardirection);
R.xml.devicesettings_header_developer,
R.xml.devicesettings_keep_activity_data_on_device,
};
//
// Connection
//
settings.add(R.xml.devicesettings_header_connection);
settings.add(R.xml.devicesettings_expose_hr_thirdparty);
settings.add(R.xml.devicesettings_bt_connected_advertisement);
settings.add(R.xml.devicesettings_high_mtu);
//
// Developer
//
settings.add(R.xml.devicesettings_header_developer);
settings.add(R.xml.devicesettings_keep_activity_data_on_device);
return ArrayUtils.toPrimitive(settings.toArray(new Integer[0]));
}
@Override
@ -212,13 +258,73 @@ public abstract class Huami2021Coordinator extends HuamiCoordinator {
};
}
@Override
public List<HuamiVibrationPatternNotificationType> getVibrationPatternNotificationTypes(final GBDevice device) {
final List<HuamiVibrationPatternNotificationType> notificationTypes = new ArrayList<>(Arrays.asList(
HuamiVibrationPatternNotificationType.APP_ALERTS,
HuamiVibrationPatternNotificationType.INCOMING_CALL,
HuamiVibrationPatternNotificationType.INCOMING_SMS,
HuamiVibrationPatternNotificationType.GOAL_NOTIFICATION,
HuamiVibrationPatternNotificationType.ALARM,
HuamiVibrationPatternNotificationType.IDLE_ALERTS
));
if (getReminderSlotCount(device) > 0) {
notificationTypes.add(HuamiVibrationPatternNotificationType.EVENT_REMINDER);
}
if (!supportsContinuousFindDevice()) {
notificationTypes.add(HuamiVibrationPatternNotificationType.FIND_BAND);
}
if (supportsToDoList()) {
notificationTypes.add(HuamiVibrationPatternNotificationType.SCHEDULE);
notificationTypes.add(HuamiVibrationPatternNotificationType.TODO_LIST);
}
return notificationTypes;
}
@Override
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
return new Huami2021SettingsCustomizer(device);
return new Huami2021SettingsCustomizer(device, getVibrationPatternNotificationTypes(device));
}
@Override
public int getBondingStyle() {
return BONDING_STYLE_REQUIRE_KEY;
}
public boolean supportsContinuousFindDevice() {
// TODO: Auto-detect continuous find device?
return false;
}
public boolean supportsControlCenter() {
// TODO: Auto-detect control center?
return false;
}
public boolean supportsToDoList() {
// TODO: Not yet implemented
// TODO: When implemented, query the capability like reminders
return false;
}
public boolean mainMenuHasMoreSection() {
// Devices that have a control center don't seem to have a "more" section in the main menu
return !supportsControlCenter();
}
public boolean hasGps(final GBDevice device) {
return supportsConfig(device, Huami2021Config.ConfigArg.WORKOUT_GPS_PRESET);
}
public boolean supportsAutoBrightness(final GBDevice device) {
return supportsConfig(device, Huami2021Config.ConfigArg.SCREEN_AUTO_BRIGHTNESS);
}
private boolean supportsConfig(final GBDevice device, final Huami2021Config.ConfigArg config) {
return Huami2021Config.deviceHasConfig(getPrefs(device), config);
}
}

View File

@ -27,6 +27,7 @@ public class Huami2021Service {
public static final short CHUNKED2021_ENDPOINT_WEATHER = 0x000e;
public static final short CHUNKED2021_ENDPOINT_ALARMS = 0x000f;
public static final short CHUNKED2021_ENDPOINT_CANNED_MESSAGES = 0x0013;
public static final short CHUNKED2021_ENDPOINT_CONNECTION = 0x0015;
public static final short CHUNKED2021_ENDPOINT_USER_INFO = 0x0017;
public static final short CHUNKED2021_ENDPOINT_STEPS = 0x0016;
public static final short CHUNKED2021_ENDPOINT_VIBRATION_PATTERNS = 0x0018;
@ -84,6 +85,7 @@ public class Huami2021Service {
public static final byte DISPLAY_ITEMS_CMD_CREATE_ACK = 0x06;
public static final byte DISPLAY_ITEMS_MENU = 0x01;
public static final byte DISPLAY_ITEMS_SHORTCUTS = 0x02;
public static final byte DISPLAY_ITEMS_CONTROL_CENTER = 0x03;
public static final byte DISPLAY_ITEMS_SECTION_MAIN = 0x01;
public static final byte DISPLAY_ITEMS_SECTION_MORE = 0x02;
public static final byte DISPLAY_ITEMS_SECTION_DISABLED = 0x03;
@ -91,12 +93,15 @@ public class Huami2021Service {
/**
* Find Device, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_FIND_DEVICE}.
*/
public static final byte FIND_BAND_ONESHOT = 0x03;
public static final byte FIND_BAND_START = 0x03;
public static final byte FIND_BAND_ACK = 0x04;
public static final byte FIND_BAND_STOP_FROM_PHONE = 0x06;
public static final byte FIND_BAND_STOP_FROM_BAND = 0x07;
public static final byte FIND_PHONE_START = 0x11;
public static final byte FIND_PHONE_ACK = 0x12;
public static final byte FIND_PHONE_STOP_FROM_BAND = 0x13;
public static final byte FIND_PHONE_STOP_FROM_PHONE = 0x14;
public static final byte FIND_PHONE_MODE = 0x15;
/**
* Steps, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_STEPS}.
@ -151,6 +156,14 @@ public class Huami2021Service {
public static final byte CANNED_MESSAGES_CMD_REPLY_SMS_CHECK = 0x0d;
public static final byte CANNED_MESSAGES_CMD_REPLY_SMS_ALLOW = 0x0e;
/**
* Connection, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_CONNECTION}.
*/
public static final byte CONNECTION_CMD_MTU_REQUEST = 0x01;
public static final byte CONNECTION_CMD_MTU_RESPONSE = 0x02;
public static final byte CONNECTION_CMD_UNKNOWN_3 = 0x03;
public static final byte CONNECTION_CMD_UNKNOWN_4 = 0x04;
/**
* Notifications, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_HEARTRATE}.
*/
@ -218,8 +231,6 @@ public class Huami2021Service {
public static final byte CONFIG_CMD_RESPONSE = 0x04;
public static final byte CONFIG_CMD_SET = 0x05;
public static final byte CONFIG_CMD_ACK = 0x06;
public static final byte CONFIG_REQUEST_TYPE_SPECIFIC = 0x00;
public static final byte CONFIG_REQUEST_TYPE_ALL = 0x01; // Don't know how to parse them properly
/**
* Config, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_ICONS}.
@ -234,6 +245,8 @@ public class Huami2021Service {
/**
* Reminders, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_REMINDERS}.
*/
public static final byte REMINDERS_CMD_CAPABILITIES_REQUEST = 0x01;
public static final byte REMINDERS_CMD_CAPABILITIES_RESPONSE = 0x02;
public static final byte REMINDERS_CMD_REQUEST = 0x03;
public static final byte REMINDERS_CMD_RESPONSE = 0x04;
public static final byte REMINDERS_CMD_CREATE = 0x05;
@ -246,6 +259,7 @@ public class Huami2021Service {
public static final int REMINDER_FLAG_TEXT = 0x0008;
public static final int REMINDER_FLAG_REPEAT_MONTH = 0x1000;
public static final int REMINDER_FLAG_REPEAT_YEAR = 0x2000;
public static final String REMINDERS_PREF_CAPABILITY = "huami_2021_capability_reminders";
/**
* Calendar, for {@link Huami2021Service#CHUNKED2021_ENDPOINT_CALENDAR}.

View File

@ -17,87 +17,405 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huami;
import android.os.Parcel;
import android.text.InputType;
import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference;
import com.mobeta.android.dslv.DragSortListPreference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.capabilities.GpsCapability;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021MenuType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Config;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType;
import nodomain.freeyourgadget.gadgetbridge.util.MapUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class Huami2021SettingsCustomizer extends HuamiSettingsCustomizer {
public Huami2021SettingsCustomizer(final GBDevice device) {
super(device);
private static final Logger LOG = LoggerFactory.getLogger(Huami2021SettingsCustomizer.class);
public Huami2021SettingsCustomizer(final GBDevice device, final List<HuamiVibrationPatternNotificationType> vibrationPatternNotificationTypes) {
super(device, vibrationPatternNotificationTypes);
}
@Override
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs) {
super.customizeSettings(handler, prefs);
setupDisplayItemsPref(
HuamiConst.PREF_DISPLAY_ITEMS_SORTABLE,
HuamiConst.PREF_ALL_DISPLAY_ITEMS,
Huami2021MenuType.displayItemNameLookup,
handler,
prefs
);
// These are not reported by the normal configs
removeUnsupportedElementsFromListPreference(HuamiConst.PREF_DISPLAY_ITEMS_SORTABLE, handler, prefs);
removeUnsupportedElementsFromListPreference(HuamiConst.PREF_SHORTCUTS_SORTABLE, handler, prefs);
removeUnsupportedElementsFromListPreference(HuamiConst.PREF_CONTROL_CENTER_SORTABLE, handler, prefs);
setupDisplayItemsPref(
HuamiConst.PREF_SHORTCUTS_SORTABLE,
HuamiConst.PREF_ALL_SHORTCUTS,
Huami2021MenuType.shortcutsNameLookup,
for (final Huami2021Config.ConfigArg config : Huami2021Config.ConfigArg.values()) {
if (config.getPrefKey() == null) {
continue;
}
switch (config.getConfigType()) {
case BYTE:
case BYTE_LIST:
case STRING_LIST:
// For list preferences, remove the unsupported items
removeUnsupportedElementsFromListPreference(config.getPrefKey(), handler, prefs);
break;
case BOOL:
case SHORT:
case INT:
case DATETIME_HH_MM:
// For other preferences, just hide them if they were not reported as supported by the device
hidePrefIfNoConfigSupported(handler, prefs, config.getPrefKey(), config);
break;
}
}
// Hide all config groups that may not be mapped directly to a preference
final Map<String, List<Huami2021Config.ConfigArg>> configScreens = new HashMap<String, List<Huami2021Config.ConfigArg>>() {{
put(DeviceSettingsPreferenceConst.PREF_SCREEN_NIGHT_MODE, Arrays.asList(
Huami2021Config.ConfigArg.NIGHT_MODE_MODE,
Huami2021Config.ConfigArg.NIGHT_MODE_SCHEDULED_START,
Huami2021Config.ConfigArg.NIGHT_MODE_SCHEDULED_END
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_SLEEP_MODE, Arrays.asList(
Huami2021Config.ConfigArg.SLEEP_MODE_SLEEP_SCREEN,
Huami2021Config.ConfigArg.SLEEP_MODE_SMART_ENABLE
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_LIFT_WRIST, Arrays.asList(
Huami2021Config.ConfigArg.LIFT_WRIST_MODE,
Huami2021Config.ConfigArg.LIFT_WRIST_SCHEDULED_START,
Huami2021Config.ConfigArg.LIFT_WRIST_SCHEDULED_END,
Huami2021Config.ConfigArg.LIFT_WRIST_RESPONSE_SENSITIVITY
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_PASSWORD, Arrays.asList(
Huami2021Config.ConfigArg.PASSWORD_ENABLED,
Huami2021Config.ConfigArg.PASSWORD_TEXT
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_ALWAYS_ON_DISPLAY, Arrays.asList(
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_MODE,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_START,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_SCHEDULED_END,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_FOLLOW_WATCHFACE,
Huami2021Config.ConfigArg.ALWAYS_ON_DISPLAY_STYLE
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_AUTO_BRIGHTNESS, Arrays.asList(
Huami2021Config.ConfigArg.SCREEN_AUTO_BRIGHTNESS
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_HEARTRATE_MONITORING, Arrays.asList(
Huami2021Config.ConfigArg.HEART_RATE_ALL_DAY_MONITORING,
Huami2021Config.ConfigArg.HEART_RATE_HIGH_ALERTS,
Huami2021Config.ConfigArg.HEART_RATE_LOW_ALERTS,
Huami2021Config.ConfigArg.HEART_RATE_ACTIVITY_MONITORING,
Huami2021Config.ConfigArg.SLEEP_HIGH_ACCURACY_MONITORING,
Huami2021Config.ConfigArg.SLEEP_BREATHING_QUALITY_MONITORING,
Huami2021Config.ConfigArg.STRESS_MONITORING,
Huami2021Config.ConfigArg.STRESS_RELAXATION_REMINDER,
Huami2021Config.ConfigArg.SPO2_ALL_DAY_MONITORING,
Huami2021Config.ConfigArg.SPO2_LOW_ALERT
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_INACTIVITY_EXTENDED, Arrays.asList(
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_ENABLED,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_START,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_SCHEDULED_END,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_ENABLED,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_START,
Huami2021Config.ConfigArg.INACTIVITY_WARNINGS_DND_SCHEDULED_END
));
put(DeviceSettingsPreferenceConst.PREF_HEADER_GPS, Arrays.asList(
Huami2021Config.ConfigArg.WORKOUT_GPS_PRESET,
Huami2021Config.ConfigArg.WORKOUT_GPS_BAND,
Huami2021Config.ConfigArg.WORKOUT_GPS_COMBINATION,
Huami2021Config.ConfigArg.WORKOUT_GPS_SATELLITE_SEARCH,
Huami2021Config.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_ENABLED,
Huami2021Config.ConfigArg.WORKOUT_AGPS_EXPIRY_REMINDER_TIME
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_SOUND_AND_VIBRATION, Arrays.asList(
Huami2021Config.ConfigArg.VOLUME,
Huami2021Config.ConfigArg.CROWN_VIBRATION,
Huami2021Config.ConfigArg.ALERT_TONE,
Huami2021Config.ConfigArg.COVER_TO_MUTE,
Huami2021Config.ConfigArg.VIBRATE_FOR_ALERT,
Huami2021Config.ConfigArg.TEXT_TO_SPEECH
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_DO_NOT_DISTURB, Arrays.asList(
Huami2021Config.ConfigArg.DND_MODE,
Huami2021Config.ConfigArg.DND_SCHEDULED_START,
Huami2021Config.ConfigArg.DND_SCHEDULED_END
));
put(DeviceSettingsPreferenceConst.PREF_HEADER_WORKOUT_DETECTION, Arrays.asList(
Huami2021Config.ConfigArg.WORKOUT_DETECTION_CATEGORY,
Huami2021Config.ConfigArg.WORKOUT_DETECTION_ALERT,
Huami2021Config.ConfigArg.WORKOUT_DETECTION_SENSITIVITY
));
put(DeviceSettingsPreferenceConst.PREF_SCREEN_OFFLINE_VOICE, Arrays.asList(
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPOND_TURN_WRIST,
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPOND_SCREEN_ON,
Huami2021Config.ConfigArg.OFFLINE_VOICE_RESPONSE_DURING_SCREEN_LIGHTING,
Huami2021Config.ConfigArg.OFFLINE_VOICE_LANGUAGE
));
}};
for (final Map.Entry<String, List<Huami2021Config.ConfigArg>> configScreen : configScreens.entrySet()) {
hidePrefIfNoConfigSupported(
handler,
prefs
prefs,
configScreen.getKey(),
configScreen.getValue().toArray(new Huami2021Config.ConfigArg[0])
);
}
private void setupDisplayItemsPref(final String prefKey,
final String allItemsPrefKey,
final Map<String, Integer> nameLookup,
// Hides the headers if none of the preferences under them are available
hidePrefIfNoneVisible(handler, DeviceSettingsPreferenceConst.PREF_HEADER_TIME, Arrays.asList(
DeviceSettingsPreferenceConst.PREF_TIMEFORMAT,
DeviceSettingsPreferenceConst.PREF_DATEFORMAT,
DeviceSettingsPreferenceConst.PREF_WORLD_CLOCKS
));
hidePrefIfNoneVisible(handler, DeviceSettingsPreferenceConst.PREF_HEADER_DISPLAY, Arrays.asList(
HuamiConst.PREF_DISPLAY_ITEMS_SORTABLE,
HuamiConst.PREF_SHORTCUTS_SORTABLE,
HuamiConst.PREF_CONTROL_CENTER_SORTABLE,
DeviceSettingsPreferenceConst.PREF_SCREEN_NIGHT_MODE,
DeviceSettingsPreferenceConst.PREF_SCREEN_SLEEP_MODE,
DeviceSettingsPreferenceConst.PREF_SCREEN_LIFT_WRIST,
DeviceSettingsPreferenceConst.PREF_SCREEN_PASSWORD,
DeviceSettingsPreferenceConst.PREF_SCREEN_ALWAYS_ON_DISPLAY,
DeviceSettingsPreferenceConst.PREF_SCREEN_TIMEOUT,
DeviceSettingsPreferenceConst.PREF_SCREEN_AUTO_BRIGHTNESS,
DeviceSettingsPreferenceConst.PREF_SCREEN_BRIGHTNESS
));
hidePrefIfNoneVisible(handler, DeviceSettingsPreferenceConst.PREF_HEADER_HEALTH, Arrays.asList(
DeviceSettingsPreferenceConst.PREF_SCREEN_HEARTRATE_MONITORING,
DeviceSettingsPreferenceConst.PREF_SCREEN_INACTIVITY_EXTENDED,
DeviceSettingsPreferenceConst.PREF_USER_FITNESS_GOAL_NOTIFICATION
));
hidePrefIfNoneVisible(handler, DeviceSettingsPreferenceConst.PREF_HEADER_WORKOUT, Arrays.asList(
DeviceSettingsPreferenceConst.PREF_HEADER_GPS,
DeviceSettingsPreferenceConst.PREF_WORKOUT_START_ON_PHONE,
DeviceSettingsPreferenceConst.PREF_WORKOUT_SEND_GPS_TO_BAND,
DeviceSettingsPreferenceConst.PREF_HEADER_WORKOUT_DETECTION
));
hidePrefIfNoneVisible(handler, DeviceSettingsPreferenceConst.PREF_HEADER_AGPS, Arrays.asList(
DeviceSettingsPreferenceConst.PREF_AGPS_EXPIRY_REMINDER_ENABLED,
DeviceSettingsPreferenceConst.PREF_AGPS_EXPIRY_REMINDER_TIME
));
setupGpsPreference(handler);
}
private void setupGpsPreference(final DeviceSpecificSettingsHandler handler) {
final ListPreference prefGpsPreset = handler.findPreference(DeviceSettingsPreferenceConst.PREF_GPS_MODE_PRESET);
final ListPreference prefGpsBand = handler.findPreference(DeviceSettingsPreferenceConst.PREF_GPS_BAND);
final ListPreference prefGpsCombination = handler.findPreference(DeviceSettingsPreferenceConst.PREF_GPS_COMBINATION);
final ListPreference prefGpsSatelliteSearch = handler.findPreference(DeviceSettingsPreferenceConst.PREF_GPS_SATELLITE_SEARCH);
if (prefGpsPreset != null) {
// When the preset preference is changed, update the band, combination and satellite search to the corresponding values
final Preference.OnPreferenceChangeListener onGpsPresetUpdated = (preference, newVal) -> {
final boolean isCustomPreset = GpsCapability.Preset.CUSTOM.name().toLowerCase(Locale.ROOT).equals(newVal);
final GpsCapability.Preset preset = GpsCapability.Preset.valueOf(newVal.toString().toUpperCase(Locale.ROOT));
final GpsCapability.Band presetBand;
final GpsCapability.Combination presetCombination;
final GpsCapability.SatelliteSearch presetSatelliteSearch;
switch (preset) {
case ACCURACY:
presetBand = GpsCapability.Band.DUAL_BAND;
presetCombination = GpsCapability.Combination.ALL_SATELLITES;
presetSatelliteSearch = GpsCapability.SatelliteSearch.ACCURACY_FIRST;
break;
case BALANCED:
presetBand = GpsCapability.Band.SINGLE_BAND;
presetCombination = GpsCapability.Combination.GPS_BDS;
presetSatelliteSearch = GpsCapability.SatelliteSearch.ACCURACY_FIRST;
break;
case POWER_SAVING:
presetBand = GpsCapability.Band.SINGLE_BAND;
presetCombination = GpsCapability.Combination.LOW_POWER_GPS;
presetSatelliteSearch = GpsCapability.SatelliteSearch.SPEED_FIRST;
break;
default:
presetBand = null;
presetCombination = null;
presetSatelliteSearch = null;
break;
}
if (prefGpsBand != null) {
prefGpsBand.setEnabled(isCustomPreset);
if (!isCustomPreset && presetBand != null) {
prefGpsBand.setValue(presetBand.name().toLowerCase(Locale.ROOT));
}
}
if (prefGpsCombination != null) {
prefGpsCombination.setEnabled(isCustomPreset);
if (!isCustomPreset && presetBand != null) {
prefGpsCombination.setValue(presetCombination.name().toLowerCase(Locale.ROOT));
}
}
if (prefGpsSatelliteSearch != null) {
prefGpsSatelliteSearch.setEnabled(isCustomPreset);
if (!isCustomPreset && presetBand != null) {
prefGpsSatelliteSearch.setValue(presetSatelliteSearch.name().toLowerCase(Locale.ROOT));
}
}
return true;
};
handler.addPreferenceHandlerFor(DeviceSettingsPreferenceConst.PREF_GPS_MODE_PRESET, onGpsPresetUpdated);
onGpsPresetUpdated.onPreferenceChange(prefGpsPreset, prefGpsPreset.getValue());
}
// The gps combination can only be chosen if the gps band is single band
if (prefGpsBand != null && prefGpsCombination != null) {
final Preference.OnPreferenceChangeListener onGpsBandUpdate = (preference, newVal) -> {
final boolean isSingleBand = GpsCapability.Band.SINGLE_BAND.name().toLowerCase(Locale.ROOT).equals(newVal);
prefGpsCombination.setEnabled(isSingleBand);
return true;
};
handler.addPreferenceHandlerFor(DeviceSettingsPreferenceConst.PREF_GPS_BAND, onGpsBandUpdate);
final boolean isCustomPreset = prefGpsPreset != null &&
GpsCapability.Preset.CUSTOM.name().toLowerCase(Locale.ROOT).equals(prefGpsPreset.getValue());
if (isCustomPreset) {
onGpsBandUpdate.onPreferenceChange(prefGpsPreset, prefGpsBand.getValue());
}
}
}
/**
* Removes all unsupported elements from a list preference. If they are not known, the preference
* is hidden.
*/
private void removeUnsupportedElementsFromListPreference(final String prefKey,
final DeviceSpecificSettingsHandler handler,
final Prefs prefs) {
final DragSortListPreference pref = handler.findPreference(prefKey);
final Preference pref = handler.findPreference(prefKey);
if (pref == null) {
return;
}
final List<String> allDisplayItems = prefs.getList(allItemsPrefKey, null);
if (allDisplayItems == null || allDisplayItems.isEmpty()) {
// Get the list of possible values for this preference, as reported by the band
final List<String> possibleValues = prefs.getList(Huami2021Config.getPrefPossibleValuesKey(prefKey), null);
if (possibleValues == null || possibleValues.isEmpty()) {
// The band hasn't reported this setting, so we don't know the possible values.
// Hide it
pref.setVisible(false);
return;
}
final CharSequence[] entries = new CharSequence[allDisplayItems.size()];
final CharSequence[] values = new CharSequence[allDisplayItems.size()];
for (int i = 0; i < allDisplayItems.size(); i++) {
final String screenId = allDisplayItems.get(i);
final String screenName;
if (screenId.equals("more")) {
screenName = handler.getContext().getString(R.string.menuitem_more);
} else if (nameLookup.containsKey(screenId)) {
screenName = handler.getContext().getString(nameLookup.get(screenId));
final CharSequence[] originalEntries;
final CharSequence[] originalValues;
if (pref instanceof ListPreference) {
originalEntries = ((ListPreference) pref).getEntries();
originalValues = ((ListPreference) pref).getEntryValues();
} else if (pref instanceof MultiSelectListPreference) {
originalEntries = ((MultiSelectListPreference) pref).getEntries();
originalValues = ((MultiSelectListPreference) pref).getEntryValues();
} else {
screenName = handler.getContext().getString(R.string.menuitem_unknown_app, screenId);
LOG.error("Unknown list pref class {}", pref.getClass().getName());
return;
}
entries[i] = screenName;
values[i] = screenId;
final List<String> prefValues = new ArrayList<>(originalValues.length);
for (final CharSequence entryValue : originalValues) {
prefValues.add(entryValue.toString());
}
pref.setEntries(entries);
pref.setEntryValues(values);
final CharSequence[] entries = new CharSequence[possibleValues.size()];
final CharSequence[] values = new CharSequence[possibleValues.size()];
for (int i = 0; i < possibleValues.size(); i++) {
final String possibleValue = possibleValues.get(i);
final int idxPrefValue = prefValues.indexOf(possibleValue);
if (idxPrefValue >= 0) {
entries[i] = originalEntries[idxPrefValue];
} else {
entries[i] = handler.getContext().getString(R.string.menuitem_unknown_app, possibleValue);
}
values[i] = possibleValue;
}
if (pref instanceof ListPreference) {
((ListPreference) pref).setEntries(entries);
((ListPreference) pref).setEntryValues(values);
} else if (pref instanceof MultiSelectListPreference) {
((MultiSelectListPreference) pref).setEntries(entries);
((MultiSelectListPreference) pref).setEntryValues(values);
}
}
/**
* Hides prefToHide if no configuration from the list has been reported by the band.
*/
private void hidePrefIfNoConfigSupported(final DeviceSpecificSettingsHandler handler,
final Prefs prefs,
final String prefToHide,
final Huami2021Config.ConfigArg... configs) {
final Preference pref = handler.findPreference(prefToHide);
if (pref == null) {
return;
}
for (final Huami2021Config.ConfigArg config : configs) {
if (Huami2021Config.deviceHasConfig(prefs, config)) {
// This preference is supported, don't hide
return;
}
}
// None of the configs were supported by the device, hide this preference
pref.setVisible(false);
}
/**
* Hides the the prefToHide preference if none of the preferences in the preferences list are
* visible.
*/
private void hidePrefIfNoneVisible(final DeviceSpecificSettingsHandler handler,
final String prefToHide,
final List<String> subPrefs) {
final Preference pref = handler.findPreference(prefToHide);
if (pref == null) {
return;
}
for (final String subPrefKey : subPrefs) {
final Preference subPref = handler.findPreference(subPrefKey);
if (subPref == null) {
continue;
}
if (subPref.isVisible()) {
// At least one preference is visible
return;
}
}
// No preference was visible, hide
pref.setVisible(false);
}
public static final Creator<Huami2021SettingsCustomizer> CREATOR = new Creator<Huami2021SettingsCustomizer>() {
@Override
public Huami2021SettingsCustomizer createFromParcel(final Parcel in) {
final GBDevice device = in.readParcelable(Huami2021SettingsCustomizer.class.getClassLoader());
final List<HuamiVibrationPatternNotificationType> vibrationPatternNotificationTypes = new ArrayList<>();
in.readList(vibrationPatternNotificationTypes, HuamiVibrationPatternNotificationType.class.getClassLoader());
return new Huami2021SettingsCustomizer(device, vibrationPatternNotificationTypes);
}
@Override
public Huami2021SettingsCustomizer[] newArray(final int size) {
return new Huami2021SettingsCustomizer[size];
}
};
}

View File

@ -57,16 +57,16 @@ public class HuamiConst {
public static final String AMAZFIT_X = "Amazfit X";
public static final String AMAZFIT_GTS3_NAME = "Amazfit GTS 3";
public static final String AMAZFIT_GTR3_NAME = "Amazfit GTR 3";
public static final String AMAZFIT_GTR4_NAME = "Amazfit GTR 4";
public static final String XIAOMI_SMART_BAND7_NAME = "Xiaomi Smart Band 7";
public static final String PREF_DISPLAY_ITEMS = "display_items";
public static final String PREF_ALL_DISPLAY_ITEMS = "all_display_items";
public static final String PREF_DISPLAY_ITEMS_SORTABLE = "display_items_sortable";
public static final String PREF_WORKOUT_ACTIVITY_TYPES_SORTABLE = "workout_activity_types_sortable";
public static final String PREF_SHORTCUTS = "shortcuts";
public static final String PREF_ALL_SHORTCUTS = "all_shortcuts";
public static final String PREF_SHORTCUTS_SORTABLE = "shortcuts_sortable";
public static final String PREF_CONTROL_CENTER_SORTABLE = "control_center_sortable";
public static final String PREF_EXPOSE_HR_THIRDPARTY = "expose_hr_thirdparty";
public static final String PREF_USE_CUSTOM_FONT = "use_custom_font";
@ -94,6 +94,8 @@ public class HuamiConst {
/**
* The suffixes match the enum {@link nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType}.
*/
public static final String PREF_HUAMI_VIBRATION_PROFILE_KEY_PREFIX = "vibration_profile_key_";
public static final String PREF_HUAMI_DEFAULT_VIBRATION_PROFILE = "default";
// profile
public static final String PREF_HUAMI_VIBRATION_PROFILE_PREFIX = "huami_vibration_profile_";
public static final String PREF_HUAMI_VIBRATION_PROFILE_APP_ALERTS = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "app_alerts";
@ -104,6 +106,8 @@ public class HuamiConst {
public static final String PREF_HUAMI_VIBRATION_PROFILE_IDLE_ALERTS = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "idle_alerts";
public static final String PREF_HUAMI_VIBRATION_PROFILE_EVENT_REMINDER = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "event_reminder";
public static final String PREF_HUAMI_VIBRATION_PROFILE_FIND_BAND = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "find_band";
public static final String PREF_HUAMI_VIBRATION_PROFILE_TODO_LIST = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "todo_list";
public static final String PREF_HUAMI_VIBRATION_PROFILE_SCHEDULE = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "schedule";
// count
public static final String PREF_HUAMI_VIBRATION_COUNT_PREFIX = "huami_vibration_count_";
public static final String PREF_HUAMI_VIBRATION_COUNT_APP_ALERTS = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "app_alerts";
@ -114,6 +118,8 @@ public class HuamiConst {
public static final String PREF_HUAMI_VIBRATION_COUNT_IDLE_ALERTS = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "idle_alerts";
public static final String PREF_HUAMI_VIBRATION_COUNT_EVENT_REMINDER = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "event_reminder";
public static final String PREF_HUAMI_VIBRATION_COUNT_FIND_BAND = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "find_band";
public static final String PREF_HUAMI_VIBRATION_COUNT_TODO_LIST = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "todo_list";
public static final String PREF_HUAMI_VIBRATION_COUNT_SCHEDULE = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "schedule";
// try
public static final String PREF_HUAMI_VIBRATION_TRY_PREFIX = "huami_vibration_try_";
public static final String PREF_HUAMI_VIBRATION_TRY_APP_ALERTS = PREF_HUAMI_VIBRATION_TRY_PREFIX + "app_alerts";
@ -124,6 +130,8 @@ public class HuamiConst {
public static final String PREF_HUAMI_VIBRATION_TRY_IDLE_ALERTS = PREF_HUAMI_VIBRATION_TRY_PREFIX + "idle_alerts";
public static final String PREF_HUAMI_VIBRATION_TRY_EVENT_REMINDER = PREF_HUAMI_VIBRATION_TRY_PREFIX + "event_reminder";
public static final String PREF_HUAMI_VIBRATION_TRY_FIND_BAND = PREF_HUAMI_VIBRATION_TRY_PREFIX + "find_band";
public static final String PREF_HUAMI_VIBRATION_TRY_TODO_LIST = PREF_HUAMI_VIBRATION_TRY_PREFIX + "todo_list";
public static final String PREF_HUAMI_VIBRATION_TRY_SCHEDULE = PREF_HUAMI_VIBRATION_TRY_PREFIX + "schedule";
public static int toActivityKind(int rawType) {
switch (rawType) {

View File

@ -17,24 +17,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami;
import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.ParcelUuid;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import de.greenrobot.dao.query.QueryBuilder;
@ -146,6 +145,10 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
return new HuamiActivitySummaryParser();
}
protected static Prefs getPrefs(final GBDevice device) {
return new Prefs(GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()));
}
public static DateTimeDisplay getDateDisplay(Context context, String deviceAddress) throws IllegalArgumentException {
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress);
String dateFormatTime = context.getString(R.string.p_dateformat_time);
@ -363,7 +366,8 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
return prefs.getBoolean("keep_activity_data_on_device", false);
}
public static VibrationProfile getVibrationProfile(String deviceAddress, HuamiVibrationPatternNotificationType notificationType) {
@Nullable
public static VibrationProfile getVibrationProfile(String deviceAddress, HuamiVibrationPatternNotificationType notificationType, boolean nullOnDeviceDefault) {
final String defaultVibrationProfileId;
final int defaultVibrationCount;
@ -400,6 +404,14 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
defaultVibrationProfileId = VibrationProfile.ID_RING;
defaultVibrationCount = 3;
break;
case TODO_LIST:
defaultVibrationProfileId = VibrationProfile.ID_SHORT;
defaultVibrationCount = 2;
break;
case SCHEDULE:
defaultVibrationProfileId = VibrationProfile.ID_SHORT;
defaultVibrationCount = 2;
break;
default:
defaultVibrationProfileId = VibrationProfile.ID_MEDIUM;
defaultVibrationCount = 2;
@ -408,8 +420,18 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress));
final String vibrationProfileId = prefs.getString(
HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX + notificationType.name().toLowerCase(Locale.ROOT),
defaultVibrationProfileId
HuamiConst.PREF_HUAMI_DEFAULT_VIBRATION_PROFILE
);
if (HuamiConst.PREF_HUAMI_DEFAULT_VIBRATION_PROFILE.equals(vibrationProfileId)) {
if (nullOnDeviceDefault) {
// Return null, so the device default is used
return null;
}
return VibrationProfile.getProfile(defaultVibrationProfileId, (short) defaultVibrationCount);
}
final int vibrationProfileCount = prefs.getInt(HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + notificationType.name().toLowerCase(Locale.ROOT), defaultVibrationCount);
return VibrationProfile.getProfile(vibrationProfileId, (short) vibrationProfileCount);
@ -423,20 +445,8 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
} else {
prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress));
}
String time = prefs.getString(key, defaultValue);
DateFormat df = new SimpleDateFormat("HH:mm");
try {
return df.parse(time);
} catch (Exception e) {
LOG.error("Unexpected exception in MiBand2Coordinator.getTime: " + e.getMessage());
}
return new Date();
}
protected static Date getTimePreference(String key, String defaultValue) {
return getTimePreference(key, defaultValue, null);
return prefs.getTimePreference(key, defaultValue);
}
public static MiBandConst.DistanceUnit getDistanceUnit() {
@ -501,13 +511,26 @@ public abstract class HuamiCoordinator extends AbstractBLEDeviceCoordinator {
}
@Override
public int getReminderSlotCount() {
public int getReminderSlotCount(final GBDevice device) {
return 22; // At least, Mi Fit still allows more
}
public List<HuamiVibrationPatternNotificationType> getVibrationPatternNotificationTypes(final GBDevice device) {
return Arrays.asList(
HuamiVibrationPatternNotificationType.APP_ALERTS,
HuamiVibrationPatternNotificationType.INCOMING_CALL,
HuamiVibrationPatternNotificationType.INCOMING_SMS,
HuamiVibrationPatternNotificationType.GOAL_NOTIFICATION,
HuamiVibrationPatternNotificationType.ALARM,
HuamiVibrationPatternNotificationType.IDLE_ALERTS,
HuamiVibrationPatternNotificationType.EVENT_REMINDER,
HuamiVibrationPatternNotificationType.FIND_BAND
);
}
@Override
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
return new HuamiSettingsCustomizer(device);
return new HuamiSettingsCustomizer(device, getVibrationPatternNotificationTypes(device));
}
public static boolean getHourlyChime(String deviceAddress) {

View File

@ -21,7 +21,9 @@ import android.text.InputType;
import androidx.preference.Preference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@ -34,9 +36,12 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class HuamiSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
final GBDevice device;
final List<HuamiVibrationPatternNotificationType> vibrationPatternNotificationTypes;
public HuamiSettingsCustomizer(final GBDevice device) {
public HuamiSettingsCustomizer(final GBDevice device,
final List<HuamiVibrationPatternNotificationType> vibrationPatternNotificationTypes) {
this.device = device;
this.vibrationPatternNotificationTypes = vibrationPatternNotificationTypes;
}
@Override
@ -46,24 +51,48 @@ public class HuamiSettingsCustomizer implements DeviceSpecificSettingsCustomizer
@Override
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs) {
// Setup the vibration patterns for all supported notification types
for (HuamiVibrationPatternNotificationType notificationType : HuamiVibrationPatternNotificationType.values()) {
final String typeKey = notificationType.name().toLowerCase(Locale.ROOT);
// Hide unsupported notification types
if (!vibrationPatternNotificationTypes.contains(notificationType)) {
final String screenKey = HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_KEY_PREFIX + typeKey;
final Preference pref = handler.findPreference(screenKey);
if (pref != null) {
pref.setVisible(false);
}
continue;
}
handler.addPreferenceHandlerFor(HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX + typeKey);
handler.addPreferenceHandlerFor(HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + typeKey);
handler.setInputTypeFor(HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + typeKey, InputType.TYPE_CLASS_NUMBER);
// Setup the try pref to vibrate the device
final String tryPrefKey = HuamiConst.PREF_HUAMI_VIBRATION_TRY_PREFIX + typeKey;
final Preference tryPref = handler.findPreference(tryPrefKey);
if (tryPref != null) {
tryPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
tryPref.setOnPreferenceClickListener(preference -> {
GBApplication.deviceService(device).onSendConfiguration(tryPrefKey);
return true;
}
});
}
// Setup the default preference - disable count if default preference is selected
final String profilePrefKey = HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX + typeKey;
final String countPrefKey = HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + typeKey;
final Preference countPref = handler.findPreference(countPrefKey);
final Preference.OnPreferenceChangeListener profilePrefListener = (preference, newValue) -> {
if (countPref != null) {
countPref.setEnabled(!HuamiConst.PREF_HUAMI_DEFAULT_VIBRATION_PROFILE.equals(newValue));
}
return true;
};
profilePrefListener.onPreferenceChange(null, prefs.getString(profilePrefKey, HuamiConst.PREF_HUAMI_DEFAULT_VIBRATION_PROFILE));
handler.addPreferenceHandlerFor(profilePrefKey, profilePrefListener);
}
}
@ -83,7 +112,9 @@ public class HuamiSettingsCustomizer implements DeviceSpecificSettingsCustomizer
@Override
public HuamiSettingsCustomizer createFromParcel(final Parcel in) {
final GBDevice device = in.readParcelable(HuamiSettingsCustomizer.class.getClassLoader());
return new HuamiSettingsCustomizer(device);
final List<HuamiVibrationPatternNotificationType> vibrationPatternNotificationTypes = new ArrayList<>();
in.readList(vibrationPatternNotificationTypes, HuamiVibrationPatternNotificationType.class.getClassLoader());
return new HuamiSettingsCustomizer(device, vibrationPatternNotificationTypes);
}
@Override
@ -100,5 +131,6 @@ public class HuamiSettingsCustomizer implements DeviceSpecificSettingsCustomizer
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, 0);
dest.writeList(vibrationPatternNotificationTypes);
}
}

View File

@ -89,7 +89,7 @@ public class AmazfitBipUCoordinator extends HuamiCoordinator {
}
@Override
public int getReminderSlotCount() {
public int getReminderSlotCount(final GBDevice device) {
return 0;
}

View File

@ -89,7 +89,7 @@ public class AmazfitBipUProCoordinator extends HuamiCoordinator {
}
@Override
public int getReminderSlotCount() {
public int getReminderSlotCount(final GBDevice device) {
return 0;
}

View File

@ -0,0 +1,78 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr4;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.Huami2021Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class AmazfitGTR4Coordinator extends Huami2021Coordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTR4Coordinator.class);
@NonNull
@Override
public DeviceType getSupportedType(final GBDeviceCandidate candidate) {
try {
final BluetoothDevice device = candidate.getDevice();
final String name = device.getName();
if (name != null && name.startsWith(HuamiConst.AMAZFIT_GTR4_NAME)) {
return DeviceType.AMAZFITGTR4;
}
} catch (final Exception e) {
LOG.error("unable to check device support", e);
}
return DeviceType.UNKNOWN;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.AMAZFITGTR4;
}
@Override
public InstallHandler findInstallHandler(final Uri uri, final Context context) {
final AmazfitGTR4FWInstallHandler handler = new AmazfitGTR4FWInstallHandler(uri, context);
return handler.isValid() ? handler : null;
}
@Override
public boolean supportsContinuousFindDevice() {
return true;
}
@Override
public boolean supportsControlCenter() {
return true;
}
@Override
public boolean supportsToDoList() {
return true;
}
}

View File

@ -0,0 +1,44 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr4;
import android.content.Context;
import android.net.Uri;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr4.AmazfitGTR4FirmwareInfo;
public class AmazfitGTR4FWHelper extends HuamiFWHelper {
public AmazfitGTR4FWHelper(final Uri uri, final Context context) throws IOException {
super(uri, context);
}
@Override
public long getMaxExpectedFileSize() {
return 1024 * 1024 * 128; // 128.0MB
}
@Override
protected void determineFirmwareInfo(final byte[] wholeFirmwareBytes) {
firmwareInfo = new AmazfitGTR4FirmwareInfo(wholeFirmwareBytes);
if (!firmwareInfo.isHeaderValid()) {
throw new IllegalArgumentException("Not a Amazfit GTR 4 firmware");
}
}
}

View File

@ -0,0 +1,49 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr4;
import android.content.Context;
import android.net.Uri;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
class AmazfitGTR4FWInstallHandler extends AbstractMiBandFWInstallHandler {
AmazfitGTR4FWInstallHandler(final Uri uri, final Context context) {
super(uri, context);
}
@Override
protected String getFwUpgradeNotice() {
return mContext.getString(R.string.fw_upgrade_notice_amazfit_gtr4, helper.getHumanFirmwareVersion());
}
@Override
protected AbstractMiBandFWHelper createHelper(final Uri uri, final Context context) throws IOException {
return new AmazfitGTR4FWHelper(uri, context);
}
@Override
protected boolean isSupportedDeviceType(final GBDevice device) {
return device.getType() == DeviceType.AMAZFITGTR4;
}
}

View File

@ -93,7 +93,7 @@ public class AmazfitNeoCoordinator extends HuamiCoordinator {
}
@Override
public int getReminderSlotCount() {
public int getReminderSlotCount(final GBDevice device) {
return 0; // Neo does not support reminders
}

View File

@ -75,7 +75,7 @@ public class MiBand2Coordinator extends HuamiCoordinator {
}
@Override
public int getReminderSlotCount() {
public int getReminderSlotCount(final GBDevice device) {
return 0;
}

View File

@ -90,7 +90,7 @@ public class MiBand5Coordinator extends HuamiCoordinator {
}
@Override
public int getReminderSlotCount() {
public int getReminderSlotCount(final GBDevice device) {
return 50; // as enforced by Zepp Life
}

View File

@ -355,8 +355,9 @@ public class GBDeviceService implements DeviceService {
}
@Override
public void onPhoneFound() {
Intent intent = createIntent().setAction(ACTION_PHONE_FOUND);
public void onFindPhone(final boolean start) {
Intent intent = createIntent().setAction(ACTION_PHONE_FOUND)
.putExtra(EXTRA_FIND_START, start);
invokeService(intent);
}

View File

@ -58,6 +58,9 @@ public class ActivityUser {
public static final int defaultUserDistanceGoalMeters = 5000;
public static final int defaultUserActiveTimeGoalMinutes = 60;
public static final int defaultUserStepLengthCm = 0;
public static final int defaultUserGoalWeightKg = 70;
public static final int defaultUserGoalStandingTimeHours = 12;
public static final int defaultUserFatBurnTimeMinutes = 30;
public static final String PREF_USER_NAME = "mi_user_alias";
public static final String PREF_USER_YEAR_OF_BIRTH = "activity_user_year_of_birth";
@ -70,6 +73,9 @@ public class ActivityUser {
public static final String PREF_USER_DISTANCE_METERS = "activity_user_distance_meters";
public static final String PREF_USER_ACTIVETIME_MINUTES = "activity_user_activetime_minutes";
public static final String PREF_USER_STEP_LENGTH_CM = "activity_user_step_length_cm";
public static final String PREF_USER_GOAL_WEIGHT_KG = "activity_user_goal_weight_kg";
public static final String PREF_USER_GOAL_STANDING_TIME_HOURS = "activity_user_goal_standing_time_minutes";
public static final String PREF_USER_GOAL_FAT_BURN_TIME_MINUTES = "activity_user_goal_fat_burn_time_minutes";
public ActivityUser() {
fetchPreferences();

View File

@ -68,6 +68,7 @@ public enum DeviceType {
MIBAND7(10041, R.drawable.ic_device_miband6, R.drawable.ic_device_miband6_disabled, R.string.devicetype_miband7),
AMAZFITGTS3(10042, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_gts3),
AMAZFITGTR3(10043, R.drawable.ic_device_zetime, R.drawable.ic_device_zetime_disabled, R.string.devicetype_amazfit_gtr3),
AMAZFITGTR4(10044, R.drawable.ic_device_zetime, R.drawable.ic_device_zetime_disabled, R.string.devicetype_amazfit_gtr4),
HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_hplus),
MAKIBESF68(41, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_makibes_f68),
EXRIZUK8(42, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_exrizu_k8),

View File

@ -186,36 +186,49 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
}
}
private void handleGBDeviceEvent(GBDeviceEventFindPhone deviceEvent) {
Context context = getContext();
LOG.info("Got GBDeviceEventFindPhone");
private void handleGBDeviceEvent(final GBDeviceEventFindPhone deviceEvent) {
final Context context = getContext();
LOG.info("Got GBDeviceEventFindPhone: {}", deviceEvent.event);
switch (deviceEvent.event) {
case START:
handleGBDeviceEventFindPhoneStart();
handleGBDeviceEventFindPhoneStart(true);
break;
case START_VIBRATE:
handleGBDeviceEventFindPhoneStart(false);
break;
case VIBRATE:
final Intent intentVibrate = new Intent(FindPhoneActivity.ACTION_VIBRATE);
LocalBroadcastManager.getInstance(context).sendBroadcast(intentVibrate);
break;
case RING:
final Intent intentRing = new Intent(FindPhoneActivity.ACTION_RING);
LocalBroadcastManager.getInstance(context).sendBroadcast(intentRing);
break;
case STOP:
Intent intent = new Intent(FindPhoneActivity.ACTION_FOUND);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
final Intent intentStop = new Intent(FindPhoneActivity.ACTION_FOUND);
LocalBroadcastManager.getInstance(context).sendBroadcast(intentStop);
break;
default:
LOG.warn("unknown GBDeviceEventFindPhone");
}
}
private void handleGBDeviceEventFindPhoneStart() {
private void handleGBDeviceEventFindPhoneStart(final boolean ring) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { // this could be used if app in foreground // TODO: Below Q?
Intent startIntent = new Intent(getContext(), FindPhoneActivity.class);
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startIntent.putExtra(FindPhoneActivity.EXTRA_RING, ring);
context.startActivity(startIntent);
} else {
handleGBDeviceEventFindPhoneStartNotification();
handleGBDeviceEventFindPhoneStartNotification(ring);
}
}
@RequiresApi(Build.VERSION_CODES.Q)
private void handleGBDeviceEventFindPhoneStartNotification() {
private void handleGBDeviceEventFindPhoneStartNotification(final boolean ring) {
LOG.info("Got handleGBDeviceEventFindPhoneStartNotification");
Intent intent = new Intent(context, FindPhoneActivity.class);
intent.putExtra(FindPhoneActivity.EXTRA_RING, ring);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
@ -303,6 +316,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
}
savePreferencesEvent.update(GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()));
gbDevice.sendDeviceUpdateIntent(context);
}
protected void handleGBDeviceEvent(GBDeviceEventUpdateDeviceState updateDeviceState) {

View File

@ -45,7 +45,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@ -54,7 +53,6 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver;
@ -94,7 +92,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.language.LanguageUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.language.Transliterator;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TRANSLITERATION_LANGUAGES;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ADD_CALENDAREVENT;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_REORDER;
@ -782,7 +779,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
break;
}
case ACTION_PHONE_FOUND: {
deviceSupport.onPhoneFound();
final boolean start = intent.getBooleanExtra(EXTRA_FIND_START, false);
deviceSupport.onFindPhone(start);
break;
}
case ACTION_SET_CONSTANT_VIBRATION: {

View File

@ -57,6 +57,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr.Ama
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr2.AmazfitGTR2Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr2.AmazfitGTR2eSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr3.AmazfitGTR3Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr4.AmazfitGTR4Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts.AmazfitGTSSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.AmazfitGTS2MiniSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.AmazfitGTS2Support;
@ -188,6 +189,8 @@ public class DeviceSupportFactory {
return new ServiceDeviceSupport(new AmazfitGTS3Support());
case AMAZFITGTR3:
return new ServiceDeviceSupport(new AmazfitGTR3Support());
case AMAZFITGTR4:
return new ServiceDeviceSupport(new AmazfitGTR4Support());
case MIBAND7:
return new ServiceDeviceSupport(new MiBand7Support());
case AMAZFITBIP:

View File

@ -303,11 +303,11 @@ public class ServiceDeviceSupport implements DeviceSupport {
}
@Override
public void onPhoneFound() {
public void onFindPhone(final boolean start) {
if (checkBusy("phone found")) {
return;
}
delegate.onPhoneFound();
delegate.onFindPhone(start);
}
@Override

View File

@ -356,7 +356,7 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
}
@Override
public void onPhoneFound() {
public void onFindPhone(boolean start) {
}

View File

@ -19,57 +19,91 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
import java.util.HashMap;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.R;
public class Huami2021MenuType {
/**
* These somewhat match the ones in {@link HuamiMenuType}, but not all. The band sends and
* receives those as 8-digit upper case hex strings.
*/
public static final Map<String, Integer> displayItemNameLookup = new HashMap<String, Integer>() {{
put("00000001", R.string.menuitem_personal_activity_intelligence);
put("00000002", R.string.menuitem_hr);
put("00000003", R.string.menuitem_workout);
put("00000004", R.string.menuitem_weather);
put("00000009", R.string.menuitem_alarm);
put("0000001A", R.string.menuitem_worldclock);
put("0000000B", R.string.menuitem_music);
put("0000000C", R.string.menuitem_stopwatch);
put("0000000D", R.string.menuitem_countdown);
put("0000000E", R.string.menuitem_findphone);
put("0000000F", R.string.menuitem_mutephone);
put("00000013", R.string.menuitem_settings);
put("00000014", R.string.menuitem_workout_history);
put("00000015", R.string.menuitem_eventreminder);
put("00000019", R.string.menuitem_pai);
put("0000000A", R.string.menuitem_takephoto);
put("0000001C", R.string.menuitem_stress);
put("0000001D", R.string.menuitem_female_health);
put("0000001E", R.string.menuitem_workout_status);
put("00000023", R.string.menuitem_sleep);
put("00000024", R.string.menuitem_spo2);
put("00000026", R.string.menuitem_events);
put("00000033", R.string.menuitem_breathing);
put("00000038", R.string.menuitem_pomodoro);
put("00000102", R.string.menuitem_flashlight);
public static final Map<String, String> displayItemNameLookup = new HashMap<String, String>() {{
put("00000001", "personal_activity_intelligence");
put("00000002", "hr");
put("00000003", "workout");
put("00000004", "weather");
put("00000009", "alarm");
put("0000000A", "takephoto");
put("0000000B", "music");
put("0000000C", "stopwatch");
put("0000000D", "countdown");
put("0000000E", "findphone");
put("0000000F", "mutephone");
put("00000013", "settings");
put("00000014", "workout_history");
put("00000015", "eventreminder");
put("00000016", "compass");
put("00000019", "pai");
put("0000001A", "worldclock");
put("0000001C", "stress");
put("0000001D", "female_health");
put("0000001E", "workout_status");
put("00000020", "calendar");
put("00000023", "sleep");
put("00000024", "spo2");
put("00000025", "phone");
put("00000026", "events");
put("00000033", "breathing");
put("00000038", "pomodoro");
put("0000003E", "todo");
put("00000041", "barometer");
put("00000042", "voice_memos");
put("00000044", "sun_moon");
put("00000045", "one_tap_measuring");
put("00000047", "membership_cards");
put("00000100", "alexa");
put("00000101", "offline_voice");
put("00000102", "flashlight");
}};
public static final Map<String, Integer> shortcutsNameLookup = new HashMap<String, Integer>() {{
put("00000001", R.string.menuitem_hr);
put("0000000A", R.string.menuitem_workout);
put("0000000C", R.string.menuitem_workout_status);
put("00000002", R.string.menuitem_weather);
put("0000001A", R.string.menuitem_worldclock);
put("00000016", R.string.menuitem_alarm);
put("00000004", R.string.menuitem_music);
put("00000020", R.string.menuitem_activity);
put("00000021", R.string.menuitem_eventreminder);
put("00000011", R.string.menuitem_female_health);
put("00000003", R.string.menuitem_pai);
put("0000000F", R.string.menuitem_stress);
put("00000005", R.string.menuitem_sleep);
put("00000013", R.string.menuitem_spo2);
put("00000018", R.string.menuitem_events);
put("00000012", R.string.menuitem_breathing);
public static final Map<String, String> shortcutsNameLookup = new HashMap<String, String>() {{
put("00000001", "hr");
put("00000002", "weather");
put("00000003", "pai");
put("00000004", "music");
put("00000005", "sleep");
put("0000000A", "workout");
put("0000000B", "workout_history");
put("0000000C", "workout_status");
put("0000000E", "one_tap_measuring");
put("0000000F", "stress");
put("00000011", "female_health");
put("00000012", "breathing");
put("00000013", "spo2");
put("00000016", "alarm");
put("00000017", "calendar");
put("00000018", "events");
put("00000019", "todo");
put("0000001A", "worldclock");
put("0000001B", "compass");
put("0000001C", "barometer");
put("0000001E", "voice_memos");
put("00000020", "activity");
put("00000021", "eventreminder");
}};
public static final Map<String, String> controlCenterNameLookup = new HashMap<String, String>() {{
put("00000007", "battery");
put("00000003", "dnd");
put("00000004", "sleep");
put("00000008", "theater_mode");
put("0000000D", "calendar");
put("00000006", "volume");
put("00000009", "screen_always_lit");
put("00000001", "brightness");
put("00000013", "settings");
put("00000000", "flashlight");
put("0000000A", "bluetooth");
put("0000000B", "wifi");
put("00000002", "lockscreen");
put("00000005", "findphone");
put("00000019", "eject_water");
}};
}

View File

@ -26,8 +26,6 @@ import net.e175.klaus.solarpositioning.SPA;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.LocalDate;
import org.threeten.bp.format.DateTimeFormatter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -82,11 +80,12 @@ public class Huami2021Weather {
return new HourlyResponse();
case "/weather/alerts":
return new AlertsResponse();
default:
LOG.error("Unknown weather path {}", path);
//case "/weather/tide":
// return new TideResponse(weatherSpec);
}
return null;
LOG.error("Unknown weather path {}", path);
return new Huami2021Weather.ErrorResponse(404, -2001, "Not found");
}
private static class RawJsonStringResponse extends Response {
@ -102,16 +101,23 @@ public class Huami2021Weather {
}
public static class ErrorResponse extends Response {
private final int code;
private final int httpStatusCode;
private final int errorCode;
private final String message;
public ErrorResponse(final int code, final String message) {
this.code = code;
public ErrorResponse(final int httpStatusCode, final int errorCode, final String message) {
this.httpStatusCode = httpStatusCode;
this.errorCode = errorCode;
this.message = message;
}
public int getCode() {
return code;
@Override
public int getHttpStatusCode() {
return httpStatusCode;
}
public int getErrorCode() {
return errorCode;
}
public String getMessage() {
@ -120,6 +126,10 @@ public class Huami2021Weather {
}
public static abstract class Response {
public int getHttpStatusCode() {
return 200;
}
public String toJson() {
return GSON.toJson(this);
}
@ -197,8 +207,8 @@ public class Huami2021Weather {
}
private static class MoonRiseSet {
public List<String> moonPhaseValue = new ArrayList<>();
public List<Range> moonRise = new ArrayList<>();
public List<String> moonPhaseValue = new ArrayList<>(); // numbers? 20 21 23...
public List<Range> moonRise = new ArrayList<>(); // yyyy-MM-dd HH:mm:ss
}
private static class Range {
@ -249,7 +259,7 @@ public class Huami2021Weather {
// locationKey=00.000,-0.000,xiaomi_accu:000000
public static class CurrentResponse extends Response {
public CurrentWeatherModel currentWeatherModel;
public Object aqiModel = new Object();
public AqiModel aqiModel = new AqiModel();
public CurrentResponse(final WeatherSpec weatherSpec) {
this.currentWeatherModel = new CurrentWeatherModel(weatherSpec);
@ -273,11 +283,52 @@ public class Huami2021Weather {
temperature = new UnitValue(Unit.TEMPERATURE_C, weatherSpec.currentTemp - 273);
uvIndex = "0";
visibility = new UnitValue(Unit.KM, "");
weather = String.valueOf(HuamiWeatherConditions.mapToAmazfitBipWeatherCode(weatherSpec.currentConditionCode) & 0xff); // is it?
weather = String.valueOf(HuamiWeatherConditions.mapToAmazfitBipWeatherCode(weatherSpec.currentConditionCode) & 0xff);
wind = new Wind(weatherSpec.windDirection, Math.round(weatherSpec.windSpeed));
}
}
private static class AqiModel {
public String pm10 = "";
public String pm25 = "";
}
// /weather/tide
//
// locale=en_US
// deviceSource=7930113
// days=10
// isGlobal=true
// latitude=00.000
// longitude=-00.000
private static class TideResponse extends Response {
public Date pubTime;
public String poiName; // poi tide station name
public String poiKey; // lat,lon,POI_ID
public List<TideDataEntry> tideData = new ArrayList<>();
public TideResponse(final WeatherSpec weatherSpec) {
pubTime = new Date(weatherSpec.timestamp * 1000L);
}
}
private static class TideDataEntry {
public String date; // YYYY-MM-DD, but LocalDate would need API 26+
public List<TideTableEntry> tideTable = new ArrayList<>();
public List<TideHourlyEntry> tideHourly = new ArrayList<>();
}
private static class TideTableEntry {
public Date fxTime; // pubTime format
public String height; // float, x.xx
public String type; // H / L
}
private static class TideHourlyEntry {
public Date fxTime; // pubTime format
public String height; // float, x.xx
}
private enum Unit {
PRESSURE_MB("mb"),
PERCENTAGE("%"),
@ -332,6 +383,7 @@ public class Huami2021Weather {
// locationKey=00.000,-0.000,xiaomi_accu:000000
public static class HourlyResponse extends Response {
public Date pubTime;
// One entry in each list per hour
public List<String> weather;
public List<String> temperature;
public List<String> humidity;

View File

@ -31,6 +31,8 @@ public enum HuamiFirmwareType {
WATCHFACE((byte) 8),
APP((byte) 8),
FONT_LATIN((byte) 11),
ZEPPOS_UNKNOWN_0X13((byte) 0x13),
ZEPPOS_APP((byte) 0xa0),
INVALID(Byte.MIN_VALUE);
private final byte value;

View File

@ -52,6 +52,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
@ -183,9 +184,7 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HEARTRATE_ALERT_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HEARTRATE_ALERT_HIGH_THRESHOLD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HEARTRATE_ALERT_LOW_THRESHOLD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HEARTRATE_SLEEP_BREATHING_QUALITY_MONITORING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HEARTRATE_STRESS_MONITORING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HEARTRATE_STRESS_RELAXATION_REMINDER;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HOURLY_CHIME_ENABLE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HOURLY_CHIME_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HOURLY_CHIME_START;
@ -203,8 +202,6 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SCREEN_ON_ON_NOTIFICATIONS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SCREEN_TIMEOUT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SOUNDS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SPO2_ALL_DAY_MONITORING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SPO2_LOW_ALERT_THRESHOLD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SYNC_CALENDAR;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_USER_FITNESS_GOAL_NOTIFICATION;
@ -236,6 +233,8 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_INCOMING_CALL;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_INCOMING_SMS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_SCHEDULE;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_TODO_LIST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_ALARM;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_APP_ALERTS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_EVENT_REMINDER;
@ -245,6 +244,8 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_INCOMING_CALL;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_INCOMING_SMS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_SCHEDULE;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_TODO_LIST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_ALARM;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_APP_ALERTS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_EVENT_REMINDER;
@ -254,6 +255,8 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_INCOMING_CALL;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_INCOMING_SMS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_PREFIX;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_SCHEDULE;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_TODO_LIST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.COMMAND_ALARMS;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.COMMAND_ALARMS_WITH_TIMES;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.COMMAND_GPS_VERSION;
@ -753,21 +756,6 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
return this;
}
protected HuamiSupport setHeartrateSleepBreathingQualityMonitoring(TransactionBuilder builder) {
LOG.warn("setHeartrateSleepBreathingQualityMonitoring not implemented");
return this;
}
protected HuamiSupport setSPO2AllDayMonitoring(TransactionBuilder builder) {
LOG.warn("setSPO2AllDayMonitoring not implemented");
return this;
}
protected HuamiSupport setSPO2AlertThreshold(TransactionBuilder builder) {
LOG.warn("setSPO2AlertThreshold not implemented");
return this;
}
protected HuamiSupport setHeartrateStressMonitoring(TransactionBuilder builder) {
final boolean enableHrStressMonitoring = HuamiCoordinator.getHeartrateStressMonitoring(gbDevice.getAddress());
LOG.info("Setting heart rate stress monitoring to {}", enableHrStressMonitoring);
@ -776,11 +764,6 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
return this;
}
protected HuamiSupport setHeartrateStressRelaxationReminder(TransactionBuilder builder) {
LOG.warn("setHeartrateStressRelaxationReminder not implemented");
return this;
}
protected HuamiSupport setHeartrateMeasurementInterval(TransactionBuilder builder, int minutes) {
if (characteristicHRControlPoint != null) {
builder.notify(characteristicHRControlPoint, true);
@ -998,7 +981,8 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
}
// Delete the remaining slots, skipping the sent reminders and reserved slots
for (int i = reminders.size() + reservedSlots; i < coordinator.getReminderSlotCount(); i++) {
final int reminderSlotCount = coordinator.getReminderSlotCount(getDevice());
for (int i = reminders.size() + reservedSlots; i < reminderSlotCount; i++) {
LOG.debug("Deleting reminder at position {}", i);
sendReminderToDevice(builder, i, null);
@ -1012,9 +996,10 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
}
final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
final int reminderSlotCount = coordinator.getReminderSlotCount(getDevice());
if (position + 1 > coordinator.getReminderSlotCount()) {
LOG.error("Reminder for position {} is over the limit of {} reminders", position, coordinator.getReminderSlotCount());
if (position + 1 > reminderSlotCount) {
LOG.error("Reminder for position {} is over the limit of {} reminders", position, reminderSlotCount);
return;
}
@ -1582,9 +1567,16 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
}
protected int getFindDeviceInterval() {
VibrationProfile findBand = HuamiCoordinator.getVibrationProfile(getDevice().getAddress(), HuamiVibrationPatternNotificationType.FIND_BAND);
final VibrationProfile findBand = HuamiCoordinator.getVibrationProfile(
getDevice().getAddress(),
HuamiVibrationPatternNotificationType.FIND_BAND,
supportsDeviceDefaultVibrationProfiles()
);
int findDeviceInterval = 0;
if (findBand != null) {
// It can be null if the device supports continuous find mode
// If that's the case, this function shouldn't even have been called
for(int len : findBand.getOnOffSequence())
findDeviceInterval += len;
@ -1593,6 +1585,9 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
if(findDeviceInterval > 10000) // 10 seconds, about as long as Mi Fit allows
findDeviceInterval = 10000;
} else {
findDeviceInterval = 10000;
}
return findDeviceInterval;
}
@ -1893,18 +1888,7 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
case HuamiDeviceEvent.MTU_REQUEST:
int mtu = (value[2] & 0xff) << 8 | value[1] & 0xff;
LOG.info("device announced MTU of " + mtu);
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
if (!prefs.getBoolean(PREF_ALLOW_HIGH_MTU, false)) {
break;
}
if (mtu < 23) {
LOG.error("Device announced unreasonable low MTU of " + mtu + ", ignoring");
break;
}
mMTU = mtu;
if (huami2021ChunkedEncoder != null) {
huami2021ChunkedEncoder.setMTU(mtu);
}
setMtu(mtu);
/*
* not really sure if this would make sense, is this event already a proof of a successful MTU
* negotiation initiated by the Huami device, and acknowledged by the phone? do we really have to
@ -2114,15 +2098,8 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
final Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
if (!prefs.getBoolean(PREF_ALLOW_HIGH_MTU, false)) {
LOG.warn("Ignoring MTU change to {}", mtu);
return;
}
LOG.info("MTU changed to {}", mtu);
this.mMTU = mtu;
setMtu(mtu);
}
protected void acknowledgeFindPhone() {
@ -2772,22 +2749,13 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
setRotateWristToSwitchInfo(builder);
break;
case ActivityUser.PREF_USER_STEPS_GOAL:
case ActivityUser.PREF_USER_CALORIES_BURNT:
case ActivityUser.PREF_USER_SLEEP_DURATION:
case ActivityUser.PREF_USER_GOAL_WEIGHT_KG:
case ActivityUser.PREF_USER_GOAL_STANDING_TIME_HOURS:
case ActivityUser.PREF_USER_GOAL_FAT_BURN_TIME_MINUTES:
setFitnessGoal(builder);
break;
case PREF_SCREEN_ON_ON_NOTIFICATIONS:
setScreenOnOnNotification(builder);
break;
case PREF_SCREEN_BRIGHTNESS:
setScreenBrightness(builder);
break;
case PREF_SCREEN_TIMEOUT:
setScreenTimeout(builder);
break;
case PREF_ALWAYS_ON_DISPLAY_MODE:
case PREF_ALWAYS_ON_DISPLAY_START:
case PREF_ALWAYS_ON_DISPLAY_END:
setAlwaysOnDisplay(builder);
break;
case MiBandConst.PREF_NIGHT_MODE:
case MiBandConst.PREF_NIGHT_MODE_START:
case MiBandConst.PREF_NIGHT_MODE_END:
@ -2855,6 +2823,8 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
case PREF_HUAMI_VIBRATION_PROFILE_IDLE_ALERTS:
case PREF_HUAMI_VIBRATION_PROFILE_EVENT_REMINDER:
case PREF_HUAMI_VIBRATION_PROFILE_FIND_BAND:
case PREF_HUAMI_VIBRATION_PROFILE_TODO_LIST:
case PREF_HUAMI_VIBRATION_PROFILE_SCHEDULE:
case PREF_HUAMI_VIBRATION_COUNT_APP_ALERTS:
case PREF_HUAMI_VIBRATION_COUNT_INCOMING_CALL:
case PREF_HUAMI_VIBRATION_COUNT_INCOMING_SMS:
@ -2863,6 +2833,8 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
case PREF_HUAMI_VIBRATION_COUNT_IDLE_ALERTS:
case PREF_HUAMI_VIBRATION_COUNT_EVENT_REMINDER:
case PREF_HUAMI_VIBRATION_COUNT_FIND_BAND:
case PREF_HUAMI_VIBRATION_COUNT_TODO_LIST:
case PREF_HUAMI_VIBRATION_COUNT_SCHEDULE:
case PREF_HUAMI_VIBRATION_TRY_APP_ALERTS:
case PREF_HUAMI_VIBRATION_TRY_INCOMING_CALL:
case PREF_HUAMI_VIBRATION_TRY_INCOMING_SMS:
@ -2871,6 +2843,8 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
case PREF_HUAMI_VIBRATION_TRY_IDLE_ALERTS:
case PREF_HUAMI_VIBRATION_TRY_EVENT_REMINDER:
case PREF_HUAMI_VIBRATION_TRY_FIND_BAND:
case PREF_HUAMI_VIBRATION_TRY_TODO_LIST:
case PREF_HUAMI_VIBRATION_TRY_SCHEDULE:
setVibrationPattern(builder, config);
break;
case PREF_HEARTRATE_ACTIVITY_MONITORING:
@ -2881,21 +2855,9 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
case PREF_HEARTRATE_ALERT_LOW_THRESHOLD:
setHeartrateAlert(builder);
break;
case PREF_HEARTRATE_SLEEP_BREATHING_QUALITY_MONITORING:
setHeartrateSleepBreathingQualityMonitoring(builder);
break;
case PREF_SPO2_ALL_DAY_MONITORING:
setSPO2AllDayMonitoring(builder);
break;
case PREF_SPO2_LOW_ALERT_THRESHOLD:
setSPO2AlertThreshold(builder);
break;
case PREF_HEARTRATE_STRESS_MONITORING:
setHeartrateStressMonitoring(builder);
break;
case PREF_HEARTRATE_STRESS_RELAXATION_REMINDER:
setHeartrateStressRelaxationReminder(builder);
break;
case PasswordCapabilityImpl.PREF_PASSWORD:
case PasswordCapabilityImpl.PREF_PASSWORD_ENABLED:
setPassword(builder);
@ -2933,13 +2895,24 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
final HuamiVibrationPatternNotificationType notificationType = HuamiVibrationPatternNotificationType.valueOf(notificationTypeName);
final boolean isTry = preferenceKey.startsWith(PREF_HUAMI_VIBRATION_TRY_PREFIX);
final VibrationProfile vibrationProfile = HuamiCoordinator.getVibrationProfile(getDevice().getAddress(), notificationType);
final VibrationProfile vibrationProfile = HuamiCoordinator.getVibrationProfile(
getDevice().getAddress(),
notificationType,
supportsDeviceDefaultVibrationProfiles()
);
setVibrationPattern(builder, notificationType, isTry, vibrationProfile);
return this;
}
/**
* Whether the device supports built-in default vibration profiles.
*/
protected boolean supportsDeviceDefaultVibrationProfiles() {
return false;
}
/**
* Test or set a {@link VibrationProfile}.
*
@ -2952,6 +2925,11 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
final HuamiVibrationPatternNotificationType notificationType,
final boolean test,
final VibrationProfile profile) {
if (profile == null) {
LOG.error("Vibration profile is null for {}", notificationType);
return;
}
final int MAX_TOTAL_LENGTH_MS = 10_000; // 10 seconds, about as long as Mi Fit allows
// The on-off sequence, until the max total length is reached
@ -2977,6 +2955,10 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
}
protected List<Short> truncateVibrationsOnOff(final VibrationProfile profile, final int limitMillis) {
if (profile == null) {
return Collections.emptyList();
}
int totalLengthMs = 0;
// The on-off sequence, until the max total length is reached
@ -3304,11 +3286,6 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
return this;
}
protected HuamiSupport setAlwaysOnDisplay(TransactionBuilder builder) {
LOG.warn("Always on display not implemented");
return this;
}
protected HuamiSupport setActivateDisplayOnLiftWrist(TransactionBuilder builder) {
ActivateDisplayOnLift displayOnLift = HuamiCoordinator.getActivateDisplayOnLiftWrist(getContext(), gbDevice.getAddress());
LOG.info("Setting activate display on lift wrist to " + displayOnLift);
@ -3812,24 +3789,6 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
return this;
}
protected HuamiSupport setScreenOnOnNotification(TransactionBuilder builder) {
LOG.warn("Function not implemented");
return this;
}
protected HuamiSupport setScreenBrightness(TransactionBuilder builder) {
LOG.warn("Function not implemented");
return this;
}
protected HuamiSupport setScreenTimeout(TransactionBuilder builder) {
LOG.warn("Function not implemented");
return this;
}
protected HuamiSupport setLanguage(TransactionBuilder builder) {
String localeString = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getString("language", "auto");
if (localeString == null || localeString.equals("auto")) {
@ -3978,10 +3937,18 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
}
}
protected void writeToChunked2021(TransactionBuilder builder, short type, byte data, boolean encrypt) {
writeToChunked2021(builder, type, new byte[]{data}, encrypt);
}
protected void writeToChunked2021(TransactionBuilder builder, short type, byte[] data, boolean encrypt) {
huami2021ChunkedEncoder.write(builder, type, data, force2021Protocol(), encrypt);
}
protected void writeToChunked2021(final String taskName, short type, byte data, boolean encrypt) {
writeToChunked2021(taskName, type, new byte[]{data}, encrypt);
}
protected void writeToChunked2021(final String taskName, short type, byte[] data, boolean encrypt) {
try {
final TransactionBuilder builder = performInitialized(taskName);
@ -4020,11 +3987,6 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
return this;
}
protected HuamiSupport requestShortcuts(TransactionBuilder builder) {
LOG.warn("Function not implemented");
return this;
}
@Override
public String customStringFilter(String inputString) {
if (HuamiCoordinator.getUseCustomFont(gbDevice.getAddress())) {
@ -4066,7 +4028,7 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
}
public void phase3Initialize(TransactionBuilder builder) {
final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
final HuamiCoordinator coordinator = getCoordinator();
LOG.info("phase3Initialize...");
@ -4094,7 +4056,7 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
setHeartrateMeasurementInterval(builder, HuamiCoordinator.getHeartRateMeasurementInterval(getDevice().getAddress()));
sendReminders(builder);
setWorldClocks(builder);
for (final HuamiVibrationPatternNotificationType type : HuamiVibrationPatternNotificationType.values()) {
for (final HuamiVibrationPatternNotificationType type : coordinator.getVibrationPatternNotificationTypes(getDevice())) {
final String typeKey = type.name().toLowerCase(Locale.ROOT);
setVibrationPattern(builder, HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX + typeKey);
}
@ -4116,6 +4078,24 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
return mMTU;
}
protected void setMtu(final int mtu) {
final Prefs prefs = getDevicePrefs();
if (!prefs.getBoolean(PREF_ALLOW_HIGH_MTU, false)) {
LOG.warn("High MTU is not allowed, ignoring");
return;
}
if (mtu < 23) {
LOG.error("Device announced unreasonable low MTU of {}, ignoring", mtu);
return;
}
this.mMTU = mtu;
if (huami2021ChunkedEncoder != null) {
huami2021ChunkedEncoder.setMTU(mtu);
}
}
public int getActivitySampleSize() {
return mActivitySampleSize;
}
@ -4124,6 +4104,14 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
return GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getBoolean("force_new_protocol", false);
}
protected HuamiCoordinator getCoordinator() {
return (HuamiCoordinator) DeviceHelper.getInstance().getCoordinator(gbDevice);
}
protected Prefs getDevicePrefs() {
return new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
}
@Override
public void handle2021Payload(int type, byte[] payload) {
if (type == Huami2021Service.CHUNKED2021_ENDPOINT_COMPAT) {

View File

@ -28,7 +28,10 @@ public enum HuamiVibrationPatternNotificationType {
ALARM(0x05),
IDLE_ALERTS(0x06),
EVENT_REMINDER(0x08),
FIND_BAND(0x09);
FIND_BAND(0x09),
TODO_LIST(0x0a),
SCHEDULE(0x0c),
;
private final byte code;

View File

@ -24,7 +24,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr3.AmazfitGTR3FWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Support;

View File

@ -0,0 +1,60 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021FirmwareInfo;
public class AmazfitGTR4FirmwareInfo extends Huami2021FirmwareInfo {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTR4FirmwareInfo.class);
private static final Map<Integer, String> crcToVersion = new HashMap<Integer, String>() {{
// firmware
}};
public AmazfitGTR4FirmwareInfo(final byte[] bytes) {
super(bytes);
}
@Override
public String deviceName() {
return HuamiConst.AMAZFIT_GTR4_NAME;
}
@Override
public byte[] getExpectedFirmwareHeader() {
return new byte[]{(byte) 0x51, (byte) 0x71, (byte) 0x9c}; // Probably bogus, only checked against 1 firmware files
}
@Override
public boolean isGenerallyCompatibleWith(final GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITGTR4;
}
@Override
protected Map<Integer, String> getCrcMap() {
return crcToVersion;
}
}

View File

@ -0,0 +1,38 @@
/* Copyright (C) 2022 José Rebelo
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgtr4;
import android.content.Context;
import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr4.AmazfitGTR4FWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.Huami2021Support;
public class AmazfitGTR4Support extends Huami2021Support {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitGTR4Support.class);
@Override
public HuamiFWHelper createFWHelper(final Uri uri, final Context context) throws IOException {
return new AmazfitGTR4FWHelper(uri, context);
}
}

View File

@ -209,8 +209,8 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
}
@Override
public void onPhoneFound() {
byte[] bytes = gbDeviceProtocol.encodePhoneFound();
public void onFindPhone(boolean start) {
byte[] bytes = gbDeviceProtocol.encodeFindPhone(start);
sendToDevice(bytes);
}

View File

@ -110,7 +110,7 @@ public abstract class GBDeviceProtocol {
return null;
}
public byte[] encodePhoneFound() {
public byte[] encodeFindPhone(boolean start) {
return null;
}

View File

@ -82,6 +82,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband6.MiBand6Coordin
import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband7.MiBand7Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts3.AmazfitGTS3Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr3.AmazfitGTR3Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr4.AmazfitGTR4Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppe.ZeppECoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr2.AmazfitGTR2eCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts.AmazfitGTSCoordinator;
@ -293,6 +294,7 @@ public class DeviceHelper {
result.add(new MiBand7Coordinator());
result.add(new AmazfitGTS3Coordinator());
result.add(new AmazfitGTR3Coordinator());
result.add(new AmazfitGTR4Coordinator());
result.add(new MiBand2HRXCoordinator());
result.add(new MiBand2Coordinator()); // Note: MiBand2 and all of the above must come before MiBand because detection is hacky, atm
result.add(new MiBandCoordinator());

View File

@ -19,9 +19,13 @@ package nodomain.freeyourgadget.gadgetbridge.util;
import android.content.SharedPreferences;
import android.util.Log;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
@ -169,10 +173,27 @@ public class Prefs {
return Arrays.asList(stringValue.split(","));
}
public Date getTimePreference(final String key, final String defaultValue) {
final String time = getString(key, defaultValue);
final DateFormat df = new SimpleDateFormat("HH:mm", Locale.ROOT);
try {
return df.parse(time);
} catch (final Exception e) {
Log.e(TAG, "Error reading datetime preference value: " + key + "; returning default current time", e); // log the first exception
}
return new Date();
}
private void logReadError(String key, Exception ex) {
Log.e(TAG, "Error reading preference value: " + key + "; returning default value", ex); // log the first exception
}
public boolean contains(final String key) {
return preferences.contains(key);
}
/**
* Access to the underlying SharedPreferences, typically only used for editing values.
* @return the underlying SharedPreferences object.

View File

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
@ -147,6 +148,23 @@ public class StringUtils {
return null;
}
@Nullable
public static String untilNullTerminator(final ByteBuffer buf) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (buf.position() < buf.limit()) {
final byte b = buf.get();
if (b == 0) {
return baos.toString();
}
baos.write(b);
}
return null;
}
public static String bytesToHex(byte[] array) {
return GB.hexdump(array, 0, -1);
}

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#7E7E7E"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15zM5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11zM5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#7E7E7E"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z"/>
</vector>

View File

@ -146,6 +146,15 @@
<item>right</item>
</string-array>
<string-array name="weardirection">
<item>@string/buttons_on_left</item>
<item>@string/buttons_on_right</item>
</string-array>
<string-array name="weardirection_values">
<item>buttons_on_left</item>
<item>buttons_on_right</item>
</string-array>
<string-array name="orientation">
<item>@string/horizontal</item>
<item>@string/vertical</item>
@ -156,6 +165,7 @@
</string-array>
<string-array name="vibration_profile">
<item>@string/vibration_profile_default</item>
<item>@string/vibration_profile_staccato</item>
<item>@string/vibration_profile_short</item>
<item>@string/vibration_profile_medium</item>
@ -165,6 +175,8 @@
<item>@string/vibration_profile_alarm_clock</item>
</string-array>
<string-array name="vibration_profile_values">
<!-- special case - default -->
<item>@string/p_default</item>
<!-- Note: corresponds with IDs in VibrationProfile -->
<item>@string/p_staccato</item>
<item>@string/p_short</item>
@ -624,12 +636,208 @@
<item>@string/p_menuitem_music</item>
</string-array>
<string-array name="pref_huami2021_display_items">
<string-array name="pref_huami2021_empty_array">
<!-- empty array, will be requested from band -->
</string-array>
<string-array name="pref_huami2021_shortcuts">
<!-- empty array, will be requested from band -->
<string-array name="pref_workout_detection_categories">
<item>@string/activity_type_walking</item>
<item>@string/activity_type_indoor_walking</item>
<item>@string/activity_type_outdoor_running</item>
<item>@string/activity_type_treadmill</item>
<item>@string/activity_type_outdoor_cycling</item>
<item>@string/activity_type_pool_swimming</item>
<item>@string/activity_type_elliptical</item>
<item>@string/activity_type_rowing_machine</item>
</string-array>
<string-array name="pref_workout_detection_categories_values">
<item>walking</item>
<item>indoor_walking</item>
<item>outdoor_running</item>
<item>treadmill</item>
<item>outdoor_cycling</item>
<item>pool_swimming</item>
<item>elliptical</item>
<item>rowing_machine</item>
</string-array>
<string-array name="pref_gps_mode_preset">
<item>@string/accuracy</item>
<item>@string/balanced</item>
<item>@string/power_saving</item>
<item>@string/custom</item>
</string-array>
<string-array name="pref_gps_mode_preset_values">
<item>accuracy</item>
<item>balanced</item>
<item>power_saving</item>
<item>custom</item>
</string-array>
<string-array name="pref_gps_band">
<item>@string/single_band</item>
<item>@string/dual_band</item>
</string-array>
<string-array name="pref_gps_band_values">
<item>single_band</item>
<item>dual_band</item>
</string-array>
<string-array name="pref_gps_combination">
<item>@string/low_power_gps</item>
<item>@string/gps</item>
<item>@string/gps_bds</item>
<item>@string/gps_gnolass</item>
<item>@string/gps_galileo</item>
<item>@string/all_satellites</item>
</string-array>
<string-array name="pref_gps_combination_values">
<item>low_power_gps</item>
<item>gps</item>
<item>gps_bds</item>
<item>gps_gnolass</item>
<item>gps_galileo</item>
<item>all_satellites</item>
</string-array>
<string-array name="pref_gps_satellite_search">
<item>@string/speed_first</item>
<item>@string/accuracy_first</item>
</string-array>
<string-array name="pref_gps_satellite_search_values">
<item>speed_first</item>
<item>accuracy_first</item>
</string-array>
<string-array name="workout_detection_sensitivity">
<item>@string/sony_speak_to_chat_sensitivity_high</item>
<item>@string/sony_speak_to_chat_sensitivity_standard</item>
<item>@string/sony_speak_to_chat_sensitivity_low</item>
</string-array>
<string-array name="workout_detection_sensitivity_values">
<item>high</item>
<item>standard</item>
<item>low</item>
</string-array>
<string-array name="pref_zepp_os_apps">
<item>@string/menuitem_activity</item>
<item>@string/menuitem_alarm</item>
<item>@string/menuitem_alexa</item>
<item>@string/menuitem_barometer</item>
<item>@string/menuitem_breathing</item>
<item>@string/menuitem_calendar</item>
<item>@string/menuitem_compass</item>
<item>@string/menuitem_countdown</item>
<item>@string/menuitem_eventreminder</item>
<item>@string/menuitem_events</item>
<item>@string/menuitem_female_health</item>
<item>@string/menuitem_findphone</item>
<item>@string/menuitem_flashlight</item>
<item>@string/menuitem_hr</item>
<item>@string/menuitem_membership_cards</item>
<item>@string/menuitem_music</item>
<item>@string/menuitem_mutephone</item>
<item>@string/menuitem_offline_voice</item>
<item>@string/menuitem_one_tap_measuring</item>
<item>@string/menuitem_pai</item>
<item>@string/menuitem_personal_activity_intelligence</item>
<item>@string/menuitem_phone</item>
<item>@string/menuitem_pomodoro</item>
<item>@string/menuitem_settings</item>
<item>@string/menuitem_sleep</item>
<item>@string/menuitem_spo2</item>
<item>@string/menuitem_stopwatch</item>
<item>@string/menuitem_stress</item>
<item>@string/menuitem_sun_moon</item>
<item>@string/menuitem_takephoto</item>
<item>@string/menuitem_todo</item>
<item>@string/menuitem_voice_memos</item>
<item>@string/menuitem_weather</item>
<item>@string/menuitem_workout_history</item>
<item>@string/menuitem_workout</item>
<item>@string/menuitem_workout_status</item>
<item>@string/menuitem_worldclock</item>
<!-- dummy item -->
<item>@string/menuitem_more</item>
</string-array>
<string-array name="pref_zepp_os_apps_values">
<item>activity</item>
<item>alarm</item>
<item>alexa</item>
<item>barometer</item>
<item>breathing</item>
<item>calendar</item>
<item>compass</item>
<item>countdown</item>
<item>eventreminder</item>
<item>events</item>
<item>female_health</item>
<item>findphone</item>
<item>flashlight</item>
<item>hr</item>
<item>membership_cards</item>
<item>music</item>
<item>mutephone</item>
<item>offline_voice</item>
<item>one_tap_measuring</item>
<item>pai</item>
<item>personal_activity_intelligence</item>
<item>phone</item>
<item>pomodoro</item>
<item>settings</item>
<item>sleep</item>
<item>spo2</item>
<item>stopwatch</item>
<item>stress</item>
<item>sun_moon</item>
<item>takephoto</item>
<item>todo</item>
<item>voice_memos</item>
<item>weather</item>
<item>workout_history</item>
<item>workout</item>
<item>workout_status</item>
<item>worldclock</item>
<!-- dummy item -->
<item>more</item>
</string-array>
<string-array name="pref_huami2021_control_center">
<item>@string/battery</item>
<item>@string/menuitem_dnd</item>
<item>@string/menuitem_sleep</item>
<item>@string/menuitem_theater_mode</item>
<item>@string/menuitem_calendar</item>
<item>@string/menuitem_volume</item>
<item>@string/menuitem_screen_always_lit</item>
<item>@string/menuitem_brightness</item>
<item>@string/menuitem_settings</item>
<item>@string/menuitem_flashlight</item>
<item>@string/menuitem_bluetooth</item>
<item>@string/menuitem_wifi</item>
<item>@string/menuitem_lockscreen</item>
<item>@string/menuitem_findphone</item>
<item>@string/menuitem_eject_water</item>
</string-array>
<string-array name="pref_huami2021_control_center_values">
<item>battery</item>
<item>dnd</item>
<item>sleep</item>
<item>theater_mode</item>
<item>calendar</item>
<item>volume</item>
<item>screen_always_lit</item>
<item>brightness</item>
<item>settings</item>
<item>flashlight</item>
<item>bluetooth</item>
<item>wifi</item>
<item>lockscreen</item>
<item>findphone</item>
<item>eject_water</item>
</string-array>
<string-array name="pref_miband5_workout_activity_types">
@ -1852,7 +2060,7 @@
<item>1800</item>
</string-array>
<string-array name="screen_timeout_5_to_15">
<string-array name="screen_timeout">
<item>@string/seconds_5</item>
<item>@string/seconds_6</item>
<item>@string/seconds_7</item>
@ -1864,8 +2072,11 @@
<item>@string/seconds_13</item>
<item>@string/seconds_14</item>
<item>@string/seconds_15</item>
<item>@string/seconds_20</item>
<item>@string/seconds_25</item>
<item>@string/seconds_30</item>
</string-array>
<string-array name="screen_timeout_5_to_15_values">
<string-array name="screen_timeout_values">
<item>5</item>
<item>6</item>
<item>7</item>
@ -1877,6 +2088,9 @@
<item>13</item>
<item>14</item>
<item>15</item>
<item>20</item>
<item>25</item>
<item>30</item>
</string-array>
<string-array name="reminder_repeat">

View File

@ -58,6 +58,21 @@
<string name="controlcenter_calibrate_device">Calibrate Device</string>
<string name="controlcenter_get_heartrate_measurement">Get heart rate measurement</string>
<string name="accuracy">Accuracy</string>
<string name="balanced">Balanced</string>
<string name="power_saving">Power Saving</string>
<string name="custom">Custom</string>
<string name="single_band">Single Band</string>
<string name="dual_band">Dual Band</string>
<string name="low_power_gps">Low Power GPS</string>
<string name="gps">GPS</string>
<string name="gps_bds">GPS + BDS</string>
<string name="gps_gnolass">GPS + GNOLASS</string>
<string name="gps_galileo">GPS + GALILEO</string>
<string name="all_satellites">All Satellites</string>
<string name="speed_first">Speed first</string>
<string name="accuracy_first">Accuracy First</string>
<string name="device_card_activity_card_title">Activity info on device card</string>
<string name="device_card_activity_card_title_summary">Choose what activity details are displayed on device card</string>
<string name="prefs_activity_in_device_card_title">Show Activity info on device card</string>
@ -134,6 +149,7 @@
<string name="fw_upgrade_notice_miband7">You are about to install the %s firmware on your Xiaomi Smart Band 7.\n\nYour band will reboot after installing the .zip file.\n\nPROCEED AT YOUR OWN RISK!</string>
<string name="fw_upgrade_notice_amazfit_gts3">You are about to install the %s firmware on your Amazfit GTS 3.\n\nYour band will reboot after installing the .zip file.\n\nPROCEED AT YOUR OWN RISK!</string>
<string name="fw_upgrade_notice_amazfit_gtr3">You are about to install the %s firmware on your Amazfit GTR 3.\n\nYour band will reboot after installing the .zip file.\n\nPROCEED AT YOUR OWN RISK!</string>
<string name="fw_upgrade_notice_amazfit_gtr4">You are about to install the %s firmware on your Amazfit GTR 4.\n\nYour band will reboot after installing the .zip file.\n\nPROCEED AT YOUR OWN RISK!</string>
<string name="fw_upgrade_notice_amazfitx">You are about to install the %s firmware on your Amazfit X.\n\nPlease make sure to install the .fw file, and after that the .res file. Your band will reboot after installing the .fw file.\n\nNote: You do not have to install .res if it is exactly the same as the one previously installed.\n\nPROCEED AT YOUR OWN RISK!</string>
<string name="fw_upgrade_notice_amazfitneo">You are about to install the %s firmware on your Amazfit Neo.
\n
@ -165,6 +181,8 @@
<string name="pref_header_connection">Connection</string>
<string name="pref_header_display">Display</string>
<string name="pref_header_health">Health</string>
<string name="pref_header_sound_vibration">Sound &amp; Vibration</string>
<string name="pref_header_offline_voice">Offline Voice</string>
<string name="pref_header_time">Time</string>
<string name="pref_header_workout">Workout</string>
<string name="pref_header_equalizer">Equalizer</string>
@ -414,10 +432,38 @@
<string name="prefs_hr_alarm_activity">Heart rate alarm during sports activity</string>
<string name="prefs_hr_alarm_low">Low limit</string>
<string name="prefs_hr_alarm_high">High limit</string>
<string name="pref_gps_header">GPS</string>
<string name="pref_gps_mode_preset">GPS Mode</string>
<string name="pref_gps_band">GPS Band</string>
<string name="pref_gps_combination">GPS Combination</string>
<string name="pref_gps_satellite_search">Satellite Search</string>
<string name="pref_crown_vibration">Crown Vibration</string>
<string name="pref_alert_tone">Alert Tone</string>
<string name="pref_cover_to_mute">Cover to Mute</string>
<string name="pref_vibrate_for_alert">Vibrate for Alert</string>
<string name="pref_text_to_speech">Text to Speech</string>
<string name="offline_voice_respond_turn_wrist">Respond when turning the wrist</string>
<string name="offline_voice_respond_screen_on">Respond when screen on</string>
<string name="offline_voice_response_during_screen_lighting">Respond during screen lighting</string>
<string name="pref_agps_header">AGPS</string>
<string name="pref_agps_expiry_reminder_enabled">AGPS Expiry Reminder</string>
<string name="pref_agps_expiry_reminder_time">AGPS Expiry Reminder Time</string>
<string name="pref_workout_start_on_phone_title">Fitness app tracking</string>
<string name="pref_workout_start_on_phone_summary">Start/stop fitness app tracking on phone when a GPS workout is started on the band</string>
<string name="pref_workout_send_gps_title">Send GPS during workout</string>
<string name="pref_workout_send_gps_summary">Send the current GPS location to the band during a workout</string>
<string name="pref_workout_detection_title">Workout Detection</string>
<string name="pref_workout_detection_summary">Detect workout automatically</string>
<string name="pref_workout_detection_categories_title">Workout Categories</string>
<string name="pref_workout_detection_categories_summary">Workouts categories to detect automatically</string>
<string name="pref_workout_detection_alert_title">Alert</string>
<string name="pref_workout_detection_alert_summary">Notify when a workout is detected</string>
<string name="pref_workout_detection_sensitivity">Sensitivity</string>
<string name="pref_sleep_mode_title">Sleep Mode</string>
<string name="pref_sleep_mode_sleep_screen_title">Sleep Screen</string>
<string name="pref_sleep_mode_sleep_screen_summary">Show the Sleep Screen when waking the screen during sleep mode, to reduce distractions</string>
<string name="pref_sleep_mode_smart_enable_title">Smart Enable</string>
<string name="pref_sleep_mode_smart_enable_summary">Enable the sleep mode automatically when wearing the band during sleep</string>
<!-- Auto export preferences -->
<string name="pref_header_auto_export">Auto export</string>
<string name="pref_title_auto_export_enabled">Auto export enabled</string>
@ -486,6 +532,8 @@
<string name="right">Right</string>
<string name="horizontal">Horizontal</string>
<string name="vertical">Vertical</string>
<string name="buttons_on_left">Buttons on left</string>
<string name="buttons_on_right">Buttons on right</string>
<string name="miband_pairing_using_dummy_userdata">No valid user data given, using dummy user data for now.</string>
<string name="miband_pairing_tap_hint">When your Mi Band vibrates and blinks, tap it a few times in a row.</string>
<string name="appinstaller_install">Install</string>
@ -506,7 +554,9 @@
<string name="busy_task_fetch_activity_data">Fetching activity data</string>
<string name="sleep_activity_date_range">From %1$s to %2$s</string>
<string name="prefs_wearside">Wearing left or right?</string>
<string name="prefs_weardirection">Wearing direction</string>
<string name="pref_screen_vibration_profile">Vibration profile</string>
<string name="vibration_profile_default">Default</string>
<string name="vibration_profile_staccato">Staccato</string>
<string name="vibration_profile_short">Short</string>
<string name="vibration_profile_medium">Medium</string>
@ -529,6 +579,8 @@
<string name="pref_screen_notification_profile_inactivity">Inactivity notification</string>
<string name="pref_screen_notification_profile_low_power">Low power warning</string>
<string name="pref_screen_notification_profile_anti_loss">Anti-loss warning</string>
<string name="pref_screen_notification_profile_schedule">Schedule</string>
<string name="pref_screen_notification_profile_todo_list">To-Do List</string>
<string name="prefs_title_heartrate_measurement_interval">Whole day HR measurement</string>
<string name="pref_screen_notification_profile_event_reminder">Event reminder</string>
<string name="pref_screen_notification_profile_find_device">Find device</string>
@ -712,6 +764,7 @@
<string name="seconds_14">14 seconds</string>
<string name="seconds_15">15 seconds</string>
<string name="seconds_20">20 seconds</string>
<string name="seconds_25">25 seconds</string>
<string name="seconds_30">30 seconds</string>
<string name="minutes_1">1 minute</string>
<string name="minutes_5">5 minutes</string>
@ -725,6 +778,8 @@
<string name="pref_title_dont_ack_transfer">Do not ACK activity data transfer</string>
<string name="pref_summary_dont_ack_transfers">If not ACKed to the band, activity data is not cleared. Useful if GB is used together with other apps.</string>
<string name="pref_summary_keep_data_on_device">Will keep activity data on the device even after synchronization. Useful if GB is used together with other apps.</string>
<string name="pref_enable_unsupported_settings_title">Enable unsupported settings</string>
<string name="pref_enable_unsupported_settings_summary">This will enable access to all available settings, even if unsupported by the device. This can cause instability and crashes on the device.</string>
<string name="pref_title_low_latency_fw_update">Use low-latency mode for firmware flashing</string>
<string name="pref_summary_low_latency_fw_update">This might help on devices where firmware flashing fails.</string>
<string name="pref_title_third_party_app_device_settings">Allow 3rd party apps to change settings</string>
@ -790,6 +845,8 @@
<string name="mi2_prefs_heart_rate_monitoring">Heart Rate Monitoring</string>
<string name="mi2_prefs_heart_rate_monitoring_summary">Configure heart rate monitoring</string>
<string name="prefs_always_on_display">Always On Display</string>
<string name="prefs_always_on_display_follow_watchface">Style follows Watchface</string>
<string name="prefs_always_on_display_style">Style</string>
<string name="prefs_always_on_display_summary">Keep the band\'s display always on</string>
<string name="prefs_password">Password</string>
<string name="prefs_password_summary">Lock the band with a password when removed from the wrist</string>
@ -806,6 +863,8 @@
<string name="mi3_prefs_night_mode_summary">Lower band screen brightness automatically at night</string>
<string name="bip_prefs_shortcuts">Shortcuts</string>
<string name="bip_prefs_shotcuts_summary">Choose the shortcuts on the band screen</string>
<string name="prefs_control_center">Control Center</string>
<string name="prefs_control_center_summary">Choose the items on the control center dropdown</string>
<string name="prefs_activate_display_on_lift_sensitivity">Sensitivity</string>
<string name="prefs_screen_timeout">Screen Timeout</string>
<string name="mi5_prefs_workout_activity_types">Workout Activity Types</string>
@ -863,6 +922,7 @@
<string name="activity_prefs_gender">Gender</string>
<string name="activity_prefs_height_cm">Height in cm</string>
<string name="activity_prefs_weight_kg">Weight in kg</string>
<string name="activity_prefs_target_weight_kg">Target weight in kg</string>
<string name="activity_prefs_step_length_cm">Step length in cm</string>
<!-- Settings - Charts Preferences -->
<string name="pref_header_charts">Charts Settings</string>
@ -903,6 +963,8 @@
<string name="activity_prefs_calories_burnt">Daily target: calories burnt</string>
<string name="activity_prefs_distance_meters">Daily target: distance in meters</string>
<string name="activity_prefs_activetime_minutes">Daily target: active time in minutes</string>
<string name="activity_prefs_goal_standing_time_minutes">Daily target: standing time in minutes</string>
<string name="activity_prefs_goal_fat_burn_time_minutes">Daily target: fat burn time in minutes</string>
<string name="pref_title_pebble_health_store_raw">Store raw record in the database</string>
<string name="pref_summary_pebble_health_store_raw">Stores the data \"as is\", increasing the database usage to allow for later interpretation.</string>
<string name="action_db_management">Data management</string>
@ -1014,10 +1076,12 @@
<string name="activity_type_running">Running</string>
<string name="activity_type_outdoor_running">Outdoor Running</string>
<string name="activity_type_walking">Walking</string>
<string name="activity_type_indoor_walking">Indoor Walking</string>
<string name="activity_type_freestyle">Freestyle</string>
<string name="activity_type_hiking">Hiking</string>
<string name="activity_type_climbing">Climbing</string>
<string name="activity_type_swimming">Swimming</string>
<string name="activity_type_pool_swimming">Pool Swimming</string>
<string name="activity_type_swimming_openwater">Swimming (Open water)</string>
<string name="activity_type_indoor_cycling">Indoor Cycling</string>
<string name="activity_type_outdoor_cycling">Outdoor Cycling</string>
@ -1088,6 +1152,7 @@
<string name="devicetype_miband7">Xiaomi Smart Band 7</string>
<string name="devicetype_amazfit_gts3">Amazfit GTS 3</string>
<string name="devicetype_amazfit_gtr3">Amazfit GTR 3</string>
<string name="devicetype_amazfit_gtr4">Amazfit GTR 4</string>
<string name="devicetype_amazfit_band5">Amazfit Band 5</string>
<string name="devicetype_amazfit_neo">Amazfit Neo</string>
<string name="devicetype_amazfit_bip">Amazfit Bip</string>
@ -1208,7 +1273,24 @@
<string name='menuitem_workout_history'>Workout History</string>
<string name='menuitem_female_health'>Female Health</string>
<string name='menuitem_workout_status'>Workout Status</string>
<string name='menuitem_calendar'>Calendar</string>
<string name='menuitem_todo'>To-Do</string>
<string name='menuitem_voice_memos'>Voice Memos</string>
<string name='menuitem_sun_moon'>Sun &amp; Moon</string>
<string name='menuitem_one_tap_measuring'>One-tap Measuring</string>
<string name='menuitem_offline_voice'>Offline Voice</string>
<string name='menuitem_membership_cards'>Membership Cards</string>
<string name='menuitem_phone'>Phone</string>
<string name='menuitem_theater_mode'>Theater Mode</string>
<string name='menuitem_volume'>Volume</string>
<string name='menuitem_screen_always_lit'>Screen Always Lit</string>
<string name='menuitem_brightness'>Brightness</string>
<string name='menuitem_bluetooth'>Bluetooth</string>
<string name='menuitem_wifi'>Wi-Fi</string>
<string name='menuitem_lockscreen'>Lockscreen</string>
<string name='menuitem_eject_water'>Eject Water</string>
<string name="menuitem_unknown_app">Unknown (%s)</string>
<string name="menuitem_unsupported">[UNSUPPORTED] %s</string>
<string name="watch9_time_minutes">Minutes:</string>
<string name="watch9_time_hours">Hours:</string>
<string name="watch9_time_seconds">Seconds:</string>
@ -1290,6 +1372,8 @@
<string name="hr_appname_workout">Workout</string>
<string name="hr_appname_stopwatch">Stopwatch</string>
<string name="hr_appname_commute">Commute</string>
<string name="pref_title_upper_button_long_press_action">Upper Button long press action</string>
<string name="pref_title_lower_button_short_press_action">Lower Button short press action</string>
<string name="pref_title_upper_button_function_short">Upper Button short</string>
<string name="pref_title_middle_button_function_short">Middle Button short</string>
<string name="pref_title_lower_button_function_short">Lower Button short</string>
@ -1753,6 +1837,7 @@
<string name="sony_speak_to_chat_sensitivity_auto">Automatic</string>
<string name="sony_speak_to_chat_sensitivity_high">High</string>
<string name="sony_speak_to_chat_sensitivity_low">Low</string>
<string name="sony_speak_to_chat_sensitivity_standard">Standard</string>
<string name="sony_speak_to_chat_focus_on_voice">Focus on Voice</string>
<string name="sony_speak_to_chat_timeout">Timeout</string>
<string name="sony_speak_to_chat_timeout_off">Off</string>
@ -1775,6 +1860,8 @@
<string name="sony_button_mode_ambient_sound_control">Ambient Sound Control</string>
<string name="sony_button_mode_playback_control">Playback Control</string>
<string name="sony_button_mode_volume_control">Volume Control</string>
<string name="pref_screen_auto_brightness_title">Auto Brightness</string>
<string name="pref_screen_auto_brightness_summary">Adjust screen brightness according to ambient light</string>
<string name="pref_screen_brightness">Screen Brightness</string>
<string name="watchface_widget_type_custom">Custom widget</string>
<string name="watchface_dialog_widget_timezone">Time zone</string>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Note: these are not translated! They are used as preference keys -->
<item name="p_default" type="string">default</item>
<item name="p_staccato" type="string">staccato</item>
<item name="p_short" type="string">short</item>
<item name="p_medium" type="string">medium</item>

View File

@ -31,6 +31,12 @@
android:maxLength="3"
android:title="@string/activity_prefs_weight_kg" />
<EditTextPreference
android:inputType="number"
android:key="activity_user_goal_weight_kg"
android:maxLength="3"
android:title="@string/activity_prefs_target_weight_kg" />
<EditTextPreference
android:inputType="number"
android:key="activity_user_step_length_cm"
@ -70,5 +76,19 @@
android:key="activity_user_activetime_minutes"
android:maxLength="3"
android:title="@string/activity_prefs_activetime_minutes" />
<EditTextPreference
android:defaultValue="12"
android:inputType="number"
android:key="activity_user_goal_standing_time_minutes"
android:maxLength="2"
android:title="@string/activity_prefs_goal_standing_time_minutes" />
<EditTextPreference
android:defaultValue="30"
android:inputType="number"
android:key="activity_user_goal_fat_burn_time_minutes"
android:maxLength="3"
android:title="@string/activity_prefs_goal_fat_burn_time_minutes" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -27,5 +27,18 @@
android:defaultValue="00:00"
android:key="always_on_display_end"
android:title="@string/mi2_prefs_do_not_disturb_end" />
<SwitchPreference
android:defaultValue="true"
android:key="always_on_display_follow_watchface"
android:title="@string/prefs_always_on_display_follow_watchface" />
<ListPreference
android:defaultValue=""
android:entries="@array/pref_huami2021_empty_array"
android:entryValues="@array/pref_huami2021_empty_array"
android:key="always_on_display_style"
android:summary="%s"
android:title="@string/prefs_always_on_display_style" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue=""
android:entries="@array/pref_zepp_os_apps"
android:entryValues="@array/pref_zepp_os_apps_values"
android:icon="@drawable/ic_smart_button"
android:key="pref_button_action_lower_short"
android:summary="%s"
android:title="@string/pref_title_lower_button_short_press_action" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue=""
android:entries="@array/pref_zepp_os_apps"
android:entryValues="@array/pref_zepp_os_apps_values"
android:icon="@drawable/ic_smart_button"
android:key="pref_button_action_upper_long"
android:summary="%s"
android:title="@string/pref_title_upper_button_long_press_action" />
</androidx.preference.PreferenceScreen>

View File

@ -2,7 +2,7 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_block"
android:key="screen_do_not_disturb"
android:key="pref_screen_do_not_disturb"
android:persistent="false"
android:summary="@string/mi2_prefs_do_not_disturb_summary"
android:title="@string/mi2_prefs_do_not_disturb">

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_gps_location"
android:key="pref_header_gps"
android:persistent="false"
android:title="@string/pref_gps_header">
<PreferenceCategory
android:key="pref_gps_header_category"
android:title="@string/pref_gps_header">
<ListPreference
android:defaultValue=""
android:entries="@array/pref_gps_mode_preset"
android:entryValues="@array/pref_gps_mode_preset_values"
android:key="pref_gps_mode_preset"
android:summary="%s"
android:title="@string/pref_gps_mode_preset" />
<ListPreference
android:defaultValue=""
android:entries="@array/pref_gps_band"
android:entryValues="@array/pref_gps_band_values"
android:key="pref_gps_band"
android:summary="%s"
android:title="@string/pref_gps_band" />
<ListPreference
android:defaultValue=""
android:entries="@array/pref_gps_combination"
android:entryValues="@array/pref_gps_combination_values"
android:key="pref_gps_combination"
android:summary="%s"
android:title="@string/pref_gps_combination" />
<ListPreference
android:defaultValue=""
android:entries="@array/pref_gps_satellite_search"
android:entryValues="@array/pref_gps_satellite_search_values"
android:key="pref_gps_satellite_search"
android:summary="%s"
android:title="@string/pref_gps_satellite_search" />
</PreferenceCategory>
<PreferenceCategory
android:key="pref_header_agps"
android:title="@string/pref_agps_header">
<SwitchPreference
android:defaultValue="false"
android:key="pref_agps_expiry_reminder_enabled"
android:title="@string/pref_agps_expiry_reminder_enabled" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="20:00"
android:dependency="pref_agps_expiry_reminder_enabled"
android:key="pref_agps_expiry_reminder_time"
android:title="@string/pref_agps_expiry_reminder_time" />
</PreferenceCategory>
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_header_sound_vibration"
android:title="@string/pref_header_sound_vibration" />
</androidx.preference.PreferenceScreen>

View File

@ -2,7 +2,7 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_heartrate"
android:key="heartrate_monitoring_key"
android:key="pref_screen_heartrate_monitoring"
android:persistent="false"
android:summary="@string/mi2_prefs_heart_rate_monitoring_alerts_summary"
android:title="@string/mi2_prefs_heart_rate_monitoring">
@ -38,6 +38,15 @@
android:title="@string/prefs_title_heartrate_measurement_interval" />
</PreferenceCategory>
<!-- Activity Monitoring -->
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_activity_unknown_small"
android:key="heartrate_activity_monitoring"
android:summary="@string/prefs_activity_monitoring_description"
android:title="@string/prefs_activity_monitoring_title" />
<!-- Heart Rate Alerts -->
<PreferenceCategory

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<com.mobeta.android.dslv.DragSortListPreference
android:defaultValue="@array/pref_huami2021_empty_array"
android:dialogTitle="@string/bip_prefs_shortcuts"
android:entries="@array/pref_huami2021_control_center"
android:entryValues="@array/pref_huami2021_control_center_values"
android:icon="@drawable/ic_menu"
android:key="control_center_sortable"
android:persistent="true"
android:summary="@string/prefs_control_center_summary"
android:title="@string/prefs_control_center" />
</androidx.preference.PreferenceScreen>

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<com.mobeta.android.dslv.DragSortListPreference
android:defaultValue="@array/pref_huami2021_display_items"
android:defaultValue="@array/pref_huami2021_empty_array"
android:dialogTitle="@string/mi2_prefs_display_items"
android:entries="@array/pref_huami2021_display_items"
android:entryValues="@array/pref_huami2021_display_items"
android:entries="@array/pref_zepp_os_apps"
android:entryValues="@array/pref_zepp_os_apps_values"
android:icon="@drawable/ic_widgets"
android:key="display_items_sortable"
android:persistent="true"

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<com.mobeta.android.dslv.DragSortListPreference
android:icon="@drawable/ic_shortcut"
android:defaultValue="@array/pref_huami2021_shortcuts"
android:defaultValue="@array/pref_huami2021_empty_array"
android:dialogTitle="@string/bip_prefs_shortcuts"
android:entries="@array/pref_huami2021_shortcuts"
android:entryValues="@array/pref_huami2021_shortcuts"
android:entries="@array/pref_zepp_os_apps"
android:entryValues="@array/pref_zepp_os_apps_values"
android:icon="@drawable/ic_shortcut"
android:key="shortcuts_sortable"
android:persistent="true"
android:summary="@string/bip_prefs_shotcuts_summary"

View File

@ -2,7 +2,7 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_chair"
android:key="screen_inactivity"
android:key="pref_screen_inactivity_extended"
android:persistent="false"
android:summary="@string/mi2_prefs_inactivity_warnings_summary"
android:title="@string/mi2_prefs_inactivity_warnings">
@ -10,8 +10,8 @@
<SwitchPreference
android:defaultValue="false"
android:key="inactivity_warnings_enable"
android:title="@string/mi2_prefs_inactivity_warnings"
android:summary="@string/mi2_prefs_inactivity_warnings_summary" />
android:summary="@string/mi2_prefs_inactivity_warnings_summary"
android:title="@string/mi2_prefs_inactivity_warnings" />
<nodomain.freeyourgadget.gadgetbridge.util.XTimePreference
android:defaultValue="06:00"

View File

@ -2,7 +2,7 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_chair"
android:key="screen_inactivity_extended"
android:key="pref_screen_inactivity_extended"
android:persistent="false"
android:summary="@string/mi2_prefs_inactivity_warnings_summary"
android:title="@string/mi2_prefs_inactivity_warnings">
@ -13,8 +13,8 @@
<SwitchPreference
android:defaultValue="false"
android:key="inactivity_warnings_enable"
android:title="@string/mi2_prefs_inactivity_warnings"
android:summary="@string/mi2_prefs_inactivity_warnings_summary" />
android:summary="@string/mi2_prefs_inactivity_warnings_summary"
android:title="@string/mi2_prefs_inactivity_warnings" />
<ListPreference
android:defaultValue="4"

View File

@ -2,7 +2,7 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_arrow_upward"
android:key="screen_liftwrist_display"
android:key="pref_screen_lift_wrist"
android:persistent="false"
android:title="@string/mi2_prefs_activate_display_on_lift">

View File

@ -3,7 +3,7 @@
<PreferenceScreen
android:icon="@drawable/ic_brightness_2"
android:key="screen_night_mode"
android:key="pref_screen_night_mode"
android:persistent="false"
android:summary="@string/mi3_prefs_night_mode_summary"
android:title="@string/mi3_prefs_night_mode">

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_voice"
android:key="pref_screen_offline_voice"
android:persistent="false"
android:title="@string/pref_header_offline_voice">
<ListPreference
android:defaultValue="en_US"
android:entries="@array/pref_language_all"
android:entryValues="@array/pref_language_all_values"
android:icon="@drawable/ic_language"
android:key="offline_voice_language"
android:summary="%s"
android:title="@string/pref_title_language" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_rotate_left"
android:key="offline_voice_respond_turn_wrist"
android:title="@string/offline_voice_respond_turn_wrist" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_always_on_display"
android:key="offline_voice_respond_screen_on"
android:title="@string/offline_voice_respond_screen_on" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_always_on_display"
android:key="offline_voice_response_during_screen_lighting"
android:title="@string/offline_voice_response_during_screen_lighting" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -2,7 +2,7 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_password"
android:key="password_monitoring_key"
android:key="pref_screen_password"
android:persistent="false"
android:summary="@string/prefs_password_summary"
android:title="@string/prefs_password">

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_wb_sunny"
android:key="pref_screen_auto_brightness"
android:persistent="false"
android:title="@string/pref_screen_brightness">
<SwitchPreference
android:defaultValue="true"
android:icon="@drawable/ic_wb_sunny"
android:key="screen_auto_brightness"
android:summary="@string/pref_screen_auto_brightness_summary"
android:title="@string/pref_screen_auto_brightness_title" />
<SeekBarPreference
android:defaultValue="50"
android:icon="@drawable/ic_wb_sunny"
android:key="screen_brightness"
android:max="100"
android:title="@string/pref_screen_brightness" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -2,8 +2,8 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue="5"
android:entries="@array/screen_timeout_5_to_15"
android:entryValues="@array/screen_timeout_5_to_15_values"
android:entries="@array/screen_timeout"
android:entryValues="@array/screen_timeout_values"
android:key="screen_timeout"
android:icon="@drawable/ic_hourglass_empty"
android:summary="%s"

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_activity_sleep"
android:key="pref_screen_sleep_mode"
android:persistent="false"
android:title="@string/pref_sleep_mode_title">
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_activity_sleep"
android:key="pref_sleep_mode_sleep_screen"
android:summary="@string/pref_sleep_mode_sleep_screen_summary"
android:title="@string/pref_sleep_mode_sleep_screen_title" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_auto_awesome"
android:key="pref_sleep_mode_smart_enable"
android:summary="@string/pref_sleep_mode_smart_enable_summary"
android:title="@string/pref_sleep_mode_smart_enable_title" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_volume_up"
android:key="pref_screen_sound_and_vibration"
android:persistent="false"
android:title="@string/pref_header_sound_vibration">
<SeekBarPreference
android:defaultValue="50"
android:icon="@drawable/ic_volume_up"
android:key="volume"
android:max="100"
android:title="@string/menuitem_volume" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_rotate_left"
android:key="crown_vibration"
android:title="@string/pref_crown_vibration" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_warning_gray"
android:key="alert_tone"
android:title="@string/pref_alert_tone" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_volume_off"
android:key="cover_to_mute"
android:title="@string/pref_cover_to_mute" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_vibration"
android:key="vibrate_for_alert"
android:title="@string/pref_vibrate_for_alert" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_voice"
android:key="text_to_speech"
android:title="@string/pref_text_to_speech" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -19,7 +19,7 @@
<PreferenceCategory android:title="@string/pref_screen_notification_profile_generic" />
<ListPreference
android:defaultValue="@string/p_short"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_app_alerts"
@ -49,7 +49,7 @@
<PreferenceCategory android:title="@string/pref_screen_notification_profile_incoming_call" />
<ListPreference
android:defaultValue="@string/p_ring"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_incoming_call"
@ -79,7 +79,7 @@
<PreferenceCategory android:title="@string/pref_screen_notification_profile_sms" />
<ListPreference
android:defaultValue="@string/p_staccato"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_incoming_sms"
@ -109,7 +109,7 @@
<PreferenceCategory android:title="@string/mi2_prefs_goal_notification" />
<ListPreference
android:defaultValue="@string/p_long"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_goal_notification"
@ -139,7 +139,7 @@
<PreferenceCategory android:title="@string/vibration_profile_alarm_clock" />
<ListPreference
android:defaultValue="@string/p_long"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_alarm"
@ -169,7 +169,7 @@
<PreferenceCategory android:title="@string/pref_screen_notification_idle_alerts" />
<ListPreference
android:defaultValue="@string/p_medium"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_idle_alerts"
@ -199,7 +199,7 @@
<PreferenceCategory android:title="@string/pref_screen_notification_profile_event_reminder" />
<ListPreference
android:defaultValue="@string/p_long"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_event_reminder"
@ -229,7 +229,7 @@
<PreferenceCategory android:title="@string/pref_screen_notification_profile_find_device" />
<ListPreference
android:defaultValue="@string/p_long"
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_find_band"
@ -249,5 +249,64 @@
android:title="@string/vibration_try"/>
</PreferenceScreen>
<PreferenceScreen
android:key="vibration_profile_key_todo_list"
android:icon="@drawable/ic_checklist"
android:title="@string/pref_screen_notification_profile_todo_list"
android:persistent="false">
<!-- workaround for missing toolbar -->
<PreferenceCategory android:title="@string/pref_screen_notification_profile_todo_list" />
<ListPreference
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_todo_list"
android:title="@string/miband_prefs_vibration"
android:summary="%s" />
<EditTextPreference
android:defaultValue="2"
android:inputType="number"
android:key="huami_vibration_count_todo_list"
android:maxLength="1"
android:title="@string/pref_title_notifications_repetitions" />
<Preference
android:persistent="false"
android:key="huami_vibration_try_todo_list"
android:title="@string/vibration_try"/>
</PreferenceScreen>
<PreferenceScreen
android:key="vibration_profile_key_schedule"
android:icon="@drawable/ic_calendar_to"
android:title="@string/pref_screen_notification_profile_schedule"
android:persistent="false">
<!-- workaround for missing toolbar -->
<PreferenceCategory android:title="@string/pref_screen_notification_profile_schedule" />
<ListPreference
android:defaultValue="@string/p_default"
android:entries="@array/vibration_profile"
android:entryValues="@array/vibration_profile_values"
android:key="huami_vibration_profile_schedule"
android:title="@string/miband_prefs_vibration"
android:summary="%s" />
<EditTextPreference
android:defaultValue="2"
android:inputType="number"
android:key="huami_vibration_count_schedule"
android:maxLength="1"
android:title="@string/pref_title_notifications_repetitions" />
<Preference
android:persistent="false"
android:key="huami_vibration_try_schedule"
android:title="@string/vibration_try"/>
</PreferenceScreen>
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue="buttons_on_right"
android:entries="@array/weardirection"
android:entryValues="@array/weardirection_values"
android:icon="@drawable/ic_switch_left"
android:key="weardirection"
android:summary="%s"
android:title="@string/prefs_weardirection" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_activity_unknown_small"
android:key="pref_header_workout_detection"
android:persistent="false"
android:summary="@string/pref_workout_detection_summary"
android:title="@string/pref_workout_detection_title">
<MultiSelectListPreference
android:defaultValue="@array/pref_huami2021_empty_array"
android:dialogTitle="@string/pref_workout_detection_categories_title"
android:entries="@array/pref_workout_detection_categories"
android:entryValues="@array/pref_workout_detection_categories_values"
android:icon="@drawable/ic_activity_unknown_small"
android:key="workout_detection_categories"
android:summary="@string/pref_workout_detection_categories_summary"
android:title="@string/pref_workout_detection_categories_title" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_notifications"
android:key="workout_detection_alert"
android:summary="@string/pref_workout_detection_alert_summary"
android:title="@string/pref_workout_detection_alert_title" />
<ListPreference
android:defaultValue=""
android:entries="@array/workout_detection_sensitivity"
android:entryValues="@array/workout_detection_sensitivity_values"
android:key="workout_detection_sensitivity"
android:summary="%s"
android:title="@string/pref_workout_detection_sensitivity" />
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -153,7 +153,7 @@ class TestDeviceSupport extends AbstractDeviceSupport {
}
@Override
public void onPhoneFound() {
public void onFindPhone(boolean start) {
}