mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-28 21:06:50 +01:00
Test Device: Add fake features and data
This commit is contained in:
parent
6a576202a9
commit
a3bd7a297a
@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test;
|
||||
|
||||
public class TestDeviceConst {
|
||||
public static final String PREF_TEST_FEATURES = "test_features";
|
||||
|
||||
private TestDeviceConst() {
|
||||
// utility class
|
||||
}
|
||||
}
|
@ -23,113 +23,596 @@ import android.net.Uri;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.widgets.WidgetManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.RoidmiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.activity.TestActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestPaiSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestSpo2SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestStressSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.samples.TestTemperatureSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.AbstractNotificationPattern;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.HeartRateSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.PaiSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.SleepRespiratoryRateSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.unknown.UnknownDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.ServiceDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.test.TestDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class TestDeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public boolean supports(GBDeviceCandidate candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
protected void deleteDevice(@NonNull final GBDevice gbDevice,
|
||||
@NonNull final Device device,
|
||||
@NonNull final DaoSession session) throws GBException {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount(GBDevice device) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
public boolean supports(final GBDeviceCandidate candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
return "Gadgetbridge";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return UnknownDeviceSupport.class;
|
||||
return TestDeviceSupport.class;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getDeviceNameResource() {
|
||||
return R.string.devicetype_test;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
return supportsActivityTracking() ? new TestSampleProvider(device, session) : super.getSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends StressSample> getStressSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
return supportsStressMeasurement() ? new TestStressSampleProvider() : super.getStressSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getStressRanges() {
|
||||
// TODO getStressRanges
|
||||
return super.getStressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends TemperatureSample> getTemperatureSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
return new TestTemperatureSampleProvider(); // TODO supportsTemperature
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends Spo2Sample> getSpo2SampleProvider(final GBDevice device, final DaoSession session) {
|
||||
return supportsSpo2() ? new TestSpo2SampleProvider() : super.getSpo2SampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends HeartRateSample> getHeartRateMaxSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
// TODO getHeartRateMaxSampleProvider
|
||||
return super.getHeartRateMaxSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends HeartRateSample> getHeartRateRestingSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
// TODO getHeartRateRestingSampleProvider
|
||||
return super.getHeartRateRestingSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends HeartRateSample> getHeartRateManualSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
// TODO getHeartRateManualSampleProvider
|
||||
return super.getHeartRateManualSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends PaiSample> getPaiSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
return supportsPai() ? new TestPaiSampleProvider() : super.getPaiSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSampleProvider<? extends SleepRespiratoryRateSample> getSleepRespiratoryRateSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
// TODO getHeartRateManualSampleProvider
|
||||
return super.getSleepRespiratoryRateSampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ActivitySummaryParser getActivitySummaryParser(final GBDevice device) {
|
||||
return supportsActivityTracks() ? new TestActivitySummaryParser() : super.getActivitySummaryParser(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getAppCacheDir() throws IOException {
|
||||
return new File(FileUtils.getExternalFilesDir(), "test-app-cache");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppCacheSortFilename() {
|
||||
return "test-app-cache-order.txt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppFileExtension() {
|
||||
return ".txt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppListFetching() {
|
||||
return supports(getTestDevice(), TestFeature.APP_LIST_FETCHING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFlashing() {
|
||||
return supports(getTestDevice(), TestFeature.FLASHING);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(final Uri uri, final Context context) {
|
||||
// TODO findInstallHandler?
|
||||
return super.findInstallHandler(uri, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return supports(getTestDevice(), TestFeature.SCREENSHOTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount(final GBDevice device) {
|
||||
return super.getAlarmSlotCount(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.SMART_WAKEUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppReordering() {
|
||||
return supports(getTestDevice(), TestFeature.APP_REORDERING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.APPS_MANAGEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCachedAppManagement(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.CACHED_APP_MANAGEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsInstalledAppManagement(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.INSTALLED_APP_MANAGEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWatchfaceManagement(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.WATCHFACE_MANAGEMENT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return AppManagerActivity.class;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getWatchfaceDesignerActivity() {
|
||||
// TODO getWatchfaceDesignerActivity
|
||||
return super.getWatchfaceDesignerActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return supports(getTestDevice(), TestFeature.CALENDAR_EVENTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return supports(getTestDevice(), TestFeature.ACTIVITY_DATA_FETCHING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return supports(getTestDevice(), TestFeature.ACTIVITY_TRACKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracks() {
|
||||
return supports(getTestDevice(), TestFeature.ACTIVITY_TRACKS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStressMeasurement() {
|
||||
return supports(getTestDevice(), TestFeature.STRESS_MEASUREMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSpo2() {
|
||||
return supports(getTestDevice(), TestFeature.SPO2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateStats() {
|
||||
return supports(getTestDevice(), TestFeature.HEART_RATE_STATS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsPai() {
|
||||
return supports(getTestDevice(), TestFeature.PAI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPaiName() {
|
||||
return R.string.menuitem_pai;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsPaiTime() {
|
||||
return supports(getTestDevice(), TestFeature.PAI_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSleepRespiratoryRate() {
|
||||
return supports(getTestDevice(), TestFeature.SLEEP_RESPIRATORY_RATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlarmSnoozing() {
|
||||
return supports(getTestDevice(), TestFeature.ALARM_SNOOZING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlarmTitle(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.ALARM_TITLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmTitleLimit(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.ALARM_TITLE_LIMIT) ? 10 : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlarmDescription(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.ALARM_DESCRIPTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return supports(getTestDevice(), TestFeature.MUSIC_INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLedColor() {
|
||||
return supports(getTestDevice(), TestFeature.LED_COLOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumReminderMessageLength() {
|
||||
// TODO getMaximumReminderMessageLength
|
||||
return 16;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReminderSlotCount(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.REMINDERS) ? 3 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCannedRepliesSlotCount(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.CANNED_REPLIES) ? 3 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWorldClocksSlotCount() {
|
||||
return supports(getTestDevice(), TestFeature.WORLD_CLOCKS) ? 3 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWorldClocksLabelLength() {
|
||||
// TODO getWorldClocksLabelLength
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDisabledWorldClocks() {
|
||||
return supports(getTestDevice(), TestFeature.DISABLED_WORLD_CLOCKS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContactsSlotCount(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.CONTACTS) ? 3 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRgbLedColor() {
|
||||
return supports(getTestDevice(), TestFeature.RGB_LED_COLOR);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public int[] getColorPresets() {
|
||||
return RoidmiConst.COLOR_PRESETS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.HEART_RATE_MEASUREMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsManualHeartRateMeasurement(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.MANUAL_HEART_RATE_MEASUREMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return supports(getTestDevice(), TestFeature.REALTIME_DATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRemSleep() {
|
||||
return supports(getTestDevice(), TestFeature.REM_SLEEP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return supports(getTestDevice(), TestFeature.WEATHER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return supports(getTestDevice(), TestFeature.FIND_DEVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUnicodeEmojis() {
|
||||
return supports(getTestDevice(), TestFeature.UNICODE_EMOJIS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificConnectionSettings() {
|
||||
return super.getSupportedDeviceSpecificConnectionSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificApplicationSettings() {
|
||||
return new int[]{
|
||||
R.xml.devicesettings_custom_deviceicon,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificSettings(final GBDevice device) {
|
||||
final List<Integer> settings = new ArrayList<>();
|
||||
|
||||
settings.add(R.xml.devicesettings_header_apps);
|
||||
settings.add(R.xml.devicesettings_loyalty_cards);
|
||||
|
||||
if (getWorldClocksSlotCount() > 0) {
|
||||
settings.add(R.xml.devicesettings_header_time);
|
||||
settings.add(R.xml.devicesettings_world_clocks);
|
||||
}
|
||||
|
||||
if (getContactsSlotCount(device) > 0) {
|
||||
settings.add(R.xml.devicesettings_header_other);
|
||||
settings.add(R.xml.devicesettings_contacts);
|
||||
}
|
||||
|
||||
settings.add(R.xml.devicesettings_header_developer);
|
||||
settings.add(R.xml.devicesettings_test_features);
|
||||
|
||||
return ArrayUtils.toPrimitive(settings.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getSupportedDeviceSpecificAuthenticationSettings() {
|
||||
return new int[]{R.xml.devicesettings_pairingkey};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new TestDeviceSpecificSettingsCustomizer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedLanguageSettings(final GBDevice device) {
|
||||
return new String[]{
|
||||
"auto",
|
||||
"en_US",
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getCalibrationActivity() {
|
||||
// TODO getCalibrationActivity
|
||||
return super.getCalibrationActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatteryCount() {
|
||||
return supports(getTestDevice(), TestFeature.BATTERIES_MULTIPLE) ? 3 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryConfig[] getBatteryConfig() {
|
||||
if (getBatteryCount() == 1) {
|
||||
return super.getBatteryConfig();
|
||||
}
|
||||
|
||||
final BatteryConfig[] ret = new BatteryConfig[getBatteryCount()];
|
||||
|
||||
for (int i = 0; i < getBatteryCount(); i++) {
|
||||
ret[i] = new BatteryConfig(i, R.drawable.ic_battery_full, R.string.battery);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsPowerOff() {
|
||||
return supports(getTestDevice(), TestFeature.POWER_OFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PasswordCapabilityImpl.Mode getPasswordCapability() {
|
||||
return super.getPasswordCapability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeartRateCapability.MeasurementInterval> getHeartRateMeasurementIntervals() {
|
||||
return super.getHeartRateMeasurementIntervals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWidgets(final GBDevice device) {
|
||||
return supports(getTestDevice(), TestFeature.WIDGETS);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WidgetManager getWidgetManager(final GBDevice device) {
|
||||
return super.getWidgetManager(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNavigation() {
|
||||
return supports(getTestDevice(), TestFeature.NAVIGATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<ServiceDeviceSupport.Flags> getInitialFlags() {
|
||||
return EnumSet.noneOf(ServiceDeviceSupport.Flags.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultIconResource() {
|
||||
// TODO getDefaultIconResource
|
||||
return super.getDefaultIconResource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisabledIconResource() {
|
||||
// TODO getDisabledIconResource
|
||||
return super.getDisabledIconResource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNotificationVibrationPatterns() {
|
||||
return supports(getTestDevice(), TestFeature.NOTIFICATION_VIBRATION_PATTERNS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNotificationVibrationRepetitionPatterns() {
|
||||
return supports(getTestDevice(), TestFeature.NOTIFICATION_VIBRATION_REPETITION_PATTERNS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNotificationLedPatterns() {
|
||||
return supports(getTestDevice(), TestFeature.NOTIFICATION_LED_PATTERNS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNotificationPattern[] getNotificationVibrationPatterns() {
|
||||
// TODO getNotificationVibrationPatterns
|
||||
return new AbstractNotificationPattern[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNotificationPattern[] getNotificationVibrationRepetitionPatterns() {
|
||||
// TODO getNotificationVibrationRepetitionPatterns
|
||||
return new AbstractNotificationPattern[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNotificationPattern[] getNotificationLedPatterns() {
|
||||
// TODO getNotificationLedPatterns
|
||||
return new AbstractNotificationPattern[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateAuthKey(final String authKey) {
|
||||
return authKey.equals("hunter2");
|
||||
}
|
||||
|
||||
public boolean supports(final GBDevice device, final TestFeature feature) {
|
||||
if (device == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Set<String> features = new HashSet<>(getPrefs(device).getStringSet(TestDeviceConst.PREF_TEST_FEATURES, Collections.emptySet()));
|
||||
// if nothing enable, enable everything
|
||||
return features.isEmpty() || features.contains(feature.name());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private GBDevice getTestDevice() {
|
||||
// HACK: Avoid the refactor of adding a device as a parameter on all functions
|
||||
// This means that it's only possible to have a single test device
|
||||
|
||||
if (GBApplication.app() == null || GBApplication.app().getDeviceManager() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
||||
for (GBDevice device : devices) {
|
||||
if (DeviceType.TEST == device.getType()) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static Prefs getPrefs(final GBDevice device) {
|
||||
return new Prefs(GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Utility class to try and get deterministic random values across multiple users. This is not efficient
|
||||
* and should definitely be improved.
|
||||
*/
|
||||
public class TestDeviceRand {
|
||||
public static final long BASE_TIMESTAMP = 1420499943000L;
|
||||
|
||||
private TestDeviceRand() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
public static int randInt(final long ts, final int min, final int max) {
|
||||
return new Random(ts - BASE_TIMESTAMP).nextInt((max - min) + 1) + min;
|
||||
}
|
||||
|
||||
public static long randLong(final long ts, final long min, final long max) {
|
||||
return (long) (new Random(ts - BASE_TIMESTAMP).nextFloat() * (max - min) + min);
|
||||
}
|
||||
|
||||
public static float randFloat(final long ts, final float min, final float max) {
|
||||
return new Random(ts - BASE_TIMESTAMP).nextFloat() * (max - min) + min;
|
||||
}
|
||||
|
||||
public static boolean randBool(final long ts, final float pTrue) {
|
||||
return new Random(ts - BASE_TIMESTAMP).nextFloat() < pTrue;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.preference.MultiSelectListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class TestDeviceSpecificSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||
@Override
|
||||
public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
|
||||
if (TestDeviceConst.PREF_TEST_FEATURES.equals(preference.getKey())) {
|
||||
handler.getDevice().sendDeviceUpdateIntent(handler.getContext());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs) {
|
||||
final Preference pref = handler.findPreference(TestDeviceConst.PREF_TEST_FEATURES);
|
||||
if (pref == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the preference directly from the enum
|
||||
final CharSequence[] entries = new CharSequence[TestFeature.values().length];
|
||||
final CharSequence[] values = new CharSequence[TestFeature.values().length];
|
||||
for (int i = 0; i < TestFeature.values().length; i++) {
|
||||
entries[i] = TestFeature.values()[i].name();
|
||||
values[i] = TestFeature.values()[i].name();
|
||||
}
|
||||
if (pref instanceof MultiSelectListPreference) {
|
||||
((MultiSelectListPreference) pref).setEntries(entries);
|
||||
((MultiSelectListPreference) pref).setEntryValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPreferenceKeysWithSummary() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public static final Creator<TestDeviceSpecificSettingsCustomizer> CREATOR = new Creator<TestDeviceSpecificSettingsCustomizer>() {
|
||||
@Override
|
||||
public TestDeviceSpecificSettingsCustomizer createFromParcel(final Parcel in) {
|
||||
return new TestDeviceSpecificSettingsCustomizer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestDeviceSpecificSettingsCustomizer[] newArray(final int size) {
|
||||
return new TestDeviceSpecificSettingsCustomizer[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test;
|
||||
|
||||
public enum TestFeature {
|
||||
ACTIVITY_DATA_FETCHING,
|
||||
ACTIVITY_TRACKING,
|
||||
ACTIVITY_TRACKS,
|
||||
ALARM_DESCRIPTION,
|
||||
ALARM_SNOOZING,
|
||||
ALARM_TITLE_LIMIT,
|
||||
ALARM_TITLE,
|
||||
APP_LIST_FETCHING,
|
||||
APP_REORDERING,
|
||||
APPS_MANAGEMENT,
|
||||
BATTERIES_MULTIPLE,
|
||||
CACHED_APP_MANAGEMENT,
|
||||
CALENDAR_EVENTS,
|
||||
CANNED_REPLIES,
|
||||
CONTACTS,
|
||||
DISABLED_WORLD_CLOCKS,
|
||||
FIND_DEVICE,
|
||||
FLASHING,
|
||||
FM_FREQUENCY,
|
||||
HEART_RATE_MEASUREMENT,
|
||||
HEART_RATE_STATS,
|
||||
INSTALLED_APP_MANAGEMENT,
|
||||
LED_COLOR,
|
||||
MANUAL_HEART_RATE_MEASUREMENT,
|
||||
MUSIC_INFO,
|
||||
NAVIGATION,
|
||||
NOTIFICATION_LED_PATTERNS,
|
||||
NOTIFICATION_VIBRATION_PATTERNS,
|
||||
NOTIFICATION_VIBRATION_REPETITION_PATTERNS,
|
||||
PAI_TIME,
|
||||
PAI,
|
||||
POWER_OFF,
|
||||
REALTIME_DATA,
|
||||
REM_SLEEP,
|
||||
REMINDERS,
|
||||
RGB_LED_COLOR,
|
||||
SCREENSHOTS,
|
||||
SLEEP_RESPIRATORY_RATE,
|
||||
SMART_WAKEUP,
|
||||
SPO2,
|
||||
STRESS_MEASUREMENT,
|
||||
UNICODE_EMOJIS,
|
||||
WATCHFACE_MANAGEMENT,
|
||||
WEATHER,
|
||||
WIDGETS,
|
||||
WORLD_CLOCKS,
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test.activity;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||
|
||||
public class TestActivitySummaryParser implements ActivitySummaryParser {
|
||||
@Override
|
||||
public BaseActivitySummary parseBinaryData(final BaseActivitySummary summary) {
|
||||
return summary;
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test.samples;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.TestDeviceRand;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.PaiSample;
|
||||
|
||||
public class TestPaiSampleProvider implements TimeSampleProvider<PaiSample> {
|
||||
@NonNull
|
||||
@Override
|
||||
public List<PaiSample> getAllSamples(final long timestampFrom, final long timestampTo) {
|
||||
final List<PaiSample> samples = new ArrayList<>();
|
||||
|
||||
int paiLow = TestDeviceRand.randInt(timestampFrom, 0, 15);
|
||||
int paiMod = TestDeviceRand.randInt(timestampFrom, 0, 15);
|
||||
int paiHigh = TestDeviceRand.randInt(timestampFrom, 0, 15);
|
||||
int paiTotal = paiLow + paiMod + paiHigh;
|
||||
|
||||
for (long ts = timestampFrom; ts < timestampTo; ts += 12 * 60 * 60 * 1000L) {
|
||||
if (TestDeviceRand.randBool(ts, 0.75f)) {
|
||||
samples.add(new TestPaiSample(
|
||||
ts,
|
||||
paiLow,
|
||||
paiMod,
|
||||
paiHigh,
|
||||
paiTotal
|
||||
));
|
||||
}
|
||||
paiTotal += paiLow + paiMod + paiHigh;
|
||||
paiLow += TestDeviceRand.randInt(ts, - paiLow, 15 - paiLow);
|
||||
paiMod += TestDeviceRand.randInt(ts, - paiMod, 15 - paiMod);
|
||||
paiHigh += TestDeviceRand.randInt(ts, - paiHigh, 15 - paiHigh);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSample(final PaiSample timeSample) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSamples(final List<PaiSample> timeSamples) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaiSample createSample() {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PaiSample getLatestSample() {
|
||||
final long ts = System.currentTimeMillis();
|
||||
return new TestPaiSample(
|
||||
ts - TestDeviceRand.randLong(ts, 10 * 1000L, 2 * 60 * 60 * 1000L),
|
||||
TestDeviceRand.randInt(ts, 85, 99),
|
||||
TestDeviceRand.randInt(ts, 85, 99),
|
||||
TestDeviceRand.randInt(ts, 85, 99),
|
||||
TestDeviceRand.randInt(ts, 85, 99)
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PaiSample getFirstSample() {
|
||||
return new TestPaiSample(
|
||||
TestDeviceRand.BASE_TIMESTAMP,
|
||||
TestDeviceRand.randInt(TestDeviceRand.BASE_TIMESTAMP, 0, 15),
|
||||
TestDeviceRand.randInt(TestDeviceRand.BASE_TIMESTAMP, 0, 15),
|
||||
TestDeviceRand.randInt(TestDeviceRand.BASE_TIMESTAMP, 0, 15),
|
||||
TestDeviceRand.randInt(TestDeviceRand.BASE_TIMESTAMP, 70, 90)
|
||||
);
|
||||
}
|
||||
|
||||
public static class TestPaiSample implements PaiSample {
|
||||
private final long timestamp;
|
||||
private final int paiLow;
|
||||
private final int paiModerate;
|
||||
private final int paiHigh;
|
||||
private final int paiTotal;
|
||||
|
||||
public TestPaiSample(final long timestamp,
|
||||
final int low,
|
||||
final int moderate,
|
||||
final int high,
|
||||
final int total) {
|
||||
this.timestamp = timestamp;
|
||||
this.paiLow = low;
|
||||
this.paiModerate = moderate;
|
||||
this.paiHigh = high;
|
||||
this.paiTotal = total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPaiLow() {
|
||||
return paiLow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPaiModerate() {
|
||||
return paiModerate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPaiHigh() {
|
||||
return paiHigh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeLow() {
|
||||
return paiLow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeModerate() {
|
||||
return paiModerate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeHigh() {
|
||||
return paiHigh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPaiToday() {
|
||||
return paiLow + paiModerate + paiHigh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPaiTotal() {
|
||||
return paiTotal;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test.samples;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.dao.AbstractDao;
|
||||
import de.greenrobot.dao.Property;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.TestDeviceRand;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
|
||||
public class TestSampleProvider extends AbstractSampleProvider<TestSampleProvider.TestActivitySample> {
|
||||
public TestSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
super(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractDao<TestActivitySample, ?> getSampleDao() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Property getRawKindSampleProperty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getTimestampSampleProperty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getDeviceIdentifierSampleProperty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int normalizeType(final int rawType) {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int toRawActivityKind(final int activityKind) {
|
||||
return activityKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalizeIntensity(final int rawIntensity) {
|
||||
return rawIntensity / 100f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestActivitySample createActivitySample() {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestActivitySample> getGBActivitySamples(final int timestamp_from, final int timestamp_to, final int activityType) {
|
||||
final List<TestActivitySample> samples = new ArrayList<>();
|
||||
|
||||
int[] sleepStages = new int[] {
|
||||
ActivityKind.TYPE_LIGHT_SLEEP,
|
||||
ActivityKind.TYPE_DEEP_SLEEP,
|
||||
};
|
||||
if (getDevice().getDeviceCoordinator().supportsRemSleep()) {
|
||||
sleepStages = ArrayUtils.add(sleepStages, ActivityKind.TYPE_REM_SLEEP);
|
||||
}
|
||||
int sleepStageCurrent = 0;
|
||||
int sleepStageDirection = 1;
|
||||
int sleepStageDurationRemaining = TestDeviceRand.randInt(timestamp_from * 1000L, 30, 90);
|
||||
|
||||
int steps = TestDeviceRand.randInt(timestamp_from * 1000L, 0, 170);
|
||||
int intensity = TestDeviceRand.randInt(timestamp_from * 1000L, 0, 100);
|
||||
int hr = TestDeviceRand.randInt(timestamp_from * 1000L, 90, 153);
|
||||
|
||||
final long bedtimeHour = TestDeviceRand.randInt(timestamp_from, 21, 22);
|
||||
final long bedtimeMinute = TestDeviceRand.randInt(timestamp_from, 0, 59);
|
||||
final long wakeHour = TestDeviceRand.randInt(timestamp_from, 6, 9);
|
||||
final long wakeMinute = TestDeviceRand.randInt(timestamp_from, 0, 59);
|
||||
|
||||
final Calendar cal = GregorianCalendar.getInstance();
|
||||
|
||||
for (long ts = timestamp_from * 1000L; ts < timestamp_to * 1000L; ts += 60 * 1000L) {
|
||||
cal.setTimeInMillis(ts);
|
||||
final int h = cal.get(Calendar.HOUR_OF_DAY);
|
||||
final int m = cal.get(Calendar.MINUTE);
|
||||
boolean isSleep = false;
|
||||
if (h == bedtimeHour) {
|
||||
isSleep = m >= bedtimeMinute;
|
||||
} else if (h > bedtimeHour) {
|
||||
isSleep = true;
|
||||
} else if (h == wakeHour) {
|
||||
isSleep = m <= wakeMinute;
|
||||
} else if (h < wakeHour) {
|
||||
isSleep = true;
|
||||
}
|
||||
|
||||
if (TestDeviceRand.randBool(ts, 0.75f)) {
|
||||
samples.add(new TestActivitySample(
|
||||
(int) (ts / 1000),
|
||||
isSleep ? sleepStages[sleepStageCurrent] : ActivityKind.TYPE_UNKNOWN,
|
||||
steps,
|
||||
intensity,
|
||||
hr
|
||||
));
|
||||
}
|
||||
|
||||
if (isSleep) {
|
||||
sleepStageDurationRemaining--;
|
||||
if (sleepStageDurationRemaining <= 0) {
|
||||
sleepStageDurationRemaining = TestDeviceRand.randInt(ts * 1000L, 30, 90);
|
||||
sleepStageCurrent += sleepStageDirection;
|
||||
if (sleepStageCurrent == 0 || sleepStageCurrent == sleepStages.length - 1) {
|
||||
sleepStageDirection *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
steps += TestDeviceRand.randInt(ts, -steps, 170 - steps);
|
||||
intensity += TestDeviceRand.randInt(ts, -1, 1);
|
||||
hr += TestDeviceRand.randInt(ts, -2, 2);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
public class TestActivitySample extends AbstractActivitySample {
|
||||
private final int timestamp;
|
||||
private final int kind;
|
||||
private final int steps;
|
||||
private final int intensity;
|
||||
private final int hr;
|
||||
|
||||
public TestActivitySample(final int timestamp,
|
||||
final int kind,
|
||||
final int steps,
|
||||
final int intensity,
|
||||
final int hr) {
|
||||
this.timestamp = timestamp;
|
||||
this.kind = kind;
|
||||
this.steps = steps;
|
||||
this.intensity = intensity;
|
||||
this.hr = hr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider getProvider() {
|
||||
return TestSampleProvider.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(final int timestamp) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserId(final long userId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeviceId(final long deviceId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDeviceId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUserId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getIntensity() {
|
||||
return intensity / 100f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeartRate() {
|
||||
return hr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawIntensity() {
|
||||
return intensity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSteps() {
|
||||
return steps;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test.samples;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.TestDeviceRand;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Spo2Sample;
|
||||
|
||||
public class TestSpo2SampleProvider implements TimeSampleProvider<Spo2Sample> {
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Spo2Sample> getAllSamples(final long timestampFrom, final long timestampTo) {
|
||||
final List<Spo2Sample> samples = new ArrayList<>();
|
||||
|
||||
int spo2 = TestDeviceRand.randInt(timestampFrom, 85, 99);
|
||||
|
||||
for (long ts = timestampFrom; ts < timestampTo; ts += 15 * 60 * 1000L) {
|
||||
if (TestDeviceRand.randBool(ts, 0.3f)) {
|
||||
samples.add(new TestSpo2Sample(ts, spo2));
|
||||
}
|
||||
spo2 += TestDeviceRand.randInt(ts, 85 - spo2, 99 - spo2);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSample(final Spo2Sample timeSample) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSamples(final List<Spo2Sample> timeSamples) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spo2Sample createSample() {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Spo2Sample getLatestSample() {
|
||||
final long ts = System.currentTimeMillis();
|
||||
return new TestSpo2Sample(
|
||||
ts - TestDeviceRand.randLong(ts, 10 * 1000L, 2 * 60 * 60 * 1000L),
|
||||
TestDeviceRand.randInt(ts, 85, 99)
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Spo2Sample getFirstSample() {
|
||||
return new TestSpo2Sample(
|
||||
TestDeviceRand.BASE_TIMESTAMP,
|
||||
TestDeviceRand.randInt(TestDeviceRand.BASE_TIMESTAMP, 85, 99)
|
||||
);
|
||||
}
|
||||
|
||||
protected static class TestSpo2Sample implements Spo2Sample {
|
||||
private final long timestamp;
|
||||
private final int spo2;
|
||||
|
||||
public TestSpo2Sample(final long timestamp, final int spo2) {
|
||||
this.timestamp = timestamp;
|
||||
this.spo2 = spo2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpo2() {
|
||||
return spo2;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test.samples;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.TestDeviceRand;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.StressSample;
|
||||
|
||||
public class TestStressSampleProvider implements TimeSampleProvider<StressSample> {
|
||||
@NonNull
|
||||
@Override
|
||||
public List<StressSample> getAllSamples(final long timestampFrom, final long timestampTo) {
|
||||
final List<StressSample> samples = new ArrayList<>();
|
||||
|
||||
int stress = TestDeviceRand.randInt(timestampFrom, 10, 90);
|
||||
|
||||
for (long ts = timestampFrom; ts < timestampTo; ts += 15 * 60 * 1000L) {
|
||||
if (TestDeviceRand.randBool(ts, 0.3f)) {
|
||||
samples.add(new TestStressSample(ts, stress));
|
||||
}
|
||||
stress += TestDeviceRand.randInt(ts, (10 - stress) / 10, (90 - stress) / 10);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSample(final StressSample timeSample) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSamples(final List<StressSample> timeSamples) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public StressSample createSample() {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public StressSample getLatestSample() {
|
||||
final long ts = System.currentTimeMillis();
|
||||
return new TestStressSample(
|
||||
ts - TestDeviceRand.randLong(ts, 10 * 1000L, 2 * 60 * 60 * 1000L),
|
||||
TestDeviceRand.randInt(ts, 10, 90)
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public StressSample getFirstSample() {
|
||||
return new TestStressSample(
|
||||
TestDeviceRand.BASE_TIMESTAMP,
|
||||
TestDeviceRand.randInt(TestDeviceRand.BASE_TIMESTAMP, 10, 90)
|
||||
);
|
||||
}
|
||||
|
||||
protected static class TestStressSample implements StressSample {
|
||||
private final long timestamp;
|
||||
private final int stress;
|
||||
|
||||
public TestStressSample(final long timestamp, final int stress) {
|
||||
this.timestamp = timestamp;
|
||||
this.stress = stress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStress() {
|
||||
return stress;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.test.samples;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.TestDeviceRand;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
|
||||
|
||||
public class TestTemperatureSampleProvider implements TimeSampleProvider<TemperatureSample> {
|
||||
@NonNull
|
||||
@Override
|
||||
public List<TemperatureSample> getAllSamples(final long timestampFrom, final long timestampTo) {
|
||||
// TODO fake samples
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSample(final TemperatureSample timeSample) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSamples(final List<TemperatureSample> timeSamples) {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemperatureSample createSample() {
|
||||
throw new UnsupportedOperationException("read-only sample provider");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TemperatureSample getLatestSample() {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TemperatureSample getFirstSample() {
|
||||
return new TestTemperatureSample(
|
||||
TestDeviceRand.BASE_TIMESTAMP,
|
||||
TestDeviceRand.randFloat(TestDeviceRand.BASE_TIMESTAMP, 36f, 38f)
|
||||
);
|
||||
}
|
||||
|
||||
protected static class TestTemperatureSample implements TemperatureSample {
|
||||
private final long timestamp;
|
||||
private final float temperature;
|
||||
|
||||
public TestTemperatureSample(final long timestamp, final float temperature) {
|
||||
this.timestamp = timestamp;
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTemperatureType() {
|
||||
return 0; // ?
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.test;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.TestDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.test.TestFeature;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
|
||||
|
||||
public class TestDeviceSupport extends AbstractDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TestDeviceSupport.class);
|
||||
|
||||
private final Handler handler = new Handler();
|
||||
|
||||
@Override
|
||||
public boolean connect() {
|
||||
LOG.info("Connecting");
|
||||
|
||||
getDevice().setState(GBDevice.State.CONNECTING);
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
|
||||
handler.postDelayed(() -> {
|
||||
LOG.info("Initialized");
|
||||
|
||||
if (getCoordinator().supportsLedColor()) {
|
||||
getDevice().setExtraInfo("led_color", 0xff3061e3);
|
||||
} else {
|
||||
getDevice().setExtraInfo("led_color", null);
|
||||
}
|
||||
|
||||
if (getCoordinator().supports(getDevice(), TestFeature.FM_FREQUENCY)) {
|
||||
getDevice().setExtraInfo("fm_frequency", 90.2f);
|
||||
} else {
|
||||
getDevice().setExtraInfo("fm_frequency", null);
|
||||
}
|
||||
|
||||
// TODO battery percentages
|
||||
// TODO hr measurements
|
||||
// TODO app list
|
||||
// TODO screenshots
|
||||
|
||||
getDevice().setState(GBDevice.State.INITIALIZED);
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
}, 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected TestDeviceCoordinator getCoordinator() {
|
||||
return (TestDeviceCoordinator) getDevice().getDeviceCoordinator();
|
||||
}
|
||||
}
|
@ -2679,4 +2679,6 @@
|
||||
<string name="pref_force_connection_type_bt_classic_value" translatable="false">BT_CLASSIC</string>
|
||||
<string name="activity_info">Activity info</string>
|
||||
<string name="warning_missing_notification_permission">Could not post ongoing notification due to missing permission</string>
|
||||
<string name="pref_test_features_title">Features</string>
|
||||
<string name="pref_test_features_summary">Enabled features for this test device</string>
|
||||
</resources>
|
||||
|
12
app/src/main/res/xml/devicesettings_test_features.xml
Normal file
12
app/src/main/res/xml/devicesettings_test_features.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- the values will be populated by the TestDeviceSpecificSettingsCustomizer -->
|
||||
<MultiSelectListPreference
|
||||
android:defaultValue="@array/empty_array"
|
||||
android:entries="@array/empty_array"
|
||||
android:entryValues="@array/empty_array"
|
||||
android:icon="@drawable/ic_developer_mode"
|
||||
android:key="test_features"
|
||||
android:summary="@string/pref_test_features_summary"
|
||||
android:title="@string/pref_test_features_title" />
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user