| | |
| | | |
| | | # Created by https://www.toptal.com/developers/gitignore/api/android |
| | | # Edit at https://www.toptal.com/developers/gitignore?templates=android |
| | | |
| | | ### Android ### |
| | | # Built application files |
| | | *.apk |
| | | *.aar |
| | | *.ap_ |
| | | *.aab |
| | | |
| | | # Files for the Dalvik VM |
| | | # Files for the ART/Dalvik VM |
| | | *.dex |
| | | |
| | | # Java class files |
| | |
| | | # Generated files |
| | | bin/ |
| | | gen/ |
| | | out/ |
| | | # Uncomment the following line in case you need and you don't have the release build type files in your app |
| | | # release/ |
| | | |
| | | # Gradle files |
| | | .gradle/ |
| | |
| | | # Proguard folder generated by Eclipse |
| | | proguard/ |
| | | |
| | | #Log Files |
| | | # Log Files |
| | | *.log |
| | | |
| | | # Android Studio Navigation editor temp files |
| | | .navigation/ |
| | | |
| | | # Android Studio captures folder |
| | | captures/ |
| | | |
| | | # IntelliJ |
| | | *.iml |
| | | .idea/workspace.xml |
| | | .idea/tasks.xml |
| | | .idea/gradle.xml |
| | | .idea/assetWizardSettings.xml |
| | | .idea/dictionaries |
| | | .idea/libraries |
| | | .idea/jarRepositories.xml |
| | | # Android Studio 3 in .gitignore file. |
| | | .idea/caches |
| | | .idea/modules.xml |
| | | # Comment next line if keeping position of elements in Navigation Editor is relevant for you |
| | | .idea/navEditor.xml |
| | | |
| | | # Keystore files |
| | | # Uncomment the following lines if you do not want to check your keystore files in. |
| | | #*.jks |
| | | #*.keystore |
| | | |
| | | # External native build folder generated in Android Studio 2.2 and later |
| | | .externalNativeBuild |
| | | .cxx/ |
| | | |
| | | # Google Services (e.g. APIs or Firebase) |
| | | # google-services.json |
| | | |
| | | # Freeline |
| | | freeline.py |
| | | freeline/ |
| | | freeline_project_description.json |
| | | |
| | | # fastlane |
| | | fastlane/report.xml |
| | | fastlane/Preview.html |
| | | fastlane/screenshots |
| | | fastlane/test_output |
| | | fastlane/readme.md |
| | | |
| | | # Version control |
| | | vcs.xml |
| | | |
| | | # lint |
| | | lint/intermediates/ |
| | | lint/generated/ |
| | | lint/outputs/ |
| | | lint/tmp/ |
| | | # lint/reports/ |
| | | |
| | | # Android Profiling |
| | | *.hprof |
| | | |
| | | ### Android Patch ### |
| | | gen-external-apklibs |
| | | output.json |
| | | |
| | | # Replacement of .externalNativeBuild directories introduced |
| | | # with Android Studio 3.5. |
| | | |
| | | # End of https://www.toptal.com/developers/gitignore/api/android |
New file |
| | |
| | | *.iml |
| | | .gradle |
| | | /local.properties |
| | | /.idea/caches |
| | | /.idea/libraries |
| | | /.idea/modules.xml |
| | | /.idea/workspace.xml |
| | | /.idea/navEditor.xml |
| | | /.idea/assetWizardSettings.xml |
| | | .DS_Store |
| | | /build |
| | | /captures |
| | | .externalNativeBuild |
| | | .cxx |
| | | local.properties |
New file |
| | |
| | | # Default ignored files
|
| | | /shelf/
|
| | | /workspace.xml
|
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?>
|
| | | <project version="4">
|
| | | <component name="CompilerConfiguration">
|
| | | <bytecodeTargetLevel target="11" />
|
| | | </component>
|
| | | </project> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?>
|
| | | <project version="4">
|
| | | <component name="GradleMigrationSettings" migrationVersion="1" />
|
| | | <component name="GradleSettings">
|
| | | <option name="linkedExternalProjectsSettings">
|
| | | <GradleProjectSettings>
|
| | | <option name="testRunner" value="PLATFORM" />
|
| | | <option name="distributionType" value="DEFAULT_WRAPPED" />
|
| | | <option name="externalProjectPath" value="$PROJECT_DIR$" />
|
| | | <option name="modules">
|
| | | <set>
|
| | | <option value="$PROJECT_DIR$" />
|
| | | <option value="$PROJECT_DIR$/app" />
|
| | | <option value="$PROJECT_DIR$/hdl-common" />
|
| | | <option value="$PROJECT_DIR$/hdl-connect" />
|
| | | <option value="$PROJECT_DIR$/hdl-socket" />
|
| | | </set>
|
| | | </option>
|
| | | <option name="resolveModulePerSourceSet" value="false" />
|
| | | <option name="useQualifiedModuleNames" value="true" />
|
| | | </GradleProjectSettings>
|
| | | </option>
|
| | | </component>
|
| | | </project> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?>
|
| | | <project version="4">
|
| | | <component name="RemoteRepositoriesConfiguration">
|
| | | <remote-repository>
|
| | | <option name="id" value="central" />
|
| | | <option name="name" value="Maven Central repository" />
|
| | | <option name="url" value="https://repo1.maven.org/maven2" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="jboss.community" />
|
| | | <option name="name" value="JBoss Community repository" />
|
| | | <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="maven" />
|
| | | <option name="name" value="maven" />
|
| | | <option name="url" value="https://maven.aliyun.com/repository/central" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="maven2" />
|
| | | <option name="name" value="maven2" />
|
| | | <option name="url" value="https://maven.aliyun.com/repository/google" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="maven5" />
|
| | | <option name="name" value="maven5" />
|
| | | <option name="url" value="https://maven.aliyun.com/repository/public/" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="maven4" />
|
| | | <option name="name" value="maven4" />
|
| | | <option name="url" value="https://maven.aliyun.com/repository/gradle-plugin" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="maven3" />
|
| | | <option name="name" value="maven3" />
|
| | | <option name="url" value="https://maven.aliyun.com/repository/jcenter" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="maven9" />
|
| | | <option name="name" value="maven9" />
|
| | | <option name="url" value="https://jitpack.io" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="maven8" />
|
| | | <option name="name" value="maven8" />
|
| | | <option name="url" value="https://maven.aliyun.com/repository/public" />
|
| | | </remote-repository>
|
| | | <remote-repository>
|
| | | <option name="id" value="Google" />
|
| | | <option name="name" value="Google" />
|
| | | <option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
| | | </remote-repository>
|
| | | </component>
|
| | | </project> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?>
|
| | | <project version="4">
|
| | | <component name="DesignSurface">
|
| | | <option name="filePathToZoomLevelMap">
|
| | | <map>
|
| | | <entry key="..\:/job/me/Android/HDLSDK/app/src/main/res/layout/activity_main.xml" value="0.1" />
|
| | | <entry key="..\:/job/me/Android/HDLSDK/app/src/main/res/layout/demo_item.xml" value="0.28958333333333336" />
|
| | | </map>
|
| | | </option>
|
| | | </component>
|
| | | <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
|
| | | <output url="file://$PROJECT_DIR$/build/classes" />
|
| | | </component>
|
| | | <component name="ProjectType">
|
| | | <option name="id" value="Android" />
|
| | | </component>
|
| | | </project> |
New file |
| | |
| | | plugins { |
| | | id 'com.android.application' |
| | | } |
| | | |
| | | android { |
| | | compileSdk 31 |
| | | |
| | | defaultConfig { |
| | | applicationId "com.hdl.hdlsdk" |
| | | minSdk 21 |
| | | targetSdk 31 |
| | | versionCode 1 |
| | | versionName "1.0" |
| | | |
| | | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
| | | } |
| | | |
| | | buildTypes { |
| | | release { |
| | | minifyEnabled false |
| | | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| | | } |
| | | } |
| | | compileOptions { |
| | | sourceCompatibility JavaVersion.VERSION_1_8 |
| | | targetCompatibility JavaVersion.VERSION_1_8 |
| | | } |
| | | } |
| | | |
| | | dependencies { |
| | | |
| | | implementation 'androidx.appcompat:appcompat:1.3.1' |
| | | implementation 'com.google.android.material:material:1.4.0' |
| | | implementation 'androidx.constraintlayout:constraintlayout:2.1.1' |
| | | |
| | | testImplementation 'junit:junit:4.+' |
| | | androidTestImplementation 'androidx.test.ext:junit:1.1.2' |
| | | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' |
| | | |
| | | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.6' |
| | | |
| | | implementation project(path: ':hdl-connect') |
| | | } |
New file |
| | |
| | | # Add project specific ProGuard rules here. |
| | | # You can control the set of applied configuration files using the |
| | | # proguardFiles setting in build.gradle. |
| | | # |
| | | # For more details, see |
| | | # http://developer.android.com/guide/developing/tools/proguard.html |
| | | |
| | | # If your project uses WebView with JS, uncomment the following |
| | | # and specify the fully qualified class name to the JavaScript interface |
| | | # class: |
| | | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
| | | # public *; |
| | | #} |
| | | |
| | | # Uncomment this to preserve the line number information for |
| | | # debugging stack traces. |
| | | #-keepattributes SourceFile,LineNumberTable |
| | | |
| | | # If you keep the line number information, uncomment this to |
| | | # hide the original source file name. |
| | | #-renamesourcefileattribute SourceFile |
New file |
| | |
| | | package com.hdl.hdlsdk; |
| | | |
| | | import android.content.Context; |
| | | |
| | | import androidx.test.platform.app.InstrumentationRegistry; |
| | | import androidx.test.ext.junit.runners.AndroidJUnit4; |
| | | |
| | | import org.junit.Test; |
| | | import org.junit.runner.RunWith; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Instrumented test, which will execute on an Android device. |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | @RunWith(AndroidJUnit4.class) |
| | | public class ExampleInstrumentedTest { |
| | | @Test |
| | | public void useAppContext() { |
| | | // Context of the app under test. |
| | | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
| | | assertEquals("com.hdl.hdlsdk", appContext.getPackageName()); |
| | | } |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?>
|
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
| | | xmlns:tools="http://schemas.android.com/tools"
|
| | | package="com.hdl.hdlsdk">
|
| | |
|
| | | <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
| | | <uses-permission android:name="android.permission.INTERNET" />
|
| | | <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
| | | <uses-permission
|
| | | android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
| | | tools:ignore="ScopedStorage" />
|
| | | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
| | |
|
| | | <application
|
| | | android:allowBackup="true"
|
| | | android:name=".App"
|
| | | android:icon="@mipmap/ic_launcher"
|
| | | android:label="@string/app_name"
|
| | | android:roundIcon="@mipmap/ic_launcher_round"
|
| | | android:supportsRtl="true"
|
| | | android:theme="@style/Theme.HDLSDK">
|
| | | <activity
|
| | | android:name=".MainActivity"
|
| | | android:exported="true">
|
| | | <intent-filter>
|
| | | <action android:name="android.intent.action.MAIN" />
|
| | |
|
| | | <category android:name="android.intent.category.LAUNCHER" />
|
| | | </intent-filter>
|
| | | </activity>
|
| | | </application>
|
| | |
|
| | | </manifest> |
New file |
| | |
| | | package com.hdl.hdlsdk;
|
| | |
|
| | | import android.app.Application;
|
| | |
|
| | | import com.hdl.sdk.common.HDLSdk;
|
| | | import com.hdl.sdk.connect.HDLSocket;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class App extends Application {
|
| | |
|
| | | @Override
|
| | | public void onCreate() {
|
| | | super.onCreate();
|
| | | HDLSdk.getInstance().init(this);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onTerminate() {
|
| | | super.onTerminate();
|
| | |
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.hdlsdk;
|
| | |
|
| | | import androidx.annotation.NonNull;
|
| | | import androidx.annotation.Nullable;
|
| | |
|
| | | import com.chad.library.adapter.base.BaseMultiItemQuickAdapter;
|
| | | import com.chad.library.adapter.base.BaseQuickAdapter;
|
| | | import com.chad.library.adapter.base.viewholder.BaseViewHolder;
|
| | |
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class DemoAdapter extends BaseMultiItemQuickAdapter<DemoBean, BaseViewHolder> {
|
| | |
|
| | | public DemoAdapter(@Nullable List<DemoBean> data) {
|
| | | super(data);
|
| | | addItemType(0,R.layout.demo_item);
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected void convert(@NonNull BaseViewHolder baseViewHolder, DemoBean demoBean) {
|
| | | baseViewHolder.setText(R.id.tv_title,demoBean.getName());
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.hdlsdk;
|
| | |
|
| | | import com.chad.library.adapter.base.entity.MultiItemEntity;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class DemoBean implements MultiItemEntity {
|
| | |
|
| | | private String name;
|
| | |
|
| | | public DemoBean(String name) {
|
| | | this.name = name;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public int getItemType() {
|
| | | return 0;
|
| | | }
|
| | |
|
| | | public String getName() {
|
| | | return name;
|
| | | }
|
| | |
|
| | | public void setName(String name) {
|
| | | this.name = name;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.hdlsdk; |
| | | |
| | | import androidx.activity.result.ActivityResultCallback; |
| | | import androidx.activity.result.ActivityResultLauncher; |
| | | import androidx.activity.result.contract.ActivityResultContracts; |
| | | import androidx.annotation.NonNull; |
| | | import androidx.appcompat.app.AppCompatActivity; |
| | | import androidx.recyclerview.widget.LinearLayoutManager; |
| | | import androidx.recyclerview.widget.RecyclerView; |
| | | |
| | | import android.Manifest; |
| | | import android.app.Instrumentation; |
| | | import android.os.Bundle; |
| | | import android.view.View; |
| | | import android.widget.TextView; |
| | | |
| | | import com.chad.library.adapter.base.BaseQuickAdapter; |
| | | import com.chad.library.adapter.base.listener.OnItemClickListener; |
| | | import com.google.gson.JsonObject; |
| | | import com.hdl.sdk.common.config.TopicConstant; |
| | | import com.hdl.sdk.common.utils.IdUtils; |
| | | import com.hdl.sdk.common.utils.IpUtils; |
| | | import com.hdl.sdk.connect.HDLSocket; |
| | | import com.hdl.sdk.connect.bean.LinkRequest; |
| | | import com.hdl.sdk.connect.protocol.LinkMessageDecoder; |
| | | import com.hdl.sdk.connect.protocol.LinkMessageEncoder; |
| | | import com.hdl.sdk.socket.SocketBoot; |
| | | import com.hdl.sdk.socket.SocketOptions; |
| | | import com.hdl.sdk.socket.client.UdpClient; |
| | | import com.hdl.sdk.socket.codec.MessagePipeLine; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | public class MainActivity extends AppCompatActivity { |
| | | |
| | | private DemoAdapter demoAdapter; |
| | | private RecyclerView rv; |
| | | private TextView tv; |
| | | private TextView responseTv; |
| | | |
| | | |
| | | |
| | | @Override |
| | | protected void onCreate(Bundle savedInstanceState) { |
| | | super.onCreate(savedInstanceState); |
| | | setContentView(R.layout.activity_main); |
| | | responseTv = findViewById(R.id.response_tv); |
| | | tv = findViewById(R.id.state_tv); |
| | | rv = findViewById(R.id.rv); |
| | | rv.setLayoutManager(new LinearLayoutManager(this)); |
| | | |
| | | ActivityResultLauncher<String[]> launcher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() { |
| | | @Override |
| | | public void onActivityResult(Map<String, Boolean> result) { |
| | | |
| | | } |
| | | }); |
| | | |
| | | launcher.launch(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}); |
| | | |
| | | final List<DemoBean> beans = new ArrayList<>(); |
| | | beans.add(new DemoBean("搜索网关")); |
| | | beans.add(new DemoBean("获取功能列表")); |
| | | beans.add(new DemoBean("功能属性读取")); |
| | | beans.add(new DemoBean("设备控制")); |
| | | beans.add(new DemoBean("状态上报")); |
| | | beans.add(new DemoBean("读取状态")); |
| | | demoAdapter = new DemoAdapter(beans); |
| | | rv.setAdapter(demoAdapter); |
| | | |
| | | |
| | | final SocketOptions options = new SocketOptions(); |
| | | |
| | | MessagePipeLine pipeLine = new MessagePipeLine(); |
| | | pipeLine.add(new LinkMessageDecoder()); |
| | | pipeLine.add(new LinkMessageEncoder()); |
| | | options.setHandleMessage(pipeLine); |
| | | options.setEnabledHeartbeat(false); |
| | | |
| | | demoAdapter.setOnItemClickListener(new OnItemClickListener() { |
| | | @Override |
| | | public void onItemClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view, int position) { |
| | | switch (position) { |
| | | case 0: |
| | | tv.setText("搜索网关中"); |
| | | responseTv.setText(""); |
| | | |
| | | HDLSocket.getInstance().searchGateway(new HDLSocket.CallBack() { |
| | | @Override |
| | | public void onError(String error) { |
| | | tv.setText("网关获取失败"); |
| | | } |
| | | |
| | | @Override |
| | | public void onResponse(String data) { |
| | | tv.setText("获取网关成功"); |
| | | responseTv.setText(data); |
| | | } |
| | | }); |
| | | |
| | | break; |
| | | case 1: |
| | | |
| | | tv.setText("获取功能列表中"); |
| | | responseTv.setText(""); |
| | | HDLSocket.getInstance().getFunctionList(new HDLSocket.CallBack() { |
| | | @Override |
| | | public void onError(String error) { |
| | | tv.setText(error); |
| | | } |
| | | |
| | | @Override |
| | | public void onResponse(String data) { |
| | | tv.setText("获取功能列表成功"); |
| | | responseTv.setText(data); |
| | | } |
| | | }); |
| | | break; |
| | | case 2: |
| | | //功能属性读取 |
| | | // HDLSocket.getInstance().getFunctionAttribute(); |
| | | |
| | | break; |
| | | case 3: |
| | | //设备控制 |
| | | // HDLSocket.getInstance().propertyDown(); |
| | | break; |
| | | case 4: |
| | | //状态上报 |
| | | //HDLSocket.getInstance().propertyUp(); |
| | | break; |
| | | case 5: |
| | | //读取状态 |
| | | // HDLSocket.getInstance().propertyRead(); |
| | | break; |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <vector xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:aapt="http://schemas.android.com/aapt" |
| | | android:width="108dp" |
| | | android:height="108dp" |
| | | android:viewportWidth="108" |
| | | android:viewportHeight="108"> |
| | | <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> |
| | | <aapt:attr name="android:fillColor"> |
| | | <gradient |
| | | android:endX="85.84757" |
| | | android:endY="92.4963" |
| | | android:startX="42.9492" |
| | | android:startY="49.59793" |
| | | android:type="linear"> |
| | | <item |
| | | android:color="#44000000" |
| | | android:offset="0.0" /> |
| | | <item |
| | | android:color="#00000000" |
| | | android:offset="1.0" /> |
| | | </gradient> |
| | | </aapt:attr> |
| | | </path> |
| | | <path |
| | | android:fillColor="#FFFFFF" |
| | | android:fillType="nonZero" |
| | | android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" |
| | | android:strokeWidth="1" |
| | | android:strokeColor="#00000000" /> |
| | | </vector> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <vector xmlns:android="http://schemas.android.com/apk/res/android" |
| | | android:width="108dp" |
| | | android:height="108dp" |
| | | android:viewportWidth="108" |
| | | android:viewportHeight="108"> |
| | | <path |
| | | android:fillColor="#3DDC84" |
| | | android:pathData="M0,0h108v108h-108z" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M9,0L9,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M19,0L19,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M29,0L29,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M39,0L39,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M49,0L49,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M59,0L59,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M69,0L69,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M79,0L79,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M89,0L89,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M99,0L99,108" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,9L108,9" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,19L108,19" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,29L108,29" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,39L108,39" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,49L108,49" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,59L108,59" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,69L108,69" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,79L108,79" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,89L108,89" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M0,99L108,99" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M19,29L89,29" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M19,39L89,39" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M19,49L89,49" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M19,59L89,59" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M19,69L89,69" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M19,79L89,79" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M29,19L29,89" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M39,19L39,89" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M49,19L49,89" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M59,19L59,89" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M69,19L69,89" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | <path |
| | | android:fillColor="#00000000" |
| | | android:pathData="M79,19L79,89" |
| | | android:strokeWidth="0.8" |
| | | android:strokeColor="#33FFFFFF" /> |
| | | </vector> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | xmlns:tools="http://schemas.android.com/tools" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | tools:context=".MainActivity"> |
| | | |
| | | <androidx.recyclerview.widget.RecyclerView |
| | | android:id="@+id/rv" |
| | | android:layout_width="0dp" |
| | | android:layout_height="0dp" |
| | | app:layout_constraintBottom_toBottomOf="parent" |
| | | app:layout_constraintEnd_toStartOf="@id/scrollView" |
| | | app:layout_constraintHorizontal_weight="1" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toTopOf="parent" /> |
| | | |
| | | <View |
| | | android:layout_width="1dp" |
| | | android:layout_height="0dp" |
| | | android:background="#f4f4f4" |
| | | app:layout_constraintBottom_toBottomOf="parent" |
| | | app:layout_constraintEnd_toStartOf="@id/scrollView" |
| | | app:layout_constraintStart_toEndOf="@id/rv" |
| | | app:layout_constraintTop_toTopOf="parent" /> |
| | | |
| | | <androidx.core.widget.NestedScrollView |
| | | android:id="@+id/scrollView" |
| | | android:layout_width="0dp" |
| | | android:layout_height="0dp" |
| | | app:layout_constraintBottom_toBottomOf="parent" |
| | | app:layout_constraintEnd_toEndOf="parent" |
| | | app:layout_constraintHorizontal_weight="2" |
| | | app:layout_constraintStart_toEndOf="@id/rv" |
| | | app:layout_constraintTop_toTopOf="parent"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:orientation="vertical"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:paddingStart="10dp" |
| | | android:text="当前状态:" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/state_tv" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_gravity="center" |
| | | android:text="未操作" /> |
| | | |
| | | <View |
| | | android:layout_width="match_parent" |
| | | android:layout_height="2dp" |
| | | android:background="#f5f5f5" /> |
| | | |
| | | <TextView |
| | | android:paddingStart="10dp" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="10dp" |
| | | android:text="响应:" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/response_tv" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="10dp" /> |
| | | |
| | | |
| | | </LinearLayout> |
| | | </androidx.core.widget.NestedScrollView> |
| | | |
| | | |
| | | </androidx.constraintlayout.widget.ConstraintLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?>
|
| | | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
| | | xmlns:app="http://schemas.android.com/apk/res-auto"
|
| | | android:layout_width="match_parent"
|
| | | android:layout_height="40dp">
|
| | |
|
| | | <TextView
|
| | | android:id="@+id/tv_title"
|
| | | android:layout_width="0dp"
|
| | | android:layout_height="0dp"
|
| | | android:gravity="center"
|
| | | app:layout_constraintBottom_toBottomOf="parent"
|
| | | app:layout_constraintEnd_toEndOf="parent"
|
| | | app:layout_constraintStart_toStartOf="parent"
|
| | | app:layout_constraintTop_toTopOf="parent" />
|
| | |
|
| | | <View
|
| | | android:layout_width="0dp"
|
| | | android:layout_height="1dp"
|
| | | android:layout_marginStart="10dp"
|
| | | android:layout_marginEnd="10dp"
|
| | | android:background="#f5f5f5"
|
| | | app:layout_constraintBottom_toBottomOf="parent"
|
| | | app:layout_constraintEnd_toEndOf="parent"
|
| | | app:layout_constraintStart_toStartOf="parent" />
|
| | |
|
| | |
|
| | | </androidx.constraintlayout.widget.ConstraintLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <background android:drawable="@drawable/ic_launcher_background" /> |
| | | <foreground android:drawable="@drawable/ic_launcher_foreground" /> |
| | | </adaptive-icon> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <background android:drawable="@drawable/ic_launcher_background" /> |
| | | <foreground android:drawable="@drawable/ic_launcher_foreground" /> |
| | | </adaptive-icon> |
New file |
| | |
| | | <resources xmlns:tools="http://schemas.android.com/tools"> |
| | | <!-- Base application theme. --> |
| | | <style name="Theme.HDLSDK" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> |
| | | <!-- Primary brand color. --> |
| | | <item name="colorPrimary">@color/purple_200</item> |
| | | <item name="colorPrimaryVariant">@color/purple_700</item> |
| | | <item name="colorOnPrimary">@color/black</item> |
| | | <!-- Secondary brand color. --> |
| | | <item name="colorSecondary">@color/teal_200</item> |
| | | <item name="colorSecondaryVariant">@color/teal_200</item> |
| | | <item name="colorOnSecondary">@color/black</item> |
| | | <!-- Status bar color. --> |
| | | <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> |
| | | <!-- Customize your theme here. --> |
| | | </style> |
| | | </resources> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <resources> |
| | | <color name="purple_200">#FFBB86FC</color> |
| | | <color name="purple_500">#FF6200EE</color> |
| | | <color name="purple_700">#FF3700B3</color> |
| | | <color name="teal_200">#FF03DAC5</color> |
| | | <color name="teal_700">#FF018786</color> |
| | | <color name="black">#FF000000</color> |
| | | <color name="white">#FFFFFFFF</color> |
| | | </resources> |
New file |
| | |
| | | <resources> |
| | | <string name="app_name">HDLSDK</string> |
| | | </resources> |
New file |
| | |
| | | <resources xmlns:tools="http://schemas.android.com/tools"> |
| | | <!-- Base application theme. --> |
| | | <style name="Theme.HDLSDK" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> |
| | | <!-- Primary brand color. --> |
| | | <item name="colorPrimary">@color/purple_500</item> |
| | | <item name="colorPrimaryVariant">@color/purple_700</item> |
| | | <item name="colorOnPrimary">@color/white</item> |
| | | <!-- Secondary brand color. --> |
| | | <item name="colorSecondary">@color/teal_200</item> |
| | | <item name="colorSecondaryVariant">@color/teal_700</item> |
| | | <item name="colorOnSecondary">@color/black</item> |
| | | <!-- Status bar color. --> |
| | | <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> |
| | | <!-- Customize your theme here. --> |
| | | </style> |
| | | </resources> |
New file |
| | |
| | | package com.hdl.hdlsdk; |
| | | |
| | | import org.junit.Test; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Example local unit test, which will execute on the development machine (host). |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | public class ExampleUnitTest { |
| | | @Test |
| | | public void addition_isCorrect() { |
| | | assertEquals(4, 2 + 2); |
| | | } |
| | | } |
New file |
| | |
| | | // Top-level build file where you can add configuration options common to all sub-projects/modules. |
| | | apply from: "config.gradle" |
| | | buildscript { |
| | | repositories { |
| | | google() |
| | | mavenCentral() |
| | | } |
| | | dependencies { |
| | | classpath "com.android.tools.build:gradle:4.2.2" |
| | | |
| | | // NOTE: Do not place your application dependencies here; they belong |
| | | // in the individual module build.gradle files |
| | | } |
| | | } |
| | | |
| | | |
| | | allprojects { |
| | | repositories { |
| | | maven { url 'https://maven.aliyun.com/repository/google' } |
| | | maven { url 'https://maven.aliyun.com/repository/jcenter' } |
| | | maven { url 'https://maven.aliyun.com/repository/public' } |
| | | google() |
| | | maven { url "https://jitpack.io" } |
| | | } |
| | | } |
| | | |
| | | |
| | | task clean(type: Delete) { |
| | | delete rootProject.buildDir |
| | | } |
New file |
| | |
| | | ext {
|
| | | minSdkVersion = 16
|
| | | targetSdkVersion = 31
|
| | | compileSdkVersion = 31
|
| | | versionName = "1.0.0"
|
| | |
|
| | | } |
New file |
| | |
| | | # Project-wide Gradle settings. |
| | | # IDE (e.g. Android Studio) users: |
| | | # Gradle settings configured through the IDE *will override* |
| | | # any settings specified in this file. |
| | | # For more details on how to configure your build environment visit |
| | | # http://www.gradle.org/docs/current/userguide/build_environment.html |
| | | # Specifies the JVM arguments used for the daemon process. |
| | | # The setting is particularly useful for tweaking memory settings. |
| | | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 |
| | | # When configured, Gradle will run in incubating parallel mode. |
| | | # This option should only be used with decoupled projects. More details, visit |
| | | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects |
| | | # org.gradle.parallel=true |
| | | # AndroidX package structure to make it clearer which packages are bundled with the |
| | | # Android operating system, and which are packaged with your app"s APK |
| | | # https://developer.android.com/topic/libraries/support-library/androidx-rn |
| | | android.useAndroidX=true |
| | | # Automatically convert third-party libraries to use AndroidX |
| | | android.enableJetifier=true |
New file |
| | |
| | | #Wed Sep 15 00:34:41 CST 2021
|
| | | distributionBase=GRADLE_USER_HOME
|
| | | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
| | | distributionPath=wrapper/dists
|
| | | zipStorePath=wrapper/dists
|
| | | zipStoreBase=GRADLE_USER_HOME
|
New file |
| | |
| | | #!/usr/bin/env sh |
| | | |
| | | # |
| | | # Copyright 2015 the original author or authors. |
| | | # |
| | | # Licensed under the Apache License, Version 2.0 (the "License"); |
| | | # you may not use this file except in compliance with the License. |
| | | # You may obtain a copy of the License at |
| | | # |
| | | # https://www.apache.org/licenses/LICENSE-2.0 |
| | | # |
| | | # Unless required by applicable law or agreed to in writing, software |
| | | # distributed under the License is distributed on an "AS IS" BASIS, |
| | | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | | # See the License for the specific language governing permissions and |
| | | # limitations under the License. |
| | | # |
| | | |
| | | ############################################################################## |
| | | ## |
| | | ## Gradle start up script for UN*X |
| | | ## |
| | | ############################################################################## |
| | | |
| | | # Attempt to set APP_HOME |
| | | # Resolve links: $0 may be a link |
| | | PRG="$0" |
| | | # Need this for relative symlinks. |
| | | while [ -h "$PRG" ] ; do |
| | | ls=`ls -ld "$PRG"` |
| | | link=`expr "$ls" : '.*-> \(.*\)$'` |
| | | if expr "$link" : '/.*' > /dev/null; then |
| | | PRG="$link" |
| | | else |
| | | PRG=`dirname "$PRG"`"/$link" |
| | | fi |
| | | done |
| | | SAVED="`pwd`" |
| | | cd "`dirname \"$PRG\"`/" >/dev/null |
| | | APP_HOME="`pwd -P`" |
| | | cd "$SAVED" >/dev/null |
| | | |
| | | APP_NAME="Gradle" |
| | | APP_BASE_NAME=`basename "$0"` |
| | | |
| | | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. |
| | | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' |
| | | |
| | | # Use the maximum available, or set MAX_FD != -1 to use that value. |
| | | MAX_FD="maximum" |
| | | |
| | | warn () { |
| | | echo "$*" |
| | | } |
| | | |
| | | die () { |
| | | echo |
| | | echo "$*" |
| | | echo |
| | | exit 1 |
| | | } |
| | | |
| | | # OS specific support (must be 'true' or 'false'). |
| | | cygwin=false |
| | | msys=false |
| | | darwin=false |
| | | nonstop=false |
| | | case "`uname`" in |
| | | CYGWIN* ) |
| | | cygwin=true |
| | | ;; |
| | | Darwin* ) |
| | | darwin=true |
| | | ;; |
| | | MINGW* ) |
| | | msys=true |
| | | ;; |
| | | NONSTOP* ) |
| | | nonstop=true |
| | | ;; |
| | | esac |
| | | |
| | | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
| | | |
| | | |
| | | # Determine the Java command to use to start the JVM. |
| | | if [ -n "$JAVA_HOME" ] ; then |
| | | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
| | | # IBM's JDK on AIX uses strange locations for the executables |
| | | JAVACMD="$JAVA_HOME/jre/sh/java" |
| | | else |
| | | JAVACMD="$JAVA_HOME/bin/java" |
| | | fi |
| | | if [ ! -x "$JAVACMD" ] ; then |
| | | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
| | | |
| | | Please set the JAVA_HOME variable in your environment to match the |
| | | location of your Java installation." |
| | | fi |
| | | else |
| | | JAVACMD="java" |
| | | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
| | | |
| | | Please set the JAVA_HOME variable in your environment to match the |
| | | location of your Java installation." |
| | | fi |
| | | |
| | | # Increase the maximum file descriptors if we can. |
| | | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then |
| | | MAX_FD_LIMIT=`ulimit -H -n` |
| | | if [ $? -eq 0 ] ; then |
| | | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |
| | | MAX_FD="$MAX_FD_LIMIT" |
| | | fi |
| | | ulimit -n $MAX_FD |
| | | if [ $? -ne 0 ] ; then |
| | | warn "Could not set maximum file descriptor limit: $MAX_FD" |
| | | fi |
| | | else |
| | | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" |
| | | fi |
| | | fi |
| | | |
| | | # For Darwin, add options to specify how the application appears in the dock |
| | | if $darwin; then |
| | | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" |
| | | fi |
| | | |
| | | # For Cygwin or MSYS, switch paths to Windows format before running java |
| | | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then |
| | | APP_HOME=`cygpath --path --mixed "$APP_HOME"` |
| | | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |
| | | |
| | | JAVACMD=`cygpath --unix "$JAVACMD"` |
| | | |
| | | # We build the pattern for arguments to be converted via cygpath |
| | | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
| | | SEP="" |
| | | for dir in $ROOTDIRSRAW ; do |
| | | ROOTDIRS="$ROOTDIRS$SEP$dir" |
| | | SEP="|" |
| | | done |
| | | OURCYGPATTERN="(^($ROOTDIRS))" |
| | | # Add a user-defined pattern to the cygpath arguments |
| | | if [ "$GRADLE_CYGPATTERN" != "" ] ; then |
| | | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" |
| | | fi |
| | | # Now convert the arguments - kludge to limit ourselves to /bin/sh |
| | | i=0 |
| | | for arg in "$@" ; do |
| | | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` |
| | | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option |
| | | |
| | | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition |
| | | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` |
| | | else |
| | | eval `echo args$i`="\"$arg\"" |
| | | fi |
| | | i=`expr $i + 1` |
| | | done |
| | | case $i in |
| | | 0) set -- ;; |
| | | 1) set -- "$args0" ;; |
| | | 2) set -- "$args0" "$args1" ;; |
| | | 3) set -- "$args0" "$args1" "$args2" ;; |
| | | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; |
| | | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; |
| | | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; |
| | | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; |
| | | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; |
| | | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; |
| | | esac |
| | | fi |
| | | |
| | | # Escape application args |
| | | save () { |
| | | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done |
| | | echo " " |
| | | } |
| | | APP_ARGS=`save "$@"` |
| | | |
| | | # Collect all arguments for the java command, following the shell quoting and substitution rules |
| | | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" |
| | | |
| | | exec "$JAVACMD" "$@" |
New file |
| | |
| | | @rem
|
| | | @rem Copyright 2015 the original author or authors.
|
| | | @rem
|
| | | @rem Licensed under the Apache License, Version 2.0 (the "License");
|
| | | @rem you may not use this file except in compliance with the License.
|
| | | @rem You may obtain a copy of the License at
|
| | | @rem
|
| | | @rem https://www.apache.org/licenses/LICENSE-2.0
|
| | | @rem
|
| | | @rem Unless required by applicable law or agreed to in writing, software
|
| | | @rem distributed under the License is distributed on an "AS IS" BASIS,
|
| | | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| | | @rem See the License for the specific language governing permissions and
|
| | | @rem limitations under the License.
|
| | | @rem
|
| | |
|
| | | @if "%DEBUG%" == "" @echo off
|
| | | @rem ##########################################################################
|
| | | @rem
|
| | | @rem Gradle startup script for Windows
|
| | | @rem
|
| | | @rem ##########################################################################
|
| | |
|
| | | @rem Set local scope for the variables with windows NT shell
|
| | | if "%OS%"=="Windows_NT" setlocal
|
| | |
|
| | | set DIRNAME=%~dp0
|
| | | if "%DIRNAME%" == "" set DIRNAME=.
|
| | | set APP_BASE_NAME=%~n0
|
| | | set APP_HOME=%DIRNAME%
|
| | |
|
| | | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
| | | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
| | |
|
| | | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
| | | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
| | |
|
| | | @rem Find java.exe
|
| | | if defined JAVA_HOME goto findJavaFromJavaHome
|
| | |
|
| | | set JAVA_EXE=java.exe
|
| | | %JAVA_EXE% -version >NUL 2>&1
|
| | | if "%ERRORLEVEL%" == "0" goto execute
|
| | |
|
| | | echo.
|
| | | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
| | | echo.
|
| | | echo Please set the JAVA_HOME variable in your environment to match the
|
| | | echo location of your Java installation.
|
| | |
|
| | | goto fail
|
| | |
|
| | | :findJavaFromJavaHome
|
| | | set JAVA_HOME=%JAVA_HOME:"=%
|
| | | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
| | |
|
| | | if exist "%JAVA_EXE%" goto execute
|
| | |
|
| | | echo.
|
| | | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
| | | echo.
|
| | | echo Please set the JAVA_HOME variable in your environment to match the
|
| | | echo location of your Java installation.
|
| | |
|
| | | goto fail
|
| | |
|
| | | :execute
|
| | | @rem Setup the command line
|
| | |
|
| | | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
| | |
|
| | |
|
| | | @rem Execute Gradle
|
| | | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
| | |
|
| | | :end
|
| | | @rem End local scope for the variables with windows NT shell
|
| | | if "%ERRORLEVEL%"=="0" goto mainEnd
|
| | |
|
| | | :fail
|
| | | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
| | | rem the _cmd.exe /c_ return code!
|
| | | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
| | | exit /b 1
|
| | |
|
| | | :mainEnd
|
| | | if "%OS%"=="Windows_NT" endlocal
|
| | |
|
| | | :omega
|
New file |
| | |
| | | plugins { |
| | | id 'com.android.library' |
| | | } |
| | | |
| | | android { |
| | | compileSdkVersion rootProject.compileSdkVersion |
| | | |
| | | defaultConfig { |
| | | minSdkVersion rootProject.minSdkVersion |
| | | targetSdkVersion rootProject.targetSdkVersion |
| | | versionCode 1 |
| | | versionName "1.0" |
| | | |
| | | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
| | | consumerProguardFiles "consumer-rules.pro" |
| | | } |
| | | |
| | | buildTypes { |
| | | release { |
| | | minifyEnabled false |
| | | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| | | } |
| | | } |
| | | compileOptions { |
| | | sourceCompatibility JavaVersion.VERSION_1_8 |
| | | targetCompatibility JavaVersion.VERSION_1_8 |
| | | } |
| | | } |
| | | |
| | | dependencies { |
| | | api 'com.google.code.gson:gson:2.8.8' |
| | | api 'androidx.annotation:annotation:1.2.0' |
| | | api 'androidx.collection:collection:1.1.0' |
| | | } |
New file |
| | |
| | | # Add project specific ProGuard rules here. |
| | | # You can control the set of applied configuration files using the |
| | | # proguardFiles setting in build.gradle. |
| | | # |
| | | # For more details, see |
| | | # http://developer.android.com/guide/developing/tools/proguard.html |
| | | |
| | | # If your project uses WebView with JS, uncomment the following |
| | | # and specify the fully qualified class name to the JavaScript interface |
| | | # class: |
| | | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
| | | # public *; |
| | | #} |
| | | |
| | | # Uncomment this to preserve the line number information for |
| | | # debugging stack traces. |
| | | #-keepattributes SourceFile,LineNumberTable |
| | | |
| | | # If you keep the line number information, uncomment this to |
| | | # hide the original source file name. |
| | | #-renamesourcefileattribute SourceFile |
New file |
| | |
| | | package com.hdl.sdk.common; |
| | | |
| | | import android.content.Context; |
| | | |
| | | import androidx.test.platform.app.InstrumentationRegistry; |
| | | import androidx.test.ext.junit.runners.AndroidJUnit4; |
| | | |
| | | import org.junit.Test; |
| | | import org.junit.runner.RunWith; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Instrumented test, which will execute on an Android device. |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | @RunWith(AndroidJUnit4.class) |
| | | public class ExampleInstrumentedTest { |
| | | @Test |
| | | public void useAppContext() { |
| | | // Context of the app under test. |
| | | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
| | | assertEquals("com.hdl.sdk.common.test", appContext.getPackageName()); |
| | | } |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| | | package="com.hdl.sdk.common"> |
| | | |
| | | </manifest> |
New file |
| | |
| | | package com.hdl.sdk.common;
|
| | |
|
| | | import android.content.Context;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/28.
|
| | | */
|
| | | public class HDLSdk {
|
| | |
|
| | | private Context context;
|
| | |
|
| | | private HDLSdk() {
|
| | | }
|
| | |
|
| | | private static class SingletonInstance {
|
| | | private static final HDLSdk INSTANCE = new HDLSdk();
|
| | | }
|
| | |
|
| | | public static HDLSdk getInstance() {
|
| | | return SingletonInstance.INSTANCE;
|
| | | }
|
| | |
|
| | | public void init(Context context) {
|
| | | this.context = context.getApplicationContext();
|
| | | }
|
| | |
|
| | | public Context getContext() {
|
| | | return context;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/28.
|
| | | * 包含所有操作
|
| | | */
|
| | | public interface IHDLClient {
|
| | |
|
| | |
|
| | | /**
|
| | | * 搜索网关
|
| | | */
|
| | | void searchGateway();
|
| | |
|
| | |
|
| | | /**
|
| | | * 获取网关详细信息
|
| | | */
|
| | | void getGatewayInfo();
|
| | |
|
| | | /**
|
| | | * 获取设备列表
|
| | | */
|
| | | void getDeviceList();
|
| | |
|
| | | /**
|
| | | * 获取功能列表
|
| | | */
|
| | | void getFunctionList();
|
| | |
|
| | | /**
|
| | | * 获取功能属性
|
| | | */
|
| | | void getFunctionAttribute();
|
| | |
|
| | | /**
|
| | | * 设备控制
|
| | | */
|
| | | void deviceControl();
|
| | |
|
| | | /**
|
| | | * 获取设备状态
|
| | | */
|
| | | void getDeviceStatus();
|
| | |
|
| | | /**
|
| | | * 改变设备状态
|
| | | */
|
| | | void changeDeviceStatus();
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.config;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | public class TopicConstant {
|
| | |
|
| | | //搜索网关
|
| | | public static final String GATEWAY_SEARCH = "/user/all/custom/gateway/search";
|
| | |
|
| | | //搜索网关响应
|
| | | public static final String GATEWAY_SEARCH_REPLY = "/user/all/custom/gateway/search_reply";
|
| | |
|
| | | //网关详细信息获取s=gw_id
|
| | | public static final String GATEWAY_INFO = "/user/%s/custom/gateway/get";
|
| | |
|
| | | //获取物理设备列表
|
| | | public static final String GET_DEVICE_LIST = " /user/%s/custom/device/list/get";
|
| | |
|
| | | //获取物理设备列表响应
|
| | | public static final String GET_DEVICE_LIST_REPLY = " /user/%s/custom/device/list/get_reply";
|
| | |
|
| | | //获取功能列表
|
| | | public static final String GET_FUNCTION_LIST = "/user/%s/custom/function/list/get";
|
| | |
|
| | | //获取功能响应
|
| | | public static final String GET_FUNCTION_LIST_REPLY = "/user/%s/custom/function/list/get_reply";
|
| | |
|
| | | //功能属性读取
|
| | | public static final String GET_FUNCTION_ATTRIBUTE = "/user/%s/custom/function/attribute/get";
|
| | |
|
| | | //功能属性响应
|
| | | public static final String GET_FUNCTION_ATTRIBUTE_REPLY = "/user/%s/custom/function/attribute/get_reply";
|
| | |
|
| | | //控制
|
| | | public static final String PROPERTY_DOWN = "/base/%s/thing/property/down";
|
| | |
|
| | | //控制响应
|
| | | public static final String PROPERTY_DOWN_REPLY = "/base/%s/thing/property/down_reply";
|
| | |
|
| | | //状态上报
|
| | | public static final String PROPERTY_UP = "/base/%s/thing/property/up";
|
| | |
|
| | | //状态上报响应
|
| | | public static final String PROPERTY_UP_REPLY = "/base/%s/thing/property/up_reply";
|
| | |
|
| | | //读取状态
|
| | | public static final String PROPERTY_READ = "/base/%s/thing/property/read";
|
| | |
|
| | | //读取状态响应
|
| | | public static final String PROPERTY_READ_REPLY = "/base/%s/thing/property/read_reply";
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.event;
|
| | |
|
| | | import androidx.annotation.NonNull;
|
| | | import androidx.collection.ArrayMap;
|
| | |
|
| | |
|
| | | import com.hdl.sdk.common.utils.ThreadToolUtils;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.List;
|
| | | import java.util.Objects;
|
| | | import java.util.concurrent.ExecutorService;
|
| | |
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | * 事件分发
|
| | | */
|
| | | public class EventDispatcher {
|
| | |
|
| | | private static final ArrayMap<Object, List<EventListener>> EVENT = new ArrayMap<>();
|
| | |
|
| | | private static final ArrayMap<EventListener, Integer> TYPE = new ArrayMap<>();
|
| | |
|
| | | private static final int MAIN_TYPE = 0;
|
| | | private static final int IO_TYPE = 1;
|
| | |
|
| | | private static final ExecutorService ioThread = ThreadToolUtils.getInstance().newFixedThreadPool(2);
|
| | |
|
| | | private EventDispatcher() {
|
| | | }
|
| | |
|
| | | private static class SingletonInstance {
|
| | | private static final EventDispatcher INSTANCE = new EventDispatcher();
|
| | | }
|
| | |
|
| | | public static EventDispatcher getInstance() {
|
| | | return SingletonInstance.INSTANCE;
|
| | | }
|
| | |
|
| | | public synchronized void register(Object tag, EventListener listener) {
|
| | | if (!EVENT.containsKey(tag)) {
|
| | | EVENT.put(tag, new ArrayList<>());
|
| | | }
|
| | | List<EventListener> events = EVENT.get(tag);
|
| | | if (events != null && !events.contains(listener)) {
|
| | | events.add(listener);
|
| | | }
|
| | | TYPE.put(listener, MAIN_TYPE);
|
| | | }
|
| | |
|
| | | public synchronized void registerIo(Object tag, EventListener listener) {
|
| | | if (!EVENT.containsKey(tag)) {
|
| | | EVENT.put(tag, new ArrayList<>());
|
| | | }
|
| | | List<EventListener> events = EVENT.get(tag);
|
| | | if (events != null && !events.contains(listener)) {
|
| | | events.add(listener);
|
| | | }
|
| | | TYPE.put(listener, IO_TYPE);
|
| | | }
|
| | |
|
| | | public synchronized void remove(Object tag) {
|
| | | ioThread.execute(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | try {
|
| | | if (EVENT.containsKey(tag)) {
|
| | | List<EventListener> list = EVENT.get(tag);
|
| | | for (EventListener eventListener : list) {
|
| | | TYPE.remove(eventListener);
|
| | | }
|
| | | EVENT.remove(tag);
|
| | | }
|
| | | } catch (Exception ignored) {
|
| | |
|
| | | }
|
| | |
|
| | | }
|
| | | });
|
| | | }
|
| | |
|
| | | public synchronized void remove(Object tag, EventListener listener) {
|
| | | ioThread.execute(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | try {
|
| | | if (EVENT.containsKey(tag)) {
|
| | | List<EventListener> ev = EVENT.get(tag);
|
| | | if (ev != null && !ev.isEmpty()) {
|
| | | TYPE.remove(listener);
|
| | | ev.remove(listener);
|
| | | }
|
| | | }
|
| | | } catch (Exception ignored) {
|
| | |
|
| | | }
|
| | |
|
| | | }
|
| | | });
|
| | | }
|
| | |
|
| | | public synchronized void post(Object tag, @NonNull Object o) {
|
| | | if (EVENT.containsKey(tag)) {
|
| | | List<EventListener> list = EVENT.get(tag);
|
| | | if (list != null && !list.isEmpty()) {
|
| | | for (EventListener listener : list) {
|
| | | ThreadToolUtils.getInstance().runOnUiThread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | if (listener != null) {
|
| | | listener.onMessage(o);
|
| | | }
|
| | | }
|
| | | });
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | public synchronized void clear() {
|
| | | EVENT.clear();
|
| | | TYPE.clear();
|
| | | }
|
| | |
|
| | | public synchronized void release() {
|
| | | clear();
|
| | | ioThread.shutdownNow();
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.event;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | public interface EventListener {
|
| | |
|
| | | void onMessage(Object msg);
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.Arrays;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/23.
|
| | | */
|
| | | public class ByteUtils {
|
| | |
|
| | | public static byte[] toByteArray(List<Byte> list) {
|
| | | Byte[] temps = list.toArray(new Byte[0]);
|
| | | byte[] result = new byte[temps.length];
|
| | | for (int i = 0; i < result.length; i++) {
|
| | | result[i] = temps[i];
|
| | | }
|
| | | return result;
|
| | |
|
| | | }
|
| | |
|
| | | public static List<Byte> toByteList(byte[] bytes) {
|
| | | final List<Byte> list = new ArrayList<>();
|
| | | for (byte aByte : bytes) {
|
| | | list.add(aByte);
|
| | | }
|
| | | return list;
|
| | |
|
| | | }
|
| | |
|
| | | public static byte[] getRangeBytes(List<Byte> list, int start, int end) {
|
| | | Byte[] temps = Arrays.copyOfRange(list.toArray(new Byte[0]), start, end);
|
| | | byte[] result = new byte[temps.length];
|
| | | for (int i = 0; i < temps.length; i++) {
|
| | | result[i] = temps[i];
|
| | | }
|
| | | return result;
|
| | |
|
| | | }
|
| | |
|
| | | /**
|
| | | * 拼接byte
|
| | | */
|
| | | public static byte[] concatBytes(byte[] bt1, byte[] bt2) {
|
| | | if (bt1 == null) {
|
| | | return bt2;
|
| | | }
|
| | | if (bt2 == null) {
|
| | | return bt1;
|
| | | }
|
| | | byte[] bt3 = new byte[bt1.length + bt2.length];
|
| | | System.arraycopy(bt1, 0, bt3, 0, bt1.length);
|
| | | System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
|
| | | return bt3;
|
| | | }
|
| | |
|
| | |
|
| | | public boolean endWith(Byte[] src, byte[] target) {
|
| | | if (src.length < target.length) {
|
| | | return false;
|
| | | }
|
| | | for (int i = 0; i < target.length; i++) {
|
| | | if (target[target.length - i - 1] != src[src.length - i - 1]) {
|
| | | return false;
|
| | | }
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | |
|
| | | public static int byteIndexOf(byte[] searched, byte[] find, int start) {
|
| | | boolean matched;
|
| | | int end = find.length - 1;
|
| | | int skip = 0;
|
| | | for (int index = start; index <= searched.length - find.length; ++index) {
|
| | | matched = true;
|
| | | if (find[0] != searched[index] || find[end] != searched[index + end]) continue;
|
| | | else skip++;
|
| | | if (end > 10)
|
| | | if (find[skip] != searched[index + skip] || find[end - skip] != searched[index + end - skip])
|
| | | continue;
|
| | | else skip++;
|
| | | for (int subIndex = skip; subIndex < find.length - skip; ++subIndex) {
|
| | | if (find[subIndex] != searched[index + subIndex]) {
|
| | | matched = false;
|
| | | break;
|
| | | }
|
| | | }
|
| | | if (matched) {
|
| | | return index;
|
| | | }
|
| | | }
|
| | | return -1;
|
| | |
|
| | | }
|
| | |
|
| | | public static int getByteIndexOf(byte[] sources, byte[] src) {
|
| | | return getByteIndexOf(sources, src, 0, sources.length);
|
| | | }
|
| | |
|
| | | //判断一个byte数值在另外一个byte数组中对应的游标值
|
| | | public static int getByteIndexOf(byte[] sources, byte[] src, int startIndex) {
|
| | | return getByteIndexOf(sources, src, startIndex, sources.length);
|
| | | }
|
| | |
|
| | |
|
| | | //判断一个byte数值在另外一个byte数组中对应的游标值,指定开始的游标和结束的游标位置
|
| | | public static int getByteIndexOf(byte[] sources, byte[] src, int startIndex, int endIndex) {
|
| | |
|
| | | if (sources == null || src == null || sources.length == 0 || src.length == 0) {
|
| | | return -1;
|
| | | }
|
| | |
|
| | | if (endIndex > sources.length) {
|
| | | endIndex = sources.length;
|
| | | }
|
| | |
|
| | | int i, j;
|
| | | for (i = startIndex; i < endIndex; i++) {
|
| | | if (sources[i] == src[0] && i + src.length < endIndex) {
|
| | | for (j = 1; j < src.length; j++) {
|
| | | if (sources[i + j] != src[j]) {
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
| | | if (j == src.length) {
|
| | | return i;
|
| | | }
|
| | | }
|
| | | }
|
| | | return -1;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils;
|
| | |
|
| | | import java.util.UUID;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class IdUtils {
|
| | | public static String getUUId() {
|
| | | return UUID.randomUUID().toString().replaceAll("-", "");
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils;
|
| | |
|
| | | import android.content.Context;
|
| | | import android.net.wifi.WifiInfo;
|
| | | import android.net.wifi.WifiManager;
|
| | |
|
| | | import java.net.InetAddress;
|
| | | import java.net.InterfaceAddress;
|
| | | import java.net.NetworkInterface;
|
| | | import java.net.SocketException;
|
| | | import java.util.Enumeration;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/27.
|
| | | */
|
| | | public class IpUtils {
|
| | |
|
| | | /**
|
| | | * @return 广播地址
|
| | | */
|
| | | public static String getBroadcastAddress() {
|
| | | try {
|
| | | for (Enumeration<NetworkInterface> niEnum = NetworkInterface.getNetworkInterfaces();
|
| | | niEnum.hasMoreElements(); ) {
|
| | | NetworkInterface ni = niEnum.nextElement();
|
| | | if (!ni.isLoopback()) {
|
| | | for (InterfaceAddress interfaceAddress : ni.getInterfaceAddresses()) {
|
| | | if (interfaceAddress.getBroadcast() != null) {
|
| | | return interfaceAddress.getBroadcast().toString().substring(1);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | } catch (SocketException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return "255.255.255.255";
|
| | | }
|
| | |
|
| | | public static String getIP(Context application) {
|
| | | WifiManager wifiManager = (WifiManager) application.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
| | | if (!wifiManager.isWifiEnabled()) {
|
| | | try {
|
| | | for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
|
| | | NetworkInterface intf = en.nextElement();
|
| | | for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
|
| | | InetAddress inetAddress = enumIpAddr.nextElement();
|
| | | if (!inetAddress.isLoopbackAddress()) {
|
| | | return inetAddress.getHostAddress();
|
| | | }
|
| | | }
|
| | | }
|
| | | } catch (SocketException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | } else {
|
| | | WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
| | | int ipAddress = wifiInfo.getIpAddress();
|
| | | return intToIp(ipAddress);
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
|
| | |
|
| | | private static String intToIp(int i) {
|
| | |
|
| | | return (i & 0xFF) + "." +
|
| | | ((i >> 8) & 0xFF) + "." +
|
| | | ((i >> 16) & 0xFF) + "." +
|
| | | (i >> 24 & 0xFF);
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/23.
|
| | | */
|
| | | public class LogUtils {
|
| | |
|
| | | private static final String TAG = "HDLSocket";
|
| | |
|
| | | private boolean isEnabled = true;
|
| | |
|
| | |
|
| | | private LogUtils() {
|
| | | }
|
| | |
|
| | | private static class SingletonInstance {
|
| | | private static final LogUtils INSTANCE = new LogUtils();
|
| | | }
|
| | |
|
| | | public static LogUtils getInstance() {
|
| | | return SingletonInstance.INSTANCE;
|
| | | }
|
| | |
|
| | | public boolean isEnabled() {
|
| | | return isEnabled;
|
| | | }
|
| | |
|
| | | public void setEnabled(boolean enabled) {
|
| | | isEnabled = enabled;
|
| | | }
|
| | |
|
| | | public static void d(String tag, String msg) {
|
| | |
|
| | | }
|
| | |
|
| | | public static void e(String tag, String msg) {
|
| | |
|
| | | }
|
| | |
|
| | | public static void w(String tag, String msg) {
|
| | |
|
| | | }
|
| | |
|
| | | public static void v(String tag, String msg) {
|
| | |
|
| | | }
|
| | |
|
| | | public static void i(String tag, String msg) {
|
| | |
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils;
|
| | |
|
| | | import android.content.Context;
|
| | | import android.content.SharedPreferences;
|
| | |
|
| | | import androidx.annotation.NonNull;
|
| | |
|
| | | import com.hdl.sdk.common.HDLSdk;
|
| | |
|
| | |
|
| | | import java.util.Collections;
|
| | | import java.util.Map;
|
| | | import java.util.Set;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/28.
|
| | | */
|
| | | public class SPUtils {
|
| | | private static final String APP_PREFERENCES_KEY = "profile";
|
| | | private static final SharedPreferences PREFERENCES =
|
| | | HDLSdk.getInstance().getContext().getApplicationContext().getSharedPreferences(APP_PREFERENCES_KEY, Context.MODE_PRIVATE);
|
| | |
|
| | | private static SharedPreferences getAppPreference() {
|
| | | return PREFERENCES;
|
| | | }
|
| | |
|
| | |
|
| | | //======通用存储========
|
| | | public static void put(@NonNull final String key, final String value) {
|
| | | getAppPreference().edit().putString(key, value).apply();
|
| | | }
|
| | |
|
| | |
|
| | | public static String getString(@NonNull final String key) {
|
| | | return getString(key, "");
|
| | | }
|
| | |
|
| | |
|
| | | public static String getString(@NonNull final String key, final String defaultValue) {
|
| | | return getAppPreference().getString(key, defaultValue);
|
| | | }
|
| | |
|
| | |
|
| | | public static void put(@NonNull final String key, final int value) {
|
| | | put(key, value, false);
|
| | | }
|
| | |
|
| | |
|
| | | public static void put(@NonNull final String key, final int value, final boolean isCommit) {
|
| | | getAppPreference().edit().putInt(key, value).apply();
|
| | | }
|
| | |
|
| | |
|
| | | public static int getInt(@NonNull final String key) {
|
| | | return getInt(key, -1);
|
| | | }
|
| | |
|
| | |
|
| | | public static int getInt(@NonNull final String key, final int defaultValue) {
|
| | | return getAppPreference().getInt(key, defaultValue);
|
| | | }
|
| | |
|
| | |
|
| | | public static void put(@NonNull final String key, final long value) {
|
| | | getAppPreference().edit().putLong(key, value).apply();
|
| | | }
|
| | |
|
| | |
|
| | | public static long getLong(@NonNull final String key) {
|
| | | return getLong(key, -1L);
|
| | | }
|
| | |
|
| | |
|
| | | public static long getLong(@NonNull final String key, final long defaultValue) {
|
| | | return getAppPreference().getLong(key, defaultValue);
|
| | | }
|
| | |
|
| | |
|
| | | public static void put(@NonNull final String key, final float value) {
|
| | | getAppPreference().edit().putFloat(key, value).apply();
|
| | | }
|
| | |
|
| | |
|
| | | public static float getFloat(@NonNull final String key) {
|
| | | return getFloat(key, -1f);
|
| | | }
|
| | |
|
| | |
|
| | | public static float getFloat(@NonNull final String key, final float defaultValue) {
|
| | | return getAppPreference().getFloat(key, defaultValue);
|
| | | }
|
| | |
|
| | |
|
| | | public static void put(@NonNull final String key, final boolean value) {
|
| | | getAppPreference().edit().putBoolean(key, value).apply();
|
| | | }
|
| | |
|
| | |
|
| | | public static boolean getBoolean(@NonNull final String key) {
|
| | | return getBoolean(key, false);
|
| | | }
|
| | |
|
| | |
|
| | | public static boolean getBoolean(@NonNull final String key, final boolean defaultValue) {
|
| | | return getAppPreference().getBoolean(key, defaultValue);
|
| | | }
|
| | |
|
| | |
|
| | | public static void put(@NonNull final String key,
|
| | | final Set<String> value
|
| | | ) {
|
| | |
|
| | | getAppPreference().edit().putStringSet(key, value).apply();
|
| | |
|
| | | }
|
| | |
|
| | |
|
| | | public static Set<String> getStringSet(@NonNull final String key) {
|
| | | return getStringSet(key, Collections.<String>emptySet());
|
| | | }
|
| | |
|
| | |
|
| | | public static Set<String> getStringSet(@NonNull final String key,
|
| | | final Set<String> defaultValue) {
|
| | | return getAppPreference().getStringSet(key, defaultValue);
|
| | | }
|
| | |
|
| | |
|
| | | public static Map<String, ?> getAll() {
|
| | | return getAppPreference().getAll();
|
| | | }
|
| | |
|
| | |
|
| | | public static boolean contains(@NonNull final String key) {
|
| | | return getAppPreference().contains(key);
|
| | | }
|
| | |
|
| | |
|
| | | public static void remove(@NonNull final String key) {
|
| | | getAppPreference().edit().remove(key).apply();
|
| | | }
|
| | |
|
| | | public static void clear() {
|
| | | getAppPreference()
|
| | | .edit()
|
| | | .clear()
|
| | | .apply();
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils;
|
| | |
|
| | | import android.os.Handler;
|
| | | import android.os.Looper;
|
| | |
|
| | | import java.util.concurrent.ExecutorService;
|
| | | import java.util.concurrent.Executors;
|
| | | import java.util.concurrent.ScheduledExecutorService;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/15.
|
| | | */
|
| | | public class ThreadToolUtils {
|
| | |
|
| | | private final Handler uiHandler = new Handler(Looper.getMainLooper());
|
| | |
|
| | | //cpu 最大线程容纳量
|
| | | private final int coreSize = Runtime.getRuntime().availableProcessors() + 1;
|
| | |
|
| | | private ThreadToolUtils() {
|
| | | }
|
| | |
|
| | | private static class SingletonInstance {
|
| | | private static final ThreadToolUtils INSTANCE = new ThreadToolUtils();
|
| | | }
|
| | |
|
| | | public static ThreadToolUtils getInstance() {
|
| | | return SingletonInstance.INSTANCE;
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 线程数量固定的线程池
|
| | | */
|
| | | public ExecutorService newFixedThreadPool(int size) {
|
| | | if (size == 0 || coreSize < size) {
|
| | | return Executors.newFixedThreadPool(coreSize);
|
| | | }
|
| | | return Executors.newFixedThreadPool(size);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 定时任务线程池
|
| | | */
|
| | | public ScheduledExecutorService newScheduledThreadPool(int size) {
|
| | | if (size == 0 || coreSize < size) {
|
| | | return Executors.newScheduledThreadPool(coreSize);
|
| | | }
|
| | | return Executors.newScheduledThreadPool(size);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 单一线程
|
| | | */
|
| | | public ExecutorService newSingleThreadPool() {
|
| | | return Executors.newSingleThreadExecutor();
|
| | | }
|
| | |
|
| | |
|
| | | public ExecutorService newCachedThreadPool() {
|
| | | return Executors.newCachedThreadPool();
|
| | | }
|
| | |
|
| | | /**
|
| | | * 切换回主线程
|
| | | */
|
| | | public void runOnUiThread(Runnable run) {
|
| | | uiHandler.post(run);
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils.gson;
|
| | |
|
| | | import com.google.gson.Gson;
|
| | | import com.google.gson.GsonBuilder;
|
| | | import com.google.gson.JsonDeserializationContext;
|
| | | import com.google.gson.JsonDeserializer;
|
| | | import com.google.gson.JsonElement;
|
| | | import com.google.gson.JsonParseException;
|
| | | import com.google.gson.JsonPrimitive;
|
| | | import com.google.gson.JsonSerializationContext;
|
| | | import com.google.gson.JsonSerializer;
|
| | |
|
| | | import java.lang.reflect.Type;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/8.
|
| | | */
|
| | | public class GsonConvert {
|
| | |
|
| | | private static Gson gson = null;
|
| | |
|
| | | public static Gson getGson() {
|
| | | if (gson == null) {
|
| | | synchronized (GsonConvert.class) {
|
| | | if (gson == null) {
|
| | | gson = new GsonBuilder()
|
| | | .setPrettyPrinting()
|
| | | .disableHtmlEscaping()
|
| | | .registerTypeAdapter(String.class, new StringTypeAdapter())
|
| | | .create();
|
| | | }
|
| | | }
|
| | | }
|
| | | return gson;
|
| | | }
|
| | |
|
| | | private static class StringTypeAdapter implements JsonSerializer<String>, JsonDeserializer<String> {
|
| | | @Override
|
| | | public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
| | | throws JsonParseException {
|
| | | if (json instanceof JsonPrimitive) {
|
| | | return json.getAsString();
|
| | | } else {
|
| | | return json.toString();
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
|
| | | return new JsonPrimitive(src);
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common.utils.gson;
|
| | |
|
| | |
|
| | | import androidx.annotation.NonNull;
|
| | |
|
| | | import java.lang.reflect.ParameterizedType;
|
| | | import java.lang.reflect.Type;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/17.
|
| | | */
|
| | | public class ParameterizedTypeImpl implements ParameterizedType {
|
| | |
|
| | | private final Type[] actualTypeArguments;
|
| | | private final Type rawType;
|
| | | private final Type ownerType;
|
| | |
|
| | | public ParameterizedTypeImpl(Type rawType, Type[] actualTypeArguments, Type ownerType) {
|
| | | this.ownerType = ownerType;
|
| | | this.rawType = rawType;
|
| | | this.actualTypeArguments = actualTypeArguments;
|
| | | }
|
| | |
|
| | | public static Type getType(Type rawType, Type[] actualTypeArguments) {
|
| | | return new ParameterizedTypeImpl(rawType, actualTypeArguments, null);
|
| | | }
|
| | |
|
| | | @NonNull
|
| | | @Override
|
| | | public Type[] getActualTypeArguments() {
|
| | | return actualTypeArguments;
|
| | | }
|
| | |
|
| | |
|
| | | @NonNull
|
| | | @Override
|
| | | public Type getRawType() {
|
| | | return rawType;
|
| | | }
|
| | |
|
| | |
|
| | | @Override
|
| | | public Type getOwnerType() {
|
| | | return ownerType;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.common; |
| | | |
| | | import org.junit.Test; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Example local unit test, which will execute on the development machine (host). |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | public class ExampleUnitTest { |
| | | @Test |
| | | public void addition_isCorrect() { |
| | | assertEquals(4, 2 + 2); |
| | | } |
| | | } |
New file |
| | |
| | | plugins { |
| | | id 'com.android.library' |
| | | } |
| | | |
| | | android { |
| | | compileSdkVersion rootProject.compileSdkVersion |
| | | |
| | | defaultConfig { |
| | | minSdkVersion rootProject.minSdkVersion |
| | | targetSdkVersion rootProject.targetSdkVersion |
| | | versionCode 1 |
| | | versionName "1.0" |
| | | |
| | | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
| | | consumerProguardFiles "consumer-rules.pro" |
| | | } |
| | | |
| | | buildTypes { |
| | | release { |
| | | minifyEnabled false |
| | | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| | | } |
| | | } |
| | | compileOptions { |
| | | sourceCompatibility JavaVersion.VERSION_1_8 |
| | | targetCompatibility JavaVersion.VERSION_1_8 |
| | | } |
| | | } |
| | | |
| | | dependencies { |
| | | api project(path: ':hdl-socket') |
| | | implementation 'androidx.appcompat:appcompat:1.2.0' |
| | | implementation 'com.google.android.material:material:1.3.0' |
| | | } |
New file |
| | |
| | | # Add project specific ProGuard rules here. |
| | | # You can control the set of applied configuration files using the |
| | | # proguardFiles setting in build.gradle. |
| | | # |
| | | # For more details, see |
| | | # http://developer.android.com/guide/developing/tools/proguard.html |
| | | |
| | | # If your project uses WebView with JS, uncomment the following |
| | | # and specify the fully qualified class name to the JavaScript interface |
| | | # class: |
| | | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
| | | # public *; |
| | | #} |
| | | |
| | | # Uncomment this to preserve the line number information for |
| | | # debugging stack traces. |
| | | #-keepattributes SourceFile,LineNumberTable |
| | | |
| | | # If you keep the line number information, uncomment this to |
| | | # hide the original source file name. |
| | | #-renamesourcefileattribute SourceFile |
New file |
| | |
| | | package com.hdl.sdk.connect; |
| | | |
| | | import android.content.Context; |
| | | |
| | | import androidx.test.platform.app.InstrumentationRegistry; |
| | | import androidx.test.ext.junit.runners.AndroidJUnit4; |
| | | |
| | | import org.junit.Test; |
| | | import org.junit.runner.RunWith; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Instrumented test, which will execute on an Android device. |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | @RunWith(AndroidJUnit4.class) |
| | | public class ExampleInstrumentedTest { |
| | | @Test |
| | | public void useAppContext() { |
| | | // Context of the app under test. |
| | | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
| | | assertEquals("com.hdl.sdk.connect.test", appContext.getPackageName()); |
| | | } |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| | | package="com.hdl.sdk.connect"> |
| | | |
| | | </manifest> |
New file |
| | |
| | | package com.hdl.sdk.connect;
|
| | |
|
| | | import android.text.TextUtils;
|
| | | import android.util.ArrayMap;
|
| | | import android.util.Log;
|
| | |
|
| | | import com.google.gson.Gson;
|
| | | import com.google.gson.JsonArray;
|
| | | import com.google.gson.JsonObject;
|
| | | import com.google.gson.reflect.TypeToken;
|
| | | import com.hdl.sdk.common.config.TopicConstant;
|
| | | import com.hdl.sdk.common.event.EventDispatcher;
|
| | | import com.hdl.sdk.common.event.EventListener;
|
| | | import com.hdl.sdk.common.utils.IdUtils;
|
| | | import com.hdl.sdk.common.utils.IpUtils;
|
| | | import com.hdl.sdk.common.utils.SPUtils;
|
| | | import com.hdl.sdk.common.utils.ThreadToolUtils;
|
| | | import com.hdl.sdk.common.utils.gson.GsonConvert;
|
| | | import com.hdl.sdk.connect.bean.BaseLocalRequest;
|
| | | import com.hdl.sdk.connect.bean.BaseLocalResponse;
|
| | | import com.hdl.sdk.connect.bean.DeviceControlRequest;
|
| | | import com.hdl.sdk.connect.bean.FunctionAttributeRequest;
|
| | | import com.hdl.sdk.connect.bean.GatewaySearchBean;
|
| | | import com.hdl.sdk.connect.bean.LinkRequest;
|
| | | import com.hdl.sdk.connect.bean.LinkResponse;
|
| | | import com.hdl.sdk.connect.bean.PropertyReadRequest;
|
| | | import com.hdl.sdk.connect.bean.PropertyUpRequest;
|
| | | import com.hdl.sdk.connect.protocol.LinkMessageDecoder;
|
| | | import com.hdl.sdk.connect.protocol.LinkMessageEncoder;
|
| | | import com.hdl.sdk.socket.SocketBoot;
|
| | | import com.hdl.sdk.socket.SocketOptions;
|
| | | import com.hdl.sdk.socket.client.TcpClient;
|
| | | import com.hdl.sdk.socket.client.UdpClient;
|
| | | import com.hdl.sdk.socket.codec.MessagePipeLine;
|
| | | import com.hdl.sdk.socket.listener.ConnectStatusListener;
|
| | | import com.hdl.sdk.socket.listener.SendListener;
|
| | |
|
| | | import java.io.UnsupportedEncodingException;
|
| | | import java.lang.reflect.ParameterizedType;
|
| | | import java.nio.charset.StandardCharsets;
|
| | | import java.util.ArrayList;
|
| | | import java.util.List;
|
| | | import java.util.concurrent.ExecutionException;
|
| | | import java.util.concurrent.ScheduledExecutorService;
|
| | | import java.util.concurrent.TimeUnit;
|
| | | import java.util.concurrent.atomic.AtomicInteger;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/26.
|
| | | * 1、通过Udp 组播或者广播搜索网关
|
| | | * 2、通过Udp 获取Tcp ip 端口统一8586
|
| | | */
|
| | | public class HDLSocket {
|
| | |
|
| | | private static final String GATEWAY_KEY = "gateway_key";
|
| | | private static final String TCP_IP_KEY = "tcp_ip_key";
|
| | | private String gatewayId;
|
| | |
|
| | | public interface CallBack {
|
| | |
|
| | | void onError(String error);
|
| | |
|
| | | void onResponse(String data);
|
| | |
|
| | | }
|
| | |
|
| | | /**
|
| | | * udp默认组播ip
|
| | | */
|
| | | private static final String UDP_GROUP_IP = "239.0.168.188";
|
| | |
|
| | | /**
|
| | | * udp默认端口
|
| | | */
|
| | | private static final int UDP_PORT = 8585;
|
| | |
|
| | | /**
|
| | | * tcp默认端口
|
| | | */
|
| | | private static final int TCP_PORT = 8586;
|
| | |
|
| | | private String tcpIp;
|
| | | private int tcpPort;
|
| | |
|
| | | private int udpPort;
|
| | | private String udpIp;
|
| | |
|
| | | private static SocketBoot updBoot;
|
| | | private SocketBoot tcpBoot;
|
| | |
|
| | | private ConnectStatusListener statusListener;
|
| | |
|
| | | private EventListener searchEvent;
|
| | | private CallBack searchCallBack;
|
| | | private ScheduledExecutorService searchGatewayThread;
|
| | | private final AtomicInteger searchCount = new AtomicInteger(0);
|
| | |
|
| | | private HDLSocket() {
|
| | | statusListener = new ConnectStatusListener() {
|
| | | @Override
|
| | | public void onConnecting() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onConnected() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onConnectFailed() {
|
| | |
|
| | | }
|
| | | };
|
| | | searchEvent = new EventListener() {
|
| | | @Override
|
| | | public void onMessage(Object msg) {
|
| | | try {
|
| | | if (msg instanceof LinkResponse) {
|
| | | LinkResponse linkResponse = (LinkResponse) msg;
|
| | | String data = linkResponse.getData();
|
| | | if (!TextUtils.isEmpty(data)) {
|
| | | final BaseLocalResponse<GatewaySearchBean> response = GsonConvert.getGson().fromJson(data, new TypeToken<BaseLocalResponse<GatewaySearchBean>>() {
|
| | | }.getType());
|
| | | GatewaySearchBean searchBean = response.getObjects();
|
| | | if (searchBean != null) {
|
| | | gatewayId = searchBean.getGatewayId();
|
| | | if (!TextUtils.isEmpty(gatewayId)) {
|
| | | SPUtils.put(GATEWAY_KEY, gatewayId);
|
| | | }
|
| | | tcpIp = searchBean.getIp_address();
|
| | | if (!TextUtils.isEmpty(tcpIp)) {
|
| | | SPUtils.put(TCP_IP_KEY, tcpIp);
|
| | | }
|
| | | }
|
| | |
|
| | | if (searchCallBack != null) {
|
| | | searchCallBack.onResponse(linkResponse.toString());
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
| | | } catch (Exception e) {
|
| | | if (searchCallBack != null) {
|
| | | searchCallBack.onError("解析失败");
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | }
|
| | | };
|
| | | }
|
| | |
|
| | | private static class SingletonInstance {
|
| | | private static final HDLSocket INSTANCE = new HDLSocket();
|
| | | }
|
| | |
|
| | | public static HDLSocket getInstance() {
|
| | | return SingletonInstance.INSTANCE;
|
| | | }
|
| | |
|
| | |
|
| | | private SocketOptions getUdpOptions() {
|
| | | final SocketOptions options = new SocketOptions();
|
| | | final MessagePipeLine pipeLine = new MessagePipeLine();
|
| | | pipeLine.add(new LinkMessageDecoder());
|
| | | pipeLine.add(new LinkMessageEncoder());
|
| | | options.setHandleMessage(pipeLine);
|
| | | options.setEnabledHeartbeat(false);
|
| | | return options;
|
| | | }
|
| | |
|
| | | private SocketOptions getTcpOptions() {
|
| | | final SocketOptions options = new SocketOptions();
|
| | | final MessagePipeLine pipeLine = new MessagePipeLine();
|
| | | pipeLine.add(new LinkMessageDecoder());
|
| | | pipeLine.add(new LinkMessageEncoder());
|
| | | options.setHandleMessage(pipeLine);
|
| | | options.setEnabledHeartbeat(false);
|
| | | return options;
|
| | | }
|
| | |
|
| | | private int getUdpPort() {
|
| | | return UDP_PORT;
|
| | | }
|
| | |
|
| | | public int getTcpPort() {
|
| | | return TCP_PORT;
|
| | | }
|
| | |
|
| | | public String getTcpIp() {
|
| | | if (!TextUtils.isEmpty(tcpIp)) {
|
| | | return tcpIp;
|
| | | }
|
| | | return SPUtils.getString(TCP_IP_KEY, "");
|
| | | }
|
| | |
|
| | | public String getGatewayId() {
|
| | | if (!TextUtils.isEmpty(gatewayId)) {
|
| | | return gatewayId;
|
| | | }
|
| | | return SPUtils.getString(GATEWAY_KEY, "");
|
| | | }
|
| | |
|
| | |
|
| | | private String getUdpIp() {
|
| | | if (TextUtils.isEmpty(udpIp)) {
|
| | | udpIp = UDP_GROUP_IP;
|
| | | }
|
| | | return udpIp;
|
| | | }
|
| | |
|
| | | public void searchGateway() {
|
| | | searchGateway(null);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 组播搜索
|
| | | */
|
| | | public void searchGateway(CallBack callBack) {
|
| | | this.searchCallBack = callBack;
|
| | |
|
| | | if (searchGatewayThread != null) {
|
| | | searchGatewayThread.shutdownNow();
|
| | | }
|
| | |
|
| | |
|
| | | new Thread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | while (TextUtils.isEmpty(gatewayId)) {
|
| | | //搜索网关
|
| | | searchGateway(IdUtils.getUUId(), searchEvent);
|
| | | try {
|
| | | Thread.sleep(1000L);
|
| | | } catch (InterruptedException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | }
|
| | | }).start();
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 通过组播搜索网关
|
| | | */
|
| | | public void searchGateway(String msgId, EventListener eventListener) {
|
| | | searchGateway(getUdpIp(), getUdpPort(), msgId, eventListener);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 通过广播搜索网关
|
| | | */
|
| | | public void searchGatewayByBroadcast(String msgId, EventListener eventListener) {
|
| | | searchGateway(IpUtils.getBroadcastAddress(), getUdpPort(), msgId, eventListener);
|
| | | }
|
| | |
|
| | | /**
|
| | | * 默认是组播搜索网关
|
| | | */
|
| | | public void searchGateway(String ip, int port, String msgId, EventListener eventListener) {
|
| | |
|
| | | if (updBoot == null) {
|
| | | updBoot = UdpClient.init(ip, port, getUdpOptions());
|
| | | updBoot.connect();
|
| | | }
|
| | |
|
| | |
|
| | | String time = String.valueOf(System.currentTimeMillis());
|
| | | JsonObject jsonObject = new JsonObject();
|
| | | jsonObject.addProperty("id", msgId);
|
| | | jsonObject.addProperty("time_stamp", time);
|
| | |
|
| | | EventDispatcher.getInstance().registerIo(TopicConstant.GATEWAY_SEARCH_REPLY, eventListener);
|
| | | LinkRequest message = new LinkRequest(TopicConstant.GATEWAY_SEARCH,
|
| | | jsonObject.toString());
|
| | |
|
| | | try {
|
| | | updBoot.sendMsg(message.toString().getBytes("utf-8"));
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 获取设备列表
|
| | | */
|
| | | public void getDeviceList(CallBack callBack) {
|
| | | if (!TextUtils.isEmpty(getGatewayId()) && !TextUtils.isEmpty(getTcpIp())) {
|
| | | String time = String.valueOf(System.currentTimeMillis());
|
| | | JsonObject jsonObject = new JsonObject();
|
| | | jsonObject.addProperty("id", IdUtils.getUUId());
|
| | | jsonObject.addProperty("time_stamp", time);
|
| | |
|
| | | String topic = String.format(TopicConstant.GET_DEVICE_LIST, getGatewayId());
|
| | |
|
| | | LinkRequest message = new LinkRequest(topic,
|
| | | jsonObject.toString());
|
| | |
|
| | | String replyTopic = String.format(TopicConstant.GET_DEVICE_LIST_REPLY, getGatewayId());
|
| | | try {
|
| | | sendMsg(message.toString().getBytes("utf-8"), replyTopic, callBack, new SendListener() {
|
| | | @Override
|
| | | public void onSucceed() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onError() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("获取设备列表失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | if (callBack != null) {
|
| | | callBack.onError("获取设备列表失败");
|
| | | }
|
| | | }
|
| | | } else {
|
| | | if (callBack != null) {
|
| | | callBack.onError("ip地址丢失");
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 获取功能列表
|
| | | */
|
| | | public void getFunctionList(CallBack callBack) {
|
| | | if (!TextUtils.isEmpty(getGatewayId()) && !TextUtils.isEmpty(getTcpIp())) {
|
| | | String time = String.valueOf(System.currentTimeMillis());
|
| | | JsonObject jsonObject = new JsonObject();
|
| | | jsonObject.addProperty("id", IdUtils.getUUId());
|
| | | jsonObject.addProperty("time_stamp", time);
|
| | |
|
| | | String topic = String.format(TopicConstant.GET_FUNCTION_LIST, getGatewayId());
|
| | |
|
| | | LinkRequest message = new LinkRequest(topic,
|
| | | jsonObject.toString());
|
| | |
|
| | | String replyTopic = String.format(TopicConstant.GET_FUNCTION_LIST_REPLY, getGatewayId());
|
| | | try {
|
| | | sendMsg(message.toString().getBytes("utf-8"), replyTopic, callBack, new SendListener() {
|
| | | @Override
|
| | | public void onSucceed() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onError() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("获取功能列表失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | if (callBack != null) {
|
| | | callBack.onError("获取功能列表失败");
|
| | | }
|
| | | }
|
| | | } else {
|
| | | if (callBack != null) {
|
| | | callBack.onError("ip地址丢失");
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 获取功能属性
|
| | | *
|
| | | * @param callBack
|
| | | * @param sid
|
| | | */
|
| | | public void getFunctionAttribute(CallBack callBack, String... sid) {
|
| | | if (!TextUtils.isEmpty(getGatewayId()) && !TextUtils.isEmpty(getTcpIp())) {
|
| | | String time = String.valueOf(System.currentTimeMillis());
|
| | |
|
| | | final BaseLocalResponse<List<FunctionAttributeRequest>> data = new BaseLocalResponse<>();
|
| | | data.setId(IdUtils.getUUId());
|
| | | data.setTime_stamp(time);
|
| | | List<FunctionAttributeRequest> list = new ArrayList<>();
|
| | | for (String s : sid) {
|
| | | list.add(new FunctionAttributeRequest(s));
|
| | | }
|
| | | data.setObjects(list);
|
| | |
|
| | | String topic = String.format(TopicConstant.GET_FUNCTION_ATTRIBUTE, getGatewayId());
|
| | | LinkRequest message = new LinkRequest(topic,
|
| | | GsonConvert.getGson().toJson(data));
|
| | |
|
| | | String replyTopic = String.format(TopicConstant.GET_FUNCTION_ATTRIBUTE_REPLY, getGatewayId());
|
| | | try {
|
| | | sendMsg(message.toString().getBytes("utf-8"), replyTopic, callBack, new SendListener() {
|
| | | @Override
|
| | | public void onSucceed() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onError() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("获取功能属性失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | if (callBack != null) {
|
| | | callBack.onError("获取功能属性失败");
|
| | | }
|
| | | }
|
| | | } else {
|
| | | if (callBack != null) {
|
| | | callBack.onError("ip地址丢失");
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * 设备控制
|
| | | */
|
| | | public void propertyDown(List<DeviceControlRequest> request, CallBack callBack) {
|
| | |
|
| | | if (!TextUtils.isEmpty(getGatewayId()) && !TextUtils.isEmpty(getTcpIp())) {
|
| | | String time = String.valueOf(System.currentTimeMillis());
|
| | |
|
| | | final BaseLocalResponse<List<DeviceControlRequest>> data = new BaseLocalResponse<>();
|
| | | data.setId(IdUtils.getUUId());
|
| | | data.setTime_stamp(time);
|
| | | data.setObjects(request);
|
| | |
|
| | |
|
| | | String topic = String.format(TopicConstant.PROPERTY_DOWN, getGatewayId());
|
| | | LinkRequest message = new LinkRequest(topic,
|
| | | GsonConvert.getGson().toJson(request));
|
| | |
|
| | | String replyTopic = String.format(TopicConstant.PROPERTY_DOWN_REPLY, getGatewayId());
|
| | | try {
|
| | | sendMsg(message.toString().getBytes("utf-8"), replyTopic, callBack, new SendListener() {
|
| | | @Override
|
| | | public void onSucceed() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onError() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("控制指令发送失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | if (callBack != null) {
|
| | | callBack.onError("控制指令发送失败");
|
| | | }
|
| | | }
|
| | | } else {
|
| | | if (callBack != null) {
|
| | | callBack.onError("控制指令发送失败");
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | /**
|
| | | * 状态上报
|
| | | */
|
| | | public void propertyUp(List<PropertyUpRequest> request, CallBack callBack) {
|
| | | if (!TextUtils.isEmpty(getGatewayId()) && !TextUtils.isEmpty(getTcpIp())) {
|
| | | String time = String.valueOf(System.currentTimeMillis());
|
| | |
|
| | | final BaseLocalResponse<List<PropertyUpRequest>> data = new BaseLocalResponse<>();
|
| | | data.setId(IdUtils.getUUId());
|
| | | data.setTime_stamp(time);
|
| | | data.setObjects(request);
|
| | |
|
| | |
|
| | | String topic = String.format(TopicConstant.PROPERTY_UP, getGatewayId());
|
| | | LinkRequest message = new LinkRequest(topic,
|
| | | GsonConvert.getGson().toJson(request));
|
| | |
|
| | | String replyTopic = String.format(TopicConstant.PROPERTY_UP_REPLY, getGatewayId());
|
| | | try {
|
| | | sendMsg(message.toString().getBytes("utf-8"), replyTopic, callBack, new SendListener() {
|
| | | @Override
|
| | | public void onSucceed() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onError() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("指令发送失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | if (callBack != null) {
|
| | | callBack.onError("指令发送失败");
|
| | | }
|
| | | }
|
| | | } else {
|
| | | if (callBack != null) {
|
| | | callBack.onError("指令发送失败");
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | /**
|
| | | * 读取状态
|
| | | */
|
| | | public void propertyRead(List<PropertyReadRequest> request, CallBack callBack) {
|
| | | if (!TextUtils.isEmpty(getGatewayId()) && !TextUtils.isEmpty(getTcpIp())) {
|
| | | String time = String.valueOf(System.currentTimeMillis());
|
| | |
|
| | | final BaseLocalResponse<List<PropertyReadRequest>> data = new BaseLocalResponse<>();
|
| | | data.setId(IdUtils.getUUId());
|
| | | data.setTime_stamp(time);
|
| | | data.setObjects(request);
|
| | |
|
| | |
|
| | | String topic = String.format(TopicConstant.PROPERTY_READ, getGatewayId());
|
| | | LinkRequest message = new LinkRequest(topic,
|
| | | GsonConvert.getGson().toJson(request));
|
| | |
|
| | | String replyTopic = String.format(TopicConstant.PROPERTY_READ_REPLY, getGatewayId());
|
| | | try {
|
| | | sendMsg(message.toString().getBytes("utf-8"), replyTopic, callBack, new SendListener() {
|
| | | @Override
|
| | | public void onSucceed() {
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onError() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("指令发送失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | if (callBack != null) {
|
| | | callBack.onError("指令发送失败");
|
| | | }
|
| | | }
|
| | | } else {
|
| | | if (callBack != null) {
|
| | | callBack.onError("指令发送失败");
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | public SocketBoot getTcp() throws RuntimeException {
|
| | | if (TextUtils.isEmpty(getTcpIp())) {
|
| | | throw new RuntimeException("请搜索网关");
|
| | | }
|
| | | if (tcpBoot == null) {
|
| | | tcpBoot = TcpClient.init(getTcpIp(), getTcpPort(), getTcpOptions());
|
| | | }
|
| | | return tcpBoot;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 清空缓存
|
| | | */
|
| | | public void clearCache() {
|
| | | SPUtils.remove(TCP_IP_KEY);
|
| | | SPUtils.remove(GATEWAY_KEY);
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 发送指令
|
| | | * 1秒没响应就让他重新发送,重试3次
|
| | | */
|
| | | public void sendMsg(byte[] data, String eventTag, CallBack callBack, SendListener sendListener) {
|
| | |
|
| | | try {
|
| | | final AtomicInteger sendCount = new AtomicInteger(0);
|
| | |
|
| | | final ScheduledExecutorService threadPool = ThreadToolUtils.getInstance().newScheduledThreadPool(1);
|
| | | final EventListener eventListener = new EventListener() {
|
| | | @Override
|
| | | public void onMessage(Object msg) {
|
| | | if (msg instanceof LinkResponse) {
|
| | |
|
| | | if (callBack != null) {
|
| | | callBack.onResponse(msg.toString());
|
| | | }
|
| | | threadPool.shutdownNow();
|
| | | }
|
| | | }
|
| | | };
|
| | |
|
| | | threadPool.scheduleWithFixedDelay(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | if (sendCount.get() < 3) {
|
| | | sendCount.set(sendCount.get() + 1);
|
| | | getTcp().sendMsg(data);
|
| | | } else {
|
| | | threadPool.shutdownNow();
|
| | | EventDispatcher.getInstance().remove(eventTag, eventListener);
|
| | | ThreadToolUtils.getInstance().runOnUiThread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("发送失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | }
|
| | | }
|
| | | }, 1000, 500, TimeUnit.MILLISECONDS);
|
| | | EventDispatcher.getInstance().register(eventTag, eventListener);
|
| | | getTcp().sendMsg(data, new SendListener() {
|
| | | @Override
|
| | | public void onSucceed() {
|
| | | if (sendListener != null) {
|
| | | sendListener.onSucceed();
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onError() {
|
| | | if (sendListener != null) {
|
| | | sendListener.onError();
|
| | | }
|
| | | }
|
| | | });
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | ThreadToolUtils.getInstance().runOnUiThread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | if (callBack != null) {
|
| | | callBack.onError("发送失败");
|
| | | }
|
| | | }
|
| | | });
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import java.io.Serializable;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/29.
|
| | | */
|
| | | public class BaseLocalRequest<T> implements Serializable {
|
| | |
|
| | |
|
| | | private String id;
|
| | | private String code;
|
| | | private String time_stamp;
|
| | | private T objects;
|
| | |
|
| | | public String getId() {
|
| | | return id;
|
| | | }
|
| | |
|
| | | public void setId(String id) {
|
| | | this.id = id;
|
| | | }
|
| | |
|
| | | public String getCode() {
|
| | | return code;
|
| | | }
|
| | |
|
| | | public void setCode(String code) {
|
| | | this.code = code;
|
| | | }
|
| | |
|
| | | public String getTime_stamp() {
|
| | | return time_stamp;
|
| | | }
|
| | |
|
| | | public void setTime_stamp(String time_stamp) {
|
| | | this.time_stamp = time_stamp;
|
| | | }
|
| | |
|
| | | public T getObjects() {
|
| | | return objects;
|
| | | }
|
| | |
|
| | | public void setObjects(T objects) {
|
| | | this.objects = objects;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import java.io.Serializable;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/29.
|
| | | */
|
| | | public class BaseLocalResponse<T> implements Serializable {
|
| | |
|
| | |
|
| | | private String id;
|
| | | private String time_stamp;
|
| | | private T objects;
|
| | |
|
| | | public String getId() {
|
| | | return id;
|
| | | }
|
| | |
|
| | | public void setId(String id) {
|
| | | this.id = id;
|
| | | }
|
| | |
|
| | | public String getTime_stamp() {
|
| | | return time_stamp;
|
| | | }
|
| | |
|
| | | public void setTime_stamp(String time_stamp) {
|
| | | this.time_stamp = time_stamp;
|
| | | }
|
| | |
|
| | | public T getObjects() {
|
| | | return objects;
|
| | | }
|
| | |
|
| | | public void setObjects(T objects) {
|
| | | this.objects = objects;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import java.io.Serializable;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class DeviceControlRequest implements Serializable {
|
| | |
|
| | | private String sid;
|
| | | private List<StatusBean> status;
|
| | |
|
| | | public String getSid() {
|
| | | return sid;
|
| | | }
|
| | |
|
| | | public void setSid(String sid) {
|
| | | this.sid = sid;
|
| | | }
|
| | |
|
| | | public List<StatusBean> getStatus() {
|
| | | return status;
|
| | | }
|
| | |
|
| | | public void setStatus(List<StatusBean> status) {
|
| | | this.status = status;
|
| | | }
|
| | |
|
| | | public static class StatusBean implements Serializable {
|
| | | private String key;
|
| | | private String value;
|
| | |
|
| | | public String getKey() {
|
| | | return key;
|
| | | }
|
| | |
|
| | | public void setKey(String key) {
|
| | | this.key = key;
|
| | | }
|
| | |
|
| | | public String getValue() {
|
| | | return value;
|
| | | }
|
| | |
|
| | | public void setValue(String value) {
|
| | | this.value = value;
|
| | | }
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import java.io.Serializable;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class FunctionAttributeRequest implements Serializable {
|
| | |
|
| | | private String sid;
|
| | |
|
| | | public FunctionAttributeRequest(String sid) {
|
| | | this.sid = sid;
|
| | | }
|
| | |
|
| | | public String getSid() {
|
| | | return sid;
|
| | | }
|
| | |
|
| | | public void setSid(String sid) {
|
| | | this.sid = sid;
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import java.io.Serializable;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/29.
|
| | | * 网关搜索
|
| | | */
|
| | | public class GatewaySearchBean implements Serializable {
|
| | |
|
| | |
|
| | | private String device_model;
|
| | | private String device_name;
|
| | | private String device_mac;
|
| | | private String gatewayId;
|
| | | private String gatewayType;
|
| | | private String gateway_type;
|
| | | private String oid;
|
| | | private String ip_address;
|
| | | private String access_mode;
|
| | | private String master;
|
| | |
|
| | | public String getDevice_model() {
|
| | | return device_model;
|
| | | }
|
| | |
|
| | | public void setDevice_model(String device_model) {
|
| | | this.device_model = device_model;
|
| | | }
|
| | |
|
| | | public String getDevice_name() {
|
| | | return device_name;
|
| | | }
|
| | |
|
| | | public void setDevice_name(String device_name) {
|
| | | this.device_name = device_name;
|
| | | }
|
| | |
|
| | | public String getDevice_mac() {
|
| | | return device_mac;
|
| | | }
|
| | |
|
| | | public void setDevice_mac(String device_mac) {
|
| | | this.device_mac = device_mac;
|
| | | }
|
| | |
|
| | | public String getGatewayId() {
|
| | | return gatewayId;
|
| | | }
|
| | |
|
| | | public void setGatewayId(String gatewayId) {
|
| | | this.gatewayId = gatewayId;
|
| | | }
|
| | |
|
| | | public String getGatewayType() {
|
| | | return gatewayType;
|
| | | }
|
| | |
|
| | | public void setGatewayType(String gatewayType) {
|
| | | this.gatewayType = gatewayType;
|
| | | }
|
| | |
|
| | | public String getGateway_type() {
|
| | | return gateway_type;
|
| | | }
|
| | |
|
| | | public void setGateway_type(String gateway_type) {
|
| | | this.gateway_type = gateway_type;
|
| | | }
|
| | |
|
| | | public String getOid() {
|
| | | return oid;
|
| | | }
|
| | |
|
| | | public void setOid(String oid) {
|
| | | this.oid = oid;
|
| | | }
|
| | |
|
| | | public String getIp_address() {
|
| | | return ip_address;
|
| | | }
|
| | |
|
| | | public void setIp_address(String ip_address) {
|
| | | this.ip_address = ip_address;
|
| | | }
|
| | |
|
| | | public String getAccess_mode() {
|
| | | return access_mode;
|
| | | }
|
| | |
|
| | | public void setAccess_mode(String access_mode) {
|
| | | this.access_mode = access_mode;
|
| | | }
|
| | |
|
| | | public String getMaster() {
|
| | | return master;
|
| | | }
|
| | |
|
| | | public void setMaster(String master) {
|
| | | this.master = master;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import android.text.TextUtils;
|
| | |
|
| | | import androidx.annotation.NonNull;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/29.
|
| | | */
|
| | | public class LinkRequest {
|
| | | private String topic;
|
| | | private String data;
|
| | | private int length;
|
| | |
|
| | | public LinkRequest() {
|
| | | }
|
| | |
|
| | | public LinkRequest(String topic, String data) {
|
| | | this.topic = topic;
|
| | | setData(data);
|
| | | }
|
| | |
|
| | | public String getTopic() {
|
| | | return topic;
|
| | | }
|
| | |
|
| | | public void setTopic(String topic) {
|
| | | this.topic = topic;
|
| | | }
|
| | |
|
| | | public String getData() {
|
| | | return data;
|
| | | }
|
| | |
|
| | | public void setData(String data) {
|
| | | this.data = data;
|
| | | if (!TextUtils.isEmpty(data)) {
|
| | | setLength(data.length());
|
| | | } else {
|
| | | setLength(0);
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | public int getLength() {
|
| | | return length;
|
| | | }
|
| | |
|
| | | private void setLength(int length) {
|
| | | this.length = length;
|
| | | }
|
| | |
|
| | | @NonNull
|
| | | @Override
|
| | | public String toString() {
|
| | | return "Topic:" +
|
| | | getTopic() +
|
| | | "\r\n" +
|
| | | "Length:" +
|
| | | getLength() +
|
| | | "\r\n\r\n" +
|
| | | getData();
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import android.text.TextUtils;
|
| | |
|
| | | import androidx.annotation.NonNull;
|
| | |
|
| | | import com.hdl.sdk.common.utils.gson.GsonConvert;
|
| | |
|
| | | import java.io.Serializable;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/27.
|
| | | */
|
| | | public class LinkResponse implements Serializable {
|
| | |
|
| | | private String topic;
|
| | | private String data;
|
| | | private int length;
|
| | |
|
| | | public String getTopic() {
|
| | | return topic;
|
| | | }
|
| | |
|
| | | public void setTopic(String topic) {
|
| | | this.topic = topic;
|
| | | }
|
| | |
|
| | | public String getData() {
|
| | | return data;
|
| | | }
|
| | |
|
| | | public void setData(String data) {
|
| | | this.data = data;
|
| | | if (!TextUtils.isEmpty(data)) {
|
| | | setLength(data.length());
|
| | | } else {
|
| | | setLength(0);
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | public int getLength() {
|
| | | return length;
|
| | | }
|
| | |
|
| | | private void setLength(int length) {
|
| | | this.length = length;
|
| | | }
|
| | |
|
| | | @NonNull
|
| | | @Override
|
| | | public String toString() {
|
| | | return GsonConvert.getGson().toJson(this);
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class PropertyReadRequest {
|
| | |
|
| | | private String sid;
|
| | |
|
| | | public String getSid() {
|
| | | return sid;
|
| | | }
|
| | |
|
| | | public void setSid(String sid) {
|
| | | this.sid = sid;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.bean;
|
| | |
|
| | | import java.io.Serializable;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class PropertyUpRequest implements Serializable {
|
| | |
|
| | | private String sid;
|
| | | private List<StatusBean> status;
|
| | |
|
| | | public String getSid() {
|
| | | return sid;
|
| | | }
|
| | |
|
| | | public void setSid(String sid) {
|
| | | this.sid = sid;
|
| | | }
|
| | |
|
| | | public List<StatusBean> getStatus() {
|
| | | return status;
|
| | | }
|
| | |
|
| | | public void setStatus(List<StatusBean> status) {
|
| | | this.status = status;
|
| | | }
|
| | |
|
| | | public static class StatusBean implements Serializable{
|
| | | private String key;
|
| | | private String value;
|
| | |
|
| | | public String getKey() {
|
| | | return key;
|
| | | }
|
| | |
|
| | | public void setKey(String key) {
|
| | | this.key = key;
|
| | | }
|
| | |
|
| | | public String getValue() {
|
| | | return value;
|
| | | }
|
| | |
|
| | | public void setValue(String value) {
|
| | | this.value = value;
|
| | | }
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.protocol;
|
| | |
|
| | |
|
| | | import com.hdl.sdk.common.event.EventDispatcher;
|
| | | import com.hdl.sdk.common.utils.ByteUtils;
|
| | | import com.hdl.sdk.connect.bean.LinkResponse;
|
| | | import com.hdl.sdk.connect.utils.ProtocolParse;
|
| | | import com.hdl.sdk.socket.codec.ByteToMessageDecoder;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | * link协议粘包拆包
|
| | | */
|
| | | public class LinkMessageDecoder extends ByteToMessageDecoder<LinkResponse> {
|
| | |
|
| | | private final List<Byte> bytes;
|
| | |
|
| | | private final byte[] head = "Topic:".getBytes();
|
| | | private final byte[] body = "\r\n\r\n".getBytes();
|
| | |
|
| | | public LinkMessageDecoder() {
|
| | | this.bytes = new ArrayList<>();
|
| | | }
|
| | |
|
| | | @Override
|
| | | protected LinkResponse decoder(Object msg) throws Exception {
|
| | | LinkResponse response = new LinkResponse();
|
| | | if (msg instanceof byte[]) {
|
| | | //解析流
|
| | | byte[] data = (byte[]) msg;
|
| | | bytes.addAll(ByteUtils.toByteList(data));
|
| | |
|
| | | byte[] byteArray = ByteUtils.toByteArray(bytes);
|
| | | int headIndex = ByteUtils.getByteIndexOf(byteArray, head);
|
| | | if (headIndex > 0) {
|
| | | //移动到head 开始位置
|
| | | bytes.subList(0, headIndex).clear();
|
| | | byteArray = ByteUtils.toByteArray(bytes);
|
| | | }
|
| | |
|
| | | int bodyIndex = ByteUtils.getByteIndexOf(byteArray, body);
|
| | | if (bodyIndex < 0) {
|
| | | //头部未获取完成
|
| | | return null;
|
| | | }
|
| | | int bodyStartIndex = bodyIndex + body.length;
|
| | |
|
| | | //解析头部
|
| | | ProtocolParse parse = new ProtocolParse(byteArray);
|
| | | response.setTopic(parse.getTopic());
|
| | |
|
| | | int bodyLength = parse.getLength();
|
| | | if (bodyLength > 0) {
|
| | | if (byteArray.length >= bodyLength + bodyStartIndex) {
|
| | | byte[] body = ByteUtils.getRangeBytes(bytes, bodyStartIndex, bodyStartIndex + bodyLength);
|
| | | response.setData(new String(body, "utf-8"));
|
| | |
|
| | | if (byteArray.length >= bodyLength + bodyStartIndex) {
|
| | | //保存余留
|
| | | byte[] remaining = ByteUtils.getRangeBytes(bytes, bodyStartIndex + bodyLength, byteArray.length);
|
| | | bytes.clear();
|
| | | for (byte b : remaining) {
|
| | | bytes.add(b);
|
| | | }
|
| | | }
|
| | | //解析完成,topic发送一次
|
| | | EventDispatcher.getInstance().post(response.getTopic(), response);
|
| | | return response;
|
| | | }
|
| | | } else if (bodyLength == 0) {
|
| | | //body为空
|
| | | return response;
|
| | | }
|
| | |
|
| | | }
|
| | | return null;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.protocol;
|
| | |
|
| | |
|
| | | import com.hdl.sdk.socket.codec.MessageToByteEncoder;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | * link协议合包
|
| | | */
|
| | | public class LinkMessageEncoder extends MessageToByteEncoder {
|
| | |
|
| | | @Override
|
| | | protected byte[] encode(byte[] data) throws Exception {
|
| | | return data;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect.utils;
|
| | |
|
| | | import android.text.TextUtils;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | * 解析Link协议
|
| | | */
|
| | | public class ProtocolParse {
|
| | |
|
| | | private String topic;
|
| | | private int length;
|
| | | private int dataIndex;
|
| | |
|
| | | public ProtocolParse(byte[] bytes) {
|
| | | parse(bytes);
|
| | | }
|
| | |
|
| | | private void parse(byte[] bytes) {
|
| | | try {
|
| | | String[] split = new String(bytes, "utf-8").split("\r\n");
|
| | | setTopic(parseTopic(split));
|
| | | setLength(parseLength(split));
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | |
|
| | | private static String parseTopic(String[] bytes) {
|
| | | try {
|
| | | for (String s : bytes) {
|
| | | if (s.startsWith("Topic:")) {
|
| | | return s.replace("Topic:", "");
|
| | | }
|
| | | }
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
|
| | | private static int parseLength(String[] bytes) {
|
| | | try {
|
| | | for (String s : bytes) {
|
| | | if (!TextUtils.isEmpty(s) && s.startsWith("Length:")) {
|
| | | return Integer.parseInt(s.replace("Length:", ""));
|
| | | }
|
| | | }
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | return -1;
|
| | | }
|
| | |
|
| | |
|
| | |
|
| | | public String getTopic() {
|
| | | return topic;
|
| | | }
|
| | |
|
| | | public void setTopic(String topic) {
|
| | | this.topic = topic;
|
| | | }
|
| | |
|
| | | public int getLength() {
|
| | | return length;
|
| | | }
|
| | |
|
| | | public void setLength(int length) {
|
| | | this.length = length;
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.connect; |
| | | |
| | | import org.junit.Test; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Example local unit test, which will execute on the development machine (host). |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | public class ExampleUnitTest { |
| | | @Test |
| | | public void addition_isCorrect() { |
| | | assertEquals(4, 2 + 2); |
| | | } |
| | | } |
New file |
| | |
| | | # hdl-socket库
|
| | |
|
| | | ##说明
|
| | | tcp+udp 通用封装,实现基本连接
|
| | |
|
| | | ##用法
|
| | | 1、创建Tcp对象
|
| | | 2、创建Udp对象
|
New file |
| | |
| | | plugins { |
| | | id 'com.android.library' |
| | | } |
| | | |
| | | android { |
| | | compileSdkVersion rootProject.compileSdkVersion |
| | | |
| | | defaultConfig { |
| | | minSdkVersion rootProject.minSdkVersion |
| | | targetSdkVersion rootProject.targetSdkVersion |
| | | versionCode 1 |
| | | versionName "1.0" |
| | | |
| | | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
| | | consumerProguardFiles "consumer-rules.pro" |
| | | } |
| | | |
| | | buildTypes { |
| | | release { |
| | | minifyEnabled false |
| | | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
| | | } |
| | | } |
| | | compileOptions { |
| | | sourceCompatibility JavaVersion.VERSION_1_8 |
| | | targetCompatibility JavaVersion.VERSION_1_8 |
| | | } |
| | | } |
| | | |
| | | dependencies { |
| | | api project(path: ':hdl-common') |
| | | |
| | | } |
New file |
| | |
| | | # Add project specific ProGuard rules here. |
| | | # You can control the set of applied configuration files using the |
| | | # proguardFiles setting in build.gradle. |
| | | # |
| | | # For more details, see |
| | | # http://developer.android.com/guide/developing/tools/proguard.html |
| | | |
| | | # If your project uses WebView with JS, uncomment the following |
| | | # and specify the fully qualified class name to the JavaScript interface |
| | | # class: |
| | | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
| | | # public *; |
| | | #} |
| | | |
| | | # Uncomment this to preserve the line number information for |
| | | # debugging stack traces. |
| | | #-keepattributes SourceFile,LineNumberTable |
| | | |
| | | # If you keep the line number information, uncomment this to |
| | | # hide the original source file name. |
| | | #-renamesourcefileattribute SourceFile |
New file |
| | |
| | | package com.hdl.sdk.socket; |
| | | |
| | | import android.content.Context; |
| | | |
| | | import androidx.test.platform.app.InstrumentationRegistry; |
| | | import androidx.test.ext.junit.runners.AndroidJUnit4; |
| | | |
| | | import org.junit.Test; |
| | | import org.junit.runner.RunWith; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Instrumented test, which will execute on an Android device. |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | @RunWith(AndroidJUnit4.class) |
| | | public class ExampleInstrumentedTest { |
| | | @Test |
| | | public void useAppContext() { |
| | | // Context of the app under test. |
| | | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
| | | assertEquals("com.hdl.sdk.socket.test", appContext.getPackageName()); |
| | | } |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| | | package="com.hdl.sdk.socket"> |
| | | |
| | | </manifest> |
New file |
| | |
| | | package com.hdl.sdk.socket;
|
| | |
|
| | | import android.text.TextUtils;
|
| | | import android.util.Log;
|
| | |
|
| | | import androidx.collection.ArrayMap;
|
| | |
|
| | | import com.hdl.sdk.common.utils.ThreadToolUtils;
|
| | | import com.hdl.sdk.socket.annotation.ConnectStatus;
|
| | | import com.hdl.sdk.socket.client.IClient;
|
| | | import com.hdl.sdk.socket.listener.SendListener;
|
| | |
|
| | | import java.net.ConnectException;
|
| | | import java.util.concurrent.BlockingQueue;
|
| | | import java.util.concurrent.ExecutorService;
|
| | | import java.util.concurrent.LinkedBlockingDeque;
|
| | | import java.util.concurrent.ScheduledExecutorService;
|
| | | import java.util.concurrent.TimeUnit;
|
| | | import java.util.concurrent.atomic.AtomicBoolean;
|
| | | import java.util.concurrent.atomic.AtomicInteger;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/26.
|
| | | * Tcp/Udp 启动器
|
| | | */
|
| | | public class SocketBoot {
|
| | |
|
| | | private ExecutorService connectThread;
|
| | | private ScheduledExecutorService heartbeatThread;
|
| | | private ExecutorService sendThread;
|
| | | private ExecutorService receiveThread;
|
| | | private ScheduledExecutorService delayThread;
|
| | |
|
| | | private final IClient client;
|
| | |
|
| | | /**
|
| | | * socket是否在运行
|
| | | */
|
| | | private final AtomicBoolean isRun = new AtomicBoolean(false);
|
| | |
|
| | | private final AtomicBoolean isOpenRetry = new AtomicBoolean(false);
|
| | |
|
| | | private final AtomicInteger resendCount = new AtomicInteger(0);
|
| | |
|
| | | private final BlockingQueue<SocketRequest> mMessageQueue = new LinkedBlockingDeque<>();
|
| | |
|
| | | private final ArrayMap<String, SendListener> sendMap = new ArrayMap<>();
|
| | |
|
| | | public SocketBoot(IClient client) {
|
| | | this.client = client;
|
| | | }
|
| | |
|
| | | public ScheduledExecutorService getHeartBeat() {
|
| | | if (heartbeatThread == null) {
|
| | | heartbeatThread = ThreadToolUtils.getInstance().newScheduledThreadPool(1);
|
| | | }
|
| | | return heartbeatThread;
|
| | | }
|
| | |
|
| | | public void connect() {
|
| | | resendCount.set(0);
|
| | | resetConnect(true);
|
| | | isOpenRetry.set(true);
|
| | | }
|
| | |
|
| | | public synchronized void resetConnect(boolean isFirst) {
|
| | | final int maxRetry = client.getOptions().getMaxRetry();
|
| | | if (maxRetry == 0 && resendCount.get() > 0 ||
|
| | | (maxRetry > 0 && maxRetry + 1 < resendCount.get())) {
|
| | | Log.d("====", "===重连次数达到最大==");
|
| | | return;
|
| | | }
|
| | | if (!client.isConnect()) {
|
| | | if (connectThread == null) {
|
| | | connectThread = ThreadToolUtils.getInstance().newFixedThreadPool(1);
|
| | | }
|
| | | connectThread.execute(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | client.onConnectStatus(ConnectStatus.CONNECTING);
|
| | | if (!isFirst) {
|
| | | try {
|
| | | resendCount.set(resendCount.get() + 1);
|
| | | Thread.sleep(300L);
|
| | | Log.d("====", "==重连第" + resendCount + "次==");
|
| | | } catch (Exception ignored) {
|
| | | }
|
| | | }
|
| | | try {
|
| | | client.connect();
|
| | | isRun.set(true);
|
| | | if (client.isConnect()) {
|
| | | Log.d("====", "====连接成功====");
|
| | | startHeartbeat();
|
| | |
|
| | | initSendThread();
|
| | | initReceiveThread();
|
| | |
|
| | | client.onConnectStatus(ConnectStatus.CONNECTED);
|
| | | resendCount.set(0);
|
| | | } else {
|
| | | throw new ConnectException();
|
| | | }
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | Log.d("====", "===连接失败===" + e);
|
| | | //再判断一下有没有连接
|
| | | if (!client.isConnect()) {
|
| | | isRun.set(false);
|
| | | client.onConnectStatus(ConnectStatus.DISCONNECT);
|
| | | stopHeartbeat();
|
| | | disconnectError();
|
| | | }
|
| | | }
|
| | | }
|
| | | });
|
| | | }
|
| | | }
|
| | |
|
| | | public void initSendThread() {
|
| | | if (sendThread == null) {
|
| | | sendThread = ThreadToolUtils.getInstance().newFixedThreadPool(1);
|
| | | }
|
| | | sendThread.execute(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | while (isRun.get()) {
|
| | | if (client.isConnect()) {
|
| | | Log.d("=====", "==发送数据==");
|
| | | try {
|
| | | SocketRequest socketRequest = mMessageQueue.take();
|
| | | final String action = socketRequest.getAction();
|
| | | try {
|
| | | client.sendMsg(socketRequest.getData());
|
| | | if (!TextUtils.isEmpty(action)) {
|
| | | SendListener sendListener = sendMap.get(action);
|
| | | if (sendListener != null) {
|
| | | sendListener.onSucceed();
|
| | | }
|
| | | }
|
| | | } catch (Exception e) {
|
| | | if (!TextUtils.isEmpty(action)) {
|
| | | SendListener sendListener = sendMap.get(action);
|
| | | if (sendListener != null) {
|
| | | sendListener.onError();
|
| | | }
|
| | | }
|
| | |
|
| | | stopHeartbeat();
|
| | | if (sendThread != null) {
|
| | | sendThread.shutdownNow();
|
| | | }
|
| | | if (isRun.get()) {
|
| | | disconnectError();
|
| | | }
|
| | |
|
| | | }
|
| | | } catch (InterruptedException ignored) {
|
| | |
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | }
|
| | | Log.d("=====", "==发送线程关闭==");
|
| | | }
|
| | | });
|
| | |
|
| | | }
|
| | |
|
| | | public void initReceiveThread() {
|
| | | if (receiveThread == null) {
|
| | | receiveThread = ThreadToolUtils.getInstance().newFixedThreadPool(1);
|
| | | }
|
| | | receiveThread.execute(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | while (isRun.get()) {
|
| | | if (client.isConnect()) {
|
| | | try {
|
| | | //读取数据
|
| | | client.onHandleResponse();
|
| | | } catch (Exception e) {
|
| | | e.printStackTrace();
|
| | | Log.d("====", "断开连接" + e.getMessage());
|
| | | disconnectError();
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | });
|
| | |
|
| | | }
|
| | |
|
| | |
|
| | | public void startHeartbeat() {
|
| | | if (heartbeatThread != null) {
|
| | | heartbeatThread.shutdownNow();
|
| | | heartbeatThread = null;
|
| | | }
|
| | | if (client.getOptions() == null || client.getOptions().getHeartbeatTimeInterval() <= 0 || !client.getOptions().isEnabledHeartbeat()) {
|
| | | return;
|
| | | }
|
| | | getHeartBeat().scheduleWithFixedDelay(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | if (isRun.get()) {
|
| | | Log.d("====", "===发送心跳包===");
|
| | | if (client.getOptions() != null) {
|
| | | final byte[] heartBeat = client.getOptions().getHeartbeatData();
|
| | | if (heartBeat != null) {
|
| | | sendMsg(heartBeat, false, null);
|
| | | } else {
|
| | | sendMsg(new byte[0], false, null);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | }, client.getOptions().getHeartbeatTimeInterval(), client.getOptions().getHeartbeatTimeInterval(), TimeUnit.MILLISECONDS);
|
| | | }
|
| | |
|
| | | public void stopHeartbeat() {
|
| | | if (heartbeatThread != null) {
|
| | | heartbeatThread.shutdownNow();
|
| | | heartbeatThread = null;
|
| | | }
|
| | | }
|
| | |
|
| | | public void sendMsg(byte[] msg) {
|
| | | sendMsg(msg, true, null);
|
| | | }
|
| | |
|
| | | public void sendMsg(byte[] msg, SendListener listener) {
|
| | | sendMsg(msg, true, listener);
|
| | | }
|
| | |
|
| | | /**
|
| | | * @param listener 一般情况无需监听
|
| | | */
|
| | | private void sendMsg(byte[] msg, boolean isRefreshRetry, SendListener listener) {
|
| | | if (isRefreshRetry) {
|
| | | //重置连接次数
|
| | | resendCount.set(0);
|
| | | }
|
| | | try {
|
| | | SocketRequest request = new SocketRequest(msg);
|
| | | if (listener != null && !TextUtils.isEmpty(request.getAction())) {
|
| | | sendMap.put(request.getAction(), listener);
|
| | | }
|
| | | mMessageQueue.put(request);
|
| | | } catch (InterruptedException ignored) {
|
| | |
|
| | | }
|
| | | if (!client.isConnect()) {
|
| | | resetConnect(false);
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | /**
|
| | | * 发生错误,重连
|
| | | */
|
| | | private void disconnectError() {
|
| | | disconnect();
|
| | | isRun.set(false);
|
| | | if (isOpenRetry.get()) {
|
| | | if (delayThread != null) {
|
| | | delayThread.shutdownNow();
|
| | | }
|
| | | delayThread = ThreadToolUtils.getInstance().newScheduledThreadPool(1);
|
| | | delayThread.schedule(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | if (!client.isConnect() && isOpenRetry.get()) {
|
| | | resetConnect(false);
|
| | | }
|
| | |
|
| | | }
|
| | | }, 3000, TimeUnit.MILLISECONDS);
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | private synchronized void disconnect() {
|
| | | if (client.isConnect()) {
|
| | | client.disconnect();
|
| | | //断开连接
|
| | | client.onConnectStatus(ConnectStatus.DISCONNECT);
|
| | | }
|
| | | }
|
| | |
|
| | | public synchronized void close() {
|
| | | isOpenRetry.set(false);
|
| | | isRun.set(false);
|
| | | if (connectThread != null) {
|
| | | connectThread.shutdownNow();
|
| | | connectThread = null;
|
| | | }
|
| | | if (heartbeatThread != null) {
|
| | | heartbeatThread.shutdownNow();
|
| | | heartbeatThread = null;
|
| | | }
|
| | | if (sendThread != null) {
|
| | | sendThread.shutdownNow();
|
| | | sendThread = null;
|
| | | }
|
| | | if (receiveThread != null) {
|
| | | receiveThread.shutdownNow();
|
| | | receiveThread = null;
|
| | | }
|
| | | sendMap.clear();
|
| | | client.disconnect();
|
| | | mMessageQueue.clear();
|
| | |
|
| | | }
|
| | |
|
| | | public synchronized void release() {
|
| | | close();
|
| | | if (client != null && client.getOptions() != null) {
|
| | | client.getOptions().clearConnectStatusListener();
|
| | | }
|
| | | }
|
| | |
|
| | | public boolean isConnect() {
|
| | | return client.isConnect();
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket;
|
| | |
|
| | |
|
| | | import com.hdl.sdk.socket.codec.IHandleMessage;
|
| | | import com.hdl.sdk.socket.listener.ConnectStatusListener;
|
| | |
|
| | | import java.io.UnsupportedEncodingException;
|
| | | import java.util.ArrayList;
|
| | | import java.util.List;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | * socket配置
|
| | | */
|
| | | public class SocketOptions {
|
| | |
|
| | | //设置读取缓存
|
| | | private int readMaxBufferSize = 512;
|
| | |
|
| | | //发送心跳包
|
| | | private boolean isEnabledHeartbeat = true;
|
| | |
|
| | | //心跳包
|
| | | private byte[] heartbeatData;
|
| | |
|
| | | //心跳包时间间隔
|
| | | private long heartbeatTimeInterval = 300L;
|
| | |
|
| | | //处理数据
|
| | | private IHandleMessage handleMessage;
|
| | |
|
| | | //监听状态
|
| | | private List<ConnectStatusListener> mConnectStatusListener;
|
| | |
|
| | | //最大重连次数,小于0无限次数,等于0不重连
|
| | | private int maxRetry = -1;
|
| | |
|
| | | private boolean isTcpNoDelay;
|
| | | private boolean isReuseAddress;
|
| | | //保持活动状态
|
| | | private boolean isKeepAlive;
|
| | | private boolean isOOBInline;
|
| | | private int sendBufferSize;
|
| | | private int receiveBufferSize;
|
| | | private int soTimeout;
|
| | | private boolean soLinger;
|
| | |
|
| | |
|
| | | public IHandleMessage getHandleMessage() {
|
| | | return handleMessage;
|
| | | }
|
| | |
|
| | | public void setHandleMessage(IHandleMessage handleMessage) {
|
| | | this.handleMessage = handleMessage;
|
| | | }
|
| | |
|
| | | public boolean isEnabledHeartbeat() {
|
| | | return isEnabledHeartbeat;
|
| | | }
|
| | |
|
| | | public void setEnabledHeartbeat(boolean enabledHeartbeat) {
|
| | | isEnabledHeartbeat = enabledHeartbeat;
|
| | | }
|
| | |
|
| | | public byte[] getHeartbeatData() {
|
| | | return heartbeatData;
|
| | | }
|
| | |
|
| | | public void setHeartbeatData(byte[] heartbeatData) {
|
| | | this.heartbeatData = heartbeatData;
|
| | | }
|
| | |
|
| | | public void setHeartbeatData(String heartbeatData) {
|
| | | try {
|
| | | this.heartbeatData = heartbeatData.getBytes("utf-8");
|
| | | } catch (UnsupportedEncodingException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | |
|
| | | public long getHeartbeatTimeInterval() {
|
| | | return heartbeatTimeInterval;
|
| | | }
|
| | |
|
| | | public void setHeartbeatTimeInterval(long heartbeatTimeInterval) {
|
| | | this.heartbeatTimeInterval = heartbeatTimeInterval;
|
| | | }
|
| | |
|
| | | public void clearConnectStatusListener() {
|
| | | if (mConnectStatusListener != null && !mConnectStatusListener.isEmpty()) {
|
| | | mConnectStatusListener.clear();
|
| | | }
|
| | | }
|
| | |
|
| | | public void addConnectStatusListener(ConnectStatusListener connectStatusListener) {
|
| | | if (mConnectStatusListener == null) {
|
| | | mConnectStatusListener = new ArrayList<>();
|
| | | }
|
| | | mConnectStatusListener.add(connectStatusListener);
|
| | | }
|
| | |
|
| | | public void removeConnectStatusListener(ConnectStatusListener connectStatusListener) {
|
| | | if (mConnectStatusListener != null) {
|
| | | mConnectStatusListener.remove(connectStatusListener);
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | public List<ConnectStatusListener> getConnectStatusListener() {
|
| | | return mConnectStatusListener;
|
| | | }
|
| | |
|
| | |
|
| | | public int getMaxRetry() {
|
| | | return maxRetry;
|
| | | }
|
| | |
|
| | | public void setMaxRetry(int maxRetry) {
|
| | | this.maxRetry = maxRetry;
|
| | | }
|
| | |
|
| | | public int getReadMaxBufferSize() {
|
| | | return readMaxBufferSize;
|
| | | }
|
| | |
|
| | | public void setReadMaxBufferSize(int readMaxBufferSize) {
|
| | | this.readMaxBufferSize = readMaxBufferSize;
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket;
|
| | |
|
| | | import java.util.UUID;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | public class SocketRequest {
|
| | |
|
| | | private String action;
|
| | |
|
| | | private byte[] data;
|
| | |
|
| | | public SocketRequest(byte[] data) {
|
| | | this.data = data;
|
| | | action = UUID.randomUUID().toString();
|
| | | }
|
| | |
|
| | | public String getAction() {
|
| | | return action;
|
| | | }
|
| | |
|
| | | public void setAction(String action) {
|
| | | this.action = action;
|
| | | }
|
| | |
|
| | | public byte[] getData() {
|
| | | return data;
|
| | | }
|
| | |
|
| | | public void setData(byte[] data) {
|
| | | this.data = data;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.annotation;
|
| | |
|
| | | import androidx.annotation.IntDef;
|
| | |
|
| | | import java.lang.annotation.ElementType;
|
| | | import java.lang.annotation.Target;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | @Target({ElementType.TYPE_PARAMETER, ElementType.PARAMETER})
|
| | | @IntDef({ConnectStatus.CONNECTING,
|
| | | ConnectStatus.CONNECTED,
|
| | | ConnectStatus.DISCONNECT})
|
| | | public @interface ConnectStatus {
|
| | |
|
| | | /**
|
| | | * 连接中
|
| | | */
|
| | | int CONNECTING = 0;
|
| | |
|
| | | /**
|
| | | * 连接成功
|
| | | */
|
| | | int CONNECTED = 1;
|
| | |
|
| | | /**
|
| | | * 连接关闭
|
| | | */
|
| | | int DISCONNECT = 2;
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.client;
|
| | |
|
| | | import android.net.Uri;
|
| | |
|
| | |
|
| | | import androidx.collection.ArrayMap;
|
| | |
|
| | | import java.net.DatagramSocket;
|
| | | import java.net.Socket;
|
| | | import java.net.SocketException;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/10/8.
|
| | | */
|
| | | public class ClientPool {
|
| | |
|
| | | private final ArrayMap<String, Socket> mTcpPool = new ArrayMap<>();
|
| | | private final ArrayMap<String, DatagramSocket> mUdpPool = new ArrayMap<>();
|
| | |
|
| | | private ClientPool() {
|
| | | }
|
| | |
|
| | | private static class SingletonInstance {
|
| | | private static final ClientPool INSTANCE = new ClientPool();
|
| | | }
|
| | |
|
| | | public static ClientPool getInstance() {
|
| | | return SingletonInstance.INSTANCE;
|
| | | }
|
| | |
|
| | | public Socket getTcpSocket(String ip, int port) {
|
| | | final StringBuilder key = new StringBuilder();
|
| | | key.append(ip).append(":").append(port);
|
| | | if (mTcpPool.containsKey(key)) {
|
| | | Socket socket = mTcpPool.get(key);
|
| | | if (socket != null && !socket.isClosed()) {
|
| | | return socket;
|
| | | }
|
| | |
|
| | | }
|
| | | return new Socket();
|
| | | }
|
| | |
|
| | | public DatagramSocket getUdpSocket(String ip, int port) throws SocketException {
|
| | | final StringBuilder key = new StringBuilder();
|
| | | key.append(ip).append(":").append(port);
|
| | | if (mUdpPool.containsKey(key)) {
|
| | | DatagramSocket socket = mUdpPool.get(key);
|
| | | if (socket != null && !socket.isClosed()) {
|
| | | return socket;
|
| | | }
|
| | |
|
| | | }
|
| | | return new DatagramSocket(port);
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.client;
|
| | |
|
| | |
|
| | | import com.hdl.sdk.socket.SocketOptions;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | public interface IClient {
|
| | |
|
| | | void connect() throws Exception;
|
| | |
|
| | | void disconnect();
|
| | |
|
| | | /**
|
| | | * 是否已经连接
|
| | | */
|
| | | boolean isConnect();
|
| | |
|
| | |
|
| | | SocketOptions getOptions();
|
| | |
|
| | | /**
|
| | | * 监听数据
|
| | | */
|
| | | void onHandleResponse() throws Exception;
|
| | |
|
| | | /**
|
| | | * 发送消息
|
| | | */
|
| | | void sendMsg(byte[] msg) throws Exception;
|
| | |
|
| | | /**
|
| | | * 连接状态
|
| | | */
|
| | | void onConnectStatus(int status);
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.client;
|
| | |
|
| | |
|
| | |
|
| | | import com.hdl.sdk.common.utils.ThreadToolUtils;
|
| | | import com.hdl.sdk.socket.SocketBoot;
|
| | | import com.hdl.sdk.socket.SocketOptions;
|
| | | import com.hdl.sdk.socket.annotation.ConnectStatus;
|
| | | import com.hdl.sdk.socket.codec.IHandleMessage;
|
| | | import com.hdl.sdk.socket.listener.ConnectStatusListener;
|
| | |
|
| | | import java.io.IOException;
|
| | | import java.io.InputStream;
|
| | | import java.io.OutputStream;
|
| | | import java.net.InetSocketAddress;
|
| | | import java.net.Socket;
|
| | | import java.util.List;
|
| | |
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/15.
|
| | | */
|
| | | public final class TcpClient implements IClient {
|
| | |
|
| | | private SocketOptions socketOptions;
|
| | |
|
| | | private final String ip;
|
| | | private final int port;
|
| | |
|
| | | private Socket mSocket;
|
| | |
|
| | | private byte[] readBuffer;
|
| | |
|
| | | private TcpClient(String ip, int port, SocketOptions socketOptions) {
|
| | | this.socketOptions = socketOptions;
|
| | | this.ip = ip;
|
| | | this.port = port;
|
| | | }
|
| | |
|
| | | public static SocketBoot init(String ip, int port, SocketOptions options) {
|
| | | return new SocketBoot(new TcpClient(ip, port, options));
|
| | | }
|
| | |
|
| | |
|
| | | @Override
|
| | | public void connect() throws Exception {
|
| | | mSocket = getSocket();
|
| | | SocketOptions options = getOptions();
|
| | | mSocket.connect(new InetSocketAddress(ip, port));
|
| | | mSocket.setTcpNoDelay(true);
|
| | | mSocket.setReuseAddress(true);
|
| | | mSocket.setKeepAlive(true);
|
| | | readBuffer = new byte[options.getReadMaxBufferSize()];
|
| | | }
|
| | |
|
| | |
|
| | | @Override
|
| | | public void disconnect() {
|
| | | if (mSocket != null) {
|
| | | try {
|
| | | mSocket.close();
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public boolean isConnect() {
|
| | | if (mSocket == null) {
|
| | | return false;
|
| | | }
|
| | |
|
| | | return mSocket.isConnected() && !mSocket.isClosed();
|
| | | }
|
| | |
|
| | |
|
| | | @Override
|
| | | public synchronized SocketOptions getOptions() {
|
| | | if (socketOptions == null) {
|
| | | socketOptions = new SocketOptions();
|
| | | }
|
| | | return socketOptions;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onHandleResponse() throws Exception {
|
| | | final InputStream stream = getInputStream();
|
| | |
|
| | | if (stream != null && getOptions() != null) {
|
| | | readBuffer = new byte[1024];
|
| | | while ((getInputStream().read(readBuffer)) != -1) {
|
| | | IHandleMessage handleMessage = getOptions().getHandleMessage();
|
| | | if (handleMessage != null) {
|
| | | handleMessage.read(readBuffer);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void sendMsg(byte[] msg) throws Exception {
|
| | | final OutputStream outputStream = getOutStream();
|
| | | if (outputStream != null && getOptions() != null) {
|
| | | try {
|
| | | IHandleMessage handleMessage = getOptions().getHandleMessage();
|
| | | handleMessage.write(handleMessage.write(msg));
|
| | | getOutStream().write(msg);
|
| | |
|
| | | } finally {
|
| | | outputStream.flush();
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | |
|
| | | /**
|
| | | * 处理连接状态
|
| | | */
|
| | | public void onConnectStatus(int status) {
|
| | | ThreadToolUtils.getInstance().runOnUiThread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | final List<ConnectStatusListener> list = getOptions().getConnectStatusListener();
|
| | | if (list != null && !list.isEmpty()) {
|
| | | for (ConnectStatusListener listener : list) {
|
| | | switch (status) {
|
| | | case ConnectStatus
|
| | | .CONNECTING:
|
| | | listener.onConnecting();
|
| | | break;
|
| | | case ConnectStatus
|
| | | .CONNECTED:
|
| | | listener.onConnected();
|
| | | break;
|
| | | case ConnectStatus
|
| | | .DISCONNECT:
|
| | | listener.onConnectFailed();
|
| | | break;
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | });
|
| | | }
|
| | |
|
| | |
|
| | | private synchronized Socket getSocket() {
|
| | | return new Socket();
|
| | | }
|
| | |
|
| | | private InputStream getInputStream() {
|
| | | if (mSocket != null && mSocket.isConnected() && !mSocket.isClosed()) {
|
| | | try {
|
| | | return mSocket.getInputStream();
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
|
| | |
|
| | | private OutputStream getOutStream() {
|
| | | if (mSocket != null && mSocket.isConnected() && !mSocket.isClosed()) {
|
| | | try {
|
| | | return mSocket.getOutputStream();
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | }
|
| | | }
|
| | | return null;
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.client;
|
| | |
|
| | |
|
| | | import android.util.Log;
|
| | |
|
| | | import com.hdl.sdk.common.HDLSdk;
|
| | | import com.hdl.sdk.common.utils.IpUtils;
|
| | | import com.hdl.sdk.common.utils.ThreadToolUtils;
|
| | | import com.hdl.sdk.socket.SocketBoot;
|
| | | import com.hdl.sdk.socket.SocketOptions;
|
| | | import com.hdl.sdk.socket.annotation.ConnectStatus;
|
| | | import com.hdl.sdk.socket.codec.IHandleMessage;
|
| | | import com.hdl.sdk.socket.listener.ConnectStatusListener;
|
| | |
|
| | | import java.io.IOException;
|
| | | import java.net.DatagramPacket;
|
| | | import java.net.DatagramSocket;
|
| | | import java.net.InetAddress;
|
| | | import java.util.List;
|
| | | import java.util.concurrent.atomic.AtomicBoolean;
|
| | |
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/15.
|
| | | * 组播需要android.permission.CHANGE_WIFI_MULTICAST_STATE权限
|
| | | * MulticastSocket
|
| | | */
|
| | | public class UdpClient implements IClient {
|
| | |
|
| | | private static DatagramSocket mSocket;
|
| | |
|
| | | private DatagramPacket receivePacket;
|
| | |
|
| | | private final int BUFFER = 4 * 1024;
|
| | |
|
| | | private final byte[] receiveByte;
|
| | |
|
| | | private final String ip;
|
| | |
|
| | | private final int port;
|
| | |
|
| | | private int monitorPort;
|
| | | private int sendPort;
|
| | |
|
| | | private SocketOptions socketOptions;
|
| | |
|
| | | private final AtomicBoolean isConnect = new AtomicBoolean(false);
|
| | |
|
| | | /**
|
| | | * @param sendPort -1 表示随机端口
|
| | | */
|
| | | private UdpClient(String ip, int port, int monitorPort, int sendPort, SocketOptions socketOptions) {
|
| | | this.socketOptions = socketOptions;
|
| | | this.ip = ip;
|
| | | this.port = port;
|
| | | this.sendPort = sendPort;
|
| | | this.monitorPort = monitorPort;
|
| | | this.receiveByte = new byte[BUFFER];
|
| | | }
|
| | |
|
| | | public UdpClient(String ip, int port) {
|
| | | this.ip = ip;
|
| | | this.port = port;
|
| | | this.receiveByte = new byte[BUFFER];
|
| | | }
|
| | |
|
| | | public static SocketBoot init(String ip, int port, int monitorPort, int sendPort, SocketOptions options) {
|
| | | return new SocketBoot(new UdpClient(ip, port, monitorPort, sendPort, options));
|
| | | }
|
| | |
|
| | | public static SocketBoot init(String ip, int port, int monitorPort, SocketOptions options) {
|
| | | return init(ip, port, monitorPort, -1, options);
|
| | | }
|
| | |
|
| | | public static SocketBoot init(String ip, int port, SocketOptions options) {
|
| | | return init(ip, port, port, -1, options);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void connect() throws Exception {
|
| | |
|
| | | try {
|
| | | mSocket = ClientPool.getInstance().getUdpSocket(ip, monitorPort);
|
| | | mSocket.setBroadcast(true);
|
| | | mSocket.setReuseAddress(true);
|
| | |
|
| | | isConnect.set(true);
|
| | | if (receivePacket == null) {
|
| | | receivePacket = new DatagramPacket(receiveByte, BUFFER);
|
| | | }
|
| | | } catch (Exception e) {
|
| | | isConnect.set(false);
|
| | | throw e;
|
| | | }
|
| | |
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void disconnect() {
|
| | | if (mSocket != null) {
|
| | | mSocket.close();
|
| | | }
|
| | | isConnect.set(false);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public boolean isConnect() {
|
| | | return isConnect.get();
|
| | | }
|
| | |
|
| | | @Override
|
| | | public synchronized SocketOptions getOptions() {
|
| | | if (socketOptions == null) {
|
| | | socketOptions = new SocketOptions();
|
| | | }
|
| | | return socketOptions;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onHandleResponse() throws Exception {
|
| | | if (receivePacket == null || mSocket == null) {
|
| | | return;
|
| | | }
|
| | | try {
|
| | | mSocket.receive(receivePacket);
|
| | | } catch (IOException e) {
|
| | | e.printStackTrace();
|
| | | isConnect.set(false);
|
| | | }
|
| | | if (receivePacket.getLength() == 0) {
|
| | | return;
|
| | | }
|
| | | //排除自己发出去的
|
| | | try {
|
| | | if (receivePacket.getAddress().getHostAddress()
|
| | | .equals(IpUtils.getIP(HDLSdk.getInstance().getContext()))) {
|
| | | return;
|
| | | }
|
| | | } catch (Exception ignored) {
|
| | |
|
| | | }
|
| | |
|
| | | IHandleMessage handleMessage = getOptions().getHandleMessage();
|
| | | if (handleMessage != null) {
|
| | | handleMessage.read(receivePacket.getData());
|
| | | }
|
| | | final String receive = new String(receivePacket.getData(), 0, receivePacket.getLength());
|
| | |
|
| | | Log.d("---->", receive + " from " + receivePacket.getAddress().getHostAddress() + ":" + receivePacket.getPort());
|
| | |
|
| | | //重置长度
|
| | | if (receivePacket != null) {
|
| | | receivePacket.setLength(BUFFER);
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void sendMsg(byte[] msg) throws Exception {
|
| | | if (msg == null) {
|
| | | msg = new byte[1];
|
| | | }
|
| | | InetAddress serverAddress = InetAddress.getByName(ip);
|
| | | final DatagramPacket sendPacket = new DatagramPacket(msg, msg.length, serverAddress, port);
|
| | | if (sendPort < 0) {
|
| | | final DatagramSocket sendSocket = new DatagramSocket();
|
| | | sendSocket.send(sendPacket);
|
| | | sendSocket.close();
|
| | | } else if (sendPort == monitorPort) {
|
| | | mSocket.send(sendPacket);
|
| | | } else {
|
| | | final DatagramSocket sendSocket = new DatagramSocket(sendPort);
|
| | | sendSocket.send(sendPacket);
|
| | | sendSocket.close();
|
| | | }
|
| | |
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void onConnectStatus(int status) {
|
| | | ThreadToolUtils.getInstance().runOnUiThread(new Runnable() {
|
| | | @Override
|
| | | public void run() {
|
| | | final List<ConnectStatusListener> list = getOptions().getConnectStatusListener();
|
| | | if (list != null && !list.isEmpty()) {
|
| | | for (ConnectStatusListener listener : list) {
|
| | | switch (status) {
|
| | | case ConnectStatus
|
| | | .CONNECTING:
|
| | | listener.onConnecting();
|
| | | break;
|
| | | case ConnectStatus
|
| | | .CONNECTED:
|
| | | listener.onConnected();
|
| | | break;
|
| | | case ConnectStatus
|
| | | .DISCONNECT:
|
| | | listener.onConnectFailed();
|
| | | break;
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | });
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.codec;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | public abstract class ByteToMessageDecoder<T> implements IHandleFlow<T> {
|
| | |
|
| | | protected abstract T decoder(Object msg)
|
| | | throws Exception;
|
| | |
|
| | | @Override
|
| | | public final T read(Object data) throws Exception {
|
| | | return decoder(data);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public final byte[] write(byte[] data) {
|
| | | return data;
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.codec;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/23.
|
| | | */
|
| | | public interface IHandleFlow<T> {
|
| | |
|
| | | T read(Object data) throws Exception;
|
| | |
|
| | | byte[] write(byte[] data) throws Exception;
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.codec;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/27.
|
| | | */
|
| | | public interface IHandleMessage {
|
| | | void read(byte[] data) throws Exception;
|
| | |
|
| | | byte[] write(byte[] data) throws Exception;
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.codec;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/23.
|
| | | */
|
| | | public interface IMessagePipeLine {
|
| | |
|
| | | void add(IHandleFlow flow);
|
| | |
|
| | | void clear();
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.codec;
|
| | |
|
| | | import java.util.ArrayList;
|
| | | import java.util.List;
|
| | | import java.util.Objects;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/23.
|
| | | */
|
| | | public class MessagePipeLine implements IMessagePipeLine, IHandleMessage {
|
| | |
|
| | | public final static List<IHandleFlow> queue = new ArrayList<>();
|
| | |
|
| | | @Override
|
| | | public void add(IHandleFlow flow) {
|
| | | queue.add(flow);
|
| | | }
|
| | |
|
| | | @Override
|
| | | public synchronized void clear() {
|
| | | queue.clear();
|
| | | }
|
| | |
|
| | | @Override
|
| | | public void read(byte[] data) throws Exception {
|
| | | Object out = data;
|
| | | for (int i = 0; i < queue.size(); i++) {
|
| | | IHandleFlow flow = queue.get(i);
|
| | | Object read = flow.read(out);
|
| | | try {
|
| | | out = Objects.requireNonNull(read);
|
| | | } catch (Exception ignored) {
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | @Override
|
| | | public byte[] write(byte[] data) throws Exception {
|
| | | byte[] out = data;
|
| | | for (int i = 0; i < queue.size(); i++) {
|
| | | IHandleFlow flow = queue.get(i);
|
| | | byte[] write = flow.write(out);
|
| | | try {
|
| | | out = Objects.requireNonNull(write);
|
| | | } catch (Exception ignored) {
|
| | |
|
| | | }
|
| | | }
|
| | | return new byte[0];
|
| | | }
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.codec;
|
| | |
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | public abstract class MessageToByteEncoder implements IHandleFlow {
|
| | |
|
| | |
|
| | | protected abstract byte[] encode(byte[] data)
|
| | | throws Exception;
|
| | |
|
| | | @Override
|
| | | public final Object read(Object data) {
|
| | | return data;
|
| | | }
|
| | |
|
| | | @Override
|
| | | public byte[] write(byte[] data) throws Exception {
|
| | | return encode(data);
|
| | | }
|
| | |
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.listener;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | * 连接状态
|
| | | */
|
| | | public interface ConnectStatusListener {
|
| | |
|
| | | /**
|
| | | * 连接中
|
| | | */
|
| | | default void onConnecting() {
|
| | | }
|
| | |
|
| | | /**
|
| | | * 连接成功
|
| | | */
|
| | | default void onConnected() {
|
| | | }
|
| | |
|
| | | /**
|
| | | * 连接失败
|
| | | */
|
| | | default void onConnectFailed() {
|
| | | }
|
| | |
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket.listener;
|
| | |
|
| | | /**
|
| | | * Created by Tong on 2021/9/22.
|
| | | */
|
| | | public interface SendListener {
|
| | |
|
| | | void onSucceed();
|
| | |
|
| | | void onError();
|
| | | }
|
New file |
| | |
| | | package com.hdl.sdk.socket; |
| | | |
| | | import org.junit.Test; |
| | | |
| | | import static org.junit.Assert.*; |
| | | |
| | | /** |
| | | * Example local unit test, which will execute on the development machine (host). |
| | | * |
| | | * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
| | | */ |
| | | public class ExampleUnitTest { |
| | | @Test |
| | | public void addition_isCorrect() { |
| | | assertEquals(4, 2 + 2); |
| | | } |
| | | } |
New file |
| | |
| | | |
| | | include ':app' |
| | | include ':hdl-socket' |
| | | include ':hdl-connect' |
| | | include ':hdl-common' |
| | | |