├── .gitignore ├── .metadata ├── README.md ├── WiFiSmartConfig └── WiFiSmartConfig.ino ├── android ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ ├── espressif │ │ │ └── iot │ │ │ │ └── esptouch │ │ │ │ ├── EsptouchResult.java │ │ │ │ ├── EsptouchTask.java │ │ │ │ ├── IEsptouchListener.java │ │ │ │ ├── IEsptouchResult.java │ │ │ │ ├── IEsptouchTask.java │ │ │ │ ├── protocol │ │ │ │ ├── DataCode.java │ │ │ │ ├── DatumCode.java │ │ │ │ ├── EsptouchGenerator.java │ │ │ │ ├── GuideCode.java │ │ │ │ └── TouchData.java │ │ │ │ ├── task │ │ │ │ ├── EsptouchTaskParameter.java │ │ │ │ ├── ICodeData.java │ │ │ │ ├── IEsptouchGenerator.java │ │ │ │ ├── IEsptouchTaskParameter.java │ │ │ │ ├── __EsptouchTask.java │ │ │ │ └── __IEsptouchTask.java │ │ │ │ ├── udp │ │ │ │ ├── UDPSocketClient.java │ │ │ │ └── UDPSocketServer.java │ │ │ │ └── util │ │ │ │ ├── ByteUtil.java │ │ │ │ ├── CRC8.java │ │ │ │ ├── EspAES.java │ │ │ │ └── EspNetUtil.java │ │ │ └── example │ │ │ └── esptouchflutterexample │ │ │ └── MainActivity.java │ │ └── res │ │ ├── drawable │ │ └── launch_background.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── flutter_01.png ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── main.m ├── lib └── main.dart ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Esp8266 smartconfig (onetouch) flutter example 2 | 3 | This example demostrate how to use SmartConfig for ESP8266/ESP32 modules using a Flutter (dart) app 4 | 5 | Arduino code for SmartConfig is in WiFiSmartConifg folder. 6 | 7 | NOTE: Support only Android for now. Made for upcomig Sinric app. 8 | 9 | Based on ESP Touch v0.3.7.0 https://github.com/EspressifApp/EsptouchForAndroid 10 | 11 | With password encryption: 12 | https://github.com/kakopappa/flutter_smartconfig_with_aes_cbc_encryption 13 | 14 | 15 | ![How it looks like](https://github.com/kakopappa/esp8266_smartconfig_flutter_example/blob/master/flutter_01.png) 16 | -------------------------------------------------------------------------------- /WiFiSmartConfig/WiFiSmartConfig.ino: -------------------------------------------------------------------------------- 1 | // https://gist.github.com/anoochit/526765176805cf19bee7 2 | 3 | #include 4 | #include 5 | 6 | WiFiUDP Udp; 7 | 8 | void setup() { 9 | int cnt = 0; 10 | 11 | Serial.begin(115200); 12 | 13 | 14 | WiFi.disconnect(true); // remove wifi details stored already 15 | 16 | WiFi.mode(WIFI_STA); 17 | 18 | while(WiFi.status() != WL_CONNECTED) { 19 | delay(500); 20 | Serial.print("."); 21 | if(cnt++ >= 10){ 22 | WiFi.beginSmartConfig(); 23 | while(1){ 24 | delay(1000); 25 | if(WiFi.smartConfigDone()){ 26 | Serial.println("SmartConfig Success"); 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | 33 | Serial.println(""); 34 | Serial.println(""); 35 | 36 | WiFi.printDiag(Serial); 37 | 38 | // Start the server 39 | Udp.begin(49999); 40 | Serial.println("USP Server started"); 41 | 42 | // Print the IP address 43 | Serial.println(WiFi.localIP()); 44 | } 45 | 46 | void loop() { 47 | // Check if a client has connected 48 | Udp.parsePacket(); 49 | while(Udp.available()){ 50 | Serial.println(Udp.remoteIP()); 51 | Udp.flush(); 52 | delay(5); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.esptouchflutterexample" 37 | minSdkVersion 16 38 | targetSdkVersion 27 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | implementation 'com.android.support:appcompat-v7:27.1.1' 60 | implementation 'com.android.support:design:27.1.1' 61 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 62 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 24 | 31 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/EsptouchResult.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch; 2 | 3 | import java.net.InetAddress; 4 | import java.util.concurrent.atomic.AtomicBoolean; 5 | 6 | public class EsptouchResult implements IEsptouchResult { 7 | 8 | private final boolean mIsSuc; 9 | private final String mBssid; 10 | private final InetAddress mInetAddress; 11 | private AtomicBoolean mIsCancelled; 12 | 13 | /** 14 | * Constructor of EsptouchResult 15 | * 16 | * @param isSuc whether the esptouch task is executed suc 17 | * @param bssid the device's bssid 18 | * @param inetAddress the device's ip address 19 | */ 20 | public EsptouchResult(boolean isSuc, String bssid, InetAddress inetAddress) { 21 | this.mIsSuc = isSuc; 22 | this.mBssid = bssid; 23 | this.mInetAddress = inetAddress; 24 | this.mIsCancelled = new AtomicBoolean(false); 25 | } 26 | 27 | @Override 28 | public boolean isSuc() { 29 | return this.mIsSuc; 30 | } 31 | 32 | @Override 33 | public String getBssid() { 34 | return this.mBssid; 35 | } 36 | 37 | @Override 38 | public boolean isCancelled() { 39 | return mIsCancelled.get(); 40 | } 41 | 42 | public void setIsCancelled(boolean isCancelled) { 43 | this.mIsCancelled.set(isCancelled); 44 | } 45 | 46 | @Override 47 | public InetAddress getInetAddress() { 48 | return this.mInetAddress; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/EsptouchTask.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | 6 | import com.espressif.iot.esptouch.protocol.TouchData; 7 | import com.espressif.iot.esptouch.task.EsptouchTaskParameter; 8 | import com.espressif.iot.esptouch.task.__EsptouchTask; 9 | import com.espressif.iot.esptouch.util.ByteUtil; 10 | import com.espressif.iot.esptouch.util.EspAES; 11 | import com.espressif.iot.esptouch.util.EspNetUtil; 12 | 13 | import java.util.List; 14 | 15 | public class EsptouchTask implements IEsptouchTask { 16 | public __EsptouchTask _mEsptouchTask; 17 | private EsptouchTaskParameter _mParameter; 18 | 19 | /** 20 | * Constructor of EsptouchTask 21 | * 22 | * @param apSsid the Ap's ssid 23 | * @param apBssid the Ap's bssid 24 | * @param apPassword the Ap's password 25 | * @param context the Context of the Application 26 | */ 27 | public EsptouchTask(String apSsid, String apBssid, String apPassword, Context context) { 28 | this(apSsid, apBssid, apPassword, null, context); 29 | } 30 | 31 | /** 32 | * Constructor of EsptouchTask 33 | * 34 | * @param apSsid the Ap's ssid 35 | * @param apBssid the Ap's bssid 36 | * @param apPassword the Ap's password 37 | * @param espAES AES secret key 38 | * @param context the Context of the Application 39 | */ 40 | public EsptouchTask(String apSsid, String apBssid, String apPassword, EspAES espAES, Context context) { 41 | if (TextUtils.isEmpty(apSsid)) { 42 | throw new NullPointerException("SSID can't be empty"); 43 | } 44 | if (TextUtils.isEmpty(apBssid)) { 45 | throw new NullPointerException("BSSID can't be empty"); 46 | } 47 | if (apPassword == null) { 48 | apPassword = ""; 49 | } 50 | TouchData ssid = new TouchData(apSsid); 51 | TouchData bssid = new TouchData(EspNetUtil.parseBssid2bytes(apBssid)); 52 | TouchData password = new TouchData(apPassword); 53 | init(context, ssid, bssid, password, espAES); 54 | } 55 | 56 | public EsptouchTask(byte[] apSsid, byte[] apBssid, byte[] apPassword, Context context) { 57 | this(apSsid, apBssid, apPassword, null, context); 58 | } 59 | 60 | public EsptouchTask(byte[] apSsid, byte[] apBssid, byte[] apPassword, EspAES espAES, Context context) { 61 | if (apSsid == null || apSsid.length == 0) { 62 | throw new NullPointerException("SSID can't be empty"); 63 | } 64 | if (apBssid == null || apBssid.length == 0) { 65 | throw new NullPointerException("BSSID can't be empty"); 66 | } 67 | if (apPassword == null) { 68 | apPassword = new byte[0]; 69 | } 70 | TouchData ssid = new TouchData(apSsid); 71 | TouchData bssid = new TouchData(apBssid); 72 | TouchData password = new TouchData(apPassword); 73 | init(context, ssid, bssid, password, espAES); 74 | } 75 | 76 | private void init(Context context, TouchData ssid, TouchData bssid, TouchData password, EspAES aes) { 77 | _mParameter = new EsptouchTaskParameter(); 78 | _mEsptouchTask = new __EsptouchTask(context, ssid, bssid, password, aes, _mParameter, true); 79 | } 80 | 81 | @Override 82 | public void interrupt() { 83 | _mEsptouchTask.interrupt(); 84 | } 85 | 86 | @Override 87 | public IEsptouchResult executeForResult() throws RuntimeException { 88 | return _mEsptouchTask.executeForResult(); 89 | } 90 | 91 | @Override 92 | public boolean isCancelled() { 93 | return _mEsptouchTask.isCancelled(); 94 | } 95 | 96 | @Override 97 | public List executeForResults(int expectTaskResultCount) 98 | throws RuntimeException { 99 | if (expectTaskResultCount <= 0) { 100 | expectTaskResultCount = Integer.MAX_VALUE; 101 | } 102 | return _mEsptouchTask.executeForResults(expectTaskResultCount); 103 | } 104 | 105 | @Override 106 | public void setEsptouchListener(IEsptouchListener esptouchListener) { 107 | _mEsptouchTask.setEsptouchListener(esptouchListener); 108 | } 109 | 110 | @Override 111 | public void setPackageBroadcast(boolean broadcast) { 112 | _mParameter.setBroadcast(broadcast); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/IEsptouchListener.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch; 2 | 3 | public interface IEsptouchListener { 4 | /** 5 | * when new esptouch result is added, the listener will call 6 | * onEsptouchResultAdded callback 7 | * 8 | * @param result the Esptouch result 9 | */ 10 | void onEsptouchResultAdded(IEsptouchResult result); 11 | } 12 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/IEsptouchResult.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch; 2 | 3 | import java.net.InetAddress; 4 | 5 | public interface IEsptouchResult { 6 | 7 | /** 8 | * check whether the esptouch task is executed suc 9 | * 10 | * @return whether the esptouch task is executed suc 11 | */ 12 | boolean isSuc(); 13 | 14 | /** 15 | * get the device's bssid 16 | * 17 | * @return the device's bssid 18 | */ 19 | String getBssid(); 20 | 21 | /** 22 | * check whether the esptouch task is cancelled by user 23 | * 24 | * @return whether the esptouch task is cancelled by user 25 | */ 26 | boolean isCancelled(); 27 | 28 | /** 29 | * get the ip address of the device 30 | * 31 | * @return the ip device of the device 32 | */ 33 | InetAddress getInetAddress(); 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/IEsptouchTask.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch; 2 | 3 | import java.util.List; 4 | 5 | public interface IEsptouchTask { 6 | String ESPTOUCH_VERSION = "v0.3.7.0"; 7 | 8 | /** 9 | * set the esptouch listener, when one device is connected to the Ap, it will be called back 10 | * 11 | * @param esptouchListener when one device is connected to the Ap, it will be called back 12 | */ 13 | void setEsptouchListener(IEsptouchListener esptouchListener); 14 | 15 | /** 16 | * Interrupt the Esptouch Task when User tap back or close the Application. 17 | */ 18 | void interrupt(); 19 | 20 | /** 21 | * Note: !!!Don't call the task at UI Main Thread or RuntimeException will 22 | * be thrown Execute the Esptouch Task and return the result 23 | *

24 | * Smart Config v2.4 support the API 25 | * 26 | * @return the IEsptouchResult 27 | * @throws RuntimeException 28 | */ 29 | IEsptouchResult executeForResult() throws RuntimeException; 30 | 31 | /** 32 | * Note: !!!Don't call the task at UI Main Thread or RuntimeException will 33 | * be thrown Execute the Esptouch Task and return the result 34 | *

35 | * Smart Config v2.4 support the API 36 | *

37 | * It will be blocked until the client receive result count >= expectTaskResultCount. 38 | * If it fail, it will return one fail result will be returned in the list. 39 | * If it is cancelled while executing, 40 | * if it has received some results, all of them will be returned in the list. 41 | * if it hasn't received any results, one cancel result will be returned in the list. 42 | * 43 | * @param expectTaskResultCount the expect result count(if expectTaskResultCount <= 0, 44 | * expectTaskResultCount = Integer.MAX_VALUE) 45 | * @return the list of IEsptouchResult 46 | * @throws RuntimeException 47 | */ 48 | List executeForResults(int expectTaskResultCount) throws RuntimeException; 49 | 50 | /** 51 | * check whether the task is cancelled by user 52 | * 53 | * @return whether the task is cancelled by user 54 | */ 55 | boolean isCancelled(); 56 | 57 | /** 58 | * Set broadcast or multicast when post config info 59 | * 60 | * @param broadcast true is broadcast, false is multicast 61 | */ 62 | void setPackageBroadcast(boolean broadcast); 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/protocol/DataCode.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.protocol; 2 | 3 | import com.espressif.iot.esptouch.task.ICodeData; 4 | import com.espressif.iot.esptouch.util.ByteUtil; 5 | import com.espressif.iot.esptouch.util.CRC8; 6 | 7 | /** 8 | * one data format:(data code should have 2 to 65 data) 9 | *

10 | * control byte high 4 bits low 4 bits 11 | * 1st 9bits: 0x0 crc(high) data(high) 12 | * 2nd 9bits: 0x1 sequence header 13 | * 3rd 9bits: 0x0 crc(low) data(low) 14 | *

15 | * sequence header: 0,1,2,... 16 | * 17 | * @author afunx 18 | */ 19 | public class DataCode implements ICodeData { 20 | 21 | public static final int DATA_CODE_LEN = 6; 22 | 23 | private static final int INDEX_MAX = 127; 24 | 25 | private final byte mSeqHeader; 26 | private final byte mDataHigh; 27 | private final byte mDataLow; 28 | // the crc here means the crc of the data and sequence header be transformed 29 | // it is calculated by index and data to be transformed 30 | private final byte mCrcHigh; 31 | private final byte mCrcLow; 32 | 33 | /** 34 | * Constructor of DataCode 35 | * 36 | * @param u8 the character to be transformed 37 | * @param index the index of the char 38 | */ 39 | public DataCode(char u8, int index) { 40 | if (index > INDEX_MAX) { 41 | throw new RuntimeException("index > INDEX_MAX"); 42 | } 43 | byte[] dataBytes = ByteUtil.splitUint8To2bytes(u8); 44 | mDataHigh = dataBytes[0]; 45 | mDataLow = dataBytes[1]; 46 | CRC8 crc8 = new CRC8(); 47 | crc8.update(ByteUtil.convertUint8toByte(u8)); 48 | crc8.update(index); 49 | byte[] crcBytes = ByteUtil.splitUint8To2bytes((char) crc8.getValue()); 50 | mCrcHigh = crcBytes[0]; 51 | mCrcLow = crcBytes[1]; 52 | mSeqHeader = (byte) index; 53 | } 54 | 55 | @Override 56 | public byte[] getBytes() { 57 | byte[] dataBytes = new byte[DATA_CODE_LEN]; 58 | dataBytes[0] = 0x00; 59 | dataBytes[1] = ByteUtil.combine2bytesToOne(mCrcHigh, mDataHigh); 60 | dataBytes[2] = 0x01; 61 | dataBytes[3] = mSeqHeader; 62 | dataBytes[4] = 0x00; 63 | dataBytes[5] = ByteUtil.combine2bytesToOne(mCrcLow, mDataLow); 64 | return dataBytes; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | StringBuilder sb = new StringBuilder(); 70 | byte[] dataBytes = getBytes(); 71 | for (int i = 0; i < DATA_CODE_LEN; i++) { 72 | String hexString = ByteUtil.convertByte2HexString(dataBytes[i]); 73 | sb.append("0x"); 74 | if (hexString.length() == 1) { 75 | sb.append("0"); 76 | } 77 | sb.append(hexString).append(" "); 78 | } 79 | return sb.toString(); 80 | } 81 | 82 | @Override 83 | public char[] getU8s() { 84 | throw new RuntimeException("DataCode don't support getU8s()"); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/protocol/DatumCode.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.protocol; 2 | 3 | import com.espressif.iot.esptouch.task.ICodeData; 4 | import com.espressif.iot.esptouch.util.ByteUtil; 5 | import com.espressif.iot.esptouch.util.CRC8; 6 | 7 | import java.net.InetAddress; 8 | import java.util.LinkedList; 9 | 10 | public class DatumCode implements ICodeData { 11 | 12 | // define by the Esptouch protocol, all of the datum code should add 1 at last to prevent 0 13 | private static final int EXTRA_LEN = 40; 14 | private static final int EXTRA_HEAD_LEN = 5; 15 | 16 | private final LinkedList mDataCodes; 17 | 18 | /** 19 | * Constructor of DatumCode 20 | * 21 | * @param apSsid the Ap's ssid 22 | * @param apBssid the Ap's bssid 23 | * @param apPassword the Ap's password 24 | * @param ipAddress the ip address of the phone or pad 25 | * @param isSsidHiden whether the Ap's ssid is hidden 26 | */ 27 | public DatumCode(byte[] apSsid, byte[] apBssid, byte[] apPassword, 28 | InetAddress ipAddress, boolean isSsidHiden) { 29 | // Data = total len(1 byte) + apPwd len(1 byte) + SSID CRC(1 byte) + 30 | // BSSID CRC(1 byte) + TOTAL XOR(1 byte)+ ipAddress(4 byte) + apPwd + apSsid apPwdLen <= 31 | // 105 at the moment 32 | 33 | // total xor 34 | char totalXor = 0; 35 | 36 | char apPwdLen = (char) apPassword.length; 37 | CRC8 crc = new CRC8(); 38 | crc.update(apSsid); 39 | char apSsidCrc = (char) crc.getValue(); 40 | 41 | crc.reset(); 42 | crc.update(apBssid); 43 | char apBssidCrc = (char) crc.getValue(); 44 | 45 | char apSsidLen = (char) apSsid.length; 46 | // hostname parse 47 | String ipAddrStrs[] = ipAddress.getHostAddress().split("\\."); 48 | int ipLen = ipAddrStrs.length; 49 | 50 | char ipAddrChars[] = new char[ipLen]; 51 | // only support ipv4 at the moment 52 | for (int i = 0; i < ipLen; ++i) { 53 | ipAddrChars[i] = (char) Integer.parseInt(ipAddrStrs[i]); 54 | } 55 | 56 | char _totalLen = (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen + apSsidLen); 57 | char totalLen = isSsidHiden ? (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen + apSsidLen) 58 | : (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen); 59 | 60 | // build data codes 61 | mDataCodes = new LinkedList<>(); 62 | mDataCodes.add(new DataCode(_totalLen, 0)); 63 | totalXor ^= _totalLen; 64 | mDataCodes.add(new DataCode(apPwdLen, 1)); 65 | totalXor ^= apPwdLen; 66 | mDataCodes.add(new DataCode(apSsidCrc, 2)); 67 | totalXor ^= apSsidCrc; 68 | mDataCodes.add(new DataCode(apBssidCrc, 3)); 69 | totalXor ^= apBssidCrc; 70 | // ESPDataCode 4 is null 71 | for (int i = 0; i < ipLen; ++i) { 72 | mDataCodes.add(new DataCode(ipAddrChars[i], i + EXTRA_HEAD_LEN)); 73 | totalXor ^= ipAddrChars[i]; 74 | } 75 | 76 | byte[] apPwdBytes = apPassword; 77 | char[] apPwdChars = new char[apPwdBytes.length]; 78 | for (int i = 0; i < apPwdBytes.length; i++) { 79 | apPwdChars[i] = ByteUtil.convertByte2Uint8(apPwdBytes[i]); 80 | } 81 | for (int i = 0; i < apPwdChars.length; i++) { 82 | mDataCodes.add(new DataCode(apPwdChars[i], i + EXTRA_HEAD_LEN + ipLen)); 83 | totalXor ^= apPwdChars[i]; 84 | } 85 | 86 | byte[] apSsidBytes = apSsid; 87 | char[] apSsidChars = new char[apSsidBytes.length]; 88 | // totalXor will xor apSsidChars no matter whether the ssid is hidden 89 | for (int i = 0; i < apSsidBytes.length; i++) { 90 | apSsidChars[i] = ByteUtil.convertByte2Uint8(apSsidBytes[i]); 91 | totalXor ^= apSsidChars[i]; 92 | } 93 | if (isSsidHiden) { 94 | for (int i = 0; i < apSsidChars.length; i++) { 95 | mDataCodes.add(new DataCode(apSsidChars[i], i + EXTRA_HEAD_LEN + ipLen + apPwdLen)); 96 | } 97 | } 98 | 99 | // add total xor last 100 | mDataCodes.add(4, new DataCode(totalXor, 4)); 101 | 102 | // add bssid 103 | int bssidInsertIndex = EXTRA_HEAD_LEN; 104 | for (int i = 0; i < apBssid.length; i++) { 105 | int index = totalLen + i; 106 | char c = ByteUtil.convertByte2Uint8(apBssid[i]); 107 | DataCode dc = new DataCode(c, index); 108 | if (bssidInsertIndex >= mDataCodes.size()) { 109 | mDataCodes.add(dc); 110 | } else { 111 | mDataCodes.add(bssidInsertIndex, dc); 112 | } 113 | bssidInsertIndex += 4; 114 | } 115 | } 116 | 117 | @Override 118 | public byte[] getBytes() { 119 | byte[] datumCode = new byte[mDataCodes.size() * DataCode.DATA_CODE_LEN]; 120 | int index = 0; 121 | for (DataCode dc : mDataCodes) { 122 | for (byte b : dc.getBytes()) { 123 | datumCode[index++] = b; 124 | } 125 | } 126 | return datumCode; 127 | } 128 | 129 | @Override 130 | public String toString() { 131 | StringBuilder sb = new StringBuilder(); 132 | byte[] dataBytes = getBytes(); 133 | for (byte dataByte : dataBytes) { 134 | String hexString = ByteUtil.convertByte2HexString(dataByte); 135 | sb.append("0x"); 136 | if (hexString.length() == 1) { 137 | sb.append("0"); 138 | } 139 | sb.append(hexString).append(" "); 140 | } 141 | return sb.toString(); 142 | } 143 | 144 | @Override 145 | public char[] getU8s() { 146 | byte[] dataBytes = getBytes(); 147 | int len = dataBytes.length / 2; 148 | char[] dataU8s = new char[len]; 149 | byte high, low; 150 | for (int i = 0; i < len; i++) { 151 | high = dataBytes[i * 2]; 152 | low = dataBytes[i * 2 + 1]; 153 | dataU8s[i] = (char) (ByteUtil.combine2bytesToU16(high, low) + EXTRA_LEN); 154 | } 155 | return dataU8s; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/protocol/EsptouchGenerator.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.protocol; 2 | 3 | import com.espressif.iot.esptouch.task.IEsptouchGenerator; 4 | import com.espressif.iot.esptouch.util.ByteUtil; 5 | 6 | import java.net.InetAddress; 7 | 8 | public class EsptouchGenerator implements IEsptouchGenerator { 9 | 10 | private final byte[][] mGcBytes2; 11 | private final byte[][] mDcBytes2; 12 | 13 | /** 14 | * Constructor of EsptouchGenerator, it will cost some time(maybe a bit 15 | * much) 16 | * 17 | * @param apSsid the Ap's ssid 18 | * @param apBssid the Ap's bssid 19 | * @param apPassword the Ap's password 20 | * @param inetAddress the phone's or pad's local ip address allocated by Ap 21 | * @param isSsidHiden whether the Ap's ssid is hidden 22 | */ 23 | public EsptouchGenerator(byte[] apSsid, byte[] apBssid, byte[] apPassword, 24 | InetAddress inetAddress, boolean isSsidHiden) { 25 | // generate guide code 26 | GuideCode gc = new GuideCode(); 27 | char[] gcU81 = gc.getU8s(); 28 | mGcBytes2 = new byte[gcU81.length][]; 29 | 30 | for (int i = 0; i < mGcBytes2.length; i++) { 31 | mGcBytes2[i] = ByteUtil.genSpecBytes(gcU81[i]); 32 | } 33 | 34 | // generate data code 35 | DatumCode dc = new DatumCode(apSsid, apBssid, apPassword, inetAddress, 36 | isSsidHiden); 37 | char[] dcU81 = dc.getU8s(); 38 | mDcBytes2 = new byte[dcU81.length][]; 39 | 40 | for (int i = 0; i < mDcBytes2.length; i++) { 41 | mDcBytes2[i] = ByteUtil.genSpecBytes(dcU81[i]); 42 | } 43 | } 44 | 45 | @Override 46 | public byte[][] getGCBytes2() { 47 | return mGcBytes2; 48 | } 49 | 50 | @Override 51 | public byte[][] getDCBytes2() { 52 | return mDcBytes2; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/protocol/GuideCode.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.protocol; 2 | 3 | import com.espressif.iot.esptouch.task.ICodeData; 4 | import com.espressif.iot.esptouch.util.ByteUtil; 5 | 6 | public class GuideCode implements ICodeData { 7 | 8 | public static final int GUIDE_CODE_LEN = 4; 9 | 10 | @Override 11 | public byte[] getBytes() { 12 | throw new RuntimeException("DataCode don't support getBytes()"); 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | StringBuilder sb = new StringBuilder(); 18 | char[] dataU8s = getU8s(); 19 | for (int i = 0; i < GUIDE_CODE_LEN; i++) { 20 | String hexString = ByteUtil.convertU8ToHexString(dataU8s[i]); 21 | sb.append("0x"); 22 | if (hexString.length() == 1) { 23 | sb.append("0"); 24 | } 25 | sb.append(hexString).append(" "); 26 | } 27 | return sb.toString(); 28 | } 29 | 30 | @Override 31 | public char[] getU8s() { 32 | char[] guidesU8s = new char[GUIDE_CODE_LEN]; 33 | guidesU8s[0] = 515; 34 | guidesU8s[1] = 514; 35 | guidesU8s[2] = 513; 36 | guidesU8s[3] = 512; 37 | return guidesU8s; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/protocol/TouchData.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.protocol; 2 | 3 | import com.espressif.iot.esptouch.util.ByteUtil; 4 | 5 | public class TouchData { 6 | private final byte[] mData; 7 | 8 | public TouchData(String string) { 9 | mData = ByteUtil.getBytesByString(string); 10 | } 11 | 12 | public TouchData(byte[] data) { 13 | if (data == null) { 14 | throw new NullPointerException("data can't be null"); 15 | } 16 | mData = data; 17 | } 18 | 19 | public byte[] getData() { 20 | return mData; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/task/EsptouchTaskParameter.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.task; 2 | 3 | import com.espressif.iot.esptouch.EsptouchTask; 4 | 5 | public class EsptouchTaskParameter implements IEsptouchTaskParameter { 6 | 7 | private static int _datagramCount = 0; 8 | private long mIntervalGuideCodeMillisecond; 9 | private long mIntervalDataCodeMillisecond; 10 | private long mTimeoutGuideCodeMillisecond; 11 | private long mTimeoutDataCodeMillisecond; 12 | private int mTotalRepeatTime; 13 | private int mEsptouchResultOneLen; 14 | private int mEsptouchResultMacLen; 15 | private int mEsptouchResultIpLen; 16 | private int mEsptouchResultTotalLen; 17 | private int mPortListening; 18 | private int mTargetPort; 19 | private int mWaitUdpReceivingMilliseond; 20 | private int mWaitUdpSendingMillisecond; 21 | private int mThresholdSucBroadcastCount; 22 | private int mExpectTaskResultCount; 23 | private boolean mBroadcast = true; 24 | 25 | public EsptouchTaskParameter() { 26 | mIntervalGuideCodeMillisecond = 8; 27 | mIntervalDataCodeMillisecond = 8; 28 | mTimeoutGuideCodeMillisecond = 2000; 29 | mTimeoutDataCodeMillisecond = 4000; 30 | mTotalRepeatTime = 1; 31 | mEsptouchResultOneLen = 1; 32 | mEsptouchResultMacLen = 6; 33 | mEsptouchResultIpLen = 4; 34 | mEsptouchResultTotalLen = 1 + 6 + 4; 35 | mPortListening = 18266; 36 | mTargetPort = 7001; 37 | mWaitUdpReceivingMilliseond = 15000; 38 | mWaitUdpSendingMillisecond = 45000; 39 | mThresholdSucBroadcastCount = 1; 40 | mExpectTaskResultCount = 1; 41 | } 42 | 43 | // the range of the result should be 1-100 44 | private static int __getNextDatagramCount() { 45 | return 1 + (_datagramCount++) % 100; 46 | } 47 | 48 | @Override 49 | public long getIntervalGuideCodeMillisecond() { 50 | return mIntervalGuideCodeMillisecond; 51 | } 52 | 53 | @Override 54 | public long getIntervalDataCodeMillisecond() { 55 | return mIntervalDataCodeMillisecond; 56 | } 57 | 58 | @Override 59 | public long getTimeoutGuideCodeMillisecond() { 60 | return mTimeoutGuideCodeMillisecond; 61 | } 62 | 63 | @Override 64 | public long getTimeoutDataCodeMillisecond() { 65 | return mTimeoutDataCodeMillisecond; 66 | } 67 | 68 | @Override 69 | public long getTimeoutTotalCodeMillisecond() { 70 | return mTimeoutGuideCodeMillisecond + mTimeoutDataCodeMillisecond; 71 | } 72 | 73 | @Override 74 | public int getTotalRepeatTime() { 75 | return mTotalRepeatTime; 76 | } 77 | 78 | @Override 79 | public int getEsptouchResultOneLen() { 80 | return mEsptouchResultOneLen; 81 | } 82 | 83 | @Override 84 | public int getEsptouchResultMacLen() { 85 | return mEsptouchResultMacLen; 86 | } 87 | 88 | @Override 89 | public int getEsptouchResultIpLen() { 90 | return mEsptouchResultIpLen; 91 | } 92 | 93 | @Override 94 | public int getEsptouchResultTotalLen() { 95 | return mEsptouchResultTotalLen; 96 | } 97 | 98 | @Override 99 | public int getPortListening() { 100 | return mPortListening; 101 | } 102 | 103 | // target hostname is : 234.1.1.1, 234.2.2.2, 234.3.3.3 to 234.100.100.100 104 | @Override 105 | public String getTargetHostname() { 106 | if (mBroadcast) { 107 | return "255.255.255.255"; 108 | } else { 109 | int count = __getNextDatagramCount(); 110 | return "234." + count + "." + count + "." + count; 111 | } 112 | } 113 | 114 | @Override 115 | public int getTargetPort() { 116 | return mTargetPort; 117 | } 118 | 119 | @Override 120 | public int getWaitUdpReceivingMillisecond() { 121 | return mWaitUdpReceivingMilliseond; 122 | } 123 | 124 | @Override 125 | public int getWaitUdpSendingMillisecond() { 126 | return mWaitUdpSendingMillisecond; 127 | } 128 | 129 | @Override 130 | public int getWaitUdpTotalMillisecond() { 131 | return mWaitUdpReceivingMilliseond + mWaitUdpSendingMillisecond; 132 | } 133 | 134 | @Override 135 | public void setWaitUdpTotalMillisecond(int waitUdpTotalMillisecond) { 136 | if (waitUdpTotalMillisecond < mWaitUdpReceivingMilliseond 137 | + getTimeoutTotalCodeMillisecond()) { 138 | // if it happen, even one turn about sending udp broadcast can't be 139 | // completed 140 | throw new IllegalArgumentException( 141 | "waitUdpTotalMillisecod is invalid, " 142 | + "it is less than mWaitUdpReceivingMilliseond + getTimeoutTotalCodeMillisecond()"); 143 | } 144 | mWaitUdpSendingMillisecond = waitUdpTotalMillisecond 145 | - mWaitUdpReceivingMilliseond; 146 | } 147 | 148 | @Override 149 | public int getThresholdSucBroadcastCount() { 150 | return mThresholdSucBroadcastCount; 151 | } 152 | 153 | @Override 154 | public int getExpectTaskResultCount() { 155 | return this.mExpectTaskResultCount; 156 | } 157 | 158 | @Override 159 | public void setExpectTaskResultCount(int expectTaskResultCount) { 160 | this.mExpectTaskResultCount = expectTaskResultCount; 161 | } 162 | 163 | @Override 164 | public void setBroadcast(boolean broadcast) { 165 | mBroadcast = broadcast; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/task/ICodeData.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.task; 2 | 3 | /** 4 | * the class used to represent some code to be transformed by UDP socket should implement the interface 5 | * 6 | * @author afunx 7 | */ 8 | public interface ICodeData { 9 | /** 10 | * Get the byte[] to be transformed. 11 | * 12 | * @return the byte[] to be transfromed 13 | */ 14 | byte[] getBytes(); 15 | 16 | /** 17 | * Get the char[](u8[]) to be transfromed. 18 | * 19 | * @return the char[](u8) to be transformed 20 | */ 21 | char[] getU8s(); 22 | } 23 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/task/IEsptouchGenerator.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.task; 2 | 3 | public interface IEsptouchGenerator { 4 | /** 5 | * Get guide code by the format of byte[][] 6 | * 7 | * @return guide code by the format of byte[][] 8 | */ 9 | byte[][] getGCBytes2(); 10 | 11 | /** 12 | * Get data code by the format of byte[][] 13 | * 14 | * @return data code by the format of byte[][] 15 | */ 16 | byte[][] getDCBytes2(); 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/task/IEsptouchTaskParameter.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.task; 2 | 3 | public interface IEsptouchTaskParameter { 4 | 5 | /** 6 | * get interval millisecond for guide code(the time between each guide code sending) 7 | * 8 | * @return interval millisecond for guide code(the time between each guide code sending) 9 | */ 10 | long getIntervalGuideCodeMillisecond(); 11 | 12 | /** 13 | * get interval millisecond for data code(the time between each data code sending) 14 | * 15 | * @return interval millisecond for data code(the time between each data code sending) 16 | */ 17 | long getIntervalDataCodeMillisecond(); 18 | 19 | /** 20 | * get timeout millisecond for guide code(the time how much the guide code sending) 21 | * 22 | * @return timeout millisecond for guide code(the time how much the guide code sending) 23 | */ 24 | long getTimeoutGuideCodeMillisecond(); 25 | 26 | /** 27 | * get timeout millisecond for data code(the time how much the data code sending) 28 | * 29 | * @return timeout millisecond for data code(the time how much the data code sending) 30 | */ 31 | long getTimeoutDataCodeMillisecond(); 32 | 33 | /** 34 | * get timeout millisecond for total code(guide code and data code altogether) 35 | * 36 | * @return timeout millisecond for total code(guide code and data code altogether) 37 | */ 38 | long getTimeoutTotalCodeMillisecond(); 39 | 40 | /** 41 | * get total repeat time for executing esptouch task 42 | * 43 | * @return total repeat time for executing esptouch task 44 | */ 45 | int getTotalRepeatTime(); 46 | 47 | /** 48 | * the length of the Esptouch result 1st byte is the total length of ssid and 49 | * password, the other 6 bytes are the device's bssid 50 | */ 51 | 52 | /** 53 | * get esptouchResult length of one 54 | * 55 | * @return length of one 56 | */ 57 | int getEsptouchResultOneLen(); 58 | 59 | /** 60 | * get esptouchResult length of mac 61 | * 62 | * @return length of mac 63 | */ 64 | int getEsptouchResultMacLen(); 65 | 66 | /** 67 | * get esptouchResult length of ip 68 | * 69 | * @return length of ip 70 | */ 71 | int getEsptouchResultIpLen(); 72 | 73 | /** 74 | * get esptouchResult total length 75 | * 76 | * @return total length 77 | */ 78 | int getEsptouchResultTotalLen(); 79 | 80 | /** 81 | * get port for listening(used by server) 82 | * 83 | * @return port for listening(used by server) 84 | */ 85 | int getPortListening(); 86 | 87 | /** 88 | * get target hostname 89 | * 90 | * @return target hostame(used by client) 91 | */ 92 | String getTargetHostname(); 93 | 94 | /** 95 | * get target port 96 | * 97 | * @return target port(used by client) 98 | */ 99 | int getTargetPort(); 100 | 101 | /** 102 | * get millisecond for waiting udp receiving(receiving without sending) 103 | * 104 | * @return millisecond for waiting udp receiving(receiving without sending) 105 | */ 106 | int getWaitUdpReceivingMillisecond(); 107 | 108 | /** 109 | * get millisecond for waiting udp sending(sending including receiving) 110 | * 111 | * @return millisecond for waiting udep sending(sending including receiving) 112 | */ 113 | int getWaitUdpSendingMillisecond(); 114 | 115 | /** 116 | * get millisecond for waiting udp sending and receiving 117 | * 118 | * @return millisecond for waiting udp sending and receiving 119 | */ 120 | int getWaitUdpTotalMillisecond(); 121 | 122 | /** 123 | * set the millisecond for waiting udp sending and receiving 124 | * 125 | * @param waitUdpTotalMillisecond the millisecond for waiting udp sending and receiving 126 | */ 127 | void setWaitUdpTotalMillisecond(int waitUdpTotalMillisecond); 128 | 129 | /** 130 | * get the threshold for how many correct broadcast should be received 131 | * 132 | * @return the threshold for how many correct broadcast should be received 133 | */ 134 | int getThresholdSucBroadcastCount(); 135 | 136 | /** 137 | * get the count of expect task results 138 | * 139 | * @return the count of expect task results 140 | */ 141 | int getExpectTaskResultCount(); 142 | 143 | /** 144 | * set the count of expect task results 145 | * 146 | * @param expectTaskResultCount the count of expect task results 147 | */ 148 | void setExpectTaskResultCount(int expectTaskResultCount); 149 | 150 | /** 151 | * Set broadcast or multicast 152 | * 153 | * @param broadcast true is broadcast, false is multicast 154 | */ 155 | void setBroadcast(boolean broadcast); 156 | } 157 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/task/__EsptouchTask.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.task; 2 | 3 | import android.content.Context; 4 | import android.os.Looper; 5 | import android.util.Log; 6 | 7 | import com.espressif.iot.esptouch.EsptouchResult; 8 | import com.espressif.iot.esptouch.IEsptouchListener; 9 | import com.espressif.iot.esptouch.IEsptouchResult; 10 | import com.espressif.iot.esptouch.IEsptouchTask; 11 | import com.espressif.iot.esptouch.protocol.EsptouchGenerator; 12 | import com.espressif.iot.esptouch.protocol.TouchData; 13 | import com.espressif.iot.esptouch.udp.UDPSocketClient; 14 | import com.espressif.iot.esptouch.udp.UDPSocketServer; 15 | import com.espressif.iot.esptouch.util.ByteUtil; 16 | import com.espressif.iot.esptouch.util.EspAES; 17 | import com.espressif.iot.esptouch.util.EspNetUtil; 18 | 19 | import java.net.InetAddress; 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.concurrent.atomic.AtomicBoolean; 25 | 26 | public class __EsptouchTask implements __IEsptouchTask { 27 | /** 28 | * one indivisible data contain 3 9bits info 29 | */ 30 | private static final int ONE_DATA_LEN = 3; 31 | 32 | private static final String TAG = "__EsptouchTask"; 33 | 34 | private final UDPSocketClient mSocketClient; 35 | private final UDPSocketServer mSocketServer; 36 | private final byte[] mApSsid; 37 | private final byte[] mApPassword; 38 | private final byte[] mApBssid; 39 | private final boolean mIsSsidHidden; 40 | private final Context mContext; 41 | private volatile List mEsptouchResultList; 42 | private volatile boolean mIsSuc = false; 43 | private volatile boolean mIsInterrupt = false; 44 | private volatile boolean mIsExecuted = false; 45 | private AtomicBoolean mIsCancelled; 46 | private IEsptouchTaskParameter mParameter; 47 | private volatile Map mBssidTaskSucCountMap; 48 | private IEsptouchListener mEsptouchListener; 49 | private Thread mTask; 50 | 51 | public __EsptouchTask(Context context, TouchData apSsid, TouchData apBssid, TouchData apPassword, EspAES espAES, 52 | IEsptouchTaskParameter parameter, boolean isSsidHidden) { 53 | Log.i(TAG, "Welcome Esptouch " + IEsptouchTask.ESPTOUCH_VERSION); 54 | mContext = context; 55 | if (espAES == null) { 56 | mApSsid = apSsid.getData(); 57 | mApPassword = apPassword.getData(); 58 | } else { 59 | mApSsid = espAES.encrypt(apSsid.getData()); 60 | mApPassword = espAES.encrypt(apPassword.getData()); 61 | } 62 | mApBssid = apBssid.getData(); 63 | mIsCancelled = new AtomicBoolean(false); 64 | mSocketClient = new UDPSocketClient(); 65 | mParameter = parameter; 66 | mSocketServer = new UDPSocketServer(mParameter.getPortListening(), 67 | mParameter.getWaitUdpTotalMillisecond(), context); 68 | mIsSsidHidden = isSsidHidden; 69 | mEsptouchResultList = new ArrayList<>(); 70 | mBssidTaskSucCountMap = new HashMap<>(); 71 | } 72 | 73 | private void __putEsptouchResult(boolean isSuc, String bssid, InetAddress inetAddress) { 74 | synchronized (mEsptouchResultList) { 75 | // check whether the result receive enough UDP response 76 | boolean isTaskSucCountEnough = false; 77 | Integer count = mBssidTaskSucCountMap.get(bssid); 78 | if (count == null) { 79 | count = 0; 80 | } 81 | ++count; 82 | if (__IEsptouchTask.DEBUG) { 83 | Log.d(TAG, "__putEsptouchResult(): count = " + count); 84 | } 85 | mBssidTaskSucCountMap.put(bssid, count); 86 | isTaskSucCountEnough = count >= mParameter 87 | .getThresholdSucBroadcastCount(); 88 | if (!isTaskSucCountEnough) { 89 | if (__IEsptouchTask.DEBUG) { 90 | Log.d(TAG, "__putEsptouchResult(): count = " + count 91 | + ", isn't enough"); 92 | } 93 | return; 94 | } 95 | // check whether the result is in the mEsptouchResultList already 96 | boolean isExist = false; 97 | for (IEsptouchResult esptouchResultInList : mEsptouchResultList) { 98 | if (esptouchResultInList.getBssid().equals(bssid)) { 99 | isExist = true; 100 | break; 101 | } 102 | } 103 | // only add the result who isn't in the mEsptouchResultList 104 | if (!isExist) { 105 | if (__IEsptouchTask.DEBUG) { 106 | Log.d(TAG, "__putEsptouchResult(): put one more result"); 107 | } 108 | final IEsptouchResult esptouchResult = new EsptouchResult(isSuc, 109 | bssid, inetAddress); 110 | mEsptouchResultList.add(esptouchResult); 111 | if (mEsptouchListener != null) { 112 | mEsptouchListener.onEsptouchResultAdded(esptouchResult); 113 | } 114 | } 115 | } 116 | } 117 | 118 | private List __getEsptouchResultList() { 119 | synchronized (mEsptouchResultList) { 120 | if (mEsptouchResultList.isEmpty()) { 121 | EsptouchResult esptouchResultFail = new EsptouchResult(false, 122 | null, null); 123 | esptouchResultFail.setIsCancelled(mIsCancelled.get()); 124 | mEsptouchResultList.add(esptouchResultFail); 125 | } 126 | 127 | return mEsptouchResultList; 128 | } 129 | } 130 | 131 | private synchronized void __interrupt() { 132 | if (!mIsInterrupt) { 133 | mIsInterrupt = true; 134 | mSocketClient.interrupt(); 135 | mSocketServer.interrupt(); 136 | // interrupt the current Thread which is used to wait for udp response 137 | if (mTask != null) { 138 | mTask.interrupt(); 139 | mTask = null; 140 | } 141 | } 142 | } 143 | 144 | @Override 145 | public void interrupt() { 146 | if (__IEsptouchTask.DEBUG) { 147 | Log.d(TAG, "interrupt()"); 148 | } 149 | mIsCancelled.set(true); 150 | __interrupt(); 151 | } 152 | 153 | private void __listenAsyn(final int expectDataLen) { 154 | mTask = new Thread() { 155 | public void run() { 156 | if (__IEsptouchTask.DEBUG) { 157 | Log.d(TAG, "__listenAsyn() start"); 158 | } 159 | long startTimestamp = System.currentTimeMillis(); 160 | // byte[] apSsidAndPassword = ByteUtil.getBytesByString(mApSsid 161 | // + mApPassword); 162 | byte expectOneByte = (byte) (mApSsid.length + mApPassword.length + 9); 163 | if (__IEsptouchTask.DEBUG) { 164 | Log.i(TAG, "expectOneByte: " + (0 + expectOneByte)); 165 | } 166 | byte receiveOneByte = -1; 167 | byte[] receiveBytes = null; 168 | while (mEsptouchResultList.size() < mParameter 169 | .getExpectTaskResultCount() && !mIsInterrupt) { 170 | receiveBytes = mSocketServer 171 | .receiveSpecLenBytes(expectDataLen); 172 | if (receiveBytes != null) { 173 | receiveOneByte = receiveBytes[0]; 174 | } else { 175 | receiveOneByte = -1; 176 | } 177 | if (receiveOneByte == expectOneByte) { 178 | if (__IEsptouchTask.DEBUG) { 179 | Log.i(TAG, "receive correct broadcast"); 180 | } 181 | // change the socket's timeout 182 | long consume = System.currentTimeMillis() 183 | - startTimestamp; 184 | int timeout = (int) (mParameter 185 | .getWaitUdpTotalMillisecond() - consume); 186 | if (timeout < 0) { 187 | if (__IEsptouchTask.DEBUG) { 188 | Log.i(TAG, "esptouch timeout"); 189 | } 190 | break; 191 | } else { 192 | if (__IEsptouchTask.DEBUG) { 193 | Log.i(TAG, "mSocketServer's new timeout is " 194 | + timeout + " milliseconds"); 195 | } 196 | mSocketServer.setSoTimeout(timeout); 197 | if (__IEsptouchTask.DEBUG) { 198 | Log.i(TAG, "receive correct broadcast"); 199 | } 200 | if (receiveBytes != null) { 201 | String bssid = ByteUtil.parseBssid( 202 | receiveBytes, 203 | mParameter.getEsptouchResultOneLen(), 204 | mParameter.getEsptouchResultMacLen()); 205 | InetAddress inetAddress = EspNetUtil 206 | .parseInetAddr( 207 | receiveBytes, 208 | mParameter 209 | .getEsptouchResultOneLen() 210 | + mParameter 211 | .getEsptouchResultMacLen(), 212 | mParameter 213 | .getEsptouchResultIpLen()); 214 | __putEsptouchResult(true, bssid, inetAddress); 215 | } 216 | } 217 | } else { 218 | if (__IEsptouchTask.DEBUG) { 219 | Log.i(TAG, "receive rubbish message, just ignore"); 220 | } 221 | } 222 | } 223 | mIsSuc = mEsptouchResultList.size() >= mParameter 224 | .getExpectTaskResultCount(); 225 | __EsptouchTask.this.__interrupt(); 226 | if (__IEsptouchTask.DEBUG) { 227 | Log.d(TAG, "__listenAsyn() finish"); 228 | } 229 | } 230 | }; 231 | mTask.start(); 232 | } 233 | 234 | private boolean __execute(IEsptouchGenerator generator) { 235 | 236 | long startTime = System.currentTimeMillis(); 237 | long currentTime = startTime; 238 | long lastTime = currentTime - mParameter.getTimeoutTotalCodeMillisecond(); 239 | 240 | byte[][] gcBytes2 = generator.getGCBytes2(); 241 | byte[][] dcBytes2 = generator.getDCBytes2(); 242 | 243 | int index = 0; 244 | while (!mIsInterrupt) { 245 | if (currentTime - lastTime >= mParameter.getTimeoutTotalCodeMillisecond()) { 246 | if (__IEsptouchTask.DEBUG) { 247 | Log.d(TAG, "send gc code "); 248 | } 249 | // send guide code 250 | while (!mIsInterrupt 251 | && System.currentTimeMillis() - currentTime < mParameter 252 | .getTimeoutGuideCodeMillisecond()) { 253 | mSocketClient.sendData(gcBytes2, 254 | mParameter.getTargetHostname(), 255 | mParameter.getTargetPort(), 256 | mParameter.getIntervalGuideCodeMillisecond()); 257 | // check whether the udp is send enough time 258 | if (System.currentTimeMillis() - startTime > mParameter.getWaitUdpSendingMillisecond()) { 259 | break; 260 | } 261 | } 262 | lastTime = currentTime; 263 | } else { 264 | mSocketClient.sendData(dcBytes2, index, ONE_DATA_LEN, 265 | mParameter.getTargetHostname(), 266 | mParameter.getTargetPort(), 267 | mParameter.getIntervalDataCodeMillisecond()); 268 | index = (index + ONE_DATA_LEN) % dcBytes2.length; 269 | } 270 | currentTime = System.currentTimeMillis(); 271 | // check whether the udp is send enough time 272 | if (currentTime - startTime > mParameter.getWaitUdpSendingMillisecond()) { 273 | break; 274 | } 275 | } 276 | 277 | return mIsSuc; 278 | } 279 | 280 | private void __checkTaskValid() { 281 | // !!!NOTE: the esptouch task could be executed only once 282 | if (this.mIsExecuted) { 283 | throw new IllegalStateException( 284 | "the Esptouch task could be executed only once"); 285 | } 286 | this.mIsExecuted = true; 287 | } 288 | 289 | @Override 290 | public IEsptouchResult executeForResult() throws RuntimeException { 291 | return executeForResults(1).get(0); 292 | } 293 | 294 | @Override 295 | public boolean isCancelled() { 296 | return this.mIsCancelled.get(); 297 | } 298 | 299 | @Override 300 | public List executeForResults(int expectTaskResultCount) 301 | throws RuntimeException { 302 | __checkTaskValid(); 303 | 304 | mParameter.setExpectTaskResultCount(expectTaskResultCount); 305 | 306 | if (__IEsptouchTask.DEBUG) { 307 | Log.d(TAG, "execute()"); 308 | } 309 | if (Looper.myLooper() == Looper.getMainLooper()) { 310 | throw new RuntimeException( 311 | "Don't call the esptouch Task at Main(UI) thread directly."); 312 | } 313 | InetAddress localInetAddress = EspNetUtil.getLocalInetAddress(mContext); 314 | if (__IEsptouchTask.DEBUG) { 315 | Log.i(TAG, "localInetAddress: " + localInetAddress); 316 | } 317 | // generator the esptouch byte[][] to be transformed, which will cost 318 | // some time(maybe a bit much) 319 | IEsptouchGenerator generator = new EsptouchGenerator(mApSsid, mApBssid, 320 | mApPassword, localInetAddress, mIsSsidHidden); 321 | // listen the esptouch result asyn 322 | __listenAsyn(mParameter.getEsptouchResultTotalLen()); 323 | boolean isSuc = false; 324 | for (int i = 0; i < mParameter.getTotalRepeatTime(); i++) { 325 | isSuc = __execute(generator); 326 | if (isSuc) { 327 | return __getEsptouchResultList(); 328 | } 329 | } 330 | 331 | if (!mIsInterrupt) { 332 | // wait the udp response without sending udp broadcast 333 | try { 334 | Thread.sleep(mParameter.getWaitUdpReceivingMillisecond()); 335 | } catch (InterruptedException e) { 336 | // receive the udp broadcast or the user interrupt the task 337 | if (this.mIsSuc) { 338 | return __getEsptouchResultList(); 339 | } else { 340 | this.__interrupt(); 341 | return __getEsptouchResultList(); 342 | } 343 | } 344 | this.__interrupt(); 345 | } 346 | 347 | return __getEsptouchResultList(); 348 | } 349 | 350 | @Override 351 | public void setEsptouchListener(IEsptouchListener esptouchListener) { 352 | mEsptouchListener = esptouchListener; 353 | } 354 | 355 | } 356 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/task/__IEsptouchTask.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.task; 2 | 3 | import com.espressif.iot.esptouch.IEsptouchListener; 4 | import com.espressif.iot.esptouch.IEsptouchResult; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * IEsptouchTask defined the task of esptouch should offer. INTERVAL here means 10 | * the milliseconds of interval of the step. REPEAT here means the repeat times 11 | * of the step. 12 | * 13 | * @author afunx 14 | */ 15 | public interface __IEsptouchTask { 16 | 17 | /** 18 | * Turn on or off the log. 19 | */ 20 | static final boolean DEBUG = true; 21 | 22 | /** 23 | * set the esptouch listener, when one device is connected to the Ap, it will be called back 24 | * 25 | * @param esptouchListener when one device is connected to the Ap, it will be called back 26 | */ 27 | void setEsptouchListener(IEsptouchListener esptouchListener); 28 | 29 | /** 30 | * Interrupt the Esptouch Task when User tap back or close the Application. 31 | */ 32 | void interrupt(); 33 | 34 | /** 35 | * Note: !!!Don't call the task at UI Main Thread or RuntimeException will 36 | * be thrown Execute the Esptouch Task and return the result 37 | * 38 | * @return the IEsptouchResult 39 | * @throws RuntimeException 40 | */ 41 | IEsptouchResult executeForResult() throws RuntimeException; 42 | 43 | /** 44 | * Note: !!!Don't call the task at UI Main Thread or RuntimeException will 45 | * be thrown Execute the Esptouch Task and return the result 46 | * 47 | * @param expectTaskResultCount the expect result count(if expectTaskResultCount <= 0, 48 | * expectTaskResultCount = Integer.MAX_VALUE) 49 | * @return the list of IEsptouchResult 50 | * @throws RuntimeException 51 | */ 52 | List executeForResults(int expectTaskResultCount) throws RuntimeException; 53 | 54 | boolean isCancelled(); 55 | } 56 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/udp/UDPSocketClient.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.udp; 2 | 3 | import android.util.Log; 4 | 5 | import com.espressif.iot.esptouch.task.__IEsptouchTask; 6 | 7 | import java.io.IOException; 8 | import java.net.DatagramPacket; 9 | import java.net.DatagramSocket; 10 | import java.net.InetAddress; 11 | import java.net.SocketException; 12 | import java.net.UnknownHostException; 13 | 14 | /** 15 | * this class is used to help send UDP data according to length 16 | * 17 | * @author afunx 18 | */ 19 | public class UDPSocketClient { 20 | 21 | private static final String TAG = "UDPSocketClient"; 22 | private DatagramSocket mSocket; 23 | private volatile boolean mIsStop; 24 | private volatile boolean mIsClosed; 25 | 26 | public UDPSocketClient() { 27 | try { 28 | this.mSocket = new DatagramSocket(); 29 | this.mIsStop = false; 30 | this.mIsClosed = false; 31 | } catch (SocketException e) { 32 | if (__IEsptouchTask.DEBUG) { 33 | Log.e(TAG, "SocketException"); 34 | } 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | @Override 40 | protected void finalize() throws Throwable { 41 | close(); 42 | super.finalize(); 43 | } 44 | 45 | public void interrupt() { 46 | if (__IEsptouchTask.DEBUG) { 47 | Log.i(TAG, "USPSocketClient is interrupt"); 48 | } 49 | this.mIsStop = true; 50 | } 51 | 52 | /** 53 | * close the UDP socket 54 | */ 55 | public synchronized void close() { 56 | if (!this.mIsClosed) { 57 | this.mSocket.close(); 58 | this.mIsClosed = true; 59 | } 60 | } 61 | 62 | /** 63 | * send the data by UDP 64 | * 65 | * @param data the data to be sent 66 | * @param targetPort the port of target 67 | * @param interval the milliseconds to between each UDP sent 68 | */ 69 | public void sendData(byte[][] data, String targetHostName, int targetPort, 70 | long interval) { 71 | sendData(data, 0, data.length, targetHostName, targetPort, interval); 72 | } 73 | 74 | 75 | /** 76 | * send the data by UDP 77 | * 78 | * @param data the data to be sent 79 | * @param offset the offset which data to be sent 80 | * @param count the count of the data 81 | * @param targetPort the port of target 82 | * @param interval the milliseconds to between each UDP sent 83 | */ 84 | public void sendData(byte[][] data, int offset, int count, 85 | String targetHostName, int targetPort, long interval) { 86 | if ((data == null) || (data.length <= 0)) { 87 | if (__IEsptouchTask.DEBUG) { 88 | Log.e(TAG, "sendData(): data == null or length <= 0"); 89 | } 90 | return; 91 | } 92 | for (int i = offset; !mIsStop && i < offset + count; i++) { 93 | if (data[i].length == 0) { 94 | continue; 95 | } 96 | try { 97 | InetAddress targetInetAddress = InetAddress.getByName(targetHostName); 98 | DatagramPacket localDatagramPacket = new DatagramPacket( 99 | data[i], data[i].length, targetInetAddress, targetPort); 100 | this.mSocket.send(localDatagramPacket); 101 | } catch (UnknownHostException e) { 102 | if (__IEsptouchTask.DEBUG) { 103 | Log.e(TAG, "sendData(): UnknownHostException"); 104 | } 105 | e.printStackTrace(); 106 | mIsStop = true; 107 | break; 108 | } catch (IOException e) { 109 | if (__IEsptouchTask.DEBUG) { 110 | Log.e(TAG, "sendData(): IOException, but just ignore it"); 111 | } 112 | // for the Ap will make some troubles when the phone send too many UDP packets, 113 | // but we don't expect the UDP packet received by others, so just ignore it 114 | } 115 | try { 116 | Thread.sleep(interval); 117 | } catch (InterruptedException e) { 118 | e.printStackTrace(); 119 | if (__IEsptouchTask.DEBUG) { 120 | Log.e(TAG, "sendData is Interrupted"); 121 | } 122 | mIsStop = true; 123 | break; 124 | } 125 | } 126 | if (mIsStop) { 127 | close(); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/udp/UDPSocketServer.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.udp; 2 | 3 | import android.content.Context; 4 | import android.net.wifi.WifiManager; 5 | import android.util.Log; 6 | 7 | import java.io.IOException; 8 | import java.net.DatagramPacket; 9 | import java.net.DatagramSocket; 10 | import java.net.InetSocketAddress; 11 | import java.net.SocketException; 12 | import java.util.Arrays; 13 | 14 | public class UDPSocketServer { 15 | private static final String TAG = "UDPSocketServer"; 16 | private final byte[] buffer; 17 | private DatagramPacket mReceivePacket; 18 | private DatagramSocket mServerSocket; 19 | private Context mContext; 20 | private WifiManager.MulticastLock mLock; 21 | private volatile boolean mIsClosed; 22 | 23 | /** 24 | * Constructor of UDP Socket Server 25 | * 26 | * @param port the Socket Server port 27 | * @param socketTimeout the socket read timeout 28 | * @param context the context of the Application 29 | */ 30 | public UDPSocketServer(int port, int socketTimeout, Context context) { 31 | this.mContext = context; 32 | this.buffer = new byte[64]; 33 | this.mReceivePacket = new DatagramPacket(buffer, 64); 34 | try { 35 | this.mServerSocket = new DatagramSocket(null); 36 | this.mServerSocket.setReuseAddress(true); 37 | this.mServerSocket.bind(new InetSocketAddress(port)); 38 | this.mServerSocket.setSoTimeout(socketTimeout); 39 | } catch (IOException e) { 40 | Log.e(TAG, "IOException"); 41 | e.printStackTrace(); 42 | } 43 | this.mIsClosed = false; 44 | WifiManager manager = (WifiManager) mContext.getApplicationContext() 45 | .getSystemService(Context.WIFI_SERVICE); 46 | mLock = manager.createMulticastLock("test wifi"); 47 | Log.d(TAG, "mServerSocket is created, socket read timeout: " 48 | + socketTimeout + ", port: " + port); 49 | } 50 | 51 | private synchronized void acquireLock() { 52 | if (mLock != null && !mLock.isHeld()) { 53 | mLock.acquire(); 54 | } 55 | } 56 | 57 | private synchronized void releaseLock() { 58 | if (mLock != null && mLock.isHeld()) { 59 | try { 60 | mLock.release(); 61 | } catch (Throwable th) { 62 | // ignoring this exception, probably wakeLock was already released 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Set the socket timeout in milliseconds 69 | * 70 | * @param timeout the timeout in milliseconds or 0 for no timeout. 71 | * @return true whether the timeout is set suc 72 | */ 73 | public boolean setSoTimeout(int timeout) { 74 | try { 75 | this.mServerSocket.setSoTimeout(timeout); 76 | return true; 77 | } catch (SocketException e) { 78 | e.printStackTrace(); 79 | } 80 | return false; 81 | } 82 | 83 | /** 84 | * Receive one byte from the port and convert it into String 85 | * 86 | * @return 87 | */ 88 | public byte receiveOneByte() { 89 | Log.d(TAG, "receiveOneByte() entrance"); 90 | try { 91 | acquireLock(); 92 | mServerSocket.receive(mReceivePacket); 93 | Log.d(TAG, "receive: " + (mReceivePacket.getData()[0])); 94 | return mReceivePacket.getData()[0]; 95 | } catch (IOException e) { 96 | e.printStackTrace(); 97 | } 98 | return Byte.MIN_VALUE; 99 | } 100 | 101 | /** 102 | * Receive specific length bytes from the port and convert it into String 103 | * 21,24,-2,52,-102,-93,-60 104 | * 15,18,fe,34,9a,a3,c4 105 | * 106 | * @return 107 | */ 108 | public byte[] receiveSpecLenBytes(int len) { 109 | Log.d(TAG, "receiveSpecLenBytes() entrance: len = " + len); 110 | try { 111 | acquireLock(); 112 | mServerSocket.receive(mReceivePacket); 113 | byte[] recDatas = Arrays.copyOf(mReceivePacket.getData(), mReceivePacket.getLength()); 114 | Log.d(TAG, "received len : " + recDatas.length); 115 | for (int i = 0; i < recDatas.length; i++) { 116 | Log.e(TAG, "recDatas[" + i + "]:" + recDatas[i]); 117 | } 118 | Log.e(TAG, "receiveSpecLenBytes: " + new String(recDatas)); 119 | if (recDatas.length != len) { 120 | Log.w(TAG, 121 | "received len is different from specific len, return null"); 122 | return null; 123 | } 124 | return recDatas; 125 | } catch (IOException e) { 126 | e.printStackTrace(); 127 | } 128 | return null; 129 | } 130 | 131 | public void interrupt() { 132 | Log.i(TAG, "USPSocketServer is interrupt"); 133 | close(); 134 | } 135 | 136 | public synchronized void close() { 137 | if (!this.mIsClosed) { 138 | Log.e(TAG, "mServerSocket is closed"); 139 | mServerSocket.close(); 140 | releaseLock(); 141 | this.mIsClosed = true; 142 | } 143 | } 144 | 145 | @Override 146 | protected void finalize() throws Throwable { 147 | close(); 148 | super.finalize(); 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/util/ByteUtil.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.util.Random; 5 | 6 | /** 7 | * In Java, it don't support unsigned int, so we use char to replace uint8. 8 | * The range of byte is [-128,127], and the range of char is [0,65535]. 9 | * So the byte could used to store the uint8. 10 | * (We assume that the String could be mapped to assic) 11 | * 12 | * @author afunx 13 | */ 14 | public class ByteUtil { 15 | 16 | public static final String ESPTOUCH_ENCODING_CHARSET = "UTF-8"; 17 | 18 | /** 19 | * Put String to byte[] 20 | * 21 | * @param destbytes the byte[] of dest 22 | * @param srcString the String of src 23 | * @param destOffset the offset of byte[] 24 | * @param srcOffset the offset of String 25 | * @param count the count of dest, and the count of src as well 26 | */ 27 | public static void putString2bytes(byte[] destbytes, String srcString, 28 | int destOffset, int srcOffset, int count) { 29 | for (int i = 0; i < count; i++) { 30 | destbytes[count + i] = srcString.getBytes()[i]; 31 | } 32 | } 33 | 34 | /** 35 | * Convert uint8 into char( we treat char as uint8) 36 | * 37 | * @param uint8 the unit8 to be converted 38 | * @return the byte of the unint8 39 | */ 40 | public static byte convertUint8toByte(char uint8) { 41 | if (uint8 > Byte.MAX_VALUE - Byte.MIN_VALUE) { 42 | throw new RuntimeException("Out of Boundary"); 43 | } 44 | return (byte) uint8; 45 | } 46 | 47 | /** 48 | * Convert char into uint8( we treat char as uint8 ) 49 | * 50 | * @param b the byte to be converted 51 | * @return the char(uint8) 52 | */ 53 | public static char convertByte2Uint8(byte b) { 54 | // char will be promoted to int for char don't support & operator 55 | // & 0xff could make negatvie value to positive 56 | return (char) (b & 0xff); 57 | } 58 | 59 | /** 60 | * Convert byte[] into char[]( we treat char[] as uint8[]) 61 | * 62 | * @param bytes the byte[] to be converted 63 | * @return the char[](uint8[]) 64 | */ 65 | public static char[] convertBytes2Uint8s(byte[] bytes) { 66 | int len = bytes.length; 67 | char[] uint8s = new char[len]; 68 | for (int i = 0; i < len; i++) { 69 | uint8s[i] = convertByte2Uint8(bytes[i]); 70 | } 71 | return uint8s; 72 | } 73 | 74 | /** 75 | * Put byte[] into char[]( we treat char[] as uint8[]) 76 | * 77 | * @param destUint8s the char[](uint8[]) array 78 | * @param srcBytes the byte[] 79 | * @param destOffset the offset of char[](uint8[]) 80 | * @param srcOffset the offset of byte[] 81 | * @param count the count of dest, and the count of src as well 82 | */ 83 | public static void putbytes2Uint8s(char[] destUint8s, byte[] srcBytes, 84 | int destOffset, int srcOffset, int count) { 85 | for (int i = 0; i < count; i++) { 86 | destUint8s[destOffset + i] = convertByte2Uint8(srcBytes[srcOffset 87 | + i]); 88 | } 89 | } 90 | 91 | /** 92 | * Convert byte to Hex String 93 | * 94 | * @param b the byte to be converted 95 | * @return the Hex String 96 | */ 97 | public static String convertByte2HexString(byte b) { 98 | char u8 = convertByte2Uint8(b); 99 | return Integer.toHexString(u8); 100 | } 101 | 102 | /** 103 | * Convert char(uint8) to Hex String 104 | * 105 | * @param u8 the char(uint8) to be converted 106 | * @return the Hex String 107 | */ 108 | public static String convertU8ToHexString(char u8) { 109 | return Integer.toHexString(u8); 110 | } 111 | 112 | /** 113 | * Split uint8 to 2 bytes of high byte and low byte. e.g. 20 = 0x14 should 114 | * be split to [0x01,0x04] 0x01 is high byte and 0x04 is low byte 115 | * 116 | * @param uint8 the char(uint8) 117 | * @return the high and low bytes be split, byte[0] is high and byte[1] is 118 | * low 119 | */ 120 | public static byte[] splitUint8To2bytes(char uint8) { 121 | if (uint8 < 0 || uint8 > 0xff) { 122 | throw new RuntimeException("Out of Boundary"); 123 | } 124 | String hexString = Integer.toHexString(uint8); 125 | byte low; 126 | byte high; 127 | if (hexString.length() > 1) { 128 | high = (byte) Integer.parseInt(hexString.substring(0, 1), 16); 129 | low = (byte) Integer.parseInt(hexString.substring(1, 2), 16); 130 | } else { 131 | high = 0; 132 | low = (byte) Integer.parseInt(hexString.substring(0, 1), 16); 133 | } 134 | byte[] result = new byte[]{high, low}; 135 | return result; 136 | } 137 | 138 | /** 139 | * Combine 2 bytes (high byte and low byte) to one whole byte 140 | * 141 | * @param high the high byte 142 | * @param low the low byte 143 | * @return the whole byte 144 | */ 145 | public static byte combine2bytesToOne(byte high, byte low) { 146 | if (high < 0 || high > 0xf || low < 0 || low > 0xf) { 147 | throw new RuntimeException("Out of Boundary"); 148 | } 149 | return (byte) (high << 4 | low); 150 | } 151 | 152 | /** 153 | * Combine 2 bytes (high byte and low byte) to 154 | * 155 | * @param high the high byte 156 | * @param low the low byte 157 | * @return the char(u8) 158 | */ 159 | public static char combine2bytesToU16(byte high, byte low) { 160 | char highU8 = convertByte2Uint8(high); 161 | char lowU8 = convertByte2Uint8(low); 162 | return (char) (highU8 << 8 | lowU8); 163 | } 164 | 165 | /** 166 | * Generate the random byte to be sent 167 | * 168 | * @return the random byte 169 | */ 170 | private static byte randomByte() { 171 | return (byte) (127 - new Random().nextInt(256)); 172 | } 173 | 174 | /** 175 | * Generate the random byte to be sent 176 | * 177 | * @param len the len presented by u8 178 | * @return the byte[] to be sent 179 | */ 180 | public static byte[] randomBytes(char len) { 181 | byte[] data = new byte[len]; 182 | for (int i = 0; i < len; i++) { 183 | data[i] = randomByte(); 184 | } 185 | return data; 186 | } 187 | 188 | public static byte[] genSpecBytes(char len) { 189 | byte[] data = new byte[len]; 190 | for (int i = 0; i < len; i++) { 191 | data[i] = '1'; 192 | } 193 | return data; 194 | } 195 | 196 | /** 197 | * Generate the random byte to be sent 198 | * 199 | * @param len the len presented by byte 200 | * @return the byte[] to be sent 201 | */ 202 | public static byte[] randomBytes(byte len) { 203 | char u8 = convertByte2Uint8(len); 204 | return randomBytes(u8); 205 | } 206 | 207 | /** 208 | * Generate the specific byte to be sent 209 | * 210 | * @param len the len presented by byte 211 | * @return the byte[] 212 | */ 213 | public static byte[] genSpecBytes(byte len) { 214 | char u8 = convertByte2Uint8(len); 215 | return genSpecBytes(u8); 216 | } 217 | 218 | public static String parseBssid(byte[] bssidBytes, int offset, int count) { 219 | byte[] bytes = new byte[count]; 220 | System.arraycopy(bssidBytes, offset, bytes, 0, count); 221 | return parseBssid(bytes); 222 | } 223 | 224 | /** 225 | * parse "24,-2,52,-102,-93,-60" to "18,fe,34,9a,a3,c4" 226 | * parse the bssid from hex to String 227 | * 228 | * @param bssidBytes the hex bytes bssid, e.g. {24,-2,52,-102,-93,-60} 229 | * @return the String of bssid, e.g. 18fe349aa3c4 230 | */ 231 | public static String parseBssid(byte[] bssidBytes) { 232 | StringBuilder sb = new StringBuilder(); 233 | int k; 234 | String hexK; 235 | String str; 236 | for (byte bssidByte : bssidBytes) { 237 | k = 0xff & bssidByte; 238 | hexK = Integer.toHexString(k); 239 | str = ((k < 16) ? ("0" + hexK) : (hexK)); 240 | System.out.println(str); 241 | sb.append(str); 242 | } 243 | return sb.toString(); 244 | } 245 | 246 | /** 247 | * @param string the string to be used 248 | * @return the byte[] of String according to {@link #ESPTOUCH_ENCODING_CHARSET} 249 | */ 250 | public static byte[] getBytesByString(String string) { 251 | try { 252 | return string.getBytes(ESPTOUCH_ENCODING_CHARSET); 253 | } catch (UnsupportedEncodingException e) { 254 | throw new IllegalArgumentException("the charset is invalid"); 255 | } 256 | } 257 | 258 | private static void test_splitUint8To2bytes() { 259 | // 20 = 0x14 260 | byte[] result = splitUint8To2bytes((char) 20); 261 | if (result[0] == 1 && result[1] == 4) { 262 | System.out.println("test_splitUint8To2bytes(): pass"); 263 | } else { 264 | System.out.println("test_splitUint8To2bytes(): fail"); 265 | } 266 | } 267 | 268 | private static void test_combine2bytesToOne() { 269 | byte high = 0x01; 270 | byte low = 0x04; 271 | if (combine2bytesToOne(high, low) == 20) { 272 | System.out.println("test_combine2bytesToOne(): pass"); 273 | } else { 274 | System.out.println("test_combine2bytesToOne(): fail"); 275 | } 276 | } 277 | 278 | private static void test_convertChar2Uint8() { 279 | byte b1 = 'a'; 280 | // -128: 1000 0000 should be 128 in unsigned char 281 | // -1: 1111 1111 should be 255 in unsigned char 282 | byte b2 = (byte) -128; 283 | byte b3 = (byte) -1; 284 | if (convertByte2Uint8(b1) == 97 && convertByte2Uint8(b2) == 128 285 | && convertByte2Uint8(b3) == 255) { 286 | System.out.println("test_convertChar2Uint8(): pass"); 287 | } else { 288 | System.out.println("test_convertChar2Uint8(): fail"); 289 | } 290 | } 291 | 292 | private static void test_convertUint8toByte() { 293 | char c1 = 'a'; 294 | // 128: 1000 0000 should be -128 in byte 295 | // 255: 1111 1111 should be -1 in byte 296 | char c2 = 128; 297 | char c3 = 255; 298 | if (convertUint8toByte(c1) == 97 && convertUint8toByte(c2) == -128 299 | && convertUint8toByte(c3) == -1) { 300 | System.out.println("test_convertUint8toByte(): pass"); 301 | } else { 302 | System.out.println("test_convertUint8toByte(): fail"); 303 | } 304 | } 305 | 306 | private static void test_parseBssid() { 307 | byte b[] = {15, -2, 52, -102, -93, -60}; 308 | if (parseBssid(b).equals("0ffe349aa3c4")) { 309 | System.out.println("test_parseBssid(): pass"); 310 | } else { 311 | System.out.println("test_parseBssid(): fail"); 312 | } 313 | } 314 | 315 | public static void main(String args[]) { 316 | test_convertUint8toByte(); 317 | test_convertChar2Uint8(); 318 | test_splitUint8To2bytes(); 319 | test_combine2bytesToOne(); 320 | test_parseBssid(); 321 | } 322 | 323 | } 324 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/util/CRC8.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.util; 2 | 3 | import java.util.zip.Checksum; 4 | 5 | public class CRC8 implements Checksum { 6 | 7 | private static final short[] crcTable = new short[256]; 8 | private static final short CRC_POLYNOM = 0x8c; 9 | private static final short CRC_INITIAL = 0x00; 10 | 11 | static { 12 | for (int dividend = 0; dividend < 256; dividend++) { 13 | int remainder = dividend;// << 8; 14 | for (int bit = 0; bit < 8; ++bit) 15 | if ((remainder & 0x01) != 0) 16 | remainder = (remainder >>> 1) ^ CRC_POLYNOM; 17 | else 18 | remainder >>>= 1; 19 | crcTable[dividend] = (short) remainder; 20 | } 21 | } 22 | 23 | private final short init; 24 | private short value; 25 | 26 | public CRC8() { 27 | this.value = this.init = CRC_INITIAL; 28 | } 29 | 30 | @Override 31 | public void update(byte[] buffer, int offset, int len) { 32 | for (int i = 0; i < len; i++) { 33 | int data = buffer[offset + i] ^ value; 34 | value = (short) (crcTable[data & 0xff] ^ (value << 8)); 35 | } 36 | } 37 | 38 | /** 39 | * Updates the current checksum with the specified array of bytes. 40 | * Equivalent to calling update(buffer, 0, buffer.length). 41 | * 42 | * @param buffer the byte array to update the checksum with 43 | */ 44 | public void update(byte[] buffer) { 45 | update(buffer, 0, buffer.length); 46 | } 47 | 48 | @Override 49 | public void update(int b) { 50 | update(new byte[]{(byte) b}, 0, 1); 51 | } 52 | 53 | @Override 54 | public long getValue() { 55 | return value & 0xff; 56 | } 57 | 58 | @Override 59 | public void reset() { 60 | value = init; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/util/EspAES.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.util; 2 | 3 | import java.security.InvalidAlgorithmParameterException; 4 | import java.security.InvalidKeyException; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | import javax.crypto.BadPaddingException; 8 | import javax.crypto.Cipher; 9 | import javax.crypto.IllegalBlockSizeException; 10 | import javax.crypto.NoSuchPaddingException; 11 | import javax.crypto.spec.IvParameterSpec; 12 | import javax.crypto.spec.SecretKeySpec; 13 | 14 | public class EspAES { 15 | private static final String TRANSFORMATION_DEFAULT = "AES/ECB/PKCS5Padding"; 16 | 17 | private final byte[] mKey; 18 | private final byte[] mIV; 19 | private final String mTransformation; 20 | private Cipher mEncryptCipher; 21 | private Cipher mDecryptCipher; 22 | 23 | public EspAES(byte[] key) { 24 | this(key, null, TRANSFORMATION_DEFAULT); 25 | } 26 | 27 | public EspAES(byte[] key, String transformation) { 28 | this(key, null, transformation); 29 | } 30 | 31 | public EspAES(byte[] key, byte[] iv) { 32 | this(key, iv, TRANSFORMATION_DEFAULT); 33 | } 34 | 35 | public EspAES(byte[] key, byte[] iv, String transformation) { 36 | mKey = key; 37 | mIV = iv; 38 | mTransformation = transformation; 39 | 40 | mEncryptCipher = createEncryptCipher(); 41 | mDecryptCipher = createDecryptCipher(); 42 | } 43 | 44 | private Cipher createEncryptCipher() { 45 | try { 46 | Cipher cipher = Cipher.getInstance(mTransformation); 47 | 48 | SecretKeySpec secretKeySpec = new SecretKeySpec(mKey, "AES"); 49 | if (mIV == null) { 50 | cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 51 | } else { 52 | IvParameterSpec parameterSpec = new IvParameterSpec(mIV); 53 | cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, parameterSpec); 54 | } 55 | 56 | return cipher; 57 | } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException 58 | e) { 59 | e.printStackTrace(); 60 | } 61 | 62 | return null; 63 | } 64 | 65 | private Cipher createDecryptCipher() { 66 | try { 67 | Cipher cipher = Cipher.getInstance(mTransformation); 68 | 69 | SecretKeySpec secretKeySpec = new SecretKeySpec(mKey, "AES"); 70 | if (mIV == null) { 71 | cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); 72 | } else { 73 | IvParameterSpec parameterSpec = new IvParameterSpec(mIV); 74 | cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, parameterSpec); 75 | } 76 | 77 | return cipher; 78 | } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException 79 | e) { 80 | e.printStackTrace(); 81 | } 82 | 83 | return null; 84 | } 85 | 86 | public byte[] encrypt(byte[] content) { 87 | try { 88 | return mEncryptCipher.doFinal(content); 89 | } catch (BadPaddingException | IllegalBlockSizeException e) { 90 | e.printStackTrace(); 91 | } 92 | return null; 93 | } 94 | 95 | public byte[] decrypt(byte[] content) { 96 | try { 97 | return mDecryptCipher.doFinal(content); 98 | } catch (BadPaddingException | IllegalBlockSizeException e) { 99 | e.printStackTrace(); 100 | } 101 | 102 | return null; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/espressif/iot/esptouch/util/EspNetUtil.java: -------------------------------------------------------------------------------- 1 | package com.espressif.iot.esptouch.util; 2 | 3 | import android.content.Context; 4 | import android.net.wifi.WifiInfo; 5 | import android.net.wifi.WifiManager; 6 | 7 | import java.net.InetAddress; 8 | import java.net.UnknownHostException; 9 | 10 | public class EspNetUtil { 11 | 12 | /** 13 | * get the local ip address by Android System 14 | * 15 | * @param context the context 16 | * @return the local ip addr allocated by Ap 17 | */ 18 | public static InetAddress getLocalInetAddress(Context context) { 19 | WifiManager wm = (WifiManager) context 20 | .getSystemService(Context.WIFI_SERVICE); 21 | WifiInfo wifiInfo = wm.getConnectionInfo(); 22 | int localAddrInt = wifiInfo.getIpAddress(); 23 | String localAddrStr = __formatString(localAddrInt); 24 | InetAddress localInetAddr = null; 25 | try { 26 | localInetAddr = InetAddress.getByName(localAddrStr); 27 | } catch (UnknownHostException e) { 28 | e.printStackTrace(); 29 | } 30 | return localInetAddr; 31 | } 32 | 33 | private static String __formatString(int value) { 34 | String strValue = ""; 35 | byte[] ary = __intToByteArray(value); 36 | for (int i = ary.length - 1; i >= 0; i--) { 37 | strValue += (ary[i] & 0xFF); 38 | if (i > 0) { 39 | strValue += "."; 40 | } 41 | } 42 | return strValue; 43 | } 44 | 45 | private static byte[] __intToByteArray(int value) { 46 | byte[] b = new byte[4]; 47 | for (int i = 0; i < 4; i++) { 48 | int offset = (b.length - 1 - i) * 8; 49 | b[i] = (byte) ((value >>> offset) & 0xFF); 50 | } 51 | return b; 52 | } 53 | 54 | /** 55 | * parse InetAddress 56 | * 57 | * @param inetAddrBytes 58 | * @return 59 | */ 60 | public static InetAddress parseInetAddr(byte[] inetAddrBytes, int offset, 61 | int count) { 62 | InetAddress inetAddress = null; 63 | StringBuilder sb = new StringBuilder(); 64 | for (int i = 0; i < count; i++) { 65 | sb.append(Integer.toString(inetAddrBytes[offset + i] & 0xff)); 66 | if (i != count - 1) { 67 | sb.append('.'); 68 | } 69 | } 70 | try { 71 | inetAddress = InetAddress.getByName(sb.toString()); 72 | } catch (UnknownHostException e) { 73 | e.printStackTrace(); 74 | } 75 | return inetAddress; 76 | } 77 | 78 | /** 79 | * parse bssid 80 | * 81 | * @param bssid the bssid like aa:bb:cc:dd:ee:ff 82 | * @return byte converted from bssid 83 | */ 84 | public static byte[] parseBssid2bytes(String bssid) { 85 | String bssidSplits[] = bssid.split(":"); 86 | byte[] result = new byte[bssidSplits.length]; 87 | for (int i = 0; i < bssidSplits.length; i++) { 88 | result[i] = (byte) Integer.parseInt(bssidSplits[i], 16); 89 | } 90 | return result; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/esptouchflutterexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.esptouchflutterexample; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.net.ConnectivityManager; 6 | import android.net.NetworkInfo; 7 | import android.net.wifi.WifiInfo; 8 | import android.net.wifi.WifiManager; 9 | import android.os.AsyncTask; 10 | import android.os.Build; 11 | import android.os.Bundle; 12 | import io.flutter.app.FlutterActivity; 13 | import io.flutter.plugins.GeneratedPluginRegistrant; 14 | import android.content.ContextWrapper; 15 | import android.content.Intent; 16 | import android.content.IntentFilter; 17 | import android.os.BatteryManager; 18 | import android.os.Build.VERSION; 19 | import android.os.Build.VERSION_CODES; 20 | import android.support.v4.content.ContextCompat; 21 | import android.util.Log; 22 | import android.content.pm.PackageManager; 23 | 24 | import com.espressif.iot.esptouch.EsptouchTask; 25 | import com.espressif.iot.esptouch.IEsptouchResult; 26 | import com.espressif.iot.esptouch.IEsptouchTask; 27 | import com.espressif.iot.esptouch.util.ByteUtil; 28 | import com.espressif.iot.esptouch.util.EspNetUtil; 29 | 30 | import org.json.JSONException; 31 | import org.json.JSONObject; 32 | import org.json.JSONArray; 33 | 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | import io.flutter.plugin.common.MethodCall; 38 | import io.flutter.plugin.common.MethodChannel; 39 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 40 | import io.flutter.plugin.common.MethodChannel.Result; 41 | 42 | public class MainActivity extends FlutterActivity { 43 | private static final String CHANNEL = "samples.flutter.io/esptouch"; 44 | private static final String TAG = "MainActivity"; 45 | 46 | private IEsptouchTask mEsptouchTask; 47 | 48 | @Override 49 | protected void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | GeneratedPluginRegistrant.registerWith(this); 52 | 53 | new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( 54 | new MethodCallHandler() { 55 | @Override 56 | public void onMethodCall(MethodCall call, Result result) { 57 | if (call.method.equals("startSmartConfig")) { 58 | String ssid = call.argument("ssid"); 59 | String bssid = call.argument("bssid"); 60 | String pass = call.argument("pass"); 61 | String deviceCount = call.argument("deviceCount"); 62 | String broadcast = call.argument("broadcast"); 63 | 64 | startSmartConfig(ssid, bssid, pass, deviceCount, broadcast, result); 65 | } 66 | else if (call.method.equals("stopSmartConfig")) { 67 | stopSmartConfig(); 68 | } 69 | else if (call.method.equals("getConnectedWiFiInfo")) { 70 | getWifiInfo(result); 71 | } 72 | else if (call.method.equals("getBatteryLevel")) { 73 | int batteryLevel = getBatteryLevel(); 74 | 75 | if (batteryLevel != -1) { 76 | result.success(batteryLevel); 77 | } else { 78 | result.error("UNAVAILABLE", "Battery level not available.", null); 79 | } 80 | } else { 81 | result.notImplemented(); 82 | } 83 | }}); 84 | } 85 | 86 | private void getWifiInfo(Result result) { 87 | final ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 88 | final NetworkInfo networkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 89 | 90 | if (networkInfo != null && networkInfo.isConnected()) { 91 | final WifiManager wifiManager = (WifiManager)getApplicationContext().getSystemService(Context.WIFI_SERVICE); 92 | final WifiInfo wifiInfo = wifiManager.getConnectionInfo(); 93 | 94 | if(wifiInfo != null) { 95 | final String ssid = wifiInfo.getSSID().replaceAll("^\"|\"$", ""); 96 | final String bssid = wifiInfo.getBSSID(); 97 | String is5G = "unknow"; 98 | 99 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 100 | int frequency = wifiInfo.getFrequency(); 101 | if (frequency > 4900 && frequency < 5900) { 102 | // Connected 5G wifi. Device does not support 5G 103 | is5G = "yes"; 104 | } else { 105 | is5G = "no"; 106 | } 107 | } 108 | 109 | JSONObject re = new JSONObject(); 110 | try { 111 | re.put("ssid", ssid); 112 | re.put("bssid", bssid); 113 | re.put("is5G", is5G); 114 | } catch (JSONException ex) { 115 | result.error("getWifiInfo", ex.getMessage(), null); 116 | return; 117 | } 118 | 119 | result.success(re.toString()); 120 | } else { 121 | result.error("getWifiInfo", "Unable to obtain WiFi details", null); 122 | } 123 | } else { 124 | result.error("getWifiInfo", "Not connected to WiFi", null); 125 | } 126 | } 127 | 128 | 129 | public void stopSmartConfig() { 130 | if (mEsptouchTask != null) { 131 | mEsptouchTask.interrupt(); 132 | } 133 | } 134 | 135 | private void startSmartConfig(String ssid, String bssid, String pass, String deviceCount, String broadcast, final Result resultResp) { 136 | stopSmartConfig(); 137 | 138 | /*Log.d(TAG, "ssid " + ssid); 139 | Log.d(TAG, "bssid " + bssid); 140 | Log.d(TAG, "pass " + pass); 141 | Log.d(TAG, "deviceCount " + deviceCount); 142 | Log.d(TAG, "broadcast " + broadcast); 143 | */ 144 | 145 | byte[] apSsid = ByteUtil.getBytesByString(ssid); 146 | byte[] apBssid = EspNetUtil.parseBssid2bytes(bssid); 147 | byte[] apPassword = ByteUtil.getBytesByString(pass); 148 | byte[] deviceCountData = deviceCount.getBytes(); 149 | byte[] broadcastData = broadcast.getBytes(); 150 | 151 | new EsptouchAsyncTask4(new TaskListener() { 152 | @Override 153 | public void onFinished(List result) { 154 | // Do Something after the task has finished 155 | 156 | 157 | 158 | try { 159 | IEsptouchResult firstResult = result.get(0); 160 | 161 | if (!firstResult.isCancelled()) { 162 | if (firstResult.isSuc()) { 163 | StringBuilder sb = new StringBuilder(); 164 | JSONArray jsonArray = new JSONArray(); 165 | 166 | for (IEsptouchResult resultInList : result) { 167 | if(!resultInList.isCancelled() &&resultInList.getBssid() != null) { 168 | 169 | sb.append("Esptouch success, bssid = ") 170 | .append(resultInList.getBssid()) 171 | .append(", InetAddress = ") 172 | .append(resultInList.getInetAddress().getHostAddress()) 173 | .append("\n"); 174 | 175 | JSONObject re = new JSONObject(); 176 | re.put("bssid", resultInList.getBssid()); 177 | re.put("ip", resultInList.getInetAddress().getHostAddress()); 178 | jsonArray.put(re); 179 | } 180 | } 181 | 182 | Log.d(TAG, sb.toString()); 183 | 184 | JSONObject configureDeviceObj = new JSONObject(); 185 | configureDeviceObj.put("devices", jsonArray); 186 | resultResp.success(configureDeviceObj.toString()); 187 | 188 | } else { 189 | resultResp.error("startSmartConfig", "Esptouch fail", null); 190 | } 191 | } else { 192 | resultResp.error("startSmartConfig", "Esptouch cancelled", null); 193 | } 194 | 195 | } catch (Exception err) { 196 | resultResp.error("startSmartConfig", err.getMessage(), null); 197 | } 198 | 199 | 200 | } 201 | }).execute(apSsid, apBssid, apPassword, deviceCountData, broadcastData); 202 | } 203 | 204 | private int getBatteryLevel() { 205 | int batteryLevel = -1; 206 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 207 | BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE); 208 | batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); 209 | } else { 210 | Intent intent = new ContextWrapper(getApplicationContext()). 211 | registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 212 | batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) / 213 | intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); 214 | } 215 | 216 | return batteryLevel; 217 | } 218 | 219 | public interface TaskListener { 220 | public void onFinished(List result); 221 | } 222 | 223 | private class EsptouchAsyncTask4 extends AsyncTask> { 224 | private static final String TAG = "EsptouchAsyncTask4"; 225 | 226 | private final TaskListener taskListener; 227 | 228 | public EsptouchAsyncTask4(TaskListener listener) { 229 | // The listener reference is passed in through the constructor 230 | this.taskListener = listener; 231 | } 232 | 233 | private final Object mLock = new Object(); 234 | 235 | @Override 236 | protected void onPreExecute() { 237 | 238 | } 239 | 240 | @Override 241 | protected List doInBackground(byte[]... params) { 242 | int taskResultCount; 243 | 244 | synchronized (mLock) { 245 | byte[] apSsid = params[0]; 246 | byte[] apBssid = params[1]; 247 | byte[] apPassword = params[2]; 248 | byte[] deviceCountData = params[3]; 249 | byte[] broadcastData = params[4]; 250 | 251 | taskResultCount = deviceCountData.length == 0 ? -1 : Integer.parseInt(new String(deviceCountData)); 252 | 253 | mEsptouchTask = new EsptouchTask(apSsid, apBssid, apPassword, 254 | getApplicationContext()); 255 | mEsptouchTask.setPackageBroadcast(broadcastData[0] == 1); // true is broadcast, false is multicast 256 | } 257 | 258 | List resultList = mEsptouchTask.executeForResults(taskResultCount); 259 | return resultList; 260 | } 261 | 262 | @Override 263 | protected void onPostExecute(List result) { 264 | 265 | IEsptouchResult firstResult = result.get(0); 266 | // check whether the task is cancelled and no results received 267 | if (!firstResult.isCancelled()) { 268 | if(this.taskListener != null) { 269 | // And if it is we call the callback function on it. 270 | this.taskListener.onFinished(result); 271 | } 272 | } 273 | } 274 | } 275 | 276 | 277 | } 278 | 279 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /flutter_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/flutter_01.png -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXCopyFilesBuildPhase section */ 26 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 27 | isa = PBXCopyFilesBuildPhase; 28 | buildActionMask = 2147483647; 29 | dstPath = ""; 30 | dstSubfolderSpec = 10; 31 | files = ( 32 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 33 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 34 | ); 35 | name = "Embed Frameworks"; 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 43 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 44 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 45 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 46 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 47 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 51 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 52 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 66 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 9740EEB11CF90186004384FC /* Flutter */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 77 | 3B80C3931E831B6300D905FE /* App.framework */, 78 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 79 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 80 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 81 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 82 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 83 | ); 84 | name = Flutter; 85 | sourceTree = ""; 86 | }; 87 | 97C146E51CF9000F007C117D = { 88 | isa = PBXGroup; 89 | children = ( 90 | 9740EEB11CF90186004384FC /* Flutter */, 91 | 97C146F01CF9000F007C117D /* Runner */, 92 | 97C146EF1CF9000F007C117D /* Products */, 93 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 97C146EF1CF9000F007C117D /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 97C146EE1CF9000F007C117D /* Runner.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 97C146F01CF9000F007C117D /* Runner */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 109 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 110 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 111 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 112 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 113 | 97C147021CF9000F007C117D /* Info.plist */, 114 | 97C146F11CF9000F007C117D /* Supporting Files */, 115 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 116 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 97C146F21CF9000F007C117D /* main.m */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 97C146ED1CF9000F007C117D /* Runner */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 135 | buildPhases = ( 136 | 9740EEB61CF901F6004384FC /* Run Script */, 137 | 97C146EA1CF9000F007C117D /* Sources */, 138 | 97C146EB1CF9000F007C117D /* Frameworks */, 139 | 97C146EC1CF9000F007C117D /* Resources */, 140 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = Runner; 148 | productName = Runner; 149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 97C146E61CF9000F007C117D /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 0910; 159 | ORGANIZATIONNAME = "The Chromium Authors"; 160 | TargetAttributes = { 161 | 97C146ED1CF9000F007C117D = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | }; 164 | }; 165 | }; 166 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | Base, 173 | ); 174 | mainGroup = 97C146E51CF9000F007C117D; 175 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 176 | projectDirPath = ""; 177 | projectRoot = ""; 178 | targets = ( 179 | 97C146ED1CF9000F007C117D /* Runner */, 180 | ); 181 | }; 182 | /* End PBXProject section */ 183 | 184 | /* Begin PBXResourcesBuildPhase section */ 185 | 97C146EC1CF9000F007C117D /* Resources */ = { 186 | isa = PBXResourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 190 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 191 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 193 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Thin Binary"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 214 | }; 215 | 9740EEB61CF901F6004384FC /* Run Script */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputPaths = ( 221 | ); 222 | name = "Run Script"; 223 | outputPaths = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | shellPath = /bin/sh; 227 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 228 | }; 229 | /* End PBXShellScriptBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | 97C146EA1CF9000F007C117D /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 237 | 97C146F31CF9000F007C117D /* main.m in Sources */, 238 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXVariantGroup section */ 245 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 246 | isa = PBXVariantGroup; 247 | children = ( 248 | 97C146FB1CF9000F007C117D /* Base */, 249 | ); 250 | name = Main.storyboard; 251 | sourceTree = ""; 252 | }; 253 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 254 | isa = PBXVariantGroup; 255 | children = ( 256 | 97C147001CF9000F007C117D /* Base */, 257 | ); 258 | name = LaunchScreen.storyboard; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXVariantGroup section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 265 | isa = XCBuildConfiguration; 266 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 287 | CLANG_WARN_STRICT_PROTOTYPES = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 292 | COPY_PHASE_STRIP = NO; 293 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 294 | ENABLE_NS_ASSERTIONS = NO; 295 | ENABLE_STRICT_OBJC_MSGSEND = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_NO_COMMON_BLOCKS = YES; 298 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 299 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 300 | GCC_WARN_UNDECLARED_SELECTOR = YES; 301 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 302 | GCC_WARN_UNUSED_FUNCTION = YES; 303 | GCC_WARN_UNUSED_VARIABLE = YES; 304 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 305 | MTL_ENABLE_DEBUG_INFO = NO; 306 | SDKROOT = iphoneos; 307 | TARGETED_DEVICE_FAMILY = "1,2"; 308 | VALIDATE_PRODUCT = YES; 309 | }; 310 | name = Profile; 311 | }; 312 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 313 | isa = XCBuildConfiguration; 314 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 315 | buildSettings = { 316 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 317 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 318 | DEVELOPMENT_TEAM = S8QB4VV633; 319 | ENABLE_BITCODE = NO; 320 | FRAMEWORK_SEARCH_PATHS = ( 321 | "$(inherited)", 322 | "$(PROJECT_DIR)/Flutter", 323 | ); 324 | INFOPLIST_FILE = Runner/Info.plist; 325 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 326 | LIBRARY_SEARCH_PATHS = ( 327 | "$(inherited)", 328 | "$(PROJECT_DIR)/Flutter", 329 | ); 330 | PRODUCT_BUNDLE_IDENTIFIER = com.example.espTouchFlutterExample; 331 | PRODUCT_NAME = "$(TARGET_NAME)"; 332 | VERSIONING_SYSTEM = "apple-generic"; 333 | }; 334 | name = Profile; 335 | }; 336 | 97C147031CF9000F007C117D /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 339 | buildSettings = { 340 | ALWAYS_SEARCH_USER_PATHS = NO; 341 | CLANG_ANALYZER_NONNULL = YES; 342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 343 | CLANG_CXX_LIBRARY = "libc++"; 344 | CLANG_ENABLE_MODULES = YES; 345 | CLANG_ENABLE_OBJC_ARC = YES; 346 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 347 | CLANG_WARN_BOOL_CONVERSION = YES; 348 | CLANG_WARN_COMMA = YES; 349 | CLANG_WARN_CONSTANT_CONVERSION = YES; 350 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 351 | CLANG_WARN_EMPTY_BODY = YES; 352 | CLANG_WARN_ENUM_CONVERSION = YES; 353 | CLANG_WARN_INFINITE_RECURSION = YES; 354 | CLANG_WARN_INT_CONVERSION = YES; 355 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 356 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 357 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 358 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 359 | CLANG_WARN_STRICT_PROTOTYPES = YES; 360 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 361 | CLANG_WARN_UNREACHABLE_CODE = YES; 362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 364 | COPY_PHASE_STRIP = NO; 365 | DEBUG_INFORMATION_FORMAT = dwarf; 366 | ENABLE_STRICT_OBJC_MSGSEND = YES; 367 | ENABLE_TESTABILITY = YES; 368 | GCC_C_LANGUAGE_STANDARD = gnu99; 369 | GCC_DYNAMIC_NO_PIC = NO; 370 | GCC_NO_COMMON_BLOCKS = YES; 371 | GCC_OPTIMIZATION_LEVEL = 0; 372 | GCC_PREPROCESSOR_DEFINITIONS = ( 373 | "DEBUG=1", 374 | "$(inherited)", 375 | ); 376 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 377 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 378 | GCC_WARN_UNDECLARED_SELECTOR = YES; 379 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 380 | GCC_WARN_UNUSED_FUNCTION = YES; 381 | GCC_WARN_UNUSED_VARIABLE = YES; 382 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 383 | MTL_ENABLE_DEBUG_INFO = YES; 384 | ONLY_ACTIVE_ARCH = YES; 385 | SDKROOT = iphoneos; 386 | TARGETED_DEVICE_FAMILY = "1,2"; 387 | }; 388 | name = Debug; 389 | }; 390 | 97C147041CF9000F007C117D /* Release */ = { 391 | isa = XCBuildConfiguration; 392 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 393 | buildSettings = { 394 | ALWAYS_SEARCH_USER_PATHS = NO; 395 | CLANG_ANALYZER_NONNULL = YES; 396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 397 | CLANG_CXX_LIBRARY = "libc++"; 398 | CLANG_ENABLE_MODULES = YES; 399 | CLANG_ENABLE_OBJC_ARC = YES; 400 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 401 | CLANG_WARN_BOOL_CONVERSION = YES; 402 | CLANG_WARN_COMMA = YES; 403 | CLANG_WARN_CONSTANT_CONVERSION = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_EMPTY_BODY = YES; 406 | CLANG_WARN_ENUM_CONVERSION = YES; 407 | CLANG_WARN_INFINITE_RECURSION = YES; 408 | CLANG_WARN_INT_CONVERSION = YES; 409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNREACHABLE_CODE = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 418 | COPY_PHASE_STRIP = NO; 419 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 420 | ENABLE_NS_ASSERTIONS = NO; 421 | ENABLE_STRICT_OBJC_MSGSEND = YES; 422 | GCC_C_LANGUAGE_STANDARD = gnu99; 423 | GCC_NO_COMMON_BLOCKS = YES; 424 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 425 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 426 | GCC_WARN_UNDECLARED_SELECTOR = YES; 427 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 428 | GCC_WARN_UNUSED_FUNCTION = YES; 429 | GCC_WARN_UNUSED_VARIABLE = YES; 430 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 431 | MTL_ENABLE_DEBUG_INFO = NO; 432 | SDKROOT = iphoneos; 433 | TARGETED_DEVICE_FAMILY = "1,2"; 434 | VALIDATE_PRODUCT = YES; 435 | }; 436 | name = Release; 437 | }; 438 | 97C147061CF9000F007C117D /* Debug */ = { 439 | isa = XCBuildConfiguration; 440 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 441 | buildSettings = { 442 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 443 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 444 | ENABLE_BITCODE = NO; 445 | FRAMEWORK_SEARCH_PATHS = ( 446 | "$(inherited)", 447 | "$(PROJECT_DIR)/Flutter", 448 | ); 449 | INFOPLIST_FILE = Runner/Info.plist; 450 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 451 | LIBRARY_SEARCH_PATHS = ( 452 | "$(inherited)", 453 | "$(PROJECT_DIR)/Flutter", 454 | ); 455 | PRODUCT_BUNDLE_IDENTIFIER = com.example.espTouchFlutterExample; 456 | PRODUCT_NAME = "$(TARGET_NAME)"; 457 | VERSIONING_SYSTEM = "apple-generic"; 458 | }; 459 | name = Debug; 460 | }; 461 | 97C147071CF9000F007C117D /* Release */ = { 462 | isa = XCBuildConfiguration; 463 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 464 | buildSettings = { 465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 466 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 467 | ENABLE_BITCODE = NO; 468 | FRAMEWORK_SEARCH_PATHS = ( 469 | "$(inherited)", 470 | "$(PROJECT_DIR)/Flutter", 471 | ); 472 | INFOPLIST_FILE = Runner/Info.plist; 473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 474 | LIBRARY_SEARCH_PATHS = ( 475 | "$(inherited)", 476 | "$(PROJECT_DIR)/Flutter", 477 | ); 478 | PRODUCT_BUNDLE_IDENTIFIER = com.example.espTouchFlutterExample; 479 | PRODUCT_NAME = "$(TARGET_NAME)"; 480 | VERSIONING_SYSTEM = "apple-generic"; 481 | }; 482 | name = Release; 483 | }; 484 | /* End XCBuildConfiguration section */ 485 | 486 | /* Begin XCConfigurationList section */ 487 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 488 | isa = XCConfigurationList; 489 | buildConfigurations = ( 490 | 97C147031CF9000F007C117D /* Debug */, 491 | 97C147041CF9000F007C117D /* Release */, 492 | 249021D3217E4FDB00AE95B9 /* Profile */, 493 | ); 494 | defaultConfigurationIsVisible = 0; 495 | defaultConfigurationName = Release; 496 | }; 497 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | 97C147061CF9000F007C117D /* Debug */, 501 | 97C147071CF9000F007C117D /* Release */, 502 | 249021D4217E4FDB00AE95B9 /* Profile */, 503 | ); 504 | defaultConfigurationIsVisible = 0; 505 | defaultConfigurationName = Release; 506 | }; 507 | /* End XCConfigurationList section */ 508 | }; 509 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 510 | } 511 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakopappa/esp8266_smartconfig_flutter_example/74ee59119bb8faabd125e959e62100d4103d11c4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | esp_touch_flutter_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | import 'package:flutter/services.dart'; 4 | import 'dart:convert'; 5 | import 'dart:io' show Platform; 6 | import 'package:device_info/device_info.dart'; 7 | import 'package:simple_permissions/simple_permissions.dart'; 8 | 9 | // Note support only Android 10 | // for iOS https://github.com/lou-lan/SmartConfig 11 | 12 | void main() => runApp(MyApp()); 13 | 14 | class MyApp extends StatelessWidget { 15 | // This widget is the root of your application. 16 | @override 17 | Widget build(BuildContext context) { 18 | return MaterialApp( 19 | title: 'ESP Onetouch Demo', 20 | theme: ThemeData( 21 | primarySwatch: Colors.blue, 22 | ), 23 | home: MyHomePage(title: 'ESP Onetouch Demo'), 24 | ); 25 | } 26 | } 27 | 28 | class MyHomePage extends StatefulWidget { 29 | MyHomePage({Key key, this.title}) : super(key: key); 30 | 31 | final String title; 32 | 33 | @override 34 | _MyHomePageState createState() => _MyHomePageState(); 35 | } 36 | 37 | class _MyHomePageState extends State { 38 | static const platform = const MethodChannel('samples.flutter.io/esptouch'); 39 | static final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); 40 | 41 | final TextEditingController _bssidFilter = new TextEditingController(); 42 | final TextEditingController _ssidFilter = new TextEditingController(); 43 | final TextEditingController _passwordFilter = new TextEditingController(); 44 | 45 | bool _isLoading = false; 46 | 47 | String _ssid = ""; 48 | String _bssid = ""; 49 | String _password = ""; 50 | String _msg = ""; 51 | 52 | _MyHomePageState() { 53 | _ssidFilter.addListener(_ssidListen); 54 | _passwordFilter.addListener(_passwordListen); 55 | _bssidFilter.addListener(_bssidListen); 56 | } 57 | 58 | void _ssidListen() { 59 | if (_ssidFilter.text.isEmpty) { 60 | _ssid = ""; 61 | } else { 62 | _ssid = _ssidFilter.text; 63 | } 64 | } 65 | 66 | void _bssidListen() { 67 | if (_bssidFilter.text.isEmpty) { 68 | _bssid = ""; 69 | } else { 70 | _bssid = _bssidFilter.text; 71 | } 72 | } 73 | 74 | void _passwordListen() { 75 | if (_passwordFilter.text.isEmpty) { 76 | _password = ""; 77 | } else { 78 | _password = _passwordFilter.text; 79 | } 80 | } 81 | 82 | 83 | Future _configureEsp() async { 84 | String output = ""; 85 | 86 | 87 | setState(() { 88 | _isLoading = true; 89 | }); 90 | 91 | try { 92 | 93 | // Change if required. 94 | const String deviceCount = "1"; // the expect result count 95 | const String broadcast = "1"; // broadcast or multicast 96 | const Duration _kLongTimeout = const Duration(seconds: 20); 97 | 98 | final String result = await platform.invokeMethod('startSmartConfig', { 99 | 'ssid': _ssid, 100 | 'bssid': _bssid, 101 | 'pass': _password, 102 | 'deviceCount': deviceCount, 103 | 'broadcast': broadcast, 104 | }).timeout(_kLongTimeout); 105 | 106 | final parsed = json.decode(result); 107 | final devices = parsed["devices"]; 108 | 109 | output = "Following devices configured: \n\n"; 110 | 111 | for (var device in devices) { 112 | output += "bssid: ${device["bssid"]} ip: ${device["ip"]} \n"; 113 | } 114 | 115 | _msg = output; 116 | 117 | } on PlatformException catch (e) { 118 | output = "Failed to configure: '${e.message}'."; 119 | } 120 | 121 | setState(() { 122 | _isLoading = false; 123 | _msg = output; 124 | }); 125 | 126 | } 127 | 128 | 129 | 130 | Future _getConnectedWiFiInfo() async { 131 | String ssid = ""; 132 | String bssid = ""; 133 | String msg = ""; 134 | 135 | if (Platform.isIOS) { 136 | print('is a IOS'); 137 | } else if (Platform.isAndroid) { 138 | // Note Build.VERSION.SDK_INT >= 28 needs Manifest.permission.ACCESS_COARSE_LOCATION 139 | AndroidDeviceInfo build = await deviceInfoPlugin.androidInfo; 140 | if (build.version.sdkInt >= 28) { 141 | Permission permission = Permission.AccessCoarseLocation; 142 | final res = await SimplePermissions.checkPermission(permission); 143 | 144 | if(res == false) { 145 | final res = await SimplePermissions.requestPermission(permission); 146 | print("permission request result is " + res.toString()); 147 | } 148 | } 149 | } 150 | 151 | try { 152 | String wiFiInfo = await platform.invokeMethod('getConnectedWiFiInfo'); 153 | final parsed = json.decode(wiFiInfo); 154 | ssid = parsed["ssid"]; 155 | bssid = parsed["bssid"]; 156 | 157 | msg = 'Connected ssid name is $ssid. bssid is $bssid'; 158 | 159 | if(parsed["is5G"] == 'yes') { 160 | msg += ". Connected to a 5G network. Cannot use OneTouch SmartConfig!"; 161 | } 162 | 163 | } on PlatformException catch (e) { 164 | msg = "Failed to get connected WiFi name: '${e.message}'."; 165 | } 166 | 167 | setState(() { 168 | _ssidFilter.text = ssid; 169 | _bssidFilter.text = bssid; 170 | 171 | _msg = msg; 172 | }); 173 | } 174 | 175 | 176 | 177 | @override 178 | Widget build(BuildContext context) { 179 | return Scaffold( 180 | appBar: AppBar( 181 | title: Text(widget.title), 182 | ), 183 | body: Center( 184 | child: _isLoading ? Container( 185 | child: Center( 186 | child: CircularProgressIndicator( 187 | valueColor: AlwaysStoppedAnimation(Colors.lightBlue), 188 | ), 189 | ), 190 | color: Colors.white.withOpacity(0.8), 191 | ) : 192 | 193 | new Container( 194 | padding: new EdgeInsets.all(10.0), 195 | child: new Column( 196 | mainAxisAlignment: MainAxisAlignment.center, 197 | children: [ 198 | 199 | new Container(height: 10), 200 | 201 | new Container( 202 | child: Column( 203 | mainAxisAlignment: MainAxisAlignment.start, 204 | children: [ 205 | Text("ESP Touch v0.3.7.0"), 206 | new TextField( 207 | controller: _ssidFilter, 208 | decoration: new InputDecoration( 209 | labelText: 'ssid' 210 | ), 211 | ), 212 | new TextField( 213 | controller: _bssidFilter, 214 | decoration: new InputDecoration( 215 | labelText: 'bssid' 216 | ), 217 | ), 218 | RaisedButton( 219 | child: Text('Get Connected WiFi details'), 220 | onPressed: _getConnectedWiFiInfo, 221 | ) 222 | ])), 223 | 224 | new Container( 225 | child: new TextField( 226 | controller: _passwordFilter, 227 | decoration: new InputDecoration( 228 | labelText: 'Password' 229 | ), 230 | ), 231 | ), 232 | 233 | new RaisedButton( 234 | child: new Text('Configure ESP'), 235 | onPressed: _configureEsp, 236 | ), 237 | 238 | new Container(height: 10), 239 | 240 | Text(_msg), 241 | 242 | ], 243 | ) 244 | 245 | ) 246 | 247 | 248 | ) // This trailing comma makes auto-formatting nicer for build methods. 249 | ); 250 | } 251 | } -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: esp_touch_flutter_example 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | environment: 13 | sdk: ">=2.0.0-dev.68.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | # The following adds the Cupertino Icons font to your application. 20 | # Use with the CupertinoIcons class for iOS style icons. 21 | cupertino_icons: ^0.1.2 22 | device_info: ^0.3.0 23 | simple_permissions: ^0.1.9 24 | 25 | dev_dependencies: 26 | flutter_test: 27 | sdk: flutter 28 | 29 | 30 | # For information on the generic Dart part of this file, see the 31 | # following page: https://www.dartlang.org/tools/pub/pubspec 32 | 33 | # The following section is specific to Flutter. 34 | flutter: 35 | 36 | # The following line ensures that the Material Icons font is 37 | # included with your application, so that you can use the icons in 38 | # the material Icons class. 39 | uses-material-design: true 40 | 41 | # To add assets to your application, add an assets section, like this: 42 | # assets: 43 | # - images/a_dot_burr.jpeg 44 | # - images/a_dot_ham.jpeg 45 | 46 | # An image asset can refer to one or more resolution-specific "variants", see 47 | # https://flutter.io/assets-and-images/#resolution-aware. 48 | 49 | # For details regarding adding assets from package dependencies, see 50 | # https://flutter.io/assets-and-images/#from-packages 51 | 52 | # To add custom fonts to your application, add a fonts section here, 53 | # in this "flutter" section. Each entry in this list should have a 54 | # "family" key with the font family name, and a "fonts" key with a 55 | # list giving the asset and other descriptors for the font. For 56 | # example: 57 | # fonts: 58 | # - family: Schyler 59 | # fonts: 60 | # - asset: fonts/Schyler-Regular.ttf 61 | # - asset: fonts/Schyler-Italic.ttf 62 | # style: italic 63 | # - family: Trajan Pro 64 | # fonts: 65 | # - asset: fonts/TrajanPro.ttf 66 | # - asset: fonts/TrajanPro_Bold.ttf 67 | # weight: 700 68 | # 69 | # For details regarding fonts from package dependencies, 70 | # see https://flutter.io/custom-fonts/#from-packages 71 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:esp_touch_flutter_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------