first commit

This commit is contained in:
Niklas 2019-01-27 19:34:09 +01:00
commit 0b035dc916
100 changed files with 4413 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
.dart_tool/
.packages
.pub/
pubspec.lock
build/

View File

@ -0,0 +1,19 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/async" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/core" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/html" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/io" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/math" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://C:\src\flutter/bin/cache/dart-sdk/lib/typed_data" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="Flutter for Android">
<CLASSES>
<root url="jar://C:\src\flutter/bin/cache/artifacts/engine/android-arm/flutter.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

10
.idea/modules.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/flutter_smartconfig.iml" filepath="$PROJECT_DIR$/flutter_smartconfig.iml" />
<module fileurl="file://$PROJECT_DIR$/android/flutter_smartconfig_android.iml" filepath="$PROJECT_DIR$/android/flutter_smartconfig_android.iml" />
<module fileurl="file://$PROJECT_DIR$/example/android/flutter_smartconfig_example_android.iml" filepath="$PROJECT_DIR$/example/android/flutter_smartconfig_example_android.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="example/lib/main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/example/lib/main.dart" />
<method />
</configuration>
</component>

45
.idea/workspace.xml Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="FileEditorManager">
<leaf>
<file leaf-file-name="flutter_smartconfig.dart" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/lib/flutter_smartconfig.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="main.dart" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="0">
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="ToolWindowManager">
<editor active="true" />
<layout>
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
</layout>
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
</navigator>
<panes>
<pane id="ProjectPane">
<option name="show-excluded-files" value="false" />
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="dart.analysis.tool.window.force.activate" value="true" />
<property name="show.migrate.to.gradle.popup" value="false" />
</component>
</project>

10
.metadata Normal file
View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
channel: beta
project_type: plugin

3
CHANGELOG.md Normal file
View File

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
LICENSE Normal file
View File

@ -0,0 +1 @@
TODO: Add your license here.

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# flutter_smartconfig
A new flutter plugin project.
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.io/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

8
android/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

43
android/build.gradle Normal file
View File

@ -0,0 +1,43 @@
group 'de.mc8051.fluttersmartconfig'
version '1.0-SNAPSHOT'
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
allprojects { // Projects
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
}
}

View File

@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1536M

1
android/settings.gradle Normal file
View File

@ -0,0 +1 @@
rootProject.name = 'flutter_smartconfig'

View File

@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.mc8051.fluttersmartconfig">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</manifest>

View File

@ -0,0 +1,51 @@
package com.espressif.iot.esptouch;
import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicBoolean;
public class EsptouchResult implements IEsptouchResult {
private final boolean mIsSuc;
private final String mBssid;
private final InetAddress mInetAddress;
private AtomicBoolean mIsCancelled;
/**
* Constructor of EsptouchResult
*
* @param isSuc whether the esptouch task is executed suc
* @param bssid the device's bssid
* @param inetAddress the device's ip address
*/
public EsptouchResult(boolean isSuc, String bssid, InetAddress inetAddress) {
this.mIsSuc = isSuc;
this.mBssid = bssid;
this.mInetAddress = inetAddress;
this.mIsCancelled = new AtomicBoolean(false);
}
@Override
public boolean isSuc() {
return this.mIsSuc;
}
@Override
public String getBssid() {
return this.mBssid;
}
@Override
public boolean isCancelled() {
return mIsCancelled.get();
}
public void setIsCancelled(boolean isCancelled) {
this.mIsCancelled.set(isCancelled);
}
@Override
public InetAddress getInetAddress() {
return this.mInetAddress;
}
}

View File

@ -0,0 +1,114 @@
package com.espressif.iot.esptouch;
import android.content.Context;
import android.text.TextUtils;
import com.espressif.iot.esptouch.protocol.TouchData;
import com.espressif.iot.esptouch.task.EsptouchTaskParameter;
import com.espressif.iot.esptouch.task.__EsptouchTask;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.EspAES;
import com.espressif.iot.esptouch.util.EspNetUtil;
import java.util.List;
public class EsptouchTask implements IEsptouchTask {
public __EsptouchTask _mEsptouchTask;
private EsptouchTaskParameter _mParameter;
/**
* Constructor of EsptouchTask
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param context the Context of the Application
*/
public EsptouchTask(String apSsid, String apBssid, String apPassword, Context context) {
this(apSsid, apBssid, apPassword, null, context);
}
/**
* Constructor of EsptouchTask
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param espAES AES secret key
* @param context the Context of the Application
*/
public EsptouchTask(String apSsid, String apBssid, String apPassword, EspAES espAES, Context context) {
if (TextUtils.isEmpty(apSsid)) {
throw new NullPointerException("SSID can't be empty");
}
if (TextUtils.isEmpty(apBssid)) {
throw new NullPointerException("BSSID can't be empty");
}
if (apPassword == null) {
apPassword = "";
}
TouchData ssid = new TouchData(apSsid);
TouchData bssid = new TouchData(EspNetUtil.parseBssid2bytes(apBssid));
TouchData password = new TouchData(apPassword);
init(context, ssid, bssid, password, espAES);
}
public EsptouchTask(byte[] apSsid, byte[] apBssid, byte[] apPassword, Context context) {
this(apSsid, apBssid, apPassword, null, context);
}
public EsptouchTask(byte[] apSsid, byte[] apBssid, byte[] apPassword, EspAES espAES, Context context) {
if (apSsid == null || apSsid.length == 0) {
throw new NullPointerException("SSID can't be empty");
}
if (apBssid == null || apBssid.length == 0) {
throw new NullPointerException("BSSID can't be empty");
}
if (apPassword == null) {
apPassword = new byte[0];
}
TouchData ssid = new TouchData(apSsid);
TouchData bssid = new TouchData(apBssid);
TouchData password = new TouchData(apPassword);
init(context, ssid, bssid, password, espAES);
}
private void init(Context context, TouchData ssid, TouchData bssid, TouchData password, EspAES aes) {
_mParameter = new EsptouchTaskParameter();
_mEsptouchTask = new __EsptouchTask(context, ssid, bssid, password, aes, _mParameter, true);
}
@Override
public void interrupt() {
_mEsptouchTask.interrupt();
}
@Override
public IEsptouchResult executeForResult() throws RuntimeException {
return _mEsptouchTask.executeForResult();
}
@Override
public boolean isCancelled() {
return _mEsptouchTask.isCancelled();
}
@Override
public List<IEsptouchResult> executeForResults(int expectTaskResultCount)
throws RuntimeException {
if (expectTaskResultCount <= 0) {
expectTaskResultCount = Integer.MAX_VALUE;
}
return _mEsptouchTask.executeForResults(expectTaskResultCount);
}
@Override
public void setEsptouchListener(IEsptouchListener esptouchListener) {
_mEsptouchTask.setEsptouchListener(esptouchListener);
}
@Override
public void setPackageBroadcast(boolean broadcast) {
_mParameter.setBroadcast(broadcast);
}
}

View File

@ -0,0 +1,11 @@
package com.espressif.iot.esptouch;
public interface IEsptouchListener {
/**
* when new esptouch result is added, the listener will call
* onEsptouchResultAdded callback
*
* @param result the Esptouch result
*/
void onEsptouchResultAdded(IEsptouchResult result);
}

View File

@ -0,0 +1,34 @@
package com.espressif.iot.esptouch;
import java.net.InetAddress;
public interface IEsptouchResult {
/**
* check whether the esptouch task is executed suc
*
* @return whether the esptouch task is executed suc
*/
boolean isSuc();
/**
* get the device's bssid
*
* @return the device's bssid
*/
String getBssid();
/**
* check whether the esptouch task is cancelled by user
*
* @return whether the esptouch task is cancelled by user
*/
boolean isCancelled();
/**
* get the ip address of the device
*
* @return the ip device of the device
*/
InetAddress getInetAddress();
}

View File

@ -0,0 +1,63 @@
package com.espressif.iot.esptouch;
import java.util.List;
public interface IEsptouchTask {
String ESPTOUCH_VERSION = "v0.3.7.0";
/**
* set the esptouch listener, when one device is connected to the Ap, it will be called back
*
* @param esptouchListener when one device is connected to the Ap, it will be called back
*/
void setEsptouchListener(IEsptouchListener esptouchListener);
/**
* Interrupt the Esptouch Task when User tap back or close the Application.
*/
void interrupt();
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
* <p>
* Smart Config v2.4 support the API
*
* @return the IEsptouchResult
* @throws RuntimeException
*/
IEsptouchResult executeForResult() throws RuntimeException;
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
* <p>
* Smart Config v2.4 support the API
* <p>
* It will be blocked until the client receive result count >= expectTaskResultCount.
* If it fail, it will return one fail result will be returned in the list.
* If it is cancelled while executing,
* if it has received some results, all of them will be returned in the list.
* if it hasn't received any results, one cancel result will be returned in the list.
*
* @param expectTaskResultCount the expect result count(if expectTaskResultCount <= 0,
* expectTaskResultCount = Integer.MAX_VALUE)
* @return the list of IEsptouchResult
* @throws RuntimeException
*/
List<IEsptouchResult> executeForResults(int expectTaskResultCount) throws RuntimeException;
/**
* check whether the task is cancelled by user
*
* @return whether the task is cancelled by user
*/
boolean isCancelled();
/**
* Set broadcast or multicast when post config info
*
* @param broadcast true is broadcast, false is multicast
*/
void setPackageBroadcast(boolean broadcast);
}

View File

@ -0,0 +1,87 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.task.ICodeData;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.CRC8;
/**
* one data format:(data code should have 2 to 65 data)
* <p>
* control byte high 4 bits low 4 bits
* 1st 9bits: 0x0 crc(high) data(high)
* 2nd 9bits: 0x1 sequence header
* 3rd 9bits: 0x0 crc(low) data(low)
* <p>
* sequence header: 0,1,2,...
*
* @author afunx
*/
public class DataCode implements ICodeData {
public static final int DATA_CODE_LEN = 6;
private static final int INDEX_MAX = 127;
private final byte mSeqHeader;
private final byte mDataHigh;
private final byte mDataLow;
// the crc here means the crc of the data and sequence header be transformed
// it is calculated by index and data to be transformed
private final byte mCrcHigh;
private final byte mCrcLow;
/**
* Constructor of DataCode
*
* @param u8 the character to be transformed
* @param index the index of the char
*/
public DataCode(char u8, int index) {
if (index > INDEX_MAX) {
throw new RuntimeException("index > INDEX_MAX");
}
byte[] dataBytes = ByteUtil.splitUint8To2bytes(u8);
mDataHigh = dataBytes[0];
mDataLow = dataBytes[1];
CRC8 crc8 = new CRC8();
crc8.update(ByteUtil.convertUint8toByte(u8));
crc8.update(index);
byte[] crcBytes = ByteUtil.splitUint8To2bytes((char) crc8.getValue());
mCrcHigh = crcBytes[0];
mCrcLow = crcBytes[1];
mSeqHeader = (byte) index;
}
@Override
public byte[] getBytes() {
byte[] dataBytes = new byte[DATA_CODE_LEN];
dataBytes[0] = 0x00;
dataBytes[1] = ByteUtil.combine2bytesToOne(mCrcHigh, mDataHigh);
dataBytes[2] = 0x01;
dataBytes[3] = mSeqHeader;
dataBytes[4] = 0x00;
dataBytes[5] = ByteUtil.combine2bytesToOne(mCrcLow, mDataLow);
return dataBytes;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
byte[] dataBytes = getBytes();
for (int i = 0; i < DATA_CODE_LEN; i++) {
String hexString = ByteUtil.convertByte2HexString(dataBytes[i]);
sb.append("0x");
if (hexString.length() == 1) {
sb.append("0");
}
sb.append(hexString).append(" ");
}
return sb.toString();
}
@Override
public char[] getU8s() {
throw new RuntimeException("DataCode don't support getU8s()");
}
}

View File

@ -0,0 +1,157 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.task.ICodeData;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.CRC8;
import java.net.InetAddress;
import java.util.LinkedList;
public class DatumCode implements ICodeData {
// define by the Esptouch protocol, all of the datum code should add 1 at last to prevent 0
private static final int EXTRA_LEN = 40;
private static final int EXTRA_HEAD_LEN = 5;
private final LinkedList<DataCode> mDataCodes;
/**
* Constructor of DatumCode
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param ipAddress the ip address of the phone or pad
* @param isSsidHiden whether the Ap's ssid is hidden
*/
public DatumCode(byte[] apSsid, byte[] apBssid, byte[] apPassword,
InetAddress ipAddress, boolean isSsidHiden) {
// Data = total len(1 byte) + apPwd len(1 byte) + SSID CRC(1 byte) +
// BSSID CRC(1 byte) + TOTAL XOR(1 byte)+ ipAddress(4 byte) + apPwd + apSsid apPwdLen <=
// 105 at the moment
// total xor
char totalXor = 0;
char apPwdLen = (char) apPassword.length;
CRC8 crc = new CRC8();
crc.update(apSsid);
char apSsidCrc = (char) crc.getValue();
crc.reset();
crc.update(apBssid);
char apBssidCrc = (char) crc.getValue();
char apSsidLen = (char) apSsid.length;
// hostname parse
String ipAddrStrs[] = ipAddress.getHostAddress().split("\\.");
int ipLen = ipAddrStrs.length;
char ipAddrChars[] = new char[ipLen];
// only support ipv4 at the moment
for (int i = 0; i < ipLen; ++i) {
ipAddrChars[i] = (char) Integer.parseInt(ipAddrStrs[i]);
}
char _totalLen = (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen + apSsidLen);
char totalLen = isSsidHiden ? (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen + apSsidLen)
: (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen);
// build data codes
mDataCodes = new LinkedList<>();
mDataCodes.add(new DataCode(_totalLen, 0));
totalXor ^= _totalLen;
mDataCodes.add(new DataCode(apPwdLen, 1));
totalXor ^= apPwdLen;
mDataCodes.add(new DataCode(apSsidCrc, 2));
totalXor ^= apSsidCrc;
mDataCodes.add(new DataCode(apBssidCrc, 3));
totalXor ^= apBssidCrc;
// ESPDataCode 4 is null
for (int i = 0; i < ipLen; ++i) {
mDataCodes.add(new DataCode(ipAddrChars[i], i + EXTRA_HEAD_LEN));
totalXor ^= ipAddrChars[i];
}
byte[] apPwdBytes = apPassword;
char[] apPwdChars = new char[apPwdBytes.length];
for (int i = 0; i < apPwdBytes.length; i++) {
apPwdChars[i] = ByteUtil.convertByte2Uint8(apPwdBytes[i]);
}
for (int i = 0; i < apPwdChars.length; i++) {
mDataCodes.add(new DataCode(apPwdChars[i], i + EXTRA_HEAD_LEN + ipLen));
totalXor ^= apPwdChars[i];
}
byte[] apSsidBytes = apSsid;
char[] apSsidChars = new char[apSsidBytes.length];
// totalXor will xor apSsidChars no matter whether the ssid is hidden
for (int i = 0; i < apSsidBytes.length; i++) {
apSsidChars[i] = ByteUtil.convertByte2Uint8(apSsidBytes[i]);
totalXor ^= apSsidChars[i];
}
if (isSsidHiden) {
for (int i = 0; i < apSsidChars.length; i++) {
mDataCodes.add(new DataCode(apSsidChars[i], i + EXTRA_HEAD_LEN + ipLen + apPwdLen));
}
}
// add total xor last
mDataCodes.add(4, new DataCode(totalXor, 4));
// add bssid
int bssidInsertIndex = EXTRA_HEAD_LEN;
for (int i = 0; i < apBssid.length; i++) {
int index = totalLen + i;
char c = ByteUtil.convertByte2Uint8(apBssid[i]);
DataCode dc = new DataCode(c, index);
if (bssidInsertIndex >= mDataCodes.size()) {
mDataCodes.add(dc);
} else {
mDataCodes.add(bssidInsertIndex, dc);
}
bssidInsertIndex += 4;
}
}
@Override
public byte[] getBytes() {
byte[] datumCode = new byte[mDataCodes.size() * DataCode.DATA_CODE_LEN];
int index = 0;
for (DataCode dc : mDataCodes) {
for (byte b : dc.getBytes()) {
datumCode[index++] = b;
}
}
return datumCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
byte[] dataBytes = getBytes();
for (byte dataByte : dataBytes) {
String hexString = ByteUtil.convertByte2HexString(dataByte);
sb.append("0x");
if (hexString.length() == 1) {
sb.append("0");
}
sb.append(hexString).append(" ");
}
return sb.toString();
}
@Override
public char[] getU8s() {
byte[] dataBytes = getBytes();
int len = dataBytes.length / 2;
char[] dataU8s = new char[len];
byte high, low;
for (int i = 0; i < len; i++) {
high = dataBytes[i * 2];
low = dataBytes[i * 2 + 1];
dataU8s[i] = (char) (ByteUtil.combine2bytesToU16(high, low) + EXTRA_LEN);
}
return dataU8s;
}
}

View File

@ -0,0 +1,55 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.task.IEsptouchGenerator;
import com.espressif.iot.esptouch.util.ByteUtil;
import java.net.InetAddress;
public class EsptouchGenerator implements IEsptouchGenerator {
private final byte[][] mGcBytes2;
private final byte[][] mDcBytes2;
/**
* Constructor of EsptouchGenerator, it will cost some time(maybe a bit
* much)
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param inetAddress the phone's or pad's local ip address allocated by Ap
* @param isSsidHiden whether the Ap's ssid is hidden
*/
public EsptouchGenerator(byte[] apSsid, byte[] apBssid, byte[] apPassword,
InetAddress inetAddress, boolean isSsidHiden) {
// generate guide code
GuideCode gc = new GuideCode();
char[] gcU81 = gc.getU8s();
mGcBytes2 = new byte[gcU81.length][];
for (int i = 0; i < mGcBytes2.length; i++) {
mGcBytes2[i] = ByteUtil.genSpecBytes(gcU81[i]);
}
// generate data code
DatumCode dc = new DatumCode(apSsid, apBssid, apPassword, inetAddress,
isSsidHiden);
char[] dcU81 = dc.getU8s();
mDcBytes2 = new byte[dcU81.length][];
for (int i = 0; i < mDcBytes2.length; i++) {
mDcBytes2[i] = ByteUtil.genSpecBytes(dcU81[i]);
}
}
@Override
public byte[][] getGCBytes2() {
return mGcBytes2;
}
@Override
public byte[][] getDCBytes2() {
return mDcBytes2;
}
}

View File

@ -0,0 +1,39 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.task.ICodeData;
import com.espressif.iot.esptouch.util.ByteUtil;
public class GuideCode implements ICodeData {
public static final int GUIDE_CODE_LEN = 4;
@Override
public byte[] getBytes() {
throw new RuntimeException("DataCode don't support getBytes()");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
char[] dataU8s = getU8s();
for (int i = 0; i < GUIDE_CODE_LEN; i++) {
String hexString = ByteUtil.convertU8ToHexString(dataU8s[i]);
sb.append("0x");
if (hexString.length() == 1) {
sb.append("0");
}
sb.append(hexString).append(" ");
}
return sb.toString();
}
@Override
public char[] getU8s() {
char[] guidesU8s = new char[GUIDE_CODE_LEN];
guidesU8s[0] = 515;
guidesU8s[1] = 514;
guidesU8s[2] = 513;
guidesU8s[3] = 512;
return guidesU8s;
}
}

View File

@ -0,0 +1,22 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.util.ByteUtil;
public class TouchData {
private final byte[] mData;
public TouchData(String string) {
mData = ByteUtil.getBytesByString(string);
}
public TouchData(byte[] data) {
if (data == null) {
throw new NullPointerException("data can't be null");
}
mData = data;
}
public byte[] getData() {
return mData;
}
}

View File

@ -0,0 +1,167 @@
package com.espressif.iot.esptouch.task;
import com.espressif.iot.esptouch.EsptouchTask;
public class EsptouchTaskParameter implements IEsptouchTaskParameter {
private static int _datagramCount = 0;
private long mIntervalGuideCodeMillisecond;
private long mIntervalDataCodeMillisecond;
private long mTimeoutGuideCodeMillisecond;
private long mTimeoutDataCodeMillisecond;
private int mTotalRepeatTime;
private int mEsptouchResultOneLen;
private int mEsptouchResultMacLen;
private int mEsptouchResultIpLen;
private int mEsptouchResultTotalLen;
private int mPortListening;
private int mTargetPort;
private int mWaitUdpReceivingMilliseond;
private int mWaitUdpSendingMillisecond;
private int mThresholdSucBroadcastCount;
private int mExpectTaskResultCount;
private boolean mBroadcast = true;
public EsptouchTaskParameter() {
mIntervalGuideCodeMillisecond = 8;
mIntervalDataCodeMillisecond = 8;
mTimeoutGuideCodeMillisecond = 2000;
mTimeoutDataCodeMillisecond = 4000;
mTotalRepeatTime = 1;
mEsptouchResultOneLen = 1;
mEsptouchResultMacLen = 6;
mEsptouchResultIpLen = 4;
mEsptouchResultTotalLen = 1 + 6 + 4;
mPortListening = 18266;
mTargetPort = 7001;
mWaitUdpReceivingMilliseond = 15000;
mWaitUdpSendingMillisecond = 45000;
mThresholdSucBroadcastCount = 1;
mExpectTaskResultCount = 1;
}
// the range of the result should be 1-100
private static int __getNextDatagramCount() {
return 1 + (_datagramCount++) % 100;
}
@Override
public long getIntervalGuideCodeMillisecond() {
return mIntervalGuideCodeMillisecond;
}
@Override
public long getIntervalDataCodeMillisecond() {
return mIntervalDataCodeMillisecond;
}
@Override
public long getTimeoutGuideCodeMillisecond() {
return mTimeoutGuideCodeMillisecond;
}
@Override
public long getTimeoutDataCodeMillisecond() {
return mTimeoutDataCodeMillisecond;
}
@Override
public long getTimeoutTotalCodeMillisecond() {
return mTimeoutGuideCodeMillisecond + mTimeoutDataCodeMillisecond;
}
@Override
public int getTotalRepeatTime() {
return mTotalRepeatTime;
}
@Override
public int getEsptouchResultOneLen() {
return mEsptouchResultOneLen;
}
@Override
public int getEsptouchResultMacLen() {
return mEsptouchResultMacLen;
}
@Override
public int getEsptouchResultIpLen() {
return mEsptouchResultIpLen;
}
@Override
public int getEsptouchResultTotalLen() {
return mEsptouchResultTotalLen;
}
@Override
public int getPortListening() {
return mPortListening;
}
// target hostname is : 234.1.1.1, 234.2.2.2, 234.3.3.3 to 234.100.100.100
@Override
public String getTargetHostname() {
if (mBroadcast) {
return "255.255.255.255";
} else {
int count = __getNextDatagramCount();
return "234." + count + "." + count + "." + count;
}
}
@Override
public int getTargetPort() {
return mTargetPort;
}
@Override
public int getWaitUdpReceivingMillisecond() {
return mWaitUdpReceivingMilliseond;
}
@Override
public int getWaitUdpSendingMillisecond() {
return mWaitUdpSendingMillisecond;
}
@Override
public int getWaitUdpTotalMillisecond() {
return mWaitUdpReceivingMilliseond + mWaitUdpSendingMillisecond;
}
@Override
public void setWaitUdpTotalMillisecond(int waitUdpTotalMillisecond) {
if (waitUdpTotalMillisecond < mWaitUdpReceivingMilliseond
+ getTimeoutTotalCodeMillisecond()) {
// if it happen, even one turn about sending udp broadcast can't be
// completed
throw new IllegalArgumentException(
"waitUdpTotalMillisecod is invalid, "
+ "it is less than mWaitUdpReceivingMilliseond + getTimeoutTotalCodeMillisecond()");
}
mWaitUdpSendingMillisecond = waitUdpTotalMillisecond
- mWaitUdpReceivingMilliseond;
}
@Override
public int getThresholdSucBroadcastCount() {
return mThresholdSucBroadcastCount;
}
@Override
public int getExpectTaskResultCount() {
return this.mExpectTaskResultCount;
}
@Override
public void setExpectTaskResultCount(int expectTaskResultCount) {
this.mExpectTaskResultCount = expectTaskResultCount;
}
@Override
public void setBroadcast(boolean broadcast) {
mBroadcast = broadcast;
}
}

View File

@ -0,0 +1,22 @@
package com.espressif.iot.esptouch.task;
/**
* the class used to represent some code to be transformed by UDP socket should implement the interface
*
* @author afunx
*/
public interface ICodeData {
/**
* Get the byte[] to be transformed.
*
* @return the byte[] to be transfromed
*/
byte[] getBytes();
/**
* Get the char[](u8[]) to be transfromed.
*
* @return the char[](u8) to be transformed
*/
char[] getU8s();
}

View File

@ -0,0 +1,17 @@
package com.espressif.iot.esptouch.task;
public interface IEsptouchGenerator {
/**
* Get guide code by the format of byte[][]
*
* @return guide code by the format of byte[][]
*/
byte[][] getGCBytes2();
/**
* Get data code by the format of byte[][]
*
* @return data code by the format of byte[][]
*/
byte[][] getDCBytes2();
}

View File

@ -0,0 +1,156 @@
package com.espressif.iot.esptouch.task;
public interface IEsptouchTaskParameter {
/**
* get interval millisecond for guide code(the time between each guide code sending)
*
* @return interval millisecond for guide code(the time between each guide code sending)
*/
long getIntervalGuideCodeMillisecond();
/**
* get interval millisecond for data code(the time between each data code sending)
*
* @return interval millisecond for data code(the time between each data code sending)
*/
long getIntervalDataCodeMillisecond();
/**
* get timeout millisecond for guide code(the time how much the guide code sending)
*
* @return timeout millisecond for guide code(the time how much the guide code sending)
*/
long getTimeoutGuideCodeMillisecond();
/**
* get timeout millisecond for data code(the time how much the data code sending)
*
* @return timeout millisecond for data code(the time how much the data code sending)
*/
long getTimeoutDataCodeMillisecond();
/**
* get timeout millisecond for total code(guide code and data code altogether)
*
* @return timeout millisecond for total code(guide code and data code altogether)
*/
long getTimeoutTotalCodeMillisecond();
/**
* get total repeat time for executing esptouch task
*
* @return total repeat time for executing esptouch task
*/
int getTotalRepeatTime();
/**
* the length of the Esptouch result 1st byte is the total length of ssid and
* password, the other 6 bytes are the device's bssid
*/
/**
* get esptouchResult length of one
*
* @return length of one
*/
int getEsptouchResultOneLen();
/**
* get esptouchResult length of mac
*
* @return length of mac
*/
int getEsptouchResultMacLen();
/**
* get esptouchResult length of ip
*
* @return length of ip
*/
int getEsptouchResultIpLen();
/**
* get esptouchResult total length
*
* @return total length
*/
int getEsptouchResultTotalLen();
/**
* get port for listening(used by server)
*
* @return port for listening(used by server)
*/
int getPortListening();
/**
* get target hostname
*
* @return target hostame(used by client)
*/
String getTargetHostname();
/**
* get target port
*
* @return target port(used by client)
*/
int getTargetPort();
/**
* get millisecond for waiting udp receiving(receiving without sending)
*
* @return millisecond for waiting udp receiving(receiving without sending)
*/
int getWaitUdpReceivingMillisecond();
/**
* get millisecond for waiting udp sending(sending including receiving)
*
* @return millisecond for waiting udep sending(sending including receiving)
*/
int getWaitUdpSendingMillisecond();
/**
* get millisecond for waiting udp sending and receiving
*
* @return millisecond for waiting udp sending and receiving
*/
int getWaitUdpTotalMillisecond();
/**
* set the millisecond for waiting udp sending and receiving
*
* @param waitUdpTotalMillisecond the millisecond for waiting udp sending and receiving
*/
void setWaitUdpTotalMillisecond(int waitUdpTotalMillisecond);
/**
* get the threshold for how many correct broadcast should be received
*
* @return the threshold for how many correct broadcast should be received
*/
int getThresholdSucBroadcastCount();
/**
* get the count of expect task results
*
* @return the count of expect task results
*/
int getExpectTaskResultCount();
/**
* set the count of expect task results
*
* @param expectTaskResultCount the count of expect task results
*/
void setExpectTaskResultCount(int expectTaskResultCount);
/**
* Set broadcast or multicast
*
* @param broadcast true is broadcast, false is multicast
*/
void setBroadcast(boolean broadcast);
}

View File

@ -0,0 +1,355 @@
package com.espressif.iot.esptouch.task;
import android.content.Context;
import android.os.Looper;
import android.util.Log;
import com.espressif.iot.esptouch.EsptouchResult;
import com.espressif.iot.esptouch.IEsptouchListener;
import com.espressif.iot.esptouch.IEsptouchResult;
import com.espressif.iot.esptouch.IEsptouchTask;
import com.espressif.iot.esptouch.protocol.EsptouchGenerator;
import com.espressif.iot.esptouch.protocol.TouchData;
import com.espressif.iot.esptouch.udp.UDPSocketClient;
import com.espressif.iot.esptouch.udp.UDPSocketServer;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.EspAES;
import com.espressif.iot.esptouch.util.EspNetUtil;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class __EsptouchTask implements __IEsptouchTask {
/**
* one indivisible data contain 3 9bits info
*/
private static final int ONE_DATA_LEN = 3;
private static final String TAG = "__EsptouchTask";
private final UDPSocketClient mSocketClient;
private final UDPSocketServer mSocketServer;
private final byte[] mApSsid;
private final byte[] mApPassword;
private final byte[] mApBssid;
private final boolean mIsSsidHidden;
private final Context mContext;
private volatile List<IEsptouchResult> mEsptouchResultList;
private volatile boolean mIsSuc = false;
private volatile boolean mIsInterrupt = false;
private volatile boolean mIsExecuted = false;
private AtomicBoolean mIsCancelled;
private IEsptouchTaskParameter mParameter;
private volatile Map<String, Integer> mBssidTaskSucCountMap;
private IEsptouchListener mEsptouchListener;
private Thread mTask;
public __EsptouchTask(Context context, TouchData apSsid, TouchData apBssid, TouchData apPassword, EspAES espAES,
IEsptouchTaskParameter parameter, boolean isSsidHidden) {
Log.i(TAG, "Welcome Esptouch " + IEsptouchTask.ESPTOUCH_VERSION);
mContext = context;
if (espAES == null) {
mApSsid = apSsid.getData();
mApPassword = apPassword.getData();
} else {
mApSsid = espAES.encrypt(apSsid.getData());
mApPassword = espAES.encrypt(apPassword.getData());
}
mApBssid = apBssid.getData();
mIsCancelled = new AtomicBoolean(false);
mSocketClient = new UDPSocketClient();
mParameter = parameter;
mSocketServer = new UDPSocketServer(mParameter.getPortListening(),
mParameter.getWaitUdpTotalMillisecond(), context);
mIsSsidHidden = isSsidHidden;
mEsptouchResultList = new ArrayList<>();
mBssidTaskSucCountMap = new HashMap<>();
}
private void __putEsptouchResult(boolean isSuc, String bssid, InetAddress inetAddress) {
synchronized (mEsptouchResultList) {
// check whether the result receive enough UDP response
boolean isTaskSucCountEnough = false;
Integer count = mBssidTaskSucCountMap.get(bssid);
if (count == null) {
count = 0;
}
++count;
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__putEsptouchResult(): count = " + count);
}
mBssidTaskSucCountMap.put(bssid, count);
isTaskSucCountEnough = count >= mParameter
.getThresholdSucBroadcastCount();
if (!isTaskSucCountEnough) {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__putEsptouchResult(): count = " + count
+ ", isn't enough");
}
return;
}
// check whether the result is in the mEsptouchResultList already
boolean isExist = false;
for (IEsptouchResult esptouchResultInList : mEsptouchResultList) {
if (esptouchResultInList.getBssid().equals(bssid)) {
isExist = true;
break;
}
}
// only add the result who isn't in the mEsptouchResultList
if (!isExist) {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__putEsptouchResult(): put one more result");
}
final IEsptouchResult esptouchResult = new EsptouchResult(isSuc,
bssid, inetAddress);
mEsptouchResultList.add(esptouchResult);
if (mEsptouchListener != null) {
mEsptouchListener.onEsptouchResultAdded(esptouchResult);
}
}
}
}
private List<IEsptouchResult> __getEsptouchResultList() {
synchronized (mEsptouchResultList) {
if (mEsptouchResultList.isEmpty()) {
EsptouchResult esptouchResultFail = new EsptouchResult(false,
null, null);
esptouchResultFail.setIsCancelled(mIsCancelled.get());
mEsptouchResultList.add(esptouchResultFail);
}
return mEsptouchResultList;
}
}
private synchronized void __interrupt() {
if (!mIsInterrupt) {
mIsInterrupt = true;
mSocketClient.interrupt();
mSocketServer.interrupt();
// interrupt the current Thread which is used to wait for udp response
if (mTask != null) {
mTask.interrupt();
mTask = null;
}
}
}
@Override
public void interrupt() {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "interrupt()");
}
mIsCancelled.set(true);
__interrupt();
}
private void __listenAsyn(final int expectDataLen) {
mTask = new Thread() {
public void run() {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__listenAsyn() start");
}
long startTimestamp = System.currentTimeMillis();
// byte[] apSsidAndPassword = ByteUtil.getBytesByString(mApSsid
// + mApPassword);
byte expectOneByte = (byte) (mApSsid.length + mApPassword.length + 9);
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "expectOneByte: " + (0 + expectOneByte));
}
byte receiveOneByte = -1;
byte[] receiveBytes = null;
while (mEsptouchResultList.size() < mParameter
.getExpectTaskResultCount() && !mIsInterrupt) {
receiveBytes = mSocketServer
.receiveSpecLenBytes(expectDataLen);
if (receiveBytes != null) {
receiveOneByte = receiveBytes[0];
} else {
receiveOneByte = -1;
}
if (receiveOneByte == expectOneByte) {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "receive correct broadcast");
}
// change the socket's timeout
long consume = System.currentTimeMillis()
- startTimestamp;
int timeout = (int) (mParameter
.getWaitUdpTotalMillisecond() - consume);
if (timeout < 0) {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "esptouch timeout");
}
break;
} else {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "mSocketServer's new timeout is "
+ timeout + " milliseconds");
}
mSocketServer.setSoTimeout(timeout);
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "receive correct broadcast");
}
if (receiveBytes != null) {
String bssid = ByteUtil.parseBssid(
receiveBytes,
mParameter.getEsptouchResultOneLen(),
mParameter.getEsptouchResultMacLen());
InetAddress inetAddress = EspNetUtil
.parseInetAddr(
receiveBytes,
mParameter
.getEsptouchResultOneLen()
+ mParameter
.getEsptouchResultMacLen(),
mParameter
.getEsptouchResultIpLen());
__putEsptouchResult(true, bssid, inetAddress);
}
}
} else {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "receive rubbish message, just ignore");
}
}
}
mIsSuc = mEsptouchResultList.size() >= mParameter
.getExpectTaskResultCount();
__EsptouchTask.this.__interrupt();
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__listenAsyn() finish");
}
}
};
mTask.start();
}
private boolean __execute(IEsptouchGenerator generator) {
long startTime = System.currentTimeMillis();
long currentTime = startTime;
long lastTime = currentTime - mParameter.getTimeoutTotalCodeMillisecond();
byte[][] gcBytes2 = generator.getGCBytes2();
byte[][] dcBytes2 = generator.getDCBytes2();
int index = 0;
while (!mIsInterrupt) {
if (currentTime - lastTime >= mParameter.getTimeoutTotalCodeMillisecond()) {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "send gc code ");
}
// send guide code
while (!mIsInterrupt
&& System.currentTimeMillis() - currentTime < mParameter
.getTimeoutGuideCodeMillisecond()) {
mSocketClient.sendData(gcBytes2,
mParameter.getTargetHostname(),
mParameter.getTargetPort(),
mParameter.getIntervalGuideCodeMillisecond());
// check whether the udp is send enough time
if (System.currentTimeMillis() - startTime > mParameter.getWaitUdpSendingMillisecond()) {
break;
}
}
lastTime = currentTime;
} else {
mSocketClient.sendData(dcBytes2, index, ONE_DATA_LEN,
mParameter.getTargetHostname(),
mParameter.getTargetPort(),
mParameter.getIntervalDataCodeMillisecond());
index = (index + ONE_DATA_LEN) % dcBytes2.length;
}
currentTime = System.currentTimeMillis();
// check whether the udp is send enough time
if (currentTime - startTime > mParameter.getWaitUdpSendingMillisecond()) {
break;
}
}
return mIsSuc;
}
private void __checkTaskValid() {
// !!!NOTE: the esptouch task could be executed only once
if (this.mIsExecuted) {
throw new IllegalStateException(
"the Esptouch task could be executed only once");
}
this.mIsExecuted = true;
}
@Override
public IEsptouchResult executeForResult() throws RuntimeException {
return executeForResults(1).get(0);
}
@Override
public boolean isCancelled() {
return this.mIsCancelled.get();
}
@Override
public List<IEsptouchResult> executeForResults(int expectTaskResultCount)
throws RuntimeException {
__checkTaskValid();
mParameter.setExpectTaskResultCount(expectTaskResultCount);
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "execute()");
}
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new RuntimeException(
"Don't call the esptouch Task at Main(UI) thread directly.");
}
InetAddress localInetAddress = EspNetUtil.getLocalInetAddress(mContext);
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "localInetAddress: " + localInetAddress);
}
// generator the esptouch byte[][] to be transformed, which will cost
// some time(maybe a bit much)
IEsptouchGenerator generator = new EsptouchGenerator(mApSsid, mApBssid,
mApPassword, localInetAddress, mIsSsidHidden);
// listen the esptouch result asyn
__listenAsyn(mParameter.getEsptouchResultTotalLen());
boolean isSuc = false;
for (int i = 0; i < mParameter.getTotalRepeatTime(); i++) {
isSuc = __execute(generator);
if (isSuc) {
return __getEsptouchResultList();
}
}
if (!mIsInterrupt) {
// wait the udp response without sending udp broadcast
try {
Thread.sleep(mParameter.getWaitUdpReceivingMillisecond());
} catch (InterruptedException e) {
// receive the udp broadcast or the user interrupt the task
if (this.mIsSuc) {
return __getEsptouchResultList();
} else {
this.__interrupt();
return __getEsptouchResultList();
}
}
this.__interrupt();
}
return __getEsptouchResultList();
}
@Override
public void setEsptouchListener(IEsptouchListener esptouchListener) {
mEsptouchListener = esptouchListener;
}
}

View File

@ -0,0 +1,55 @@
package com.espressif.iot.esptouch.task;
import com.espressif.iot.esptouch.IEsptouchListener;
import com.espressif.iot.esptouch.IEsptouchResult;
import java.util.List;
/**
* IEsptouchTask defined the task of esptouch should offer. INTERVAL here means
* the milliseconds of interval of the step. REPEAT here means the repeat times
* of the step.
*
* @author afunx
*/
public interface __IEsptouchTask {
/**
* Turn on or off the log.
*/
static final boolean DEBUG = true;
/**
* set the esptouch listener, when one device is connected to the Ap, it will be called back
*
* @param esptouchListener when one device is connected to the Ap, it will be called back
*/
void setEsptouchListener(IEsptouchListener esptouchListener);
/**
* Interrupt the Esptouch Task when User tap back or close the Application.
*/
void interrupt();
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
*
* @return the IEsptouchResult
* @throws RuntimeException
*/
IEsptouchResult executeForResult() throws RuntimeException;
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
*
* @param expectTaskResultCount the expect result count(if expectTaskResultCount <= 0,
* expectTaskResultCount = Integer.MAX_VALUE)
* @return the list of IEsptouchResult
* @throws RuntimeException
*/
List<IEsptouchResult> executeForResults(int expectTaskResultCount) throws RuntimeException;
boolean isCancelled();
}

View File

@ -0,0 +1,130 @@
package com.espressif.iot.esptouch.udp;
import android.util.Log;
import com.espressif.iot.esptouch.task.__IEsptouchTask;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/**
* this class is used to help send UDP data according to length
*
* @author afunx
*/
public class UDPSocketClient {
private static final String TAG = "UDPSocketClient";
private DatagramSocket mSocket;
private volatile boolean mIsStop;
private volatile boolean mIsClosed;
public UDPSocketClient() {
try {
this.mSocket = new DatagramSocket();
this.mIsStop = false;
this.mIsClosed = false;
} catch (SocketException e) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "SocketException");
}
e.printStackTrace();
}
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
public void interrupt() {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "USPSocketClient is interrupt");
}
this.mIsStop = true;
}
/**
* close the UDP socket
*/
public synchronized void close() {
if (!this.mIsClosed) {
this.mSocket.close();
this.mIsClosed = true;
}
}
/**
* send the data by UDP
*
* @param data the data to be sent
* @param targetPort the port of target
* @param interval the milliseconds to between each UDP sent
*/
public void sendData(byte[][] data, String targetHostName, int targetPort,
long interval) {
sendData(data, 0, data.length, targetHostName, targetPort, interval);
}
/**
* send the data by UDP
*
* @param data the data to be sent
* @param offset the offset which data to be sent
* @param count the count of the data
* @param targetPort the port of target
* @param interval the milliseconds to between each UDP sent
*/
public void sendData(byte[][] data, int offset, int count,
String targetHostName, int targetPort, long interval) {
if ((data == null) || (data.length <= 0)) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData(): data == null or length <= 0");
}
return;
}
for (int i = offset; !mIsStop && i < offset + count; i++) {
if (data[i].length == 0) {
continue;
}
try {
InetAddress targetInetAddress = InetAddress.getByName(targetHostName);
DatagramPacket localDatagramPacket = new DatagramPacket(
data[i], data[i].length, targetInetAddress, targetPort);
this.mSocket.send(localDatagramPacket);
} catch (UnknownHostException e) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData(): UnknownHostException");
}
e.printStackTrace();
mIsStop = true;
break;
} catch (IOException e) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData(): IOException, but just ignore it");
}
// for the Ap will make some troubles when the phone send too many UDP packets,
// but we don't expect the UDP packet received by others, so just ignore it
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData is Interrupted");
}
mIsStop = true;
break;
}
}
if (mIsStop) {
close();
}
}
}

View File

@ -0,0 +1,151 @@
package com.espressif.iot.esptouch.udp;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.util.Log;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Arrays;
public class UDPSocketServer {
private static final String TAG = "UDPSocketServer";
private final byte[] buffer;
private DatagramPacket mReceivePacket;
private DatagramSocket mServerSocket;
private Context mContext;
private WifiManager.MulticastLock mLock;
private volatile boolean mIsClosed;
/**
* Constructor of UDP Socket Server
*
* @param port the Socket Server port
* @param socketTimeout the socket read timeout
* @param context the context of the Application
*/
public UDPSocketServer(int port, int socketTimeout, Context context) {
this.mContext = context;
this.buffer = new byte[64];
this.mReceivePacket = new DatagramPacket(buffer, 64);
try {
this.mServerSocket = new DatagramSocket(null);
this.mServerSocket.setReuseAddress(true);
this.mServerSocket.bind(new InetSocketAddress(port));
this.mServerSocket.setSoTimeout(socketTimeout);
} catch (IOException e) {
Log.e(TAG, "IOException");
e.printStackTrace();
}
this.mIsClosed = false;
WifiManager manager = (WifiManager) mContext.getApplicationContext()
.getSystemService(Context.WIFI_SERVICE);
mLock = manager.createMulticastLock("test wifi");
Log.d(TAG, "mServerSocket is created, socket read timeout: "
+ socketTimeout + ", port: " + port);
}
private synchronized void acquireLock() {
if (mLock != null && !mLock.isHeld()) {
mLock.acquire();
}
}
private synchronized void releaseLock() {
if (mLock != null && mLock.isHeld()) {
try {
mLock.release();
} catch (Throwable th) {
// ignoring this exception, probably wakeLock was already released
}
}
}
/**
* Set the socket timeout in milliseconds
*
* @param timeout the timeout in milliseconds or 0 for no timeout.
* @return true whether the timeout is set suc
*/
public boolean setSoTimeout(int timeout) {
try {
this.mServerSocket.setSoTimeout(timeout);
return true;
} catch (SocketException e) {
e.printStackTrace();
}
return false;
}
/**
* Receive one byte from the port and convert it into String
*
* @return
*/
public byte receiveOneByte() {
Log.d(TAG, "receiveOneByte() entrance");
try {
acquireLock();
mServerSocket.receive(mReceivePacket);
Log.d(TAG, "receive: " + (mReceivePacket.getData()[0]));
return mReceivePacket.getData()[0];
} catch (IOException e) {
e.printStackTrace();
}
return Byte.MIN_VALUE;
}
/**
* Receive specific length bytes from the port and convert it into String
* 21,24,-2,52,-102,-93,-60
* 15,18,fe,34,9a,a3,c4
*
* @return
*/
public byte[] receiveSpecLenBytes(int len) {
Log.d(TAG, "receiveSpecLenBytes() entrance: len = " + len);
try {
acquireLock();
mServerSocket.receive(mReceivePacket);
byte[] recDatas = Arrays.copyOf(mReceivePacket.getData(), mReceivePacket.getLength());
Log.d(TAG, "received len : " + recDatas.length);
for (int i = 0; i < recDatas.length; i++) {
Log.e(TAG, "recDatas[" + i + "]:" + recDatas[i]);
}
Log.e(TAG, "receiveSpecLenBytes: " + new String(recDatas));
if (recDatas.length != len) {
Log.w(TAG,
"received len is different from specific len, return null");
return null;
}
return recDatas;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void interrupt() {
Log.i(TAG, "USPSocketServer is interrupt");
close();
}
public synchronized void close() {
if (!this.mIsClosed) {
Log.e(TAG, "mServerSocket is closed");
mServerSocket.close();
releaseLock();
this.mIsClosed = true;
}
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
}

View File

@ -0,0 +1,323 @@
package com.espressif.iot.esptouch.util;
import java.io.UnsupportedEncodingException;
import java.util.Random;
/**
* In Java, it don't support unsigned int, so we use char to replace uint8.
* The range of byte is [-128,127], and the range of char is [0,65535].
* So the byte could used to store the uint8.
* (We assume that the String could be mapped to assic)
*
* @author afunx
*/
public class ByteUtil {
public static final String ESPTOUCH_ENCODING_CHARSET = "UTF-8";
/**
* Put String to byte[]
*
* @param destbytes the byte[] of dest
* @param srcString the String of src
* @param destOffset the offset of byte[]
* @param srcOffset the offset of String
* @param count the count of dest, and the count of src as well
*/
public static void putString2bytes(byte[] destbytes, String srcString,
int destOffset, int srcOffset, int count) {
for (int i = 0; i < count; i++) {
destbytes[count + i] = srcString.getBytes()[i];
}
}
/**
* Convert uint8 into char( we treat char as uint8)
*
* @param uint8 the unit8 to be converted
* @return the byte of the unint8
*/
public static byte convertUint8toByte(char uint8) {
if (uint8 > Byte.MAX_VALUE - Byte.MIN_VALUE) {
throw new RuntimeException("Out of Boundary");
}
return (byte) uint8;
}
/**
* Convert char into uint8( we treat char as uint8 )
*
* @param b the byte to be converted
* @return the char(uint8)
*/
public static char convertByte2Uint8(byte b) {
// char will be promoted to int for char don't support & operator
// & 0xff could make negatvie value to positive
return (char) (b & 0xff);
}
/**
* Convert byte[] into char[]( we treat char[] as uint8[])
*
* @param bytes the byte[] to be converted
* @return the char[](uint8[])
*/
public static char[] convertBytes2Uint8s(byte[] bytes) {
int len = bytes.length;
char[] uint8s = new char[len];
for (int i = 0; i < len; i++) {
uint8s[i] = convertByte2Uint8(bytes[i]);
}
return uint8s;
}
/**
* Put byte[] into char[]( we treat char[] as uint8[])
*
* @param destUint8s the char[](uint8[]) array
* @param srcBytes the byte[]
* @param destOffset the offset of char[](uint8[])
* @param srcOffset the offset of byte[]
* @param count the count of dest, and the count of src as well
*/
public static void putbytes2Uint8s(char[] destUint8s, byte[] srcBytes,
int destOffset, int srcOffset, int count) {
for (int i = 0; i < count; i++) {
destUint8s[destOffset + i] = convertByte2Uint8(srcBytes[srcOffset
+ i]);
}
}
/**
* Convert byte to Hex String
*
* @param b the byte to be converted
* @return the Hex String
*/
public static String convertByte2HexString(byte b) {
char u8 = convertByte2Uint8(b);
return Integer.toHexString(u8);
}
/**
* Convert char(uint8) to Hex String
*
* @param u8 the char(uint8) to be converted
* @return the Hex String
*/
public static String convertU8ToHexString(char u8) {
return Integer.toHexString(u8);
}
/**
* Split uint8 to 2 bytes of high byte and low byte. e.g. 20 = 0x14 should
* be split to [0x01,0x04] 0x01 is high byte and 0x04 is low byte
*
* @param uint8 the char(uint8)
* @return the high and low bytes be split, byte[0] is high and byte[1] is
* low
*/
public static byte[] splitUint8To2bytes(char uint8) {
if (uint8 < 0 || uint8 > 0xff) {
throw new RuntimeException("Out of Boundary");
}
String hexString = Integer.toHexString(uint8);
byte low;
byte high;
if (hexString.length() > 1) {
high = (byte) Integer.parseInt(hexString.substring(0, 1), 16);
low = (byte) Integer.parseInt(hexString.substring(1, 2), 16);
} else {
high = 0;
low = (byte) Integer.parseInt(hexString.substring(0, 1), 16);
}
byte[] result = new byte[]{high, low};
return result;
}
/**
* Combine 2 bytes (high byte and low byte) to one whole byte
*
* @param high the high byte
* @param low the low byte
* @return the whole byte
*/
public static byte combine2bytesToOne(byte high, byte low) {
if (high < 0 || high > 0xf || low < 0 || low > 0xf) {
throw new RuntimeException("Out of Boundary");
}
return (byte) (high << 4 | low);
}
/**
* Combine 2 bytes (high byte and low byte) to
*
* @param high the high byte
* @param low the low byte
* @return the char(u8)
*/
public static char combine2bytesToU16(byte high, byte low) {
char highU8 = convertByte2Uint8(high);
char lowU8 = convertByte2Uint8(low);
return (char) (highU8 << 8 | lowU8);
}
/**
* Generate the random byte to be sent
*
* @return the random byte
*/
private static byte randomByte() {
return (byte) (127 - new Random().nextInt(256));
}
/**
* Generate the random byte to be sent
*
* @param len the len presented by u8
* @return the byte[] to be sent
*/
public static byte[] randomBytes(char len) {
byte[] data = new byte[len];
for (int i = 0; i < len; i++) {
data[i] = randomByte();
}
return data;
}
public static byte[] genSpecBytes(char len) {
byte[] data = new byte[len];
for (int i = 0; i < len; i++) {
data[i] = '1';
}
return data;
}
/**
* Generate the random byte to be sent
*
* @param len the len presented by byte
* @return the byte[] to be sent
*/
public static byte[] randomBytes(byte len) {
char u8 = convertByte2Uint8(len);
return randomBytes(u8);
}
/**
* Generate the specific byte to be sent
*
* @param len the len presented by byte
* @return the byte[]
*/
public static byte[] genSpecBytes(byte len) {
char u8 = convertByte2Uint8(len);
return genSpecBytes(u8);
}
public static String parseBssid(byte[] bssidBytes, int offset, int count) {
byte[] bytes = new byte[count];
System.arraycopy(bssidBytes, offset, bytes, 0, count);
return parseBssid(bytes);
}
/**
* parse "24,-2,52,-102,-93,-60" to "18,fe,34,9a,a3,c4"
* parse the bssid from hex to String
*
* @param bssidBytes the hex bytes bssid, e.g. {24,-2,52,-102,-93,-60}
* @return the String of bssid, e.g. 18fe349aa3c4
*/
public static String parseBssid(byte[] bssidBytes) {
StringBuilder sb = new StringBuilder();
int k;
String hexK;
String str;
for (byte bssidByte : bssidBytes) {
k = 0xff & bssidByte;
hexK = Integer.toHexString(k);
str = ((k < 16) ? ("0" + hexK) : (hexK));
System.out.println(str);
sb.append(str);
}
return sb.toString();
}
/**
* @param string the string to be used
* @return the byte[] of String according to {@link #ESPTOUCH_ENCODING_CHARSET}
*/
public static byte[] getBytesByString(String string) {
try {
return string.getBytes(ESPTOUCH_ENCODING_CHARSET);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("the charset is invalid");
}
}
private static void test_splitUint8To2bytes() {
// 20 = 0x14
byte[] result = splitUint8To2bytes((char) 20);
if (result[0] == 1 && result[1] == 4) {
System.out.println("test_splitUint8To2bytes(): pass");
} else {
System.out.println("test_splitUint8To2bytes(): fail");
}
}
private static void test_combine2bytesToOne() {
byte high = 0x01;
byte low = 0x04;
if (combine2bytesToOne(high, low) == 20) {
System.out.println("test_combine2bytesToOne(): pass");
} else {
System.out.println("test_combine2bytesToOne(): fail");
}
}
private static void test_convertChar2Uint8() {
byte b1 = 'a';
// -128: 1000 0000 should be 128 in unsigned char
// -1: 1111 1111 should be 255 in unsigned char
byte b2 = (byte) -128;
byte b3 = (byte) -1;
if (convertByte2Uint8(b1) == 97 && convertByte2Uint8(b2) == 128
&& convertByte2Uint8(b3) == 255) {
System.out.println("test_convertChar2Uint8(): pass");
} else {
System.out.println("test_convertChar2Uint8(): fail");
}
}
private static void test_convertUint8toByte() {
char c1 = 'a';
// 128: 1000 0000 should be -128 in byte
// 255: 1111 1111 should be -1 in byte
char c2 = 128;
char c3 = 255;
if (convertUint8toByte(c1) == 97 && convertUint8toByte(c2) == -128
&& convertUint8toByte(c3) == -1) {
System.out.println("test_convertUint8toByte(): pass");
} else {
System.out.println("test_convertUint8toByte(): fail");
}
}
private static void test_parseBssid() {
byte b[] = {15, -2, 52, -102, -93, -60};
if (parseBssid(b).equals("0ffe349aa3c4")) {
System.out.println("test_parseBssid(): pass");
} else {
System.out.println("test_parseBssid(): fail");
}
}
public static void main(String args[]) {
test_convertUint8toByte();
test_convertChar2Uint8();
test_splitUint8To2bytes();
test_combine2bytesToOne();
test_parseBssid();
}
}

View File

@ -0,0 +1,63 @@
package com.espressif.iot.esptouch.util;
import java.util.zip.Checksum;
public class CRC8 implements Checksum {
private static final short[] crcTable = new short[256];
private static final short CRC_POLYNOM = 0x8c;
private static final short CRC_INITIAL = 0x00;
static {
for (int dividend = 0; dividend < 256; dividend++) {
int remainder = dividend;// << 8;
for (int bit = 0; bit < 8; ++bit)
if ((remainder & 0x01) != 0)
remainder = (remainder >>> 1) ^ CRC_POLYNOM;
else
remainder >>>= 1;
crcTable[dividend] = (short) remainder;
}
}
private final short init;
private short value;
public CRC8() {
this.value = this.init = CRC_INITIAL;
}
@Override
public void update(byte[] buffer, int offset, int len) {
for (int i = 0; i < len; i++) {
int data = buffer[offset + i] ^ value;
value = (short) (crcTable[data & 0xff] ^ (value << 8));
}
}
/**
* Updates the current checksum with the specified array of bytes.
* Equivalent to calling <code>update(buffer, 0, buffer.length)</code>.
*
* @param buffer the byte array to update the checksum with
*/
public void update(byte[] buffer) {
update(buffer, 0, buffer.length);
}
@Override
public void update(int b) {
update(new byte[]{(byte) b}, 0, 1);
}
@Override
public long getValue() {
return value & 0xff;
}
@Override
public void reset() {
value = init;
}
}

View File

@ -0,0 +1,104 @@
package com.espressif.iot.esptouch.util;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EspAES {
private static final String TRANSFORMATION_DEFAULT = "AES/ECB/PKCS5Padding";
private final byte[] mKey;
private final byte[] mIV;
private final String mTransformation;
private Cipher mEncryptCipher;
private Cipher mDecryptCipher;
public EspAES(byte[] key) {
this(key, null, TRANSFORMATION_DEFAULT);
}
public EspAES(byte[] key, String transformation) {
this(key, null, transformation);
}
public EspAES(byte[] key, byte[] iv) {
this(key, iv, TRANSFORMATION_DEFAULT);
}
public EspAES(byte[] key, byte[] iv, String transformation) {
mKey = key;
mIV = iv;
mTransformation = transformation;
mEncryptCipher = createEncryptCipher();
mDecryptCipher = createDecryptCipher();
}
private Cipher createEncryptCipher() {
try {
Cipher cipher = Cipher.getInstance(mTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(mKey, "AES");
if (mIV == null) {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
} else {
IvParameterSpec parameterSpec = new IvParameterSpec(mIV);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, parameterSpec);
}
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException
e) {
e.printStackTrace();
}
return null;
}
private Cipher createDecryptCipher() {
try {
Cipher cipher = Cipher.getInstance(mTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(mKey, "AES");
if (mIV == null) {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
} else {
IvParameterSpec parameterSpec = new IvParameterSpec(mIV);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, parameterSpec);
}
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException
e) {
e.printStackTrace();
}
return null;
}
public byte[] encrypt(byte[] content) {
try {
return mEncryptCipher.doFinal(content);
} catch (BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
public byte[] decrypt(byte[] content) {
try {
return mDecryptCipher.doFinal(content);
} catch (BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,92 @@
package com.espressif.iot.esptouch.util;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class EspNetUtil {
/**
* get the local ip address by Android System
*
* @param context the context
* @return the local ip addr allocated by Ap
*/
public static InetAddress getLocalInetAddress(Context context) {
WifiManager wm = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wm.getConnectionInfo();
int localAddrInt = wifiInfo.getIpAddress();
String localAddrStr = __formatString(localAddrInt);
InetAddress localInetAddr = null;
try {
localInetAddr = InetAddress.getByName(localAddrStr);
} catch (UnknownHostException e) {
e.printStackTrace();
}
return localInetAddr;
}
private static String __formatString(int value) {
String strValue = "";
byte[] ary = __intToByteArray(value);
for (int i = ary.length - 1; i >= 0; i--) {
strValue += (ary[i] & 0xFF);
if (i > 0) {
strValue += ".";
}
}
return strValue;
}
private static byte[] __intToByteArray(int value) {
byte[] b = new byte[4];
for (int i = 0; i < 4; i++) {
int offset = (b.length - 1 - i) * 8;
b[i] = (byte) ((value >>> offset) & 0xFF);
}
return b;
}
/**
* parse InetAddress
*
* @param inetAddrBytes
* @return
*/
public static InetAddress parseInetAddr(byte[] inetAddrBytes, int offset,
int count) {
InetAddress inetAddress = null;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(Integer.toString(inetAddrBytes[offset + i] & 0xff));
if (i != count - 1) {
sb.append('.');
}
}
try {
inetAddress = InetAddress.getByName(sb.toString());
} catch (UnknownHostException e) {
e.printStackTrace();
}
return inetAddress;
}
/**
* parse bssid
*
* @param bssid the bssid like aa:bb:cc:dd:ee:ff
* @return byte converted from bssid
*/
public static byte[] parseBssid2bytes(String bssid) {
String bssidSplits[] = bssid.split(":");
byte[] result = new byte[bssidSplits.length];
for (int i = 0; i < bssidSplits.length; i++) {
result[i] = (byte) Integer.parseInt(bssidSplits[i], 16);
}
return result;
}
}

View File

@ -0,0 +1,237 @@
package de.mc8051.fluttersmartconfig;
import android.app.Activity;
import android.content.Context;
import android.Manifest;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.content.pm.PackageManager;
import com.espressif.iot.esptouch.EsptouchTask;
import com.espressif.iot.esptouch.IEsptouchResult;
import com.espressif.iot.esptouch.IEsptouchTask;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.EspNetUtil;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONArray;
import java.util.ArrayList;
import java.util.List;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/** FlutterSmartconfigPlugin */
public class FlutterSmartconfigPlugin implements MethodCallHandler {
private IEsptouchTask mEsptouchTask;
private Context moContext;
private Activity moActivity;
private FlutterSmartconfigPlugin(Activity poActivity) {
this.moActivity = poActivity;
this.moContext = poActivity.getApplicationContext();
}
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "plugins.flutter.io/flutter_smartconfig");
channel.setMethodCallHandler(new FlutterSmartconfigPlugin(registrar.activity()));
}
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("startSmartConfig")) {
String ssid = call.argument("ssid");
String bssid = call.argument("bssid");
String pass = call.argument("pass");
String deviceCount = call.argument("deviceCount");
String broadcast = call.argument("broadcast");
startSmartConfig(ssid, bssid, pass, deviceCount, broadcast, result);
} else if (call.method.equals("stopSmartConfig")) {
stopSmartConfig();
} else if (call.method.equals("getConnectedWiFiInfo")) {
getWifiInfo(result);
} else {
result.notImplemented();
}
}
private void getWifiInfo(Result result) {
final ConnectivityManager connManager = (ConnectivityManager) moContext.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
if (activeNetwork != null && activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
final WifiManager wifiManager = (WifiManager) moContext.getSystemService(Context.WIFI_SERVICE);
final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo != null) {
final String ssid = wifiInfo.getSSID().replaceAll("^\"|\"$", "");
final String bssid = wifiInfo.getBSSID();
String is5G = "unknow";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int frequency = wifiInfo.getFrequency();
if (frequency > 4900 && frequency < 5900) {
// Connected 5G wifi. Device does not support 5G
is5G = "yes";
} else {
is5G = "no";
}
}
JSONObject re = new JSONObject();
try {
re.put("ssid", ssid);
re.put("bssid", bssid);
re.put("is5G", is5G);
} catch (JSONException ex) {
result.error("getWifiInfo", ex.getMessage(), null);
return;
}
result.success(re.toString());
} else {
result.error("getWifiInfo", "Unable to obtain WiFi details", null);
}
} else {
result.error("getWifiInfo", "Not connected to WiFi", null);
}
}
public void stopSmartConfig() {
if (mEsptouchTask != null) {
mEsptouchTask.interrupt();
}
}
private void startSmartConfig(String ssid, String bssid, String pass, String deviceCount, String broadcast, final Result resultResp) {
stopSmartConfig();
byte[] apSsid = ByteUtil.getBytesByString(ssid);
byte[] apBssid = EspNetUtil.parseBssid2bytes(bssid);
byte[] apPassword = ByteUtil.getBytesByString(pass);
byte[] deviceCountData = deviceCount.getBytes();
byte[] broadcastData = broadcast.getBytes();
new EsptouchAsyncTask4(new TaskListener() {
@Override
public void onFinished(List < IEsptouchResult > result) {
// Do Something after the task has finished
try {
IEsptouchResult firstResult = result.get(0);
if (!firstResult.isCancelled()) {
if (firstResult.isSuc()) {
StringBuilder sb = new StringBuilder();
JSONArray jsonArray = new JSONArray();
for (IEsptouchResult resultInList: result) {
if (!resultInList.isCancelled() && resultInList.getBssid() != null) {
sb.append("Esptouch success, bssid = ")
.append(resultInList.getBssid())
.append(", InetAddress = ")
.append(resultInList.getInetAddress().getHostAddress())
.append("\n");
JSONObject re = new JSONObject();
re.put("bssid", resultInList.getBssid());
re.put("ip", resultInList.getInetAddress().getHostAddress());
jsonArray.put(re);
}
}
JSONObject configureDeviceObj = new JSONObject();
configureDeviceObj.put("devices", jsonArray);
resultResp.success(configureDeviceObj.toString());
} else {
resultResp.error("startSmartConfig", "Esptouch fail", null);
}
} else {
resultResp.error("startSmartConfig", "Esptouch cancelled", null);
}
} catch (Exception err) {
resultResp.error("startSmartConfig", err.getMessage(), null);
}
}
}).execute(apSsid, apBssid, apPassword, deviceCountData, broadcastData);
}
public interface TaskListener {
public void onFinished(List < IEsptouchResult > result);
}
private class EsptouchAsyncTask4 extends AsyncTask < byte[], Void, List < IEsptouchResult >> {
private final TaskListener taskListener;
public EsptouchAsyncTask4(TaskListener listener) {
// The listener reference is passed in through the constructor
this.taskListener = listener;
}
private final Object mLock = new Object();
@Override
protected void onPreExecute() {
}
@Override
protected List < IEsptouchResult > doInBackground(byte[]...params) {
int taskResultCount;
synchronized(mLock) {
byte[] apSsid = params[0];
byte[] apBssid = params[1];
byte[] apPassword = params[2];
byte[] deviceCountData = params[3];
byte[] broadcastData = params[4];
taskResultCount = deviceCountData.length == 0 ? -1 : Integer.parseInt(new String(deviceCountData));
mEsptouchTask = new EsptouchTask(apSsid, apBssid, apPassword, moContext);
mEsptouchTask.setPackageBroadcast(broadcastData[0] == 1); // true is broadcast, false is multicast
}
List < IEsptouchResult > resultList = mEsptouchTask.executeForResults(taskResultCount);
return resultList;
}
@Override
protected void onPostExecute(List < IEsptouchResult > result) {
IEsptouchResult firstResult = result.get(0);
// check whether the task is cancelled and no results received
if (!firstResult.isCancelled()) {
if (this.taskListener != null) {
// And if it is we call the callback function on it.
this.taskListener.onFinished(result);
}
}
}
}
}

71
example/.gitignore vendored Normal file
View File

@ -0,0 +1,71 @@
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

10
example/.metadata Normal file
View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
channel: beta
project_type: app

16
example/README.md Normal file
View File

@ -0,0 +1,16 @@
# flutter_smartconfig_example
Demonstrates how to use the flutter_smartconfig plugin.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -0,0 +1,61 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 27
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "de.mc8051.fluttersmartconfigexample"
minSdkVersion 16
targetSdkVersion 27
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

View File

@ -0,0 +1,39 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.mc8051.fluttersmartconfigexample">
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="flutter_smartconfig_example"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,13 @@
package de.mc8051.fluttersmartconfigexample;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>

View File

@ -0,0 +1,29 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1536M

View File

@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

View File

@ -0,0 +1,15 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@ -0,0 +1,510 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
97C146F21CF9000F007C117D /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0910;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = S8QB4VV633;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = de.mc8051.flutterSmartconfigExample;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = de.mc8051.flutterSmartconfigExample;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = de.mc8051.flutterSmartconfigExample;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,6 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end

View File

@ -0,0 +1,13 @@
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

View File

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
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.

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>flutter_smartconfig_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,9 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

187
example/lib/main.dart Normal file
View File

@ -0,0 +1,187 @@
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_smartconfig/flutter_smartconfig.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ESP Onetouch Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'ESP Onetouch Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _bssidFilter = new TextEditingController();
final TextEditingController _ssidFilter = new TextEditingController();
final TextEditingController _passwordFilter = new TextEditingController();
bool _isLoading = false;
String _ssid = "";
String _bssid = "";
String _password = "";
_MyHomePageState() {
_ssidFilter.addListener(_ssidListen);
_passwordFilter.addListener(_passwordListen);
_bssidFilter.addListener(_bssidListen);
}
void _ssidListen() {
if (_ssidFilter.text.isEmpty) {
_ssid = "";
} else {
_ssid = _ssidFilter.text;
}
}
void _bssidListen() {
if (_bssidFilter.text.isEmpty) {
_bssid = "";
} else {
_bssid = _bssidFilter.text;
}
}
void _passwordListen() {
if (_passwordFilter.text.isEmpty) {
_password = "";
} else {
_password = _passwordFilter.text;
}
}
Future<void> _configureEsp() async {
setState(() {
_isLoading = true;
});
dynamic data = await FlutterSmartconfig.configureEsp(
ssid: _ssid, bssid: _bssid, password: _password);
print(data);
setState(() {
_isLoading = false;
});
}
Future<void> _getConnectedWiFiInfo() async {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler().requestPermissions([
PermissionGroup.location,
PermissionGroup.locationAlways,
PermissionGroup.locationWhenInUse
]);
if (permissions[PermissionGroup.location] != PermissionStatus.granted ||
permissions[PermissionGroup.locationAlways] !=
PermissionStatus.granted ||
permissions[PermissionGroup.locationWhenInUse] !=
PermissionStatus.granted) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Fehler!"),
content: new Text(
"Wir benötigen Zugriff auf deinen Standort um auf die WLAN Funktion deines Betriessystems zugreifen zu können!"),
actions: <Widget>[
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
return;
}
Map<String, String> data = await FlutterSmartconfig.getConnectedWiFiInfo();
setState(() {
_ssidFilter.text = data["ssid"];
_bssidFilter.text = data["bssid"];
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: _isLoading
? Container(
child: Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Colors.lightBlue),
),
),
color: Colors.white.withOpacity(0.8),
)
: new Container(
padding: new EdgeInsets.all(10.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Container(height: 10),
new Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("ESP Touch v0.3.7.0"),
new TextField(
controller: _ssidFilter,
decoration:
new InputDecoration(labelText: 'ssid'),
),
new TextField(
controller: _bssidFilter,
decoration:
new InputDecoration(labelText: 'bssid'),
),
RaisedButton(
child: Text('Get Connected WiFi details'),
onPressed: _getConnectedWiFiInfo,
)
])),
new Container(
child: new TextField(
controller: _passwordFilter,
decoration:
new InputDecoration(labelText: 'Password'),
),
),
new RaisedButton(
child: new Text('Configure ESP'),
onPressed: _configureEsp,
),
],
))) // This trailing comma makes auto-formatting nicer for build methods.
);
}
}

64
example/pubspec.yaml Normal file
View File

@ -0,0 +1,64 @@
name: flutter_smartconfig_example
description: Demonstrates how to use the flutter_smartconfig plugin.
publish_to: 'none'
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
permission_handler: ^2.1.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_smartconfig:
path: ../
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.io/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.io/custom-fonts/#from-packages

View File

@ -0,0 +1,27 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_smartconfig_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data.startsWith('Running on:'),
),
findsOneWidget,
);
});
}

19
flutter_smartconfig.iml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
</component>
</module>

36
ios/.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig

0
ios/Assets/.gitkeep Normal file
View File

View File

@ -0,0 +1,4 @@
#import <Flutter/Flutter.h>
@interface FlutterSmartconfigPlugin : NSObject<FlutterPlugin>
@end

View File

@ -0,0 +1,20 @@
#import "FlutterSmartconfigPlugin.h"
@implementation FlutterSmartconfigPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"flutter_smartconfig"
binaryMessenger:[registrar messenger]];
FlutterSmartconfigPlugin* instance = [[FlutterSmartconfigPlugin alloc] init];
[registrar addMethodCallDelegate:instance channel:channel];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else {
result(FlutterMethodNotImplemented);
}
}
@end

View File

@ -0,0 +1,21 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'flutter_smartconfig'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.description = <<-DESC
A new flutter plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.ios.deployment_target = '8.0'
end

View File

@ -0,0 +1,75 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'dart:convert';
import 'dart:io' show Platform;
// Note support only Android
// for iOS https://github.com/lou-lan/SmartConfig
class FlutterSmartconfig {
static const platform = const MethodChannel("plugins.flutter.io/flutter_smartconfig");
static Future<dynamic> configureEsp(
{String ssid, String bssid, String password}) async {
if (Platform.isAndroid) {
try {
// Change if required.
const String deviceCount = "1"; // the expect result count
const String broadcast = "1"; // broadcast or multicast
const Duration _kLongTimeout = const Duration(seconds: 20);
final String result =
await platform.invokeMethod('startSmartConfig', <String, dynamic>{
'ssid': ssid,
'bssid': bssid,
'pass': password,
'deviceCount': deviceCount,
'broadcast': broadcast,
}).timeout(_kLongTimeout);
final parsed = json.decode(result);
final devices = parsed["devices"];
return Future<dynamic>(() {
return devices;
});
} on PlatformException catch (e) {
return Future<Map<String, String>>(() {
throw new PlatformException(
code: "Failed to configure: '${e.message}'.");
});
}
} else {
return Future<Map<String, String>>(() {
throw new PlatformException(code: "only Android is supported");
});
}
}
// Build.VERSION.SDK_INT >= 28 needs Manifest.permission.ACCESS_COARSE_LOCATION
static Future<Map<String, String>> getConnectedWiFiInfo() async {
if (Platform.isAndroid) {
try {
String wiFiInfo = await platform.invokeMethod('getConnectedWiFiInfo');
final parsed = json.decode(wiFiInfo);
return Future<Map<String, String>>(() {
return {
"ssid": parsed["ssid"],
"bssid": parsed["bssid"],
"is5G": parsed["is5G"]
};
});
} on PlatformException catch (e) {
return Future<Map<String, String>>(() {
throw new PlatformException(
code: "Failed to get connected WiFi name: '${e.message}'.");
});
}
} else {
return Future<Map<String, String>>(() {
throw new PlatformException(code: "only Android is supported");
});
}
}
}

56
pubspec.yaml Normal file
View File

@ -0,0 +1,56 @@
name: flutter_smartconfig
description: A new flutter plugin project.
version: 0.0.1
author:
homepage:
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# This section identifies this Flutter project as a plugin project.
# The androidPackage and pluginClass identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
androidPackage: de.mc8051.fluttersmartconfig
pluginClass: FlutterSmartconfigPlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.io/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.io/custom-fonts/#from-packages