package com.accent_systems.ibkshelloworld; import android.content.Context; import android.graphics.Paint; import android.graphics.Typeface; import android.support.v4.util.LruCache; import android.text.TextPaint; import android.text.style.MetricAffectingSpan; /** * Created by Dmeytro on 29/05/2018. */ public class TypefaceSpan extends MetricAffectingSpan { private static LruCache sTypefaceCache = new LruCache(12); private Typeface mTypeface; public TypefaceSpan(Context context, String typefaceName) { mTypeface = sTypefaceCache.get(typefaceName); if (mTypeface == null) { mTypeface = Typeface.createFromAsset(context.getApplicationContext() .getAssets(), String.format("fonts/%s", typefaceName)); // Cache the loaded Typeface sTypefaceCache.put(typefaceName, mTypeface); } } @Override public void updateMeasureState(TextPaint p) { p.setTypeface(mTypeface); // Note: This flag is required for proper typeface rendering p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG); } @Override public void updateDrawState(TextPaint tp) { tp.setTypeface(mTypeface); // Note: This flag is required for proper typeface rendering tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG); } } package com.accent_systems.ibkshelloworld; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.support.annotation.RequiresApi; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.util.Log; import android.view.View; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class ScanActivity extends AppCompatActivity { //DEFINE VARS String TAG = "ScanActivity"; BluetoothAdapter mBluetoothAdapter; BluetoothGatt mBluetoothGatt; BluetoothLeScanner scanner; ScanSettings scanSettings; private List scannedDeivcesList; private ArrayAdapter adapter; //DEFINE LAYOUT ListView devicesList; //THIS METHOD RUNS ON APP LAUNCH @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scan); //Define listview in layout devicesList = (ListView) findViewById(R.id.devicesList); //Setup list on device click listener setupListClickListener(); //Inicialize de devices list scannedDeivcesList = new ArrayList<>(); //Inicialize the list adapter for the listview with params: Context / Layout file / TextView ID in layout file / Devices list adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, scannedDeivcesList); //Set the adapter to the listview devicesList.setAdapter(adapter); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); assert getSupportActionBar() != null; getSupportActionBar().setDisplayHomeAsUpEnabled(true); SpannableString s = new SpannableString("Scan beacons"); s.setSpan(new com.accent_systems.ibkshelloworld.TypefaceSpan(this, "Khand-Bold.ttf"), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); s.setSpan(new ForegroundColorSpan(Color.parseColor("#3a3c3e")), 0, s.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); setTitle(s); getSupportActionBar().setLogo(R.mipmap.ibks); getSupportActionBar().setDisplayUseLogoEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); //init Bluetooth adapter initBT(); //Start scan of bluetooth devices startLeScan(true); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onStop() { super.onStop(); startLeScan(false); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void initBT(){ final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); //Create the scan settings ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder(); //Set scan latency mode. Lower latency, faster device detection/more battery and resources consumption scanSettingsBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); //Wrap settings together and save on a settings var (declared globally). scanSettings = scanSettingsBuilder.build(); //Get the BLE scanner from the BT adapter (var declared globally) scanner = mBluetoothAdapter.getBluetoothLeScanner(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void startLeScan(boolean endis) { if (endis) { //******************** //START THE BLE SCAN //******************** //Scanning parameters FILTER / SETTINGS / RESULT CALLBACK. Filter are used to define a particular //device to scan for. The Callback is defined above as a method. scanner.startScan(null, scanSettings, mScanCallback); }else{ //Stop scan scanner.stopScan(mScanCallback); } } private ScanCallback mScanCallback = new ScanCallback() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); //Here will be received all the detected BLE devices around. "result" contains the device //address and name as a BLEPeripheral, the advertising content as a ScanRecord, the Rx RSSI //and the timestamp when received. Type result.get... to see all the available methods you can call. //Convert advertising bytes to string for a easier parsing. GetBytes may return a NullPointerException. Treat it right(try/catch). String advertisingString = byteArrayToHex(result.getScanRecord().getBytes()); //Print the advertising String in the LOG with other device info (ADDRESS - RSSI - ADVERTISING - NAME) Log.i(TAG, result.getDevice().getAddress()+" - RSSI: "+result.getRssi()+"\t - "+advertisingString+" - "+result.getDevice().getName()); //Check if scanned device is already in the list by mac address boolean contains = false; for(int i=0; i add to list scannedDeivcesList.add(result.getRssi()+" "+result.getDevice().getName()+ "\n ("+result.getDevice().getAddress()+")"); } //After modify the list, notify the adapter that changes have been made so it updates the UI. //UI changes must be done in the main thread runOnUiThread(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); } }); } }; //Method to convert a byte array to a HEX. string. private String byteArrayToHex(byte[] a) { StringBuilder sb = new StringBuilder(a.length * 2); for(byte b: a) sb.append(String.format("%02x", b & 0xff)); return sb.toString(); } void setupListClickListener(){ devicesList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onItemClick(AdapterView parent, View view, int position, long id) { //Stop the scan Log.i(TAG, "SCAN STOPED"); scanner.stopScan(mScanCallback); //Get the string from the item clicked String fullString = scannedDeivcesList.get(position); //Get only the address from the previous string. Substring from '(' to ')' String address = fullString.substring(fullString.indexOf("(")+1, fullString.indexOf(")")); //Get BLE device with address BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); //****************************** //START CONNECTION WITH DEVICE AND DECLARE GATT //****************************** Log.i(TAG,"*************************************************"); Log.i(TAG, "CONNECTION STARTED TO DEVICE "+address); Log.i(TAG,"*************************************************"); //ConnectGatt parameters are CONTEXT / AUTOCONNECT to connect the next time it is scanned / GATT CALLBACK to receive GATT notifications and data // Note: On Samsung devices, the connection must be done on main thread mBluetoothGatt = device.connectGatt(ScanActivity.this, false, mGattCallback); /* There is also another simplest way to connect to a device. If you already stored the device in a list (List) you can retrieve it directly and connect to it: mBluetoothGatt = mList.get(position).connectGatt(MainActivity.this, false, mGattCallback); */ } }); } //Connection callback BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { //Device connected, start discovering services Log.i(TAG, "DEVICE CONNECTED. DISCOVERING SERVICES..."); mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //Device disconnected Log.i(TAG, "DEVICE DISCONNECTED"); } } // On discover services method @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { //Services discovered successfully. Start parsing services and characteristics Log.i(TAG, "SERVICES DISCOVERED. PARSING..."); displayGattServices(gatt.getServices()); } else { //Failed to discover services Log.i(TAG, "FAILED TO DISCOVER SERVICES"); } } //When reading a characteristic, here you receive the task result and the value @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { //READ WAS SUCCESSFUL Log.i(TAG, "ON CHARACTERISTIC READ SUCCESSFUL"); //Read characteristic value like: characteristic.getValue(); //Which it returns a byte array. Convert it to HEX. string. } else { Log.i(TAG, "ERROR READING CHARACTERISTIC"); } } //When writing, here you can check whether the task was completed successfully or not @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { Log.i(TAG, "ON CHARACTERISTIC WRITE SUCCESSFUL"); } else { Log.i(TAG, "ERROR WRITING CHARACTERISTIC"); } } //In this method you can read the new values from a received notification @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { Log.i(TAG, "NEW NOTIFICATION RECEIVED"); //New notification received. Check the characteristic it comes from and parse to string if(characteristic.getUuid().toString().contains("0000fff3")){ characteristic.getValue(); } } //RSSI values from the connection with the remote device are received here @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { Log.i(TAG, "NEW RSSI VALUE RECEIVED"); //Read remote RSSI like: mBluetoothGatt.readRemoteRssi(); //Here you get the gatt table where the rssi comes from, the rssi value and the //status of the task. } }; //Method which parses all services and characteristics from the GATT table. @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) private void displayGattServices(List gattServices) { //Check if there is any gatt services. If not, return. if (gattServices == null) return; // Loop through available GATT Services. for (BluetoothGattService gattService : gattServices) { Log.i(TAG, "SERVICE FOUND: "+gattService.getUuid().toString()); //Loop through available characteristics for each service for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) { Log.i(TAG, " CHAR. FOUND: "+gattCharacteristic.getUuid().toString()); } } //**************************************** // CONNECTION PROCESS FINISHED! //**************************************** Log.i(TAG, "*************************************"); Log.i(TAG, "CONNECTION COMPLETED SUCCESFULLY"); Log.i(TAG, "*************************************"); } } /* One you have connected and discovered all services, you can start reading, writing and enabling notifications for the characteristics. First save on a var the characteristic you want in the loop above as: ************************************************************************* * if(gattCharacteristic.getUuid().toString().contains("0000fff3")){ * * BluetoothGattCharacteristic myChar = gattCharacteristic; * * } * ************************************************************************* ####IMPORTANT: All the read, write and enable notification task must be done in a Thread. To read a characteristic simply use: ************************************************* * mBluetoothGatt.readCharacteristic(myChar); * ************************************************* You will receive the read value and result in the mGattCallback above To write a characteristic, first set the value and then start the write task. Remember that the value must be a byte array *************************************************** * byte[] mValue = {0x01, 0x02}; * * myChar.setValue(mValue); * * mBluetoothGatt.writeCharacteristic(myChar); * *************************************************** To enable notifications fot a characteristics add: *********************************************************************** * mBluetoothGatt.setCharacteristicNotification(myChar, true/false); * *********************************************************************** You also need to set the client characteristic configuration descriptor 0x2902 ***************************************************************************** * UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); * * BluetoothGattDescriptor descriptor = myChar.getDescriptor(uuid); * * descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); * * mBluetoothGatt.writeDescriptor(descriptor); * ***************************************************************************** You will receive the notifications in the mGattCallback above. A simple thread example to run this code could be this: ********************************************************* * Thread writeThread = new Thread(writeThreadMethod); * * writeThread.start(); * ********************************************************* And the writeThreadMethod looks like this: ************************************************************* * Thread writeThreadMethod = new Thread(new Runnable() { * * @Override * * public void run() { * * //Add read, write or enable notifications here * * runOnUiThread(new Runnable() { * * @Override * * public void run() { * * //Add UI changes here * * } * * }); * * } * * }); * ************************************************************* In the next examples we will see how to register and check EID devices. */ package com.accent_systems.ibkshelloworld; import android.annotation.TargetApi; import android.app.Dialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Typeface; import android.os.Build; import android.os.Bundle; import android.support.annotation.RequiresApi; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.Spannable; import android.text.SpannableString; import android.text.TextWatcher; import android.text.style.ForegroundColorSpan; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; public class NotificationDemo extends AppCompatActivity { String TAG = "NotificationDemo"; private BluetoothAdapter mBluetoothAdapter; private BluetoothLeScanner scanner; private ScanSettings scanSettings; SharedPreferences mPrefs; SharedPreferences.Editor edt; String mUuid, dRange, mMsg, frameType; TextView editBtn; Animation animation; ImageView wave; double dist = 0.5, counter = 0; Thread startAnim; private Context mContext; @TargetApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) @Override protected void onResume() { super.onResume(); mPrefs = getSharedPreferences("iBKSDemo", Context.MODE_PRIVATE); animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.radar_rotate_anim); editBtn = (TextView) findViewById(R.id.editBtn); editBtn.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); editBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAnim.interrupt(); wave.setVisibility(View.INVISIBLE); wave.clearAnimation(); showSettingsDialog(); } }); wave = (ImageView) findViewById(R.id.notifWave); loadFilter(); //image animation of scan startAnim = new Thread(loopAnimation); startAnim.start(); //init Bluetooth adapter initBT(); //Start scan of bluetooth devices startLeScan(true); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = this; setContentView(R.layout.activity_notification); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); assert getSupportActionBar() != null; getSupportActionBar().setDisplayHomeAsUpEnabled(true); SpannableString s = new SpannableString("Notifications"); s.setSpan(new com.accent_systems.ibkshelloworld.TypefaceSpan(this, "Khand-Bold.ttf"), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); s.setSpan(new ForegroundColorSpan(Color.parseColor("#3a3c3e")), 0, s.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); setTitle(s); getSupportActionBar().setLogo(R.mipmap.ibks); getSupportActionBar().setDisplayUseLogoEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); } @Override protected void onPause() { super.onPause(); startLeScan(false); startAnim.interrupt(); wave.clearAnimation(); } @Override protected void onStop() { super.onStop(); startLeScan(false); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) private void initBT(){ final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); //Create the scan settings ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder(); //Set scan latency mode. Lower latency, faster device detection/more battery and resources consumption scanSettingsBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); //Wrap settings together and save on a settings var (declared globally). scanSettings = scanSettingsBuilder.build(); //Get the BLE scanner from the BT adapter (var declared globally) scanner = mBluetoothAdapter.getBluetoothLeScanner(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void startLeScan(boolean endis) { if (endis) { //******************** //START THE BLE SCAN //******************** //Scanning parameters FILTER / SETTINGS / RESULT CALLBACK. Filter are used to define a particular //device to scan for. The Callback is defined above as a method. scanner.startScan(null, scanSettings, mScanCallback); }else{ //Stop scan scanner.stopScan(mScanCallback); } } Thread loopAnimation = new Thread() { @Override public void run() { try { sleep(300); runOnUiThread(new Runnable() { @Override public void run() { wave.setVisibility(View.VISIBLE); wave.startAnimation(animation); } }); while(true){ if(counter > 0){ counter--; } sleep(1000); } } catch (InterruptedException e) { return; } } }; private ScanCallback mScanCallback = new ScanCallback() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); //Convert advertising bytes to string for a easier parsing. GetBytes may return a NullPointerException. Treat it right(try/catch). String advertisingString = byteArrayToHex(result.getScanRecord().getBytes()); //Print the advertising String in the LOG with other device info (ADDRESS - RSSI - ADVERTISING - NAME) Log.i(TAG, result.getDevice().getAddress()+" - RSSI: "+result.getRssi()+"\t - "+advertisingString+" - "+result.getDevice().getName()); Log.i(TAG,"UID = "+ mUuid); if (advertisingString.contains(mUuid.replace("-",""))) { if (result.getRssi() > dist) { if(counter == 0){ counter = 5; startAnim.interrupt(); wave.clearAnimation(); wave.setVisibility(View.INVISIBLE); showDialog(); } } } } }; public static String byteArrayToHex(byte[] a) { StringBuilder sb = new StringBuilder(a.length * 2); for(byte b: a) sb.append(String.format("%02x", b & 0xff)); return sb.toString(); } private void showDialog(){ final Dialog dialog = new Dialog(NotificationDemo.this); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setCancelable(false); dialog.setContentView(R.layout.custom_dialog_notification); TextView text = (TextView) dialog.findViewById(R.id.dialogMsg); text.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/OpenSans-Light.ttf")); //The message introduced in settings dialog is showed in this dialog text.setText(mMsg); Button dialogButton = (Button) dialog.findViewById(R.id.closeBtn); dialogButton.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); dialogButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAnim = new Thread(loopAnimation); startAnim.start(); dialog.dismiss(); } }); dialog.show(); } /** Dialog to set the trigger conditions of notification dialog **/ private void showSettingsDialog(){ final Dialog dialog = new Dialog(NotificationDemo.this); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setCancelable(false); dialog.setContentView(R.layout.custom_dialog_settings); TextView title = (TextView) dialog.findViewById(R.id.settTitle); title.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); TextView uuidTV = (TextView) dialog.findViewById(R.id.settUuid); uuidTV.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); final Spinner spinneruid = (Spinner) dialog.findViewById(R.id.spinneruid); title.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); spinneruid.setSelection(Integer.parseInt(frameType)); //83FB83FE-A5CE-2865-45CA-19EB81A0502A spinneruid.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) { final int ft = arg0.getSelectedItemPosition(); runOnUiThread(new Runnable() { @Override public void run() { EditText uuidET = (EditText) dialog.findViewById(R.id.uuid); String uid; if(ft == 0) { uid = mPrefs.getString("uuid-ibeacon", "83FB83FE-A5CE-2865-45CA-19EB81A0502A"); } else uid = mPrefs.getString("uuid-edstuid", "00000000000000000000-000000000001"); uuidET.setText(uid); } }); } @Override public void onNothingSelected(AdapterView arg0) { //optionally do something here } }); final EditText uuid = (EditText) dialog.findViewById(R.id.uuid); uuid.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/OpenSans-Regular.ttf")); uuid.setText(mUuid); TextView rannge = (TextView) dialog.findViewById(R.id.settRange); rannge.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); final Spinner dstt = (Spinner) dialog.findViewById(R.id.distSpinner); if(dRange.equals("Immediate")){ dstt.setSelection(0); }else if(dRange.equals("Near")){ dstt.setSelection(1); }else{ dstt.setSelection(2); } TextView msgg = (TextView) dialog.findViewById(R.id.settMsg); msgg.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); final EditText msgt = (EditText) dialog.findViewById(R.id.msgg); msgt.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/OpenSans-Regular.ttf")); msgt.setText(mMsg); msgt.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @Override public void afterTextChanged(Editable editable) { if (null != msgt.getLayout() && msgt.getLayout().getLineCount() > 5) { msgt.getText().delete(msgt.getText().length() - 1, msgt.getText().length()); } } }); final TextView err = (TextView) dialog.findViewById(R.id.errorMsg); err.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/OpenSans-Light.ttf")); Button cancelButton = (Button) dialog.findViewById(R.id.cancelBtn); cancelButton.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAnim = new Thread(loopAnimation); startAnim.start(); dialog.dismiss(); } }); Button saveButton = (Button) dialog.findViewById(R.id.saveBtn); saveButton.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); saveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { boolean isOK=true; edt = mPrefs.edit(); if(spinneruid.getSelectedItemPosition() == 0) { if (uuid.getText().length() != 42) { isOK=false; } else edt.putString("uuid-ibeacon",uuid.getText().toString()); }else{ if (uuid.getText().length() != 33){ isOK=false; } else edt.putString("uuid-edstuid",uuid.getText().toString()); } if(isOK){ edt.putString("frametype", Integer.toString(spinneruid.getSelectedItemPosition())); edt.putString("range", dstt.getSelectedItem().toString()); edt.putString("msg", msgt.getText().toString()); edt.commit(); loadFilter(); err.setVisibility(View.GONE); startAnim = new Thread(loopAnimation); startAnim.start(); dialog.dismiss(); }else { err.setVisibility(View.VISIBLE); } } }); dialog.show(); } //get parameters saved on SharedPreferences private void loadFilter(){ frameType = mPrefs.getString("frametype","0"); if(frameType.equals("0")) mUuid = mPrefs.getString("uuid-ibeacon", "83FB83FE-A5CE-2865-45CA-19EB81A0502A"); else mUuid = mPrefs.getString("uuid-edstuid", "00000000000000000000-000000000001"); dRange = mPrefs.getString("range", "Immediate"); mMsg = mPrefs.getString("msg", "Hey from Dmytro Kochkurov!"); if(dRange.equals("Immediate")){ dist = -38; }else if(dRange.equals("Near")) { dist = -60; }else if(dRange.equals("Far")){ dist = -120; } } } package com.accent_systems.ibkshelloworld; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.Typeface; import android.os.Build; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.util.Log; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { TextView prox; TextView notif; LinearLayout scanBtn; LinearLayout notifBtn; private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1; private static final int PERMISSION_REQUEST_COARSE_BL = 2; //THIS METHOD RUNS ON APP LAUNCH @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); prox = (TextView) findViewById(R.id.proxTV); prox.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); notif = (TextView) findViewById(R.id.notifTV); notif.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Khand-Bold.ttf")); SpannableString s = new SpannableString("iBKS My Beacon"); s.setSpan(new com.accent_systems.ibkshelloworld.TypefaceSpan(this,"Khand-Bold.ttf"), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); s.setSpan(new ForegroundColorSpan(Color.parseColor("#3a3c3e")), 0, s.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); setTitle(s); assert getSupportActionBar() != null; getSupportActionBar().setLogo(R.mipmap.ibks); getSupportActionBar().setDisplayUseLogoEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); scanBtn = (LinearLayout) findViewById(R.id.proxBtn); scanBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent startProxDemoActivity = new Intent(MainActivity.this, ScanActivity.class); startActivity(startProxDemoActivity); } }); notifBtn = (LinearLayout) findViewById(R.id.notifBtn); notifBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent startNotifDemoActivity = new Intent(MainActivity.this, NotificationDemo.class); startActivity(startNotifDemoActivity); } }); checkLocBT(); inicializeBluetooth(); } private BluetoothAdapter mBTAdapter; private void inicializeBluetooth(){ //Check if device does support BT by hardware if (!getBaseContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { //Toast shows a message on the screen for a LENGTH_SHORT period Toast.makeText(this, "BLUETOOTH NOT SUPPORTED!", Toast.LENGTH_SHORT).show(); finish(); } //Check if device does support BT Low Energy by hardware. Else close the app(finish())! if (!getBaseContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { //Toast shows a message on the screen for a LENGTH_SHORT period Toast.makeText(this, "BLE NOT SUPPORTED!", Toast.LENGTH_SHORT).show(); finish(); }else { //If BLE is supported, get the BT adapter. Preparing for use! mBTAdapter = BluetoothAdapter.getDefaultAdapter(); //If getting the adapter returns error, close the app with error message! if (mBTAdapter == null) { Toast.makeText(this, "ERROR GETTING BLUETOOTH ADAPTER!", Toast.LENGTH_SHORT).show(); finish(); }else{ //Check if BT is enabled! This method requires BT permissions in the manifest. if (!mBTAdapter.isEnabled()) { //If it is not enabled, ask user to enable it with default BT enable dialog! BT enable response will be received in the onActivityResult method. Intent enableBTintent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBTintent, PERMISSION_REQUEST_COARSE_BL); } } } } @TargetApi(23) private void checkLocBT(){ //If Android version is M (6.0 API 23) or newer, check if it has Location permissions if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ if(this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){ //If Location permissions are not granted for the app, ask user for it! Request response will be received in the onRequestPermissionsResult. requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION); } } } public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) { //Check if permission request response is from Location switch (requestCode) { case PERMISSION_REQUEST_COARSE_LOCATION: { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { //User granted permissions. Setup the scan settings Log.d("TAG", "coarse location permission granted"); } else { //User denied Location permissions. Here you could warn the user that without //Location permissions the app is not able to scan for BLE devices and eventually //Close the app finish(); } return; } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { //Check if the response is from BT if(requestCode == PERMISSION_REQUEST_COARSE_BL){ // User chose not to enable Bluetooth. if (resultCode == Activity.RESULT_CANCELED) { finish(); return; } super.onActivityResult(requestCode, resultCode, data); } } } package com.accent_systems.ibkshelloworld; import android.content.Context; import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import java.io.IOException; public class FirstActivity extends AppCompatActivity { private TextView sound; private TextView soundOff; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); sound = (TextView) findViewById(R.id.volume); soundOff = (TextView) findViewById(R.id.volume_off); final MediaPlayer mp = MediaPlayer.create(this,R.raw.bensound_dubstep); sound.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mp.start(); /* try { mp.reset(); AssetFileDescriptor afd; afd = getAssets().openFd("AudioFile.mp3"); mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength()); mp.prepare(); mp.start(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } */ } }); soundOff.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mp.isPlaying()) { mp.stop(); } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.game_menu, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.bookmark_menu: Intent intent = new Intent(getApplicationContext(), MainActivity.class); startActivity(intent); return true; } return super.onOptionsItemSelected(item); } } package com.accent_systems.ibkshelloworld; import android.app.Application; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; import android.graphics.Color; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.widget.Toast; import org.altbeacon.beacon.Beacon; import org.altbeacon.beacon.BeaconConsumer; import org.altbeacon.beacon.BeaconManager; import org.altbeacon.beacon.BeaconParser; import org.altbeacon.beacon.Identifier; import org.altbeacon.beacon.RangeNotifier; import org.altbeacon.beacon.Region; import org.altbeacon.beacon.startup.BootstrapNotifier; import org.altbeacon.beacon.startup.RegionBootstrap; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class BackgroundScan extends Application implements BootstrapNotifier, BeaconConsumer { private static final String TAG = "BackgroundScan"; private RegionBootstrap regionBootstrap; private BeaconManager mBeaconManager; Region regions[]; public void onCreate() { super.onCreate(); mBeaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this); mBeaconManager.getBeaconParsers().clear(); //set Beacon Layout for iBeacon packet mBeaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")); //set Beacon Layout for Eddystone-UID packet mBeaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(BeaconParser.EDDYSTONE_UID_LAYOUT)); //set period of scan in background and foreground. In every period 'didRangeBeaconsInRegion' callback is called mBeaconManager.setForegroundScanPeriod(5100); mBeaconManager.setForegroundBetweenScanPeriod(3000); mBeaconManager.setBackgroundScanPeriod(5100); mBeaconManager.setBackgroundBetweenScanPeriod(6000); BeaconManager.setAndroidLScanningDisabled(true); regions = new Region[2]; //With "new Region" you are adding the beacon identifier to the list that will be checked in every Scan Period //To add iBeacon region it's necessary to pass as parameters --> (uniqueId = region name, id1=uuid, id2 = major, id3 = minor) regions[0] = new Region("iBeaconAdvertising", Identifier.parse("83FB83FE-A5CE-2865-45CA-19EB81A0502A"), Identifier.parse(""+Integer.parseInt("0006", 16)), Identifier.parse(""+Integer.parseInt("0006", 16))); //To add Eddystone-UID region it's necessary to pass as parameters --> (uniqueId = region name, id1=namespace, id2 = instance, id3 = null) regions[1] = new Region("EdstUIDAdvertising", Identifier.parse("0x00000000000000000000"), Identifier.parse("0x000000000001"), null); mBeaconManager.bind(this); } public void enableRegions() { try { if (regionBootstrap == null) { List list = new ArrayList<>(); for (int i = 0; i < regions.length; i++) { if (regions[i] != null) { list.add(regions[i]); } } regionBootstrap = new RegionBootstrap(this, list); } for (int i = 0; i < regions.length; i++) { if (regions[i] != null) { mBeaconManager.startRangingBeaconsInRegion(regions[i]); } } } catch (Exception e) { e.printStackTrace(); } } public void disableRegions() { try { if (regionBootstrap != null) regionBootstrap.disable(); regionBootstrap = null; for (int i = 0; i < regions.length; i++) { if (regions[i] != null) { mBeaconManager.stopRangingBeaconsInRegion(regions[i]); } } } catch (Exception e) { e.printStackTrace(); } } @Override public void onBeaconServiceConnect() { mBeaconManager.setRangeNotifier(new RangeNotifier() { @Override //Here enters each scan period public void didRangeBeaconsInRegion(Collection beacons, Region region) { Log.i(TAG, "Found " + beacons.size() + " beacons in Region "+ region.getUniqueId() + " - " + region.getId1()+ " - " + region.getId2()+ " - " + region.getId3()); //if number of beacons in region is greater than '0' it means that a beacon or beacons with the correspondent identifier is/are detected. if(beacons.size() > 0) { for (Beacon beacon: beacons) { int rssi = beacon.getRssi(); NotificationCompat.Builder builder = new NotificationCompat.Builder(BackgroundScan.this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("!Beacon found!") .setContentText("You found this beacon ID: "+ region.getId1()+ " - " + region.getId2()+ " - " + region.getId3() + " with RSSI = "+ Integer.toString(rssi)) .setStyle(new NotificationCompat.BigTextStyle() .bigText("You found this beacon ID: "+ region.getId1()+ " - " + region.getId2()+ " - " + region.getId3()+ " with RSSI = "+ Integer.toString(rssi))); builder.setAutoCancel(true); builder.setLights(Color.BLUE, 500, 500); Notification notification = builder.build(); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(1111, notification); //Do something when a packet is received (notification, dialog, open app, ...) /*** EXAMPLE CODE FOR NOTIFICATION *** NotificationCompat.Builder builder = new NotificationCompat.Builder(BackgroundScan.this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("!Beacon found!") .setContentText("You found this beacon ID: "+ region.getId1()+ " - " + region.getId2()+ " - " + region.getId3() + " with RSSI = "+ Integer.toString(rssi)) .setStyle(new NotificationCompat.BigTextStyle() .bigText("You found this beacon ID: "+ region.getId1()+ " - " + region.getId2()+ " - " + region.getId3()+ " with RSSI = "+ Integer.toString(rssi))); builder.setAutoCancel(true); builder.setLights(Color.BLUE, 500, 500); Notification notification = builder.build(); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(1111, notification); // number that identifies our notification /*************************************/ } } } }); //add regions to the list and start scan enableRegions(); } @Override public void didEnterRegion(Region region) { Log.d(TAG, "Beacon detected with namespace id " + region.getId1() +" and instance id: " + region.getId2()); Toast.makeText(getApplicationContext(),"Beacon detected with namespace id " + region.getId1() +" and instance id: " + region.getId2(),Toast.LENGTH_SHORT).show(); } @Override public void didExitRegion(Region region) { Toast.makeText(getApplicationContext(),"Beacon out of region with namespace id " + region.getId1() +" and instance id: " + region.getId2(),Toast.LENGTH_SHORT).show(); Log.d(TAG, "Beacon out of region with namespace id " + region.getId1() +" and instance id: " + region.getId2()); } @Override public void didDetermineStateForRegion(int i, Region region) { //Ignore } }