1
0
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:
Andreas Shimokawa 2019-04-21 21:18:08 +02:00
parent 2a8fe5c3a0
commit db48707764
15 changed files with 241 additions and 6 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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);

View File

@ -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) {
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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

View File

@ -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>

View 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>