Java tutorial
/* * Copyright (C) 2014 Microchip Technology Inc. and its subsidiaries. You may use this software and any derivatives * exclusively with Microchip products. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS * SOFTWARE, INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR * PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION. * * IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, * COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF * THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON * ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID * DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. * * This file includes code modified from "The Android Open Source Project" copyright (C) 2013. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE TERMS. */ package com.microchip.pcs; import android.Manifest; import android.app.Activity; import android.app.ListActivity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; /** * Activity for scanning and displaying available Bluetooth LE devices. */ public class DeviceScanActivity extends ListActivity { private final static String TAG = DeviceScanActivity.class.getSimpleName(); private LeDeviceListAdapter mLeDeviceListAdapter; //List adapter to hold list of BLE devices from a scan private BluetoothAdapter mBluetoothAdapter; //BluetoothAdapter represents the radio in the Smartphone private boolean mScanning; //Keep track of whether there is a scan in progress private Handler mHandler; //Handler used to stop scanning after time delay private static final int REQUEST_ENABLE_BT = 1; //Constant to identify response from Activity that enables Bluetooth private static final long SCAN_PERIOD = 10000; //Length of time in milliseconds to scan for BLE devices // ---------------------------------------------------------------------------------------------------------------- // Activity launched @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.getActionBar().setTitle("MultiEVo"); //Display "BLE Device Scan" on the action bar mHandler = new Handler(); //Create Handler to stop scanning getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { //Check if BLE is supported Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); //Message that BLE not supported finish(); //End the app } final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); //Get the BluetoothManager mBluetoothAdapter = bluetoothManager.getAdapter(); //Get a reference to the BluetoothAdapter (radio) if (mBluetoothAdapter == null) { //Check if we got the BluetoothAdapter Toast.makeText(this, R.string.bluetooth_not_supported, Toast.LENGTH_SHORT).show(); //Message that Bluetooth not supported finish(); //End the app } } // ---------------------------------------------------------------------------------------------------------------- // Activity resumed // Enable BT if not already enabled, initialize list of BLE devices, start scan for BLE devices @Override protected void onResume() { super.onResume(); ContextCompat.checkSelfPermission(DeviceScanActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION); if (ContextCompat.checkSelfPermission(DeviceScanActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(DeviceScanActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION)) { } else { ActivityCompat.requestPermissions(DeviceScanActivity.this, new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, 1); } } ContextCompat.checkSelfPermission(DeviceScanActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (ContextCompat.checkSelfPermission(DeviceScanActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(DeviceScanActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { } else { ActivityCompat.requestPermissions(DeviceScanActivity.this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 1); } } if (!mBluetoothAdapter.isEnabled()) { //Check if BT is not enabled Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //Create an intent to get permission to enable BT startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); //Fire the intent to start the activity that will return a result based on user response } mLeDeviceListAdapter = new LeDeviceListAdapter(); //Create new list adapter to hold list of BLE devices found during scan setListAdapter(mLeDeviceListAdapter); //Bind our ListActivity to the new list adapter scanLeDevice(true); }//Start scanning for BLE devices @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case 1: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! Do the // contacts-related task you need to do. } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return; } // other 'case' lines to check for other // permissions this app might request } } // ---------------------------------------------------------------------------------------------------------------- // Activity paused // Stop scan and clear device list @Override protected void onPause() { super.onPause(); scanLeDevice(false); //Stop scanning for BLE devices mLeDeviceListAdapter.clear(); //Clear the list of BLE devices found during the scan } // ---------------------------------------------------------------------------------------------------------------- // Options menu is different depending on whether scanning or not // Show Scan option if not scanning or show Stop option if we are scanning @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.scan_start_stop, menu); //Show the Options menu if (!mScanning) { //See if not scanning menu.findItem(R.id.menu_stop).setVisible(false); // hide Stop scan menu option menu.findItem(R.id.menu_scan).setVisible(true); // and show Scan menu option } else { //Else are scanning menu.findItem(R.id.menu_stop).setVisible(true); // show Stop menu option menu.findItem(R.id.menu_scan).setVisible(false); // and hide Scan menu option menu.findItem(R.id.menu_refresh).setActionView(R.layout.indeterminate_progress); //Show progress indicator on Action Bar } return true; } // ---------------------------------------------------------------------------------------------------------------- // Menu item selected // Start or stop scanning for BLE devices @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { //Get which menu item was selected case R.id.menu_scan: //Option to Scan chosen mLeDeviceListAdapter.clear(); //Clear list of BLE devices found scanLeDevice(true); //Start scanning break; case R.id.menu_stop: //Option to Stop scanning chosen scanLeDevice(false); //Stop scanning break; } return true; } // ---------------------------------------------------------------------------------------------------------------- // Test response from request to enable BT adapter in case user did not enable @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { //User chose not to enable Bluetooth. finish(); //Destroy the activity - end the application return; } super.onActivityResult(requestCode, resultCode, data); //Pass the activity result up to the parent method } // ---------------------------------------------------------------------------------------------------------------- // Device selected in list adapter // Start DeviceControlActivity and pass the BLE device name and address to the activity @Override protected void onListItemClick(ListView l, View v, int position, long id) { final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position); //Get the Bluetooth device from the list adapter if (device == null) //Ignore if device is not valid return; final Intent intent = new Intent(this, DeviceControlActivity.class); //Create Intent to start the DeviceControlActivity intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName()); //Add BLE device name to the intent (for info, not needed) intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress()); //Add BLE device address to the intent if (mScanning) { //See if still scanning mBluetoothAdapter.stopLeScan(mLeScanCallback); //Stop the scan in progress mScanning = false; //Indicate that we are not scanning } startActivity(intent); //Start the DeviceControlActivity } // ---------------------------------------------------------------------------------------------------------------- // Scan for BLE device for SCAN_PERIOD milliseconds. // The mLeScanCallback method is called each time a device is found during the scan private void scanLeDevice(final boolean enable) { if (enable) { //Method was called with option to start scanning mHandler.postDelayed(new Runnable() { //Create delayed runnable that will stop the scan when it runs after SCAN_PERIOD milliseconds @Override public void run() { mScanning = false; //Indicate that we are not scanning - used for menu Stop/Scan context mBluetoothAdapter.stopLeScan(mLeScanCallback); //Stop scanning - callback method indicates which scan to stop invalidateOptionsMenu(); //Indicate that the options menu has changed, so should be recreated. } }, SCAN_PERIOD); mScanning = true; //Indicate that we are busy scanning - used for menu Stop/Scan context mBluetoothAdapter.startLeScan(mLeScanCallback); //Start scanning with callback method to execute when a new BLE device is found } else { //Method was called with option to stop scanning mScanning = false; //Indicate that we are not scanning - used for menu Stop/Scan context mBluetoothAdapter.stopLeScan(mLeScanCallback); //Stop scanning - callback method indicates which scan to stop } invalidateOptionsMenu(); //Indicate that the options menu has changed, so should be recreated. } // ---------------------------------------------------------------------------------------------------------------- // Device scan callback. Bluetooth adapter calls this method when a new device is discovered during a scan. private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { //Android calls method with Bluetooth device advertising information runOnUiThread(new Runnable() { //Create runnable that will add the device to the list adapter @Override public void run() { mLeDeviceListAdapter.addDevice(device); //Add the device to the list adapter that will show all the available devices Log.d(TAG, "Found BLE Device: " + device.getAddress()); //Debug information to log the devices as they are found mLeDeviceListAdapter.notifyDataSetChanged(); //Tell the list adapter that it needs to refresh the view } }); } }; // ---------------------------------------------------------------------------------------------------------------- // Adapter for holding devices found through scanning. Acts as a bridge between the list data and the view deiplaying the data private class LeDeviceListAdapter extends BaseAdapter { private final ArrayList<BluetoothDevice> mLeDevices; //List of Bluetooth devices found during scan private final LayoutInflater mInflator; //Layout inflator to display information about each device public LeDeviceListAdapter() { //Constructor initializes the list super(); mLeDevices = new ArrayList<BluetoothDevice>(); //New arrayliat to hold the Bluetooth devices mInflator = DeviceScanActivity.this.getLayoutInflater(); //Get the layout inflator associated with this activity } public void addDevice(BluetoothDevice device) { //Method to add device to list if (!mLeDevices.contains(device)) { //First check that it is a new device not in the list mLeDevices.add(device); //Add the device to the list } } public BluetoothDevice getDevice(int position) { //Method to get device from list return mLeDevices.get(position); //Return the device at the selected position } public void clear() { //Method to clear the list mLeDevices.clear(); } @Override public int getCount() { //Method to get number of devices in the list return mLeDevices.size(); } @Override public Object getItem(int i) { //Method to get generic object from the list - Not used return mLeDevices.get(i); } @Override public long getItemId(int i) { //Method to get object's ID from the list - Not used return i; } @Override public View getView(int i, View view, ViewGroup viewGroup) { //Method to get view of device and show on the screen ViewHolder viewHolder; //Declare the structure to hold TextViews to show the name and address that will be displayed // General ListView optimization code. if (view == null) { //Check that we were passed a reference for a new view view = mInflator.inflate(R.layout.listitem_device, null); //Put the list item (from XML layout file) on the screen viewHolder = new ViewHolder(); //New object to hold references to which TextViews hold the name and address of the device viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address); //Get reference to TextView for the address viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name); //Get reference to TextView for the name view.setTag(viewHolder); //Tag the view with the ViewHolder } else { viewHolder = (ViewHolder) view.getTag(); //View already exists so get the ViewHolder that was used tag the view } BluetoothDevice device = mLeDevices.get(i); //Get the device associated with this view final String deviceName = device.getName(); //Get the name of the device if (deviceName != null && deviceName.length() > 0) { //Check that the name is valid (name is not required in the BLE advertising packet) viewHolder.deviceName.setText(deviceName); //If so show it on the screen in the TextView } else { viewHolder.deviceName.setText(R.string.unknown_device); //If name is invalid, put "Unknown Device" on the screen } //(this happens if the advertisement packet does not contain a name) viewHolder.deviceAddress.setText(device.getAddress()); //Print the MAC address on the screen return view; } } // ---------------------------------------------------------------------------------------------------------------- // Class to hold device name and address static class ViewHolder { TextView deviceName; //TextView to show device name in the list on the screen TextView deviceAddress; //TextView to show device address in the list on the screen } }