Java tutorial
/* * Copyright 2016 Google Inc. All rights reserved. * * 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. */ package org.physical_web.physicalweb; import android.app.Activity; import android.app.ProgressDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothProfile; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.net.Uri; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.support.v4.content.FileProvider; import android.widget.Toast; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.UUID; /** * This class is used to connect to a ble gatt service and download a web page. */ public class BluetoothSite extends BluetoothGattCallback { private static final String TAG = BluetoothSite.class.getSimpleName(); private static final UUID SERVICE_UUID = UUID.fromString("ae5946d4-e587-4ba8-b6a5-a97cca6affd3"); private static final UUID CHARACTERISTIC_WEBPAGE_UUID = UUID.fromString("d1a517f0-2499-46ca-9ccc-809bc1c966fa"); // This is BluetoothGatt.CONNECTION_PRIORITY_HIGH, from API level 21 private static final int CONNECTION_PRIORITY_HIGH = 1; private FileOutputStream mHtml; private Activity activity; private BluetoothGatt mBluetoothGatt; private BluetoothGattCharacteristic characteristic; private ConnectionListener mCallback; private ProgressDialog progress; private int transferRate = 20; private boolean running = false; public BluetoothSite(Activity activity) { this.activity = activity; } public Boolean isRunning() { return running; } /** * Connects to the Gatt service of the device to download a web page and displays a progress bar * for the title. * @param deviceAddress The mac address of the bar * @param title The title of the web page being downloaded */ public void connect(String deviceAddress, String title) { running = true; String progressTitle = activity.getString(R.string.page_loading_title) + " " + title; progress = new ProgressDialog(activity); progress.setCancelable(true); progress.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { Log.i(TAG, "Dialog box canceled"); close(); } }); progress.setTitle(progressTitle); progress.setMessage(activity.getString(R.string.page_loading_message)); progress.show(); activity.runOnUiThread(new Runnable() { @Override public void run() { mBluetoothGatt = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(deviceAddress) .connectGatt(activity, false, BluetoothSite.this); } }); } /** * Connects to the Gatt service of the device to download a web page and displays a progress bar * for the title. * @param deviceAddress The mac address of the bar * @param title The title of the web page being downloaded * @param callback The callback for when the connection is complete. */ public void connect(String deviceAddress, String title, ConnectionListener callback) { mCallback = callback; connect(deviceAddress, title); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { // Make sure the site is running. It can stop if the dialog is dismissed very quickly. if (!isRunning()) { close(); return; } // Make sure the read was successful. if (status != BluetoothGatt.GATT_SUCCESS) { Log.i(TAG, "onCharacteristicRead unsuccessful: " + status); close(); Toast.makeText(activity, R.string.ble_download_error_message, Toast.LENGTH_SHORT).show(); return; } // Record the data. Log.i(TAG, "onCharacteristicRead successful"); try { mHtml.write(characteristic.getValue()); } catch (IOException e) { Log.e(TAG, "Could not write to buffer", e); close(); return; } // Request a new read if we are not done. if (characteristic.getValue().length == transferRate) { gatt.readCharacteristic(this.characteristic); return; } // At this point we are done. Show the file. Log.i(TAG, "transfer is complete"); close(); openInChrome(getHtmlFile()); } @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED && status == gatt.GATT_SUCCESS) { Log.i(TAG, "Connected to GATT server"); mBluetoothGatt = gatt; if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { gatt.requestConnectionPriority(CONNECTION_PRIORITY_HIGH); gatt.requestMtu(505); } else { gatt.discoverServices(); } } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { Log.i(TAG, "Disconnected to GATT server"); // ensure progress dialog is removed and running is set false close(); } else if (status != gatt.GATT_SUCCESS) { Log.i(TAG, "Status is " + status); close(); } } @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { Log.i(TAG, "MTU changed to " + mtu); transferRate = mtu - 5; gatt.discoverServices(); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { Log.i(TAG, "Services Discovered"); if (status != BluetoothGatt.GATT_SUCCESS) { Log.e(TAG, "Service discovery failed!"); return; } try { mHtml = new FileOutputStream(getHtmlFile()); } catch (FileNotFoundException e) { Log.e(TAG, "File not found", e); return; } characteristic = gatt.getService(SERVICE_UUID).getCharacteristic(CHARACTERISTIC_WEBPAGE_UUID); gatt.readCharacteristic(characteristic); } private void close() { if (mCallback != null) { mCallback.onConnectionFinished(); } if (progress != null) { progress.dismiss(); } if (mHtml != null) { try { mHtml.close(); } catch (IOException e) { Log.e(TAG, "Failed to close file", e); return; } mHtml = null; } running = false; if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; } private File getHtmlFile() { File websiteDir = new File(activity.getFilesDir(), "Websites"); websiteDir.mkdir(); return new File(websiteDir, "website.html"); } private void openInChrome(File file) { Intent intent = new Intent(Intent.ACTION_VIEW); Uri contentUri = new FileProvider().getUriForFile(activity, "org.physical_web.fileprovider", file); activity.grantUriPermission("com.android.chrome", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(contentUri, "text/html"); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); activity.startActivity(intent); } }