/*
HeartRateDeviceSettingsFragment
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.heartrate.fragment;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
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.ServiceConnection;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import org.deviceconnect.android.deviceplugin.heartrate.HeartRateDeviceService;
import org.deviceconnect.android.deviceplugin.heartrate.HeartRateManager;
import org.deviceconnect.android.deviceplugin.heartrate.R;
import org.deviceconnect.android.deviceplugin.heartrate.ble.BleUtils;
import org.deviceconnect.android.deviceplugin.heartrate.data.HeartRateDevice;
import org.deviceconnect.android.deviceplugin.heartrate.fragment.dialog.ErrorDialogFragment;
import org.deviceconnect.android.deviceplugin.heartrate.fragment.dialog.ProgressDialogFragment;
import org.deviceconnect.android.message.DConnectMessageService;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static org.deviceconnect.android.deviceplugin.heartrate.HeartRateManager.OnHeartRateDiscoveryListener;
/**
* This fragment do setting of the connection to the ble device.
*
* @author NTT DOCOMO, INC.
*/
public class HeartRateDeviceSettingsFragment extends Fragment {
private HeartRateManager mHeartRateManager;
private boolean mBound;
/**
* Adapter.
*/
private DeviceAdapter mDeviceAdapter;
/**
* Error Dialog.
*/
private ErrorDialogFragment mErrorDialogFragment;
/**
* Progress Dialog.
*/
private ProgressDialogFragment mProgressDialogFragment;
/**
* Handler.
*/
private Handler mHandler = new Handler();
/**
* Bluetooth device list view.
*/
private ListView mListView;
/**
* footer view.
*/
private View mFooterView;
/**
* Received a event that Bluetooth has been changed.
*/
private final BroadcastReceiver mSensorReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_ON ){
addFooterView();
getManager().startScanBle();
} else if (state == BluetoothAdapter.STATE_OFF) {
addFooterView();
getManager().stopScanBle();
}
}
}
};
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
mDeviceAdapter = new DeviceAdapter(getActivity(), createDeviceContainers());
mFooterView = inflater.inflate(R.layout.item_heart_rate_searching, null);
View rootView = inflater.inflate(R.layout.fragment_heart_rate_device_settings, null);
mListView = (ListView) rootView.findViewById(R.id.device_list_view);
mListView.setAdapter(mDeviceAdapter);
mListView.setItemsCanFocus(true);
return rootView;
}
@Override
public void onResume() {
super.onResume();
bindMessageService();
registerBluetoothFilter();
}
@Override
public void onPause() {
dismissProgressDialog();
dismissErrorDialog();
unregisterBluetoothFilter();
HeartRateManager mgr = getManager();
if (mgr != null) {
mgr.removeOnHeartRateDiscoveryListener(mEvtListener);
mgr.stopScanBle();
}
unbindMessageService();
super.onPause();
}
private void bindMessageService() {
if (!mBound) {
Intent intent = new Intent(getActivity().getApplicationContext(), HeartRateDeviceService.class);
getActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
}
private void unbindMessageService() {
if (mBound) {
getActivity().unbindService(mConnection);
mBound = false;
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
DConnectMessageService.LocalBinder binder = (DConnectMessageService.LocalBinder) service;
HeartRateDeviceService hrService = (HeartRateDeviceService) binder.getMessageService();
mHeartRateManager = hrService.getManager();
startScanBle();
mBound = true;
}
@Override
public void onServiceDisconnected(final ComponentName name) {
mBound = false;
}
};
private void startScanBle() {
HeartRateManager mgr = getManager();
if (mgr != null) {
updateView(null);
mgr.addOnHeartRateDiscoveryListener(mEvtListener);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mgr.startScanBle();
} else {
if (BleUtils.isBLEPermission(getActivity())) {
mgr.startScanBle();
}
}
addFooterView();
}
}
/**
* Added the view at ListView.
*/
private void addFooterView() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
LayoutInflater inflater = getActivity().getLayoutInflater();
if (mFooterView != null) {
mListView.removeFooterView(mFooterView);
}
if (!BleUtils.isBLEPermission(getActivity())) {
mFooterView = inflater.inflate(R.layout.item_heart_rate_error, null);
TextView textView = (TextView) mFooterView.findViewById(R.id.error_message);
textView.setText(getString(R.string.heart_rate_setting_dialog_error_permission));
} else if (getManager().isEnabledBle()) {
mFooterView = inflater.inflate(R.layout.item_heart_rate_searching, null);
} else {
mFooterView = inflater.inflate(R.layout.item_heart_rate_error, null);
TextView textView = (TextView) mFooterView.findViewById(R.id.error_message);
textView.setText(getString(R.string.heart_rate_setting_dialog_disable_bluetooth));
mDeviceAdapter.clear();
mDeviceAdapter.addAll(createDeviceContainers());
mDeviceAdapter.notifyDataSetChanged();
}
mListView.addFooterView(mFooterView);
}
});
}
/**
* Register a BroadcastReceiver of Bluetooth event.
*/
private void registerBluetoothFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
getActivity().registerReceiver(mSensorReceiver, filter, null, mHandler);
}
/**
* Unregister a previously registered BroadcastReceiver.
*/
private void unregisterBluetoothFilter() {
getActivity().unregisterReceiver(mSensorReceiver);
}
/**
* Connect to the BLE device that have heart rate service.
*
* @param device BLE device that have heart rate service.
*/
private void connectDevice(final DeviceContainer device) {
if (getManager().isEnabledBle()) {
getManager().connectBleDevice(device.getAddress());
showProgressDialog(device.getName());
}
}
/**
* Disconnect to the BLE device that have heart rate service.
*
* @param device BLE device that have heart rate service.
*/
private void disconnectDevice(final DeviceContainer device) {
getManager().disconnectBleDevice(device.getAddress());
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
DeviceContainer container = findDeviceContainerByAddress(device.getAddress());
if (container != null) {
container.setRegisterFlag(false);
mDeviceAdapter.notifyDataSetChanged();
}
}
});
}
/**
* Display the dialog of connecting a ble device.
*
* @param name device name
*/
private void showProgressDialog(final String name) {
dismissProgressDialog();
Resources res = getActivity().getResources();
String title = res.getString(R.string.heart_rate_setting_connecting_title);
String message = res.getString(R.string.heart_rate_setting_connecting_message, name);
mProgressDialogFragment = ProgressDialogFragment.newInstance(title, message);
mProgressDialogFragment.show(getFragmentManager(), "dialog");
}
/**
* Dismiss the dialog of connecting a ble device.
*/
private void dismissProgressDialog() {
if (mProgressDialogFragment != null) {
mProgressDialogFragment.dismiss();
mProgressDialogFragment = null;
}
}
/**
* Display the error dialog of not connect device.
*
* @param name device name
*/
private void showErrorDialogNotConnect(final String name) {
Resources res = getActivity().getResources();
String message;
if (name == null) {
message = res.getString(R.string.heart_rate_setting_dialog_error_message,
getString(R.string.heart_rate_setting_default_name));
} else {
message = res.getString(R.string.heart_rate_setting_dialog_error_message, name);
}
showErrorDialog(message);
}
/**
* Display the error dialog for no permissions.
*/
private void showErrorDialogNoPermissions() {
Resources res = getActivity().getResources();
String message = res.getString(R.string.heart_rate_setting_dialog_error_permission);
showErrorDialog(message);
}
/**
* Display the error dialog.
*
* @param message error message
*/
public void showErrorDialog(final String message) {
dismissErrorDialog();
Resources res = getActivity().getResources();
String title = res.getString(R.string.heart_rate_setting_dialog_error_title);
mErrorDialogFragment = ErrorDialogFragment.newInstance(title, message);
mErrorDialogFragment.show(getFragmentManager(), "error_dialog");
mErrorDialogFragment.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mErrorDialogFragment = null;
}
});
}
/**
* Dismiss the error dialog.
*/
private void dismissErrorDialog() {
if (mErrorDialogFragment != null) {
mErrorDialogFragment.dismiss();
mErrorDialogFragment = null;
}
}
/**
* Gets a instance of HeartRateManager.
*
* @return HeartRateManager
*/
private HeartRateManager getManager() {
return mHeartRateManager;
}
private void runOnUiThread(final Runnable run) {
if (getActivity() != null) {
getActivity().runOnUiThread(run);
}
}
private OnHeartRateDiscoveryListener mEvtListener = new OnHeartRateDiscoveryListener() {
@Override
public void onConnected(final HeartRateDevice device) {
runOnUiThread(new Runnable() {
@Override
public void run() {
DeviceContainer container = findDeviceContainerByAddress(device.getAddress());
if (container != null) {
container.setRegisterFlag(true);
mDeviceAdapter.notifyDataSetChanged();
}
dismissProgressDialog();
}
});
}
@Override
public void onConnectFailed(final BluetoothDevice device) {
runOnUiThread(new Runnable() {
@Override
public void run() {
dismissProgressDialog();
showErrorDialogNotConnect(device.getName());
}
});
}
@Override
public void onDisconnected(final HeartRateDevice device) {
}
@Override
public void onDiscovery(final List<BluetoothDevice> devices) {
updateView(devices);
}
};
private void updateView(final List<BluetoothDevice> devices) {
if (mDeviceAdapter == null) {
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
mDeviceAdapter.clear();
mDeviceAdapter.addAll(createDeviceContainers());
if (devices != null) {
for (BluetoothDevice device : devices) {
if (!containAddressForAdapter(device.getAddress())) {
mDeviceAdapter.add(createContainer(device));
}
}
}
mDeviceAdapter.notifyDataSetChanged();
}
});
}
/**
* Create a list of device.
*
* @return list of device
*/
private List<DeviceContainer> createDeviceContainers() {
List<DeviceContainer> containers = new ArrayList<>();
HeartRateManager mgr = getManager();
if (mgr != null) {
List<HeartRateDevice> devices = mgr.getRegisterDevices();
if (devices != null) {
for (HeartRateDevice device : devices) {
containers.add(createContainer(device, true));
}
}
// MEMO: add of device that are paired to smart phone.
Set<BluetoothDevice> pairing = mgr.getBondedDevices();
if (pairing != null) {
for (BluetoothDevice device : pairing) {
String name = device.getName();
if (name != null && name.contains("PS-100")
&& !containAddressForList(containers, device.getAddress())) {
containers.add(createContainer(device));
}
}
}
}
return containers;
}
/**
* Returns true if this address contains the list of device.
*
* @param containers list of device
* @param address address of device
* @return true if address is an element of this List, false otherwise
*/
private boolean containAddressForList(final List<DeviceContainer> containers, final String address) {
for (DeviceContainer container : containers) {
if (container.getAddress().equalsIgnoreCase(address)) {
return true;
}
}
return false;
}
/**
* Look for a DeviceContainer with the given address.
*
* @param address address of device
* @return The DeviceContainer that has the given address or null
*/
private DeviceContainer findDeviceContainerByAddress(final String address) {
int size = mDeviceAdapter.getCount();
for (int i = 0; i < size; i++) {
DeviceContainer container = mDeviceAdapter.getItem(i);
if (container.getAddress().equalsIgnoreCase(address)) {
return container;
}
}
return null;
}
/**
* Create a DeviceContainer from BluetoothDevice.
*
* @param device Instance of BluetoothDevice
* @return Instance of DeviceContainer
*/
private DeviceContainer createContainer(final BluetoothDevice device) {
DeviceContainer container = new DeviceContainer();
container.setName(device.getName());
container.setAddress(device.getAddress());
return container;
}
/**
* Create a DeviceContainer from HeartRateDevice.
*
* @param device Instance of HeartRateDevice
* @param register Registration flag
* @return Instance of DeviceContainer
*/
private DeviceContainer createContainer(final HeartRateDevice device, final boolean register) {
DeviceContainer container = new DeviceContainer();
container.setName(device.getName());
container.setAddress(device.getAddress());
container.setRegisterFlag(register);
return container;
}
/**
* Returns true if this address contains the mDeviceAdapter.
*
* @param address address of device
* @return true if address is an element of mDeviceAdapter, false otherwise
*/
private boolean containAddressForAdapter(final String address) {
int size = mDeviceAdapter.getCount();
for (int i = 0; i < size; i++) {
DeviceContainer container = mDeviceAdapter.getItem(i);
if (container.getAddress().equalsIgnoreCase(address)) {
return true;
}
}
return false;
}
private class DeviceContainer {
private String mName;
private String mAddress;
private boolean mRegisterFlag;
public String getName() {
return mName;
}
public void setName(final String name) {
if (name == null) {
mName = getActivity().getResources().getString(
R.string.heart_rate_setting_default_name);
} else {
mName = name;
}
}
public String getAddress() {
return mAddress;
}
public void setAddress(final String address) {
mAddress = address;
}
public boolean isRegisterFlag() {
return mRegisterFlag;
}
public void setRegisterFlag(boolean registerFlag) {
mRegisterFlag = registerFlag;
}
}
private class DeviceAdapter extends ArrayAdapter<DeviceContainer> {
private LayoutInflater mInflater;
public DeviceAdapter(final Context context, final List<DeviceContainer> objects) {
super(context, 0, objects);
mInflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_heart_rate_device, null);
}
final DeviceContainer device = getItem(position);
String name = device.getName();
if (device.isRegisterFlag()) {
if (getManager() != null && getManager().containConnectedHeartRateDevice(device.getAddress())) {
name += " " + getResources().getString(R.string.heart_rate_setting_online);
} else {
name += " " + getResources().getString(R.string.heart_rate_setting_offline);
}
}
TextView nameView = (TextView) convertView.findViewById(R.id.device_name);
nameView.setText(name);
TextView addressView = (TextView) convertView.findViewById(R.id.device_address);
addressView.setText(device.getAddress());
Button btn = (Button) convertView.findViewById(R.id.btn_connect_device);
if (device.isRegisterFlag()) {
btn.setBackgroundResource(R.drawable.button_red);
btn.setText(R.string.heart_rate_setting_disconnect);
} else {
btn.setBackgroundResource(R.drawable.button_blue);
btn.setText(R.string.heart_rate_setting_connect);
}
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (device.isRegisterFlag()) {
disconnectDevice(device);
} else {
connectDevice(device);
}
}
});
return convertView;
}
}
}