diff --git a/.gitignore b/.gitignore index 650f395..43f53aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Built application files *.apk *.ap_ +*.jar # Files for the ART/Dalvik VM *.dex @@ -12,7 +13,7 @@ bin/ gen/ out/ - +libs/ # Gradle files .gradle/ build/ diff --git a/app/build.gradle b/app/build.gradle index 44a1642..a69b422 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,11 +19,22 @@ android { } } +repositories { + maven { + url "https://repo.eclipse.org/content/repositories/paho-releases/" + } +} + dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) + compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:23.4.0' + compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0' + compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.0' testCompile 'junit:junit:4.12' + compile 'com.github.nkzawa:socket.io-client:0.3.0' +// compile files('libs/yunba-sdk-release1.8.0.jar') + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index db4d0c0..382a1a6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,14 +33,17 @@ - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/net/chaoc/blescanner/MainActivity.java b/app/src/main/java/net/chaoc/blescanner/MainActivity.java index 9fa4bf2..2561ed0 100644 --- a/app/src/main/java/net/chaoc/blescanner/MainActivity.java +++ b/app/src/main/java/net/chaoc/blescanner/MainActivity.java @@ -1,33 +1,32 @@ package net.chaoc.blescanner; -import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; +import android.bluetooth.le.AdvertiseCallback; +import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertiseSettings; +import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; +import android.content.BroadcastReceiver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.AsyncTask; -import android.support.v4.app.FragmentActivity; -import android.support.v4.util.SparseArrayCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.Editable; +import android.text.Html; +import android.text.TextWatcher; import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.view.View; import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.TextView; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttToken; - -import java.io.ByteArrayInputStream; - -import io.yunba.android.manager.YunBaManager; +import android.widget.Toast; +import android.widget.ToggleButton; import static android.content.ContentValues.TAG; @@ -40,7 +39,15 @@ public class MainActivity extends AppCompatActivity implements android.view.View Button stopScanningButton; TextView peripheralTextView; private final static int REQUEST_ENABLE_BT = 1; - private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1; + public String advDataStr; + AdvertiseData advData; + BluetoothLeAdvertiser advertiser; + AdvertiseSettings advSettings; + + AdvertiseCallback advCallback; + + long matchCount = 0; + long otherCount = 0; @Override @@ -66,56 +73,82 @@ public void onClick(View v) { } }); stopScanningButton.setVisibility(View.INVISIBLE); + } - YunBaManager.start(getApplicationContext()); - YunBaManager.subscribe(getApplicationContext(), new String[]{"t1"}, new IMqttActionListener() { - - @Override - public void onSuccess(IMqttToken arg0) { - Log.d("main", "Subscribe topic succeed"); - - } - - @Override - public void onFailure(IMqttToken arg0, Throwable arg1) { - Log.d("main", "Subscribe topic failed" ); - } - }); - - registerMessageReceiver(); - + @Override + public void onClick(View v) { } + public void init(){ btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); btAdapter = btManager.getAdapter(); btScanner = btAdapter.getBluetoothLeScanner(); + advertiser = btAdapter.getBluetoothLeAdvertiser(); if (btAdapter != null && !btAdapter.isEnabled()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent,REQUEST_ENABLE_BT); } + // ble advertisers + advSettings = new AdvertiseSettings.Builder() + .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) + .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) + .setConnectable( false ) + .build(); + advDataStr = "121383000000"; + ToggleButton toggle = (ToggleButton) findViewById(R.id.toggleButton2); + final EditText eText = (EditText) findViewById(R.id.editText4); + eText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + + } + + @Override + public void afterTextChanged(Editable s) { + String text; + text = s.toString(); + if (text.matches("-?[0-9a-fA-F]+") == true) { + advDataStr = s.toString(); + } else { + s.clear(); + s.append("121383000000"); + } + } + }); + + toggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + //enabled + //update text value; + //validated it; if false then disabled this + //start advertising; + advertise(toByteArray(advDataStr)); + + + } else { + //disabled + //stop advertising; + stopAdvertise(); + } + } + }); - // Make sure we have access coarse location enabled, if not, prompt the user to enable it -// if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { -// final AlertDialog.Builder builder = new AlertDialog.Builder(this); -// builder.setTitle("This app needs location access"); -// builder.setMessage("Please grant location access so this app can detect peripherals."); -// builder.setPositiveButton(android.R.string.ok, null); -// builder.setOnDismissListener(new DialogInterface.OnDismissListener() { -// @Override -// public void onDismiss(DialogInterface dialog) { -// requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION); -// } -// }); -// builder.show(); -// } } public void startScanning() { System.out.println("start scanning"); peripheralTextView.setText(""); + startScanningButton.setVisibility(View.INVISIBLE); stopScanningButton.setVisibility(View.VISIBLE); AsyncTask.execute(new Runnable() { @@ -144,13 +177,50 @@ public void run() { private ScanCallback leScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { - peripheralTextView.append("Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() + " : " + result.getScanRecord().getManufacturerSpecificData().keyAt(0) + "\n" + toHexString(result.getScanRecord().getManufacturerSpecificData().valueAt(0))+ "\n"); - - // auto scroll for text view - final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight(); - // if there is no need to scroll, scrollAmount will be <=0 - if (scrollAmount > 0) - peripheralTextView.scrollTo(0, scrollAmount); + //if (result.getScanRecord().getManufacturerSpecificData().keyAt(0) == 65280) { + if (false) { + matchCount += 1; + + peripheralTextView.append("$" + matchCount + ":N:" + result.getDevice().getName() + " rssi: " + result.getRssi() + " : " + result.getScanRecord().getManufacturerSpecificData().keyAt(0) + "\n" + toHexString(result.getScanRecord().getBytes()) + "\n"); + + // auto scroll for text view + final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight(); + // if there is no need to scroll, scrollAmount will be <=0 + if (scrollAmount > 0) + peripheralTextView.scrollTo(0, scrollAmount); + } else { + if (result.getDevice().getName() != null ) { + //if (result.getDevice().getName().toString().startsWith("Brac")) { + try { + String data = toHexString(result.getScanRecord().getManufacturerSpecificData().valueAt(0)); + String payload = toHexString(result.getScanRecord().getBytes()); + String name = result.getDevice().getName(); + int key = result.getScanRecord().getManufacturerSpecificData().keyAt(0); + int rssi = result.getRssi(); + + String str = "$:" + name + " rssi" + rssi + " : " + key + "\n" + + data + "\n" + + payload + "\n"; + if (key == 65281 && data.startsWith("12")) { + peripheralTextView.append(Html.fromHtml(""+str+"")); + } else { + peripheralTextView.append(str); + } + peripheralTextView.append("\n"); + + otherCount += 1; + Log.w(this.getClass().getSimpleName(), toHexString(result.getScanRecord().getBytes())); + } catch (Exception e) { + Log.e(this.getClass().getSimpleName(), "get manufacture data error"); + } + // auto scroll for text view + final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight(); + // if there is no need to scroll, scrollAmount will be <=0 + if (scrollAmount > 0) + peripheralTextView.scrollTo(0, scrollAmount); + //} + } + } } }; @@ -168,5 +238,45 @@ public String toHexString(byte[] bytes){ return hexString.toString(); } + public static byte[] toByteArray(String s){ + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + + public void advertise(byte[] manuData){ + advData = new AdvertiseData.Builder() + .setIncludeDeviceName(true) + .addManufacturerData(65281, manuData) + .build(); + advCallback = new AdvertiseCallback() { + @Override + public void onStartSuccess(AdvertiseSettings settingsInEffect) { + super.onStartSuccess(settingsInEffect); + Toast.makeText(getApplicationContext(), "start advertising", Toast.LENGTH_LONG).show(); + } + }; + advertiser.startAdvertising(advSettings, advData, advCallback); + } + + public void stopAdvertise(){ + advertiser.stopAdvertising(advCallback); + + } + + public class MessageReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + Log.i(TAG, "Action - " + intent.getAction()); + Toast.makeText(getApplicationContext(), "msg received", Toast.LENGTH_SHORT).show(); + } + } } + + diff --git a/app/src/main/java/net/chaoc/blescanner/RemoteReceiver.java b/app/src/main/java/net/chaoc/blescanner/RemoteReceiver.java deleted file mode 100644 index 806491a..0000000 --- a/app/src/main/java/net/chaoc/blescanner/RemoteReceiver.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.chaoc.blescanner; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; - -import io.yunba.android.manager.YunBaManager; - -/** - * Created by chaocao on 2016/10/29. - */ - -public class RemoteReceiver extends BroadcastReceiver { - private final static String REPORT_MSG_SHOW_NOTIFICARION = "1000"; - private final static String REPORT_MSG_SHOW_NOTIFICARION_FAILED = "1001"; - @Override - public void onReceive(Context context, Intent intent) { - if (YunBaManager.MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) { - - String topic = intent.getStringExtra(YunBaManager.MQTT_TOPIC); - String msg = intent.getStringExtra(YunBaManager.MQTT_MSG); - - - StringBuilder showMsg = new StringBuilder(); - showMsg.append("Received message from server: ").append(YunBaManager.MQTT_TOPIC) - .append(" = ").append(topic).append(" ") - .append(YunBaManager.MQTT_MSG).append(" = ").append(msg); - boolean flag = ResUtil.showNotifation(context, topic, msg); - //上报显示通知栏状态, 以方便后台统计 - if (flag) YunBaManager.report(context, REPORT_MSG_SHOW_NOTIFICARION, topic); - else YunBaManager.report(context, REPORT_MSG_SHOW_NOTIFICARION_FAILED, topic); - - // send msg to app - - } else if(YunBaManager.PRESENCE_RECEIVED_ACTION.equals(intent.getAction())) { - //msg from presence. - String topic = intent.getStringExtra(YunBaManager.MQTT_TOPIC); - String payload = intent.getStringExtra(YunBaManager.MQTT_MSG); - StringBuilder showMsg = new StringBuilder(); - showMsg.append("Received message presence: ").append(YunBaManager.MQTT_TOPIC) - .append(" = ").append(topic).append(" ") - .append(YunBaManager.MQTT_MSG).append(" = ").append(payload); - Log.d("DemoReceiver", showMsg.toString()); - - } - - - - - - } - //send msg to MainActivity - private void processCustomMessage(Context context, Intent intent) { - - intent.setAction(MainActivity.MESSAGE_RECEIVED_ACTION); - intent.addCategory(context.getPackageName()); - context.sendBroadcast(intent); - - } - - -} diff --git a/app/src/main/java/net/chaoc/blescanner/ResUtil.java b/app/src/main/java/net/chaoc/blescanner/ResUtil.java deleted file mode 100644 index e091d50..0000000 --- a/app/src/main/java/net/chaoc/blescanner/ResUtil.java +++ /dev/null @@ -1,199 +0,0 @@ -package net.chaoc.blescanner; - -import android.app.ActivityManager; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.media.RingtoneManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.TaskStackBuilder; -import android.telephony.TelephonyManager; - -import java.util.List; -import java.util.Random; - -import io.yunba.android.manager.YunBaManager; - -/** - * Created by chaocao on 2016/10/29. - */ - -public class ResUtil { - public static boolean isEmpty(String s) { - if (null == s) - return true; - if (s.length() == 0) - return true; - if (s.trim().length() == 0) - return true; - return false; - } - - public static boolean showNotifation(Context context, String topic, - String msg) { - try { - Uri alarmSound = RingtoneManager - .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); - - long[] pattern = { 500, 500, 500 }; - NotificationCompat.Builder mBuilder = new - NotificationCompat.Builder( - context) //missing icon: .setSmallIcon(R.drawable.ic_launcher) - .setContentTitle(topic).setContentText(msg) - .setSound(alarmSound).setVibrate(pattern).setAutoCancel(true); - // Creates an explicit intent for an Activity in your app - Intent resultIntent = new Intent(context, - MainActivity.class); - - if (!ResUtil.isEmpty(topic)) - resultIntent.putExtra(YunBaManager.MQTT_TOPIC, topic); - if (!ResUtil.isEmpty(msg)) - resultIntent.putExtra(YunBaManager.MQTT_MSG, msg); - // The stack builder object will contain an artificial back stack - // for the - // started Activity. - // This ensures that navigating backward from the Activity leads - - // of - // your application to the Home screen. - TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); - // Adds the back stack for the Intent (but not the Intent itself) - stackBuilder.addParentStack(MainActivity.class); - - // Adds the Intent that starts the Activity to the top of the - - stackBuilder.addNextIntent(resultIntent); - PendingIntent resultPendingIntent = - stackBuilder.getPendingIntent( - 0, PendingIntent.FLAG_UPDATE_CURRENT); - - mBuilder.setContentIntent(resultPendingIntent); - NotificationManager mNotificationManager = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); - // mId allows you to update the notification later on. - Random r = new Random(); - mNotificationManager.notify(r.nextInt(), mBuilder.build()); - -// NotificationCompat.Builder mBuilder = new NotificationCompat.Builder( -// context).setSmallIcon(R.drawable.ic_launcher) // notification -// // icon -// .setContentTitle(topic) // title for notification -// .setContentText(msg) // message for notification -// .setAutoCancel(true); // clear notification after click -// Intent intent = new Intent(context, YunBaTabActivity.class); -// -// if (!DemoUtil.isEmpty(topic)) -// intent.putExtra(YunBaManager.MQTT_TOPIC, topic); -// if (!DemoUtil.isEmpty(msg)) -// intent.putExtra(YunBaManager.MQTT_MSG, msg); -// PendingIntent pi = PendingIntent.getActivity(context, 0, intent, -// Intent.FLAG_ACTIVITY_NEW_TASK); -// mBuilder.setContentIntent(pi); -// NotificationManager mNotificationManager = (NotificationManager) context -// .getSystemService(Context.NOTIFICATION_SERVICE); -// mNotificationManager.notify(0, mBuilder.build()); - } catch (Exception e) { - return false; - } - return true; - } - - public static void showToast(final String toast, final Context context) { - // if (!isAppOnForeground(context)) return; - // new Thread(new Runnable() { - // - // @Override - // public void run() { - // Looper.prepare(); - // Toast.makeText(context, toast, Toast.LENGTH_SHORT).show(); - // Looper.loop(); - // } - // }).start(); - } - - public static boolean isAppOnForeground(Context context) { - ActivityManager activityManager = (ActivityManager) context - .getSystemService(Context.ACTIVITY_SERVICE); - // Returns a list of application processes that are running on the - // device - List appProcesses = activityManager - .getRunningAppProcesses(); - if (appProcesses == null) - return false; - for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { - // importance: - // The relative importance level that the system places - // on this process. - // May be one of IMPORTANCE_FOREGROUND, IMPORTANCE_VISIBLE, - // IMPORTANCE_SERVICE, IMPORTANCE_BACKGROUND, or IMPORTANCE_EMPTY. - // These constants are numbered so that "more important" values are - // always smaller than "less important" values. - // processName: - // The name of the process that this object is associated with. - if (appProcess.processName.equals(context.getPackageName()) - && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { - return true; - } - } - return false; - } - - public static String join(T[] array, String cement) { - StringBuilder builder = new StringBuilder(); - - if (array == null || array.length == 0) { - return null; - } - for (T t : array) { - builder.append(t).append(cement); - } - - builder.delete(builder.length() - cement.length(), builder.length()); - - return builder.toString(); - } - - public static boolean isNetworkEnabled(Context context) { - ConnectivityManager conn = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo info = conn.getActiveNetworkInfo(); - return (info != null && info.isConnected()); - } - - public static String getImei(Context context, String imei) { - TelephonyManager telephonyManager = (TelephonyManager) context - .getSystemService(Context.TELEPHONY_SERVICE); - imei = telephonyManager.getDeviceId(); - return imei; - } - - public static String getAppKey(Context context) { - Bundle metaData = null; - String appKey = null; - try { - ApplicationInfo ai = context.getPackageManager() - .getApplicationInfo(context.getPackageName(), - PackageManager.GET_META_DATA); - if (null != ai) { - metaData = ai.metaData; - } - if (null != metaData) { - appKey = metaData.getString("YUNBA_APPKEY"); - if ((null == appKey) || appKey.length() != 24) { - appKey = "Error"; - } - } - } catch (PackageManager.NameNotFoundException e) { - - } - return appKey; - } - -} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 43860d1..d4837a4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,7 @@ - -