mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-29 03:55:49 +01:00
Add a device specific settings activity, currently used for setting the auth key for Amazfit Bip/Cor
When connecting to a new device, a random key gets generated, which can be looked up from the device specific settings (accessible via the gear icon in the device card in the main activity). Old devices keep their 0123456789@ABCDE key, they have to be re-paired to change that. During pairing, long-pressing the device candidate in the discovery activity will also start the device specific settings activity, where the auth key can be set manually priror to pairing. This is usefull to keep the ability to pair one device with multiple android devices. Fixes #1308
This commit is contained in:
parent
2a8fe5c3a0
commit
db48707764
@ -67,6 +67,7 @@ dependencies {
|
||||
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "androidx.appcompat:appcompat:1.0.2"
|
||||
implementation "androidx.preference:preference:1.0.0"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation "androidx.recyclerview:recyclerview:1.0.0"
|
||||
implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||
|
@ -406,7 +406,10 @@
|
||||
<activity
|
||||
android:name=".activities.ConfigureAlarms"
|
||||
android:label="@string/title_activity_set_alarm"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
android:parentActivityName=".activities.ControlCenterv2" />
|
||||
<activity
|
||||
android:name=".activities.devicesettings.DeviceSettingsActivity"
|
||||
android:label="@string/title_activity_device_specific_settings" />
|
||||
<activity
|
||||
android:name=".activities.AlarmDetails"
|
||||
android:label="@string/title_activity_alarm_details"
|
||||
|
@ -49,6 +49,8 @@ import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -56,9 +58,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.DeviceCandidateAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@ -69,7 +71,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
|
||||
public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener {
|
||||
public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
|
||||
private static final long SCAN_DURATION = 60000; // 60s
|
||||
|
||||
@ -279,6 +281,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
cadidateListAdapter = new DeviceCandidateAdapter(this, deviceCandidates);
|
||||
deviceCandidatesView.setAdapter(cadidateListAdapter);
|
||||
deviceCandidatesView.setOnItemClickListener(this);
|
||||
deviceCandidatesView.setOnItemLongClickListener(this);
|
||||
|
||||
IntentFilter bluetoothIntents = new IntentFilter();
|
||||
bluetoothIntents.addAction(BluetoothDevice.ACTION_FOUND);
|
||||
@ -577,6 +580,27 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||
GBDeviceCandidate deviceCandidate = deviceCandidates.get(position);
|
||||
if (deviceCandidate == null) {
|
||||
LOG.error("Device candidate clicked, but item not found");
|
||||
return true;
|
||||
}
|
||||
|
||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate);
|
||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
||||
if (!coordinator.supportsDeviceSpecificSettings(device)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Intent startIntent;
|
||||
startIntent = new Intent(this, DeviceSettingsActivity.class);
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||
startActivity(startIntent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
GBDeviceCandidate deviceCandidate = deviceCandidates.get(position);
|
||||
|
@ -0,0 +1,64 @@
|
||||
/* Copyright (C) 2018-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
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.devicesettings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
|
||||
|
||||
public class DeviceSettingsActivity extends AbstractGBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DeviceSettingsActivity.class);
|
||||
|
||||
private GBDevice device;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_device_settings);
|
||||
|
||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
|
||||
PreferenceFragmentCompat fragment = coordinator.getDeviceSpecificSettingsFragment(device);
|
||||
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings_container, fragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
|
||||
public class DoesNotExistSettingsFragment extends PreferenceFragmentCompat {
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
abstract public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
void setSettingsFileSuffix(String settingsFileSuffix) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString("settingsFileSuffix", settingsFileSuffix);
|
||||
setArguments(args);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
String settingsFileSuffix = getArguments().getString("settingsFileSuffix", "_bug");
|
||||
getPreferenceManager().setSharedPreferencesName("devicesettings_" + settingsFileSuffix);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
public class HuamiSettingsFragment extends DeviceSpecificSettingsFragment {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
setPreferencesFromResource(R.xml.devicesettings_huami, rootKey);
|
||||
}
|
||||
|
||||
public static HuamiSettingsFragment newInstance(String settingsFileSuffix) {
|
||||
HuamiSettingsFragment fragment = new HuamiSettingsFragment();
|
||||
fragment.setSettingsFileSuffix(settingsFileSuffix);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.ActivitySummariesActivity
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9CalibrationActivity;
|
||||
@ -152,6 +153,18 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
|
||||
//device specific settings
|
||||
holder.deviceSpecificSettingsView.setVisibility(coordinator.supportsDeviceSpecificSettings(device) ? View.VISIBLE : View.GONE);
|
||||
holder.deviceSpecificSettingsView.setOnClickListener(new View.OnClickListener()
|
||||
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent startIntent;
|
||||
startIntent = new Intent(context, DeviceSettingsActivity.class);
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||
context.startActivity(startIntent);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//fetch activity data
|
||||
holder.fetchActivityDataBox.setVisibility((device.isInitialized() && coordinator.supportsActivityDataFetching()) ? View.VISIBLE : View.GONE);
|
||||
|
@ -31,6 +31,7 @@ import androidx.annotation.NonNull;
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
@ -157,4 +158,10 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
||||
public boolean supportsDeviceSpecificSettings(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsFragment getDeviceSpecificSettingsFragment(GBDevice device) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import java.util.Collection;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
@ -283,4 +284,9 @@ public interface DeviceCoordinator {
|
||||
* Indicates whether the device supports device specific settings (not per device type or family, but unique per device).
|
||||
*/
|
||||
boolean supportsDeviceSpecificSettings(GBDevice device);
|
||||
|
||||
/**
|
||||
* Creates and returns a device specific settings fragment, or null if there is none
|
||||
*/
|
||||
DeviceSpecificSettingsFragment getDeviceSpecificSettingsFragment(GBDevice device);
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.HuamiSettingsFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@ -74,4 +77,15 @@ public class AmazfitBipCoordinator extends HuamiCoordinator {
|
||||
public boolean supportsWeather() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsDeviceSpecificSettings(GBDevice device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsFragment getDeviceSpecificSettingsFragment(GBDevice device) {
|
||||
return HuamiSettingsFragment.newInstance(device.getAddress());
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.HuamiSettingsFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@ -77,4 +80,14 @@ public class AmazfitCorCoordinator extends HuamiCoordinator {
|
||||
|
||||
@Override
|
||||
public boolean supportsUnicodeEmojis() { return true; }
|
||||
|
||||
@Override
|
||||
public boolean supportsDeviceSpecificSettings(GBDevice device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsFragment getDeviceSpecificSettingsFragment(GBDevice device) {
|
||||
return HuamiSettingsFragment.newInstance(device.getAddress());
|
||||
}
|
||||
}
|
||||
|
@ -23,16 +23,19 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
@ -116,7 +119,7 @@ public class MiBandPairingActivity extends AbstractGBActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_mi_band_pairing);
|
||||
|
||||
message = (TextView) findViewById(R.id.miband_pair_message);
|
||||
message = findViewById(R.id.miband_pair_message);
|
||||
Intent intent = getIntent();
|
||||
deviceCandidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
||||
if (deviceCandidate == null && savedInstanceState != null) {
|
||||
@ -129,6 +132,21 @@ public class MiBandPairingActivity extends AbstractGBActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate);
|
||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
||||
|
||||
if (coordinator.supportsDeviceSpecificSettings(device)) {
|
||||
SharedPreferences sharedPrefs = getSharedPreferences("devicesettings_" + device.getAddress(), Context.MODE_PRIVATE);
|
||||
String authKey = sharedPrefs.getString("authkey", null);
|
||||
if (authKey == null || authKey.isEmpty()) {
|
||||
SharedPreferences.Editor editor = sharedPrefs.edit();
|
||||
|
||||
String randomAuthkey = RandomStringUtils.random(16, true, true);
|
||||
editor.putString("authkey", randomAuthkey);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
if (!MiBandCoordinator.hasValidUserInfo()) {
|
||||
Intent userSettingsIntent = new Intent(this, MiBandPreferencesActivity.class);
|
||||
startActivityForResult(userSettingsIntent, REQ_CODE_USER_SETTINGS, null);
|
||||
|
@ -18,6 +18,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -79,7 +81,15 @@ public class InitOperation extends AbstractBTLEOperation<HuamiSupport> {
|
||||
}
|
||||
|
||||
private byte[] getSecretKey() {
|
||||
return new byte[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45};
|
||||
byte[] authKeyBytes = new byte[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45};
|
||||
|
||||
SharedPreferences preferences = getContext().getSharedPreferences("devicesettings_" + getDevice().getAddress(),Context.MODE_PRIVATE);
|
||||
String authKey = preferences.getString("authkey", null);
|
||||
if (authKey != null && !authKey.isEmpty()) {
|
||||
byte[] srcBytes = authKey.getBytes();
|
||||
System.arraycopy(srcBytes, 0, authKeyBytes, 0, Math.min(srcBytes.length,16));
|
||||
}
|
||||
return authKeyBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -219,6 +219,11 @@
|
||||
<string name="zetime_signaling_beep_twice">Beep twice</string>
|
||||
<string name="zetime_signaling_vibrate_beep_once">Vibrate and beep once</string>
|
||||
|
||||
<!-- Device specific settings -->
|
||||
<string name="title_activity_device_specific_settings">Device specific settings</string>
|
||||
<string name="pref_title_authkey">Auth Key</string>
|
||||
<string name="pref_summary_authkey">Change the auth key to a common key on all your Android devices from which you would like to connect from. The previous default key for all devices is 0123456789@ABCDE</string>
|
||||
|
||||
<!-- Auto export preferences -->
|
||||
<string name="pref_header_auto_export">Auto export</string>
|
||||
<string name="pref_title_auto_export_enabled">Auto export enabled</string>
|
||||
|
11
app/src/main/res/xml/devicesettings_huami.xml
Normal file
11
app/src/main/res/xml/devicesettings_huami.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<EditTextPreference
|
||||
android:key="authkey"
|
||||
android:maxLength="16"
|
||||
android:summary="@string/pref_summary_authkey"
|
||||
android:title="@string/pref_title_authkey" />
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user