mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-29 05:16:51 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
7697b588d1
@ -1,8 +0,0 @@
|
||||
kind: pipeline
|
||||
name: android
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: androidsdk/android-29
|
||||
commands:
|
||||
- ./gradlew assembleDebug --stacktrace
|
@ -1,6 +0,0 @@
|
||||
pipeline:
|
||||
test:
|
||||
image: androidsdk/android-30
|
||||
commands:
|
||||
- pwd #bump
|
||||
- ./gradlew assembleDebug --stacktrace
|
13
.woodpecker/can_master_build.yml
Normal file
13
.woodpecker/can_master_build.yml
Normal 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
|
||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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}.
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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--;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
5
app/src/main/res/drawable/ic_auto_awesome.xml
Normal file
5
app/src/main/res/drawable/ic_auto_awesome.xml
Normal 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>
|
5
app/src/main/res/drawable/ic_pressure.xml
Normal file
5
app/src/main/res/drawable/ic_pressure.xml
Normal 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>
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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 & 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>
|
||||
|
@ -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>
|
||||
|
@ -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"
|
10
app/src/main/res/xml/devicesettings_connect_two_devices.xml
Normal file
10
app/src/main/res/xml/devicesettings_connect_two_devices.xml
Normal 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>
|
@ -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>
|
@ -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>
|
@ -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">
|
||||
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
Loading…
Reference in New Issue
Block a user