├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── drawable
│ │ │ │ ├── linear_layout_bg.xml
│ │ │ │ ├── checkbox_selector.xml
│ │ │ │ ├── checkbox_unchecked.xml
│ │ │ │ ├── checkbox_checked.xml
│ │ │ │ ├── linear_layout_bottom_bg.xml
│ │ │ │ ├── linear_layout_limian_bg.xml
│ │ │ │ ├── button_check_bg.xml
│ │ │ │ ├── linear_button_bg.xml
│ │ │ │ ├── custom_progress_bar_horizontal.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── head_layout.xml
│ │ │ │ ├── firmware_item.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── dialog_custom.xml
│ │ │ │ ├── activity_ota.xml
│ │ │ │ ├── device_item.xml
│ │ │ │ └── activity_filelist.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── bw
│ │ │ │ └── ydb
│ │ │ │ ├── utils
│ │ │ │ ├── config
│ │ │ │ │ ├── UUIDDataBase.java
│ │ │ │ │ ├── GattAttributes.java
│ │ │ │ │ ├── Constants.java
│ │ │ │ │ └── BroadCast.java
│ │ │ │ ├── WriteStatus.java
│ │ │ │ ├── tool
│ │ │ │ │ └── BWDataParser.java
│ │ │ │ └── BleManage.java
│ │ │ │ ├── event
│ │ │ │ ├── BleEvent.java
│ │ │ │ └── ConnectEvent.java
│ │ │ │ ├── model
│ │ │ │ ├── OTAFileModel.java
│ │ │ │ └── BleModel.java
│ │ │ │ ├── ui
│ │ │ │ ├── adapter
│ │ │ │ │ ├── OTAFileListAdapter.java
│ │ │ │ │ └── LeDeviceListAdapter.java
│ │ │ │ └── activity
│ │ │ │ │ ├── OTAActivity.java
│ │ │ │ │ ├── FileListActivity.java
│ │ │ │ │ └── MainActivity.java
│ │ │ │ ├── widgets
│ │ │ │ ├── TextProgressBar.java
│ │ │ │ └── CustomsDialog.java
│ │ │ │ └── data
│ │ │ │ └── service
│ │ │ │ └── BluetoothService.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── bw
│ │ │ └── ydb
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── bw
│ │ └── ydb
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .idea
├── dictionaries
│ └── rnd.xml
├── caches
│ └── build_file_checksums.ser
├── vcs.xml
├── compiler.xml
├── gradle.xml
├── codeStyles
│ └── Project.xml
├── jarRepositories.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/.idea/dictionaries/rnd.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemBleUpdate/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | YModemBleUpdate
3 |
4 | Confirm
5 | Cancel
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_layout_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon May 16 09:07:04 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/utils/config/UUIDDataBase.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.utils.config;
2 |
3 | import java.util.UUID;
4 |
5 | public class UUIDDataBase {
6 | public final static UUID UUID_OTA_UPDATE_CHARACTERISTIC = UUID
7 | .fromString(GattAttributes.BW_PROJECT_OTA_DATA);
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/utils/WriteStatus.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.utils;
2 |
3 | import com.clj.fastble.exception.BleException;
4 |
5 | public interface WriteStatus {
6 |
7 | void onSuccess(int current, int total, byte[] justWrite);
8 |
9 | void onFail(BleException exception);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/checkbox_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/event/BleEvent.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.event;
2 |
3 | import com.bw.ydb.model.BleModel;
4 |
5 | public class BleEvent {
6 |
7 | public BleEvent(BleModel model){
8 | this.model = model;
9 | }
10 |
11 | public BleModel getModel() {
12 | return model;
13 | }
14 |
15 | public void setModel(BleModel model) {
16 | this.model = model;
17 | }
18 |
19 | private BleModel model;
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/test/java/com/bw/ydb/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/utils/config/GattAttributes.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.utils.config;
2 |
3 | public class GattAttributes {
4 | //温度类型
5 | public static final String TEMPERATURE_TYPE = "00002a1d-0000-1000-8000-00805f9b34fb";
6 | //usual
7 | public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
8 | //通过OTA发送数据到设备 更新设备的功能
9 | public static final String BW_PROJECT_OTA_DATA = "XXXXX-BB42-452D-B573-E0645F03C230";
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/event/ConnectEvent.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.event;
2 |
3 | import com.bw.ydb.model.BleModel;
4 |
5 | import java.util.List;
6 |
7 | public class ConnectEvent {
8 |
9 | public ConnectEvent(List list){
10 | this.list = list;
11 | }
12 |
13 | public List getList() {
14 | return list;
15 | }
16 |
17 | public void setList(List list) {
18 | this.list = list;
19 | }
20 |
21 | private List list;
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/checkbox_unchecked.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
11 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/checkbox_checked.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
11 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_layout_bottom_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 | -
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/utils/config/Constants.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.utils.config;
2 |
3 | public class Constants {
4 | public static final String EXTRA_CHARACTERISTIC_ERROR_MESSAGE = "com.bw.bwp.EXTRA_CHARACTERISTIC_ERROR_MESSAGE";
5 | /**
6 | * OTA File Selection Extras
7 | * OTA文件选择
8 | */
9 | public static final String REQ_FILE_COUNT = "REQ_FILE_COUNT";
10 | public static final String SELECTION_FLAG = "SELECTION_FLAG";
11 | public static final String ARRAYLIST_SELECTED_FILE_PATHS = "ARRAYLIST_SELECTED_FILE_PATHS";
12 | public static final String ARRAYLIST_SELECTED_FILE_NAMES = "ARRAYLIST_SELECTED_FILE_NAMES";
13 |
14 | public static final String BLE_MODEL = "BLE_MODEL";
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_layout_limian_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 | -
12 |
13 |
14 |
15 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_check_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 | -
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_button_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 | -
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 | android.useAndroidX=true
16 | android.enableJetifier=true
17 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/bw/ydb/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.bw.ydb", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/custom_progress_bar_horizontal.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
9 |
10 |
11 |
12 | -
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/utils/tool/BWDataParser.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.utils.tool;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 | import android.util.Log;
5 |
6 | public class BWDataParser {
7 |
8 | public static String getOtaData(BluetoothGattCharacteristic characteristic){
9 | byte[] data = characteristic.getValue();
10 | String qq = byteArrayToStr(data);
11 | Log.i("qq is ",qq);
12 | return qq;
13 | }
14 |
15 | //温度数据处理
16 | public static String getTempData(BluetoothGattCharacteristic characteristic){
17 | byte[] data = characteristic.getValue();
18 | String qq = byteArrayToStr(data);
19 | Log.i("qq is ",qq);
20 | return qq;
21 | }
22 |
23 | //数组转为string
24 | private static String byteArrayToStr(byte[] byteArray) {
25 | if (byteArray == null) {
26 | return null;
27 | }
28 | String str = new String(byteArray);
29 | return str;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/head_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
16 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/utils/config/BroadCast.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.utils.config;
2 |
3 | public class BroadCast {
4 | public final static String ACTION_GATT_CHARACTERISTIC_ERROR =
5 | "com.bw.bwp.action.ACTION_GATT_CHARACTERISTIC_ERROR";
6 | public final static String ACTION_GATT_CONNECTED
7 | = "com.bw.bwp.action.ACTION_GATT_CONNECTED"; //连接
8 | public final static String ACTION_GATT_DISCONNECTED
9 | = "com.bw.bwp.action.ACTION_GATT_DISCONNECTED"; //断开链接
10 | public final static String ACTION_GATT_SERVICES_DISCOVERED
11 | = "com.bw.bwp.action.ACTION_GATT_SERVICES_DISCOVERED"; //发现设备
12 | public final static String ACTION_DATA_AVAILABLE
13 | = "com.bw.bwp.action.ACTION_DATA_AVAILABLE";
14 |
15 | public final static String ACTION_OTA_DATA_AVAILABLE =
16 | "com.bw.bwp.action.ACTION_OTA_DATA_AVAILABLE";
17 |
18 | public final static String ACTION_OTA_DATA="com.bw.bwp.action.ACTION_OTA_DATA";
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/firmware_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
18 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
13 |
17 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 30
5 | buildToolsVersion "30.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.bw.ydb"
9 | minSdkVersion 19
10 | targetSdkVersion 30
11 | versionCode 2
12 | versionName "1.0.2"
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | compileOptions {
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | implementation 'androidx.appcompat:appcompat:1.2.0'
29 | implementation 'com.google.android.material:material:1.3.0'
30 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
31 | implementation 'androidx.recyclerview:recyclerview:1.1.0'
32 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
33 | testImplementation 'junit:junit:4.+'
34 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
35 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
36 |
37 | implementation 'com.github.ArdWang:YModemLib:2.0.1'
38 |
39 | // rxjava3.0
40 | implementation 'io.reactivex.rxjava3:rxjava:3.0.11-RC5'
41 |
42 | // 支持rxjava3.0
43 | implementation 'com.github.tbruyelle:rxpermissions:0.12'
44 |
45 | implementation 'com.github.Jasonchenlijian:FastBle:2.4.0'
46 |
47 | implementation 'org.greenrobot:eventbus:3.2.0'
48 |
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/model/OTAFileModel.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.model;
2 |
3 | /**
4 | * Data Model class for OTA File
5 | */
6 | public class OTAFileModel {
7 | /**
8 | *File name
9 | */
10 | private String mFileName = null;
11 | /**
12 | *File path
13 | */
14 | private String mFilePath = null;
15 | /**
16 | * File parent
17 | */
18 | private String mFileParent = null;
19 | /**
20 | *Selection Flag
21 | *
22 | */
23 | private boolean mSelected = false;
24 |
25 |
26 | // Constructor
27 | public OTAFileModel(String fileName, String filePath, boolean selected, String fileParent) {
28 | super();
29 | this.mFileName = fileName;
30 | this.mFilePath = filePath;
31 | this.mSelected = selected;
32 | this.mFileParent = fileParent;
33 | }
34 |
35 | public OTAFileModel() {
36 | super();
37 | }
38 |
39 | public String getFileName() {
40 | return mFileName;
41 | }
42 |
43 | public String getmFileParent() {
44 | return mFileParent;
45 | }
46 |
47 | public void setmFileParent(String mFileParent) {
48 | this.mFileParent = mFileParent;
49 | }
50 |
51 | public void setFileName(String mFileName) {
52 | this.mFileName = mFileName;
53 | }
54 |
55 | public String getFilePath() {
56 | return mFilePath;
57 | }
58 |
59 | public void setName(String mFilePath) {
60 | this.mFilePath = mFilePath;
61 | }
62 |
63 | public boolean isSelected() {
64 | return mSelected;
65 | }
66 |
67 | public void setSelected(boolean selected) {
68 | this.mSelected = selected;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 |
5 | #303F9F
6 |
7 | #FF4081
8 |
9 | #436EEE
10 |
11 | #436EEE
12 |
13 | #6495ED
14 |
15 | #FFFFFF
16 |
17 | #436EEE
18 |
19 | #EE0000
20 |
21 | #4D4D4D
22 |
23 | #EAEAEA
24 |
25 | #EDEDED
26 |
27 | #111111
28 |
29 | #999999
30 |
31 | #4169E1
32 |
33 | #00FF00
34 |
35 | #EEEE00
36 |
37 | #EFEFEF
38 |
39 | #ABABAB
40 |
41 | #0c377b
42 | #EFEFF4
43 | #701ABB00
44 |
45 |
46 | #B0B0B0
47 | #8F8F8F
48 | #949494
49 | #8B8B7A
50 | #8B8B83
51 | #878787
52 | #848484
53 | #7F7F7F
54 | #778899
55 | #707070
56 |
57 |
58 | #222222
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
30 |
31 |
37 |
38 |
46 |
47 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
20 |
21 |
29 |
30 |
37 |
38 |
44 |
45 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_ota.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
13 |
14 |
15 |
16 |
22 |
28 |
34 |
44 |
45 |
46 |
47 |
55 |
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/ui/adapter/OTAFileListAdapter.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | package com.bw.ydb.ui.adapter;
4 |
5 | import android.content.Context;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.BaseAdapter;
10 | import android.widget.CheckBox;
11 | import android.widget.LinearLayout;
12 | import android.widget.TextView;
13 |
14 | import com.bw.ydb.R;
15 | import com.bw.ydb.model.OTAFileModel;
16 | import java.util.ArrayList;
17 |
18 |
19 | public class OTAFileListAdapter extends BaseAdapter {
20 |
21 | ArrayList mFileList;
22 | LayoutInflater mInflater;
23 | int mRequiredFilesCount;
24 | Context mContext;
25 |
26 | public OTAFileListAdapter(Context context, ArrayList fileList,
27 | int requiredFilesCount) {
28 | this.mFileList = fileList;
29 | this.mContext = context;
30 | this.mRequiredFilesCount = requiredFilesCount;
31 | mInflater = LayoutInflater.from(this.mContext); // only context can also be used
32 | }
33 |
34 |
35 | @Override
36 | public int getCount() {
37 | return mFileList.size();
38 | }
39 |
40 | @Override
41 | public Object getItem(int position) {
42 | return mFileList.get(position);
43 | }
44 |
45 | @Override
46 | public long getItemId(int position) {
47 | return position;
48 | }
49 |
50 | @Override
51 | public View getView(final int position, View convertView, ViewGroup parent) {
52 | MyViewHolder mViewHolder;
53 | if (convertView == null) {
54 | convertView = mInflater.inflate(R.layout.firmware_item, null);
55 | mViewHolder = new MyViewHolder();
56 | mViewHolder.fileName = convertView.findViewById(R.id.file_name);
57 | mViewHolder.layout = convertView.findViewById(R.id.itemParent);
58 | mViewHolder.fileSelect = convertView.findViewById(R.id.file_checkbox);
59 | convertView.setTag(mViewHolder);
60 | }
61 | mViewHolder = (MyViewHolder) convertView.getTag();
62 | OTAFileModel file = mFileList.get(position);
63 | mViewHolder.fileName.setText(file.getFileName());
64 | mViewHolder.fileSelect.setChecked(file.isSelected());
65 | return convertView;
66 | }
67 |
68 | public void addFiles(ArrayList fileModels) {
69 | this.mFileList = fileModels;
70 | }
71 |
72 | private class MyViewHolder {
73 | TextView fileName;
74 | CheckBox fileSelect;
75 | LinearLayout layout;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/device_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
22 |
27 |
34 |
42 |
51 |
52 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_filelist.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
14 |
15 |
16 |
27 |
31 |
39 |
45 |
55 |
56 |
57 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/widgets/TextProgressBar.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.widgets;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.util.AttributeSet;
8 | import android.util.DisplayMetrics;
9 | import android.widget.ProgressBar;
10 | import com.bw.ydb.R;
11 |
12 | public class TextProgressBar extends ProgressBar{
13 |
14 | private String mProgressText;
15 |
16 | private Paint mProgressPaint;
17 |
18 | public TextProgressBar(Context context) {
19 | super(context);
20 | initializeProgressBar();
21 | }
22 |
23 | public TextProgressBar(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | initializeProgressBar();
26 | }
27 |
28 | public TextProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
29 | super(context, attrs, defStyleAttr);
30 | initializeProgressBar();
31 | }
32 |
33 | public void initializeProgressBar() {
34 | mProgressText = "0%";
35 | mProgressPaint = new Paint();
36 | //Logger.e("DPI>>" + getResources().getDisplayMetrics().densityDpi);
37 | switch (getResources().getDisplayMetrics().densityDpi) {
38 | case DisplayMetrics.DENSITY_TV:
39 | mProgressPaint.setTextSize(14);
40 | break;
41 | case DisplayMetrics.DENSITY_HIGH:
42 | mProgressPaint.setTextSize(16);
43 | break;
44 | case DisplayMetrics.DENSITY_XHIGH:
45 | mProgressPaint.setTextSize(30);
46 | break;
47 | case DisplayMetrics.DENSITY_XXHIGH:
48 | mProgressPaint.setTextSize(40);
49 | break;
50 | case DisplayMetrics.DENSITY_XXXHIGH:
51 | mProgressPaint.setTextSize(50);
52 | break;
53 | default:
54 | mProgressPaint.setTextSize(50);
55 | break;
56 | }
57 | mProgressPaint.setColor(getResources().getColor(R.color.colorRed));
58 | }
59 |
60 | @Override
61 | protected synchronized void onDraw(Canvas canvas) {
62 | super.onDraw(canvas);
63 | Rect boundsProgress = new Rect();
64 | mProgressPaint.getTextBounds(mProgressText, 0, mProgressText.length(), boundsProgress);
65 | int xp;
66 | switch (getResources().getDisplayMetrics().densityDpi) {
67 | case DisplayMetrics.DENSITY_TV:
68 | xp = getWidth() - 70;
69 | break;
70 | case DisplayMetrics.DENSITY_HIGH:
71 | xp = getWidth() - 80;
72 | break;
73 | case DisplayMetrics.DENSITY_XHIGH:
74 | xp = getWidth() - 100;
75 | break;
76 | case DisplayMetrics.DENSITY_XXHIGH:
77 | xp = getWidth() - 120;
78 | break;
79 | case DisplayMetrics.DENSITY_XXXHIGH:
80 | xp = getWidth() - 140;
81 | break;
82 | default:
83 | xp = getWidth() - 140;
84 | break;
85 | }
86 | int yp = getHeight() / 2 - boundsProgress.centerY();
87 | canvas.drawText(mProgressText, xp, yp, mProgressPaint);
88 | }
89 |
90 | public synchronized void setProgressText(String text) {
91 | this.mProgressText = text;
92 | drawableStateChanged();
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/model/BleModel.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.model;
2 |
3 |
4 |
5 | import android.os.Parcel;
6 | import android.os.Parcelable;
7 | import com.clj.fastble.data.BleDevice;
8 |
9 | import java.util.Date;
10 |
11 |
12 | public class BleModel implements Parcelable {
13 |
14 | public BleModel(){
15 |
16 | }
17 |
18 | protected BleModel(Parcel in) {
19 | sendCharacter = in.readString();
20 | name = in.readString();
21 | mac = in.readString();
22 | if (in.readByte() == 0) {
23 | rss = null;
24 | } else {
25 | rss = in.readInt();
26 | }
27 | byte tmpConnect = in.readByte();
28 | connect = tmpConnect == 0 ? null : tmpConnect == 1;
29 | service = in.readString();
30 | bleDevice = in.readParcelable(BleDevice.class.getClassLoader());
31 | otaByte = in.createByteArray();
32 | }
33 |
34 | public static final Creator CREATOR = new Creator() {
35 | @Override
36 | public BleModel createFromParcel(Parcel in) {
37 | return new BleModel(in);
38 | }
39 |
40 | @Override
41 | public BleModel[] newArray(int size) {
42 | return new BleModel[size];
43 | }
44 | };
45 |
46 | public String getSendCharacter() {
47 | return sendCharacter;
48 | }
49 |
50 | public void setSendCharacter(String sendCharacter) {
51 | this.sendCharacter = sendCharacter;
52 | }
53 |
54 | public String sendCharacter;
55 |
56 | public String getName() {
57 | return name;
58 | }
59 |
60 | public void setName(String name) {
61 | this.name = name;
62 | }
63 |
64 | public String getMac() {
65 | return mac;
66 | }
67 |
68 | public void setMac(String mac) {
69 | this.mac = mac;
70 | }
71 |
72 | public Integer getRss() {
73 | return rss;
74 | }
75 |
76 | public void setRss(Integer rss) {
77 | this.rss = rss;
78 | }
79 |
80 | public Boolean getConnect() {
81 | return connect;
82 | }
83 |
84 | public void setConnect(Boolean connect) {
85 | this.connect = connect;
86 | }
87 |
88 | public Date getDate() {
89 | return date;
90 | }
91 |
92 | public void setDate(Date date) {
93 | this.date = date;
94 | }
95 |
96 | public String getService() {
97 | return service;
98 | }
99 |
100 | public void setService(String service) {
101 | this.service = service;
102 | }
103 |
104 | public BleDevice getBleDevice() {
105 | return bleDevice;
106 | }
107 |
108 | public void setBleDevice(BleDevice bleDevice) {
109 | this.bleDevice = bleDevice;
110 | }
111 |
112 | private String name;
113 |
114 | private String mac;
115 |
116 | private Integer rss;
117 |
118 | private Boolean connect;
119 |
120 | private Date date;
121 |
122 | private String service;
123 |
124 | private BleDevice bleDevice;
125 |
126 | public byte[] getOtaByte() {
127 | return otaByte;
128 | }
129 |
130 | public void setOtaByte(byte[] otaByte) {
131 | this.otaByte = otaByte;
132 | }
133 |
134 | private byte[] otaByte;
135 |
136 |
137 | @Override
138 | public int describeContents() {
139 | return 0;
140 | }
141 |
142 | @Override
143 | public void writeToParcel(Parcel dest, int flags) {
144 | dest.writeString(sendCharacter);
145 | dest.writeString(name);
146 | dest.writeString(mac);
147 | if (rss == null) {
148 | dest.writeByte((byte) 0);
149 | } else {
150 | dest.writeByte((byte) 1);
151 | dest.writeInt(rss);
152 | }
153 | dest.writeByte((byte) (connect == null ? 0 : connect ? 1 : 2));
154 | dest.writeString(service);
155 | dest.writeParcelable(bleDevice, flags);
156 | dest.writeByteArray(otaByte);
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/ui/adapter/LeDeviceListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.ui.adapter;
2 |
3 |
4 | import android.content.Context;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.BaseAdapter;
9 | import android.widget.TextView;
10 |
11 | import com.bw.ydb.R;
12 | import com.bw.ydb.model.BleModel;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class LeDeviceListAdapter extends BaseAdapter {
17 | public static List mLeDevices;
18 | private LayoutInflater mInflator;
19 | ViewHolder viewHolder;
20 | private int postion=-1;
21 |
22 | public LeDeviceListAdapter(Context context) {
23 | super();
24 | mLeDevices = new ArrayList<>();
25 | mInflator =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
26 | }
27 |
28 | public boolean isNullDervice(){
29 | if(mLeDevices.size()>0){
30 | return true;
31 | }else{
32 | return false;
33 | }
34 | }
35 |
36 | /**
37 | * contains()是判断是否有相同的字符串
38 | * @param bleModel
39 | */
40 | public void addDevice(BleModel bleModel) {
41 | if(!mLeDevices.contains(bleModel)) {
42 | if(bleModel.getName()!=null){
43 | mLeDevices.add(bleModel);
44 | }
45 | }
46 | }
47 |
48 | public BleModel getDevice(int position) {
49 | return mLeDevices.get(position);
50 | }
51 |
52 |
53 | public void addPostion(int i){
54 | postion = i;
55 | }
56 |
57 |
58 |
59 | public void clear() {
60 | mLeDevices.clear();
61 | }
62 |
63 | @Override
64 | public int getCount() {
65 | return mLeDevices.size();
66 | }
67 |
68 | @Override
69 | public Object getItem(int i) {
70 | return mLeDevices.get(i);
71 | }
72 |
73 | @Override
74 | public long getItemId(int i) {
75 | return i;
76 | }
77 |
78 | @Override
79 | public View getView(int i, View view, ViewGroup viewGroup) {
80 | // General ListView optimization code.
81 | if (view == null) {
82 | view = mInflator.inflate(R.layout.device_item, null);
83 | viewHolder = new ViewHolder();
84 | viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_addre);
85 | viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
86 | viewHolder.clickcontent = (TextView) view.findViewById(R.id.click_content);
87 | //viewHolder.isConnected = (TextView) view.findViewById(R.id.is_connect);
88 | view.setTag(viewHolder);
89 | } else {
90 | viewHolder = (ViewHolder) view.getTag();
91 | }
92 |
93 | // BluetoothDevice device = mLeDevices.get(i);
94 |
95 | BleModel model = mLeDevices.get(i);
96 |
97 | final String deviceName = model.getName();
98 | final String deviceAddre = model.getBleDevice().getDevice().getAddress();
99 |
100 | if(i==postion){
101 | viewHolder.clickcontent.setText("Connected");
102 | }
103 |
104 | if (!deviceName.isEmpty()) {
105 | viewHolder.deviceName.setText(deviceName);
106 | } else {
107 | viewHolder.deviceName.setText("Unkown Dervice");
108 | }
109 | if (!deviceAddre.isEmpty()) {
110 | String array[] = deviceAddre.split(":");
111 | String address = "S/N:"+array[0]+array[1]+array[2]+array[3]+array[4]+array[5];
112 | viewHolder.deviceAddress.setText(address);
113 | } else {
114 | viewHolder.deviceAddress.setText("Unkown Address");
115 | }
116 |
117 | return view;
118 | }
119 |
120 | private static class ViewHolder {
121 | TextView deviceName;
122 | TextView deviceAddress;
123 | TextView clickcontent;
124 | //TextView isConnected; //是否 连接
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
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 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # YModemBleUpdate
2 | YModemBleUpdate OTA Code
3 |
4 | update 2022/11/7
5 |
6 | Flutter ymodem is provided Welcome to view,
7 |
8 | https://github.com/QuickDevelopers/flutter_ymodem
9 |
10 |
11 |
12 | update 2022/5/17
13 |
14 | 1. 本次更新修复了安卓高版本蓝牙读取不出来的错误
15 |
16 | 2. 需要注意的是蓝牙OTA UUID需要替换自己项目 uuid
17 |
18 | ```aidl
19 |
20 | GattAttributes.java 中 修改此类代码
21 |
22 | //通过OTA发送数据到设备 更新设备的功能
23 | public static final String BW_PROJECT_OTA_DATA = "XXXXX-BB42-452D-B573-E0645F03C230";
24 |
25 | ```
26 | 3. 蓝牙所有数据传输采用Fastble框架进行操作
27 | 4. 此demo有不完善的地方还期待着修复
28 |
29 |
30 |
31 | update 2022/5/16
32 |
33 | 增加测试文件的创建,把要升级的ota bin文件放入到TESTBLE文件中然后可以进行选择文件操作
34 |
35 |
36 | update 2022/5/13
37 |
38 | 更新YmodemDemo 兼容 androidX版本
39 |
40 | 安卓蓝牙YModem固件升级
41 |
42 | 原理
43 |
44 | 1.首先理解什么是YModem通讯?
45 |
46 | YModem协议是XModem的改进协议,它最用于调制解调器之间的文件传输的协议,具有快速,稳定传输的优点。它的传输速度比XModem快,这是由于它可以一次传输1024字节的信息块,同时它还支持传输多个文件,也就是常说的批文件传输。
47 |
48 | YModem分成YModem-1K与YModem-g。
49 |
50 | 我使用的是YModem-1K 也就是一次传输1024字节。
51 |
52 |
53 | YModem-1K用1024字节信息块传输取代标准的128字节传输,数据的发送回使用CRC校验,保证数据传输的正确性。它每传输一个信息块数据时,就会等待接收端回应ACK信号,接收到回应后,才会继续传输下一个信息块,保证数据已经全部接收。
54 |
55 |
56 | YModem-g传输形式与YModem-1K差不多,但是它去掉了数据的CRC校验码,同时在发送完一个数据块信息后,它不会等待接收端的ACK信号,而直接传输下一个数据块。正是它没有涉及错误校验,才使得它的传输速度比YModem-1K来得块。
57 |
58 | 一般都会选择YModem-1K传输,平时所说的YModem也是指的是YModem-1K。下面就讲讲它的传输协议
59 |
60 | 由于上面都是些 C语言相关的所以省略了直接进入主题。
61 | 2.理解传输数据格式
62 | ```java
63 | /**
64 | * ========================================================================================
65 | * THE YMODEM:
66 | * Send 0x05>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>* 发送0x05
67 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
68 | * SOH 00 FF "foo.c" "1064'' NUL[118] CRC CRC >>>>>>>>>>>>>
69 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
70 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
71 | * STX 01 FE data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>>
72 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
73 | * ACK STX 02 FD data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
74 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
75 | * ACK STX 03 FC data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
76 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
77 | * STX 04 FB data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
78 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
79 | * SOH 05 FA data[100] 1A[28] CRC CRC>>>>>>>>>>>>>>>>>>
80 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
81 | * EOT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
82 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< NAK
83 | * EOT>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
84 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
85 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
86 | * SOH 00 FF NUL[128] CRC CRC >>>>>>>>>>>>>>>>>>>>>>>
87 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
88 | * ===========================================================================================
89 | **/
90 | ```
91 | 我们用的设备首先要发送0x05与蓝牙通讯 然后设备返回一个C 接受到C后立即发送头部包到设备 此处 需要CRC16校验 采用标准的欧美标准
92 |
93 | 接下来就可以依次进行数据发送
94 |
95 | 注意:每一个公司的协议是不一样的但是你理解原理之后 协议不管怎么改 都可以去解决。
96 | 3.使用 YModem
97 |
98 | 关于YModem的安卓使用百度上搜下来是有很多的Demo,但是关键在于怎么去和蓝牙通讯这一块相当来说比较少了。
99 |
100 | 推荐的YModem通讯 安卓版本 https://github.com/ArdWang/YModemLib
101 |
102 | 里面有一套详细的文件校验,解包,发送bin文件。
103 |
104 | 4.与蓝牙设备去通讯
105 |
106 | 关键部分来了 首先通讯的方式 跟蓝牙通讯是一摸一样的
107 | ```
108 | private void startTransmission(){
109 | //String md5 = MD5Util.MD5(mCurrentFilePath);
110 | yModem = new YModem.Builder()
111 | .with(this)
112 | .filePath(mCurrentFilePath)
113 | .fileName(mCurrentFileName)
114 | .checkMd5("")
115 | .callback(new YModemListener() {
116 | @Override
117 | public void onDataReady(byte[] data) {
118 | if(sendData) {
119 | if (bluetoothService.writeCharacteristic(mSendOTACharacteristic, data)) {
120 | Log.i("==Send Data is:", bytesToHexFun(data));
121 | }
122 | }
123 | }
124 |
125 | @Override
126 | public void onProgress(int currentSent, int total) {
127 | float currentPt = (float)currentSent/total;
128 | int a = (int)(currentPt*100);
129 | mUpgradeBar.setProgress(currentSent); // Main Progress
130 | mUpgradeBar.setMax(total); // Maximum Progress
131 | if(a<=100){
132 | mUpgradeBar.setProgressText(""+a+"%");
133 | }else{
134 | mUpgradeBar.setProgressText("100%");
135 | }
136 |
137 | }
138 |
139 | @Override
140 | public void onSuccess() {
141 | Toast.makeText(OTAActivity.this,"固件升级完成",Toast.LENGTH_LONG).show();
142 | finish();
143 | }
144 |
145 | @Override
146 | public void onFailed(String reason) {
147 | Toast.makeText(OTAActivity.this,reason,Toast.LENGTH_LONG).show();
148 | }
149 | }).build();
150 | yModem.start();
151 | }
152 | ```
153 | 关键在这段代码中 此段代码放入 连接完成设备的Activity中
154 | startTransmission()要置入 你蓝牙发送数据或者接受数据的代码中,每一个人不一样。我的是发送中。
155 | 然后Ymodem会做出反映 你应该把反馈的包值写入到你的蓝牙设备中来进行升级
156 |
157 | 这一段是写入的代码
158 | ```
159 | public void onDataReady(byte[] data) {
160 | if(sendData) {
161 | if (bluetoothService.writeCharacteristic(mSendOTACharacteristic, data)) {
162 | Log.i("==Send Data is:", bytesToHexFun(data));
163 | }
164 | }
165 | }
166 | ```
167 | 其它的 包扣错误返回,进度等等你要做相应的处理
168 | 具体详细的请查看完整的Demo地址
169 |
170 | 点击打开链接
171 |
172 | https://github.com/ArdWang/YModemBleUpdate
173 |
174 |
175 |
176 | 如果有什么问题 请联系我
177 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/widgets/CustomsDialog.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.widgets;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.Button;
10 | import android.widget.TextView;
11 |
12 | import androidx.annotation.NonNull;
13 | import androidx.annotation.Nullable;
14 |
15 | import com.bw.ydb.R;
16 |
17 | /**
18 | * Created by rnd on 2018/3/23.
19 | * 常用的dialog
20 | */
21 | public class CustomsDialog extends Dialog {
22 |
23 | public CustomsDialog(@NonNull Context context) {
24 | super(context);
25 | }
26 |
27 | public CustomsDialog(@NonNull Context context, int themeResId) {
28 | super(context, themeResId);
29 | }
30 |
31 | protected CustomsDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
32 | super(context, cancelable, cancelListener);
33 | }
34 |
35 | public static class Builder{
36 |
37 | private Context context;
38 |
39 | private OnClickListener positiveButtonClickListener;
40 |
41 | private OnClickListener negativeButtonClickListener;
42 |
43 | private String positiveButtonText;
44 |
45 | private String negativeButtonText;
46 |
47 | private TextView mTips;
48 |
49 | private TextView mContent;
50 |
51 | public String getTips() {
52 | return tips;
53 | }
54 |
55 | public Builder setTips(String tips) {
56 | this.tips = tips;
57 | return this;
58 | }
59 |
60 | public String getContent() {
61 | return content;
62 | }
63 |
64 | public Builder setContent(String content) {
65 | this.content = content;
66 | return this;
67 | }
68 |
69 | private String tips;
70 |
71 | private String content;
72 |
73 | public Builder(Context context){
74 | this.context = context;
75 | }
76 |
77 | /**
78 | * Set the positive button resource and it's listener
79 | *
80 | * @param positiveButtonText
81 | * @return
82 | */
83 | public Builder setPositiveButton(int positiveButtonText,
84 | OnClickListener listener) {
85 | this.positiveButtonText = (String) context
86 | .getText(positiveButtonText);
87 | this.positiveButtonClickListener = listener;
88 | return this;
89 | }
90 |
91 |
92 | public Builder setNegativeButton(int negativeButtonText,
93 | OnClickListener listener) {
94 | this.negativeButtonText = (String)context.getText(negativeButtonText);
95 | this.negativeButtonClickListener = listener;
96 | return this;
97 | }
98 |
99 |
100 | public CustomsDialog create(){
101 | LayoutInflater inflater = (LayoutInflater) context
102 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
103 | // instantiate the dialog with the custom Theme
104 | final CustomsDialog dialog = new CustomsDialog(context, R.style.Dialog);
105 | View layout = inflater.inflate(R.layout.dialog_custom, null);
106 | //设置布局
107 | dialog.addContentView(layout, new ViewGroup.LayoutParams(
108 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
109 |
110 | mTips = layout.findViewById(R.id.mTips);
111 |
112 | mContent = layout.findViewById(R.id.mContent);
113 |
114 | if(!getTips().isEmpty()){
115 | mTips.setText(getTips());
116 | }
117 |
118 | if(!getContent().isEmpty()){
119 | mContent.setText(getContent());
120 | }
121 |
122 | // set the confirm button
123 | if (positiveButtonText != null) {
124 | ((Button) layout.findViewById(R.id.positiveButton))
125 | .setText(positiveButtonText);
126 | if (positiveButtonClickListener != null) {
127 | (layout.findViewById(R.id.positiveButton))
128 | .setOnClickListener(new View.OnClickListener() {
129 | public void onClick(View v) {
130 | positiveButtonClickListener.onClick(dialog,
131 | DialogInterface.BUTTON_POSITIVE);
132 | }
133 | });
134 | }
135 | } else {
136 | // if no confirm button just set the visibility to GONE
137 | layout.findViewById(R.id.positiveButton).setVisibility(
138 | View.GONE);
139 | }
140 | // set the cancel button
141 | if (negativeButtonText != null) {
142 |
143 | ((Button) layout.findViewById(R.id.negativeButton))
144 | .setText(negativeButtonText);
145 |
146 | if (negativeButtonClickListener != null) {
147 | (layout.findViewById(R.id.negativeButton))
148 | .setOnClickListener(new View.OnClickListener() {
149 | public void onClick(View v) {
150 | negativeButtonClickListener.onClick(dialog,
151 | DialogInterface.BUTTON_NEGATIVE);
152 | }
153 | });
154 | }
155 | } else {
156 | // if no confirm button just set the visibility to GONE
157 | layout.findViewById(R.id.negativeButton).setVisibility(
158 | View.GONE);
159 | }
160 | dialog.setContentView(layout);
161 | return dialog;
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/ui/activity/OTAActivity.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.ui.activity;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 | import android.view.View;
8 | import android.widget.Button;
9 | import android.widget.TextView;
10 | import android.widget.Toast;
11 | import androidx.annotation.Nullable;
12 | import androidx.appcompat.app.AppCompatActivity;
13 | import com.bw.ydb.R;
14 | import com.bw.ydb.event.ConnectEvent;
15 | import com.bw.ydb.model.BleModel;
16 | import com.bw.ydb.utils.BleManage;
17 | import com.bw.ydb.utils.WriteStatus;
18 | import com.bw.ydb.utils.config.Constants;
19 | import com.bw.ydb.widgets.TextProgressBar;
20 | import com.bw.yml.YModem;
21 | import com.bw.yml.YModemListener;
22 | import com.clj.fastble.exception.BleException;
23 | import org.greenrobot.eventbus.EventBus;
24 | import org.greenrobot.eventbus.Subscribe;
25 | import org.greenrobot.eventbus.ThreadMode;
26 | import java.util.ArrayList;
27 | import java.util.Objects;
28 |
29 |
30 | public class OTAActivity extends AppCompatActivity implements View.OnClickListener{
31 | /**
32 | * 静态的非变量成员
33 | */
34 | public static final int mApplicationUpgrade = 101;
35 | public static final int mApplicationAndStackCombined = 201;
36 | public static final int mApplicationAndStackSeparate = 301;
37 | public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
38 | public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
39 |
40 | private Button mOTAUpdate;
41 |
42 | private YModem yModem;
43 |
44 | private String mCurrentFilePath;
45 |
46 | private String mCurrentFileName;
47 |
48 | private boolean sendData;
49 |
50 | private TextProgressBar mUpgradeBar;
51 |
52 | private TextView mUpgradeFilename;
53 |
54 | private Button mOTAStop;
55 |
56 | private static final String TAG = "OTAActivity";
57 |
58 | private BleModel bleModel;
59 |
60 |
61 | @Override
62 | protected void onCreate(@Nullable Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | setContentView(R.layout.activity_ota);
65 |
66 | EventBus.getDefault().register(this);
67 |
68 | initView();
69 | initData();
70 | }
71 |
72 | @Override
73 | protected void onResume() {
74 | super.onResume();
75 |
76 | if(mCurrentFileName!=null) {
77 | mUpgradeFilename.setText(mCurrentFileName);
78 | }else{
79 | mUpgradeFilename.setText("选择需要升级的文件");
80 | }
81 | }
82 |
83 |
84 |
85 |
86 | private void initView() {
87 | mOTAUpdate = findViewById(R.id.mOTAUpdate);
88 | mUpgradeBar = findViewById(R.id.mUpgradeBar);
89 | mUpgradeFilename = findViewById(R.id.mUpgradeFilename);
90 | mOTAStop = findViewById(R.id.mOTAStop);
91 |
92 | mOTAUpdate.setOnClickListener(this);
93 | mOTAStop.setOnClickListener(this);
94 | }
95 |
96 | private void initData(){
97 | // 获取传递归来的BleModel
98 | bleModel = getIntent().getParcelableExtra(Constants.BLE_MODEL);
99 |
100 | //deviceAddre = getIntent().getStringExtra(OTAActivity.EXTRAS_DEVICE_ADDRESS);
101 | }
102 |
103 |
104 | @Subscribe(threadMode = ThreadMode.MAIN)
105 | public void onBleEvent(ConnectEvent event){
106 | if(event.getList().size() > 0){
107 | for(BleModel model:event.getList()){
108 | // 只能一致才能读取数据
109 | if(Objects.equals(model.getBleDevice().getMac(), bleModel.getBleDevice().getMac())){
110 | // 必须要有返回数据才能进行操作
111 | if(model.getOtaByte() != null) {
112 | // 进行数据处理 ota接收数据
113 | yModem.onReceiveData(model.getOtaByte());
114 | sendData = true;
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
121 |
122 | public void onDataReceivedFromBLE(byte[] data) {
123 | yModem.onReceiveData(data);
124 | sendData = true;
125 | }
126 |
127 | public static byte[] strToByteArray(String str) {
128 | if (str == null) {
129 | return null;
130 | }
131 | return str.getBytes();
132 | }
133 |
134 | private void startTransmission(){
135 | //String md5 = MD5Util.MD5(mCurrentFilePath);
136 | yModem = new YModem.Builder()
137 | .with(this)
138 | .filePath(mCurrentFilePath)
139 | .fileName(mCurrentFileName)
140 | .checkMd5("")
141 | .sendSize(1024)
142 | .callback(new YModemListener() {
143 | @Override
144 | public void onDataReady(byte[] data) {
145 | if(sendData) {
146 | BleManage.getInstance().write(bleModel.getBleDevice(), bleModel.getService(), bleModel.getSendCharacter(), data, new WriteStatus() {
147 | @Override
148 | public void onSuccess(int current, int total, byte[] justWrite) {
149 | Log.i("==Send Data is:", bytesToHexFun(justWrite));
150 | }
151 |
152 | @Override
153 | public void onFail(BleException exception) {
154 | Log.e("==Send Data fail!",exception.toString());
155 | }
156 | });
157 | }
158 | }
159 |
160 | @Override
161 | public void onProgress(int currentSent, int total) {
162 | float currentPt = (float)currentSent/total;
163 | int a = (int)(currentPt*100);
164 | mUpgradeBar.setProgress(currentSent); // Main Progress
165 | mUpgradeBar.setMax(total); // Maximum Progress
166 | if(a<=100){
167 | mUpgradeBar.setProgressText(""+a+"%");
168 | }else{
169 | mUpgradeBar.setProgressText("100%");
170 | }
171 | }
172 |
173 | @Override
174 | public void onSuccess() {
175 | Toast.makeText(OTAActivity.this,"固件升级完成",Toast.LENGTH_LONG).show();
176 | finish();
177 | }
178 |
179 | @Override
180 | public void onFailed(String reason) {
181 | Toast.makeText(OTAActivity.this,reason,Toast.LENGTH_LONG).show();
182 | }
183 | }).build();
184 | yModem.start();
185 | }
186 |
187 | public static String bytesToHexFun(byte[] bytes) {
188 | StringBuilder buf = new StringBuilder(bytes.length * 2);
189 | for(byte b : bytes) { // 使用String的format方法进行转换
190 | buf.append(String.format("%02x", b & 0xff));
191 | }
192 | return buf.toString();
193 | }
194 |
195 |
196 | @Override
197 | public void onClick(View view) {
198 | switch (view.getId()){
199 | case R.id.mOTAUpdate:
200 |
201 | if(bleModel.getBleDevice() != null) {
202 | // 这一步进行读取服务
203 | BleManage.getInstance().readServer(bleModel.getBleDevice());
204 | }
205 |
206 | if(mCurrentFileName==null&&mCurrentFilePath==null){
207 | mOTAUpdate.setText("点我升级");
208 | Intent ApplicationAndStackCombined = new Intent(OTAActivity.this, FileListActivity.class);
209 | ApplicationAndStackCombined.putExtra("FilesName", "Files");
210 | ApplicationAndStackCombined.putExtra(Constants.REQ_FILE_COUNT, mApplicationAndStackCombined);
211 | startActivityForResult(ApplicationAndStackCombined, mApplicationAndStackCombined);
212 | }else {
213 | mOTAUpdate.setVisibility(View.GONE);
214 | mOTAStop.setVisibility(View.VISIBLE);
215 |
216 | // 开始协议每个项目都不一样 需要针对底层去协议去沟通,本次0x05只针对本项目
217 |
218 | BleManage.getInstance().write(bleModel.getBleDevice(), bleModel.getService(), bleModel.getSendCharacter(), "0x05".getBytes(), new WriteStatus() {
219 | @Override
220 | public void onSuccess(int current, int total, byte[] justWrite) {
221 | Log.i("==Send Data is:", bytesToHexFun(justWrite));
222 | startTransmission();
223 | }
224 |
225 | @Override
226 | public void onFail(BleException exception) {
227 | Log.e("==Send Data fail! ",exception.toString());
228 | }
229 | });
230 | }
231 | break;
232 |
233 | case R.id.mOTAStop:
234 | finish();
235 | break;
236 | }
237 | }
238 |
239 | @Override
240 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
241 | if (resultCode == Activity.RESULT_OK) {
242 | ArrayList selectedFiles = data.
243 | getStringArrayListExtra(Constants.ARRAYLIST_SELECTED_FILE_NAMES);
244 | ArrayList selectedFilesPaths = data.
245 | getStringArrayListExtra(Constants.ARRAYLIST_SELECTED_FILE_PATHS);
246 | if (requestCode == mApplicationUpgrade) {
247 | mCurrentFileName = selectedFiles.get(0);
248 | mCurrentFilePath = selectedFilesPaths.get(0);
249 | } else if (requestCode == mApplicationAndStackCombined) {
250 | mCurrentFileName = selectedFiles.get(0);
251 | mCurrentFilePath = selectedFilesPaths.get(0);
252 | }
253 | }
254 | super.onActivityResult(requestCode, resultCode, data);
255 | }
256 |
257 | @Override
258 | protected void onDestroy() {
259 | super.onDestroy();
260 | if(yModem != null) {
261 | yModem.stop();
262 | }
263 | BleManage.getInstance().disAll();
264 | EventBus.getDefault().unregister(this);
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/ui/activity/FileListActivity.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.ui.activity;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.AlertDialog;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.os.Bundle;
9 | import android.os.Environment;
10 |
11 | import android.view.View;
12 | import android.webkit.MimeTypeMap;
13 | import android.widget.AdapterView;
14 | import android.widget.Button;
15 | import android.widget.ListView;
16 | import android.widget.TextView;
17 | import android.widget.Toast;
18 |
19 | import androidx.annotation.Nullable;
20 | import androidx.fragment.app.FragmentActivity;
21 |
22 | import com.bw.ydb.R;
23 | import com.bw.ydb.model.OTAFileModel;
24 | import com.bw.ydb.ui.adapter.OTAFileListAdapter;
25 | import com.bw.ydb.utils.config.Constants;
26 | import java.io.File;
27 | import java.util.ArrayList;
28 | import java.util.Objects;
29 |
30 | public class FileListActivity extends FragmentActivity implements View.OnClickListener{
31 | private int mFilesCount;
32 | public static Boolean mApplicationInBackground = false;
33 | private final ArrayList mArrayListFiles = new ArrayList<>();
34 | private final ArrayList mArrayListPaths = new ArrayList<>();
35 | private final ArrayList mArrayListFileNames = new ArrayList<>();
36 |
37 | private OTAFileListAdapter mFirmwareAdapter;
38 | private ListView mFileListView;
39 | private Button mUpgrade;
40 | private Button mNext;
41 | private TextView mHeading;
42 |
43 |
44 | @Override
45 | protected void onCreate(@Nullable Bundle savedInstanceState) {
46 | super.onCreate(savedInstanceState);
47 | setContentView(R.layout.activity_filelist);
48 |
49 | initView();
50 |
51 | initData();
52 | }
53 |
54 | private void initView() {
55 | mFileListView = findViewById(R.id.mFileListView);
56 | mUpgrade = findViewById(R.id.upgrade_button);
57 | mNext = findViewById(R.id.next_button);
58 | mHeading = findViewById(R.id.heading_2);
59 |
60 | mUpgrade.setOnClickListener(this);
61 | mNext.setOnClickListener(this);
62 | }
63 |
64 | @SuppressLint("SetTextI18n")
65 | private void initData(){
66 | Bundle extras = getIntent().getExtras();
67 | if (extras != null) {
68 | mFilesCount = extras.getInt(Constants.REQ_FILE_COUNT);
69 | }
70 |
71 | /*
72 | 文件夹可以以随意创建 TESTBLE
73 | */
74 | File filedir = new File(Environment.getExternalStorageDirectory()
75 | + File.separator + "TESTBLE");
76 | mFirmwareAdapter = new OTAFileListAdapter(this,
77 | mArrayListFiles, mFilesCount);
78 | mFileListView.setAdapter(mFirmwareAdapter);
79 | searchRequiredFile(filedir);
80 |
81 | if (mFilesCount == OTAActivity.mApplicationAndStackSeparate) {
82 | mHeading.setText("Select stack upgrade file");
83 | mUpgrade.setVisibility(View.GONE);
84 | mNext.setVisibility(View.VISIBLE);
85 | } else {
86 | mUpgrade.setVisibility(View.VISIBLE);
87 | mNext.setVisibility(View.GONE);
88 | }
89 |
90 | /**
91 | * File Selection click event
92 | */
93 | mFileListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
94 | public void onItemClick(AdapterView> parent, View view,
95 | int position, long id) {
96 | OTAFileModel model = mArrayListFiles.get(position);
97 | model.setSelected(!model.isSelected());
98 | for (int i = 0; i < mArrayListFiles.size(); i++) {
99 | if (position != i) {
100 | mArrayListFiles.get(i).setSelected(false);
101 | }
102 | }
103 | mFirmwareAdapter.notifyDataSetChanged();
104 | }
105 | });
106 | }
107 |
108 |
109 | @Override
110 | public void onClick(View view) {
111 | switch (view.getId()){
112 | case R.id.upgrade_button:
113 | if (mFilesCount == OTAActivity.mApplicationAndStackSeparate) {
114 | for (int count = 0; count < mArrayListFiles.size(); count++) {
115 | if (mArrayListFiles.get(count).isSelected()) {
116 | mArrayListPaths.add(1, mArrayListFiles.get(count).getFilePath());
117 | mArrayListFileNames.add(1, mArrayListFiles.get(count).getFileName());
118 | }
119 | }
120 | } else {
121 | for (int count = 0; count < mArrayListFiles.size(); count++) {
122 | if (mArrayListFiles.get(count).isSelected()) {
123 | mArrayListPaths.add(0, mArrayListFiles.get(count).getFilePath());
124 | mArrayListFileNames.add(0, mArrayListFiles.get(count).getFileName());
125 | }
126 | }
127 | }
128 |
129 | if (mFilesCount == OTAActivity.mApplicationAndStackSeparate) {
130 | if (mArrayListPaths.size() == 2) {
131 | Intent returnIntent = new Intent();
132 | returnIntent.putExtra(Constants.SELECTION_FLAG, true);
133 | returnIntent.putExtra(Constants.ARRAYLIST_SELECTED_FILE_PATHS, mArrayListPaths);
134 | returnIntent.putExtra(Constants.ARRAYLIST_SELECTED_FILE_NAMES, mArrayListFileNames);
135 | setResult(RESULT_OK, returnIntent);
136 | finish();
137 | } else {
138 | alertFileSelection("Select the application upgrade file to be performed!");
139 | }
140 | } else if (mFilesCount != OTAActivity.mApplicationAndStackSeparate
141 | && mArrayListPaths.size() == 1) {
142 | Intent returnIntent = new Intent();
143 | returnIntent.putExtra(Constants.SELECTION_FLAG, true);
144 | returnIntent.putExtra(Constants.ARRAYLIST_SELECTED_FILE_PATHS, mArrayListPaths);
145 | returnIntent.putExtra(Constants.ARRAYLIST_SELECTED_FILE_NAMES, mArrayListFileNames);
146 | setResult(RESULT_OK, returnIntent);
147 | finish();
148 | } else {
149 | if (mFilesCount != OTAActivity.mApplicationAndStackCombined) {
150 | alertFileSelection("Select file for application upgrade!");
151 | } else {
152 | alertFileSelection("Select the stack and application upgrade files for the merged file!");
153 | }
154 | }
155 |
156 | break;
157 |
158 | case R.id.next_button:
159 | for (int count = 0; count < mArrayListFiles.size(); count++) {
160 | if (mArrayListFiles.get(count).isSelected()) {
161 | mArrayListPaths.add(0, mArrayListFiles.get(count).getFilePath());
162 | mArrayListFileNames.add(0, mArrayListFiles.get(count).getFileName());
163 | mHeading.setText("Select the application upgrade file");
164 | mArrayListFiles.remove(count);
165 | mFirmwareAdapter.addFiles(mArrayListFiles);
166 | mFirmwareAdapter.notifyDataSetChanged();
167 | mUpgrade.setVisibility(View.VISIBLE);
168 | mNext.setVisibility(View.GONE);
169 | }
170 | }
171 |
172 | if(mArrayListPaths.size() == 0){
173 | alertFileSelection("Select stack upgrade file!");
174 | }
175 | break;
176 | }
177 | }
178 |
179 | /**
180 | * Method to search phone/directory for the .bin files
181 | * 对手机搜索/目录的方法.bin文件
182 | * @param dir
183 | */
184 | private void searchRequiredFile(File dir) {
185 | if (dir.exists()) {
186 | String filePattern = "bin";
187 | File[] allFilesList = dir.listFiles();
188 | for (int pos = 0; pos < Objects.requireNonNull(allFilesList).length; pos++) {
189 | File analyseFile = allFilesList[pos];
190 | if (analyseFile != null) {
191 | if (analyseFile.isDirectory()) {
192 | searchRequiredFile(analyseFile);
193 | } else {
194 | Uri selectedUri = Uri.fromFile(analyseFile);
195 | String fileExtension
196 | = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
197 | if (fileExtension.equalsIgnoreCase(filePattern)) {
198 | OTAFileModel fileModel = new OTAFileModel(analyseFile.getName(),
199 | analyseFile.getAbsolutePath(), false, analyseFile.getParent());
200 | mArrayListFiles.add(fileModel);
201 | mFirmwareAdapter.addFiles(mArrayListFiles);
202 | mFirmwareAdapter.notifyDataSetChanged();
203 | }
204 | }
205 | }
206 | }
207 | } else {
208 | Toast.makeText(this, "Directory does not exist", Toast.LENGTH_SHORT).show();
209 | }
210 | }
211 |
212 | private void alertFileSelection(String message) {
213 | final AlertDialog.Builder builder = new AlertDialog.Builder(this);
214 | builder.setMessage(message)
215 | .setTitle(R.string.app_name)
216 | .setCancelable(true)
217 | .setPositiveButton("OK", new DialogInterface.OnClickListener() {
218 | public void onClick(DialogInterface dialog, int id) {
219 | dialog.dismiss();
220 | }
221 | });
222 | AlertDialog alert = builder.create();
223 | alert.show();
224 | }
225 |
226 | @Override
227 | protected void onResume() {
228 | mApplicationInBackground = false;
229 | super.onResume();
230 | }
231 |
232 | @Override
233 | protected void onPause() {
234 | mApplicationInBackground = true;
235 | super.onPause();
236 | }
237 |
238 | @Override
239 | protected void onDestroy() {
240 | super.onDestroy();
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/utils/BleManage.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.utils;
2 |
3 | import android.bluetooth.BluetoothGatt;
4 | import android.bluetooth.BluetoothGattCharacteristic;
5 | import android.bluetooth.BluetoothGattService;
6 | import android.util.Log;
7 |
8 | import com.bw.ydb.event.BleEvent;
9 | import com.bw.ydb.event.ConnectEvent;
10 | import com.bw.ydb.model.BleModel;
11 | import com.bw.ydb.utils.config.GattAttributes;
12 | import com.clj.fastble.BleManager;
13 | import com.clj.fastble.callback.BleGattCallback;
14 | import com.clj.fastble.callback.BleMtuChangedCallback;
15 | import com.clj.fastble.callback.BleNotifyCallback;
16 | import com.clj.fastble.callback.BleScanCallback;
17 | import com.clj.fastble.callback.BleWriteCallback;
18 | import com.clj.fastble.data.BleDevice;
19 | import com.clj.fastble.exception.BleException;
20 | import com.clj.fastble.scan.BleScanRuleConfig;
21 |
22 | import org.greenrobot.eventbus.EventBus;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 | import java.util.Objects;
27 |
28 | public class BleManage {
29 |
30 | private static BleManage instance = null;
31 |
32 | public static synchronized BleManage getInstance(){
33 | if(instance == null){
34 | instance = new BleManage();
35 | }
36 | return instance;
37 | }
38 |
39 |
40 | private final List successList = new ArrayList<>();
41 |
42 | private WriteStatus writeStatus;
43 |
44 | // 定义
45 | public void init(){
46 | int SCAN_PERIOD = 10000;
47 | BleManager.getInstance().enableLog(true)
48 | .setReConnectCount(10,5000)
49 | .setConnectOverTime(20000)
50 | .setOperateTimeout(SCAN_PERIOD);
51 | }
52 |
53 | // 规则
54 | public void rule(){
55 | BleScanRuleConfig scanRuleConfig = new BleScanRuleConfig.Builder()
56 | .setServiceUuids(null) // 只扫描指定的服务的设备,可选
57 | .setDeviceName(true) // 只扫描指定广播名的设备,可选
58 | .setDeviceMac(null) // 只扫描指定mac的设备,可选
59 | .setAutoConnect(false) // 连接时的autoConnect参数,可选,默认false
60 | .setScanTimeOut(10000) // 扫描超时时间,可选,默认10秒
61 | .build();
62 | BleManager.getInstance().initScanRule(scanRuleConfig);
63 | }
64 |
65 | //扫描
66 | public void scan(){
67 | BleManager.getInstance().scan(new BleScanCallback(){
68 |
69 | @Override
70 | public void onScanStarted(boolean success) {
71 |
72 | }
73 |
74 | @Override
75 | public void onLeScan(BleDevice bleDevice) {
76 | super.onLeScan(bleDevice);
77 | Log.i("Begin...","Start...");
78 | }
79 |
80 | @Override
81 | public void onScanning(BleDevice bleDevice) {
82 | BleModel model = new BleModel();
83 | model.setBleDevice(bleDevice);
84 | model.setName(bleDevice.getName());
85 | model.setMac(bleDevice.getMac());
86 | model.setRss(bleDevice.getRssi());
87 |
88 | EventBus.getDefault().post(new BleEvent(model));
89 |
90 | }
91 |
92 | @Override
93 | public void onScanFinished(List scanResultList) {
94 |
95 | }
96 | });
97 | }
98 |
99 |
100 | // 取消
101 | public void cancel(){
102 | BleManager.getInstance().cancelScan();
103 | }
104 |
105 | // 连接
106 | public void connect(BleDevice bleDevice){
107 |
108 | BleManager.getInstance().connect(bleDevice,new BleGattCallback(){
109 |
110 | @Override
111 | public void onStartConnect() {
112 | Log.i("====TAG===","开始连接");
113 | }
114 |
115 | @Override
116 | public void onConnectFail(BleDevice bleDevice, BleException exception) {
117 | if(successList.size() > 0){
118 | for(int i =0; i< successList.size(); i++){
119 | if(Objects.equals(bleDevice.getMac(), successList.get(i).getBleDevice().getMac())){
120 | successList.remove(i);
121 | }
122 | }
123 | }
124 | EventBus.getDefault().post(new ConnectEvent(successList));
125 | }
126 |
127 | @Override
128 | public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
129 | if(bleDevice != null){
130 | BleModel model = new BleModel();
131 | model.setBleDevice(bleDevice);
132 | model.setConnect(true);
133 | successList.add(model);
134 | }
135 | EventBus.getDefault().post(new ConnectEvent(successList));
136 | }
137 |
138 | @Override
139 | public void onDisConnected(boolean isActiveDisConnected, BleDevice device, BluetoothGatt gatt, int status) {
140 | if(successList.size() > 0){
141 | for(int i =0; i< successList.size(); i++){
142 | if(Objects.equals(bleDevice.getMac(), successList.get(i).getBleDevice().getMac())){
143 | successList.remove(i);
144 | }
145 | }
146 | }
147 | EventBus.getDefault().post(new ConnectEvent(successList));
148 | }
149 | });
150 |
151 | }
152 |
153 | // 通知
154 | public void notify(BleDevice bleDevice, String service, String characteristic){
155 | BleManager.getInstance().notify(bleDevice, service, characteristic, new MyBleNotifyCallback(bleDevice));
156 | }
157 |
158 |
159 |
160 | // 监听通知
161 | class MyBleNotifyCallback extends BleNotifyCallback{
162 |
163 | private BleDevice bleDevice;
164 |
165 | public MyBleNotifyCallback(BleDevice bleDevice) {
166 | this.bleDevice = bleDevice;
167 | }
168 |
169 | @Override
170 | public void onNotifySuccess() {
171 |
172 | }
173 |
174 | @Override
175 | public void onNotifyFailure(BleException exception) {
176 | Log.i("Error", exception.toString());
177 | }
178 |
179 | @Override
180 | public void onCharacteristicChanged(byte[] data) {
181 | try {
182 | // 接收数据
183 | for (int i = 0; i < successList.size(); i++) {
184 | if (successList.get(i).sendCharacter != null) {
185 | if (Objects.equals(successList.get(i).getMac(), bleDevice.getMac())) {
186 | // 进行ota的数据读取或者交换
187 | for(BleModel model:successList){
188 | model.setOtaByte(data);
189 | }
190 | }
191 | }
192 | }
193 | //返回数据
194 | EventBus.getDefault().post(new ConnectEvent(successList));
195 |
196 | }catch (Exception e){
197 | e.printStackTrace();
198 | }
199 | }
200 | }
201 |
202 |
203 | // 读取服务
204 | public void readServer(BleDevice bleDevice){
205 | try {
206 | BluetoothGatt gatt = BleManager.getInstance().getBluetoothGatt(bleDevice);
207 | List serviceList = gatt.getServices();
208 | for (BluetoothGattService service : serviceList) {
209 | Log.i("services is : ", service.getUuid().toString());
210 | List characteristicList = service.getCharacteristics();
211 | for (BluetoothGattCharacteristic characteristic : characteristicList) {
212 | if (characteristic.getUuid().toString().equals(GattAttributes.BW_PROJECT_OTA_DATA)) {
213 | Log.i("characteristic is : ", characteristic.getUuid().toString());
214 | for (BleModel model : successList) {
215 | if (Objects.equals(model.getBleDevice().getMac(), bleDevice.getMac())) {
216 | model.setSendCharacter(characteristic.toString());
217 | model.setService(service.toString());
218 | // 发送通知
219 | notify(bleDevice, service.getUuid().toString(), characteristic.getUuid().toString());
220 | }
221 | }
222 | }
223 | }
224 | }
225 | }catch (Exception e){
226 | e.printStackTrace();
227 | }
228 | }
229 |
230 | // 写入蓝牙数据
231 | public void write(BleDevice bleDevice, String service, String character, byte[] data, WriteStatus lister){
232 | BleManager.getInstance().write(
233 | bleDevice,
234 | service,
235 | character,
236 | data,
237 | new BleWriteCallback() {
238 | @Override
239 | public void onWriteSuccess(int current, int total, byte[] justWrite) {
240 | Log.i("Success", justWrite.toString());
241 | lister.onSuccess(current,total,justWrite);
242 | }
243 |
244 | @Override
245 | public void onWriteFailure(BleException exception) {
246 | Log.i("Error", exception.toString());
247 | lister.onFail(exception);
248 | }
249 | }
250 | );
251 | }
252 |
253 |
254 | // 断开蓝牙
255 | public void disCon(BleDevice bleDevice){
256 | if(successList.size() > 0){
257 | for(int i =0; i< successList.size(); i++){
258 | if(Objects.equals(bleDevice.getMac(), successList.get(i).getBleDevice().getMac())){
259 | successList.remove(i);
260 | }
261 | }
262 | }
263 | BleManager.getInstance().disconnect(bleDevice);
264 | EventBus.getDefault().post(new ConnectEvent(successList));
265 | }
266 |
267 |
268 | //断开所有蓝牙
269 | public void disAll(){
270 | if(successList.size() > 0){
271 | successList.clear();
272 | }
273 | BleManager.getInstance().disconnectAllDevice();
274 | }
275 |
276 |
277 | // 设置mtu
278 | public void sendMtu(BleDevice bleDevice){
279 | BleManager.getInstance().setMtu(bleDevice, 512, new BleMtuChangedCallback() {
280 | @Override
281 | public void onSetMTUFailure(BleException exception) {
282 | Log.i("Send mtu tag is ", exception.toString());
283 | }
284 |
285 | @Override
286 | public void onMtuChanged(int mtu) {
287 | Log.i("Send mtu tag is "+mtu, "mtu success!");
288 | }
289 | });
290 | }
291 |
292 |
293 |
294 |
295 |
296 |
297 | }
298 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/data/service/BluetoothService.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.data.service;
2 |
3 | import android.app.Service;
4 | import android.bluetooth.BluetoothAdapter;
5 | import android.bluetooth.BluetoothDevice;
6 | import android.bluetooth.BluetoothGatt;
7 | import android.bluetooth.BluetoothGattCallback;
8 | import android.bluetooth.BluetoothGattCharacteristic;
9 | import android.bluetooth.BluetoothGattDescriptor;
10 | import android.bluetooth.BluetoothGattService;
11 | import android.bluetooth.BluetoothManager;
12 | import android.bluetooth.BluetoothProfile;
13 | import android.content.Context;
14 | import android.content.Intent;
15 | import android.os.Binder;
16 | import android.os.IBinder;
17 | import android.util.Log;
18 |
19 | import com.bw.ydb.utils.config.BroadCast;
20 | import com.bw.ydb.utils.config.Constants;
21 | import com.bw.ydb.utils.config.GattAttributes;
22 | import com.bw.ydb.utils.config.UUIDDataBase;
23 | import com.bw.ydb.utils.tool.BWDataParser;
24 |
25 | import java.util.List;
26 | import java.util.UUID;
27 |
28 |
29 | public class BluetoothService extends Service{
30 | private final static String TAG = BluetoothService.class.getSimpleName();
31 | private final IBinder iBinder = new LocalBinder();
32 | private BluetoothManager bluetoothManager;
33 | private BluetoothAdapter bluetoothAdapter;
34 | private BluetoothGatt bluetoothGatt;
35 | private String bluetoothDeviceAddress;
36 | private Context mContext;
37 |
38 |
39 |
40 |
41 | @Override
42 | public IBinder onBind(Intent intent) {
43 | return iBinder;
44 | }
45 |
46 | public class LocalBinder extends Binder {
47 | public BluetoothService getService() {
48 | return BluetoothService.this;
49 | }
50 | }
51 |
52 | /**
53 | 初始化蓝牙
54 | */
55 | public boolean initialize() {
56 | if (bluetoothManager == null) {
57 | bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
58 | if (bluetoothManager == null) {
59 | Log.e(TAG, "Unable to initialize BluetoothManager.");
60 | return false;
61 | }
62 | }
63 | bluetoothAdapter = bluetoothManager.getAdapter();
64 | if (bluetoothManager == null) {
65 | Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
66 | return false;
67 | }
68 | return true;
69 | }
70 |
71 | /**
72 | 解除绑定
73 | */
74 | @Override
75 | public boolean onUnbind(Intent intent) {
76 | //执行解绑的操作
77 | return super.onUnbind(intent);
78 | }
79 |
80 | /**
81 | 连接蓝牙
82 | */
83 | public boolean connect(final String address,Context context) {
84 | mContext = context;
85 | if (bluetoothAdapter == null || address == null) {
86 | Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
87 | return false;
88 | }
89 |
90 | if (address.equals(bluetoothDeviceAddress)
91 | && bluetoothGatt != null) {
92 | Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
93 | if (bluetoothGatt.connect()) {
94 | //mConnectionState = STATE_CONNECTING;
95 | return true;
96 | } else {
97 | return false;
98 | }
99 | }
100 |
101 | final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
102 | if (device == null) {
103 | Log.w(TAG, "Device not found. Unable to connect.");
104 | return false;
105 | }
106 | bluetoothGatt = device.connectGatt(this, false, mGattCallback);
107 | Log.d(TAG, "Trying to create a new connection.");
108 | bluetoothDeviceAddress = address;
109 | return true;
110 | }
111 |
112 | /**
113 | 利用BluetoothGatt连接蓝牙
114 | 蓝牙传输数据
115 | */
116 | private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
117 | @Override
118 | public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
119 | String intentAction;
120 | if (status == BluetoothGatt.GATT_SUCCESS) {
121 | //连接成功的时候
122 | if (newState == BluetoothProfile.STATE_CONNECTED) {
123 | //连接成功
124 | intentAction = BroadCast.ACTION_GATT_CONNECTED;
125 | //发送广播
126 | broadcastUpdate(intentAction);
127 | //打印连接服务
128 | Log.i(TAG, "Connected to GATT server.");
129 | //立即去执行发现服务
130 | gatt.discoverServices();
131 | }
132 |
133 | //当断开连接的时候
134 | else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
135 | //断开连接
136 | intentAction = BroadCast.ACTION_GATT_DISCONNECTED;
137 | //打印断开连接
138 | Log.i(TAG, "Disconnected from GATT server.");
139 | //发送广播
140 | broadcastUpdate(intentAction);
141 | }
142 | }
143 | }
144 |
145 | @Override
146 | public void onServicesDiscovered(BluetoothGatt gatt, int status) {
147 | //连接成功
148 | if(status==BluetoothGatt.GATT_SUCCESS){
149 | //发送发现服务的广播
150 | broadcastUpdate(BroadCast.ACTION_GATT_SERVICES_DISCOVERED);
151 | Log.i(TAG,"discoverServiced is ok");
152 | }else {
153 | Log.w(TAG, "onServicesDiscovered received: " + status); //发现设备的时候 发送广播
154 | }
155 | }
156 |
157 | @Override
158 | public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
159 | Log.i("读取出来的值",gatt+" "+characteristic+" "+status);
160 | if (status == BluetoothGatt.GATT_SUCCESS) {
161 | //读取到里面的数据时候发送广播
162 | broadcastUpdate(BroadCast.ACTION_DATA_AVAILABLE, characteristic);
163 | Log.i(TAG,"Read Data");
164 | }
165 | }
166 |
167 | @Override
168 | public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
169 | broadcastUpdate(BroadCast.ACTION_DATA_AVAILABLE, characteristic);
170 | }
171 |
172 | @Override
173 | public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
174 | if(status == BluetoothGatt.GATT_SUCCESS){
175 | BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
176 | broadcastUpdate(BroadCast.ACTION_DATA_AVAILABLE, characteristic);
177 | Log.i(TAG,"success is data");
178 | }
179 | }
180 |
181 | @Override
182 | public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
183 | if (status == BluetoothGatt.GATT_SUCCESS) {
184 | Log.i(TAG,"write success data");
185 | }else{
186 | Intent intent = new Intent(BroadCast.ACTION_GATT_CHARACTERISTIC_ERROR);
187 | intent.putExtra(Constants.EXTRA_CHARACTERISTIC_ERROR_MESSAGE, "" + status);
188 | mContext.sendBroadcast(intent);
189 | }
190 | }
191 | };
192 |
193 | /**
194 | 更新的时候发送广播
195 | */
196 | private void broadcastUpdate(final String action) {
197 | final Intent intent = new Intent(action);
198 | sendBroadcast(intent);
199 | }
200 |
201 | /**
202 | 广播更新数据2
203 | */
204 | private void broadcastUpdate(final String action,final BluetoothGattCharacteristic characteristic){
205 | final Intent intent = new Intent(action);
206 | // case for OTA characteristic received
207 | if (characteristic.getUuid().equals(UUIDDataBase.UUID_OTA_UPDATE_CHARACTERISTIC)) {
208 | String ota = BWDataParser.getOtaData(characteristic);
209 | intent.putExtra(BroadCast.ACTION_OTA_DATA,ota);
210 | }
211 | sendBroadcast(intent);
212 | }
213 |
214 | public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
215 | if (bluetoothGatt == null) {
216 | Log.w(TAG, "BluetoothAdapter not initialized");
217 | return;
218 | }
219 | bluetoothGatt.readCharacteristic(characteristic);
220 | }
221 |
222 |
223 | public void setCharacteristicIndication(
224 | BluetoothGattCharacteristic characteristic, boolean enabled) {
225 | String serviceUUID = characteristic.getService().getUuid().toString();
226 | String characteristicUUID = characteristic.getUuid().toString();
227 | Log.i("==TAG==",serviceUUID+" "+characteristicUUID);
228 | if (bluetoothAdapter == null) {
229 | return;
230 | }
231 | if (characteristic.getDescriptor(UUID.fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)) != null) {
232 | if (enabled) {
233 | BluetoothGattDescriptor descriptor = characteristic
234 | .getDescriptor(UUID
235 | .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
236 | descriptor
237 | .setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
238 | bluetoothGatt.writeDescriptor(descriptor);
239 |
240 | } else {
241 | BluetoothGattDescriptor descriptor = characteristic
242 | .getDescriptor(UUID
243 | .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
244 | descriptor
245 | .setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
246 | bluetoothGatt.writeDescriptor(descriptor);
247 |
248 | }
249 | }
250 | bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
251 | }
252 |
253 | public boolean writeCharacteristic(BluetoothGattCharacteristic charac,String message){
254 | //check mBluetoothGatt is available
255 | if (bluetoothGatt == null) {
256 | Log.e(TAG, "lost connection");
257 | return false;
258 | }
259 | if(charac!=null&&!message.equals(null)&&!message.equals("")){
260 | //int a = Integer.parseInt(message);
261 | byte []a = convertingTobyteArray(message);
262 | charac.setValue(a);
263 | boolean status = bluetoothGatt.writeCharacteristic(charac);
264 | return status;
265 | }else{
266 | return false;
267 | }
268 | }
269 |
270 | /**
271 | * Method to convert hex to byteArray
272 | */
273 | private static byte[] convertingTobyteArray(String result) {
274 | String[] splited = result.split("\\s+");
275 | byte[] valueByte = new byte[splited.length];
276 | for (int i = 0; i < splited.length; i++) {
277 | if (splited[i].length() > 2) {
278 | String trimmedByte = splited[i].split("x")[1];
279 | valueByte[i] = (byte) convertstringtobyte(trimmedByte);
280 | }
281 | }
282 | return valueByte;
283 | }
284 |
285 | /**
286 | * Convert the string to byte
287 | *
288 | * @param string
289 | * @return
290 | */
291 | private static int convertstringtobyte(String string) {
292 | return Integer.parseInt(string, 16);
293 | }
294 |
295 |
296 |
297 | /**
298 | 写入蓝牙数据
299 | */
300 | public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic,byte[] data) {
301 | if (bluetoothGatt == null) {
302 | Log.e(TAG, "lost connection");
303 | return false;
304 | }
305 | if (characteristic != null && (data.length > 0)) {
306 | characteristic.setValue(data);
307 | boolean status = bluetoothGatt.writeCharacteristic(characteristic);
308 | return status;
309 | }else{
310 | return false;
311 | }
312 | }
313 |
314 |
315 | public List getSupportedGattServices() {
316 | if (bluetoothGatt == null) {
317 | return null;
318 | }
319 | return bluetoothGatt.getServices();
320 | }
321 |
322 | public void close() {
323 | if(bluetoothGatt==null){
324 | return;
325 | }else{
326 | bluetoothGatt.close();
327 | bluetoothGatt = null;
328 | }
329 | }
330 |
331 | public void disconnect() {
332 | if (bluetoothAdapter == null || bluetoothGatt == null) {
333 | Log.w(TAG, "BluetoothAdapter not initialized");
334 | return;
335 | }
336 | bluetoothGatt.disconnect();
337 | }
338 |
339 |
340 |
341 |
342 | }
343 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ydb/ui/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.bw.ydb.ui.activity;
2 |
3 | import android.Manifest;
4 | import android.content.DialogInterface;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.net.Uri;
8 | import android.os.Environment;
9 | import android.os.Handler;
10 |
11 | import android.os.Bundle;
12 | import android.os.Looper;
13 | import android.provider.Settings;
14 | import android.util.Log;
15 | import android.view.KeyEvent;
16 | import android.view.View;
17 | import android.widget.AdapterView;
18 | import android.widget.ListView;
19 | import android.widget.Toast;
20 |
21 | import androidx.annotation.NonNull;
22 | import androidx.annotation.Nullable;
23 | import androidx.appcompat.app.AlertDialog;
24 | import androidx.appcompat.app.AppCompatActivity;
25 | import androidx.core.app.ActivityCompat;
26 | import androidx.core.content.ContextCompat;
27 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
28 |
29 | import com.bw.ydb.R;
30 | import com.bw.ydb.event.BleEvent;
31 | import com.bw.ydb.model.BleModel;
32 | import com.bw.ydb.ui.adapter.LeDeviceListAdapter;
33 | import com.bw.ydb.utils.BleManage;
34 | import com.bw.ydb.utils.config.Constants;
35 | import com.bw.ydb.widgets.CustomsDialog;
36 | import com.clj.fastble.BleManager;
37 |
38 | import org.greenrobot.eventbus.EventBus;
39 | import org.greenrobot.eventbus.Subscribe;
40 | import org.greenrobot.eventbus.ThreadMode;
41 |
42 | import java.io.File;
43 | import java.util.ArrayList;
44 | import java.util.List;
45 |
46 |
47 | /**
48 | * mac 代码 格式化 OPTION + CMD + L
49 | */
50 |
51 | public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener{
52 |
53 | private ListView mDeviceList;
54 | private SwipeRefreshLayout mRefresh;
55 | private LeDeviceListAdapter mLeDeviceListAdapter;
56 |
57 | private static final int MY_PERMISSION_REQUEST_CODE = 10000;
58 |
59 | //扫描时间为5秒
60 | private static final int SCAN_PERIOD = 10000;
61 | private static final int REQUEST_ENABLE_BT = 1;
62 |
63 | private CustomsDialog mDialog;
64 | private Handler mHandler;
65 |
66 | protected List dataList = new ArrayList<>();
67 |
68 |
69 | @Override
70 | protected void onCreate(Bundle savedInstanceState) {
71 | super.onCreate(savedInstanceState);
72 | setContentView(R.layout.activity_main);
73 |
74 | EventBus.getDefault().register(this);
75 |
76 | initView();
77 |
78 | BleManager.getInstance().init(getApplication());
79 | //初始化
80 | BleManage.getInstance().init();
81 | BleManage.getInstance().rule();
82 |
83 | requestPermissions();
84 |
85 | }
86 |
87 |
88 |
89 | private void requestPermissions(){
90 | /**
91 | * 第 1 步: 检查是否有相应的权限
92 | */
93 | boolean isAllGranted = checkPermissionAllGranted(
94 | new String[]{
95 | Manifest.permission.ACCESS_FINE_LOCATION,
96 | Manifest.permission.WRITE_EXTERNAL_STORAGE,
97 | Manifest.permission.READ_EXTERNAL_STORAGE,
98 | Manifest.permission.ACCESS_COARSE_LOCATION,
99 | Manifest.permission.BLUETOOTH
100 | }
101 | );
102 | // 如果这3个权限全都拥有, 则直接执行备份代码
103 | if (isAllGranted) {
104 | autoRefresh();
105 | return;
106 | }
107 |
108 | /**
109 | * 第 2 步: 请求权限
110 | */
111 | // 一次请求多个权限, 如果其他有权限是已经授予的将会自动忽略掉
112 | ActivityCompat.requestPermissions(
113 | this,
114 | new String[]{
115 | Manifest.permission.ACCESS_FINE_LOCATION,
116 | Manifest.permission.WRITE_EXTERNAL_STORAGE,
117 | Manifest.permission.READ_EXTERNAL_STORAGE,
118 | Manifest.permission.ACCESS_COARSE_LOCATION,
119 | Manifest.permission.BLUETOOTH
120 | },
121 | MY_PERMISSION_REQUEST_CODE
122 | );
123 |
124 | }
125 |
126 | @Subscribe(threadMode = ThreadMode.MAIN)
127 | public void onBleEvent(BleEvent event){
128 | if(!dataList.contains(event.getModel())){
129 | if(event.getModel().getBleDevice().getName() != null){
130 | dataList.add(event.getModel());
131 | LeDeviceListAdapter.mLeDevices = dataList;
132 | mLeDeviceListAdapter.notifyDataSetChanged();
133 | }
134 | }
135 | }
136 |
137 |
138 | /**
139 | * 检查是否拥有指定的所有权限
140 | */
141 | private boolean checkPermissionAllGranted(String[] permissions) {
142 | for (String permission : permissions) {
143 | if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
144 | // 只要有一个权限没有被授予, 则直接返回 false
145 | return false;
146 | }
147 | }
148 | return true;
149 | }
150 |
151 |
152 |
153 | @Override
154 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
155 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
156 |
157 | if (requestCode == MY_PERMISSION_REQUEST_CODE) {
158 | boolean isAllGranted = true;
159 |
160 | // 判断是否所有的权限都已经授予了
161 | for (int grant : grantResults) {
162 | if (grant != PackageManager.PERMISSION_GRANTED) {
163 | isAllGranted = false;
164 | break;
165 | }
166 | }
167 |
168 | if (isAllGranted) {
169 | // 如果所有的权限都授予了, 则执行备份代码
170 | //doBackup();
171 | autoRefresh();
172 | } else {
173 | // 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮
174 | openAppDetails();
175 | }
176 | }
177 | }
178 |
179 | /**
180 | * 打开 APP 的详情设置
181 | */
182 | private void openAppDetails() {
183 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
184 | builder.setMessage("蓝牙需要访问 “定位” 和 “外部存储器”,“蓝牙”,请到 “应用信息 -> 权限” 中授予!");
185 | builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() {
186 | @Override
187 | public void onClick(DialogInterface dialog, int which) {
188 | Intent intent = new Intent();
189 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
190 | intent.addCategory(Intent.CATEGORY_DEFAULT);
191 | intent.setData(Uri.parse("package:" + getPackageName()));
192 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
193 | intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
194 | intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
195 | startActivity(intent);
196 | }
197 | });
198 | builder.setNegativeButton("取消", null);
199 | builder.show();
200 | }
201 |
202 |
203 |
204 | /**
205 | * 启动的时候要扫描蓝牙设备
206 | */
207 | @Override
208 | protected void onResume() {
209 | super.onResume();
210 | //自动扫描
211 | requestPermissions();
212 | }
213 |
214 | /**
215 | * 自动执行刷新
216 | */
217 | public void autoRefresh(){
218 | new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
219 | @Override
220 | public void run() {
221 |
222 | if(dataList.size() > 0){
223 | dataList.clear();
224 | }
225 |
226 | //BleManage.getInstance().cancel();
227 | BleManage.getInstance().scan();
228 |
229 | mRefresh.setRefreshing(false);
230 | }
231 | }, 1000);
232 | mRefresh.setRefreshing(true); //直接调用是没有用的
233 | }
234 |
235 |
236 |
237 | private void initView() {
238 | mHandler = new Handler(Looper.getMainLooper());
239 | mDeviceList = findViewById(R.id.mDeviceList);
240 | mRefresh = findViewById(R.id.mRefresh);
241 | mRefresh.setOnRefreshListener(this);
242 | mRefresh.setColorSchemeResources(R.color.colorNav);
243 | //点击事件
244 | mDeviceList.setOnItemClickListener(onItemClickListener);
245 |
246 | // Initializes list view adapter.
247 | mLeDeviceListAdapter = new LeDeviceListAdapter(this);
248 | //添加到蓝牙设备
249 | mDeviceList.setAdapter(mLeDeviceListAdapter);
250 |
251 | try {
252 | if (isok()) {
253 | String b = "/storage/emulated/0/TESTBLE";
254 | File file = new File(b);
255 | if (!file.exists()) {
256 | file.mkdirs();
257 | Log.i("Create_file", "文件夹不存在创建文件夹");
258 | } else {
259 | Log.i("Create_file", "文件夹存在不需要创建");
260 | }
261 | }
262 | } catch (Exception e) {
263 | e.printStackTrace();
264 | }
265 |
266 | }
267 |
268 | private boolean isok() {
269 | String status = Environment.getExternalStorageState();
270 | return status.equals(Environment.MEDIA_MOUNTED);
271 | }
272 |
273 |
274 |
275 | /**
276 | * listview点击项目
277 | */
278 | private AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
279 | @Override
280 | public void onItemClick(AdapterView> adapterView, View view, int position, long l) {
281 | BleModel device = mLeDeviceListAdapter
282 | .getDevice(position);
283 | if (device == null) {
284 | return;
285 | }
286 | showDialog(device);
287 | }
288 | };
289 |
290 | private void showDialog(BleModel model){
291 | CustomsDialog.Builder builder = new CustomsDialog.Builder(MainActivity.this);
292 | builder.setTips("蓝牙连接");
293 | builder.setContent("OTA升级");
294 |
295 | builder.setNegativeButton(R.string.custom_dialog_left, (dialogInterface, i) -> {
296 | dialogInterface.dismiss();
297 | // 取消扫描
298 | BleManage.getInstance().cancel();
299 | // 连接蓝牙
300 | BleManage.getInstance().connect(model.getBleDevice());
301 |
302 | Intent intent = new Intent(MainActivity.this,OTAActivity.class);
303 | //intent.putExtra(OTAActivity.EXTRAS_DEVICE_NAME,model.getName());
304 | // intent.putExtra(OTAActivity.EXTRAS_DEVICE_ADDRESS, model.getBleDevice().getDevice().getAddress());
305 |
306 | intent.putExtra(Constants.BLE_MODEL,model);
307 |
308 | startActivity(intent);
309 | }).setPositiveButton(R.string.custom_dialog_right, new DialogInterface.OnClickListener() {
310 | @Override
311 | public void onClick(DialogInterface dialogInterface, int i) {
312 | dialogInterface.dismiss();
313 | }
314 | });
315 | mDialog = builder.create();
316 | mDialog.show();
317 | mDialog.setCanceledOnTouchOutside(true);
318 | }
319 |
320 |
321 |
322 | @Override
323 | public void onRefresh() {
324 | new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
325 | @Override
326 | public void run() {
327 |
328 | if(dataList.size() > 0){
329 | dataList.clear();
330 | }
331 |
332 | BleManage.getInstance().cancel();
333 |
334 | BleManage.getInstance().scan();
335 |
336 | // 停止刷新
337 | mRefresh.setRefreshing(false);
338 | }
339 | }, 2000); // 5秒后发送消息,停止刷新
340 | }
341 |
342 | /**
343 | * 退出程序显示提示
344 | */
345 | private long mExitTime;
346 | @Override
347 | public boolean onKeyDown(int keyCode, KeyEvent event) {
348 | if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN
349 | && event.getRepeatCount() == 0) {
350 | if ((System.currentTimeMillis() - mExitTime) > 2000) {
351 | Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
352 | mExitTime = System.currentTimeMillis();
353 | }
354 | else {
355 | //ActivityCollector.finishAll();
356 | finish();
357 | System.exit(0);
358 | }
359 | return true;
360 | }
361 | return super.onKeyDown(keyCode, event);
362 | }
363 |
364 |
365 | @Override
366 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
367 | super.onActivityResult(requestCode, resultCode, data);
368 | if (requestCode == REQUEST_ENABLE_BT) {
369 | if (resultCode == RESULT_OK) {
370 | Log.d("TAG", "打开蓝牙成功!");
371 | }
372 |
373 | if (resultCode == RESULT_CANCELED) {
374 | Log.d("TAG", "放弃打开蓝牙!");
375 | }
376 |
377 | } else {
378 | Log.d("TAG", "蓝牙异常!");
379 | }
380 | }
381 |
382 |
383 |
384 | @Override
385 | protected void onDestroy() {
386 | super.onDestroy();
387 |
388 | BleManage.getInstance().cancel();
389 | BleManage.getInstance().disAll();
390 | BleManager.getInstance().destroy();
391 | EventBus.getDefault().unregister(this);
392 |
393 | }
394 |
395 | }
396 |
397 | /*
398 |
399 |
400 |
401 |
402 | * */
403 |
--------------------------------------------------------------------------------