1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-03 02:06:21 +02:00

Merge remote-tracking branch 'origin/master'

This commit is contained in:
Daniel Dakhno 2022-01-20 15:51:25 +01:00
commit 7697b588d1
61 changed files with 1442 additions and 332 deletions

View File

@ -1,8 +0,0 @@
kind: pipeline
name: android
steps:
- name: build
image: androidsdk/android-29
commands:
- ./gradlew assembleDebug --stacktrace

View File

@ -1,6 +0,0 @@
pipeline:
test:
image: androidsdk/android-30
commands:
- pwd #bump
- ./gradlew assembleDebug --stacktrace

View File

@ -0,0 +1,13 @@
pipeline:
build:
image: androidsdk/android-30
commands:
- pwd #bump
- ./gradlew assembleDebug --stacktrace
#this doesn't work yet:
#https://github.com/woodpecker-ci/woodpecker/issues/687
#when:
#repo: Freeyourgadget/Gadgetbridge
#branch: master

View File

@ -1,5 +1,19 @@
### Changelog
### Next
* Sony WH-1000XM4: Initial Support
* Sony WH-1000XM3: Disable equalizer, surround and sound position while in SBC codec
* Improve Sony Headphones initialization on connection
* Fixed accidentally disabled time synchronization and pairing of new Casio GBX/GBD-series watches
* Fossil HR: improved Device Applications List handling. Added ability to change activity recognition settings on the watch
* Fossil HR: Make width of custom widget configurable. Disable non-configurable buttons preferences
* Add Support for Bip U series China Variant
* Add icon for VESC devices
* Add commit id into About screen
* Make debug activity notification test to persist text while switching apps
* Add Portuguese to the list of language options
* Update configuration button icon in app notification settings
### 0.64.0
* Initial support for VESC NRF/HM10 devices
* Initial support vor Bose QC35

View File

@ -48,6 +48,8 @@ vendor's servers.
- Casio GB-5600B
- Casio STB-1000
- Casio GBX-100
- Casio GBD-100
- Casio GBD-200
- [FitPro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/FitPro)
- Fossil
- [Hybrid HR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Fossil-Hybrid-HR) [**\[!\]**](#special-pairing-procedures)
@ -79,7 +81,7 @@ vendor's servers.
- PineTime (InfiniTime Firmware)
- Roidmi, Roidmi 3, Mojietu 3 (Bluetooth FM Transmitters)
- [SMA](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/SMA) Q2 (SMA-Q2-OSS Firmware)
- Sony WH-1000XM3, WF-SP800N
- [Sony WH-1000XM3, WH-1000XM4, WF-SP800N](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Sony-Headphones)
- Teclast H10, H30
- TLW64
- Vibratissimo (Experimental)

View File

@ -101,6 +101,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage;
public class DebugActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class);
private static Bundle dataLossSave;
private static final String EXTRA_REPLY = "reply";
private static final String ACTION_REPLY
= "nodomain.freeyourgadget.gadgetbridge.DebugActivity.action.reply";
@ -508,6 +510,29 @@ public class DebugActivity extends AbstractGBActivity {
}
@Override
protected void onPause() {
super.onPause();
if (dataLossSave != null ) {
dataLossSave.clear();
dataLossSave = null ;
}
dataLossSave = new Bundle();
dataLossSave.putString("editContent", editContent.getText().toString());
}
@Override
protected void onResume() {
super.onResume();
if (dataLossSave != null ) {
editContent.setText(dataLossSave.getString("editContent", ""));
}else{
editContent.setText("Test");
}
}
private void deleteWidgetsPrefs() {
WidgetPreferenceStorage widgetPreferenceStorage = new WidgetPreferenceStorage();
widgetPreferenceStorage.deleteWidgetsPrefs(DebugActivity.this);

View File

@ -121,8 +121,13 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_SONY_AMBIENT_SOUND_CONTROL = "pref_sony_ambient_sound_control";
public static final String PREF_SONY_FOCUS_VOICE = "pref_sony_focus_voice";
public static final String PREF_SONY_AMBIENT_SOUND_LEVEL = "pref_sony_ambient_sound_level";
public static final String PREF_SONY_NOISE_OPTIMIZER_START = "pref_sony_noise_optimizer_start";
public static final String PREF_SONY_NOISE_OPTIMIZER_CANCEL = "pref_sony_noise_optimizer_cancel";
public static final String PREF_SONY_NOISE_OPTIMIZER_STATUS = "pref_sony_noise_optimizer_status";
public static final String PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE = "pref_sony_noise_optimizer_state_pressure";
public static final String PREF_SONY_SOUND_POSITION = "pref_sony_sound_position";
public static final String PREF_SONY_SURROUND_MODE = "pref_sony_surround_mode";
public static final String PREF_SONY_EQUALIZER = "pref_sony_equalizer";
public static final String PREF_SONY_EQUALIZER_MODE = "pref_sony_equalizer_mode";
public static final String PREF_SONY_AUDIO_UPSAMPLING = "pref_sony_audio_upsampling";
public static final String PREF_SONY_EQUALIZER_BAND_400 = "pref_sony_equalizer_band_400";
@ -137,6 +142,11 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_SONY_BUTTON_MODE_RIGHT = "pref_sony_button_mode_right";
public static final String PREF_SONY_AUTOMATIC_POWER_OFF = "pref_sony_automatic_power_off";
public static final String PREF_SONY_NOTIFICATION_VOICE_GUIDE = "pref_sony_notification_voice_guide";
public static final String PREF_SONY_SPEAK_TO_CHAT = "pref_sony_speak_to_chat";
public static final String PREF_SONY_SPEAK_TO_CHAT_SENSITIVITY = "pref_sony_speak_to_chat_sensitivity";
public static final String PREF_SONY_SPEAK_TO_CHAT_FOCUS_ON_VOICE = "pref_sony_speak_to_chat_focus_on_voice";
public static final String PREF_SONY_SPEAK_TO_CHAT_TIMEOUT = "pref_sony_speak_to_chat_timeout";
public static final String PREF_SONY_CONNECT_TWO_DEVICES = "pref_sony_connect_two_devices";
public static final String PREF_QC35_NOISE_CANCELLING_LEVEL = "qc35_noise_cancelling_level";

View File

@ -16,11 +16,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings;
import android.os.Parcelable;
import androidx.preference.Preference;
/**
* A device-specific preference handler, that allows for concrete implementations to customize the preferences in
* the {@link DeviceSpecificSettingsFragment}.
*/
public interface DeviceSpecificSettingsCustomizer {
public interface DeviceSpecificSettingsCustomizer extends Parcelable {
/**
* Called when a {@link Preference} changes, not caused by user input (so the preference change listener is not called).
*
* @param preference the {@link Preference} preference that changed
* @param handler the {@link DeviceSpecificSettingsHandler}
*/
void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler);
/**
* Customize the settings on the {@link DeviceSpecificSettingsFragment}.
*

View File

@ -56,108 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.XTimePreference;
import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ALTITUDE_CALIBRATE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AMPM_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ANTILOST_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOREMOVE_NOTIFICATIONS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BT_CONNECTED_ADVERTISEMENT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOLIGHT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOREMOVE_MESSAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_DOUBLE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_LONG;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_SHORT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_DOUBLE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_LONG;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_SHORT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_DOUBLE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_LONG;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_SHORT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_BP_CALIBRATE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DATEFORMAT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FAKE_RING_DURATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FIND_PHONE_ENABLED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOICE_FOCUS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOLUME;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_DOLBY;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_GAME_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_LOCK_TOUCH;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_LEFT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_RIGHT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_LIVE_ANC;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_PRESSURE_RELIEF;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_DANGEROUS_EXTERNAL_INTENTS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_DRAW_WIDGET_CIRCLES;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_FORCE_WHITE_COLOR;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_SAVE_RAW_ACTIVITY_FILES;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_RUNNING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_BIKING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_WALKING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_ROWING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYDRATION_PERIOD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYDRATION_SWITCH;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_KEY_VIBRATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LANGUAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LEFUN_INTERFACE_LANGUAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LIFTWRIST_NOSHED;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_PERIOD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_SWITCH;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_SWITCH;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_SLEEP;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_INTERVAL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTIFICATION_ENABLE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_OPERATING_SOUNDS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_POWER_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SCREEN_ORIENTATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME_END;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_LOW_VIBRATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_SMART_INTERVAL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_STAMINA;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_PAUSE_WHEN_TAKEN_OFF;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SOUNDS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_STRENGH_PERCENTAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_ENABLE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_INEAR;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_FOCUS_VOICE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SURROUND_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_400;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_1000;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_2500;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_6300;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_16000;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BASS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_UPSAMPLING;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_TOUCH_SENSOR;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_MODE_RIGHT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_MODE_LEFT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AUTOMATIC_POWER_OFF;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOTIFICATION_VOICE_GUIDE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_DISTANCE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_DEVICE_CHARTS_TABS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT;
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;
@ -194,18 +93,14 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR
public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat implements DeviceSpecificSettingsHandler {
private final DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer;
public DeviceSpecificSettingsFragment(final DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer) {
this.deviceSpecificSettingsCustomizer = deviceSpecificSettingsCustomizer;
}
private static final Logger LOG = LoggerFactory.getLogger(DeviceSpecificSettingsFragment.class);
static final String FRAGMENT_TAG = "DEVICE_SPECIFIC_SETTINGS_FRAGMENT";
private final SharedPreferencesChangeHandler sharedPreferencesChangeHandler = new SharedPreferencesChangeHandler();
private DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer;
private void setSettingsFileSuffix(String settingsFileSuffix, @NonNull int[] supportedSettings, String[] supportedLanguages) {
Bundle args = new Bundle();
args.putString("settingsFileSuffix", settingsFileSuffix);
@ -214,6 +109,11 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
setArguments(args);
}
private void setDeviceSpecificSettingsCustomizer(final DeviceSpecificSettingsCustomizer customizer) {
final Bundle args = getArguments() != null ? getArguments() : new Bundle();
args.putParcelable("deviceSpecificSettingsCustomizer", customizer);
setArguments(args);
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@ -224,6 +124,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
String settingsFileSuffix = arguments.getString("settingsFileSuffix", null);
int[] supportedSettings = arguments.getIntArray("supportedSettings");
String[] supportedLanguages = arguments.getStringArray("supportedLanguages");
this.deviceSpecificSettingsCustomizer = arguments.getParcelable("deviceSpecificSettingsCustomizer");
if (settingsFileSuffix == null || supportedSettings == null) {
return;
@ -294,6 +195,19 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
getListView().post(runnable);
}
/*
* delayed execution so that the preferences are applied first
*/
@Override
public void notifyPreferenceChanged(final String preferenceKey) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(preferenceKey);
}
});
}
private void setChangeListener() {
final Prefs prefs = new Prefs(getPreferenceManager().getSharedPreferences());
String disconnectNotificationState = prefs.getString(PREF_DISCONNECT_NOTIFICATION, PREF_DO_NOT_DISTURB_OFF);
@ -305,12 +219,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
disconnectNotificationStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_START);
}
});
notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION_START);
return true;
}
});
@ -322,12 +231,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
disconnectNotificationEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_END);
}
});
notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION_END);
return true;
}
});
@ -342,12 +246,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
Objects.requireNonNull(disconnectNotificationStart).setEnabled(scheduled);
Objects.requireNonNull(disconnectNotificationEnd).setEnabled(scheduled);
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION);
}
});
notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION);
return true;
}
});
@ -363,12 +262,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
nightModeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_START);
}
});
notifyPreferenceChanged(PREF_NIGHT_MODE_START);
return true;
}
});
@ -380,12 +274,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
nightModeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_END);
}
});
notifyPreferenceChanged(PREF_NIGHT_MODE_END);
return true;
}
});
@ -402,12 +291,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
Objects.requireNonNull(nightModeStart).setEnabled(scheduled);
Objects.requireNonNull(nightModeEnd).setEnabled(scheduled);
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE);
}
});
notifyPreferenceChanged(PREF_NIGHT_MODE);
return true;
}
});
@ -423,12 +307,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
doNotDisturbStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_START);
}
});
notifyPreferenceChanged(PREF_DO_NOT_DISTURB_START);
return true;
}
});
@ -440,12 +319,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
doNotDisturbEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_END);
}
});
notifyPreferenceChanged(PREF_DO_NOT_DISTURB_END);
return true;
}
});
@ -461,12 +335,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
Objects.requireNonNull(doNotDisturbStart).setEnabled(scheduled);
Objects.requireNonNull(doNotDisturbEnd).setEnabled(scheduled);
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB);
}
});
notifyPreferenceChanged(PREF_DO_NOT_DISTURB);
return true;
}
});
@ -577,6 +446,11 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
addPreferenceHandlerFor(PREF_SONY_BUTTON_MODE_RIGHT);
addPreferenceHandlerFor(PREF_SONY_AUTOMATIC_POWER_OFF);
addPreferenceHandlerFor(PREF_SONY_NOTIFICATION_VOICE_GUIDE);
addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT);
addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT_SENSITIVITY);
addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT_FOCUS_ON_VOICE);
addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT_TIMEOUT);
addPreferenceHandlerFor(PREF_SONY_CONNECT_TWO_DEVICES);
addPreferenceHandlerFor(PREF_QC35_NOISE_CANCELLING_LEVEL);
@ -589,12 +463,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
sleepTimeInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME);
}
});
notifyPreferenceChanged(PREF_SLEEP_TIME);
return true;
}
});
@ -606,12 +475,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
sleepTimeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_START);
}
});
notifyPreferenceChanged(PREF_SLEEP_TIME_START);
return true;
}
});
@ -623,12 +487,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
sleepTimeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_END);
}
});
notifyPreferenceChanged(PREF_SLEEP_TIME_END);
return true;
}
});
@ -645,12 +504,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
if (sleepTimeInfo != null) {
//sleepTimeInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString()));
}
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME);
}
});
notifyPreferenceChanged(PREF_SLEEP_TIME);
return true;
}
});
@ -664,12 +518,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
rotateWristCycleInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO);
}
});
notifyPreferenceChanged(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO);
return true;
}
});
@ -681,12 +530,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
displayOnLiftStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_START);
}
});
notifyPreferenceChanged(PREF_DISPLAY_ON_LIFT_START);
return true;
}
});
@ -698,12 +542,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
displayOnLiftEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_END);
}
});
notifyPreferenceChanged(PREF_DISPLAY_ON_LIFT_END);
return true;
}
});
@ -720,12 +559,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
if (rotateWristCycleInfo != null) {
rotateWristCycleInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString()));
}
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT);
}
});
notifyPreferenceChanged(PREF_ACTIVATE_DISPLAY_ON_LIFT);
return true;
}
});
@ -852,8 +686,9 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
@NonNull int[] supportedSettings,
String[] supportedLanguages,
DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer) {
DeviceSpecificSettingsFragment fragment = new DeviceSpecificSettingsFragment(deviceSpecificSettingsCustomizer);
final DeviceSpecificSettingsFragment fragment = new DeviceSpecificSettingsFragment();
fragment.setSettingsFileSuffix(settingsFileSuffix, supportedSettings, supportedLanguages);
fragment.setDeviceSpecificSettingsCustomizer(deviceSpecificSettingsCustomizer);
return fragment;
}
@ -895,12 +730,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
if (pref != null) {
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newVal) {
invokeLater(new Runnable() {
@Override
public void run() {
GBApplication.deviceService().onSendConfiguration(preferenceKey);
}
});
notifyPreferenceChanged(preferenceKey);
if (extraListener != null) {
return extraListener.onPreferenceChange(preference, newVal);
@ -962,7 +792,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
final Preference preference = findPreference(key);
if (preference == null) {
LOG.warn("Preference {} not found, ignoring", key);
LOG.warn("Preference {} not found", key);
return;
}
@ -976,11 +806,18 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
} else if (preference instanceof ListPreference) {
final ListPreference listPreference = (ListPreference) preference;
listPreference.setValue(prefs.getString(key, listPreference.getValue()));
} else if (preference instanceof EditTextPreference) {
final EditTextPreference editTextPreference = (EditTextPreference) preference;
editTextPreference.setText(prefs.getString(key, editTextPreference.getText()));
} else if (preference instanceof PreferenceScreen) {
// Ignoring
} else {
LOG.warn("Unknown preference class {}, ignoring", preference.getClass());
}
if (deviceSpecificSettingsCustomizer != null) {
deviceSpecificSettingsCustomizer.onPreferenceChange(preference, DeviceSpecificSettingsFragment.this);
}
}
}
}

View File

@ -39,6 +39,13 @@ public interface DeviceSpecificSettingsHandler {
*/
void addPreferenceHandlerFor(final String preferenceKey);
/**
* Notify the device that a preference changed.
*
* @param preferenceKey the preference key.
*/
void notifyPreferenceChanged(final String preferenceKey);
/**
* Adds a preference handler for a preference key. On change, this handler calls the provided extra listener, and then sends the preference to the device.
*

View File

@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -25,6 +26,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@ -65,27 +67,24 @@ public class DeviceCandidateAdapter extends ArrayAdapter<GBDeviceCandidate> {
deviceAddressLabel.setText(device.getMacAddress());
deviceImageView.setImageResource(device.getDeviceType().getIcon());
String status = "";
final List<String> statusLines = new ArrayList<>();
if (device.getDevice().getBondState() == BluetoothDevice.BOND_BONDED) {
status += getContext().getString(R.string.device_is_currently_bonded);
statusLines.add(getContext().getString(R.string.device_is_currently_bonded));
if (!GBApplication.getPrefs().getBoolean("ignore_bonded_devices", true)) { // This could be passed to the constructor instead
deviceImageView.setImageResource(device.getDeviceType().getDisabledIcon());
}
}
if (!device.getDeviceType().isSupported()) {
status += " UNSUPPORTED";
statusLines.add(getContext().getString(R.string.device_unsupported));
}
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_REQUIRE_KEY) {
if (device.getDevice().getBondState() == BluetoothDevice.BOND_BONDED) {
status += "\n";
}
status += getContext().getString(R.string.device_requires_key);
statusLines.add(getContext().getString(R.string.device_requires_key));
}
deviceStatus.setText(status);
deviceStatus.setText(TextUtils.join("\n", statusLines));
return view;
}

View File

@ -164,6 +164,8 @@ public class HuamiService {
public static final byte[] COMMAND_DISABLE_DISCONNECT_NOTIFCATION = new byte[]{ENDPOINT_DISPLAY, 0x0c, 0x00, 0x00, 0, 0, 0, 0};
public static final byte[] COMMAND_REQUEST_ALARMS = new byte[]{0x0d};
public static final byte[] COMMAND_REQUEST_ALARMS_WITH_TIMES = new byte[]{(byte) 0xff,0x01,0x00,0x00,0x00};
public static final byte[] COMMAND_REQUEST_GPS_VERSION = new byte[]{0x0e};
// The third byte controls the threshold, in minutes

View File

@ -0,0 +1,62 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpop;
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.amazfitbipu.AmazfitBipUCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class AmazfitPopCoordinator extends AmazfitBipUCoordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitPopCoordinator.class);
@Override
public DeviceType getDeviceType() {
return DeviceType.AMAZFITPOP;
}
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
try {
BluetoothDevice device = candidate.getDevice();
String name = device.getName();
if (name != null && (name.equalsIgnoreCase("Amazfit Pop"))) {
return DeviceType.AMAZFITPOP;
}
} catch (Exception ex) {
LOG.error("unable to check device support", ex);
}
return DeviceType.UNKNOWN;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
AmazfitPopFWInstallHandler handler = new AmazfitPopFWInstallHandler(uri, context);
return handler.isValid() ? handler : null;
}
}

View File

@ -0,0 +1,40 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpop;
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.amazfitpoppro.AmazfitPopProFirmwareInfo;
public class AmazfitPopFWHelper extends HuamiFWHelper {
public AmazfitPopFWHelper(Uri uri, Context context) throws IOException {
super(uri, context);
}
@Override
protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) {
firmwareInfo = new AmazfitPopProFirmwareInfo(wholeFirmwareBytes);
if (!firmwareInfo.isHeaderValid()) {
throw new IllegalArgumentException("Not an Amazfit Pop Pro firmware");
}
}
}

View File

@ -0,0 +1,49 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpop;
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 AmazfitPopFWInstallHandler extends AbstractMiBandFWInstallHandler {
AmazfitPopFWInstallHandler(Uri uri, Context context) {
super(uri, context);
}
@Override
protected String getFwUpgradeNotice() {
return mContext.getString(R.string.fw_upgrade_notice_amazfitbip, helper.getHumanFirmwareVersion());
}
@Override
protected AbstractMiBandFWHelper createHelper(Uri uri, Context context) throws IOException {
return new AmazfitPopFWHelper(uri, context);
}
@Override
protected boolean isSupportedDeviceType(GBDevice device) {
return device.getType() == DeviceType.AMAZFITPOP;
}
}

View File

@ -0,0 +1,62 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpoppro;
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.amazfitbipupro.AmazfitBipUProCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class AmazfitPopProCoordinator extends AmazfitBipUProCoordinator {
private static final Logger LOG = LoggerFactory.getLogger(AmazfitPopProCoordinator.class);
@Override
public DeviceType getDeviceType() {
return DeviceType.AMAZFITPOPPRO;
}
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
try {
BluetoothDevice device = candidate.getDevice();
String name = device.getName();
if (name != null && (name.equalsIgnoreCase("Amazfit Pop Pro"))) {
return DeviceType.AMAZFITPOPPRO;
}
} catch (Exception ex) {
LOG.error("unable to check device support", ex);
}
return DeviceType.UNKNOWN;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
AmazfitPopProFWInstallHandler handler = new AmazfitPopProFWInstallHandler(uri, context);
return handler.isValid() ? handler : null;
}
}

View File

@ -0,0 +1,40 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpoppro;
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.amazfitpoppro.AmazfitPopProFirmwareInfo;
public class AmazfitPopProFWHelper extends HuamiFWHelper {
public AmazfitPopProFWHelper(Uri uri, Context context) throws IOException {
super(uri, context);
}
@Override
protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) {
firmwareInfo = new AmazfitPopProFirmwareInfo(wholeFirmwareBytes);
if (!firmwareInfo.isHeaderValid()) {
throw new IllegalArgumentException("Not an Amazfit Pop Pro firmware");
}
}
}

View File

@ -0,0 +1,49 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpoppro;
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 AmazfitPopProFWInstallHandler extends AbstractMiBandFWInstallHandler {
AmazfitPopProFWInstallHandler(Uri uri, Context context) {
super(uri, context);
}
@Override
protected String getFwUpgradeNotice() {
return mContext.getString(R.string.fw_upgrade_notice_amazfitbip, helper.getHumanFirmwareVersion());
}
@Override
protected AbstractMiBandFWHelper createHelper(Uri uri, Context context) throws IOException {
return new AmazfitPopProFWHelper(uri, context);
}
@Override
protected boolean isSupportedDeviceType(GBDevice device) {
return device.getType() == DeviceType.AMAZFITPOPPRO;
}
}

View File

@ -394,6 +394,8 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
widgets.add(new HybridHRWatchfaceWidget(widgetName,
layoutItem.getJSONObject("pos").getInt("x"),
layoutItem.getJSONObject("pos").getInt("y"),
layoutItem.getJSONObject("size").getInt("w"),
layoutItem.getJSONObject("size").getInt("h"),
widgetColor,
widgetTimezone));
} catch (JSONException e) {
@ -408,6 +410,8 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
widgets.add(new HybridHRWatchfaceWidget(widgetName,
layoutItem.getJSONObject("pos").getInt("x"),
layoutItem.getJSONObject("pos").getInt("y"),
layoutItem.getJSONObject("size").getInt("w"),
layoutItem.getJSONObject("size").getInt("h"),
widgetColor,
widgetUpdateTimeout,
widgetTimeoutHideText,
@ -416,6 +420,8 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
widgets.add(new HybridHRWatchfaceWidget(widgetName,
layoutItem.getJSONObject("pos").getInt("x"),
layoutItem.getJSONObject("pos").getInt("y"),
layoutItem.getJSONObject("size").getInt("w"),
layoutItem.getJSONObject("size").getInt("h"),
widgetColor));
}
}
@ -603,6 +609,13 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
posY.setText("120");
}
});
// Set widget size
final LinearLayout sizeLayout = layout.findViewById(R.id.watchface_widget_size_layout);
sizeLayout.setVisibility(View.GONE);
final EditText widgetWidth = layout.findViewById(R.id.watchface_widget_width);
if ((widget != null) && (widget.getWidth() >= 0)) {
widgetWidth.setText(Integer.toString(widget.getWidth()));
}
// Populate timezone spinner
String[] timezonesList = TimeZone.getAvailableIDs();
final Spinner tzSpinner = layout.findViewById(R.id.watchface_widget_timezone_spinner);
@ -641,8 +654,10 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
timezoneLayout.setVisibility(View.GONE);
}
if (selectedType.equals("widgetCustom")) {
sizeLayout.setVisibility(View.VISIBLE);
updateTimeoutLayout.setVisibility(View.VISIBLE);
} else {
sizeLayout.setVisibility(View.GONE);
updateTimeoutLayout.setVisibility(View.GONE);
}
}
@ -678,6 +693,12 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
if (selectedPosX > 240) selectedPosX = 240;
if (selectedPosY < 1) selectedPosY = 1;
if (selectedPosY > 240) selectedPosY = 240;
int selectedWidth = 76;
try {
selectedWidth = Integer.parseInt(widgetWidth.getText().toString());
} catch (NumberFormatException e) {
LOG.warn("Error parsing input", e);
}
String selectedType = widgetTypesArray.get(typeSpinner.getSelectedItemPosition());
String selectedTZ = tzSpinner.getSelectedItem().toString();
int selectedUpdateTimeout = 0;
@ -694,11 +715,11 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem
boolean selectedTimeoutShowCircle = timeoutShowCircle.isChecked();
HybridHRWatchfaceWidget widgetConfig;
if (selectedType.equals("widget2ndTZ")) {
widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, colorSpinner.getSelectedItemPosition(), selectedTZ);
widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, 76, 76, colorSpinner.getSelectedItemPosition(), selectedTZ);
} else if (selectedType.equals("widgetCustom")) {
widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, colorSpinner.getSelectedItemPosition(), selectedUpdateTimeout, selectedTimeoutHideText, selectedTimeoutShowCircle);
widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, selectedWidth, 76, colorSpinner.getSelectedItemPosition(), selectedUpdateTimeout, selectedTimeoutHideText, selectedTimeoutShowCircle);
} else {
widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, colorSpinner.getSelectedItemPosition());
widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, 76, 76, colorSpinner.getSelectedItemPosition());
}
if (index >= 0) {
widgets.set(index, widgetConfig);

View File

@ -105,8 +105,8 @@ public class HybridHRWatchfaceFactory {
return;
}
JSONObject size = new JSONObject();
size.put("w", 76);
size.put("h", 76);
size.put("w", widgetDesc.getWidth());
size.put("h", widgetDesc.getHeight());
widget.put("size", size);
JSONObject pos = new JSONObject();
pos.put("x", widgetDesc.getPosX());
@ -336,8 +336,8 @@ public class HybridHRWatchfaceFactory {
complicationContent.put("inversion", "#$e");
dimension = new JSONObject();
dimension.put("type", "rigid");
dimension.put("width", 76);
dimension.put("height", 76);
dimension.put("width", "#size.w");
dimension.put("height", "#size.h");
complicationContent.put("dimension", dimension);
placement = new JSONObject();
placement.put("type", "relative");

View File

@ -31,7 +31,9 @@ public class HybridHRWatchfaceWidget {
private String widgetType;
private int posX;
private int posY;
private int color = 0;
private int width;
private int height;
private int color;
private String timezone;
private int updateTimeout = -1;
private boolean timeoutHideText = true;
@ -40,18 +42,20 @@ public class HybridHRWatchfaceWidget {
public static int COLOR_WHITE = 0;
public static int COLOR_BLACK = 1;
public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int color) {
public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int width, int height, int color) {
this.widgetType = widgetType;
this.posX = posX;
this.posY = posY;
this.width = width;
this.height = height;
this.color = color;
}
public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int color, String timezone) {
this(widgetType, posX, posY, color);
public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int width, int height, int color, String timezone) {
this(widgetType, posX, posY, width, height, color);
this.timezone = timezone;
}
public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int color, int updateTimeout, boolean timeoutHideText, boolean timeoutShowCircle) {
this(widgetType, posX, posY, color);
public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int width, int height, int color, int updateTimeout, boolean timeoutHideText, boolean timeoutShowCircle) {
this(widgetType, posX, posY, width, height, color);
this.updateTimeout = updateTimeout;
this.timeoutHideText = timeoutHideText;
this.timeoutShowCircle = timeoutShowCircle;
@ -100,6 +104,20 @@ public class HybridHRWatchfaceWidget {
this.posY = posY;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getColor() {
return color;
}

View File

@ -41,7 +41,7 @@ public abstract class SonyHeadphonesCoordinator extends AbstractDeviceCoordinato
@Override
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
return new SonyHeadphonesSettingsCustomizer();
return new SonyHeadphonesSettingsCustomizer(device);
}
@Override

View File

@ -18,18 +18,101 @@ package nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_CODEC;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_1000;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_16000;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_2500;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_400;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_6300;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BASS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_FOCUS_VOICE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_CANCEL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_START;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATUS;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SURROUND_MODE;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Parcel;
import androidx.preference.EditTextPreference;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus;
public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
private ProgressDialog ancOptimizerProgressDialog;
final GBDevice device;
public SonyHeadphonesSettingsCustomizer(final GBDevice device) {
this.device = device;
}
@Override
public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
// Disable equalizer, sound position and surround mode if not in SBC codec, for WH-1000XM3
// TODO: Should the coordinator be responsible for this compatibility check?
if (preference.getKey().equals(PREF_SONY_AUDIO_CODEC) && device.getType().equals(DeviceType.SONY_WH_1000XM3)) {
final boolean isSbcCodec = ((EditTextPreference) preference).getText().equalsIgnoreCase("sbc");
final List<Preference> prefsToDisable = Arrays.asList(
handler.findPreference(PREF_SONY_EQUALIZER),
handler.findPreference(PREF_SONY_EQUALIZER_MODE),
handler.findPreference(PREF_SONY_EQUALIZER_BAND_400),
handler.findPreference(PREF_SONY_EQUALIZER_BAND_1000),
handler.findPreference(PREF_SONY_EQUALIZER_BAND_2500),
handler.findPreference(PREF_SONY_EQUALIZER_BAND_6300),
handler.findPreference(PREF_SONY_EQUALIZER_BAND_16000),
handler.findPreference(PREF_SONY_EQUALIZER_BASS),
handler.findPreference(PREF_SONY_SOUND_POSITION),
handler.findPreference(PREF_SONY_SURROUND_MODE)
);
for (Preference pref : prefsToDisable) {
if (pref != null) {
pref.setEnabled(isSbcCodec);
}
}
}
// Handle ANC Optimizer status
if (preference.getKey().equals(PREF_SONY_NOISE_OPTIMIZER_STATUS)) {
final EditTextPreference optimizerStatusPreference = (EditTextPreference) preference;
final NoiseCancellingOptimizerStatus optimizerStatus = NoiseCancellingOptimizerStatus.valueOf(optimizerStatusPreference.getText().toUpperCase(Locale.ROOT));
if (ancOptimizerProgressDialog != null) {
switch (optimizerStatus) {
case FINISHED:
case NOT_RUNNING:
ancOptimizerProgressDialog.dismiss();
ancOptimizerProgressDialog = null;
break;
default:
ancOptimizerProgressDialog.setMessage(optimizerStatus.i18n(preference.getContext()));
}
}
}
}
@Override
public void customizeSettings(final DeviceSpecificSettingsHandler handler) {
// Only enable the focus on voice check and voice level slider if the ambient sound control mode is ambient sound
@ -52,5 +135,76 @@ public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC
ambientSoundControlPrefListener.onPreferenceChange(ambientSoundControl, ambientSoundControl.getValue());
handler.addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_CONTROL, ambientSoundControlPrefListener);
}
// ANC Optimizer
final Preference ancOptimizer = handler.findPreference("pref_sony_anc_optimizer");
if (ancOptimizer != null) {
ancOptimizer.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
if (ancOptimizerProgressDialog != null) {
// Already optimizing
return true;
}
final Context context = preference.getContext();
new AlertDialog.Builder(context)
.setTitle(R.string.sony_anc_optimize_confirmation_title)
.setMessage(R.string.sony_anc_optimize_confirmation_description)
.setIcon(R.drawable.ic_hearing)
.setPositiveButton(R.string.start, new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int whichButton) {
handler.notifyPreferenceChanged(PREF_SONY_NOISE_OPTIMIZER_START);
ancOptimizerProgressDialog = new ProgressDialog(context);
ancOptimizerProgressDialog.setCancelable(false);
ancOptimizerProgressDialog.setMessage(context.getString(R.string.sony_anc_optimizer_status_starting));
ancOptimizerProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
ancOptimizerProgressDialog.setProgress(0);
ancOptimizerProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.Cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
dialog.dismiss();
ancOptimizerProgressDialog = null;
handler.notifyPreferenceChanged(PREF_SONY_NOISE_OPTIMIZER_CANCEL);
}
});
ancOptimizerProgressDialog.show();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
return true;
}
});
}
}
public static final Creator<SonyHeadphonesSettingsCustomizer> CREATOR = new Creator<SonyHeadphonesSettingsCustomizer>() {
@Override
public SonyHeadphonesSettingsCustomizer createFromParcel(final Parcel in) {
final GBDevice device = in.readParcelable(SonyHeadphonesSettingsCustomizer.class.getClassLoader());
return new SonyHeadphonesSettingsCustomizer(device);
}
@Override
public SonyHeadphonesSettingsCustomizer[] newArray(final int size) {
return new SonyHeadphonesSettingsCustomizer[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeParcelable(device, 0);
}
}

View File

@ -69,8 +69,9 @@ public class SonyWFSP800NCoordinator extends SonyHeadphonesCoordinator {
R.xml.devicesettings_header_system,
R.xml.devicesettings_sony_headphones_button_modes_left_right,
R.xml.devicesettings_sony_headphones_pause_when_taken_off,
R.xml.devicesettings_sony_wf_sp800n,
R.xml.devicesettings_sony_headphones_notifications_voice_guide
R.xml.devicesettings_automatic_power_off_when_taken_off,
R.xml.devicesettings_sony_headphones_notifications_voice_guide,
R.xml.devicesettings_sony_headphones_device_info
};
}
}

View File

@ -27,7 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class SonyWH1000XM3Coordinator extends SonyHeadphonesCoordinator {
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
public DeviceType getSupportedType(final GBDeviceCandidate candidate) {
if (candidate.getName().contains("WH-1000XM3")) {
return DeviceType.SONY_WH_1000XM3;
}
@ -43,17 +43,19 @@ public class SonyWH1000XM3Coordinator extends SonyHeadphonesCoordinator {
@Override
public int[] getSupportedDeviceSpecificSettings(final GBDevice device) {
return new int[]{
R.xml.devicesettings_sony_warning_wh1000xm3,
R.xml.devicesettings_sony_headphones_ambient_sound_control_wind_noise_reduction,
R.xml.devicesettings_sony_headphones_anc_optimizer,
R.xml.devicesettings_header_other,
R.xml.devicesettings_sony_warning_wh1000xm3,
R.xml.devicesettings_sony_headphones_equalizer,
R.xml.devicesettings_sony_headphones_sound_position,
R.xml.devicesettings_sony_headphones_surround_mode,
R.xml.devicesettings_sony_headphones_audio_upsampling,
R.xml.devicesettings_header_system,
R.xml.devicesettings_sony_headphones_touch_sensor_single,
R.xml.devicesettings_automatic_power_off,
R.xml.devicesettings_sony_headphones_notifications_voice_guide
R.xml.devicesettings_automatic_power_off_by_time,
R.xml.devicesettings_sony_headphones_notifications_voice_guide,
R.xml.devicesettings_sony_headphones_device_info
};
}
}

View File

@ -0,0 +1,62 @@
/* Copyright (C) 2021 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.sony.headphones.coordinators;
import androidx.annotation.NonNull;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class SonyWH1000XM4Coordinator extends SonyHeadphonesCoordinator {
@NonNull
@Override
public DeviceType getSupportedType(final GBDeviceCandidate candidate) {
if (candidate.getName().contains("WH-1000XM4")) {
return DeviceType.SONY_WH_1000XM4;
}
return DeviceType.UNKNOWN;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.SONY_WH_1000XM4;
}
@Override
public int[] getSupportedDeviceSpecificSettings(final GBDevice device) {
return new int[]{
// TODO: Function of [CUSTOM] button
R.xml.devicesettings_sony_headphones_ambient_sound_control_wind_noise_reduction,
R.xml.devicesettings_sony_headphones_anc_optimizer,
R.xml.devicesettings_header_other,
R.xml.devicesettings_sony_headphones_equalizer,
R.xml.devicesettings_sony_headphones_audio_upsampling,
R.xml.devicesettings_header_system,
// TODO R.xml.devicesettings_connect_two_devices,
// TODO R.xml.devicesettings_sony_headphones_speak_to_chat_with_settings,
R.xml.devicesettings_sony_headphones_touch_sensor_single,
R.xml.devicesettings_sony_headphones_pause_when_taken_off,
R.xml.devicesettings_automatic_power_off_when_taken_off,
R.xml.devicesettings_sony_headphones_notifications_voice_guide,
R.xml.devicesettings_sony_headphones_device_info
};
}
}

View File

@ -54,7 +54,7 @@ public enum DeviceType {
AMAZFITGTS2(27, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_gts2),
AMAZFITBIPU(28, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_bipu),
AMAZFITVERGEL(29, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_vergel),
AMAZFITBIPUPRO(30, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_bipu),
AMAZFITBIPUPRO(30, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_bipupro),
AMAZFITNEO(31, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_neo),
AMAZFITGTS2_MINI(32, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_gts2_mini),
ZEPP_E(33, R.drawable.ic_device_zetime, R.drawable.ic_device_zetime_disabled, R.string.devicetype_zepp_e),
@ -63,6 +63,8 @@ public enum DeviceType {
AMAZFITX(36, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_amazfit_x),
MIBAND6(37, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_miband6),
AMAZFITTREXPRO(38, R.drawable.ic_device_zetime, R.drawable.ic_device_zetime_disabled, R.string.devicetype_amazfit_trex_pro),
AMAZFITPOP(39, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_pop),
AMAZFITPOPPRO(10040, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_pop_pro),
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),
@ -105,6 +107,7 @@ public enum DeviceType {
GALAXY_BUDS(420, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_galaxybuds),
SONY_WH_1000XM3(430, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_sony_wh_1000xm3),
SONY_WF_SP800N(431, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_wf_sp800n),
SONY_WH_1000XM4(432, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_sony_wh_1000xm4),
BOSE_QC35(440, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_bose_qc35),
VESC_NRF(500, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc),
VESC_HM10(501, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc),

View File

@ -59,6 +59,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.Am
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.AmazfitGTS2Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.AmazfitGTS2eSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitneo.AmazfitNeoSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitpop.AmazfitPopSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitpoppro.AmazfitPopProSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfittrex.AmazfitTRexSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfittrexpro.AmazfitTRexProSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitvergel.AmazfitVergeLSupport;
@ -204,6 +206,12 @@ public class DeviceSupportFactory {
case AMAZFITBIPUPRO:
deviceSupport = new ServiceDeviceSupport(new AmazfitBipUProSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITPOP:
deviceSupport = new ServiceDeviceSupport(new AmazfitPopSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITPOPPRO:
deviceSupport = new ServiceDeviceSupport(new AmazfitPopProSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case AMAZFITGTR:
deviceSupport = new ServiceDeviceSupport(new AmazfitGTRSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
@ -375,6 +383,9 @@ public class DeviceSupportFactory {
case SONY_WH_1000XM3:
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SONY_WH_1000XM4:
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case SONY_WF_SP800N:
deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;

View File

@ -424,6 +424,7 @@ public class CasioGBX100DeviceSupport extends CasioSupport implements SharedPref
public void writeCurrentTime(TransactionBuilder builder) {
byte[] arr = new byte[11];
arr[0] = CasioConstants.characteristicToByte.get("CASIO_CURRENT_TIME");
byte[] tmp = prepareCurrentTime();
System.arraycopy(tmp, 0, arr, 1, 10);

View File

@ -31,5 +31,6 @@ public class HuamiDeviceEvent {
public static final byte TICK_30MIN = 0x0e; // unsure
public static final byte FIND_PHONE_STOP = 0x0f;
public static final byte MTU_REQUEST = 0x16;
public static final byte ALARM_CHANGED = 0x1a;
public static final byte MUSIC_CONTROL = (byte) 0xfe;
}

View File

@ -1473,7 +1473,8 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
processDeviceEvent(HuamiDeviceEvent.START_NONWEAR);
break;
case HuamiDeviceEvent.ALARM_TOGGLED:
LOG.info("An alarm was toggled");
case HuamiDeviceEvent.ALARM_CHANGED:
LOG.info("An alarm was toggled or changed");
TransactionBuilder builder = new TransactionBuilder("requestAlarms");
requestAlarms(builder);
builder.queue(getQueue());
@ -1810,40 +1811,78 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
gbDevice.setFirmwareVersion2(gpsVersion);
} else if (value[1] == 0x0d) {
LOG.info("got alarms from watch");
decodeAndUpdateAlarmStatus(value);
decodeAndUpdateAlarmStatus(value, false);
} else {
LOG.warn("got configuration info we do not handle yet " + GB.hexdump(value, 3, -1));
}
} else if (value[0] == ((byte) 0x80) && value[1] == 0x01 && value[2] == (byte) 0xc0 && value[3] == 0x00 &&
value[4] == 0x01 && value[5] == 0x00 && value[6] == 0x00 && value[7] == 0x00 && value[8] == 0x01) {
LOG.info("got full alarm configuration."); // TODO: with low mtu they come in chunks
byte[] alarmData = new byte[value.length - 9];
System.arraycopy(value, 9, alarmData, 0, alarmData.length);
decodeAndUpdateAlarmStatus(alarmData, true);
} else {
LOG.warn("error received from configuration request " + GB.hexdump(value, 0, -1));
LOG.warn("unknown response got from configuration request " + GB.hexdump(value, 0, -1));
}
}
private void decodeAndUpdateAlarmStatus(byte[] response) {
private void decodeAndUpdateAlarmStatus(byte[] response, boolean withTimes) {
List<nodomain.freeyourgadget.gadgetbridge.entities.Alarm> alarms = DBHelper.getAlarms(gbDevice);
int maxAlarms = 10;
//FIXME: we can rather have a full struct here probably
boolean[] alarmsInUse = new boolean[maxAlarms];
boolean[] alarmsEnabled = new boolean[maxAlarms];
int nr_alarms = response[8];
byte[] alarmsMinute = new byte[maxAlarms];
byte[] alarmsHour = new byte[maxAlarms];
byte[] alarmsRepetition = new byte[maxAlarms];
int nr_alarms;
byte enable_flag;
if (withTimes) {
nr_alarms = response.length / 4;
enable_flag = (byte) 0x80;
} else {
nr_alarms = response[8];
enable_flag = (byte) 0x10;
}
for (int i = 0; i < nr_alarms; i++) {
byte alarm_data = response[9 + i];
int offset;
if (withTimes) {
offset = i * 4;
} else {
offset = 9 + i;
}
byte alarm_data = response[offset];
int index = alarm_data & 0xf;
if (index >= maxAlarms) {
GB.toast("Unexpected alarm index from device, ignoring: " + index, Toast.LENGTH_SHORT, GB.ERROR);
return;
}
alarmsInUse[index] = true;
boolean enabled = (alarm_data & 0x10) == 0x10;
boolean enabled = (alarm_data & enable_flag) == enable_flag;
alarmsEnabled[index] = enabled;
if (withTimes) {
alarmsHour[index] = response[offset + 1];
alarmsMinute[index] = response[offset + 2];
alarmsRepetition[index] = response[offset + 3];
}
LOG.info("alarm " + index + " is enabled:" + enabled);
}
for (nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm : alarms) {
boolean enabled = alarmsEnabled[alarm.getPosition()];
boolean unused = !alarmsInUse[alarm.getPosition()];
if (alarm.getEnabled() != enabled || alarm.getUnused() != unused) {
LOG.info("updating alarm index " + alarm.getPosition() + " unused=" + unused + ", enabled=" + enabled);
int pos = alarm.getPosition();
boolean enabled = alarmsEnabled[pos];
boolean unused = !alarmsInUse[pos];
if (alarm.getEnabled() != enabled || alarm.getUnused() != unused || (withTimes && !unused && (alarm.getHour() != alarmsHour[pos] || alarm.getMinute() != alarmsMinute[pos] || alarm.getRepetition() != alarmsRepetition[pos]))) {
LOG.info("updating alarm index " + pos + " unused=" + unused + ", enabled=" + enabled);
alarm.setEnabled(enabled);
alarm.setUnused(unused);
if (withTimes && !unused) {
alarm.setHour(alarmsHour[pos]);
alarm.setMinute(alarmsMinute[pos]);
alarm.setRepetition(alarmsRepetition[pos]);
}
DBHelper.store(alarm);
Intent intent = new Intent(DeviceService.ACTION_SAVE_ALARMS);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
@ -3140,7 +3179,9 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
private HuamiSupport requestAlarms(TransactionBuilder builder) {
LOG.info("Requesting alarms");
writeToConfiguration(builder, HuamiService.COMMAND_REQUEST_ALARMS);
//FIXME: on older devices only the first one works, and on newer only the last is sufficiant
writeToConfiguration(builder, HuamiService.COMMAND_REQUEST_ALARMS);
writeToConfiguration(builder, HuamiService.COMMAND_REQUEST_ALARMS_WITH_TIMES);
return this;
}

View File

@ -0,0 +1,81 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpop;
import java.util.HashMap;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband4.MiBand4FirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
public class AmazfitPopFirmwareInfo extends HuamiFirmwareInfo {
private static Map<Integer, String> crcToVersion = new HashMap<>();
static {
// fw
// resources
// font
}
public AmazfitPopFirmwareInfo(byte[] bytes) {
super(bytes);
}
@Override
protected HuamiFirmwareType determineFirmwareType(byte[] bytes) {
if (ArrayUtils.equals(bytes, MiBand4FirmwareInfo.FW_HEADER, MiBand4FirmwareInfo.FW_HEADER_OFFSET)) {
if (searchString32BitAligned(bytes, "\0\0Amazfit Pop")) {
return HuamiFirmwareType.FIRMWARE;
}
return HuamiFirmwareType.INVALID;
}
if (ArrayUtils.startsWith(bytes,NEWRES_HEADER)) {
return HuamiFirmwareType.RES;
}
if (ArrayUtils.startsWith(bytes, UIHH_HEADER) && (bytes[4] == 1 || bytes[4] == 2)) {
return HuamiFirmwareType.WATCHFACE;
}
if (ArrayUtils.startsWith(bytes, NEWFT_HEADER)) {
if (bytes[10] == 0x01 || bytes[10] == 0x06 || bytes[10] == 0x03) {
return HuamiFirmwareType.FONT;
}
}
return HuamiFirmwareType.INVALID;
}
@Override
public boolean isGenerallyCompatibleWith(GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITPOP;
}
@Override
protected Map<Integer, String> getCrcMap() {
return crcToVersion;
}
}

View File

@ -0,0 +1,33 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpop;
import android.content.Context;
import android.net.Uri;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpop.AmazfitPopFWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbipu.AmazfitBipUSupport;
public class AmazfitPopSupport extends AmazfitBipUSupport {
@Override
public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException {
return new AmazfitPopFWHelper(uri, context);
}
}

View File

@ -0,0 +1,81 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpoppro;
import java.util.HashMap;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband4.MiBand4FirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
public class AmazfitPopProFirmwareInfo extends HuamiFirmwareInfo {
private static Map<Integer, String> crcToVersion = new HashMap<>();
static {
// fw
// resources
// font
}
public AmazfitPopProFirmwareInfo(byte[] bytes) {
super(bytes);
}
@Override
protected HuamiFirmwareType determineFirmwareType(byte[] bytes) {
if (ArrayUtils.equals(bytes, MiBand4FirmwareInfo.FW_HEADER, MiBand4FirmwareInfo.FW_HEADER_OFFSET)) {
if (searchString32BitAligned(bytes, "\0\0Amazfit Pop Pro")) {
return HuamiFirmwareType.FIRMWARE;
}
return HuamiFirmwareType.INVALID;
}
if (ArrayUtils.startsWith(bytes,NEWRES_HEADER)) {
return HuamiFirmwareType.RES;
}
if (ArrayUtils.startsWith(bytes, UIHH_HEADER) && (bytes[4] == 1 || bytes[4] == 2)) {
return HuamiFirmwareType.WATCHFACE;
}
if (ArrayUtils.startsWith(bytes, NEWFT_HEADER)) {
if (bytes[10] == 0x01 || bytes[10] == 0x06 || bytes[10] == 0x03) {
return HuamiFirmwareType.FONT;
}
}
return HuamiFirmwareType.INVALID;
}
@Override
public boolean isGenerallyCompatibleWith(GBDevice device) {
return isHeaderValid() && device.getType() == DeviceType.AMAZFITPOPPRO;
}
@Override
protected Map<Integer, String> getCrcMap() {
return crcToVersion;
}
}

View File

@ -0,0 +1,33 @@
/* Copyright (C) 2022 Andreas Shimokawa
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.amazfitpoppro;
import android.content.Context;
import android.net.Uri;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpoppro.AmazfitPopProFWHelper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbipupro.AmazfitBipUProSupport;
public class AmazfitPopProSupport extends AmazfitBipUProSupport {
@Override
public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException {
return new AmazfitPopProFWHelper(uri, context);
}
}

View File

@ -41,8 +41,7 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread {
private final SonyHeadphonesProtocol mProtocol;
// Track whether we got the first reply
private boolean firstReply = false;
// Track whether we got the first init reply
private final Handler handler = new Handler();
private int initRetries = 0;
@ -53,7 +52,7 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread {
private final Runnable initSendRunnable = new Runnable() {
public void run() {
// If we still haven't got any reply, re-send the init
if (!firstReply) {
if (!mProtocol.hasProtocolImplementation()) {
if (initRetries++ < 2) {
LOG.warn("Init retry {}", initRetries);
@ -103,8 +102,6 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread {
msgStream.write(incoming);
} while (incoming[0] != Message.MESSAGE_TRAILER);
firstReply = true;
LOG.trace("Raw message: {}", GB.hexdump(msgStream.toByteArray()));
return msgStream.toByteArray();

View File

@ -104,9 +104,10 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
} else if (message.getPayload().length == 6) {
LOG.warn("Sony Headphones protocol v2 is not yet supported");
return null;
} else {
LOG.error("Unexpected init response payload length: {}", message.getPayload().length);
return null;
}
return null;
}
}
@ -151,6 +152,12 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
case DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL:
configRequest = protocolImpl.setAmbientSoundControl(AmbientSoundControl.fromPreferences(prefs));
break;
case DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_START:
configRequest = protocolImpl.startNoiseCancellingOptimizer(true);
break;
case DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_CANCEL:
configRequest = protocolImpl.startNoiseCancellingOptimizer(false);
break;
case DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION:
configRequest = protocolImpl.setSoundPosition(SoundPosition.fromPreferences(prefs));
break;
@ -187,7 +194,15 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
case DeviceSettingsPreferenceConst.PREF_SONY_NOTIFICATION_VOICE_GUIDE:
configRequest = protocolImpl.setVoiceNotifications(VoiceNotifications.fromPreferences(prefs));
break;
case DeviceSettingsPreferenceConst.PREF_SONY_CONNECT_TWO_DEVICES:
LOG.warn("Connection to two devices not implemented ('{}')", config);
return super.encodeSendConfiguration(config);
case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT:
case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_SENSITIVITY:
case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_FOCUS_ON_VOICE:
case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_TIMEOUT:
LOG.warn("Speak-to-chat is not implemented ('{}')", config);
return super.encodeSendConfiguration(config);
default:
LOG.warn("Unknown config '{}'", config);
return super.encodeSendConfiguration(config);
@ -202,10 +217,6 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
public byte[] encodeTestNewFunction() {
//return Request.fromHex(MessageType.COMMAND_1, "c40100").encode(sequenceNumber);
if (protocolImpl != null) {
return protocolImpl.startNoiseCancellingOptimizer().encode(sequenceNumber);
}
return null;
}
@ -252,6 +263,10 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
return requestQueue.remove().encode(sequenceNumber);
}
public boolean hasProtocolImplementation() {
return protocolImpl != null;
}
private GBDeviceEvent handleAck() {
pendingAcks--;

View File

@ -50,6 +50,8 @@ public abstract class AbstractSonyProtocolImpl {
public abstract Request setAmbientSoundControl(final AmbientSoundControl config);
public abstract Request getNoiseCancellingOptimizerState();
public abstract Request getAudioCodec();
public abstract Request getBattery(final BatteryType batteryType);
@ -94,7 +96,7 @@ public abstract class AbstractSonyProtocolImpl {
public abstract Request setVoiceNotifications(final VoiceNotifications config);
public abstract Request startNoiseCancellingOptimizer();
public abstract Request startNoiseCancellingOptimizer(final boolean start);
public abstract Request powerOff();

View File

@ -53,7 +53,12 @@ public enum PayloadType {
AMBIENT_SOUND_CONTROL_SET(MessageType.COMMAND_1, 0x68),
AMBIENT_SOUND_CONTROL_NOTIFY(MessageType.COMMAND_1, 0x69),
NOISE_CANCELLING_OPTIMIZER_START_REQUEST(MessageType.COMMAND_1, 0x84),
NOISE_CANCELLING_OPTIMIZER_START(MessageType.COMMAND_1, 0x84),
NOISE_CANCELLING_OPTIMIZER_STATUS(MessageType.COMMAND_1, 0x85),
NOISE_CANCELLING_OPTIMIZER_STATE_GET(MessageType.COMMAND_1, 0x86),
NOISE_CANCELLING_OPTIMIZER_STATE_RET(MessageType.COMMAND_1, 0x87),
NOISE_CANCELLING_OPTIMIZER_STATE_NOTIFY(MessageType.COMMAND_1, 0x89),
TOUCH_SENSOR_GET(MessageType.COMMAND_1, 0xd6),
TOUCH_SENSOR_RET(MessageType.COMMAND_1, 0xd7),

View File

@ -16,6 +16,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATUS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -53,6 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.prot
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.AbstractSonyProtocolImpl;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.AudioCodec;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.VirtualSoundParam;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -130,6 +134,17 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return new Request(PayloadType.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array());
}
@Override
public Request getNoiseCancellingOptimizerState() {
return new Request(
PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getMessageType(),
new byte[]{
PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getCode(),
(byte) 0x01
}
);
}
@Override
public Request getAudioCodec() {
return new Request(
@ -399,14 +414,14 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
}
@Override
public Request startNoiseCancellingOptimizer() {
public Request startNoiseCancellingOptimizer(final boolean start) {
return new Request(
PayloadType.NOISE_CANCELLING_OPTIMIZER_START_REQUEST.getMessageType(),
PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getMessageType(),
new byte[]{
PayloadType.NOISE_CANCELLING_OPTIMIZER_START_REQUEST.getCode(),
PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getCode(),
(byte) 0x01,
(byte) 0x00,
(byte) 0x01
(byte) (start ? 0x01 : 0x00)
}
);
}
@ -447,6 +462,11 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
case AMBIENT_SOUND_CONTROL_RET:
case AMBIENT_SOUND_CONTROL_NOTIFY:
return handleAmbientSoundControl(payload);
case NOISE_CANCELLING_OPTIMIZER_STATUS:
return handleNoiseCancellingOptimizerStatus(payload);
case NOISE_CANCELLING_OPTIMIZER_STATE_RET:
case NOISE_CANCELLING_OPTIMIZER_STATE_NOTIFY:
return handleNoiseCancellingOptimizerState(payload);
case TOUCH_SENSOR_RET:
case TOUCH_SENSOR_NOTIFY:
return handleTouchSensor(payload);
@ -486,6 +506,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
capabilityRequests.add(getBattery(BatteryType.SINGLE));
capabilityRequests.add(getAudioCodec());
capabilityRequests.add(getAmbientSoundControl());
capabilityRequests.add(getNoiseCancellingOptimizerState());
capabilityRequests.add(getAudioUpsampling());
capabilityRequests.add(getVoiceNotifications());
capabilityRequests.add(getAutomaticPowerOff());
@ -494,6 +515,19 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
capabilityRequests.add(getSoundPosition());
capabilityRequests.add(getEqualizer());
break;
case SONY_WH_1000XM4:
capabilityRequests.add(getFirmwareVersion());
capabilityRequests.add(getBattery(BatteryType.SINGLE));
capabilityRequests.add(getAudioCodec());
capabilityRequests.add(getAmbientSoundControl());
capabilityRequests.add(getNoiseCancellingOptimizerState());
capabilityRequests.add(getAudioUpsampling());
capabilityRequests.add(getVoiceNotifications());
capabilityRequests.add(getAutomaticPowerOff());
capabilityRequests.add(getTouchSensor());
capabilityRequests.add(getEqualizer());
capabilityRequests.add(getPauseWhenTakenOff());
break;
case SONY_WF_SP800N:
capabilityRequests.add(getFirmwareVersion());
capabilityRequests.add(getBattery(BatteryType.DUAL));
@ -580,6 +614,50 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.singletonList(eventUpdatePreferences);
}
public List<? extends GBDeviceEvent> handleNoiseCancellingOptimizerStatus(final byte[] payload) {
if (payload.length != 4) {
LOG.warn("Unexpected payload length {}", payload.length);
return Collections.emptyList();
}
final NoiseCancellingOptimizerStatus status = NoiseCancellingOptimizerStatus.fromCode(payload[3]);
if (status == null) {
LOG.warn("Unable to determine noise cancelling opptimizer status from {}", GB.hexdump(payload));
return Collections.emptyList();
}
LOG.info("Noise Cancelling Optimizer status: {}", status);
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
.withPreference(PREF_SONY_NOISE_OPTIMIZER_STATUS, status.name().toLowerCase(Locale.ROOT));
return Collections.singletonList(event);
}
public List<? extends GBDeviceEvent> handleNoiseCancellingOptimizerState(final byte[] payload) {
// 89 01 01 01 01 0A
if (payload.length != 6) {
LOG.warn("Unexpected payload length {}", payload.length);
return Collections.emptyList();
}
final float pressure = payload[5] / 10.0f;
if (pressure <= 0 || pressure > 1.0f) {
LOG.warn("Invalid Noise Cancelling Optimizer pressure: {} atm, ignoring", pressure);
return Collections.emptyList();
}
LOG.info("Noise Cancelling Optimizer pressure: {} atm", pressure);
final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
.withPreference(PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE, String.format(Locale.getDefault(), "%.2f atm", pressure));
return Collections.singletonList(event);
}
public List<? extends GBDeviceEvent> handleAudioUpsampling(final byte[] payload) {
if (payload.length != 4) {
LOG.warn("Unexpected payload length {}", payload.length);
@ -1004,6 +1082,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
switch (deviceType) {
case SONY_WH_1000XM3:
case SONY_WH_1000XM4:
return true;
case SONY_WF_SP800N:
return false;

View File

@ -17,6 +17,7 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params;
public enum AudioCodec {
UNKNOWN(0x00),
SBC(0x01),
AAC(0x02),
LDAC(0x10),

View File

@ -0,0 +1,56 @@
/* Copyright (C) 2021 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.sony.headphones.protocol.impl.v1.params;
import android.content.Context;
import java.util.Locale;
public enum NoiseCancellingOptimizerStatus {
NOT_RUNNING(0x00),
WEARING_CONDITION(0x01),
ATMOSPHERIC_PRESSURE(0x02),
ANALYZING(0x10),
FINISHED(0x11),
;
private final byte code;
NoiseCancellingOptimizerStatus(final int code) {
this.code = (byte) code;
}
public byte getCode() {
return this.code;
}
public static NoiseCancellingOptimizerStatus fromCode(final byte code) {
for (final NoiseCancellingOptimizerStatus audioCodec : values()) {
if (audioCodec.code == code) {
return audioCodec;
}
}
return null;
}
public String i18n(final Context context) {
final String stringName = String.format("sony_anc_optimizer_status_%s", name().toLowerCase(Locale.ROOT));
final int stringId = context.getResources().getIdentifier(stringName, "string", context.getPackageName());
return context.getString(stringId);
}
}

View File

@ -71,6 +71,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor2.AmazfitCor
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr.AmazfitGTRCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr.AmazfitGTRLiteCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr2.AmazfitGTR2Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpop.AmazfitPopCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpoppro.AmazfitPopProCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfittrexpro.AmazfitTRexProCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitx.AmazfitXCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband6.MiBand6Coordinator;
@ -108,6 +110,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss.SMAQ2OSSCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyWH1000XM4Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordinator;
@ -269,6 +272,8 @@ public class DeviceHelper {
result.add(new AmazfitBipSLiteCoordinator());
result.add(new AmazfitBipUCoordinator());
result.add(new AmazfitBipUProCoordinator());
result.add(new AmazfitPopCoordinator());
result.add(new AmazfitPopProCoordinator());
result.add(new AmazfitBand5Coordinator());
result.add(new AmazfitNeoCoordinator());
result.add(new MiBand3Coordinator());
@ -319,6 +324,7 @@ public class DeviceHelper {
result.add(new GalaxyBudsLiveDeviceCoordinator());
result.add(new VescCoordinator());
result.add(new SonyWH1000XM3Coordinator());
result.add(new SonyWH1000XM4Coordinator());
result.add(new SonyWFSP800NCoordinator());
result.add(new QC35Coordinator());

View File

@ -115,6 +115,9 @@ public class LanguageUtils {
put('ř',"r"); put('ě',"e"); put('ý',"y"); put('á',"a"); put('í',"i"); put('é',"e");
put('ó',"o"); put('ú',"u"); put('ů',"u"); put('ď',"d"); put('ť',"t"); put('ň',"n");
// Turkish
put('ı',"i");
//TODO: these must be configurable. If someone wants to transliterate cyrillic it does not mean his device has no German umlauts
// all or nothing is really bad here
}

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="M19,9l1.25,-2.75L23,5l-2.75,-1.25L19,1l-1.25,2.75L15,5l2.75,1.25L19,9zM11.5,9.5L9,4 6.5,9.5 1,12l5.5,2.5L9,20l2.5,-5.5L17,12l-5.5,-2.5zM19,15l-1.25,2.75L15,19l2.75,1.25L19,23l1.25,-2.75L23,19l-2.75,-1.25L19,15z"/>
</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="M20.38,8.57l-1.23,1.85a8,8 0,0 1,-0.22 7.58L5.07,18A8,8 0,0 1,15.58 6.85l1.85,-1.23A10,10 0,0 0,3.35 19a2,2 0,0 0,1.72 1h13.85a2,2 0,0 0,1.74 -1,10 10,0 0,0 -0.27,-10.44zM10.59,15.41a2,2 0,0 0,2.83 0l5.66,-8.49 -8.49,5.66a2,2 0,0 0,0 2.83z"/>
</vector>

View File

@ -29,7 +29,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bluetooth scan:" />
android:text="@string/discovery_bluetooth_scan" />
<ProgressBar
android:id="@+id/discovery_progressbar"
@ -50,7 +50,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bluetooth LE scan:" />
android:text="@string/discovery_bluetooth_le_scan" />
<ProgressBar
android:id="@+id/discovery_ble_progressbar"

View File

@ -76,6 +76,23 @@
android:text="@string/watchface_dialog_widget_preset_right"/>
</LinearLayout>
<LinearLayout
android:id="@+id/watchface_widget_size_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/watchface_dialog_widget_width" />
<EditText
android:id="@+id/watchface_widget_width"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:text="76"/>
</LinearLayout>
<LinearLayout
android:id="@+id/watchface_widget_timezone_layout"
android:layout_width="match_parent"
@ -104,7 +121,8 @@
android:id="@+id/watchface_widget_update_timeout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"/>
android:inputType="number"
android:text="60"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@ -80,6 +80,6 @@
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:srcCompat="@drawable/ic_menu" />
app:srcCompat="@drawable/ic_settings" />
</RelativeLayout>

View File

@ -20,6 +20,7 @@
<item name="es">Español</item>
<item name="fr">Français</item>
<item name="pl">Polski</item>
<item name="pt">Português</item>
<item name="ru">Русский</item>
<item name="vi">Tiếng Việt</item>
<item name="tr">Türkçe</item>
@ -39,6 +40,7 @@
<item>es</item>
<item>fr</item>
<item>pl</item>
<item>pt</item>
<item>ru</item>
<item>vi</item>
<item>tr</item>
@ -2020,16 +2022,42 @@
<item>after_3_hour</item>
</string-array>
<string-array name="sony_wfsp800n_automatic_power_off_names">
<string-array name="sony_automatic_power_off_when_taken_off_names">
<item>@string/sony_automatic_power_off_off</item>
<item>@string/sony_automatic_power_off_when_taken_off</item>
</string-array>
<string-array name="sony_wfsp800n_automatic_power_off_values">
<string-array name="sony_automatic_power_off_when_taken_off_values">
<item>off</item>
<item>when_taken_off</item>
</string-array>
<string-array name="sony_speak_to_chat_voice_sensitivity_names">
<item>@string/sony_speak_to_chat_sensitivity_auto</item>
<item>@string/sony_speak_to_chat_sensitivity_high</item>
<item>@string/sony_speak_to_chat_sensitivity_low</item>
</string-array>
<string-array name="sony_speak_to_chat_voice_sensitivity_values">
<item>auto</item>
<item>high</item>
<item>low</item>
</string-array>
<string-array name="sony_speak_to_chat_voice_timeout_names">
<item>@string/sony_speak_to_chat_timeout_off</item>
<item>@string/sony_speak_to_chat_timeout_short</item>
<item>@string/sony_speak_to_chat_timeout_standard</item>
<item>@string/sony_speak_to_chat_timeout_long</item>
</string-array>
<string-array name="sony_speak_to_chat_voice_timeout_values">
<item>off</item>
<item>short</item>
<item>standard</item>
<item>long</item>
</string-array>
<string-array name="sony_wfsp800n_button_mode_names">
<item>@string/sony_button_mode_off</item>
<item>@string/sony_button_mode_ambient_sound_control</item>

View File

@ -355,6 +355,7 @@
<string name="connected">Connected</string>
<string name="unknown_state">Unknown state</string>
<string name="_unknown_">(unknown)</string>
<string name="unknown">Unknown</string>
<string name="test">Test</string>
<string name="test_notification">Test notification</string>
<string name="this_is_a_test_notification_from_gadgetbridge">This is a test notification from Gadgetbridge</string>
@ -379,6 +380,8 @@
<string name="title_activity_discovery">Device discovery</string>
<string name="discovery_stop_scanning">Stop scanning</string>
<string name="discovery_start_scanning">Start discovery</string>
<string name="discovery_bluetooth_scan">Bluetooth scan:</string>
<string name="discovery_bluetooth_le_scan">Bluetooth LE scan:</string>
<string name="action_discover">Connect new device</string>
<string name="device_with_rssi">%1$s (%2$s)</string>
<string name="title_activity_android_pairing">Pair device</string>
@ -769,6 +772,7 @@
<string name="Cancel">Cancel</string>
<string name="Delete">Delete</string>
<string name="ok">OK</string>
<string name="start">Start</string>
<string name="set">Set</string>
<string name="activity_data_management_directory_content_title">Export/Import directory content</string>
<string name="activity_DB_ShowContentButton">Show Export/Import directory content</string>
@ -889,6 +893,8 @@
<string name="devicetype_amazfit_bips_lite">Amazfit Bip S Lite</string>
<string name="devicetype_amazfit_bipu">Amazfit Bip U</string>
<string name="devicetype_amazfit_bipupro">Amazfit Bip U Pro</string>
<string name="devicetype_amazfit_pop">Amazfit Pop</string>
<string name="devicetype_amazfit_pop_pro">Amazfit Pop Pro</string>
<string name="devicetype_amazfit_gtr2">Amazfit GTR 2</string>
<string name="devicetype_amazfit_gtr2e">Amazfit GTR 2e</string>
<string name="devicetype_amazfit_gts2">Amazfit GTS 2</string>
@ -925,12 +931,13 @@
<string name="devicetype_banglejs">Bangle.js</string>
<string name="devicetype_tlw64">TLW64</string>
<string name="devicetype_pinetime_jf">PineTime (JF Firmware)</string>
<string name="devicetype_sonyswr12" translatable="false">Sony SWR12</string>
<string name="devicetype_sonyswr12">Sony SWR12</string>
<string name="devicetype_waspos">Wasp-os</string>
<string name="devicetype_smaq2oss">SMA-Q2 OSS</string>
<string name="devicetype_fitpro">FitPro</string>
<string name="devicetype_domyos_t540"/>
<string name="devicetype_domyos_t540">Domyos T540</string>
<string name="devicetype_sony_wh_1000xm3">Sony WH-1000XM3</string>
<string name="devicetype_sony_wh_1000xm4">Sony WH-1000XM4</string>
<string name="devicetype_sony_wf_sp800n">Sony WF-SP800N</string>
<string name="choose_auto_export_location">Choose export location</string>
<string name="notification_channel_name">General</string>
@ -1101,6 +1108,7 @@
<string name="error_background_service_reason_truncated">Starting the background service failed because…</string>
<string name="device_is_currently_bonded">ALREADY BONDED</string>
<string name="device_requires_key">KEY REQUIRED</string>
<string name="device_unsupported">UNSUPPORTED</string>
<string name="error_background_service_reason">Starting the background service failed because of an exception. Error: </string>
<string name="pref_check_permission_status">Check permission status</string>
<string name="pref_check_permission_status_summary">Check and ask for missing permissions even when they might not be instantly needed. Disable this only if your devices actually doesn\'t support any of these features. Not granting a permission might cause issues!</string>
@ -1379,7 +1387,9 @@
<string name="battery_case">Battery case</string>
<string name="left_earbud">Left earbud</string>
<string name="right_earbud">Right earbud</string>
<string name="audio_codec">Audio Codec</string>
<string name="pref_header_sony_ambient_sound_control">Ambient Sound Control</string>
<string name="pref_header_sony_device_info">Device Information</string>
<string name="sony_ambient_sound">Mode</string>
<string name="sony_ambient_sound_off">Off</string>
<string name="sony_ambient_sound_noise_cancelling">Noise Cancelling</string>
@ -1387,6 +1397,12 @@
<string name="sony_ambient_sound_ambient_sound">Ambient Sound</string>
<string name="sony_ambient_sound_focus_voice">Focus on Voice</string>
<string name="sony_ambient_sound_level">Ambient Sound Level</string>
<string name="pref_header_sony_anc_optimizer">Noise Cancelling Optimizer</string>
<string name="sony_anc_optimize_title">Optimize</string>
<string name="sony_anc_optimize_description">Click to start the noise cancelling optimizer.</string>
<string name="sony_anc_optimize_confirmation_title">Noise Cancelling Optimizer</string>
<string name="sony_anc_optimize_confirmation_description">Use the headphones as you normally would. If the wearing condition or atmospheric pressure change, run the optimizer again.</string>
<string name="pref_anc_optimizer_state_pressure">Atmospheric pressure</string>
<string name="sony_sound_position">Sound Position</string>
<string name="sony_sound_position_off">Off</string>
<string name="sony_sound_position_front">Front</string>
@ -1402,6 +1418,12 @@
<string name="sony_surround_mode_concert_hall">Concert Hall</string>
<string name="sony_warn_sbc_codec">Warning: The equalizer, audio position and surround settings only work for the SBC audio codec.</string>
<string name="sony_equalizer">Equalizer</string>
<string name="sony_anc_optimizer_status_starting">Starting…</string>
<string name="sony_anc_optimizer_status_not_running">Not Running</string>
<string name="sony_anc_optimizer_status_wearing_condition">Measuring wearing condition…</string>
<string name="sony_anc_optimizer_status_atmospheric_pressure">Measuring atmostpheric pressure…</string>
<string name="sony_anc_optimizer_status_analyzing">Analyzing…</string>
<string name="sony_anc_optimizer_status_finished">Finishing…</string>
<string name="sony_equalizer_preset_off">Off</string>
<string name="sony_equalizer_preset_bright">Bright</string>
<string name="sony_equalizer_preset_excited">Excited</string>
@ -1425,6 +1447,18 @@
<string name="sony_equalizer_clear_bass">Clear Bass</string>
<string name="sony_audio_upsampling">Audio Upsampling</string>
<string name="sony_touch_sensor">Touch sensor control</string>
<string name="sony_speak_to_chat">Speak-to-chat</string>
<string name="sony_speak_to_chat_sensitivity">Voice Detection Sensitivity</string>
<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_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>
<string name="sony_speak_to_chat_timeout_short">Short (15s)</string>
<string name="sony_speak_to_chat_timeout_standard">Standard (30s)</string>
<string name="sony_speak_to_chat_timeout_long">Long (1m)</string>
<string name="sony_connect_two_devices">Connect to 2 devices simultaneously</string>
<string name="sony_notification_voice_guide">Notifications &amp; Voice Guide</string>
<string name="sony_automatic_power_off">Automatic Power Off</string>
<string name="sony_automatic_power_off_off">Do not turn off</string>
@ -1480,4 +1514,5 @@
<string name="menuitem_menu">Menu</string>
<string name="fossil_hr_button_config_info">Some buttons cannot be configured because their functions are hard-coded in the watch firmware.\n\nWarning: long-pressing the upper button when a watchface from the official Fossil app is installed will also toggle between showing/hiding widgets.</string>
<string name="watchface_dialog_widget_width">Width:</string>
</resources>

View File

@ -1,5 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
<release version="next" versioncode="209">
<change>Sony WH-1000XM4: Initial Support</change>
<change>Sony WH-1000XM3: Disable equalizer, surround and sound position while in SBC codec</change>
<change>Improve Sony Headphones initialization on connection</change>
<change>Fixed accidentally disabled time synchronization and pairing of new Casio GBX/GBD-series watches</change>
<change>Fossil HR: improved Device Applications List handling. Added ability to change activity recognition settings on the watch</change>
<change>Fossil HR: Make width of custom widget configurable. Disable non-configurable buttons preferences</change>
<change>Add Support for Bip U series China Variant</change>
<change>Add icon for VESC devices</change>
<change>Add commit id into About screen</change>
<change>Make debug activity notification test to persist text while switching apps</change>
<change>Add Portuguese to the list of language options</change>
<change>Update configuration button icon in app notification settings</change>
</release>
<release version="0.64.0" versioncode="208">
<change>Initial support for VESC NRF/HM10 devices</change>
<change>Initial support vor Bose QC35</change>

View File

@ -2,8 +2,8 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:defaultValue="off"
android:entries="@array/sony_wfsp800n_automatic_power_off_names"
android:entryValues="@array/sony_wfsp800n_automatic_power_off_values"
android:entries="@array/sony_automatic_power_off_when_taken_off_names"
android:entryValues="@array/sony_automatic_power_off_when_taken_off_values"
android:icon="@drawable/ic_power_settings_new"
android:key="pref_sony_automatic_power_off"
android:summary="%s"

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_devices_other"
android:key="pref_sony_connect_two_devices"
android:title="@string/sony_connect_two_devices" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:key="pref_key_header_sony_anc_optimizer"
android:title="@string/pref_header_sony_anc_optimizer">
<EditTextPreference
android:defaultValue="@string/unknown"
android:enabled="false"
android:icon="@drawable/ic_pressure"
android:key="pref_sony_noise_optimizer_state_pressure"
android:shouldDisableView="false"
android:title="@string/pref_anc_optimizer_state_pressure"
app:useSimpleSummaryProvider="true" />
<Preference
android:icon="@drawable/ic_auto_awesome"
android:key="pref_sony_anc_optimizer"
android:summary="@string/sony_anc_optimize_description"
android:title="@string/sony_anc_optimize_title" />
<EditTextPreference
android:defaultValue="NOT_RUNNING"
android:enabled="false"
android:key="pref_sony_noise_optimizer_status"
android:shouldDisableView="false"
android:title="pref_sony_noise_optimizer_status"
app:isPreferenceVisible="false" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:key="pref_key_header_sony_device_info"
android:title="@string/pref_header_sony_device_info">
<EditTextPreference
android:defaultValue="\?"
android:enabled="false"
android:icon="@drawable/ic_music_note"
android:key="pref_sony_audio_codec"
android:shouldDisableView="false"
android:title="@string/audio_codec"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>

View File

@ -3,7 +3,7 @@
<PreferenceScreen
android:icon="@drawable/ic_graphic_eq"
android:key="pref_key_equalizer"
android:key="pref_sony_equalizer"
android:persistent="false"
android:title="@string/pref_header_equalizer">

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_voice"
android:key="pref_sony_speak_to_chat"
android:title="@string/sony_speak_to_chat" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,48 @@
<?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_sony_speak_to_chat_header"
android:persistent="false"
android:title="@string/sony_speak_to_chat">
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_voice"
android:key="pref_sony_speak_to_chat"
android:title="@string/sony_speak_to_chat" />
<PreferenceCategory
android:key="pref_key_sony_speak_to_chat_settings_header"
android:title="@string/sony_speak_to_chat">
<ListPreference
android:defaultValue="auto"
android:dependency="pref_sony_speak_to_chat"
android:entries="@array/sony_speak_to_chat_voice_sensitivity_names"
android:entryValues="@array/sony_speak_to_chat_voice_sensitivity_values"
android:key="pref_sony_speak_to_chat_sensitivity"
android:summary="%s"
android:title="@string/sony_speak_to_chat_sensitivity" />
<SwitchPreference
android:defaultValue="false"
android:dependency="pref_sony_speak_to_chat"
android:icon="@drawable/ic_voice"
android:key="pref_sony_speak_to_chat_focus_on_voice"
android:title="@string/sony_speak_to_chat_focus_on_voice" />
<ListPreference
android:defaultValue="standard"
android:dependency="pref_sony_speak_to_chat"
android:entries="@array/sony_speak_to_chat_voice_timeout_names"
android:entryValues="@array/sony_speak_to_chat_voice_timeout_values"
android:icon="@drawable/ic_timer"
android:key="pref_sony_speak_to_chat_timeout"
android:summary="%s"
android:title="@string/sony_speak_to_chat_timeout" />
</PreferenceCategory>
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_key_other"
android:title="@string/pref_header_system">
<SwitchPreference
android:defaultValue="true"
android:icon="@drawable/ic_touch"
android:key="pref_sony_touch_sensor"
android:title="@string/sony_touch_sensor" />
<ListPreference
android:defaultValue="off"
android:entries="@array/sony_automatic_power_off_names"
android:entryValues="@array/sony_automatic_power_off_values"
android:icon="@drawable/ic_power_settings_new"
android:key="pref_sony_automatic_power_off"
android:summary="%s"
android:title="@string/sony_automatic_power_off" />
<SwitchPreference
android:defaultValue="true"
android:icon="@drawable/ic_notifications"
android:key="pref_sony_notification_voice_guide"
android:title="@string/sony_notification_voice_guide" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>