Skip to content

Commit

Permalink
Build a service to do beacon scanning and it holds the minBeacon. Rem…
Browse files Browse the repository at this point in the history
…ove unnecessary SwipeRefreshLayout.

Signed-off-by: Chengzhi Hu <tony.hu1213@gmail.com>
  • Loading branch information
Tony080 committed Jun 5, 2018
1 parent 0a142c4 commit cb53e07
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 87 deletions.
2 changes: 2 additions & 0 deletions mobile/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
<service
android:name=".core.OpenHABVoiceService"
android:exported="false" />
<service android:name=".core.OpenHABBleService"
android:exported="false" />

<receiver
android:name=".ui.VoiceWidget"
Expand Down
111 changes: 111 additions & 0 deletions mobile/src/main/java/org/openhab/habdroid/core/OpenHABBleService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package org.openhab.habdroid.core;

import android.app.IntentService;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;

import org.openhab.habdroid.R;
import org.openhab.habdroid.model.OpenHABBeacon;
import org.openhab.habdroid.ui.OpenHABBleAdapter;
import org.openhab.habdroid.util.bleBeaconUtil.BleBeaconConnector;

import java.util.ArrayList;
import java.util.List;

public class OpenHABBleService extends IntentService{
private static final String TAG = OpenHABBleService.class.getSimpleName();
private IBinder mBinder;
private BleBeaconConnector mBleBeaconConnector;
private List<OpenHABBeacon> mBeaconList;
private OpenHABBleAdapter mOpenHABBleAdapter;
private OpenHABBeacon mMinBeacon;

public class LocalBinder extends Binder {
public OpenHABBleService getService(){
return OpenHABBleService.this;
}
}

public OpenHABBleService(){
super(TAG);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
while (true){
try {
mBleBeaconConnector.startLeScan();
Thread.sleep(BleBeaconConnector.SCAN_PERIOD);
} catch (InterruptedException e) {
Log.d(TAG, "Sleep failed!");
break;
}
}
}

This comment has been minimized.

Copy link
@maniac103

maniac103 Jun 5, 2018

Contributor

This looks like it would better be implemented using a normal Service and a Handler doing the periodic scheduling.

This comment has been minimized.

Copy link
@Tony080

Tony080 Jun 5, 2018

Author

You are right. It's better to take control of the Service by myself. I'll rebuild this class.

This comment has been minimized.

Copy link
@Tony080

Tony080 Jun 7, 2018

Author

Update: Rebuild BLE Service class with normal Service. And a listener is introduced to make a connection between UI and Service.


@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
mBinder = new LocalBinder();
mBleBeaconConnector = BleBeaconConnector.getInstance();
mBeaconList = new ArrayList<>();
mBleBeaconConnector.bindLeScanCallback(this);
Toast.makeText(this, R.string.ble_service_start, Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

public void addBeacon(OpenHABBeacon beacon){
int index;

//Update if already exists. Avoid duplicate.
if ((index = findBeaconByAddress(beacon.address())) >= 0){
mBeaconList.set(index, beacon);

if (mOpenHABBleAdapter != null) {
mOpenHABBleAdapter.notifyItemChanged(index);
}
} else {
mBeaconList.add(beacon);

if (mOpenHABBleAdapter != null) {
mOpenHABBleAdapter.notifyItemInserted(mBeaconList.size() - 1);
}
}
updateMin(beacon);
}

private int findBeaconByAddress(String address){
for (int i = 0; i < mBeaconList.size(); i++){
if (address.equals(mBeaconList.get(i).address())){
return i;
}
}
return -1;
}

public void bindOpenHABBleAdapter(OpenHABBleAdapter openHABBleAdapter){
mOpenHABBleAdapter = openHABBleAdapter;
mOpenHABBleAdapter.bindData(mBeaconList);
}

This comment has been minimized.

Copy link
@maniac103

maniac103 Jun 5, 2018

Contributor

IMHO, service and UI shouldn't be mixed. I think it's better to give the service a listener interface (for adding/removing a change listener), let the list UI implement the listener interface and do the adapter update there.

This comment has been minimized.

Copy link
@Tony080

Tony080 Jun 5, 2018

Author

Thanks for the suggestion. I'll separate and redesign the logic.


public void unBindOpenHABBleAdapter(){
mOpenHABBleAdapter = null;
}

private void updateMin(OpenHABBeacon beacon){
if (mMinBeacon == null || mMinBeacon.address().equals(beacon.address())
|| beacon.distance() < mMinBeacon.distance()){
mMinBeacon = beacon;
Log.d(TAG, "Min beacon changed to: " + mMinBeacon);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package org.openhab.habdroid.model;

import android.graphics.Path;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;

/**
* This is the data storage class to store ble beacon info.
* The hashCode() and equals() methods are manually overridden based on MAC address.
* That means, if two objects held the same MAC address are treated as the same beacon.
*/
@AutoValue
public abstract class OpenHABBeacon {
public enum Type{
Expand Down Expand Up @@ -41,12 +45,12 @@ public enum Type{
public abstract String major();
@Nullable
public abstract String minor();
//Add more selective value here

public static Builder builder(Type beaconType){
return new AutoValue_OpenHABBeacon.Builder()
.setType(beaconType);
}
//Add more selective value here

@AutoValue.Builder
public abstract static class Builder {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.openhab.habdroid.ui;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
Expand All @@ -10,15 +13,29 @@
import android.view.MenuItem;

import org.openhab.habdroid.R;
import org.openhab.habdroid.core.OpenHABBleService;
import org.openhab.habdroid.ui.widget.DividerItemDecoration;
import org.openhab.habdroid.util.Util;
import org.openhab.habdroid.util.bleBeaconUtil.BleBeaconConnector;

public class OpenHABBleActivity extends AppCompatActivity implements
SwipeRefreshLayout.OnRefreshListener{
private BleBeaconConnector mBleBeaconConnector;
private RecyclerView mRecyclerView;
private SwipeRefreshLayout mSwipeRefreshLayout;
public class OpenHABBleActivity extends AppCompatActivity{
private OpenHABBleAdapter mOpenHABBleAdapter;
private OpenHABBleService mBleService;
private Intent mBleServiceIntent;

private ServiceConnection mBleServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBleService = ((OpenHABBleService.LocalBinder)service).getService();
mBleService.bindOpenHABBleAdapter(mOpenHABBleAdapter);
}

@Override
public void onServiceDisconnected(ComponentName name) {
mBleService.unBindOpenHABBleAdapter();
mBleService = null;
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -31,24 +48,33 @@ protected void onCreate(Bundle savedInstanceState) {
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

mSwipeRefreshLayout = findViewById(R.id.swipe_container);
mSwipeRefreshLayout.setOnRefreshListener(this);
RecyclerView recyclerView = findViewById(R.id.ble_recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mOpenHABBleAdapter = new OpenHABBleAdapter();
recyclerView.setAdapter(mOpenHABBleAdapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new DividerItemDecoration(this));

mRecyclerView = findViewById(R.id.ble_recyclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
OpenHABBleAdapter openHABBleAdapter = new OpenHABBleAdapter();
mRecyclerView.setAdapter(openHABBleAdapter);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addItemDecoration(new DividerItemDecoration(this));
boolean bleNotSupport = BleBeaconConnector.getInstance().isNotSupport();
if (!bleNotSupport){
mBleServiceIntent = new Intent(this, OpenHABBleService.class);
}
}

mBleBeaconConnector = BleBeaconConnector.getInstance(this);
if (mBleBeaconConnector.isNotSupport()){
return;
@Override
protected void onStart() {
super.onStart();
if (mBleService == null && mBleServiceIntent != null) {
bindService(mBleServiceIntent, mBleServiceConnection, BIND_AUTO_CREATE);
}
mBleBeaconConnector.bindLeScanCallback(openHABBleAdapter);
}

mSwipeRefreshLayout.setRefreshing(true);
onRefresh();
@Override
protected void onStop() {
if (mBleService != null){
unbindService(mBleServiceConnection);
}
super.onStop();
}

@Override
Expand All @@ -59,15 +85,4 @@ public boolean onOptionsItemSelected(MenuItem item) {
}
return super.onOptionsItemSelected(item);
}

@Override
public void onRefresh() {
if (mSwipeRefreshLayout.isRefreshing()) {
((OpenHABBleAdapter)mRecyclerView.getAdapter()).clearList();
mBleBeaconConnector.stopLeScan();
mBleBeaconConnector.startLeScan();
mSwipeRefreshLayout.postDelayed(() -> mSwipeRefreshLayout.setRefreshing(false)
, BleBeaconConnector.SCAN_PERIOD);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
import org.openhab.habdroid.R;
import org.openhab.habdroid.model.OpenHABBeacon;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class OpenHABBleAdapter extends RecyclerView.Adapter<OpenHABBleAdapter.ViewHolder>{
Expand All @@ -39,12 +37,7 @@ public ViewHolder(View itemView) {
}
}

List<OpenHABBeacon> mOpenHABBeacons;


public OpenHABBleAdapter() {
mOpenHABBeacons = Collections.synchronizedList(new ArrayList<>());
}
private List<OpenHABBeacon> mBeaconList;

@NonNull
@Override
Expand All @@ -55,7 +48,7 @@ public OpenHABBleAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent

@Override
public void onBindViewHolder(@NonNull OpenHABBleAdapter.ViewHolder holder, int position) {
OpenHABBeacon item = mOpenHABBeacons.get(position);
OpenHABBeacon item = mBeaconList.get(position);
Resources resources = holder.itemView.getResources();
holder.mName.setText(Html.fromHtml(resources.getString(R.string.beacon_name, item.name())));
holder.mMac.setText(Html.fromHtml(resources.getString(R.string.beacon_address, item.address())));
Expand All @@ -82,32 +75,11 @@ public void onBindViewHolder(@NonNull OpenHABBleAdapter.ViewHolder holder, int p

@Override
public int getItemCount() {
return mOpenHABBeacons.size();
}

public void addBeacon(OpenHABBeacon beacon){
int index = indexOf(beacon.address());
if (index >= 0){
mOpenHABBeacons.set(index, beacon);
notifyItemChanged(index);
} else {
mOpenHABBeacons.add(0, beacon);
notifyItemInserted(0);
}
}

private int indexOf(String address){
for (int i = 0; i < mOpenHABBeacons.size(); i++){
if (address.equals(mOpenHABBeacons.get(i).address())){
return i;
}
}
return -1;
return mBeaconList == null ? 0 : mBeaconList.size();
}

public void clearList(){
int size = mOpenHABBeacons.size();
mOpenHABBeacons.clear();
notifyItemRangeRemoved(0, size);
public void bindData(List<OpenHABBeacon> beaconList){
mBeaconList = beaconList;
notifyItemRangeChanged(0, mBeaconList.size());
}
}
Loading

0 comments on commit cb53e07

Please sign in to comment.