Controller - streams sensor data from your Android device
38 | to Bluefruit LE over UART
39 |
40 |
41 |
42 |
43 |
44 | Note - If a peripheral does not offer the UART service,
45 | tapping Connect will automatically enter Info mode
46 |
47 |
48 |
49 | Privacy policy:
50 |
51 | https://www.adafruit.com/appprivacy
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/ui/utils/ExpandableHeightListView.java:
--------------------------------------------------------------------------------
1 | // Based on ExpandedHeightGridView from http://stackoverflow.com/questions/8481844/gridview-height-gets-cut
2 |
3 | package com.adafruit.bluefruit.le.connect.ui.utils;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ListView;
10 | import android.widget.ScrollView;
11 |
12 | public class ExpandableHeightListView extends ListView {
13 |
14 | boolean mExpanded = false;
15 |
16 | public ExpandableHeightListView(Context context) {
17 | super(context);
18 | }
19 |
20 | public ExpandableHeightListView(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | }
23 |
24 | public ExpandableHeightListView(Context context, AttributeSet attrs, int defStyle) {
25 | super(context, attrs, defStyle);
26 | }
27 |
28 | public boolean isExpanded() {
29 | return mExpanded;
30 | }
31 |
32 | @Override
33 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
34 | // HACK! TAKE THAT ANDROID!
35 | if (isExpanded()) {
36 | // Calculate entire height by providing a very large height hint.
37 | // View.MEASURED_SIZE_MASK represents the largest height possible.
38 | int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
39 | super.onMeasure(widthMeasureSpec, expandSpec);
40 |
41 | ViewGroup.LayoutParams params = getLayoutParams();
42 | params.height = getMeasuredHeight();
43 | } else {
44 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
45 | }
46 | }
47 |
48 | public void setExpanded(boolean expanded) {
49 | mExpanded = expanded;
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_neopixel_boardselector.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
20 |
21 |
25 |
26 |
33 |
34 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/settings/barcode/BarcodeTrackerFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.adafruit.bluefruit.le.connect.app.settings.barcode;
17 |
18 | import com.google.android.gms.vision.MultiProcessor;
19 | import com.google.android.gms.vision.Tracker;
20 | import com.google.android.gms.vision.barcode.Barcode;
21 |
22 | /**
23 | * Factory for creating a tracker and associated graphic to be associated with a new barcode. The
24 | * multi-processor uses this factory to create barcode trackers as needed -- one for each barcode.
25 | */
26 | public class BarcodeTrackerFactory implements MultiProcessor.Factory {
27 | private GraphicOverlay mGraphicOverlay;
28 | public BarcodeGraphicTracker.BarcodeTrackerListener mListener;
29 |
30 | public BarcodeTrackerFactory(GraphicOverlay barcodeGraphicOverlay, BarcodeGraphicTracker.BarcodeTrackerListener listener) {
31 | mGraphicOverlay = barcodeGraphicOverlay;
32 | mListener = listener;
33 | }
34 |
35 | @Override
36 | public Tracker create(Barcode barcode) {
37 | BarcodeGraphic graphic = new BarcodeGraphic(mGraphicOverlay);
38 | return new BarcodeGraphicTracker(mGraphicOverlay, graphic, mListener);
39 | }
40 |
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.utils;
2 |
3 | import android.content.res.AssetManager;
4 | import android.os.Environment;
5 |
6 | import java.io.File;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.io.OutputStream;
11 |
12 | public class FileUtils {
13 |
14 | public static String readAssetsFile(String filename, AssetManager assetManager) {
15 | String result = null;
16 |
17 | try {
18 | InputStream is = assetManager.open(filename);
19 | int size = is.available();
20 |
21 | byte[] buffer = new byte[size];
22 | is.read(buffer);
23 | is.close();
24 |
25 | result = new String(buffer, "UTF-8");
26 |
27 | } catch (IOException e) {
28 | e.printStackTrace();
29 | }
30 |
31 | return result;
32 | }
33 |
34 | public static String copyAssetFile(AssetManager assetManager, String inputFilename, String outputFilename) {
35 |
36 | String outPath = null;
37 |
38 | try {
39 | InputStream input = assetManager.open(inputFilename);
40 |
41 | // Create new file to copy into.
42 | outPath = Environment.getExternalStorageDirectory() + java.io.File.separator + outputFilename;
43 | File outFile = new File(outPath);
44 | OutputStream output = new FileOutputStream(outFile);
45 | copyFile(input, output);
46 | input.close();
47 | output.flush();
48 | output.close();
49 |
50 | } catch (IOException e) {
51 | e.printStackTrace();
52 | }
53 |
54 | return outPath;
55 | }
56 |
57 | private static void copyFile(InputStream in, OutputStream out) throws IOException {
58 | byte[] buffer = new byte[1024];
59 | int read;
60 | while ((read = in.read(buffer)) != -1) {
61 | out.write(buffer, 0, read);
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/antonio/Development/_androidsdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | -keep class * extends java.util.ListResourceBundle {
20 | protected Object[][] getContents();
21 | }
22 |
23 | -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
24 | public static final *** NULL;
25 | }
26 |
27 | -keepnames @com.google.android.gms.common.annotation.KeepName class *
28 | -keepclassmembernames class * {
29 | @com.google.android.gms.common.annotation.KeepName *;
30 | }
31 |
32 | -keepnames class * implements android.os.Parcelable {
33 | public static final ** CREATOR;
34 | }
35 |
36 | # Custom Proguard settings
37 | -keep class * extends android.app.Activity
38 | -assumenosideeffects class android.util.Log {
39 | public static boolean isLoggable(java.lang.String, int);
40 | public static int v(...);
41 | public static int i(...);
42 | public static int w(...);
43 | public static int d(...);
44 | public static int e(...);
45 | }
46 |
47 | # Nordic DFU library
48 | -keep class no.nordicsemi.android.dfu.** { *; }
49 |
50 | # Paho library logger
51 | -keep class org.eclipse.paho.client.mqttv3.logging.JSR47Logger {
52 | *;
53 | }
54 |
55 | # Avoid warnings for old code in Paho 1.0.2 on Android Studio 2
56 | -keep class org.eclipse.paho.client.mqttv3.persist.** { *; }
57 | -dontwarn org.eclipse.paho.client.mqttv3.persist.**
58 | -keepattributes Exceptions, Signature, InnerClasses
--------------------------------------------------------------------------------
/app/src/main/assets/help/controller_help.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
The Controller module streams sensor data from your iOS device to Bluefruit LE over UART at a frequency of 10Hz. Activation of a switch will begin sending relevant data. Each
4 | packet sent begins with single byte char “!” (0x21) followed by a single byte char initial, and sensor data encoded as float values of 4 byte length
5 |
6 |
Quaternion sends Android Device Motion data to describe device attitude. This data is derived from Accelerometer, Gyro, and Magnetometer readings.
7 | Format: [ ‘!’ ] [ ‘Q’ ] [ float x ] [ float y ] [ float z ] [ float w ]
8 |
9 |
10 |
Accelerometer sends raw accelerometer data
11 | Format: [ ‘!’ ] [ ‘A’ ] [ float x ] [ float y ] [ float z ]
12 |
13 |
14 |
Gyro sends raw gyroscope data as:
15 | Format: [ ‘!’ ] [ ‘G’ ] [ float x ] [ float y ] [ float z ]
16 |
17 |
18 |
Magnetometer sends raw magnetometer data which is uncalibrated and does not account for interference from source device.
19 | Format: [ ‘!’ ] [ ‘M’ ] [ float x ] [ float y ] [ float z ]
20 |
21 |
22 |
Location sends GPS data and requires user permission before initial use.
23 | Format: [ ‘!’ ] [ ‘L’ ] [ float latitude ] [ float longitude ] [ float altitude ]
24 |
25 |
26 | The Control Pad function provides a familiar momentary button interface for common control scenarios. Data is sent on the press and release of each button. Each packet consists of
27 | 4 bytes, each representing a char value. The first two chars identify the packet as a button message, the third specifies a button, and the fourth signifies either a press or
28 | release. Example:
29 |
30 | Button 4 pressed: [ ‘!’ ] [ ‘B’ ] [ ‘4’ ] [ ‘1’ ]
31 | Button 4 released: [ ‘!’ ] [ ‘B’ ] [ ‘4’ ] [ ‘0’ ]
32 |
33 |
34 | Note: Any activated sensor data streams will continue while using the Control Pad.
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/shortener/BitlyShortenerAsyncTask.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.app.shortener;
2 |
3 | // TODO: remove Apache library and use HttpURLConnection (Apache is deprecated since Android Marshmallow) http://developer.android.com/preview/behavior-changes.html#behavior-apache-http-client
4 |
5 | import org.apache.http.HttpEntity;
6 | import org.apache.http.HttpResponse;
7 | import org.apache.http.client.HttpClient;
8 | import org.apache.http.client.methods.HttpGet;
9 | import org.apache.http.impl.client.DefaultHttpClient;
10 | import org.apache.http.util.EntityUtils;
11 |
12 | import java.io.IOException;
13 | import java.io.UnsupportedEncodingException;
14 | import java.net.URLEncoder;
15 |
16 | public class BitlyShortenerAsyncTask extends ShortenerAsyncTask {
17 |
18 | // Constants
19 | private static final String kBitlyApiKey = "abcdefghijklmnopqr1234567890"; // The previous key wasnt valid either but folks kept bugging us over it.
20 |
21 |
22 | public BitlyShortenerAsyncTask(ShortenerListener listener) {
23 | super(listener);
24 | }
25 |
26 | @Override
27 | protected String doInBackground(String... urls) {
28 | try {
29 | String originalUrl = urls[0];
30 | String url = bitlyShorteningEndPoint(originalUrl);
31 |
32 | HttpGet httpGet = new HttpGet(url);
33 | HttpClient httpclient = new DefaultHttpClient();
34 | HttpResponse response = httpclient.execute(httpGet);
35 |
36 | final int status = response.getStatusLine().getStatusCode();
37 | if (status >= 200 && status < 300) {
38 | HttpEntity entity = response.getEntity();
39 | String data = EntityUtils.toString(entity);
40 | return data;
41 | }
42 |
43 |
44 | } catch (IOException e) {
45 | e.printStackTrace();
46 | }
47 | return null;
48 | }
49 |
50 | private String bitlyShorteningEndPoint(String uri) throws UnsupportedEncodingException {
51 | String encodedUri = URLEncoder.encode(uri, "UTF-8");
52 | return String.format("https://api-ssl.bitly.com/v3/shorten?access_token=%s&longUrl=%s&format=txt", kBitlyApiKey, encodedUri);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_scan_item_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
25 |
26 |
32 |
33 |
34 |
35 |
42 |
43 |
51 |
52 |
56 |
57 |
58 |
59 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_mqttsettingscodereader.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
26 |
27 |
32 |
33 |
40 |
41 |
45 |
46 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/ui/keyboard/CustomEditTextFormatter.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.ui.keyboard;
2 |
3 | import android.text.Editable;
4 | import android.text.TextUtils;
5 | import android.text.TextWatcher;
6 | import android.widget.EditText;
7 |
8 | public class CustomEditTextFormatter {
9 |
10 | public static void attachToEditText(final EditText editText, final int maxNumCharacters, final String separator, final int groupCharactersCount) {
11 | editText.addTextChangedListener(new TextWatcher() {
12 | @Override
13 | public void onTextChanged(CharSequence s, int start, int before, int count) {
14 | }
15 |
16 | @Override
17 | public void beforeTextChanged(CharSequence s, int start, int count,
18 | int after) {
19 | }
20 |
21 | @Override
22 | public void afterTextChanged(Editable s) {
23 | String text = s.toString();
24 |
25 | String newText = formatText(text, maxNumCharacters, separator, groupCharactersCount);
26 |
27 | if (!text.equals(newText)) {
28 | editText.setText(newText);
29 | editText.setSelection(newText.length());
30 | }
31 | }
32 | });
33 | }
34 |
35 | public static String formatText(String text, int maxNumCharacters, String separator, int groupCharactersCount) {
36 | // Split the string into character groups
37 | String mergedText = text.replaceAll(separator, "");
38 |
39 | if (mergedText.length() > maxNumCharacters) {
40 | mergedText = mergedText.substring(0, maxNumCharacters);
41 | }
42 |
43 | String[] characterGroups = splitStringEvery(mergedText, groupCharactersCount);
44 | String newText = TextUtils.join(separator, characterGroups);
45 |
46 | return newText;
47 | }
48 |
49 |
50 | private static String[] splitStringEvery(String s, int interval) { // based on: http://stackoverflow.com/questions/12295711/split-a-string-at-every-nth-position
51 | int arrayLength = (int) Math.ceil(((s.length() / (double) interval)));
52 | String[] result = new String[arrayLength];
53 |
54 | int j = 0;
55 | int lastIndex = result.length - 1;
56 | for (int i = 0; i < lastIndex; i++) {
57 | result[i] = s.substring(j, j + interval);
58 | j += interval;
59 | }
60 | if (lastIndex >= 0) {
61 | result[lastIndex] = s.substring(j);
62 | }
63 |
64 | return result;
65 | }
66 |
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/update/ProgressFragmentDialog.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.app.update;
2 |
3 |
4 | import android.app.Dialog;
5 | import android.app.DialogFragment;
6 | import android.app.ProgressDialog;
7 | import android.content.DialogInterface;
8 | import android.os.Bundle;
9 |
10 | public class ProgressFragmentDialog extends DialogFragment {
11 | private ProgressDialog mDialog;
12 | private DialogInterface.OnCancelListener mCancelListener;
13 |
14 | private String mMessage;
15 | private int mProgress;
16 | private boolean mIndeterminate;
17 |
18 | @Override
19 | public Dialog onCreateDialog(Bundle savedInstanceState) {
20 | setRetainInstance(true);
21 |
22 | mMessage = getArguments().getString("message");
23 |
24 | mDialog = new ProgressDialog(getActivity());
25 | mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
26 | mDialog.setMax(100);
27 | mDialog.setCanceledOnTouchOutside(false);
28 | mDialog.setCancelable(true);
29 |
30 | updateUI();
31 |
32 | return mDialog;
33 | }
34 |
35 | @Override
36 | public void onDestroyView()
37 | {
38 | Dialog dialog = getDialog();
39 |
40 | // Work around bug: http://code.google.com/p/android/issues/detail?id=17423
41 | if ((dialog != null) && getRetainInstance())
42 | dialog.setDismissMessage(null);
43 |
44 | super.onDestroyView();
45 | }
46 |
47 | @Override
48 | public void onCancel(DialogInterface dialog) { // to avoid problems with setting oncancellistener after dialog has been created
49 | if (mCancelListener != null) {
50 | mCancelListener.onCancel(dialog);
51 | }
52 |
53 | super.onCancel(dialog);
54 | }
55 |
56 |
57 | public void setOnCancelListener(DialogInterface.OnCancelListener listener) {
58 | mCancelListener = listener;
59 | }
60 |
61 | /*
62 | public ProgressDialog getDialog() {
63 | return mDialog;
64 | }
65 | */
66 |
67 | public void setMessage(String message) {
68 | mMessage = message;
69 | mDialog.setMessage(message);
70 | }
71 |
72 | public void setProgress(int progress) {
73 | mProgress = progress;
74 | mDialog.setProgress(progress);
75 | }
76 |
77 | public void setIndeterminate(boolean indeterminate) {
78 | mIndeterminate = indeterminate;
79 | mDialog.setIndeterminate(indeterminate);
80 | }
81 |
82 | private void updateUI() {
83 | mDialog.setMessage(mMessage);
84 | mDialog.setProgress(mProgress);
85 | mDialog.setIndeterminate(mIndeterminate);
86 | }
87 |
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_info_item_characteristic.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
27 |
28 |
33 |
34 |
35 |
40 |
41 |
48 |
49 |
56 |
57 |
58 |
59 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_neopixel_boardtype.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
20 |
21 |
25 |
26 |
33 |
34 |
38 |
39 |
44 |
45 |
50 |
51 |
57 |
58 |
59 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/ui/utils/ExpandableHeightExpandableListView.java:
--------------------------------------------------------------------------------
1 | // Based on ExpandedHeightGridView from http://stackoverflow.com/questions/8481844/gridview-height-gets-cut
2 |
3 | package com.adafruit.bluefruit.le.connect.ui.utils;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ExpandableListView;
10 | import android.widget.ScrollView;
11 |
12 | public class ExpandableHeightExpandableListView extends ExpandableListView {
13 |
14 | private boolean mExpanded = false;
15 |
16 | public ExpandableHeightExpandableListView(Context context) {
17 | super(context);
18 | }
19 |
20 | public ExpandableHeightExpandableListView(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | }
23 |
24 | public ExpandableHeightExpandableListView(Context context, AttributeSet attrs, int defStyle) {
25 | super(context, attrs, defStyle);
26 | }
27 |
28 | public boolean isExpanded() {
29 | return mExpanded;
30 | }
31 |
32 | @Override
33 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
34 | // HACK! TAKE THAT ANDROID!
35 | if (isExpanded()) {
36 | // Calculate entire height by providing a very large height hint.
37 | // View.MEASURED_SIZE_MASK represents the largest height possible.
38 | int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
39 | super.onMeasure(widthMeasureSpec, expandSpec);
40 |
41 | ViewGroup.LayoutParams params = getLayoutParams();
42 | params.height = getMeasuredHeight();
43 | } else {
44 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
45 | }
46 | }
47 |
48 | public void setExpanded(boolean expanded) {
49 | mExpanded = expanded;
50 | }
51 |
52 |
53 | public void scrollToGroup(int groupPosition, View view, ScrollView parentScrollView){
54 | final float baseY = getY();
55 | final float currentGroupPosY = baseY + view.getY();
56 | final int currentScrollY = parentScrollView.getScrollY();
57 | final View nextGroupView = findViewWithTag(groupPosition+1);
58 |
59 | if (currentScrollY > currentGroupPosY) {
60 | parentScrollView.smoothScrollTo(parentScrollView.getScrollX(), view.getTop());
61 | }
62 | else if (nextGroupView != null) {
63 | final float nextGroupPosY = baseY + nextGroupView.getY();
64 | if (currentScrollY + parentScrollView.getHeight() < nextGroupPosY) {
65 | parentScrollView.smoothScrollTo(0, nextGroupView.getBottom());
66 | }
67 | }
68 | else {
69 | parentScrollView.smoothScrollTo(parentScrollView.getScrollX(), getBottom()-parentScrollView.getHeight());
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/neopixel/NeopixelHelpActivity.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.app.neopixel;
2 |
3 | import android.content.Intent;
4 | import android.content.res.AssetManager;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.support.v4.content.FileProvider;
8 | import android.util.Log;
9 | import android.view.View;
10 |
11 | import com.adafruit.bluefruit.le.connect.R;
12 | import com.adafruit.bluefruit.le.connect.app.CommonHelpActivity;
13 |
14 | import java.io.File;
15 | import java.io.FileOutputStream;
16 | import java.io.IOException;
17 | import java.io.InputStream;
18 |
19 | public class NeopixelHelpActivity extends CommonHelpActivity {
20 |
21 | // Constants
22 | private static final String AUTHORITY = "com.adafruit.bluefruit.le.connect.fileprovider"; // Same as the authority field on the manifest provider
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_neopixelhelp);
28 |
29 | setupHelp();
30 | }
31 |
32 | public void onClickExportSketch(View view) {
33 | exportSketch();
34 | }
35 |
36 | private void exportSketch() {
37 |
38 | // Copy file from assets to FilesDir
39 | String filename = "Neopixel_Arduino.zip";
40 | File file = new File(getFilesDir(), filename);
41 | AssetManager assets = getResources().getAssets();
42 | try {
43 | copy(assets.open("neopixel" + File.separator + filename), file);
44 | } catch (IOException e) {
45 | Log.e("FileProvider", "Exception copying from assets", e);
46 | }
47 |
48 | // Export uri
49 | Uri uri = FileProvider.getUriForFile(this, AUTHORITY, file);
50 |
51 | if (uri != null) {
52 | Intent intentShareFile = new Intent(Intent.ACTION_SEND);
53 | intentShareFile.setType("application/zip");
54 |
55 | intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
56 |
57 | intentShareFile.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.neopixel_help_export_subject));
58 | intentShareFile.putExtra(Intent.EXTRA_TEXT, getString(R.string.neopixel_help_export_text));
59 | intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // to avoid the android.os.FileUriExposedException on api 24+
60 | startActivity(Intent.createChooser(intentShareFile, getString(R.string.neopixel_help_export_chooser_title)));
61 | }
62 | }
63 |
64 | private static void copy(InputStream in, File dst) throws IOException {
65 | FileOutputStream out = new FileOutputStream(dst);
66 | byte[] buf = new byte[1024];
67 | int len;
68 |
69 | while ((len = in.read(buf)) > 0) {
70 | out.write(buf, 0, len);
71 | }
72 |
73 | in.close();
74 | out.close();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/settings/barcode/BarcodeGraphicTracker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.adafruit.bluefruit.le.connect.app.settings.barcode;
17 |
18 | import com.google.android.gms.vision.Detector;
19 | import com.google.android.gms.vision.Tracker;
20 | import com.google.android.gms.vision.barcode.Barcode;
21 |
22 | /**
23 | * Generic tracker which is used for tracking or reading a barcode (and can really be used for
24 | * any type of item). This is used to receive newly detected items, add a graphical representation
25 | * to an overlay, update the graphics as the item changes, and remove the graphics when the item
26 | * goes away.
27 | */
28 | public class BarcodeGraphicTracker extends Tracker {
29 |
30 | public interface BarcodeTrackerListener {
31 | void onCodeScanned(String contents);
32 | }
33 |
34 |
35 | private GraphicOverlay mOverlay;
36 | private BarcodeGraphic mGraphic;
37 | private BarcodeTrackerListener mListener;
38 |
39 | BarcodeGraphicTracker(GraphicOverlay overlay, BarcodeGraphic graphic, BarcodeGraphicTracker.BarcodeTrackerListener listener) {
40 | mOverlay = overlay;
41 | mGraphic = graphic;
42 | mListener = listener;
43 | }
44 |
45 | /**
46 | * Start tracking the detected item instance within the item overlay.
47 | */
48 | @Override
49 | public void onNewItem(int id, Barcode item) {
50 | mGraphic.setId(id);
51 | }
52 |
53 | /**
54 | * Update the position/characteristics of the item within the overlay.
55 | */
56 | @Override
57 | public void onUpdate(Detector.Detections detectionResults, Barcode item) {
58 | mOverlay.add(mGraphic);
59 | mGraphic.updateItem(item);
60 | if (mListener != null) {
61 | mListener.onCodeScanned(item.rawValue);
62 | }
63 | }
64 |
65 | /**
66 | * Hide the graphic when the corresponding object was not detected. This can happen for
67 | * intermediate frames temporarily, for example if the object was momentarily blocked from
68 | * view.
69 | */
70 | @Override
71 | public void onMissing(Detector.Detections detectionResults) {
72 | mOverlay.remove(mGraphic);
73 | }
74 |
75 | /**
76 | * Called when the item is assumed to be gone for good. Remove the graphic annotation from
77 | * the overlay.
78 | */
79 | @Override
80 | public void onDone() {
81 | mOverlay.remove(mGraphic);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/UriBeaconUtils.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.app;
2 |
3 | import com.adafruit.bluefruit.le.connect.ble.BleUtils;
4 |
5 | import java.io.UnsupportedEncodingException;
6 | import java.util.Arrays;
7 |
8 | class UriBeaconUtils {
9 |
10 | private static String getSchemeFromPrefix(byte schemePrefix) {
11 | switch (schemePrefix) {
12 | case 0:
13 | return "http://www.";
14 | case 1:
15 | return "https://www.";
16 | case 2:
17 | return "http://";
18 | case 3:
19 | return "https://";
20 | case 4:
21 | return "urn:uuid:";
22 | default:
23 | return null;
24 | }
25 | }
26 |
27 | private static String getUrlEncodingFromByte(byte value) {
28 | switch (value) {
29 | case 0:
30 | return ".com/";
31 | case 1:
32 | return ".org/";
33 | case 2:
34 | return ".edu/";
35 | case 3:
36 | return ".net/";
37 | case 4:
38 | return ".info/";
39 | case 5:
40 | return ".biz/";
41 | case 6:
42 | return ".gov/";
43 | case 7:
44 | return ".com/";
45 | case 8:
46 | return ".org/";
47 | case 9:
48 | return ".edu/";
49 | case 10:
50 | return ".net/";
51 | case 11:
52 | return ".info/";
53 | case 12:
54 | return ".biz/";
55 | case 13:
56 | return ".gov/";
57 | default:
58 | return null;
59 | }
60 | }
61 |
62 | static String getUriFromAdvertisingPacket(byte[] scanRecord) {
63 | byte schemeByte = scanRecord[10];
64 | String scheme = UriBeaconUtils.getSchemeFromPrefix(schemeByte);
65 |
66 | String url = "";
67 | if (schemeByte == 0x04) // Special case for urn:uuid
68 | {
69 | byte[] urlBytes = Arrays.copyOfRange(scanRecord, 11, 11 + 16);
70 | url = BleUtils.getUuidStringFromByteArray(urlBytes);
71 | } else {
72 | final int length = scanRecord[4] - 6; // 6 fixed fields bytes (uri length is total lenght-6)
73 | byte[] urlBytes = Arrays.copyOfRange(scanRecord, 11, 11 + length);
74 |
75 | try {
76 | url = new String(urlBytes, "UTF-8");
77 | } catch (UnsupportedEncodingException e) {
78 | e.printStackTrace();
79 | }
80 |
81 | for (int i = urlBytes.length - 1; i >= 0; i--) // Go backwards because we are replacing single characters with strings that will change the url lenght
82 | {
83 | String urlEncoding = getUrlEncodingFromByte(urlBytes[i]);
84 | if (urlEncoding != null) {
85 | url = new StringBuffer(url).insert(i, urlEncoding).toString();
86 | }
87 | }
88 | }
89 |
90 | String text = scheme + url;
91 | return text;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/neopixel/NeopixelColorPickerActivity.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.app.neopixel;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Color;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.view.View;
8 | import android.widget.Button;
9 | import android.widget.TextView;
10 |
11 | import com.adafruit.bluefruit.le.connect.R;
12 | import com.larswerkman.holocolorpicker.ColorPicker;
13 | import com.larswerkman.holocolorpicker.SaturationBar;
14 | import com.larswerkman.holocolorpicker.ValueBar;
15 |
16 | public class NeopixelColorPickerActivity extends AppCompatActivity implements ColorPicker.OnColorChangedListener {
17 |
18 | // Result return
19 | public static final String kActivityParameter_SelectedColorKey = "kActivityParameter_SelectedColorKey";
20 | public static final String kActivityResult_SelectedColorResultKey = "kActivityResult_SelectedColorResultKey";
21 |
22 | // UI
23 | private ColorPicker mColorPicker;
24 | private View mRgbColorView;
25 | private TextView mRgbTextView;
26 |
27 | private int mSelectedColor;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | // setContentView(R.layout.activity_neopixel_colorpicker);
33 | setContentView(R.layout.activity_color_picker);
34 |
35 | Intent intent = getIntent();
36 | mSelectedColor = intent.getIntExtra(kActivityParameter_SelectedColorKey, Color.WHITE);
37 |
38 | // UI
39 | mRgbColorView = findViewById(R.id.rgbColorView);
40 | mRgbTextView = (TextView) findViewById(R.id.rgbTextView);
41 |
42 | SaturationBar mSaturationBar = (SaturationBar) findViewById(R.id.saturationbar);
43 | ValueBar mValueBar = (ValueBar) findViewById(R.id.valuebar);
44 | mColorPicker = (ColorPicker) findViewById(R.id.colorPicker);
45 | if (mColorPicker != null) {
46 | mColorPicker.addSaturationBar(mSaturationBar);
47 | mColorPicker.addValueBar(mValueBar);
48 | mColorPicker.setOnColorChangedListener(this);
49 |
50 | mColorPicker.setOldCenterColor(mSelectedColor);
51 | mColorPicker.setColor(mSelectedColor);
52 | }
53 | onColorChanged(mSelectedColor);
54 |
55 | Button sendButton = (Button) findViewById(R.id.sendButton);
56 | sendButton.setText(R.string.neopixel_colorpicker_setcolor);
57 |
58 | }
59 |
60 | @Override
61 | public void onColorChanged(int color) {
62 | // Save selected color
63 | mSelectedColor = color;
64 |
65 | // Update UI
66 | mRgbColorView.setBackgroundColor(color);
67 |
68 | final int r = (color >> 16) & 0xFF;
69 | final int g = (color >> 8) & 0xFF;
70 | final int b = (color >> 0) & 0xFF;
71 | final String text = String.format(getString(R.string.colorpicker_rgbformat), r, g, b);
72 | mRgbTextView.setText(text);
73 |
74 | }
75 |
76 | public void onClickSend(View view) {
77 | Intent output = new Intent();
78 | output.putExtra(kActivityResult_SelectedColorResultKey, mSelectedColor);
79 | setResult(RESULT_OK, output);
80 | finish();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/assets/help/licenses.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Firmware Update based on Nordic DFU Library
8 |
9 |
Copyright (c) 2015, Nordic Semiconductor All rights reserved.
10 |
11 |
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
12 |
13 |
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
14 |
15 |
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
16 |
17 |
* Neither the name of Android-DFU-Library nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
18 |
19 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Color picker based on HoloColorPicker by Lars Werkman.
28 |
29 |
Copyright 2012 Lars Werkman
30 |
31 |
Licensed under the Apache License, Version 2.0 (the "License");
32 | you may not use this file except in compliance with the License.
33 | You may obtain a copy of the License at
34 |
35 |
http://www.apache.org/licenses/LICENSE-2.0
36 |
37 |
Unless required by applicable law or agreed to in writing, software
38 | distributed under the License is distributed on an "AS IS" BASIS,
39 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40 | See the License for the specific language governing permissions and
41 | limitations under the License.
42 |
43 |
44 |
45 |
46 |
47 |
48 |
LegacyCompatFileProvider by CommonsWare.
49 |
50 |
Copyright 2015 CommonsWare, LLC
51 |
52 |
Licensed under the Apache License, Version 2.0 (the "License");
53 | you may not use this file except in compliance with the License.
54 | You may obtain a copy of the License at
55 |
56 |
http://www.apache.org/licenses/LICENSE-2.0
57 |
58 |
Unless required by applicable law or agreed to in writing, software
59 | distributed under the License is distributed on an "AS IS" BASIS,
60 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
61 | See the License for the specific language governing permissions and
62 | limitations under the License.
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_color_picker.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
16 |
17 |
22 |
23 |
28 |
29 |
34 |
35 |
40 |
41 |
48 |
49 |
53 |
54 |
55 |
60 |
61 |
66 |
67 |
68 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_controller.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
23 |
24 |
30 |
31 |
39 |
40 |
41 |
44 |
45 |
49 |
50 |
55 |
56 |
64 |
65 |
71 |
72 |
73 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/settings/barcode/BarcodeGraphic.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.adafruit.bluefruit.le.connect.app.settings.barcode;
17 |
18 | import android.graphics.Canvas;
19 | import android.graphics.Color;
20 | import android.graphics.Paint;
21 | import android.graphics.RectF;
22 |
23 | import com.google.android.gms.vision.barcode.Barcode;
24 |
25 | /**
26 | * Graphic instance for rendering barcode position, size, and ID within an associated graphic
27 | * overlay view.
28 | */
29 | public class BarcodeGraphic extends GraphicOverlay.Graphic {
30 |
31 | private int mId;
32 |
33 | private static final int COLOR_CHOICES[] = {
34 | Color.BLUE,
35 | Color.CYAN,
36 | Color.GREEN
37 | };
38 |
39 | private static int mCurrentColorIndex = 0;
40 |
41 | private Paint mRectPaint;
42 | private Paint mTextPaint;
43 | private volatile Barcode mBarcode;
44 |
45 | BarcodeGraphic(GraphicOverlay overlay) {
46 | super(overlay);
47 |
48 | mCurrentColorIndex = (mCurrentColorIndex + 1) % COLOR_CHOICES.length;
49 | final int selectedColor = COLOR_CHOICES[mCurrentColorIndex];
50 |
51 | mRectPaint = new Paint();
52 | mRectPaint.setColor(selectedColor);
53 | mRectPaint.setStyle(Paint.Style.STROKE);
54 | mRectPaint.setStrokeWidth(4.0f);
55 |
56 | mTextPaint = new Paint();
57 | mTextPaint.setColor(selectedColor);
58 | mTextPaint.setTextSize(36.0f);
59 | }
60 |
61 | public int getId() {
62 | return mId;
63 | }
64 |
65 | public void setId(int id) {
66 | this.mId = id;
67 | }
68 |
69 | public Barcode getBarcode() {
70 | return mBarcode;
71 | }
72 |
73 | /**
74 | * Updates the barcode instance from the detection of the most recent frame. Invalidates the
75 | * relevant portions of the overlay to trigger a redraw.
76 | */
77 | void updateItem(Barcode barcode) {
78 | mBarcode = barcode;
79 | postInvalidate();
80 | }
81 |
82 | /**
83 | * Draws the barcode annotations for position, size, and raw value on the supplied canvas.
84 | */
85 | @Override
86 | public void draw(Canvas canvas) {
87 | Barcode barcode = mBarcode;
88 | if (barcode == null) {
89 | return;
90 | }
91 |
92 | // Draws the bounding box around the barcode.
93 | RectF rect = new RectF(barcode.getBoundingBox());
94 | rect.left = translateX(rect.left);
95 | rect.top = translateY(rect.top);
96 | rect.right = translateX(rect.right);
97 | rect.bottom = translateY(rect.bottom);
98 | canvas.drawRect(rect, mRectPaint);
99 |
100 | // Draws a label at the bottom of the barcode indicate the barcode value that was detected.
101 | //canvas.drawText(barcode.rawValue, rect.left, rect.bottom, mTextPaint);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_application_files_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
18 |
19 |
25 |
26 |
30 |
31 |
41 |
42 |
43 |
49 |
50 |
51 |
57 |
58 |
62 |
63 |
69 |
70 |
74 |
75 |
85 |
86 |
87 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_uart.xml:
--------------------------------------------------------------------------------
1 |
123 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/update/ApplicationFilesFragmentDialog.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.app.update;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.Dialog;
6 | import android.app.DialogFragment;
7 | import android.content.DialogInterface;
8 | import android.net.Uri;
9 | import android.os.Bundle;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.widget.TextView;
13 | import android.widget.Toast;
14 |
15 | import com.adafruit.bluefruit.le.connect.R;
16 |
17 | import java.io.File;
18 |
19 | public class ApplicationFilesFragmentDialog extends DialogFragment {
20 | // UI
21 | private String mMessage;
22 | private TextView mHexTextView;
23 | private TextView mIniTextView;
24 | private AlertDialog mDialog;
25 | private int mFileType;
26 |
27 | // Data
28 | public interface ApplicationFilesDialogListener {
29 | void onApplicationFilesDialogDoneClick();
30 |
31 | void onApplicationFilesDialogCancelClick();
32 | }
33 |
34 | ApplicationFilesDialogListener mListener;
35 | Uri mHexUri, mIniUri;
36 |
37 | @Override
38 | public void onAttach(Activity activity) {
39 | super.onAttach(activity);
40 | // Verify that the host activity implements the callback interface
41 | try {
42 | // Instantiate the NoticeDialogListener so we can send events to the host
43 | mListener = (ApplicationFilesDialogListener) activity;
44 | } catch (ClassCastException e) {
45 | // The activity doesn't implement the interface, throw exception
46 | throw new ClassCastException(activity.toString() + " must implement ApplicationFilesDialogListener");
47 | }
48 | }
49 |
50 | @Override
51 | public Dialog onCreateDialog(Bundle savedInstanceState) {
52 | setRetainInstance(true);
53 |
54 | LayoutInflater inflater = getActivity().getLayoutInflater();
55 | View contentView = inflater.inflate(R.layout.layout_application_files_dialog, null);
56 | mHexTextView = (TextView) contentView.findViewById(R.id.hexFileTextView);
57 | mIniTextView = (TextView) contentView.findViewById(R.id.iniFileTextView);
58 |
59 | mMessage = getArguments().getString("message");
60 | mFileType = getArguments().getInt("fileType");
61 |
62 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
63 | builder.setView(contentView);
64 | builder.setMessage(mMessage)
65 | .setPositiveButton(R.string.firmware_customfile_dialog_done, new DialogInterface.OnClickListener() {
66 | public void onClick(DialogInterface dialog, int id) {
67 | if (getHexUri() == null) {
68 | Toast.makeText(getActivity(), R.string.firmware_customfile_hexundefined, Toast.LENGTH_LONG).show();
69 | }
70 |
71 | mListener.onApplicationFilesDialogDoneClick();
72 | }
73 | })
74 | .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
75 | public void onClick(DialogInterface dialog, int id) {
76 | mListener.onApplicationFilesDialogCancelClick();
77 | }
78 | });
79 | mDialog = builder.create();
80 |
81 | updateUI();
82 |
83 | return mDialog;
84 | }
85 |
86 | @Override
87 | public void onDestroyView() {
88 | Dialog dialog = getDialog();
89 |
90 | // Work around bug: http://code.google.com/p/android/issues/detail?id=17423
91 | if ((dialog != null) && getRetainInstance())
92 | dialog.setDismissMessage(null);
93 |
94 | super.onDestroyView();
95 | }
96 |
97 | private String filenameFromUri(Uri uri) {
98 | String name = "";
99 |
100 | if (uri != null) {
101 | File file = new File(uri.getPath());
102 | name = file.getName();
103 | }
104 | return name;
105 | }
106 |
107 | public void setHexFilename(Uri uri) {
108 | mHexUri = uri;
109 | updateUI();
110 | }
111 |
112 | public void setIniFilename(Uri uri) {
113 | mIniUri = uri;
114 | updateUI();
115 | }
116 |
117 | private void updateUI() {
118 | String hexName = filenameFromUri(mHexUri);
119 | mHexTextView.setText(hexName);
120 | String iniName = filenameFromUri(mIniUri);
121 | mIniTextView.setText(iniName);
122 |
123 | }
124 |
125 | public Uri getHexUri() {
126 | return mHexUri;
127 | }
128 |
129 | public Uri getIniUri() {
130 | return mIniUri;
131 | }
132 |
133 | public int getFileType() {
134 | return mFileType;
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/ble/KnownUUIDs.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.ble;
2 |
3 |
4 | import com.adafruit.bluefruit.le.connect.app.UartInterfaceActivity;
5 |
6 | import java.util.Collections;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | public class KnownUUIDs extends StandardUUIDs {
11 |
12 | // Service UUIDs
13 | private static final Map sServiceUUIDs;
14 |
15 | static {
16 | Map aMap = new HashMap<>();
17 |
18 | aMap.put("0000febb-0000-1000-8000-00805f9b34fb".toUpperCase(), "Adafruit Unified Sensor");
19 | aMap.put(UartInterfaceActivity.UUID_SERVICE.toUpperCase(), "Nordic UART");
20 | aMap.put("00001530-1212-efde-1523-785feabcd123".toUpperCase(), "Nordic Device Firmware Update Service");
21 | aMap.put("ee0c2080-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone-URL Config Service");
22 | aMap.put("8D53DC1D-1DB7-4CD3-868B-8A527460AA84".toUpperCase(), "Newtmgr Service");
23 |
24 | sServiceUUIDs = Collections.unmodifiableMap(aMap);
25 | }
26 |
27 | // Characteristic UUIDs
28 | private static final Map sCharacteristicUUIDs;
29 |
30 | static {
31 | Map aMap = new HashMap<>();
32 |
33 | // Unified
34 | aMap.put("B71E0102-7E57-4AFE-EB1E-5CA1AB1E1DEA".toUpperCase(), "Static Sensor Info");
35 | aMap.put("B71E0103-7E57-4AFE-EB1E-5CA1AB1E1DEA".toUpperCase(), "Dynamic Sensor Info");
36 | aMap.put("b71e0104-7e57-4afe-eb1e-5ca1ab1e1dea".toUpperCase(), "Sensor Data");
37 | aMap.put("00002a24-0000-1000-8000-00805f9b34fb".toUpperCase(), "Model Number");
38 | aMap.put("00001530-1212-efde-1523-785feabcd123".toUpperCase(), "Nordic Device Firmware Update Service");
39 |
40 | // DFU
41 | aMap.put("00001532-1212-efde-1523-785feabcd123".toUpperCase(), "DFU Packet");
42 | aMap.put("00001531-1212-efde-1523-785feabcd123".toUpperCase(), "DFU Control Point");
43 | aMap.put("00001534-1212-efde-1523-785feabcd123".toUpperCase(), "DFU Version");
44 |
45 | // Uart
46 | aMap.put(UartInterfaceActivity.UUID_RX.toUpperCase(), "RX Buffer");
47 | aMap.put(UartInterfaceActivity.UUID_TX.toUpperCase(), "TX Buffer");
48 | aMap.put(UartInterfaceActivity.UUID_DFU.toUpperCase(), "DFU Service");
49 |
50 | // Eddystone
51 | aMap.put("ee0c2081-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone Lock State");
52 | aMap.put("ee0c2082-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone Lock");
53 | aMap.put("ee0c2083-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone Unlock");
54 | aMap.put("ee0c2084-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone URI Data");
55 | aMap.put("ee0c2085-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone Flags");
56 | aMap.put("ee0c2086-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone Adv. TX Power Levels");
57 | aMap.put("ee0c2087-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone TX Power Mode");
58 | aMap.put("ee0c2088-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone Beacon Period");
59 | aMap.put("ee0c2089-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone Reset");
60 | aMap.put("ee0c2090-8786-40ba-ab96-99b91ac981d8".toUpperCase(), "Eddystone (Reserved)");
61 |
62 | // Newtmgr
63 | aMap.put("DA2E7828-FBCE-4E01-AE9E-261174997C48".toUpperCase(), "Newtmgr Characteristic");
64 |
65 | sCharacteristicUUIDs = Collections.unmodifiableMap(aMap);
66 | }
67 |
68 | // Descriptors UUIDs
69 | private static final Map sDescriptorUUIDs;
70 |
71 | static {
72 | Map aMap = new HashMap<>();
73 |
74 | sDescriptorUUIDs = Collections.unmodifiableMap(aMap);
75 | }
76 |
77 |
78 | // Public Getters
79 | public static String getServiceName(String uuid) {
80 | String result;
81 |
82 | uuid = uuid.toUpperCase(); // To avoid problems with lowercase/uppercase
83 | result = sServiceUUIDs.get(uuid);
84 | if (result == null) {
85 | result = StandardUUIDs.getServiceName(uuid);
86 | }
87 |
88 | return result;
89 | }
90 |
91 | public static String getCharacteristicName(String uuid) {
92 | String result;
93 |
94 | uuid = uuid.toUpperCase(); // To avoid problems with lowercase/uppercase
95 | result = sCharacteristicUUIDs.get(uuid);
96 | if (result == null) {
97 | result = StandardUUIDs.getCharacteristicName(uuid);
98 | }
99 |
100 | return result;
101 | }
102 |
103 | public static String getDescriptorName(String uuid) {
104 | String result;
105 |
106 | uuid = uuid.toUpperCase(); // To avoid problems with lowercase/uppercase
107 | result = sDescriptorUUIDs.get(uuid);
108 | if (result == null) {
109 | result = StandardUUIDs.getDescriptorName(uuid);
110 | }
111 |
112 | return result;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
14 |
15 |
20 |
21 |
22 |
25 |
26 |
31 |
32 |
37 |
38 |
43 |
44 |
51 |
52 |
53 |
54 |
55 |
58 |
59 |
65 |
66 |
67 |
68 |
69 |
72 |
77 |
78 |
83 |
84 |
89 |
90 |
95 |
96 |
101 |
102 |
107 |
108 |
109 |
112 |
117 |
118 |
--------------------------------------------------------------------------------
/app/src/main/java/com/adafruit/bluefruit/le/connect/app/update/DownloadTask.java:
--------------------------------------------------------------------------------
1 | package com.adafruit.bluefruit.le.connect.app.update;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.os.AsyncTask;
6 | import android.os.PowerManager;
7 | import android.util.Log;
8 |
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.net.HttpURLConnection;
13 | import java.net.URL;
14 |
15 |
16 | class DownloadTask extends AsyncTask {
17 | // Constants
18 | private final static String TAG = DownloadTask.class.getSimpleName();
19 |
20 | // Data
21 | private Context mContext;
22 | private PowerManager.WakeLock mWakeLock;
23 | private DownloadTaskListener mListener;
24 | private int mOperationId;
25 | private String mUrlAddress;
26 | private Object mTag;
27 |
28 | DownloadTask(Context context, DownloadTaskListener listener, int operationId) {
29 | mContext = context;
30 | mListener = listener;
31 | mOperationId = operationId;
32 | }
33 |
34 | @Override
35 | protected ByteArrayOutputStream doInBackground(String... sUrl) {
36 | InputStream input = null;
37 | ByteArrayOutputStream output = null;
38 | HttpURLConnection connection = null;
39 | try {
40 | mUrlAddress = sUrl[0];
41 |
42 | int fileLength = 0;
43 | Uri uri = Uri.parse(sUrl[0]);
44 | String uriScheme = uri.getScheme();
45 | //Log.d(TAG, "Downloading from "+uriScheme);
46 | boolean shouldBeConsideredAsInputStream = (uriScheme.equalsIgnoreCase("file") || uriScheme.equalsIgnoreCase("content"));
47 | if (shouldBeConsideredAsInputStream) {
48 | input = mContext.getContentResolver().openInputStream(uri);
49 | } else {
50 | URL url = new URL(mUrlAddress);
51 | connection = (HttpURLConnection) url.openConnection();
52 | connection.connect();
53 |
54 | // expect HTTP 200 OK, so we don't mistakenly save error report instead of the file
55 | if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
56 | return null;
57 | }
58 |
59 | // this will be useful to display download percentage might be -1: server did not report the length
60 | fileLength = connection.getContentLength();
61 |
62 | // download the file
63 | input = connection.getInputStream();
64 | }
65 | // Log.d(TAG, "\tFile size: "+fileLength);
66 |
67 | // download the file
68 | output = new ByteArrayOutputStream();
69 |
70 | byte data[] = new byte[4096];
71 | long total = 0;
72 | int count;
73 | while ((count = input.read(data)) != -1) {
74 | // allow canceling
75 | if (isCancelled()) {
76 | input.close();
77 | return null;
78 | }
79 | total += count;
80 |
81 | // publishing the progress....
82 | if (fileLength > 0) {// only if total length is known
83 | publishProgress((int) (total * 100 / fileLength));
84 | }
85 | output.write(data, 0, count);
86 | }
87 |
88 | } catch (Exception e) {
89 | Log.w(TAG, "Error DownloadTask " + e);
90 | return null;
91 | } finally {
92 | try {
93 | if (output != null) {
94 | output.close();
95 | }
96 | if (input != null) {
97 | input.close();
98 | }
99 | } catch (IOException ignored) {
100 | }
101 |
102 | if (connection != null) {
103 | connection.disconnect();
104 | }
105 | }
106 | return output;
107 | }
108 |
109 | @Override
110 | protected void onPreExecute() {
111 | super.onPreExecute();
112 | // take CPU lock to prevent CPU from going off if the user presses the power button during download
113 | PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
114 | mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
115 | mWakeLock.acquire();
116 | }
117 |
118 | @Override
119 | protected void onProgressUpdate(Integer... progress) {
120 | super.onProgressUpdate(progress);
121 |
122 | mListener.onDownloadProgress(mOperationId, progress[0]);
123 | }
124 |
125 | @Override
126 | protected void onPostExecute(ByteArrayOutputStream result) {
127 | mWakeLock.release();
128 |
129 | mListener.onDownloadCompleted(mOperationId, mUrlAddress, result);
130 | }
131 |
132 | static interface DownloadTaskListener {
133 | void onDownloadProgress(int operationId, int progress);
134 |
135 | void onDownloadCompleted(int operationId, String url, ByteArrayOutputStream result);
136 | }
137 |
138 | public Object getTag() {
139 | return mTag;
140 | }
141 |
142 | public void setTag(Object tag) {
143 | this.mTag = tag;
144 | }
145 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_uart.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 |
20 |
25 |
26 |
35 |
36 |
41 |
42 |
43 |
49 |
50 |
56 |
57 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
79 |
85 |
86 |
96 |
97 |
101 |
102 |
110 |
111 |
119 |
120 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/androidSupportLibrary/androidSupportLibrary.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |