mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-23 17:34:28 +01:00
Merge branch 'develop' of https://github.com/NordicSemiconductor/Android-nRF-Toolbox into develop
This commit is contained in:
@@ -27,6 +27,8 @@ public abstract class BatteryManager<T extends BatteryManagerCallbacks> extends
|
|||||||
private final static UUID BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb");
|
private final static UUID BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
private BluetoothGattCharacteristic mBatteryLevelCharacteristic;
|
private BluetoothGattCharacteristic mBatteryLevelCharacteristic;
|
||||||
|
/** Last received Battery Level value. */
|
||||||
|
private Integer mBatteryLevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The manager constructor.
|
* The manager constructor.
|
||||||
@@ -41,10 +43,12 @@ public abstract class BatteryManager<T extends BatteryManagerCallbacks> extends
|
|||||||
protected abstract BatteryManagerGattCallback getGattCallback();
|
protected abstract BatteryManagerGattCallback getGattCallback();
|
||||||
|
|
||||||
public void readBatteryLevelCharacteristic() {
|
public void readBatteryLevelCharacteristic() {
|
||||||
|
if (isConnected()) {
|
||||||
readCharacteristic(mBatteryLevelCharacteristic)
|
readCharacteristic(mBatteryLevelCharacteristic)
|
||||||
.with(new BatteryLevelDataCallback() {
|
.with(new BatteryLevelDataCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
|
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
|
||||||
|
mBatteryLevel = batteryLevel;
|
||||||
mCallbacks.onBatteryLevelChanged(device, batteryLevel);
|
mCallbacks.onBatteryLevelChanged(device, batteryLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,13 +59,16 @@ public abstract class BatteryManager<T extends BatteryManagerCallbacks> extends
|
|||||||
})
|
})
|
||||||
.fail(status -> log(LogContract.Log.Level.WARNING, "Battery Level characteristic not found"));
|
.fail(status -> log(LogContract.Log.Level.WARNING, "Battery Level characteristic not found"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void enableBatteryLevelCharacteristicNotifications() {
|
public void enableBatteryLevelCharacteristicNotifications() {
|
||||||
|
if (isConnected()) {
|
||||||
// If the Battery Level characteristic is null, the request will be ignored
|
// If the Battery Level characteristic is null, the request will be ignored
|
||||||
enableNotifications(mBatteryLevelCharacteristic)
|
enableNotifications(mBatteryLevelCharacteristic)
|
||||||
.with(new BatteryLevelDataCallback() {
|
.with(new BatteryLevelDataCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
|
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
|
||||||
|
mBatteryLevel = batteryLevel;
|
||||||
mCallbacks.onBatteryLevelChanged(device, batteryLevel);
|
mCallbacks.onBatteryLevelChanged(device, batteryLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,11 +80,26 @@ public abstract class BatteryManager<T extends BatteryManagerCallbacks> extends
|
|||||||
.done(() -> log(LogContract.Log.Level.INFO, "Battery Level notifications enabled"))
|
.done(() -> log(LogContract.Log.Level.INFO, "Battery Level notifications enabled"))
|
||||||
.fail(status -> log(LogContract.Log.Level.WARNING, "Battery Level characteristic not found"));
|
.fail(status -> log(LogContract.Log.Level.WARNING, "Battery Level characteristic not found"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
public void disableBatteryLevelCharacteristicNotifications() {
|
public void disableBatteryLevelCharacteristicNotifications() {
|
||||||
|
if (isConnected()) {
|
||||||
disableNotifications(mBatteryLevelCharacteristic)
|
disableNotifications(mBatteryLevelCharacteristic)
|
||||||
.done(() -> log(LogContract.Log.Level.INFO, "Battery Level notifications disabled"));
|
.done(() -> log(LogContract.Log.Level.INFO, "Battery Level notifications disabled"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last received Battery Level value.
|
||||||
|
* The value is set to null when the device disconnects.
|
||||||
|
* @return Battery Level value, in percent.
|
||||||
|
*/
|
||||||
|
public Integer getBatteryLevel() {
|
||||||
|
return mBatteryLevel;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract class BatteryManagerGattCallback extends BleManagerGattCallback {
|
protected abstract class BatteryManagerGattCallback extends BleManagerGattCallback {
|
||||||
|
|
||||||
@@ -99,6 +121,7 @@ public abstract class BatteryManager<T extends BatteryManagerCallbacks> extends
|
|||||||
@Override
|
@Override
|
||||||
protected void onDeviceDisconnected() {
|
protected void onDeviceDisconnected() {
|
||||||
mBatteryLevelCharacteristic = null;
|
mBatteryLevelCharacteristic = null;
|
||||||
|
mBatteryLevel = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,17 +35,20 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import no.nordicsemi.android.nrftoolbox.R;
|
import no.nordicsemi.android.nrftoolbox.R;
|
||||||
import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
|
import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
|
||||||
import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity;
|
import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity;
|
||||||
|
import no.nordicsemi.android.nrftoolbox.proximity.ProximityService;
|
||||||
|
|
||||||
public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMSBinder> implements PopupMenu.OnMenuItemClickListener {
|
public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMSBinder> implements PopupMenu.OnMenuItemClickListener {
|
||||||
private View mControlPanelStd;
|
private View mControlPanelStd;
|
||||||
private View mControlPanelAbort;
|
private View mControlPanelAbort;
|
||||||
private ListView mRecordsListView;
|
private ListView mRecordsListView;
|
||||||
|
private TextView mBatteryLevelView;
|
||||||
private CGMSRecordsAdapter mCgmsRecordsAdapter;
|
private CGMSRecordsAdapter mCgmsRecordsAdapter;
|
||||||
|
|
||||||
private CGMService.CGMSBinder mBinder;
|
private CGMService.CGMSBinder mBinder;
|
||||||
@@ -65,6 +68,7 @@ public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMS
|
|||||||
mRecordsListView = findViewById(R.id.list);
|
mRecordsListView = findViewById(R.id.list);
|
||||||
mControlPanelStd = findViewById(R.id.cgms_control_std);
|
mControlPanelStd = findViewById(R.id.cgms_control_std);
|
||||||
mControlPanelAbort = findViewById(R.id.cgms_control_abort);
|
mControlPanelAbort = findViewById(R.id.cgms_control_abort);
|
||||||
|
mBatteryLevelView = findViewById(R.id.battery);
|
||||||
|
|
||||||
findViewById(R.id.action_last).setOnClickListener(v -> {
|
findViewById(R.id.action_last).setOnClickListener(v -> {
|
||||||
clearRecords();
|
clearRecords();
|
||||||
@@ -166,10 +170,15 @@ public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMS
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onBatteryLevelChanged(final BluetoothDevice device, final int value) {
|
||||||
|
mBatteryLevelView.setText(getString(R.string.battery, value));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeviceDisconnected(final BluetoothDevice device) {
|
public void onDeviceDisconnected(final BluetoothDevice device) {
|
||||||
super.onDeviceDisconnected(device);
|
super.onDeviceDisconnected(device);
|
||||||
setOperationInProgress(false);
|
setOperationInProgress(false);
|
||||||
|
mBatteryLevelView.setText(R.string.not_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -181,6 +190,7 @@ public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMS
|
|||||||
@Override
|
@Override
|
||||||
protected void setDefaultUI() {
|
protected void setDefaultUI() {
|
||||||
clearRecords();
|
clearRecords();
|
||||||
|
mBatteryLevelView.setText(R.string.not_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -217,6 +227,7 @@ public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMS
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive(final Context context, final Intent intent) {
|
public void onReceive(final Context context, final Intent intent) {
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
|
final BluetoothDevice device = intent.getParcelableExtra(ProximityService.EXTRA_DEVICE);
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case CGMService.BROADCAST_NEW_CGMS_VALUE: {
|
case CGMService.BROADCAST_NEW_CGMS_VALUE: {
|
||||||
@@ -237,6 +248,11 @@ public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMS
|
|||||||
// Update GUI
|
// Update GUI
|
||||||
setOperationInProgress(true);
|
setOperationInProgress(true);
|
||||||
break;
|
break;
|
||||||
|
case CGMService.BROADCAST_BATTERY_LEVEL:
|
||||||
|
final int batteryLevel = intent.getIntExtra(CGMService.EXTRA_BATTERY_LEVEL, 0);
|
||||||
|
// Update GUI
|
||||||
|
onBatteryLevelChanged(device, batteryLevel);
|
||||||
|
break;
|
||||||
case CGMService.OPERATION_FAILED:
|
case CGMService.OPERATION_FAILED:
|
||||||
// Update GUI
|
// Update GUI
|
||||||
showToast(R.string.gls_operation_failed);
|
showToast(R.string.gls_operation_failed);
|
||||||
@@ -258,6 +274,7 @@ public class CGMSActivity extends BleProfileServiceReadyActivity<CGMService.CGMS
|
|||||||
intentFilter.addAction(CGMService.OPERATION_NOT_SUPPORTED);
|
intentFilter.addAction(CGMService.OPERATION_NOT_SUPPORTED);
|
||||||
intentFilter.addAction(CGMService.OPERATION_ABORTED);
|
intentFilter.addAction(CGMService.OPERATION_ABORTED);
|
||||||
intentFilter.addAction(CGMService.OPERATION_FAILED);
|
intentFilter.addAction(CGMService.OPERATION_FAILED);
|
||||||
|
intentFilter.addAction(CGMService.BROADCAST_BATTERY_LEVEL);
|
||||||
return intentFilter;
|
return intentFilter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ import no.nordicsemi.android.nrftoolbox.profile.BleProfileService;
|
|||||||
|
|
||||||
public class CGMService extends BleProfileService implements CGMSManagerCallbacks {
|
public class CGMService extends BleProfileService implements CGMSManagerCallbacks {
|
||||||
private static final String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.cgms.ACTION_DISCONNECT";
|
private static final String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.cgms.ACTION_DISCONNECT";
|
||||||
|
|
||||||
|
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
|
||||||
|
public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
|
||||||
|
|
||||||
public static final String BROADCAST_NEW_CGMS_VALUE = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_NEW_CGMS_VALUE";
|
public static final String BROADCAST_NEW_CGMS_VALUE = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_NEW_CGMS_VALUE";
|
||||||
public static final String BROADCAST_DATA_SET_CLEAR = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_DATA_SET_CLEAR";
|
public static final String BROADCAST_DATA_SET_CLEAR = "no.nordicsemi.android.nrftoolbox.cgms.BROADCAST_DATA_SET_CLEAR";
|
||||||
public static final String OPERATION_STARTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_STARTED";
|
public static final String OPERATION_STARTED = "no.nordicsemi.android.nrftoolbox.cgms.OPERATION_STARTED";
|
||||||
@@ -33,9 +37,6 @@ public class CGMService extends BleProfileService implements CGMSManagerCallback
|
|||||||
public static final String EXTRA_CGMS_RECORD = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_CGMS_RECORD";
|
public static final String EXTRA_CGMS_RECORD = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_CGMS_RECORD";
|
||||||
public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_DATA";
|
public static final String EXTRA_DATA = "no.nordicsemi.android.nrftoolbox.cgms.EXTRA_DATA";
|
||||||
|
|
||||||
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
|
|
||||||
public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
|
|
||||||
|
|
||||||
private final static int NOTIFICATION_ID = 229;
|
private final static int NOTIFICATION_ID = 229;
|
||||||
private final static int OPEN_ACTIVITY_REQ = 0;
|
private final static int OPEN_ACTIVITY_REQ = 0;
|
||||||
private final static int DISCONNECT_REQ = 1;
|
private final static int DISCONNECT_REQ = 1;
|
||||||
@@ -277,10 +278,10 @@ public class CGMService extends BleProfileService implements CGMSManagerCallback
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int value) {
|
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
|
||||||
final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
|
final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
|
||||||
broadcast.putExtra(EXTRA_DEVICE, getBluetoothDevice());
|
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||||
broadcast.putExtra(EXTRA_BATTERY_LEVEL, value);
|
broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
|
||||||
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ public class CSCActivity extends BleProfileServiceReadyActivity<CSCService.CSCBi
|
|||||||
mBatteryLevelView.setText(R.string.not_available);
|
mBatteryLevelView.setText(R.string.not_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMeasurementReceived(float speed, float distance, float totalDistance) {
|
private void onMeasurementReceived(final BluetoothDevice device, float speed, float distance, float totalDistance) {
|
||||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
final int unit = Integer.parseInt(preferences.getString(SettingsFragment.SETTINGS_UNIT, String.valueOf(SettingsFragment.SETTINGS_UNIT_DEFAULT)));
|
final int unit = Integer.parseInt(preferences.getString(SettingsFragment.SETTINGS_UNIT, String.valueOf(SettingsFragment.SETTINGS_UNIT_DEFAULT)));
|
||||||
|
|
||||||
@@ -223,12 +223,12 @@ public class CSCActivity extends BleProfileServiceReadyActivity<CSCService.CSCBi
|
|||||||
mSpeedView.setText(String.format(Locale.US, "%.1f", speed));
|
mSpeedView.setText(String.format(Locale.US, "%.1f", speed));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onGearRatioUpdate(final float ratio, final int cadence) {
|
private void onGearRatioUpdate(final BluetoothDevice device, final int cadence, final float ratio) {
|
||||||
mGearRatioView.setText(String.format(Locale.US, "%.1f", ratio));
|
|
||||||
mCadenceView.setText(String.format(Locale.US, "%d", cadence));
|
mCadenceView.setText(String.format(Locale.US, "%d", cadence));
|
||||||
|
mGearRatioView.setText(String.format(Locale.US, "%.1f", ratio));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onBatteryLevelChanged(final int value) {
|
public void onBatteryLevelChanged(final BluetoothDevice device, final int value) {
|
||||||
mBatteryLevelView.setText(getString(R.string.battery, value));
|
mBatteryLevelView.setText(getString(R.string.battery, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,22 +236,23 @@ public class CSCActivity extends BleProfileServiceReadyActivity<CSCService.CSCBi
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive(final Context context, final Intent intent) {
|
public void onReceive(final Context context, final Intent intent) {
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
|
final BluetoothDevice device = intent.getParcelableExtra(CSCService.EXTRA_DEVICE);
|
||||||
|
|
||||||
if (CSCService.BROADCAST_WHEEL_DATA.equals(action)) {
|
if (CSCService.BROADCAST_WHEEL_DATA.equals(action)) {
|
||||||
final float speed = intent.getFloatExtra(CSCService.EXTRA_SPEED, 0.0f); // [m/s]
|
final float speed = intent.getFloatExtra(CSCService.EXTRA_SPEED, 0.0f); // [m/s]
|
||||||
final float distance = intent.getFloatExtra(CSCService.EXTRA_DISTANCE, 0);
|
final float distance = intent.getFloatExtra(CSCService.EXTRA_DISTANCE, 0);
|
||||||
final float totalDistance = intent.getFloatExtra(CSCService.EXTRA_TOTAL_DISTANCE, 0);
|
final float totalDistance = intent.getFloatExtra(CSCService.EXTRA_TOTAL_DISTANCE, 0);
|
||||||
// Update GUI
|
// Update GUI
|
||||||
onMeasurementReceived(speed, distance, totalDistance);
|
onMeasurementReceived(device, speed, distance, totalDistance);
|
||||||
} else if (CSCService.BROADCAST_CRANK_DATA.equals(action)) {
|
} else if (CSCService.BROADCAST_CRANK_DATA.equals(action)) {
|
||||||
final float ratio = intent.getFloatExtra(CSCService.EXTRA_GEAR_RATIO, 0);
|
final float ratio = intent.getFloatExtra(CSCService.EXTRA_GEAR_RATIO, 0);
|
||||||
final int cadence = intent.getIntExtra(CSCService.EXTRA_CADENCE, 0);
|
final int cadence = intent.getIntExtra(CSCService.EXTRA_CADENCE, 0);
|
||||||
// Update GUI
|
// Update GUI
|
||||||
onGearRatioUpdate(ratio, cadence);
|
onGearRatioUpdate(device, cadence, ratio);
|
||||||
} else if (CSCService.BROADCAST_BATTERY_LEVEL.equals(action)) {
|
} else if (CSCService.BROADCAST_BATTERY_LEVEL.equals(action)) {
|
||||||
final int batteryLevel = intent.getIntExtra(CSCService.EXTRA_BATTERY_LEVEL, 0);
|
final int batteryLevel = intent.getIntExtra(CSCService.EXTRA_BATTERY_LEVEL, 0);
|
||||||
// Update GUI
|
// Update GUI
|
||||||
onBatteryLevelChanged(batteryLevel);
|
onBatteryLevelChanged(device, batteryLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -261,12 +261,6 @@ public abstract class BleMulticonnectProfileService extends Service implements B
|
|||||||
|
|
||||||
if (!mActivityIsChangingConfiguration) {
|
if (!mActivityIsChangingConfiguration) {
|
||||||
onRebind();
|
onRebind();
|
||||||
// This method will read the Battery Level value from each connected device, if possible and then try to enable battery notifications (if it has NOTIFY property).
|
|
||||||
// If the Battery Level characteristic has only the NOTIFY property, it will only try to enable notifications.
|
|
||||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values()) {
|
|
||||||
if (manager.isConnected())
|
|
||||||
manager.readBatteryLevel();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,12 +280,6 @@ public abstract class BleMulticonnectProfileService extends Service implements B
|
|||||||
if (!mActivityIsChangingConfiguration) {
|
if (!mActivityIsChangingConfiguration) {
|
||||||
if (!mManagedDevices.isEmpty()) {
|
if (!mManagedDevices.isEmpty()) {
|
||||||
onUnbind();
|
onUnbind();
|
||||||
// When we are connected, but the application is not open, we are not really interested in battery level notifications.
|
|
||||||
// But we will still be receiving other values, if enabled.
|
|
||||||
for (final BleManager<BleManagerCallbacks> manager : mBleManagers.values()) {
|
|
||||||
if (manager.isConnected())
|
|
||||||
manager.disableBatteryLevelNotifications();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// The last activity has disconnected from the service and there are no devices to manage. The service may be stopped.
|
// The last activity has disconnected from the service and there are no devices to manage. The service may be stopped.
|
||||||
stopSelf();
|
stopSelf();
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ public class DeviceAdapter extends RecyclerView.Adapter<DeviceAdapter.ViewHolder
|
|||||||
actionButton.setImageResource(on ? R.drawable.ic_stat_notify_proximity_silent : R.drawable.ic_stat_notify_proximity_find);
|
actionButton.setImageResource(on ? R.drawable.ic_stat_notify_proximity_silent : R.drawable.ic_stat_notify_proximity_find);
|
||||||
actionButton.setVisibility(state == BluetoothGatt.STATE_CONNECTED ? View.VISIBLE : View.GONE);
|
actionButton.setVisibility(state == BluetoothGatt.STATE_CONNECTED ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
final int batteryValue = mService.getBatteryLevel(device);
|
final Integer batteryValue = mService.getBatteryLevel(device);
|
||||||
if (batteryValue >= 0) {
|
if (batteryValue != null) {
|
||||||
batteryView.getCompoundDrawables()[0 /*left*/].setLevel(batteryValue);
|
batteryView.getCompoundDrawables()[0 /*left*/].setLevel(batteryValue);
|
||||||
batteryView.setVisibility(View.VISIBLE);
|
batteryView.setVisibility(View.VISIBLE);
|
||||||
batteryView.setText(batteryView.getResources().getString(R.string.battery, batteryValue));
|
batteryView.setText(batteryView.getResources().getString(R.string.battery, batteryValue));
|
||||||
|
|||||||
@@ -23,7 +23,12 @@ package no.nordicsemi.android.nrftoolbox.proximity;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
|
||||||
@@ -50,6 +55,17 @@ public class ProximityActivity extends BleMulticonnectProfileServiceReadyActivit
|
|||||||
setGUI();
|
setGUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onInitialize(final Bundle savedInstanceState) {
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, makeIntentFilter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
private void setGUI() {
|
private void setGUI() {
|
||||||
final RecyclerView recyclerView = mDevicesView = findViewById(android.R.id.list);
|
final RecyclerView recyclerView = mDevicesView = findViewById(android.R.id.list);
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||||
@@ -133,12 +149,12 @@ public class ProximityActivity extends BleMulticonnectProfileServiceReadyActivit
|
|||||||
showLinklossDialog(device.getName());
|
showLinklossDialog(device.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void onBatteryLevelChanged(final BluetoothDevice device, final int batteryLevel) {
|
||||||
public void onBatteryValueReceived(final BluetoothDevice device, final int value) {
|
|
||||||
if (mAdapter != null)
|
if (mAdapter != null)
|
||||||
mAdapter.onBatteryValueReceived(device); // Value will be obtained from the service
|
mAdapter.onBatteryValueReceived(device); // Value will be obtained from the service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void showLinklossDialog(final String name) {
|
private void showLinklossDialog(final String name) {
|
||||||
try {
|
try {
|
||||||
final LinklossFragment dialog = LinklossFragment.getInstance(name);
|
final LinklossFragment dialog = LinklossFragment.getInstance(name);
|
||||||
@@ -147,4 +163,23 @@ public class ProximityActivity extends BleMulticonnectProfileServiceReadyActivit
|
|||||||
// the activity must have been destroyed
|
// the activity must have been destroyed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(final Context context, final Intent intent) {
|
||||||
|
final String action = intent.getAction();
|
||||||
|
final BluetoothDevice device = intent.getParcelableExtra(ProximityService.EXTRA_DEVICE);
|
||||||
|
|
||||||
|
if (ProximityService.BROADCAST_BATTERY_LEVEL.equals(action)) {
|
||||||
|
final int batteryLevel = intent.getIntExtra(ProximityService.EXTRA_BATTERY_LEVEL, 0);
|
||||||
|
// Update GUI
|
||||||
|
onBatteryLevelChanged(device, batteryLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static IntentFilter makeIntentFilter() {
|
||||||
|
final IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(ProximityService.BROADCAST_BATTERY_LEVEL);
|
||||||
|
return intentFilter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,24 +28,19 @@ import android.bluetooth.BluetoothGattService;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import no.nordicsemi.android.ble.BleManager;
|
|
||||||
import no.nordicsemi.android.ble.Request;
|
|
||||||
import no.nordicsemi.android.log.LogContract;
|
import no.nordicsemi.android.log.LogContract;
|
||||||
import no.nordicsemi.android.log.Logger;
|
import no.nordicsemi.android.nrftoolbox.battery.BatteryManager;
|
||||||
import no.nordicsemi.android.nrftoolbox.parser.AlertLevelParser;
|
import no.nordicsemi.android.nrftoolbox.parser.AlertLevelParser;
|
||||||
import no.nordicsemi.android.nrftoolbox.utility.DebugLogger;
|
|
||||||
|
|
||||||
public class ProximityManager extends BleManager<ProximityManagerCallbacks> {
|
class ProximityManager extends BatteryManager<ProximityManagerCallbacks> {
|
||||||
private final String TAG = "ProximityManager";
|
private final String TAG = "ProximityManager";
|
||||||
|
|
||||||
/** Immediate Alert service UUID */
|
/** Immediate Alert service UUID */
|
||||||
public final static UUID IMMEDIATE_ALERT_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
|
private final static UUID IMMEDIATE_ALERT_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
|
||||||
/** Linkloss service UUID */
|
/** Linkloss service UUID */
|
||||||
public final static UUID LINKLOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb");
|
final static UUID LINKLOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb");
|
||||||
/** Alert Level characteristic UUID */
|
/** Alert Level characteristic UUID */
|
||||||
private static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-0000-1000-8000-00805f9b34fb");
|
private static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
@@ -55,9 +50,8 @@ public class ProximityManager extends BleManager<ProximityManagerCallbacks> {
|
|||||||
|
|
||||||
private BluetoothGattCharacteristic mAlertLevelCharacteristic, mLinklossCharacteristic;
|
private BluetoothGattCharacteristic mAlertLevelCharacteristic, mLinklossCharacteristic;
|
||||||
private boolean mAlertOn;
|
private boolean mAlertOn;
|
||||||
private int mBatteryLevel;
|
|
||||||
|
|
||||||
public ProximityManager(final Context context) {
|
ProximityManager(final Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,17 +61,18 @@ public class ProximityManager extends BleManager<ProximityManagerCallbacks> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BleManagerGattCallback getGattCallback() {
|
protected BatteryManagerGattCallback getGattCallback() {
|
||||||
return mGattCallback;
|
return mGattCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc
|
* BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc
|
||||||
*/
|
*/
|
||||||
private final BleManagerGattCallback mGattCallback = new BleManagerGattCallback() {
|
private final BatteryManagerGattCallback mGattCallback = new BatteryManagerGattCallback() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initialize(@NonNull final BluetoothDevice device) {
|
protected void initialize(@NonNull final BluetoothDevice device) {
|
||||||
|
super.initialize(device);
|
||||||
writeCharacteristic(mLinklossCharacteristic, HIGH_ALERT);
|
writeCharacteristic(mLinklossCharacteristic, HIGH_ALERT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +87,7 @@ public class ProximityManager extends BleManager<ProximityManagerCallbacks> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isOptionalServiceSupported(@NonNull final BluetoothGatt gatt) {
|
protected boolean isOptionalServiceSupported(@NonNull final BluetoothGatt gatt) {
|
||||||
|
super.isOptionalServiceSupported(gatt);
|
||||||
final BluetoothGattService iaService = gatt.getService(IMMEDIATE_ALERT_SERVICE_UUID);
|
final BluetoothGattService iaService = gatt.getService(IMMEDIATE_ALERT_SERVICE_UUID);
|
||||||
if (iaService != null) {
|
if (iaService != null) {
|
||||||
mAlertLevelCharacteristic = iaService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID);
|
mAlertLevelCharacteristic = iaService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID);
|
||||||
@@ -101,6 +97,7 @@ public class ProximityManager extends BleManager<ProximityManagerCallbacks> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDeviceDisconnected() {
|
protected void onDeviceDisconnected() {
|
||||||
|
super.onDeviceDisconnected();
|
||||||
mAlertLevelCharacteristic = null;
|
mAlertLevelCharacteristic = null;
|
||||||
mLinklossCharacteristic = null;
|
mLinklossCharacteristic = null;
|
||||||
// Reset the alert flag
|
// Reset the alert flag
|
||||||
@@ -140,12 +137,4 @@ public class ProximityManager extends BleManager<ProximityManagerCallbacks> {
|
|||||||
boolean isAlertEnabled() {
|
boolean isAlertEnabled() {
|
||||||
return mAlertOn;
|
return mAlertOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the last obtained Battery Level value in percent.
|
|
||||||
* @return battery level value
|
|
||||||
*/
|
|
||||||
int getBatteryLevel() {
|
|
||||||
return mBatteryLevel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
package no.nordicsemi.android.nrftoolbox.proximity;
|
package no.nordicsemi.android.nrftoolbox.proximity;
|
||||||
|
|
||||||
import no.nordicsemi.android.ble.BleManagerCallbacks;
|
import no.nordicsemi.android.nrftoolbox.battery.BatteryManagerCallbacks;
|
||||||
|
|
||||||
public interface ProximityManagerCallbacks extends BleManagerCallbacks {
|
interface ProximityManagerCallbacks extends BatteryManagerCallbacks {
|
||||||
// No additional methods
|
// No additional methods
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ import no.nordicsemi.android.nrftoolbox.parser.AlertLevelParser;
|
|||||||
import no.nordicsemi.android.nrftoolbox.profile.multiconnect.IDeviceLogger;
|
import no.nordicsemi.android.nrftoolbox.profile.multiconnect.IDeviceLogger;
|
||||||
import no.nordicsemi.android.nrftoolbox.utility.ParserUtils;
|
import no.nordicsemi.android.nrftoolbox.utility.ParserUtils;
|
||||||
|
|
||||||
public class ProximityServerManager {
|
class ProximityServerManager {
|
||||||
private final String TAG = "ProximityServerManager";
|
private final String TAG = "ProximityServerManager";
|
||||||
|
|
||||||
/** Immediate Alert service UUID */
|
/** Immediate Alert service UUID */
|
||||||
public final static UUID IMMEDIATE_ALERT_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
|
final static UUID IMMEDIATE_ALERT_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb");
|
||||||
/** Linkloss service UUID */
|
/** Linkloss service UUID */
|
||||||
public final static UUID LINKLOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb");
|
final static UUID LINKLOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb");
|
||||||
/** Alert Level characteristic UUID */
|
/** Alert Level characteristic UUID */
|
||||||
private static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-0000-1000-8000-00805f9b34fb");
|
private static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ public class ProximityServerManager {
|
|||||||
void onGattServerFailed(final int error);
|
void onGattServerFailed(final int error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProximityServerManager(final ProximityServerManagerCallbacks callbacks) {
|
ProximityServerManager(final ProximityServerManagerCallbacks callbacks) {
|
||||||
mHandler = new Handler();
|
mHandler = new Handler();
|
||||||
mCallbacks = callbacks;
|
mCallbacks = callbacks;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ package no.nordicsemi.android.nrftoolbox.proximity;
|
|||||||
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
|
||||||
import no.nordicsemi.android.ble.BleManagerCallbacks;
|
public interface ProximityServerManagerCallbacks {
|
||||||
|
|
||||||
public interface ProximityServerManagerCallbacks extends BleManagerCallbacks {
|
|
||||||
void onAlarmTriggered(final BluetoothDevice device);
|
void onAlarmTriggered(final BluetoothDevice device);
|
||||||
|
|
||||||
void onAlarmStopped(final BluetoothDevice device);
|
void onAlarmStopped(final BluetoothDevice device);
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ import android.media.AudioManager;
|
|||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.app.NotificationManagerCompat;
|
import android.support.v4.app.NotificationManagerCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -45,7 +47,6 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import no.nordicsemi.android.ble.BleManager;
|
import no.nordicsemi.android.ble.BleManager;
|
||||||
import no.nordicsemi.android.ble.BleManagerCallbacks;
|
|
||||||
import no.nordicsemi.android.log.LogContract;
|
import no.nordicsemi.android.log.LogContract;
|
||||||
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
|
import no.nordicsemi.android.nrftoolbox.FeaturesActivity;
|
||||||
import no.nordicsemi.android.nrftoolbox.R;
|
import no.nordicsemi.android.nrftoolbox.R;
|
||||||
@@ -56,10 +57,12 @@ public class ProximityService extends BleMulticonnectProfileService implements P
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static final String TAG = "ProximityService";
|
private static final String TAG = "ProximityService";
|
||||||
|
|
||||||
|
public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
|
||||||
|
public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
|
||||||
|
|
||||||
private final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.proximity.ACTION_DISCONNECT";
|
private final static String ACTION_DISCONNECT = "no.nordicsemi.android.nrftoolbox.proximity.ACTION_DISCONNECT";
|
||||||
private final static String ACTION_FIND = "no.nordicsemi.android.nrftoolbox.proximity.ACTION_FIND";
|
private final static String ACTION_FIND = "no.nordicsemi.android.nrftoolbox.proximity.ACTION_FIND";
|
||||||
private final static String ACTION_SILENT = "no.nordicsemi.android.nrftoolbox.proximity.ACTION_SILENT";
|
private final static String ACTION_SILENT = "no.nordicsemi.android.nrftoolbox.proximity.ACTION_SILENT";
|
||||||
private final static String EXTRA_DEVICE = "no.nordicsemi.android.nrftoolbox.proximity.EXTRA_DEVICE";
|
|
||||||
|
|
||||||
private final static String PROXIMITY_GROUP_ID = "proximity_connected_tags";
|
private final static String PROXIMITY_GROUP_ID = "proximity_connected_tags";
|
||||||
private final static int NOTIFICATION_ID = 1000;
|
private final static int NOTIFICATION_ID = 1000;
|
||||||
@@ -109,9 +112,10 @@ public class ProximityService extends BleMulticonnectProfileService implements P
|
|||||||
/**
|
/**
|
||||||
* Returns the last received battery level value.
|
* Returns the last received battery level value.
|
||||||
* @param device the device of which battery level should be returned
|
* @param device the device of which battery level should be returned
|
||||||
* @return battery value or -1 if no value was received or Battery Level characteristic was not found
|
* @return battery value or null if no value was received or Battery Level characteristic was not found,
|
||||||
|
* or the device is disconnected
|
||||||
*/
|
*/
|
||||||
public int getBatteryLevel(final BluetoothDevice device) {
|
public Integer getBatteryLevel(final BluetoothDevice device) {
|
||||||
final ProximityManager manager = (ProximityManager) getBleManager(device);
|
final ProximityManager manager = (ProximityManager) getBleManager(device);
|
||||||
return manager.getBatteryLevel();
|
return manager.getBatteryLevel();
|
||||||
}
|
}
|
||||||
@@ -232,10 +236,24 @@ public class ProximityService extends BleMulticonnectProfileService implements P
|
|||||||
protected void onRebind() {
|
protected void onRebind() {
|
||||||
// When the activity rebinds to the service, remove the notification
|
// When the activity rebinds to the service, remove the notification
|
||||||
cancelNotifications();
|
cancelNotifications();
|
||||||
|
|
||||||
|
// This method will read the Battery Level value from each connected device, if possible and then try to enable battery notifications (if it has NOTIFY property).
|
||||||
|
// If the Battery Level characteristic has only the NOTIFY property, it will only try to enable notifications.
|
||||||
|
for (final BluetoothDevice device : getManagedDevices()) {
|
||||||
|
final ProximityManager manager = (ProximityManager) getBleManager(device);
|
||||||
|
manager.readBatteryLevelCharacteristic();
|
||||||
|
manager.enableBatteryLevelCharacteristicNotifications();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUnbind() {
|
public void onUnbind() {
|
||||||
|
// When we are connected, but the application is not open, we are not really interested in battery level notifications.
|
||||||
|
// But we will still be receiving other values, if enabled.
|
||||||
|
for (final BluetoothDevice device : getManagedDevices()) {
|
||||||
|
final ProximityManager manager = (ProximityManager) getBleManager(device);
|
||||||
|
manager.disableBatteryLevelCharacteristicNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
createBackgroundNotification();
|
createBackgroundNotification();
|
||||||
}
|
}
|
||||||
@@ -292,6 +310,14 @@ public class ProximityService extends BleMulticonnectProfileService implements P
|
|||||||
stopAlarm(device);
|
stopAlarm(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBatteryLevelChanged(@NonNull final BluetoothDevice device, final int batteryLevel) {
|
||||||
|
final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
|
||||||
|
broadcast.putExtra(EXTRA_DEVICE, device);
|
||||||
|
broadcast.putExtra(EXTRA_BATTERY_LEVEL, batteryLevel);
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
|
||||||
|
}
|
||||||
|
|
||||||
private void createBackgroundNotification() {
|
private void createBackgroundNotification() {
|
||||||
final List<BluetoothDevice> connectedDevices = getConnectedDevices();
|
final List<BluetoothDevice> connectedDevices = getConnectedDevices();
|
||||||
for (final BluetoothDevice device : connectedDevices) {
|
for (final BluetoothDevice device : connectedDevices) {
|
||||||
|
|||||||
Reference in New Issue
Block a user