> 1)^0xA001;
150 | }else{
151 | Reg_CRC >>=1;
152 | }
153 | }
154 | }
155 | return Integer.toHexString((Reg_CRC&0xffff));
156 | }
157 |
158 |
159 | /**
160 | * 计算CRC16校验码
161 | *
162 | * @param bytes
163 | * @return
164 | */
165 | public static String getCRC(byte[] bytes) {
166 | int CRC = 0x0000ffff;
167 | int POLYNOMIAL = 0x0000a001;
168 |
169 | int i, j;
170 | for (i = 0; i < bytes.length; i++) {
171 | CRC ^= ((int) bytes[i] & 0x000000ff);
172 | for (j = 0; j < 8; j++) {
173 | if ((CRC & 0x00000001) != 0) {
174 | CRC >>= 1;
175 | CRC ^= POLYNOMIAL;
176 | } else {
177 | CRC >>= 1;
178 | }
179 | }
180 | }
181 | return Integer.toHexString(CRC);
182 | }
183 |
184 |
185 |
186 | ```
187 |
188 |
189 |
190 |
191 |
192 | #### Gradle Use
193 |
194 | ```java
195 | allprojects {
196 | repositories {
197 | ...
198 | maven { url 'https://jitpack.io' }
199 | }
200 | }
201 | ```
202 |
203 |
204 | ```java
205 | dependencies {
206 | implementation 'com.github.ArdWang:YModemLib:2.0.3'
207 | }
208 |
209 | ```
210 |
211 | #### Maven Use
212 |
213 | ```java
214 |
215 | allprojects {
216 | repositories {
217 | ...
218 | maven { url 'https://jitpack.io' }
219 | }
220 | }
221 |
222 |
223 |
224 | jitpack.io
225 | https://jitpack.io
226 |
227 |
228 | ```
229 |
230 | ```java
231 |
232 | com.github.ArdWang
233 | YModemLib
234 | version
235 |
236 |
237 | ```
238 |
239 | For other operations, please see the operations in the app
240 | into [YModemBleDemo](https://github.com/ArdWang/YModemBleUpdate "悬停显示")
241 |
242 |
243 | YModem delegate
244 | ```java
245 | /**
246 | * ========================================================================================
247 | * THE YMODEM:
248 | * Send 0x05>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>* 发送0x05
249 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
250 | * SOH 00 FF "foo.c" "1064'' NUL[118] CRC CRC >>>>>>>>>>>>>
251 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
252 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
253 | * STX 01 FE data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>>
254 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
255 | * ACK STX 02 FD data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
256 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
257 | * ACK STX 03 FC data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
258 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
259 | * STX 04 FB data[256] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
260 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
261 | * SOH 05 FA data[100] 1A[28] CRC CRC>>>>>>>>>>>>>>>>>>
262 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
263 | * EOT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
264 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< NAK
265 | * EOT>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
266 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
267 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
268 | * SOH 00 FF NUL[128] CRC CRC >>>>>>>>>>>>>>>>>>>>>>>
269 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
270 | * ===========================================================================================
271 | **/
272 |
273 | ```
274 |
--------------------------------------------------------------------------------
/YModemLibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/YModemLibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 27
5 |
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 19
10 | targetSdkVersion 27
11 |
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 |
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 |
28 | implementation 'com.android.support:appcompat-v7:27.1.1'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
31 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
32 | }
33 |
34 |
35 | task deleteJar(type: Delete) {
36 | delete 'libs/jars/logmanagementlib.jar'
37 | }
38 |
39 | task createJar(type: Copy) {
40 | from('build/intermediates/aar_main_jar/release/')
41 | into('libs/jars/')
42 | include('classes.jar')
43 | rename('classes.jar', 'ymodem.jar')
44 | }
45 |
46 | createJar.dependsOn(deleteJar, build)
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/YModemLibrary/libs/jars/ymodem.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/YModemLibrary/libs/jars/ymodem.jar
--------------------------------------------------------------------------------
/YModemLibrary/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 |
--------------------------------------------------------------------------------
/YModemLibrary/src/androidTest/java/com/bw/yml/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
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.yml.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/CRC16.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | /**
4 | * Uses table for irreducible polynomial: 1 + x^2 + x^15 + x^16
5 | */
6 |
7 | public class CRC16 {
8 |
9 | private static final int[] table = {
10 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
11 | 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
12 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
13 | 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
14 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
15 | 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
16 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
17 | 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
18 | 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
19 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
20 | 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
21 | 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
22 | 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
23 | 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
24 | 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
25 | 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
26 | 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
27 | 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
28 | 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
29 | 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
30 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
31 | 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
32 | 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
33 | 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
34 | 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
35 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
36 | 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
37 | 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
38 | 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
39 | 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
40 | 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
41 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
42 | };
43 |
44 | public int getCRCLength() {
45 | return 2;
46 | }
47 |
48 | public long calcCRC(byte[] block) {
49 | int crc = 0x0000;
50 | for (byte b : block) {
51 | crc = ((crc << 8) ^ table[((crc >> 8) ^ (0xff & b))]) & 0xFFFF;
52 | }
53 |
54 | return crc;
55 | }
56 |
57 | public static int crc16_byte(int crc, byte b) {
58 | final int[] crc16_table = {
59 | 0x0000, 0xCC01, 0xD801, 0x1400,
60 | 0xF001, 0x3C00, 0x2800, 0xE401,
61 | 0xA001, 0x6C00, 0x7800, 0xB401,
62 | 0x5000, 0x9C01, 0x8801, 0x4400
63 | };
64 | int temp;
65 | // Compute checksum of lower four bits of a byte.
66 | temp = crc16_table[crc & 0xF];
67 | crc = (crc >> 4) & 0x0FFF;
68 | crc = crc ^ temp ^ crc16_table[b & 0xF];
69 | // Now compute checksum of upper four bits of a byte.
70 | temp = crc16_table[crc & 0xF];
71 | crc = (crc >> 4) & 0x0FFF;
72 | crc = crc ^ temp ^ crc16_table[(b >> 4) & 0xF];
73 | return crc;
74 | }
75 |
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/FileStreamThread.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.util.concurrent.atomic.AtomicBoolean;
8 |
9 | /**
10 | * Thread for reading input Stream and encapsulating into a ymodem package
11 | *
12 | */
13 |
14 | public class FileStreamThread extends Thread {
15 |
16 | private final Context mContext;
17 | private InputStream inputStream = null;
18 | private DataRaderListener listener;
19 | private final String filePath;
20 | private final AtomicBoolean isDataAcknowledged = new AtomicBoolean(false);
21 | private boolean isKeepRunning = false;
22 | private int fileByteSize = 0;
23 |
24 | FileStreamThread(Context mContext, String filePath, DataRaderListener listener) {
25 | this.mContext = mContext;
26 | this.filePath = filePath;
27 | this.listener = listener;
28 | }
29 |
30 | int getFileByteSize(){
31 | if (fileByteSize == 0 || inputStream == null) {
32 | initStream();
33 | }
34 | return fileByteSize;
35 | }
36 |
37 | @Override
38 | public void run() {
39 | try {
40 | prepareData();
41 | } catch (IOException e) {
42 | e.printStackTrace();
43 | }
44 | }
45 |
46 | private void prepareData() throws IOException {
47 | initStream();
48 | //1024 修改为 n
49 | byte[] block = new byte[YModem.mSize];
50 | int dataLength;
51 | byte blockSequence = 1;//The data package of a file is actually started from 1 文件的数据包实际上是从1开始的。
52 | isDataAcknowledged.set(true);
53 | isKeepRunning = true;
54 | while (isKeepRunning) {
55 |
56 | if (!isDataAcknowledged.get()) {
57 | try {
58 | //We need to sleep for a while as the sending 1024 bytes data from ble would take several seconds
59 | //In my circumstances, this can be up to 3 seconds.
60 | ////我们需要睡眠一段时间,因为从BLE发送1024字节数据需要几秒钟。
61 | //在我的情况下,这可以长达3秒。
62 | Thread.sleep(100);
63 | } catch (InterruptedException e) {
64 | e.printStackTrace();
65 | }
66 | continue;
67 | }
68 |
69 | if ((dataLength = inputStream.read(block)) == -1) {
70 | Lg.f("The file data has all been read...");
71 | if (listener != null) {
72 | onStop();
73 | listener.onFinish();
74 | }
75 | break;
76 | }
77 |
78 | byte[] pack = YModemUtil.getDataPackage(block, dataLength, blockSequence);
79 |
80 | if (listener != null) {
81 | listener.onDataReady(pack);
82 | }
83 |
84 | blockSequence++;
85 | isDataAcknowledged.set(false);
86 | }
87 |
88 | }
89 |
90 | /**
91 | * When received response from the terminal ,we should keep the thread keep going
92 | */
93 | void keepReading() {
94 | isDataAcknowledged.set(true);
95 | }
96 |
97 | void release() {
98 | onStop();
99 | listener = null;
100 | }
101 |
102 | private void onStop() {
103 | isKeepRunning = false;
104 | isDataAcknowledged.set(false);
105 | fileByteSize = 0;
106 | onReadFinished();
107 | }
108 |
109 | private void initStream() {
110 | if (inputStream == null) {
111 | try {
112 | inputStream = YModemUtil.getInputStream(mContext, filePath);
113 | fileByteSize = inputStream.available();
114 | } catch (IOException e) {
115 | e.printStackTrace();
116 | }
117 | }
118 | }
119 |
120 | private void onReadFinished() {
121 | if (inputStream != null) {
122 | try {
123 | inputStream.close();
124 | inputStream = null;
125 | } catch (IOException e) {
126 | e.printStackTrace();
127 | }
128 | }
129 | }
130 |
131 | public interface DataRaderListener {
132 | void onDataReady(byte[] data);
133 | void onFinish();
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/InputStreamSource.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | import android.content.Context;
4 | import java.io.BufferedInputStream;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 |
9 | /**
10 | * Get InputStream from different source, files from sd card/assets supported.
11 | */
12 |
13 | class InputStreamSource {
14 | //文件容量大小改为 32*n
15 | private static final int BUFFER_SIZE = 32 * YModem.mSize;
16 | //private static final String ERROR_UNSUPPORTED_SCHEME = "Unsupported file source";
17 |
18 | InputStream getStream(Context context, String imageUri) throws IOException {
19 | switch (SourceScheme.ofUri(imageUri)) {
20 | case FILE:
21 | return getStreamFromFile(imageUri);
22 |
23 | case ASSETS:
24 | return getStreamFromAssets(context, imageUri);
25 |
26 | case UNKNOWN:
27 | default:
28 | return getStreamFromOtherSource(imageUri);
29 | }
30 | }
31 |
32 | private InputStream getStreamFromFile(String fileUri) throws IOException {
33 | String filePath = SourceScheme.FILE.crop(fileUri);
34 | return new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE);
35 | }
36 |
37 | private InputStream getStreamFromAssets(Context context, String fileUri) throws IOException {
38 | String filePath = SourceScheme.ASSETS.crop(fileUri);
39 | return context.getAssets().open(filePath);
40 | }
41 |
42 | /**
43 | * 从其它的地方获取数据
44 | */
45 | private InputStream getStreamFromOtherSource(String fileUri) throws IOException{
46 | return new BufferedInputStream(new FileInputStream(fileUri), BUFFER_SIZE);
47 | //throw new UnsupportedOperationException(String.format(ERROR_UNSUPPORTED_SCHEME, fileUri));
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/Lg.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | import android.util.Log;
4 |
5 | public class Lg{
6 |
7 | private static final String TAG = "YMODEM";
8 |
9 | private Lg() {
10 | /* cannot be instantiated */
11 | throw new UnsupportedOperationException("cannot be instantiated");
12 | }
13 |
14 | private static final boolean DEBUGGING = true;
15 |
16 | public static void f(String msg) {
17 | if (DEBUGGING) {
18 | Log.e(TAG, msg);
19 | }
20 | }
21 |
22 | // 下面四个是默认tag的函数
23 | public static void i(String msg) {
24 | if (DEBUGGING)
25 | Log.i(TAG, msg);
26 | }
27 |
28 | public static void d(String msg) {
29 | if (DEBUGGING)
30 | Log.d(TAG, msg);
31 | }
32 |
33 | public static void e(String msg) {
34 | if (DEBUGGING)
35 | Lg.e(TAG, msg);
36 | }
37 |
38 | public static void v(String msg) {
39 | if (DEBUGGING)
40 | Log.v(TAG, msg);
41 | }
42 |
43 | public static void w(String msg) {
44 | if (DEBUGGING)
45 | Log.w(TAG, msg);
46 | }
47 |
48 | // 下面是传入自定义tag的函数
49 | public static void i(String tag, String msg) {
50 | if (DEBUGGING)
51 | Log.i(tag, msg);
52 | }
53 |
54 | public static void d(String tag, String msg) {
55 | if (DEBUGGING)
56 | Log.d(tag, msg);
57 | }
58 |
59 | public static void e(String tag, String msg) {
60 | if (DEBUGGING)
61 | Log.e(tag, msg);
62 | }
63 |
64 | public static void v(String tag, String msg) {
65 | if (DEBUGGING)
66 | Log.v(tag, msg);
67 | }
68 |
69 | public static void w(String tag, String msg) {
70 | if (DEBUGGING)
71 | Lg.w(tag, msg);
72 | }
73 |
74 | public static void e(String tag, String msg, Throwable throwable) {
75 | if (DEBUGGING)
76 | Log.e(tag, msg, throwable);
77 | }
78 |
79 | public static void wtf(String tag, String msg) {
80 | if (DEBUGGING)
81 | Log.wtf(tag, msg);
82 | }
83 |
84 | }
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/SourceScheme.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | import java.util.Locale;
4 |
5 | /**
6 | * create by ardWang
7 | */
8 | public enum SourceScheme {
9 |
10 | FILE("file"), ASSETS("assets"), UNKNOWN("");
11 |
12 | private final String scheme;
13 | private final String uriPrefix;
14 |
15 | SourceScheme(String scheme) {
16 | this.scheme = scheme;
17 | uriPrefix = scheme + "://";
18 | }
19 |
20 | /**
21 | * Defines scheme of incoming URI
22 | *
23 | * @param uri URI for scheme detection
24 | * @return SourceScheme of incoming URI
25 | */
26 | public static SourceScheme ofUri(String uri) {
27 | if (uri != null) {
28 | for (SourceScheme s : values()) {
29 | if (s.belongsTo(uri)) {
30 | return s;
31 | }
32 | }
33 | }
34 | return UNKNOWN;
35 | }
36 |
37 | private boolean belongsTo(String uri) {
38 | return uri.toLowerCase(Locale.US).startsWith(uriPrefix);
39 | }
40 |
41 | /**
42 | * Removed scheme part ("scheme://") from incoming URI
43 | */
44 | public String crop(String uri) {
45 | if (!belongsTo(uri)) {
46 | throw new IllegalArgumentException(String.format("URI [%1$s] doesn't have expected scheme [%2$s]", uri, scheme));
47 | }
48 | return uri.substring(uriPrefix.length());
49 | }
50 | }
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/TimeOutHelper.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | import android.os.Handler;
4 |
5 | /**
6 | * A timer util for counting the time past after we sent a package to the terminal
7 | */
8 |
9 | class TimeOutHelper {
10 |
11 | private ITimeOut listener;
12 |
13 | private final Handler timeoutHandler = new Handler();
14 |
15 | private final Runnable timer = new Runnable() {
16 | @Override
17 | public void run() {
18 | stopTimer();
19 | if (listener != null) {
20 | listener.onTimeOut();
21 | }
22 | }
23 | };
24 |
25 | void startTimer(ITimeOut timeoutListener, long delay) {
26 | listener = timeoutListener;
27 | timeoutHandler.postDelayed(timer, delay);
28 | }
29 |
30 | void stopTimer() {
31 | timeoutHandler.removeCallbacksAndMessages(null);
32 | }
33 |
34 | void unRegisterListener() {
35 | listener = null;
36 | }
37 |
38 | public interface ITimeOut {
39 | void onTimeOut();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/YModem.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | import android.content.Context;
4 | import java.io.IOException;
5 |
6 | /**
7 | * ========================================================================================
8 | * THE YMODEM:
9 | * Send 0x05>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>* 发送0x05
10 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
11 | * SOH 00 FF "foo.c" "1064'' NUL[118] CRC CRC >>>>>>>>>>>>>
12 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
13 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
14 | * STX 01 FE data[n] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>>
15 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
16 | * ACK STX 02 FD data[n] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
17 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
18 | * ACK STX 03 FC data[n] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
19 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
20 | * STX 04 FB data[n] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
21 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
22 | * SOH 05 FA data[100] 1A[28] CRC CRC>>>>>>>>>>>>>>>>>>
23 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
24 | * EOT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
25 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< NAK
26 | * EOT>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
27 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
28 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
29 | * SOH 00 FF NUL[128] CRC CRC >>>>>>>>>>>>>>>>>>>>>>>
30 | * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
31 | * ===========================================================================================
32 | *
33 | * 传输协议 编辑 ArdWang
34 | * 于 2018/6/5 15:49完成
35 | *
36 | * version v2.0.0
37 | * version v2.0.1
38 | *
39 | * version v2.0.2
40 | *
41 | */
42 |
43 | public class YModem implements FileStreamThread.DataRaderListener {
44 |
45 | private static final int STEP_HELLO = 0x00;
46 | private static final int STEP_FILE_NAME = 0x01;
47 | private static final int STEP_FILE_BODY = 0x02;
48 | private static final int STEP_EOT = 0x03;
49 | private static final int STEP_END = 0x04;
50 | private static int CURR_STEP = STEP_HELLO;
51 |
52 | private static final byte ACK = 0x06; /* ACKnowlege */
53 | private static final byte NAK = 0x15; /* Negative AcKnowlege */
54 | private static final byte CAN = 0x18; /* CANcel character */
55 | private static final byte ST_C = 'C';
56 | private static final String MD5_OK = "MD5_OK";
57 | private static final String MD5_ERR = "MD5_ERR";
58 |
59 | private final Context mContext;
60 | private final String filePath;
61 | private final String fileNameString;
62 | private final String fileMd5String;
63 | private final YModemListener listener;
64 |
65 | private final TimeOutHelper timerHelper = new TimeOutHelper();
66 | private FileStreamThread streamThread;
67 |
68 | //bytes has been sent of this transmission
69 | private int bytesSent = 0;
70 | //package data of current sending, used for int case of fail
71 | private byte[] currSending = null;
72 | private int packageErrorTimes = 0;
73 | private static final int MAX_PACKAGE_SEND_ERROR_TIMES = 6;
74 | //the timeout interval for a single package
75 | private static final int PACKAGE_TIME_OUT = 6000;
76 | static Integer mSize = 1024;
77 |
78 | /**
79 | * Construct of the YModemBLE,you may don't need the fileMD5 checking,remove it
80 | * YMODESMLE的构造,您可能不需要FLIMD5检查,删除它
81 | *
82 | * @param filePath absolute path of the file
83 | * @param fileNameString file name for sending to the terminal
84 | * @param fileMd5String md5 for terminal checking after transmission finished 传输结束后的终端检查MD5
85 | */
86 | private YModem(Context context, String filePath,
87 | String fileNameString, String fileMd5String,Integer size,
88 | YModemListener listener) {
89 | this.filePath = filePath;
90 | this.fileNameString = fileNameString;
91 | this.fileMd5String = fileMd5String;
92 | if(size==0) {
93 | size = 1024;
94 | }
95 | mSize = size;
96 | this.mContext = context;
97 | this.listener = listener;
98 | }
99 |
100 | /**
101 | * Start the transmission
102 | */
103 | public void start(String data) {
104 | sendData(data);
105 | }
106 |
107 | /**
108 | * Stop the transmission when you don't need it or shut it down in an accident
109 | * 停止传输当你不需要它或关闭它在一次事故
110 | */
111 | public void stop() {
112 | bytesSent = 0;
113 | currSending = null;
114 | packageErrorTimes = 0;
115 | if (streamThread != null) {
116 | streamThread.release();
117 | }
118 | timerHelper.stopTimer();
119 | timerHelper.unRegisterListener();
120 | }
121 |
122 | /**
123 | * Method for the outer caller when received data from the terminal
124 | * 接收来自终端的数据时外部呼叫者的方法
125 | */
126 | public void onReceiveData(byte[] respData) {
127 | //Stop the package timer
128 | timerHelper.stopTimer();
129 | if (respData != null && respData.length > 0) {
130 | Lg.f("YModem received " + respData.length + " bytes.");
131 | switch (CURR_STEP) {
132 | case STEP_HELLO:
133 | handleData(respData);
134 | break;
135 | case STEP_FILE_NAME:
136 | handleFileName(respData);
137 | break;
138 | case STEP_FILE_BODY:
139 | handleFileBody(respData);
140 | break;
141 | case STEP_EOT:
142 | handleEOT(respData);
143 | break;
144 | case STEP_END:
145 | handleEnd(respData);
146 | break;
147 | default:
148 | break;
149 | }
150 | } else {
151 | Lg.f("The terminal do responsed something, but received nothing??");
152 | }
153 | }
154 |
155 | /**
156 | * ==============================================================================
157 | * Methods for sending data begin
158 | *
159 | * 此方法更改如果没有第一包标注位就不需要发送数据
160 | * =》直接发送FileName
161 | *
162 | * ==============================================================================
163 | *
164 | */
165 | private void sendData(String data) {
166 | streamThread = new FileStreamThread(mContext, filePath, this);
167 | if(data != null) {
168 | CURR_STEP = STEP_HELLO;
169 | Lg.f("StartData!!!");
170 | byte[] hello = YModemUtil.getYModelData(data);
171 | sendPackageData(hello);
172 | }else{
173 | packageErrorTimes = 0;
174 | sendFileName();
175 | }
176 | }
177 |
178 | private void sendFileName() {
179 | CURR_STEP = STEP_FILE_NAME;
180 | Lg.f("sendFileName");
181 | try {
182 | int fileByteSize = streamThread.getFileByteSize();
183 | byte[] fileNamePackage = YModemUtil.getFileNamePackage(fileNameString, fileByteSize
184 | , fileMd5String);
185 | sendPackageData(fileNamePackage);
186 | } catch (IOException e) {
187 | e.printStackTrace();
188 | }
189 | }
190 |
191 | private void startSendFileData() {
192 | CURR_STEP = STEP_FILE_BODY;
193 | Lg.f("startSendFileData");
194 | streamThread.start();
195 | }
196 |
197 | //Callback from the data reading thread when a data package is ready
198 | @Override
199 | public void onDataReady(byte[] data) {
200 | sendPackageData(data);
201 | }
202 |
203 | private void sendEOT() {
204 | CURR_STEP = STEP_EOT;
205 | Lg.f("sendEOT");
206 | if (listener != null) {
207 | listener.onDataReady(YModemUtil.getEOT());
208 | }
209 | }
210 |
211 | private void sendEND() {
212 | CURR_STEP = STEP_END;
213 | Lg.f("sendEND");
214 | if (listener != null) {
215 | try {
216 | listener.onDataReady(YModemUtil.getEnd());
217 | } catch (IOException e) {
218 | e.printStackTrace();
219 | }
220 | }
221 | }
222 |
223 | private void sendPackageData(byte[] packageData) {
224 | if (listener != null && packageData != null) {
225 | currSending = packageData;
226 | //Start the timer, it will be cancelled when reponse received,
227 | // or trigger the timeout and resend the current package data
228 | //启动计时器,当收到回复时将被取消,
229 | //或触发超时并重新发送当前包数据
230 | timerHelper.startTimer(timeoutListener, PACKAGE_TIME_OUT);
231 | listener.onDataReady(packageData);
232 | }
233 | }
234 |
235 | /**
236 | * ==============================================================================
237 | * Method for handling the response of a package
238 | * ==============================================================================
239 | */
240 | private void handleData(byte[] value) {
241 | int character = value[0];
242 | if (character == ST_C) {//Receive "C" for "HELLO"
243 | Lg.f("Received 'C'");
244 | packageErrorTimes = 0;
245 | sendFileName();
246 | } else {
247 | handleOthers(character);
248 | }
249 | }
250 |
251 | //The file name package was responsed
252 | private void handleFileName(byte[] value) {
253 | if (value.length == 2 && value[0] == ACK && value[1] == ST_C) {//Receive 'ACK C' for file name
254 | Lg.f("Received 'ACK C'");
255 | packageErrorTimes = 0;
256 | startSendFileData();
257 | } else if (value[0] == ST_C) {//Receive 'C' for file name, this package should be resent
258 | Lg.f("Received 'C'");
259 | handlePackageFail("Received 'C' without 'ACK' after sent file name");
260 | } else {
261 | handleOthers(value[0]);
262 | }
263 | }
264 |
265 | private void handleFileBody(byte[] value) {
266 | if (value.length == 1 && value[0] == ACK) {//Receive ACK for file data
267 | Lg.f("Received 'ACK'");
268 | packageErrorTimes = 0;
269 | bytesSent += currSending.length;
270 | try {
271 | if (listener != null) {
272 | listener.onProgress(bytesSent, streamThread.getFileByteSize());
273 | }
274 | } catch (Exception e) {
275 | e.printStackTrace();
276 | }
277 | streamThread.keepReading();
278 |
279 | } else if (value.length == 1 && value[0] == ST_C) {
280 | Lg.f("Received 'C'");
281 | //Receive C for file data, the ymodem cannot handle this circumstance, transmission failed...
282 | handlePackageFail("Received 'C' after sent file data");
283 | } else {
284 | handleOthers(value[0]);
285 | }
286 | }
287 |
288 | private void handleEOT(byte[] value) {
289 | if (value[0] == ACK) {
290 | Lg.f("Received 'ACK'");
291 | packageErrorTimes = 0;
292 | sendEND();
293 | } else if (value[0] == ST_C) {//As we haven't received ACK, we should resend EOT
294 | handlePackageFail("Received 'C' after sent EOT");
295 | } else if(value[0]==NAK){ //如果是NAK的话 再次发送一次EOT数据
296 | sendEOT();
297 | }else{
298 | handleOthers(value[0]);
299 | }
300 | }
301 |
302 | private void handleEnd(byte[] character) {
303 | if (character[0] == ACK) {//The last ACK represents that the transmission has been finished, but we should validate the file
304 | Lg.f("Received 'ACK'");
305 | packageErrorTimes = 0;
306 | //发送已经成功,完全结束
307 | if (listener != null) {
308 | listener.onSuccess();
309 | }
310 | } else if ((new String(character)).equals(MD5_OK)) {//The file data has been checked,Well Done!
311 | Lg.f("Received 'MD5_OK'");
312 | stop();
313 | if (listener != null) {
314 | listener.onSuccess();
315 | }
316 | } else if ((new String(character)).equals(MD5_ERR)) {//Oops...Transmission Failed...
317 | Lg.f("Received 'MD5_ERR'");
318 | stop();
319 | if (listener != null) {
320 | listener.onFailed("MD5 check failed!!!");
321 | }
322 | } else {
323 | handleOthers(character[0]);
324 | }
325 | }
326 |
327 | private void handleOthers(int character) {
328 | if (character == NAK) {//We need to resend this package as the terminal failed when checking the crc
329 | Lg.f("Received 'NAK'");
330 | handlePackageFail("Received NAK");
331 | } else if (character == CAN) {//Some big problem occurred, transmission failed...
332 | Lg.f("Received 'CAN'");
333 | if (listener != null) {
334 | listener.onFailed("Received CAN");
335 | }
336 | stop();
337 | }
338 | }
339 |
340 | //Handle a failed package data ,resend it up to MAX_PACKAGE_SEND_ERROR_TIMES times.
341 | //处理失败的包数据
342 | //If still failed, then the transmission failed.
343 | private void handlePackageFail(String reason) {
344 | packageErrorTimes++;
345 | Lg.f("Fail:" + reason + " for " + packageErrorTimes + " times");
346 | if (packageErrorTimes < MAX_PACKAGE_SEND_ERROR_TIMES) {
347 | sendPackageData(currSending);
348 | } else {
349 | //Still, we stop the transmission, release the resources
350 | stop();
351 | if (listener != null) {
352 | listener.onFailed(reason);
353 | }
354 | }
355 | }
356 |
357 | /* The InputStream data reading thread was done */
358 | @Override
359 | public void onFinish() {
360 | sendEOT();
361 | }
362 |
363 | //The timeout listener
364 | private final TimeOutHelper.ITimeOut timeoutListener = new TimeOutHelper.ITimeOut() {
365 | @Override
366 | public void onTimeOut() {
367 | Lg.f("------ time out ------");
368 | if (currSending != null) {
369 | handlePackageFail("package timeout...");
370 | }
371 | }
372 | };
373 |
374 | public static class Builder {
375 | private Context context;
376 | private String filePath;
377 | private String fileNameString;
378 | private String fileMd5String;
379 | private Integer size;
380 | private YModemListener listener;
381 |
382 | public Builder with(Context context) {
383 | this.context = context;
384 | return this;
385 | }
386 |
387 | public Builder filePath(String filePath) {
388 | this.filePath = filePath;
389 | return this;
390 | }
391 |
392 | public Builder fileName(String fileName) {
393 | this.fileNameString = fileName;
394 | return this;
395 | }
396 |
397 | public Builder sendSize(Integer size){
398 | this.size = size;
399 | return this;
400 | }
401 |
402 | public Builder checkMd5(String fileMd5String) {
403 | this.fileMd5String = fileMd5String;
404 | return this;
405 | }
406 |
407 | public Builder callback(YModemListener listener) {
408 | this.listener = listener;
409 | return this;
410 | }
411 |
412 | public YModem build() {
413 | return new YModem(context, filePath, fileNameString, fileMd5String, size, listener);
414 | }
415 |
416 | }
417 |
418 | }
419 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/YModemListener.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | /**
4 | * Listener of the transmission process
5 | */
6 | public interface YModemListener {
7 |
8 | /* the data package has been encapsulated */
9 | void onDataReady(byte[] data);
10 |
11 | /*just the file data progress*/
12 | void onProgress(int currentSent, int total);
13 |
14 | /* the file has been correctly sent to the terminal */
15 | void onSuccess();
16 |
17 | /* the task has failed with several remedial measures like retrying some times*/
18 | void onFailed(String reason);
19 |
20 | }
--------------------------------------------------------------------------------
/YModemLibrary/src/main/java/com/bw/yml/YModemUtil.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
2 |
3 | import android.content.Context;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.DataOutputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.util.Arrays;
9 |
10 | /**
11 | * Util for encapsulating data package of ymodem protocol
12 | *
13 | * Created by leonxtp on 2017/9/16.
14 | * Modified by rnd on 2019/10/28
15 | */
16 |
17 | class YModemUtil {
18 |
19 | /*This is my concrete ymodem start signal, customise it to your needs*/
20 | //private static final String Data = "Data BOOTLOADER";
21 | private static final byte SOH = 0x01; /* Start Of Header with data size :128*/
22 | private static final byte STX = 0x02; /* Start Of Header with data size : 1024*/
23 | private static final byte EOT = 0x04; /* End Of Transmission */
24 | private static final byte CPMEOF = 0x1A;/* Fill the last package if not long enough */
25 | private static final CRC16 crc16 = new CRC16();
26 | //private static byte[] mInitBytes;
27 |
28 | /*
29 | * Get the first package data for hello with a terminal
30 | */
31 | // static byte[] getYModelData() {
32 | // return Data.getBytes();
33 | // }
34 |
35 | /**
36 | * Get the first package data for hello with a terminal
37 | * 2024
38 | * 3/13更新 修改成动态的 调用采用
39 | * String customData = "Customized Data";
40 | * byte[] dataBytes = getYModelData(customData);
41 | */
42 | static byte[] getYModelData(String dynamicData) {
43 | return dynamicData.getBytes();
44 | }
45 |
46 |
47 | /**
48 | * Get the file name package data
49 | *
50 | * @param fileNameString file name in String
51 | * @param fileByteSize file byte size of int value
52 | * @param fileMd5String the md5 of the file in String
53 | *
54 | */
55 | static byte[] getFileNamePackage(String fileNameString,
56 | int fileByteSize,
57 | String fileMd5String) throws IOException {
58 |
59 | byte seperator = 0x0;
60 | String fileSize = fileByteSize + "";
61 | byte[] byteFileSize = fileSize.getBytes();
62 |
63 | byte[] fileNameBytes1 = concat(fileNameString.getBytes(),
64 | new byte[]{seperator},
65 | byteFileSize);
66 |
67 | byte[] fileNameBytes2;
68 | fileNameBytes2 = Arrays.copyOf(concat(fileNameBytes1,
69 | new byte[]{seperator},
70 | fileMd5String.getBytes()), 128);
71 |
72 | byte seq = 0x00;
73 | return getDataPackage(fileNameBytes2, 128, seq);
74 | }
75 |
76 | /**
77 | * Get a encapsulated package data block
78 | *
79 | * @param block byte data array
80 | * @param dataLength the actual content length in the block without 0 filled in it.
81 | * @param sequence the package serial number
82 | * @return a encapsulated package data block
83 | */
84 | static byte[] getDataPackage(byte[] block, int dataLength, byte sequence) throws IOException {
85 | // 选择合适的包头类型:SOH (128字节) 或 STX (1024字节)
86 | byte headerType = (block.length == 128) ? SOH : (block.length == 1024) ? STX : SOH;
87 | byte[] header = getDataHeader(sequence, headerType);
88 |
89 | // 填充剩余数据为 CPMEOF(如果数据不足)
90 | if (dataLength < block.length) {
91 | int startFil = dataLength;
92 | while (startFil < block.length) {
93 | block[startFil] = CPMEOF;
94 | startFil++;
95 | }
96 | }
97 |
98 | // 计算CRC校验
99 | short crc = (short) crc16.calcCRC(block);
100 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
101 | DataOutputStream dos = new DataOutputStream(baos);
102 | dos.writeShort(crc);
103 | dos.close();
104 | byte[] crcBytes = baos.toByteArray();
105 |
106 | // 返回完整的包(包头 + 数据 + CRC)
107 | return concat(header, block, crcBytes);
108 | }
109 |
110 |
111 | /**
112 | * Get the EOT package
113 | */
114 | static byte[] getEOT() {
115 | return new byte[]{EOT};
116 | }
117 |
118 | /**
119 | * Get the Last package
120 | */
121 | static byte[] getEnd() throws IOException {
122 | byte seq = 0x00;
123 | return getDataPackage(new byte[128], 128, seq);
124 | }
125 |
126 | /**
127 | * Get InputStream from Assets, you can customize it from the other sources
128 | *
129 | * @param fileAbsolutePath absolute path of the file in asstes
130 | */
131 | static InputStream getInputStream(Context context, String fileAbsolutePath) throws IOException {
132 | return new InputStreamSource().getStream(context, fileAbsolutePath);
133 | }
134 |
135 | private static byte[] getDataHeader(byte sequence, byte start) {
136 | //The serial number of the package increases Cyclically up to 256
137 | byte modSequence = (byte) (sequence % 0x256);
138 | byte complementSeq = (byte) ~modSequence;
139 |
140 | return concat(new byte[]{start},
141 | new byte[]{modSequence},
142 | new byte[]{complementSeq});
143 | }
144 |
145 | private static byte[] concat(byte[] a, byte[] b, byte[] c) {
146 | int aLen = a.length;
147 | int bLen = b.length;
148 | int cLen = c.length;
149 | byte[] concated = new byte[aLen + bLen + cLen];
150 | System.arraycopy(a, 0, concated, 0, aLen);
151 | System.arraycopy(b, 0, concated, aLen, bLen);
152 | System.arraycopy(c, 0, concated, aLen + bLen, cLen);
153 | return concated;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/YModemLibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | YModemLibrary
3 |
4 |
--------------------------------------------------------------------------------
/YModemLibrary/src/test/java/com/bw/yml/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.bw.yml;
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/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 33
5 | defaultConfig {
6 | applicationId "com.bw.ym"
7 | minSdkVersion 19
8 | targetSdkVersion 33
9 | versionCode 2
10 | versionName "1.0.3"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | compileOptions {
21 | targetCompatibility JavaVersion.VERSION_1_8
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'androidx.appcompat:appcompat:1.3.0'
28 | implementation 'com.android.support.constraint:constraint-layout:2.0.4'
29 | testImplementation 'junit:junit:4.13.2'
30 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
31 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
32 |
33 | api project(':YModemLibrary')
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/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/ym/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.bw.ym;
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.ym", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ym/demo/ConnectThread.java:
--------------------------------------------------------------------------------
1 | package com.bw.ym.demo;
2 |
3 | import android.bluetooth.BluetoothSocket;
4 | import android.util.Log;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.util.Arrays;
10 |
11 |
12 | /*
13 | * Copyright © 2018 Radiance Instruments Ltd. All rights reserved.
14 | * author ArdWang
15 | * email 278161009@qq.com
16 | * Created by ArdWang on 10/28/19.
17 | */
18 |
19 | public class ConnectThread extends Thread{
20 | private static final String TAG = "ConnectedThread";
21 | private BluetoothSocket mmSocket;
22 | private InputStream mmInStream;
23 | private OutputStream mmOutStream;
24 | //是否是主动断开
25 | private boolean isStop = false;
26 | //发起蓝牙连接的线程
27 | private ConnectThread connectThread;
28 |
29 | public void terminalClose(ConnectThread connectThread){
30 | isStop = true;
31 | this.connectThread = connectThread;
32 | }
33 |
34 | public ConnectThread(BluetoothSocket socket){
35 | mmSocket = socket;
36 |
37 | InputStream tmpIn = null;
38 | OutputStream tmpOut = null;
39 |
40 | //使用临时对象获取输入和输出流,因为成员流是静态类型
41 |
42 | //1、获取 InputStream 和 OutputStream
43 | try {
44 | tmpIn = socket.getInputStream();
45 | tmpOut = socket.getOutputStream();
46 |
47 | } catch (IOException e) {
48 | Log.e(TAG,"ConnectedThread-->获取InputStream 和 OutputStream异常!");
49 | }
50 |
51 | mmInStream = tmpIn;
52 | mmOutStream = tmpOut;
53 |
54 | if(mmInStream != null){
55 | Log.d(TAG,"ConnectedThread-->已获取InputStream");
56 | }
57 |
58 | if(mmOutStream != null){
59 | Log.d(TAG,"ConnectedThread-->已获取OutputStream");
60 | }
61 |
62 | }
63 |
64 | public void run(){
65 | //最大缓存区 存放流
66 | byte[] buffer = new byte[1024 * 2]; //buffer store for the stream
67 | //从流的read()方法中读取的字节数
68 | int bytes = 0; //bytes returned from read()
69 | //持续监听输入流直到发生异常
70 | while(!isStop){
71 | try {
72 | if(mmInStream == null){
73 | Log.e(TAG,"ConnectedThread:run-->输入流mmInStream == null");
74 | break;
75 | }
76 | //先判断是否有数据,有数据再读取
77 | if(mmInStream.available() != 0){
78 | //2、接收数据
79 | bytes = mmInStream.read(buffer); //从(mmInStream)输入流中(读取内容)读取的一定数量字节数,并将它们存储到缓冲区buffer数组中,bytes为实际读取的字节数
80 | byte[] b = Arrays.copyOf(buffer,bytes); //存放实际读取的数据内容
81 | Log.w(TAG,"ConnectedThread:run-->收到消息,长度" + b.length + "->" + bytes2HexString(b, b.length)); //有空格的16进制字符串
82 | if(onSendReceiveDataListener != null){
83 | onSendReceiveDataListener.onReceiveDataSuccess(b); //成功收到消息
84 | }
85 | }
86 |
87 | } catch (IOException e) {
88 | Log.e(TAG,"ConnectedThread:run-->接收消息异常!" + e.getMessage());
89 | if(onSendReceiveDataListener != null){
90 | onSendReceiveDataListener.onReceiveDataError("接收消息异常:" + e.getMessage()); //接收消息异常
91 | }
92 | //关闭流和socket
93 | boolean isClose = cancel();
94 | if(isClose){
95 | Log.e(TAG,"ConnectedThread:run-->接收消息异常,成功断开连接!");
96 | }
97 | break;
98 | }
99 | }
100 | //关闭流和socket
101 | boolean isClose = cancel();
102 | if(isClose){
103 | Log.d(TAG,"ConnectedThread:run-->接收消息结束,断开连接!");
104 | }
105 | }
106 |
107 | //发送数据
108 | public boolean write(byte[] bytes){
109 | try {
110 | if(mmOutStream == null){
111 | Log.e(TAG, "mmOutStream == null");
112 | return false;
113 | }
114 |
115 | //发送数据
116 | mmOutStream.write(bytes);
117 | Log.d(TAG, "写入成功:"+ bytes2HexString(bytes, bytes.length));
118 | if(onSendReceiveDataListener != null){
119 | onSendReceiveDataListener.onSendDataSuccess(bytes); //发送数据成功回调
120 | }
121 | return true;
122 |
123 | } catch (IOException e) {
124 | Log.e(TAG, "写入失败:"+ bytes2HexString(bytes, bytes.length));
125 | if(onSendReceiveDataListener != null){
126 | onSendReceiveDataListener.onSendDataError(bytes,"写入失败"); //发送数据失败回调
127 | }
128 | return false;
129 | }
130 | }
131 |
132 | /**
133 | * 释放
134 | * @return true 断开成功 false 断开失败
135 | */
136 | public boolean cancel(){
137 | try {
138 | if(mmInStream != null){
139 | mmInStream.close(); //关闭输入流
140 | }
141 | if(mmOutStream != null){
142 | mmOutStream.close(); //关闭输出流
143 | }
144 | if(mmSocket != null){
145 | mmSocket.close(); //关闭socket
146 | }
147 | if(connectThread != null){
148 | connectThread.cancel();
149 | }
150 |
151 | connectThread = null;
152 | mmInStream = null;
153 | mmOutStream = null;
154 | mmSocket = null;
155 |
156 | Log.w(TAG,"ConnectedThread:cancel-->成功断开连接");
157 | return true;
158 |
159 | } catch (IOException e) {
160 | // 任何一部分报错,都将强制关闭socket连接
161 | mmInStream = null;
162 | mmOutStream = null;
163 | mmSocket = null;
164 |
165 | Log.e(TAG, "ConnectedThread:cancel-->断开连接异常!" + e.getMessage());
166 | return false;
167 | }
168 | }
169 |
170 | /**
171 | * 字节数组-->16进制字符串
172 | * @param b 字节数组
173 | * @param length 字节数组长度
174 | * @return 16进制字符串 有空格类似“0A D5 CD 8F BD E5 F8”
175 | */
176 | public static String bytes2HexString(byte[] b, int length) {
177 | StringBuffer result = new StringBuffer();
178 | String hex;
179 | for (int i = 0; i < length; i++) {
180 | hex = Integer.toHexString(b[i] & 0xFF);
181 | if (hex.length() == 1) {
182 | hex = '0' + hex;
183 | }
184 | result.append(hex.toUpperCase()).append(" ");
185 | }
186 | return result.toString();
187 | }
188 |
189 | private OnSendReceiveDataListener onSendReceiveDataListener;
190 |
191 | public void setOnSendReceiveDataListener(OnSendReceiveDataListener onSendReceiveDataListener) {
192 | this.onSendReceiveDataListener = onSendReceiveDataListener;
193 | }
194 |
195 | //收发数据监听者
196 | public interface OnSendReceiveDataListener{
197 | void onSendDataSuccess(byte[] data); //发送数据结束
198 | void onSendDataError(byte[] data, String errorMsg); //发送数据出错
199 | void onReceiveDataSuccess(byte[] buffer); //接收到数据
200 | void onReceiveDataError(String errorMsg); //接收数据出错
201 | }
202 |
203 | }
204 |
--------------------------------------------------------------------------------
/app/src/main/java/com/bw/ym/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.bw.ym.demo;
2 |
3 | import android.bluetooth.BluetoothAdapter;
4 | import android.bluetooth.BluetoothSocket;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.widget.Toast;
9 |
10 | import androidx.appcompat.app.AppCompatActivity;
11 |
12 | import com.bw.ym.R;
13 | import com.bw.yml.YModem;
14 | import com.bw.yml.YModemListener;
15 |
16 | /**
17 | * 使用介绍
18 | * 版本 v2.0.0->
19 | * 如果你想使用请你配合你的蓝牙一起使用
20 | * 具体操操作 有相对应的Demo
21 | * 这里只是一个实列 这是代表经典蓝牙
22 | * 本demo只能实现怎么发送数据的过程 具体经典蓝牙连接socket需要去查看文档
23 | */
24 |
25 | public class MainActivity extends AppCompatActivity implements ConnectThread.OnSendReceiveDataListener {
26 |
27 | private YModem yModem;
28 |
29 | private ConnectThread thread;
30 |
31 | private BluetoothAdapter bluetoothAdapter;
32 |
33 | private final static String TAG = "MainActivity";
34 |
35 | private BluetoothSocket bluetoothSocket;
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_main);
41 | initData();
42 | }
43 |
44 | private void initData(){
45 | initBluetooth();
46 | if(bluetoothSocket!=null)
47 | thread = new ConnectThread(bluetoothSocket);
48 | }
49 |
50 | private void initBluetooth() {
51 | bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
52 | if(bluetoothAdapter == null){
53 | Toast.makeText(this, "当前手机设备不支持蓝牙", Toast.LENGTH_SHORT).show();
54 | }else{
55 | //手机设备支持蓝牙,判断蓝牙是否已开启
56 | if(bluetoothAdapter.isEnabled()){
57 | Toast.makeText(this, "手机蓝牙已开启", Toast.LENGTH_SHORT).show();
58 | }else{
59 | //蓝牙没有打开,去打开蓝牙。推荐使用第二种打开蓝牙方式
60 | //第一种方式:直接打开手机蓝牙,没有任何提示
61 | // bluetoothAdapter.enable(); //BLUETOOTH_ADMIN权限
62 | //第二种方式:友好提示用户打开蓝牙
63 | Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
64 | startActivity(enableBtIntent);
65 | }
66 | }
67 | }
68 |
69 | private void searchBtDevice() {
70 | if(bluetoothAdapter.isDiscovering()){ //当前正在搜索设备...
71 | return;
72 | }
73 | //开始搜索
74 | bluetoothAdapter.startDiscovery();
75 | }
76 |
77 |
78 |
79 | /**
80 | * 开始Ymodel放在蓝牙点击按钮 启动的时候使用
81 | */
82 | private void startYmodem(){
83 | String customData = "Customized Data";
84 | yModem = new YModem.Builder()
85 | .with(this)
86 | .filePath("你的文件夹路径") //存放到手机的文件路径 stroge/0/.../xx.bin 这种路径
87 | .fileName("你的文件名字")
88 | .checkMd5("") //Md5可以写可以不写 看自己的通讯协议
89 | .sendSize(1024) //可以修改成你需要的大小
90 | .callback(new YModemListener() {
91 | @Override
92 | public void onDataReady(byte[] data) {
93 | thread.write(data);
94 | }
95 |
96 | @Override
97 | public void onProgress(int currentSent, int total) {
98 | //进度条处理
99 | }
100 |
101 | @Override
102 | public void onSuccess() {
103 | //成功的显示
104 | }
105 |
106 | @Override
107 | public void onFailed(String reason) {
108 |
109 | }
110 | }).build();
111 |
112 | // 默认为空
113 | yModem.start(null);
114 | // 有需要的时候再添加
115 | //yModem.start(customData);
116 | }
117 |
118 | //用于接受到你蓝牙设备给你反馈的蓝牙信息
119 | public void onDataReceivedFromBLE(byte[] data) {
120 | yModem.onReceiveData(data);
121 | }
122 |
123 | /*stop the transmission*/
124 | public void onStopClick(View view) {
125 | yModem.stop();
126 | }
127 |
128 |
129 | @Override
130 | protected void onDestroy() {
131 | super.onDestroy();
132 | yModem.stop();
133 |
134 | //你相应的一系列蓝牙的操作也要停止掉
135 | }
136 |
137 |
138 | @Override
139 | public void onSendDataSuccess(byte[] data) {
140 | //成功数据的打印
141 | }
142 |
143 | @Override
144 | public void onSendDataError(byte[] data, String errorMsg) {
145 |
146 | }
147 |
148 | @Override
149 | public void onReceiveDataSuccess(byte[] data) {
150 | if(data.length>0)
151 | yModem.onDataReady(data);
152 | }
153 |
154 | @Override
155 | public void onReceiveDataError(String errorMsg) {
156 |
157 | }
158 |
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/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/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/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
--------------------------------------------------------------------------------
/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/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BWYModem
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/bw/ym/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.bw.ym;
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 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | mavenCentral()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:4.1.0'
11 |
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | mavenCentral()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArdWang/YModemlib_Android/e1196c8e0a2e7817293960af619040e3e7846c1b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 06 08:54:03 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':YModemLibrary'
2 |
--------------------------------------------------------------------------------
/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | wjprogram@gmail.com
7 | 123456
8 | central
9 |
10 |
11 | wjprogram@gmail.com
12 | 123456
13 | snapshots
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | false
22 |
23 | central
24 | github-libs-release
25 | https://programs.jfrog.io/artifactory/github-libs-release
26 |
27 |
28 |
29 | snapshots
30 | github-libs-snapshot
31 | https://programs.jfrog.io/artifactory/github-libs-snapshot
32 |
33 |
34 |
35 |
36 |
37 | false
38 |
39 | central
40 | github-libs-release
41 | https://programs.jfrog.io/artifactory/github-libs-release
42 |
43 |
44 |
45 | snapshots
46 | github-libs-snapshot
47 | https://programs.jfrog.io/artifactory/github-libs-snapshot
48 |
49 |
50 | artifactory
51 |
52 |
53 |
54 | artifactory
55 |
56 |
57 |
58 | central
59 | a0p7vf5eepvwc-artifactory-primary-0-releases
60 | https://programs.jfrog.io/artifactory/github-libs-release
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------