mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-08-25 08:40:43 +02:00
This also touches parts of the app not only used for bangle.js. E.g. pending intents gets new flags from SDK 23 inclusive. Bluetooth permissions are updated to work on SDK 31. Permission handling is updated to the new way for doing it with introduction of a new function. This is called for newer sdk versions. bump Bangle.js flavor targetSdkVersion to 31 update comments re SDK 31 set the 'exported=true' I introduced to false instead - except for three places add uses-permission for handling bluetooth in order to work on api >30 add if-blocks adding FLAG_IMMUTABLE to PendingIntents on api >30 add link to bluetooth documentation Add comment to banglejs manifest. Add requirement annotation to ControlCenterv bump compileSdkVersion to 31 add "OpenAppSettings" permission popup while working out individual permission popups on android 13 if SDK < 31 do permissions one by one, else send user to app info page to switch permissions manually working solution, but needs cleaning do some cleaning, not done though remove some logging remove import Log tweak and remove toasts in new permissions handling Change conditions `> Build.VERSION_CODES.Q` to `>= Build.VERSION_CODES.R` matching the style used everywhere else Revert "Change conditions `> Build.VERSION_CODES.Q` to `>= Build.VERSION_CODES.R` matching the style used everywhere else" This reverts commit 2929629ff43fbb685eb3d15e42459f321f68fa11. Revert "add if-blocks adding FLAG_IMMUTABLE to PendingIntents on api >30" This reverts commit ed8e1df7bb8b71fee745fbf9d10747d47c8f6cb8. Pending intents gets `PendingIntent.FLAG_IMMUTABLE` if `(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)`. Bangle.js: undo `@RequiresApi` code R ... to remove error in Android Studio where declared required api was higher then minSDK version. Use FLAG_MUTABLE for reply to test notification This should fix Gadgetbridge crashing when replying to the test notification from the debug activity. As reported here: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2924#issuecomment-917282 Change to use FLAG_IMMUTABLE/_MUTABLE from SDK 23 ... as suggested by Android Studio. This is supposed to make the app more secure by not allowing certain changes to pending intents where they are not expected. If I understood correctly. Add PendingIntentUtils class to manage mutability
1049 lines
47 KiB
Java
1049 lines
47 KiB
Java
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
|
Gobbetti, Frank Slezak, ivanovlev, Kasha, Lem Dulfo, Pavel Elagin, Steffen
|
|
Liebergeld, vanous
|
|
|
|
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.activities;
|
|
|
|
import static android.content.Intent.EXTRA_SUBJECT;
|
|
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
|
|
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.app.DatePickerDialog;
|
|
import android.app.PendingIntent;
|
|
import android.appwidget.AppWidgetHost;
|
|
import android.appwidget.AppWidgetManager;
|
|
import android.bluetooth.BluetoothDevice;
|
|
import android.companion.AssociationRequest;
|
|
import android.companion.BluetoothDeviceFilter;
|
|
import android.companion.CompanionDeviceManager;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.IntentSender;
|
|
import android.content.SharedPreferences;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.preference.PreferenceManager;
|
|
import android.text.Editable;
|
|
import android.text.TextUtils;
|
|
import android.text.TextWatcher;
|
|
import android.util.Pair;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.widget.AdapterView;
|
|
import android.widget.ArrayAdapter;
|
|
import android.widget.Button;
|
|
import android.widget.CheckBox;
|
|
import android.widget.CompoundButton;
|
|
import android.widget.DatePicker;
|
|
import android.widget.EditText;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.Spinner;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.RequiresApi;
|
|
import androidx.core.app.ActivityCompat;
|
|
import androidx.core.app.NavUtils;
|
|
import androidx.core.app.NotificationCompat;
|
|
import androidx.core.app.RemoteInput;
|
|
import androidx.core.content.FileProvider;
|
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.io.File;
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Calendar;
|
|
import java.util.Collections;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Random;
|
|
import java.util.TreeMap;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
import nodomain.freeyourgadget.gadgetbridge.Widget;
|
|
import nodomain.freeyourgadget.gadgetbridge.adapter.SpinnerWithIconAdapter;
|
|
import nodomain.freeyourgadget.gadgetbridge.adapter.SpinnerWithIconItem;
|
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
|
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
|
import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationManager;
|
|
import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksContentObserver;
|
|
import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksController;
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
|
|
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
|
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
|
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";
|
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
switch (Objects.requireNonNull(intent.getAction())) {
|
|
case ACTION_REPLY: {
|
|
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
|
|
CharSequence reply = remoteInput.getCharSequence(EXTRA_REPLY);
|
|
LOG.info("got wearable reply: " + reply);
|
|
GB.toast(context, "got wearable reply: " + reply, Toast.LENGTH_SHORT, GB.INFO);
|
|
break;
|
|
}
|
|
case DeviceService.ACTION_REALTIME_SAMPLES:
|
|
handleRealtimeSample(intent.getSerializableExtra(DeviceService.EXTRA_REALTIME_SAMPLE));
|
|
break;
|
|
default:
|
|
LOG.info("ignoring intent action " + intent.getAction());
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
private Spinner sendTypeSpinner;
|
|
private EditText editContent;
|
|
public static final long SELECT_DEVICE = 999L;
|
|
private long selectedTestDeviceKey = SELECT_DEVICE;
|
|
private String selectedTestDeviceMAC;
|
|
|
|
private static final int SELECT_DEVICE_REQUEST_CODE = 1;
|
|
|
|
private void handleRealtimeSample(Serializable extra) {
|
|
if (extra instanceof ActivitySample) {
|
|
ActivitySample sample = (ActivitySample) extra;
|
|
GB.toast(this, "Heart Rate measured: " + sample.getHeartRate(), Toast.LENGTH_LONG, GB.INFO);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.activity_debug);
|
|
|
|
IntentFilter filter = new IntentFilter();
|
|
filter.addAction(ACTION_REPLY);
|
|
filter.addAction(DeviceService.ACTION_REALTIME_SAMPLES);
|
|
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
|
|
registerReceiver(mReceiver, filter); // for ACTION_REPLY
|
|
|
|
editContent = findViewById(R.id.editContent);
|
|
|
|
final ArrayList<String> spinnerArray = new ArrayList<>();
|
|
for (NotificationType notificationType : NotificationType.sortedValues()) {
|
|
spinnerArray.add(notificationType.name());
|
|
}
|
|
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, spinnerArray);
|
|
sendTypeSpinner = findViewById(R.id.sendTypeSpinner);
|
|
sendTypeSpinner.setAdapter(spinnerArrayAdapter);
|
|
|
|
Button sendButton = findViewById(R.id.sendButton);
|
|
sendButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
NotificationSpec notificationSpec = new NotificationSpec();
|
|
String testString = editContent.getText().toString();
|
|
notificationSpec.phoneNumber = testString;
|
|
notificationSpec.body = testString;
|
|
notificationSpec.sender = testString;
|
|
notificationSpec.subject = testString;
|
|
if (notificationSpec.type != NotificationType.GENERIC_SMS) {
|
|
// SMS notifications don't have a source app ID when sent by the SMSReceiver,
|
|
// so let's not set it here as well for consistency
|
|
notificationSpec.sourceAppId = BuildConfig.APPLICATION_ID;
|
|
}
|
|
notificationSpec.sourceName = getApplicationContext().getApplicationInfo()
|
|
.loadLabel(getApplicationContext().getPackageManager())
|
|
.toString();
|
|
notificationSpec.type = NotificationType.sortedValues()[sendTypeSpinner.getSelectedItemPosition()];
|
|
notificationSpec.pebbleColor = notificationSpec.type.color;
|
|
notificationSpec.attachedActions = new ArrayList<>();
|
|
|
|
if (notificationSpec.type == NotificationType.GENERIC_SMS) {
|
|
// REPLY action
|
|
NotificationSpec.Action replyAction = new NotificationSpec.Action();
|
|
replyAction.title = "Reply";
|
|
replyAction.type = NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR;
|
|
notificationSpec.attachedActions.add(replyAction);
|
|
}
|
|
|
|
GBApplication.deviceService().onNotification(notificationSpec);
|
|
}
|
|
});
|
|
|
|
Button incomingCallButton = findViewById(R.id.incomingCallButton);
|
|
incomingCallButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
CallSpec callSpec = new CallSpec();
|
|
callSpec.command = CallSpec.CALL_INCOMING;
|
|
callSpec.number = editContent.getText().toString();
|
|
GBApplication.deviceService().onSetCallState(callSpec);
|
|
}
|
|
});
|
|
Button outgoingCallButton = findViewById(R.id.outgoingCallButton);
|
|
outgoingCallButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
CallSpec callSpec = new CallSpec();
|
|
callSpec.command = CallSpec.CALL_OUTGOING;
|
|
callSpec.number = editContent.getText().toString();
|
|
GBApplication.deviceService().onSetCallState(callSpec);
|
|
}
|
|
});
|
|
|
|
Button startCallButton = findViewById(R.id.startCallButton);
|
|
startCallButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
CallSpec callSpec = new CallSpec();
|
|
callSpec.command = CallSpec.CALL_START;
|
|
GBApplication.deviceService().onSetCallState(callSpec);
|
|
}
|
|
});
|
|
Button endCallButton = findViewById(R.id.endCallButton);
|
|
endCallButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
CallSpec callSpec = new CallSpec();
|
|
callSpec.command = CallSpec.CALL_END;
|
|
GBApplication.deviceService().onSetCallState(callSpec);
|
|
}
|
|
});
|
|
|
|
Button rebootButton = findViewById(R.id.rebootButton);
|
|
rebootButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
GBApplication.deviceService().onReset(GBDeviceProtocol.RESET_FLAGS_REBOOT);
|
|
}
|
|
});
|
|
|
|
Button factoryResetButton = findViewById(R.id.factoryResetButton);
|
|
factoryResetButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
new AlertDialog.Builder(DebugActivity.this)
|
|
.setCancelable(true)
|
|
.setTitle(R.string.debugactivity_really_factoryreset_title)
|
|
.setMessage(R.string.debugactivity_really_factoryreset)
|
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
GBApplication.deviceService().onReset(GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET);
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
}
|
|
})
|
|
.show();
|
|
}
|
|
});
|
|
|
|
Button heartRateButton = findViewById(R.id.HeartRateButton);
|
|
heartRateButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
GB.toast("Measuring heart rate, please wait...", Toast.LENGTH_LONG, GB.INFO);
|
|
GBApplication.deviceService().onHeartRateTest();
|
|
}
|
|
});
|
|
|
|
Button setFetchTimeButton = findViewById(R.id.SetFetchTimeButton);
|
|
setFetchTimeButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
|
|
final Calendar currentDate = Calendar.getInstance();
|
|
Context context = getApplicationContext();
|
|
|
|
if (context instanceof GBApplication) {
|
|
GBApplication gbApp = (GBApplication) context;
|
|
final List<GBDevice> devices = gbApp.getDeviceManager().getSelectedDevices();
|
|
if(devices.size() == 0){
|
|
GB.toast("Device not selected/connected", Toast.LENGTH_LONG, GB.INFO);
|
|
return;
|
|
}
|
|
new DatePickerDialog(DebugActivity.this, new DatePickerDialog.OnDateSetListener() {
|
|
@Override
|
|
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
|
|
Calendar date = Calendar.getInstance();
|
|
date.set(year, monthOfYear, dayOfMonth);
|
|
|
|
long timestamp = date.getTimeInMillis() - 1000;
|
|
GB.toast("Setting lastSyncTimeMillis: " + timestamp, Toast.LENGTH_LONG, GB.INFO);
|
|
|
|
for(GBDevice device : devices){
|
|
SharedPreferences.Editor editor = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()).edit();
|
|
editor.remove("lastSyncTimeMillis"); //FIXME: key reconstruction is BAD
|
|
editor.putLong("lastSyncTimeMillis", timestamp);
|
|
editor.apply();
|
|
}
|
|
}
|
|
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
|
|
}
|
|
|
|
|
|
}
|
|
});
|
|
|
|
Button setWeatherButton = findViewById(R.id.setWeatherButton);
|
|
setWeatherButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
if (Weather.getInstance().getWeatherSpec() == null) {
|
|
final WeatherSpec weatherSpec = new WeatherSpec();
|
|
weatherSpec.forecasts = new ArrayList<>();
|
|
|
|
weatherSpec.location = "Green Hill";
|
|
weatherSpec.currentConditionCode = 601; // snow
|
|
weatherSpec.currentCondition = Weather.getConditionString(weatherSpec.currentConditionCode);
|
|
|
|
weatherSpec.currentTemp = 15 + 273;
|
|
weatherSpec.currentHumidity = 30;
|
|
|
|
weatherSpec.windSpeed = 10;
|
|
weatherSpec.windDirection = 12;
|
|
weatherSpec.timestamp = (int) (System.currentTimeMillis() / 1000);
|
|
weatherSpec.todayMinTemp = 10 + 273;
|
|
weatherSpec.todayMaxTemp = 25 + 273;
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
final WeatherSpec.Forecast gbForecast = new WeatherSpec.Forecast();
|
|
gbForecast.minTemp = 10 + i + 273;
|
|
gbForecast.maxTemp = 25 + i + 273;
|
|
|
|
gbForecast.conditionCode = 800; // clear
|
|
weatherSpec.forecasts.add(gbForecast);
|
|
}
|
|
|
|
Weather.getInstance().setWeatherSpec(weatherSpec);
|
|
}
|
|
|
|
GBApplication.deviceService().onSendWeather(Weather.getInstance().getWeatherSpec());
|
|
}
|
|
});
|
|
|
|
Button setMusicInfoButton = findViewById(R.id.setMusicInfoButton);
|
|
setMusicInfoButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
MusicSpec musicSpec = new MusicSpec();
|
|
String testString = editContent.getText().toString();
|
|
musicSpec.artist = testString + "(artist)";
|
|
musicSpec.album = testString + "(album)";
|
|
musicSpec.track = testString + "(track)";
|
|
musicSpec.duration = 10;
|
|
musicSpec.trackCount = 5;
|
|
musicSpec.trackNr = 2;
|
|
|
|
GBApplication.deviceService().onSetMusicInfo(musicSpec);
|
|
|
|
MusicStateSpec stateSpec = new MusicStateSpec();
|
|
stateSpec.position = 0;
|
|
stateSpec.state = 0x01; // playing
|
|
stateSpec.playRate = 100;
|
|
stateSpec.repeat = 1;
|
|
stateSpec.shuffle = 1;
|
|
|
|
GBApplication.deviceService().onSetMusicState(stateSpec);
|
|
}
|
|
});
|
|
|
|
Button setTimeButton = findViewById(R.id.setTimeButton);
|
|
setTimeButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
GBApplication.deviceService().onSetTime();
|
|
}
|
|
});
|
|
|
|
Button testNotificationButton = findViewById(R.id.testNotificationButton);
|
|
testNotificationButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
testNotification();
|
|
}
|
|
});
|
|
|
|
Button testPebbleKitNotificationButton = findViewById(R.id.testPebbleKitNotificationButton);
|
|
testPebbleKitNotificationButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
testPebbleKitNotification();
|
|
}
|
|
});
|
|
|
|
Button fetchDebugLogsButton = findViewById(R.id.fetchDebugLogsButton);
|
|
fetchDebugLogsButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_DEBUGLOGS);
|
|
}
|
|
});
|
|
|
|
Button testNewFunctionalityButton = findViewById(R.id.testNewFunctionality);
|
|
testNewFunctionalityButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
testNewFunctionality();
|
|
}
|
|
});
|
|
|
|
Button shareLogButton = findViewById(R.id.shareLog);
|
|
shareLogButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(DebugActivity.this);
|
|
Prefs prefs = new Prefs(sharedPrefs);
|
|
boolean logging_enabled = prefs.getBoolean("log_to_file", false);
|
|
if (logging_enabled) {
|
|
showLogSharingWarning();
|
|
} else {
|
|
showLogSharingNotEnabledAlert();
|
|
}
|
|
}
|
|
});
|
|
|
|
Button showWidgetsButton = findViewById(R.id.showWidgetsButton);
|
|
showWidgetsButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
showAllRegisteredAppWidgets();
|
|
}
|
|
});
|
|
|
|
Button unregisterWidgetsButton = findViewById(R.id.deleteWidgets);
|
|
unregisterWidgetsButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
unregisterAllRegisteredAppWidgets();
|
|
}
|
|
});
|
|
|
|
Button showWidgetsPrefsButton = findViewById(R.id.showWidgetsPrefs);
|
|
showWidgetsPrefsButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
showAppWidgetsPrefs();
|
|
}
|
|
});
|
|
|
|
Button deleteWidgetsPrefsButton = findViewById(R.id.deleteWidgetsPrefs);
|
|
deleteWidgetsPrefsButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
deleteWidgetsPrefs();
|
|
}
|
|
});
|
|
|
|
Button removeDevicePreferencesButton = findViewById(R.id.removeDevicePreferences);
|
|
removeDevicePreferencesButton.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
new AlertDialog.Builder(DebugActivity.this)
|
|
.setCancelable(true)
|
|
.setTitle(R.string.debugactivity_confirm_remove_device_preferences_title)
|
|
.setMessage(R.string.debugactivity_confirm_remove_device_preferences)
|
|
.setPositiveButton(R.string.ok, (dialog, which) -> {
|
|
final GBApplication gbApp = (GBApplication) getApplicationContext();
|
|
final List<GBDevice> devices = gbApp.getDeviceManager().getSelectedDevices();
|
|
for(final GBDevice device : devices){
|
|
GBApplication.deleteDeviceSpecificSharedPrefs(device.getAddress());
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.Cancel, (dialog, which) -> {})
|
|
.show();
|
|
}
|
|
});
|
|
|
|
Button runDebugFunction = findViewById(R.id.runDebugFunction);
|
|
runDebugFunction.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
//SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit();
|
|
//editor.remove("notification_list_is_blacklist").apply();
|
|
}
|
|
});
|
|
|
|
Button addDeviceButtonDebug = findViewById(R.id.addDeviceButtonDebug);
|
|
addDeviceButtonDebug.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
LinkedHashMap<String, Pair<Long, Integer>> allDevices;
|
|
allDevices = getAllSupportedDevices(getApplicationContext());
|
|
|
|
final LinearLayout linearLayout = new LinearLayout(DebugActivity.this);
|
|
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
|
|
|
final LinearLayout macLayout = new LinearLayout(DebugActivity.this);
|
|
macLayout.setOrientation(LinearLayout.HORIZONTAL);
|
|
macLayout.setPadding(20, 0, 20, 0);
|
|
|
|
final TextView textView = new TextView(DebugActivity.this);
|
|
textView.setText("MAC Address: ");
|
|
final EditText editText = new EditText(DebugActivity.this);
|
|
selectedTestDeviceMAC = randomMac();
|
|
editText.setText(selectedTestDeviceMAC);
|
|
editText.addTextChangedListener(new TextWatcher() {
|
|
@Override
|
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void afterTextChanged(Editable editable) {
|
|
selectedTestDeviceMAC = editable.toString();
|
|
|
|
}
|
|
});
|
|
|
|
macLayout.addView(textView);
|
|
macLayout.addView(editText);
|
|
|
|
final Spinner deviceListSpinner = new Spinner(DebugActivity.this);
|
|
ArrayList<SpinnerWithIconItem> deviceListArray = new ArrayList<>();
|
|
for (Map.Entry<String, Pair<Long, Integer>> item : allDevices.entrySet()) {
|
|
deviceListArray.add(new SpinnerWithIconItem(item.getKey(), item.getValue().first, item.getValue().second));
|
|
}
|
|
final SpinnerWithIconAdapter deviceListAdapter = new SpinnerWithIconAdapter(DebugActivity.this,
|
|
R.layout.spinner_with_image_layout, R.id.spinner_item_text, deviceListArray);
|
|
deviceListSpinner.setAdapter(deviceListAdapter);
|
|
addListenerOnSpinnerDeviceSelection(deviceListSpinner);
|
|
|
|
linearLayout.addView(deviceListSpinner);
|
|
linearLayout.addView(macLayout);
|
|
|
|
new AlertDialog.Builder(DebugActivity.this)
|
|
.setCancelable(true)
|
|
.setTitle(R.string.add_test_device)
|
|
.setView(linearLayout)
|
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
createTestDevice(DebugActivity.this, selectedTestDeviceKey, selectedTestDeviceMAC);
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
}
|
|
})
|
|
.show();
|
|
}
|
|
});
|
|
|
|
CheckBox activity_list_debug_extra_time_range = findViewById(R.id.activity_list_debug_extra_time_range);
|
|
activity_list_debug_extra_time_range.setAllCaps(true);
|
|
boolean activity_list_debug_extra_time_range_value = GBApplication.getPrefs().getPreferences().getBoolean("activity_list_debug_extra_time_range", false);
|
|
activity_list_debug_extra_time_range.setChecked(activity_list_debug_extra_time_range_value);
|
|
|
|
activity_list_debug_extra_time_range.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
|
@Override
|
|
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
|
|
GBApplication.getPrefs().getPreferences().getBoolean("activity_list_debug_extra_time_range", false);
|
|
SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit();
|
|
editor.putBoolean("activity_list_debug_extra_time_range", b).apply();
|
|
}
|
|
});
|
|
|
|
Button startFitnessAppTracking = findViewById(R.id.startFitnessAppTracking);
|
|
startFitnessAppTracking.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
OpenTracksController.startRecording(DebugActivity.this);
|
|
}
|
|
});
|
|
|
|
Button stopFitnessAppTracking = findViewById(R.id.stopFitnessAppTracking);
|
|
stopFitnessAppTracking.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
OpenTracksController.stopRecording(DebugActivity.this);
|
|
}
|
|
});
|
|
|
|
Button stopPhoneGpsLocationListener = findViewById(R.id.stopPhoneGpsLocationListener);
|
|
stopPhoneGpsLocationListener.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
GBLocationManager.stopAll(getBaseContext());
|
|
}
|
|
});
|
|
|
|
Button showCompanionDevices = findViewById(R.id.showCompanionDevices);
|
|
showCompanionDevices.setVisibility(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? View.VISIBLE : View.GONE);
|
|
showCompanionDevices.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
LOG.warn("Android version < O, companion devices not supported");
|
|
return;
|
|
}
|
|
|
|
final CompanionDeviceManager manager = (CompanionDeviceManager) GBApplication.getContext().getSystemService(Context.COMPANION_DEVICE_SERVICE);
|
|
final List<String> associations = new ArrayList<>(manager.getAssociations());
|
|
Collections.sort(associations);
|
|
String companionDevicesList = String.format(Locale.ROOT, "%d companion devices", associations.size());
|
|
if (!associations.isEmpty()) {
|
|
companionDevicesList += "\n\n" + StringUtils.join("\n", associations.toArray(new String[0]));
|
|
}
|
|
|
|
new AlertDialog.Builder(DebugActivity.this)
|
|
.setCancelable(false)
|
|
.setTitle("Companion Devices")
|
|
.setMessage(companionDevicesList)
|
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
}
|
|
})
|
|
.show();
|
|
}
|
|
});
|
|
|
|
final Button pairAsCompanion = findViewById(R.id.pairAsCompanion);
|
|
pairAsCompanion.setVisibility(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? View.VISIBLE : View.GONE);
|
|
pairAsCompanion.setOnClickListener(v -> {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
pairCurrentAsCompanion();
|
|
}
|
|
});
|
|
|
|
Button showStatusFitnessAppTracking = findViewById(R.id.showStatusFitnessAppTracking);
|
|
final int delay = 2 * 1000;
|
|
|
|
showStatusFitnessAppTracking.setOnClickListener(new View.OnClickListener() {
|
|
final Handler handler = new Handler();
|
|
Runnable runnable;
|
|
|
|
@Override
|
|
public void onClick(View v) {
|
|
final AlertDialog.Builder fitnesStatusBuilder = new AlertDialog.Builder(DebugActivity.this);
|
|
fitnesStatusBuilder
|
|
.setCancelable(false)
|
|
.setTitle("openTracksObserver Status")
|
|
.setMessage("Starting openTracksObserver watcher, waiting for an update, refreshing every: " + delay + "ms")
|
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
handler.removeCallbacks(runnable);
|
|
}
|
|
});
|
|
final AlertDialog alert = fitnesStatusBuilder.show();
|
|
|
|
|
|
runnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
LOG.debug("openTracksObserver debug watch dialog running");
|
|
handler.postDelayed(this, delay); //schedule next execution
|
|
|
|
OpenTracksContentObserver openTracksObserver = GBApplication.app().getOpenTracksObserver();
|
|
if (openTracksObserver == null) {
|
|
LOG.debug("openTracksObserver is null");
|
|
alert.cancel();
|
|
alert.setMessage("openTracksObserver not running");
|
|
alert.show();
|
|
return;
|
|
}
|
|
LOG.debug("openTracksObserver is not null, updating debug view");
|
|
long timeSecs = openTracksObserver.getTimeMillisChange() / 1000;
|
|
float distanceCM = openTracksObserver.getDistanceMeterChange() * 100;
|
|
|
|
LOG.debug("Time: " + timeSecs + " distanceCM " + distanceCM);
|
|
alert.cancel();
|
|
alert.setMessage("TimeSec: " + timeSecs + " distanceCM " + distanceCM);
|
|
alert.show();
|
|
}
|
|
};
|
|
handler.postDelayed(runnable, delay);
|
|
}
|
|
});
|
|
}
|
|
|
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
|
requestCode == SELECT_DEVICE_REQUEST_CODE &&
|
|
resultCode == Activity.RESULT_OK) {
|
|
|
|
final BluetoothDevice deviceToPair = data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE);
|
|
|
|
if (deviceToPair != null) {
|
|
if (deviceToPair.getBondState() != BluetoothDevice.BOND_BONDED) {
|
|
GB.toast("Creating bond...", Toast.LENGTH_SHORT, GB.INFO);
|
|
deviceToPair.createBond();
|
|
} else {
|
|
GB.toast("Bonding complete", Toast.LENGTH_LONG, GB.INFO);
|
|
}
|
|
} else {
|
|
GB.toast("No device to pair", Toast.LENGTH_LONG, GB.ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
private void pairCurrentAsCompanion() {
|
|
final GBApplication gbApp = (GBApplication) getApplicationContext();
|
|
final List<GBDevice> devices = gbApp.getDeviceManager().getSelectedDevices();
|
|
if (devices.size() != 1) {
|
|
GB.toast("Please connect to a single device that you want to pair as companion", Toast.LENGTH_LONG, GB.WARN);
|
|
return;
|
|
}
|
|
|
|
final GBDevice device = devices.get(0);
|
|
|
|
final CompanionDeviceManager manager = (CompanionDeviceManager) GBApplication.getContext().getSystemService(Context.COMPANION_DEVICE_SERVICE);
|
|
|
|
if (manager.getAssociations().contains(device.getAddress())) {
|
|
GB.toast(device.getAliasOrName() + " already paired as companion", Toast.LENGTH_LONG, GB.INFO);
|
|
return;
|
|
}
|
|
|
|
final BluetoothDeviceFilter deviceFilter = new BluetoothDeviceFilter.Builder()
|
|
.setAddress(device.getAddress())
|
|
.build();
|
|
|
|
final AssociationRequest pairingRequest = new AssociationRequest.Builder()
|
|
.addDeviceFilter(deviceFilter)
|
|
.setSingleDevice(true)
|
|
.build();
|
|
|
|
CompanionDeviceManager.Callback callback = new CompanionDeviceManager.Callback() {
|
|
@Override
|
|
public void onFailure(final CharSequence error) {
|
|
GB.toast("Companion pairing failed: " + error, Toast.LENGTH_LONG, GB.ERROR);
|
|
}
|
|
|
|
@Override
|
|
public void onDeviceFound(final IntentSender chooserLauncher) {
|
|
GB.toast("Found device", Toast.LENGTH_SHORT, GB.INFO);
|
|
|
|
try {
|
|
ActivityCompat.startIntentSenderForResult(
|
|
DebugActivity.this,
|
|
chooserLauncher,
|
|
SELECT_DEVICE_REQUEST_CODE,
|
|
null,
|
|
0,
|
|
0,
|
|
0,
|
|
null
|
|
);
|
|
} catch (final IntentSender.SendIntentException e) {
|
|
LOG.error("Failed to send intent", e);
|
|
}
|
|
}
|
|
};
|
|
|
|
manager.associate(pairingRequest, callback, null);
|
|
}
|
|
|
|
@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);
|
|
widgetPreferenceStorage.showAppWidgetsPrefs(DebugActivity.this);
|
|
}
|
|
|
|
private void showAppWidgetsPrefs() {
|
|
WidgetPreferenceStorage widgetPreferenceStorage = new WidgetPreferenceStorage();
|
|
widgetPreferenceStorage.showAppWidgetsPrefs(DebugActivity.this);
|
|
|
|
}
|
|
|
|
private void showAllRegisteredAppWidgets() {
|
|
//https://stackoverflow.com/questions/17387191/check-if-a-widget-is-exists-on-homescreen-using-appwidgetid
|
|
|
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(DebugActivity.this);
|
|
AppWidgetHost appWidgetHost = new AppWidgetHost(DebugActivity.this, 1); // for removing phantoms
|
|
int[] appWidgetIDs = appWidgetManager.getAppWidgetIds(new ComponentName(DebugActivity.this, Widget.class));
|
|
GB.toast("Number of registered app widgets: " + appWidgetIDs.length, Toast.LENGTH_SHORT, GB.INFO);
|
|
for (int appWidgetID : appWidgetIDs) {
|
|
GB.toast("Widget: " + appWidgetID, Toast.LENGTH_SHORT, GB.INFO);
|
|
}
|
|
}
|
|
|
|
private void unregisterAllRegisteredAppWidgets() {
|
|
//https://stackoverflow.com/questions/17387191/check-if-a-widget-is-exists-on-homescreen-using-appwidgetid
|
|
|
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(DebugActivity.this);
|
|
AppWidgetHost appWidgetHost = new AppWidgetHost(DebugActivity.this, 1); // for removing phantoms
|
|
int[] appWidgetIDs = appWidgetManager.getAppWidgetIds(new ComponentName(DebugActivity.this, Widget.class));
|
|
GB.toast("Number of registered app widgets: " + appWidgetIDs.length, Toast.LENGTH_SHORT, GB.INFO);
|
|
for (int appWidgetID : appWidgetIDs) {
|
|
appWidgetHost.deleteAppWidgetId(appWidgetID);
|
|
GB.toast("Removing widget: " + appWidgetID, Toast.LENGTH_SHORT, GB.INFO);
|
|
}
|
|
}
|
|
|
|
private void showLogSharingNotEnabledAlert() {
|
|
new AlertDialog.Builder(this)
|
|
.setTitle(R.string.note)
|
|
.setPositiveButton(R.string.ok, null)
|
|
.setMessage(R.string.share_log_not_enabled_message)
|
|
.show();
|
|
}
|
|
|
|
private void showLogSharingWarning() {
|
|
new AlertDialog.Builder(this)
|
|
.setCancelable(true)
|
|
.setTitle(R.string.warning)
|
|
.setMessage(R.string.share_log_warning)
|
|
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
shareLog();
|
|
}
|
|
})
|
|
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
// do nothing
|
|
}
|
|
})
|
|
.show();
|
|
}
|
|
|
|
private void testNewFunctionality() {
|
|
GBApplication.deviceService().onTestNewFunction();
|
|
}
|
|
|
|
private void shareLog() {
|
|
String fileName = GBApplication.getLogPath();
|
|
if (fileName != null && fileName.length() > 0) {
|
|
File logFile = new File(fileName);
|
|
if (!logFile.exists()) {
|
|
GB.toast("File does not exist", Toast.LENGTH_LONG, GB.INFO);
|
|
return;
|
|
}
|
|
|
|
final Uri providerUri = FileProvider.getUriForFile(
|
|
this,
|
|
getApplicationContext().getPackageName() + ".screenshot_provider",
|
|
logFile
|
|
);
|
|
|
|
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
|
|
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
emailIntent.setType("*/*");
|
|
emailIntent.putExtra(EXTRA_SUBJECT, "Gadgetbridge log file");
|
|
emailIntent.putExtra(Intent.EXTRA_STREAM, providerUri);
|
|
startActivity(Intent.createChooser(emailIntent, "Share File"));
|
|
}
|
|
}
|
|
|
|
private void testNotification() {
|
|
Intent notificationIntent = new Intent(getApplicationContext(), DebugActivity.class);
|
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
|
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
PendingIntent pendingIntent = PendingIntentUtils.getActivity(getApplicationContext(), 0,
|
|
notificationIntent, 0, false);
|
|
|
|
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_REPLY)
|
|
.build();
|
|
|
|
Intent replyIntent = new Intent(ACTION_REPLY);
|
|
|
|
PendingIntent replyPendingIntent = PendingIntentUtils.getBroadcast(this, 0, replyIntent, 0, true);
|
|
|
|
NotificationCompat.Action action =
|
|
new NotificationCompat.Action.Builder(android.R.drawable.ic_input_add, "Reply", replyPendingIntent)
|
|
.addRemoteInput(remoteInput)
|
|
.build();
|
|
|
|
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender().addAction(action);
|
|
|
|
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
|
.setContentTitle(getString(R.string.test_notification))
|
|
.setContentText(getString(R.string.this_is_a_test_notification_from_gadgetbridge))
|
|
.setTicker(getString(R.string.this_is_a_test_notification_from_gadgetbridge))
|
|
.setSmallIcon(R.drawable.ic_notification)
|
|
.setAutoCancel(true)
|
|
.setContentIntent(pendingIntent)
|
|
.extend(wearableExtender);
|
|
|
|
GB.notify((int) System.currentTimeMillis(), ncomp.build(), this);
|
|
}
|
|
|
|
private void testPebbleKitNotification() {
|
|
Intent pebbleKitIntent = new Intent("com.getpebble.action.SEND_NOTIFICATION");
|
|
pebbleKitIntent.putExtra("messageType", "PEBBLE_ALERT");
|
|
pebbleKitIntent.putExtra("notificationData", "[{\"title\":\"PebbleKitTest\",\"body\":\"sent from Gadgetbridge\"}]");
|
|
getApplicationContext().sendBroadcast(pebbleKitIntent);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case android.R.id.home:
|
|
NavUtils.navigateUpFromSameTask(this);
|
|
return true;
|
|
}
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
|
|
unregisterReceiver(mReceiver);
|
|
}
|
|
|
|
private void addListenerOnSpinnerDeviceSelection(Spinner spinner) {
|
|
spinner.setOnItemSelectedListener(new CustomOnDeviceSelectedListener());
|
|
}
|
|
|
|
protected static void createTestDevice(Context context, long deviceKey, String deviceMac) {
|
|
if (deviceKey == SELECT_DEVICE) {
|
|
return;
|
|
}
|
|
DeviceType deviceType = DeviceType.fromKey((int) deviceKey);
|
|
try (
|
|
DBHandler db = GBApplication.acquireDB()) {
|
|
DaoSession daoSession = db.getDaoSession();
|
|
GBDevice gbDevice = new GBDevice(deviceMac, deviceType.name(), "", null, deviceType);
|
|
gbDevice.setFirmwareVersion("N/A");
|
|
gbDevice.setFirmwareVersion2("N/A");
|
|
|
|
//this causes the attributes (fw version) to be stored as well. Not much useful, but still...
|
|
gbDevice.setState(GBDevice.State.INITIALIZED);
|
|
|
|
Device device = DBHelper.getDevice(gbDevice, daoSession); //the addition happens here
|
|
Intent refreshIntent = new Intent(DeviceManager.ACTION_REFRESH_DEVICELIST);
|
|
LocalBroadcastManager.getInstance(context).sendBroadcast(refreshIntent);
|
|
GB.toast(context, "Added test device: " + deviceType.name(), Toast.LENGTH_SHORT, GB.INFO);
|
|
|
|
} catch (
|
|
Exception e) {
|
|
GB.log("Error accessing database", GB.ERROR, e);
|
|
}
|
|
}
|
|
|
|
private String randomMac() {
|
|
Random random = new Random();
|
|
String separator = ":";
|
|
String[] mac = {
|
|
String.format("%02x", random.nextInt(0xff)),
|
|
String.format("%02x", random.nextInt(0xff)),
|
|
String.format("%02x", random.nextInt(0xff)),
|
|
String.format("%02x", random.nextInt(0xff)),
|
|
String.format("%02x", random.nextInt(0xff)),
|
|
String.format("%02x", random.nextInt(0xff))
|
|
};
|
|
return TextUtils.join(separator, mac).toUpperCase(Locale.ROOT);
|
|
}
|
|
|
|
public static LinkedHashMap getAllSupportedDevices(Context appContext) {
|
|
LinkedHashMap<String, Pair<Long, Integer>> newMap = new LinkedHashMap<>(1);
|
|
GBApplication app = (GBApplication) appContext;
|
|
for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) {
|
|
DeviceType deviceType = coordinator.getDeviceType();
|
|
int icon = deviceType.getIcon();
|
|
String name = app.getString(deviceType.getName()) + " (" + coordinator.getManufacturer() + ")";
|
|
long deviceId = deviceType.getKey();
|
|
newMap.put(name, new Pair(deviceId, icon));
|
|
}
|
|
TreeMap <String, Pair<Long, Integer>> sortedMap = new TreeMap<>(newMap);
|
|
newMap = new LinkedHashMap<>(1);
|
|
newMap.put(app.getString(R.string.widget_settings_select_device_title), new Pair(SELECT_DEVICE, R.drawable.ic_device_unknown));
|
|
newMap.putAll(sortedMap);
|
|
|
|
return newMap;
|
|
}
|
|
|
|
public class CustomOnDeviceSelectedListener implements AdapterView.OnItemSelectedListener {
|
|
|
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
|
SpinnerWithIconItem selectedItem = (SpinnerWithIconItem) parent.getItemAtPosition(pos);
|
|
selectedTestDeviceKey = selectedItem.getId();
|
|
}
|
|
|
|
@Override
|
|
public void onNothingSelected(AdapterView<?> arg0) {
|
|
// TODO Auto-generated method stub
|
|
}
|
|
|
|
}
|
|
|
|
}
|