diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fec6a1b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.devcontainer/ +.vscode/ +.idea/ +.git/ +.gitlab/ + +generated_* +generated-* +pb_gen/ +build/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..09ea6d9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{json,yml,yaml,tmpl}] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c3ad15c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.so filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 36b13f1..9d79d4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,176 +1,97 @@ -# ---> Python -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class +# Built application files +*.apk +*.aar +*.ap_ +*.aab -# C extensions -*.so +# Files for the ART/Dalvik VM +*.dex -# Distribution / packaging -.Python +# Java class files +*.class + +# 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/ build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec +# Local configuration file (sdk path, etc) +local.properties -# Installer logs -pip-log.txt -pip-delete-this-directory.txt +# Proguard folder generated by Eclipse +proguard/ -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: +# Log Files *.log -local_settings.py -db.sqlite3 -db.sqlite3-journal -# Flask stuff: -instance/ -.webassets-cache +# Android Studio Navigation editor temp files +.navigation/ -# Scrapy stuff: -.scrapy +# Android Studio captures folder +captures/ -# Sphinx documentation -docs/_build/ +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# 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 -# PyBuilder -.pybuilder/ -target/ +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore -# Jupyter Notebook -.ipynb_checkpoints +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ -# IPython -profile_default/ -ipython_config.py +# Google Services (e.g. APIs or Firebase) +# google-services.json -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version +# Freeline +freeline.py +freeline/ +freeline_project_description.json -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -#uv.lock +# Version control +vcs.xml -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# Ruff stuff: -.ruff_cache/ - -# PyPI configuration file -.pypirc +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ +# Android Profiling +*.hprof +*.iml +.gradle +/local.properties +.idea +.DS_Store +/build +/captures +.externalNativeBuild +/local \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..5038a24 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,3 @@ +include: + - project: "infra/gitlab-cicd" + file: "/templates/global.yml" diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS new file mode 100644 index 0000000..cb29527 --- /dev/null +++ b/.gitlab/CODEOWNERS @@ -0,0 +1,8 @@ +^[default] @group +* + +[CODEOWNERS][1] @org/codeowner-dev +CODEOWNERS + +[LICENSE][1] @org/legal-dev +LICENSE diff --git a/.gitlab/LICENSE b/.gitlab/LICENSE new file mode 100644 index 0000000..6a6e520 --- /dev/null +++ b/.gitlab/LICENSE @@ -0,0 +1,28 @@ +# 闭源商业代码使用协议(非开源部分代码的引用和接口调用) + +欢迎使用本闭源商业代码,为了确保您和我们共同遵守法律和条款,请在使用之前仔细阅读以下协议。如果您不同意本协议的任何内容,请勿使用该代码。 + +1. 版权所有 + 本代码是由 中科海微 开发并拥有版权。未经许可,您无权复制、修改、分发或出售本代码的任何部分。 + +2. 授权范围 + 在遵守本协议的前提下,中科海微 授予您对本代码的有限和非排他使用权限。您可以将本代码集成到您的商业产品中,并通过您的商业产品向最终用户提供服务。 + +3. 限制与禁止 + 您被授权使用本代码,但是不得进行以下行为: + - 对本代码进行逆向工程、反编译或尝试获取源代码; + - 删除本代码中的任何版权声明、标记或其他明示或暗示的所有权信息; + - 利用本代码直接或间接参与非法活动; + - 将本代码用于政治目的、色情目的,或用于违反法律法规的任何目的。 + +4. 免责声明 + 本代码按“原样”提供,没有任何明示或暗示的保证或条件。您对使用本代码所造成的风险自行承担。中科海微 不承担与本代码使用相关的任何责任。 + +5. 协议终止 + 如您违反了本协议的任何规定,中科海微 有权随时终止您使用本代码的权限。在终止后,您必须立即停止使用本代码。 + +6. 法律适用和管辖 + 本协议受中华人民共和国法律管辖,并在中华人民共和国境内解释执行。 + +7. 协议修改 + 中科海微 有权根据需要随时修改本协议的内容。修改后的协议将在官方网站上公布,您应及时查阅最新版本。 diff --git a/README.md b/README.md index 8fcbd88..f5d2990 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,96 @@ -# TJ-android +# Code Project Template -贴件仓库 -我的 \ No newline at end of file + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Update CODEOWNERS +- [ ] [Update the owner of default section to parent group](https://docs.gitlab.com/ee/user/project/codeowners/) + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://git.seawayos.com/template/code-project-template.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://git.seawayos.com/template/code-project-template/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README + +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..5f7f00d --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,189 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-android-extensions' + id("kotlin-kapt") // Only for Kotlin projects. + id("io.objectbox") // Add after other plugins. +} + +android { + compileSdkVersion 31 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "com.qidian.zhongkesmart" + minSdkVersion 24 +// minSdkVersion 21 + targetSdkVersion 29 +// targetSdkVersion 31 + versionCode 1 + versionName "1.0.1" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + +// ndk { +// //选择要添加的对应 cpu 类型的 .so 库。 +// abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86' +// // 还可以添加 'x86', 'x86_64', 'mips', 'mips64' +// } + ndk { + rootProject.ext.ndkAbis.each { abi -> + abiFilter(abi) + } + } + + } + + + sourceSets { + main { + jniLibs.srcDir 'libs' + } + } + repositories { + flatDir { dirs 'libs' } + mavenCentral() + } + signingConfigs { + release { + keyAlias 'key0' + keyPassword '123456' + storeFile file('../zksmart.jks') + storePassword '123456' + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + } + debug { + signingConfig signingConfigs.release + } + } + applicationVariants.all { variant -> + variant.outputs.all { + def createTime = new Date().format("MMddHHmm", TimeZone.getTimeZone("GMT+08:00")) + //只处理生产版本 + if (buildType.name == 'release') { + // app包名称 + outputFileName = "app_zksmart_release" + defaultConfig.versionName + "_" + defaultConfig.versionCode + "_" + createTime + ".apk" + } else { + outputFileName = "app_zksmart_test" + defaultConfig.versionName + "_" + defaultConfig.versionCode + "_" + createTime + ".apk" + } + } + } + /* buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + }*/ + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + + + packagingOptions { + pickFirst 'lib/arm64-v8a/libc++_shared.so' + pickFirst 'lib/armeabi-v7a/libc++_shared.so' + } + + def appKey = "72eed074506376b102a91e6a80ef9a71" + + // app key for code + defaultConfig { + buildConfigField "String", "APP_KEY", "\"${appKey}\"" + } + // base server url + defaultConfig { + buildConfigField "String", "BASE_URL", "\"https://yiyong.netease.im/\"" + } + + configurations.all { + // check for updates every build + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + } +} + +dependencies { + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.3.1' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + implementation files('libs\\core_3.0.1.jar') + implementation files('libs\\QWeather_Public_Android_V4.10.jar') + + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + implementation 'com.android.support:support-v4:28.0.0' + implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-26' + //状态栏 + implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + implementation 'net.idik:slimadapter:2.1.2' + + def aar = ['FlycoDialog-1.3.2'] + aar.each { implementation(name: it, ext: 'aar') } + +// implementation 'cn.yipianfengye.android:zxing-library:2.2' + implementation 'pub.devrel:easypermissions:3.0.0' + //chart + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //验证码样式 + implementation 'com.jacktuotuo.customview:verificationcodeview:1.0.5' + //eventbus 事件分发 + implementation 'org.greenrobot:eventbus:3.1.1' +// implementation 'com.squareup.okhttp3:okhttp:3.10.0' + //网络okgo + implementation 'com.lzy.net:okgo:3.0.4' + //json + implementation 'com.alibaba:fastjson:1.2.76' +// implementation 'com.google.code.gson:gson:2.8.8' + api 'io.reactivex.rxjava2:rxjava:2.2.9' + api 'io.reactivex.rxjava2:rxandroid:2.1.1' + //蓝牙连接 +// implementation 'com.inuker.bluetooth:library:1.4.0' +// api project(":lib-zxing") +// implementation project(path: ':FlycoDialog_Lib') + implementation 'com.vise.xiaoyaoyou:viselog:1.1.2' + /*蓝牙BLE*/ + implementation 'com.vise.xiaoyaoyou:xsnow:2.1.3' + //eventbus 事件分发 + implementation 'org.greenrobot:eventbus:3.1.1' + implementation 'com.makeramen:roundedimageview:2.3.0' + + implementation 'com.hyman:flowlayout-lib:1.1.2' + implementation 'com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar' + + //高德地图-天气预报需要 + implementation files('libs\\AMap2DMap_6.0.0_AMapSearch_9.2.0_AMapLocation_6.1.0_20220414.jar') + compile 'com.squareup.okhttp3:okhttp:3.12.12' + compile 'com.google.code.gson:gson:2.6.2' + + implementation 'com.tencent.bugly:crashreport:latest.release' //其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.1.9 + implementation 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新Bugly NDK版本号,也可以指定明确的版本号,例如3.0 + + implementation 'androidx.recyclerview:recyclerview:1.1.0' + + api 'com.squareup.okhttp3:logging-interceptor:3.12.12' + implementation 'com.blankj:utilcodex:1.30.5' + api 'com.netease.yunxin.kit:call-ui:1.6.1' + implementation 'com.jakewharton:butterknife:10.1.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' + + implementation 'no.nordicsemi.android:dfu:1.11.1' + // Manually add objectbox-android-objectbrowser only for debug builds, + // and objectbox-android for release builds. +// debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion") +} +// Apply the plugin after the dependencies block so it picks up +// and does not add objectbox-android. +apply plugin: 'io.objectbox' \ No newline at end of file diff --git a/app/libs/AMap2DMap_6.0.0_AMapSearch_9.2.0_AMapLocation_6.1.0_20220414.jar b/app/libs/AMap2DMap_6.0.0_AMapSearch_9.2.0_AMapLocation_6.1.0_20220414.jar new file mode 100644 index 0000000..869293a Binary files /dev/null and b/app/libs/AMap2DMap_6.0.0_AMapSearch_9.2.0_AMapLocation_6.1.0_20220414.jar differ diff --git a/app/libs/QWeather_Public_Android_V4.10.jar b/app/libs/QWeather_Public_Android_V4.10.jar new file mode 100644 index 0000000..66a8c41 Binary files /dev/null and b/app/libs/QWeather_Public_Android_V4.10.jar differ diff --git a/app/libs/core_3.0.1.jar b/app/libs/core_3.0.1.jar new file mode 100644 index 0000000..d1b35fc Binary files /dev/null and b/app/libs/core_3.0.1.jar differ diff --git a/app/objectbox-models/default.json b/app/objectbox-models/default.json new file mode 100644 index 0000000..c79b5e0 --- /dev/null +++ b/app/objectbox-models/default.json @@ -0,0 +1,146 @@ +{ + "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", + "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", + "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", + "entities": [ + { + "id": "1:8870068616457392677", + "lastPropertyId": "18:2692014094612841667", + "name": "DeviceInfo", + "properties": [ + { + "id": "1:6521131736238904230", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:5321779084544836486", + "name": "mac", + "type": 9 + }, + { + "id": "3:5565654943114914994", + "name": "name", + "type": 9 + }, + { + "id": "4:4477724591133570579", + "name": "type", + "type": 9 + }, + { + "id": "5:5761568316025382114", + "name": "time", + "type": 9 + }, + { + "id": "6:6210103608815305678", + "name": "power", + "type": 9 + }, + { + "id": "7:1468224017784560265", + "name": "hVersion", + "type": 9 + }, + { + "id": "8:5321966599309179601", + "name": "fVersion", + "type": 9 + }, + { + "id": "9:2980799595530192890", + "name": "frequency", + "type": 5 + }, + { + "id": "10:8084290042726521451", + "name": "model1Init", + "type": 1 + }, + { + "id": "11:9202164223710450367", + "name": "model1Time", + "type": 9 + }, + { + "id": "12:5769566883528611402", + "name": "model1Threshold", + "type": 5 + }, + { + "id": "13:955728021080652231", + "name": "model2Init", + "type": 1 + }, + { + "id": "14:691873319222183189", + "name": "model2Time", + "type": 9 + }, + { + "id": "15:8657884220131345907", + "name": "model2Threshold", + "type": 5 + }, + { + "id": "16:6172923985060486493", + "name": "isOnline", + "type": 1 + }, + { + "id": "17:5915524316767102281", + "name": "isBind", + "type": 1 + }, + { + "id": "18:2692014094612841667", + "name": "updateTime", + "type": 9 + } + ], + "relations": [] + }, + { + "id": "2:8894725199741668450", + "lastPropertyId": "4:8713898675265447700", + "name": "ActionInfo", + "properties": [ + { + "id": "1:2646917254676249586", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:8087768899070666486", + "name": "mac", + "type": 9 + }, + { + "id": "3:6810165371149848163", + "name": "data", + "type": 23 + }, + { + "id": "4:8713898675265447700", + "name": "time", + "type": 9 + } + ], + "relations": [] + } + ], + "lastEntityId": "2:8894725199741668450", + "lastIndexId": "0:0", + "lastRelationId": "0:0", + "lastSequenceId": "0:0", + "modelVersion": 5, + "modelVersionParserMinimum": 5, + "retiredEntityUids": [], + "retiredIndexUids": [], + "retiredPropertyUids": [], + "retiredRelationUids": [], + "version": 1 +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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 \ No newline at end of file diff --git a/app/src/androidTest/java/com/qidian/zhongkesmart/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/qidian/zhongkesmart/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..9db9686 --- /dev/null +++ b/app/src/androidTest/java/com/qidian/zhongkesmart/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.qidian.zhongkesmart + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.qidian.zhongkesmart", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..51165b0 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/SDK1702_d_1.0.4.zip b/app/src/main/assets/SDK1702_d_1.0.4.zip new file mode 100644 index 0000000..bfeb1cf Binary files /dev/null and b/app/src/main/assets/SDK1702_d_1.0.4.zip differ diff --git a/app/src/main/assets/SDK1702_l_1.0.1.zip b/app/src/main/assets/SDK1702_l_1.0.1.zip new file mode 100644 index 0000000..5822aed Binary files /dev/null and b/app/src/main/assets/SDK1702_l_1.0.1.zip differ diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..e02b21b Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/qidian/baseble/ViseBle.java b/app/src/main/java/com/qidian/baseble/ViseBle.java new file mode 100644 index 0000000..98114a7 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ViseBle.java @@ -0,0 +1,407 @@ +package com.qidian.baseble; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; +import android.util.Log; + +import com.qidian.baseble.ble.BluetoothDeviceManager; +import com.qidian.baseble.callback.IConnectCallback; +import com.qidian.baseble.callback.scan.IScanCallback; +import com.qidian.baseble.callback.scan.ScanCallback; +import com.qidian.baseble.callback.scan.SingleFilterScanCallback; +import com.qidian.baseble.common.BleConfig; +import com.qidian.baseble.common.ConnectState; +import com.qidian.baseble.core.DeviceMirror; +import com.qidian.baseble.core.DeviceMirrorPool; +import com.qidian.baseble.exception.TimeoutException; +import com.qidian.baseble.model.BluetoothLeDevice; +import com.qidian.baseble.model.BluetoothLeDeviceStore; +import com.vise.log.ViseLog; + +/** + * @Description: BLE设备操作入口 + * @author: DAWI + * @date: 17/8/1 22:24. + */ +public class ViseBle { + private Context context;//上下文 + private BluetoothManager bluetoothManager;//蓝牙管理 + private BluetoothAdapter bluetoothAdapter;//蓝牙适配器 + private DeviceMirrorPool deviceMirrorPool;//设备连接池 + private DeviceMirror lastDeviceMirror;//上次操作设备镜像 + + private static ViseBle instance;//入口操作管理 + private static BleConfig bleConfig = BleConfig.getInstance(); + + /** + * 单例方式获取蓝牙通信入口 + * + * @return 返回ViseBluetooth + */ + public static ViseBle getInstance() { + if (instance == null) { + synchronized (ViseBle.class) { + if (instance == null) { + instance = new ViseBle(); + } + } + } + return instance; + } + + private ViseBle() { + } + + /** + * 获取配置对象,可进行相关配置的修改 + * + * @return + */ + public static BleConfig config() { + return bleConfig; + } + + /** + * 初始化 + * + * @param context 上下文 + */ + public void init(Context context) { + if (this.context == null && context != null) { + this.context = context.getApplicationContext(); + bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE); + bluetoothAdapter = bluetoothManager.getAdapter(); + deviceMirrorPool = new DeviceMirrorPool(); + } + } + + /** + * 开始扫描 + * + * @param scanCallback 自定义回调 + */ + public void startScan(ScanCallback scanCallback) { + if (scanCallback == null) { + throw new IllegalArgumentException("this ScanCallback is Null!"); + } + scanCallback.setScan(true).scan(); + } + + /** + * 停止扫描 + * + * @param scanCallback 自定义回调 + */ + public void stopScan(ScanCallback scanCallback) { + if (scanCallback == null) { + throw new IllegalArgumentException("this ScanCallback is Null!"); + } + scanCallback.setScan(false).removeHandlerMsg().scan(); + } + + /** + * 指定扫描 + */ + public void startScanByDevice(BluetoothLeDevice mBluetoothLeDevice) { + ScanCallback scanCallback = new ScanCallback(new IScanCallback() { + @Override + public void onDeviceFound(BluetoothLeDevice bluetoothLeDevice) { + if (!TextUtils.isEmpty(bluetoothLeDevice.getAddress())) { + if (mBluetoothLeDevice.getAddress().equals(bluetoothLeDevice.getAddress())) { + Log.i( + "BlueTooth==", + "periodScanCallback:发现指定设备"+bluetoothLeDevice.getAddress()); + if (!BluetoothDeviceManager.getInstance().isConnected(bluetoothLeDevice)) { + BluetoothDeviceManager.getInstance().connect(bluetoothLeDevice); + } + } + Log.i( + "BlueTooth==", + "periodScanCallback:发现设备"+bluetoothLeDevice.getAddress()); + } + } + + @Override + public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) { + + } + + @Override + public void onScanTimeout() { + + } + }); + + scanCallback.setScan(true).scan(); + } + + /** + * 连接设备 + * + * @param bluetoothLeDevice + * @param connectCallback + */ + public void connect(BluetoothLeDevice bluetoothLeDevice, IConnectCallback connectCallback) { + if (bluetoothLeDevice == null || connectCallback == null) { + ViseLog.e("This bluetoothLeDevice or connectCallback is null."); + return; + } + if (deviceMirrorPool != null && !deviceMirrorPool.isContainDevice(bluetoothLeDevice)) { + DeviceMirror deviceMirror = new DeviceMirror(bluetoothLeDevice); + if (lastDeviceMirror != null && !TextUtils.isEmpty(lastDeviceMirror.getUniqueSymbol()) + && lastDeviceMirror.getUniqueSymbol().equals(deviceMirror.getUniqueSymbol())) { + deviceMirror = lastDeviceMirror;//防止重复创建设备镜像 + } + deviceMirror.connect(connectCallback); + lastDeviceMirror = deviceMirror; + } else { + ViseLog.i("This device is connected."); + } + } + + /** + * 连接指定mac地址的设备 + * + * @param mac 设备mac地址 + * @param connectCallback 连接回调 + */ + public void connectByMac(String mac, final IConnectCallback connectCallback) { + if (mac == null || connectCallback == null) { + ViseLog.e("This mac or connectCallback is null."); + return; + } + startScan(new SingleFilterScanCallback(new IScanCallback() { + @Override + public void onDeviceFound(BluetoothLeDevice bluetoothLeDevice) { + + } + + @Override + public void onScanFinish(final BluetoothLeDeviceStore bluetoothLeDeviceStore) { + if (bluetoothLeDeviceStore.getDeviceList().size() > 0) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + connect(bluetoothLeDeviceStore.getDeviceList().get(0), connectCallback); + } + }); + } else { + connectCallback.onConnectFailure(new TimeoutException()); + } + } + + @Override + public void onScanTimeout() { + connectCallback.onConnectFailure(new TimeoutException()); + } + + }).setDeviceMac(mac)); + } + + /** + * 连接指定设备名称的设备 + * + * @param name 设备名称 + * @param connectCallback 连接回调 + */ + public void connectByName(String name, final IConnectCallback connectCallback) { + if (name == null || connectCallback == null) { + ViseLog.e("This name or connectCallback is null."); + return; + } + startScan(new SingleFilterScanCallback(new IScanCallback() { + @Override + public void onDeviceFound(BluetoothLeDevice bluetoothLeDevice) { + + } + + @Override + public void onScanFinish(final BluetoothLeDeviceStore bluetoothLeDeviceStore) { + if (bluetoothLeDeviceStore.getDeviceList().size() > 0) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + connect(bluetoothLeDeviceStore.getDeviceList().get(0), connectCallback); + } + }); + } else { + connectCallback.onConnectFailure(new TimeoutException()); + } + } + + @Override + public void onScanTimeout() { + connectCallback.onConnectFailure(new TimeoutException()); + } + + }).setDeviceName(name)); + } + + /** + * 获取连接池中的设备镜像,如果没有连接则返回空 + * + * @param bluetoothLeDevice + * @return + */ + public DeviceMirror getDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { + if (deviceMirrorPool != null) { + return deviceMirrorPool.getDeviceMirror(bluetoothLeDevice); + } + return null; + } + + /** + * 获取该设备连接状态 + * + * @param bluetoothLeDevice + * @return + */ + public ConnectState getConnectState(BluetoothLeDevice bluetoothLeDevice) { + if (deviceMirrorPool != null) { + return deviceMirrorPool.getConnectState(bluetoothLeDevice); + } + return ConnectState.CONNECT_DISCONNECT; + } + + /** + * 判断该设备是否已连接 + * + * @param bluetoothLeDevice + * @return + */ + public boolean isConnect(BluetoothLeDevice bluetoothLeDevice) { + if (deviceMirrorPool != null) { + return deviceMirrorPool.isContainDevice(bluetoothLeDevice); + } + return false; + } + /** + * 判断该设备是否已连接 + * 只允许连接一个 + * 2022 + * @return + */ + public boolean isConnectDevice() { + if (deviceMirrorPool != null) { + return deviceMirrorPool.isConnectDevice(); + } + return false; + } + + /** + * 断开某一个设备 + * + * @param bluetoothLeDevice + */ + public void disconnect(BluetoothLeDevice bluetoothLeDevice) { + if (deviceMirrorPool != null) { + deviceMirrorPool.disconnect(bluetoothLeDevice); + } + } + + /** + * 断开所有设备 + */ + public void disconnect() { + if (deviceMirrorPool != null) { + deviceMirrorPool.disconnect(); + } + } + + /** + * 清除资源,在退出应用时调用 + */ + public void clear() { + if (deviceMirrorPool != null) { + deviceMirrorPool.clear(); + } + } + + /** + * 获取Context + * + * @return 返回Context + */ + public Context getContext() { + return context; + } + + /** + * 获取蓝牙管理 + * + * @return 返回蓝牙管理 + */ + public BluetoothManager getBluetoothManager() { + return bluetoothManager; + } + + /** + * 获取蓝牙适配器 + * + * @return 返回蓝牙适配器 + */ + public BluetoothAdapter getBluetoothAdapter() { + return bluetoothAdapter; + } + + /** + * 获取设备镜像池 + * + * @return + */ + public DeviceMirrorPool getDeviceMirrorPool() { + return deviceMirrorPool; + } + + /** + * 获取当前连接失败重试次数 + * + * @return + */ + public int getConnectRetryCount() { + if (lastDeviceMirror == null) { + return 0; + } + return lastDeviceMirror.getConnectRetryCount(); + } + + /** + * 获取当前读取数据失败重试次数 + * + * @return + */ + public int getReadDataRetryCount() { + if (lastDeviceMirror == null) { + return 0; + } + return lastDeviceMirror.getReadDataRetryCount(); + } + + /** + * 获取当前使能数据失败重试次数 + * + * @return + */ + public int getReceiveDataRetryCount() { + if (lastDeviceMirror == null) { + return 0; + } + return lastDeviceMirror.getReceiveDataRetryCount(); + } + + /** + * 获取当前写入数据失败重试次数 + * + * @return + */ + public int getWriteDataRetryCount() { + if (lastDeviceMirror == null) { + return 0; + } + return lastDeviceMirror.getWriteDataRetryCount(); + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/BluetoothDeviceManager.java b/app/src/main/java/com/qidian/baseble/ble/BluetoothDeviceManager.java new file mode 100644 index 0000000..de62541 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/BluetoothDeviceManager.java @@ -0,0 +1,819 @@ +package com.qidian.baseble.ble; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.qidian.baseble.ViseBle; +import com.qidian.baseble.callback.IBleCallback; +import com.qidian.baseble.callback.IConnectCallback; +import com.qidian.baseble.common.PropertyType; +import com.qidian.baseble.core.BluetoothGattChannel; +import com.qidian.baseble.core.DeviceMirror; +import com.qidian.baseble.core.DeviceMirrorPool; +import com.qidian.baseble.exception.BleException; +import com.qidian.baseble.model.BluetoothLeDevice; +import com.qidian.baseble.utils.HexUtil; +import com.qidian.zhongkesmart.config.EventCode; +import com.qidian.zhongkesmart.model.ActionInfo; +import com.qidian.zhongkesmart.model.DeviceInfo; +import com.qidian.zhongkesmart.utils.DataServer; +import com.qidian.zhongkesmart.utils.EventBusUtil; +import com.qidian.zhongkesmart.utils.ObjectBoxUtils; +import com.qidian.zhongkesmart.utils.TimeUtil; +import com.qidian.zhongkesmart.utils.Utils; +import com.vise.log.ViseLog; +import com.vise.xsnow.event.BusManager; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.UUID; + +import static com.qidian.baseble.utils.HexUtil.GetTempTime; +import static com.qidian.baseble.utils.HexUtil.addBytes; +import static com.qidian.baseble.utils.HexUtil.byteArrayToInt; +import static com.qidian.baseble.utils.HexUtil.byteToInt; +import static com.qidian.baseble.utils.HexUtil.byte_String; +import static com.qidian.baseble.utils.HexUtil.decodeHex; +import static com.qidian.baseble.utils.HexUtil.int2ByteArray; + + +public class BluetoothDeviceManager { + private static BluetoothDeviceManager instance; + private DeviceMirrorPool mDeviceMirrorPool; + private ScanEvent scanEvent = new ScanEvent(); + private ConnectEvent connectEvent = new ConnectEvent(); + private CallbackDataEvent callbackDataEvent = new CallbackDataEvent(); + private NotifyDataEvent notifyDataEvent = new NotifyDataEvent(); + + private final String TAG = "BlueTooth=="; + private BluetoothLeDevice mDevice; + private Map deviceAction = new HashMap<>(); + private DeviceInfo deviceInfo; + private List list; + //动作学习时间 + private String timeString = ""; + /** + * 连接回调 + */ + private IConnectCallback connectCallback = new IConnectCallback() { + + @Override + public void onConnectSuccess(final DeviceMirror deviceMirror) { + Log.i("BlueTooth==", "connectCallback:Connect Success"); + BusManager.getBus().post(connectEvent.setDeviceMirror(deviceMirror).setSuccess(true)); + } + + @Override + public void onConnectFailure(BleException exception) { + Log.i("BlueTooth==", "connectCallback:onConnectFailure"); + BusManager.getBus().post(connectEvent.setSuccess(false).setDisconnected(false)); + } + + @Override + public void onDisconnect(boolean isActive) { + Log.i("BlueTooth==", "connectCallback:Disconnect"); + BusManager.getBus().post(connectEvent.setSuccess(false).setDisconnected(true)); + } + }; + + public synchronized void close() { + } + + /** + * 接收数据回调 + */ + private IBleCallback receiveCallback = new IBleCallback() { + @Override + public void onSuccess(final byte[] data, BluetoothGattChannel bluetoothGattInfo, BluetoothLeDevice bluetoothLeDevice) { + if (data == null) { + return; + } + mDevice = bluetoothLeDevice; + Log.i("BlueTooth==", "notify success:" + HexUtil.encodeHexStr(data)); + ViseLog.i("notify success:" + HexUtil.encodeHexStr(data)); +// BusManager.getBus().post(notifyDataEvent.setData(data) +// .setBluetoothLeDevice(bluetoothLeDevice) +// .setBluetoothGattChannel(bluetoothGattInfo)); + deviceProcess(data); + } + + @Override + public void onFailure(BleException exception) { + if (exception == null) { + return; + } + ViseLog.i("notify fail:" + exception.getDescription()); + Log.i("BlueTooth==", "notify fail"); + + } + }; + + /** + * 操作数据回调 + */ + private IBleCallback bleCallback = new IBleCallback() { + @Override + public void onSuccess(final byte[] data, BluetoothGattChannel bluetoothGattInfo, BluetoothLeDevice bluetoothLeDevice) { + if (data == null) { + return; + } + Log.i("BlueTooth==", "bleCallback success" +data); + Log.i("BlueTooth==", "bleCallback success" + HexUtil.bytesToHex(data)); + if(data.length>10) { + String sVer = new String(data); + Log.i("BlueTooth==", "bleCallback success" + sVer); + } + ViseLog.i("callback success:" + HexUtil.encodeHexStr(data)); + BusManager.getBus().post(callbackDataEvent.setData(data).setSuccess(true) + .setBluetoothLeDevice(bluetoothLeDevice) + .setBluetoothGattChannel(bluetoothGattInfo)); + if (bluetoothGattInfo != null && (bluetoothGattInfo.getPropertyType() == PropertyType.PROPERTY_INDICATE + || bluetoothGattInfo.getPropertyType() == PropertyType.PROPERTY_NOTIFY)) { + DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice); + if (deviceMirror != null) { + deviceMirror.setNotifyListener(bluetoothGattInfo.getGattInfoKey(), receiveCallback); + } + } + } + + @Override + public void onFailure(BleException exception) { + Log.i("BlueTooth==", "bleCallback fail"); + if (exception == null) { + return; + } + ViseLog.i("BlueTooth==callback fail:" + exception.getDescription()); + BusManager.getBus().post(callbackDataEvent.setSuccess(false)); + } + }; + + private BluetoothDeviceManager() { + + } + + public static BluetoothDeviceManager getInstance() { + if (instance == null) { + synchronized (BluetoothDeviceManager.class) { + if (instance == null) { + instance = new BluetoothDeviceManager(); + } + } + } + return instance; + } + + public void init(Context context) { + if (context == null) { + return; + } + //蓝牙相关配置修改 + ViseBle.config() + .setScanTimeout(-1) //扫描超时时间,-1设置为永久扫描 + .setScanRepeatInterval(10*60*1000)//扫描间隔 + .setConnectTimeout(8 * 1000)//连接超时时间 + .setOperateTimeout(5 * 1000)//设置数据操作超时时间 + .setConnectRetryCount(0)//设置连接失败重试次数 + .setConnectRetryInterval(1000)//设置连接失败重试间隔时间 + .setOperateRetryCount(0)//设置数据操作失败重试次数 + .setOperateRetryInterval(1000)//设置数据操作失败重试间隔时间 + .setMaxConnectCount(1);//设置最大连接设备数量 + //蓝牙信息初始化,全局唯一,必须在应用初始化时调用 + ViseBle.getInstance().init(context.getApplicationContext()); + mDeviceMirrorPool = ViseBle.getInstance().getDeviceMirrorPool(); + + + } + + public void connect(BluetoothLeDevice bluetoothLeDevice) { + ViseBle.getInstance().connect(bluetoothLeDevice, connectCallback); + } + + public void connect(String macStr) { + ViseBle.getInstance().connectByMac(macStr, connectCallback); + } + + + public void connect(String macStr, IConnectCallback connectCallback) { + ViseBle.getInstance().connectByMac(macStr, connectCallback); + } + public void connect(BluetoothLeDevice bluetoothLeDevice, IConnectCallback connectCallback) { + ViseBle.getInstance().connect(bluetoothLeDevice, connectCallback); + } + public void disconnect(BluetoothLeDevice bluetoothLeDevice) { + ViseBle.getInstance().disconnect(bluetoothLeDevice); + } + public void disconnect() { + ViseBle.getInstance().disconnect(); + } + + public boolean isConnected(BluetoothLeDevice bluetoothLeDevice) { + return ViseBle.getInstance().isConnect(bluetoothLeDevice); + } + + public boolean isConnectedDevice() { + return ViseBle.getInstance().isConnectDevice(); + } + + public void bindChannel(BluetoothLeDevice bluetoothLeDevice, PropertyType propertyType, UUID serviceUUID, + UUID characteristicUUID, UUID descriptorUUID) { + DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice); + if (deviceMirror != null) { + BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder() + .setBluetoothGatt(deviceMirror.getBluetoothGatt()) + .setPropertyType(propertyType) + .setServiceUUID(serviceUUID) + .setCharacteristicUUID(characteristicUUID) + .setDescriptorUUID(descriptorUUID) + .builder(); + deviceMirror.bindChannel(bleCallback, bluetoothGattChannel); + + } + } + + public void write(final BluetoothLeDevice bluetoothLeDevice, byte[] data,final String ml) { + if (dataInfoQueue != null) { + dataInfoQueue.clear(); + dataInfoQueue = splitPacketFor20Byte(data); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + send(bluetoothLeDevice,ml); + } + }); + } + + + } + + public void read(BluetoothLeDevice bluetoothLeDevice) { + DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice); + if (deviceMirror != null) { + deviceMirror.readData(); + } + } + + + public void registerNotify(BluetoothLeDevice bluetoothLeDevice, boolean isIndicate) { + DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice); + if (deviceMirror != null) { + deviceMirror.registerNotify(isIndicate); + } + } + + //发送队列,提供一种简单的处理方式,实际项目场景需要根据需求优化 + private Queue dataInfoQueue = new LinkedList<>(); + + private void send(final BluetoothLeDevice bluetoothLeDevice,final String ml) { + if (dataInfoQueue != null && !dataInfoQueue.isEmpty()) { + DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice); + if (dataInfoQueue.peek() != null && deviceMirror != null) { + deviceMirror.writeData(dataInfoQueue.poll(),ml); + } + if (dataInfoQueue.peek() != null) { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + send(bluetoothLeDevice,ml); + } + }, 100); + } + } + + + } + + /** + * 数据分包 + * + * @param data + * @return + */ + private Queue splitPacketFor20Byte(byte[] data) { + Queue dataInfoQueue = new LinkedList<>(); + if (data != null) { + int index = 0; + do { + byte[] surplusData = new byte[data.length - index]; + byte[] currentData; + System.arraycopy(data, index, surplusData, 0, data.length - index); + if (surplusData.length <= 20) { + currentData = new byte[surplusData.length]; + System.arraycopy(surplusData, 0, currentData, 0, surplusData.length); + index += surplusData.length; + } else { + currentData = new byte[20]; + System.arraycopy(data, index, currentData, 0, 20); + index += 20; + } + dataInfoQueue.offer(currentData); + } while (index < data.length); + } + return dataInfoQueue; + } + + /** + * 设备数据处理流程 + * @param data + */ + private void deviceProcess(byte[] data) { + String newHex = Integer.toHexString(data[1]& 0xFF); + Log.i(TAG, "DeviceProcess:" + newHex); + String mac = ""; + String formatMac = ""; + switch (data[1]) { + case 0x01: + String hVer = new String(new byte[]{data[2], data[3], data[4], data[5], + data[6], data[7], data[8], data[9], + data[10], data[11], data[12], data[13]}); + mac = byte_String(data[19]) + byte_String(data[18]) + + byte_String(data[17]) + + byte_String(data[16]) + + byte_String(data[15]) + + byte_String(data[14]); + Log.i(TAG, mac+"的硬件版本号:" + hVer); + formatMac = Utils.formatMac(mac,":"); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(formatMac); + if(list!=null&&list.size()>0){ + deviceInfo = list.get(0); + deviceInfo.setHVersion(hVer); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + }else{ + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(formatMac); + deviceInfo.setHVersion(hVer); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } +// ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + case 0x02: + String sVer = new String(new byte[]{data[2], data[3], data[4], data[5], + data[6], data[7], data[8], data[9], + data[10], data[11], data[12], data[13]}); + mac = byte_String(data[19]) + byte_String(data[18]) + + byte_String(data[17]) + + byte_String(data[16]) + + byte_String(data[15]) + + byte_String(data[14]); + Log.i(TAG, mac+"的软件版本号:" + sVer); + formatMac = Utils.formatMac(mac,":"); + if(deviceInfo!=null&&deviceInfo.getMac().equals(formatMac)){ + deviceInfo.setFVersion(sVer); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } +// list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac,":")); +// if(list!=null&&list.size()>0){ +// deviceInfo = list.get(0); +// deviceInfo.setFVersion(sVer); +// deviceInfo.setOnline(true); +// deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); +// }else{ +// deviceInfo = new DeviceInfo(); +// deviceInfo.setMac(Utils.formatMac(mac, ":")); +// deviceInfo.setFVersion(sVer); +// deviceInfo.setOnline(true); +// deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); +// } +// ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + break; + + case 0x03: + int frequency = byteArrayToInt(new byte[]{data[4], data[5]}); + int model1Threshold = byteArrayToInt(new byte[]{data[6], data[7]}); + int model2Threshold = byteArrayToInt(new byte[]{data[8], data[9]}); + int power = byteToInt(data[10]); + mac = byte_String(data[19]) + byte_String(data[18]) + + byte_String(data[17]) + + byte_String(data[16]) + + byte_String(data[15]) + + byte_String(data[14]); + Log.i(TAG, mac+"频率:" +frequency+",模型1阈值:"+model1Threshold+",模型2阈值:"+model2Threshold+",电量:"+power); + formatMac = Utils.formatMac(mac,":"); + if(deviceInfo!=null&&deviceInfo.getMac().equals(formatMac)){ + deviceInfo.setFrequency(frequency); + deviceInfo.setModel1Threshold(model1Threshold); + deviceInfo.setModel2Threshold(model2Threshold); + deviceInfo.setPower(power+""); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } +// list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac,":")); +// if(list!=null&&list.size()>0){ +// deviceInfo = list.get(0); +// deviceInfo.setFrequency(frequency); +// deviceInfo.setPower(power+""); +// deviceInfo.setOnline(true); +// deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); +// }else{ +// deviceInfo = new DeviceInfo(); +// deviceInfo.setMac(Utils.formatMac(mac, ":")); +// deviceInfo.setFrequency(frequency); +// deviceInfo.setPower(power+""); +// deviceInfo.setOnline(true); +// deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); +// } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + break; + + case 0x04: + mac = byte_String(data[19]) + byte_String(data[18]) + + byte_String(data[17]) + + byte_String(data[16]) + + byte_String(data[15]) + + byte_String(data[14]); + int value = byteArrayToInt(new byte[]{data[5], data[6]}); + int flag = byteToInt(data[2]); + BusManager.getBus().post(new DeviceSettingEvent(new byte[]{data[2],data[3],data[4]},mac,4)); + switch (data[3]){ + case 0x00: + int type = byteToInt(data[4]); + switch (type){ + case 1: + Log.i(TAG, mac+"频率设置成功"); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac,":")); + if(list!=null&&list.size()>0){ + deviceInfo = list.get(0); + deviceInfo.setFrequency(value); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + }else{ + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setFrequency(value); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + break; + case 255: + switch (flag) { + case 0: + Log.i(TAG, mac+"算法模型1学习成功"); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac,":")); + if(list!=null&&list.size()>0){ + deviceInfo = list.get(0); + deviceInfo.setModel1Init(true); + deviceInfo.setModel1Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + }else{ + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setModel1Init(true); + deviceInfo.setModel1Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + break; + case 1: + break; + } + break; + case 254: + Log.i(TAG, mac+"算法模型1阈值设置成功"); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac,":")); + if(list!=null&&list.size()>0){ + deviceInfo = list.get(0); + deviceInfo.setModel1Threshold(value); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + }else{ + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setModel1Threshold(value); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + break; + case 253: + switch (flag) { + case 0: + Log.i(TAG, mac+"算法模型2学习成功"); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac,":")); + if(list!=null&&list.size()>0){ + deviceInfo = list.get(0); + deviceInfo.setModel2Init(true); + deviceInfo.setModel2Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + }else{ + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setModel2Init(true); + deviceInfo.setModel2Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + break; + case 1: + break; + } + break; + case 252: + Log.i(TAG, mac+"算法模型2阈值设置成功"); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac,":")); + if(list!=null&&list.size()>0){ + deviceInfo = list.get(0); + deviceInfo.setModel2Threshold(value); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + }else{ + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setModel2Threshold(value); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo,DeviceInfo.class); + break; + } + break; + case (byte)0xFC: + Log.i(TAG, mac+"设备忙碌"); + break; + case (byte)0xFB: + Log.i(TAG, mac+"保存参数失败"); + break; + case (byte)0xFA: + Log.i(TAG, mac+"算法模型学习失败"); + break; + case (byte) 0xF9: + Log.i(TAG, mac+"指令无效"); + break; + } + break; + + case 0x05: + mac = byte_String(data[19]) + byte_String(data[18]) + + byte_String(data[17]) + + byte_String(data[16]) + + byte_String(data[15]) + + byte_String(data[14]); + BusManager.getBus().post(new DeviceSettingEvent(new byte[]{data[2],data[3],data[4]},mac,5)); + switch (data[4]){ + case 0x00: + int type = byteToInt(data[3]); + switch (type){ + case 0: + Log.i(TAG, mac+"正常工作成功"); + break; + case 1: + Log.i(TAG, mac+"强制休眠成功"); + break; + case 2: + int flag1 = byteToInt(data[2]); + switch (flag1) { + case 0: + Log.i(TAG, mac + "结束模型验证成功"); + break; + case 1: + Log.i(TAG, mac + "开启模型验证成功"); + break; + } + break; + case 3: + Log.i(TAG, mac+"开启模型2验证成功"); + break; + } + break; + case (byte)0xFC: + Log.i(TAG, mac+"设备忙碌"); + break; + case (byte)0xFB: + Log.i(TAG, mac+"保存参数失败"); + break; + case (byte)0xFA: + Log.i(TAG, mac+"算法模型学习失败"); + break; + case (byte) 0xF9: + Log.i(TAG, mac+"指令无效"); + break; + } + break; + + case 0x06: +// if(!Arrays.equals(lastAction,new byte[]{data[4], data[5]})||System.currentTimeMillis()-lastTime>3000) { +// lastAction = new byte[]{data[4], data[5]}; +// lastTime = System.currentTimeMillis(); + mac = byte_String(data[19]) + byte_String(data[18]) + + byte_String(data[17]) + + byte_String(data[16]) + + byte_String(data[15]) + + byte_String(data[14]); + String realMac = Utils.formatMac(mac, ":"); +// if (!deviceAction.containsKey(realMac)||(!Arrays.equals(deviceAction.get(realMac), new byte[]{data[4], data[5]})&&System.currentTimeMillis()-deviceTime.get(realMac)>10000)) { +// deviceAction.put(realMac,new byte[]{data[4], data[5]}); +// deviceTime.put(realMac,System.currentTimeMillis()); + if (!deviceAction.containsKey(realMac)||!Arrays.equals(deviceAction.get(realMac), new byte[]{data[2], data[3]})){ + deviceAction.put(realMac,new byte[]{data[2], data[3]}); + int cActionDataIndex = byteArrayToInt(new byte[]{data[2], data[3]}); + int cActionIndex = byteArrayToInt(new byte[]{data[4], data[5]}); + byte[] actionData = new byte[]{data[4], data[5]}; + int power6 = byteToInt(data[6]); + Log.i(TAG, mac + ",动作数据=" + cActionIndex + ",index="+cActionDataIndex); + if (DataServer.INSTANCE.getNBlueToothVerifyMac().equals(Utils.formatMac(mac, ":"))) { + if (DataServer.INSTANCE.getConnectMode() == 2) { + //如果收到DataServer.nBlueToothVerifyMac的贴件数据,说明贴件唤醒成功 + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.DEVICE_WAKE_UP_SUCCEED)); + } + if(!Arrays.equals(actionData,new byte[]{0x00, 0x00})) { + BusManager.getBus().post(new DeviceSettingEvent(new byte[]{data[4], data[5]}, mac, 6)); + } + } + + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac, ":")); + if (list != null && list.size() > 0) { + deviceInfo = list.get(0); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + deviceInfo.setPower(power6+""); + //贴件已绑定,才保存数据 + if(deviceInfo.isBind()){ + if(!Arrays.equals(actionData,new byte[]{0x00, 0x00})) { + ActionInfo actionInfo = new ActionInfo(); + actionInfo.setMac(Utils.formatMac(mac, ":")); + actionInfo.setData(actionData); + actionInfo.setTime(TimeUtil.INSTANCE.getNowTime()); + ObjectBoxUtils.INSTANCE.addData(actionInfo, ActionInfo.class); + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET, mac)); + } + } + } else { + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + deviceInfo.setPower(power6+""); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo, DeviceInfo.class); + } + break; + + case 0x08: + timeString = byte_String(data[3]) + byte_String(data[4]) + "-" + + byte_String(data[5]) + "-" + + byte_String(data[6]) + " " + + byte_String(data[7]) + ":" + + byte_String(data[8]) + ":" + byte_String(data[9]); + mac = byte_String(data[19]) + byte_String(data[18]) + + byte_String(data[17]) + + byte_String(data[16]) + + byte_String(data[15]) + + byte_String(data[14]); + BusManager.getBus().post(new DeviceSettingEvent(new byte[]{data[2],data[3],data[4]},mac,4)); + int type = byteToInt(data[2]); + if(timeString!=null&&!timeString.startsWith("0000")){ + switch (type) { + case 255: + Log.i(TAG, mac + "模型1学习时间:" + timeString); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac, ":")); + if (list != null && list.size() > 0) { + deviceInfo = list.get(0); + deviceInfo.setModel1Init(true); + deviceInfo.setModel1Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } else { + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setModel1Init(true); + deviceInfo.setModel1Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo, DeviceInfo.class); + break; + case 253: + Log.i(TAG, mac + "模型2学习时间:" + timeString); + list = ObjectBoxUtils.INSTANCE.getDeviceByMac(Utils.formatMac(mac, ":")); + if (list != null && list.size() > 0) { + deviceInfo = list.get(0); + deviceInfo.setModel2Init(true); + deviceInfo.setModel2Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } else { + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(Utils.formatMac(mac, ":")); + deviceInfo.setModel2Init(true); + deviceInfo.setModel2Time(timeString); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo, DeviceInfo.class); + break; + } + } + break; + } + } + + /** + * + * @param flag + * 1开始 0结束 + * @param type + ** 1-频率 + * * 252-算法模型2阈值 + * *253-算法模型2学习 + * *254-算法模型1阈值 + * * 255-算法模型1学习 + * @param value + */ + public void setParam(String mac,boolean flag, int type,int value){ + byte[] head = new byte[]{(byte) 0x90, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if(flag){ + head[2] = 0x01; + }else{ + head[2] = 0x00; + } + head[3] = int2ByteArray(type)[3]; + head[4] =int2ByteArray(value)[2]; + head[5] = int2ByteArray(value)[3]; + byte[] last = decodeHex(Utils.reverseMac(mac)); + + byte[] result = addBytes(head, last); + write(mDevice,result,"设置参数"); + } + + /** + * + * @param flag + * 1开始 0结束 + * @param type + ** 1-频率 + * * 252-算法模型2阈值 + * *253-算法模型2学习 + * *254-算法模型1阈值 + * * 255-算法模型1学习 + * @param value + */ + public void setParamTime(String mac,boolean flag, int type,int value,Date date){ + byte[] head = new byte[]{(byte) 0x90, 0x14, 0x00, 0x00, 0x00, 0x00}; + if(flag){ + head[2] = 0x01; + }else{ + head[2] = 0x00; + } + head[3] = int2ByteArray(type)[3]; + head[4] =int2ByteArray(value)[2]; + head[5] = int2ByteArray(value)[3]; + timeString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date); + String timeShort = new SimpleDateFormat("yyyyMMddHHmmss").format(date); + byte[] time = GetTempTime(timeShort); + byte[] last = decodeHex(Utils.reverseMac(mac)); + byte[] res = addBytes(head,time); + byte[] resul = addBytes(res,new byte[]{0x00}); + byte[] result = addBytes(resul, last); + write(mDevice,result,"设置参数"); + } + + /** + *0-正常工作 + * 1-强制休眠 + * 2-开启模型1验证 + * 3-开启模型2验证 + * @param mac + * @param flag + * @param type + */ + public void setMode(String mac,boolean flag, int type){ + byte[] head = new byte[]{(byte) 0x90, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if(flag){ + head[2] = 0x01; + }else{ + head[2] = 0x00; + } + head[3] = int2ByteArray(type)[3]; + byte[] last = decodeHex(Utils.reverseMac(mac)); + + byte[] result = addBytes(head, last); + write(mDevice,result,"设置工作模式"); + } + + /** + * @param type + ** 1-频率 + * *253-算法模型2学习 + * * 255-算法模型1学习 + */ + public void getTime(String mac, int type){ + byte[] head = new byte[]{(byte) 0x90, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + head[2] = int2ByteArray(type)[3]; + byte[] last = decodeHex(Utils.reverseMac(mac)); + byte[] result = addBytes(head, last); + write(mDevice,result,"获取模型学习时间"); + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/CallbackDataEvent.java b/app/src/main/java/com/qidian/baseble/ble/CallbackDataEvent.java new file mode 100644 index 0000000..d977e25 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/CallbackDataEvent.java @@ -0,0 +1,49 @@ +package com.qidian.baseble.ble; + + +import com.qidian.baseble.core.BluetoothGattChannel; +import com.qidian.baseble.model.BluetoothLeDevice; +import com.vise.xsnow.event.IEvent; + +public class CallbackDataEvent implements IEvent { + private byte[] data; + private boolean isSuccess; + private BluetoothLeDevice bluetoothLeDevice; + private BluetoothGattChannel bluetoothGattChannel; + + public CallbackDataEvent setSuccess(boolean success) { + isSuccess = success; + return this; + } + + public byte[] getData() { + return data; + } + + public CallbackDataEvent setData(byte[] data) { + this.data = data; + return this; + } + + public boolean isSuccess() { + return isSuccess; + } + + public BluetoothLeDevice getBluetoothLeDevice() { + return bluetoothLeDevice; + } + + public CallbackDataEvent setBluetoothLeDevice(BluetoothLeDevice bluetoothLeDevice) { + this.bluetoothLeDevice = bluetoothLeDevice; + return this; + } + + public BluetoothGattChannel getBluetoothGattChannel() { + return bluetoothGattChannel; + } + + public CallbackDataEvent setBluetoothGattChannel(BluetoothGattChannel bluetoothGattChannel) { + this.bluetoothGattChannel = bluetoothGattChannel; + return this; + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/ConnectEvent.java b/app/src/main/java/com/qidian/baseble/ble/ConnectEvent.java new file mode 100644 index 0000000..b562c40 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/ConnectEvent.java @@ -0,0 +1,37 @@ +package com.qidian.baseble.ble; + +import com.qidian.baseble.core.DeviceMirror; +import com.vise.xsnow.event.IEvent; + +public class ConnectEvent implements IEvent { + private boolean isSuccess; + private boolean isDisconnected; + private DeviceMirror deviceMirror; + + public boolean isSuccess() { + return isSuccess; + } + + public ConnectEvent setSuccess(boolean success) { + isSuccess = success; + return this; + } + + public boolean isDisconnected() { + return isDisconnected; + } + + public ConnectEvent setDisconnected(boolean disconnected) { + isDisconnected = disconnected; + return this; + } + + public DeviceMirror getDeviceMirror() { + return deviceMirror; + } + + public ConnectEvent setDeviceMirror(DeviceMirror deviceMirror) { + this.deviceMirror = deviceMirror; + return this; + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/DeviceSettingEvent.java b/app/src/main/java/com/qidian/baseble/ble/DeviceSettingEvent.java new file mode 100644 index 0000000..d9e0b92 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/DeviceSettingEvent.java @@ -0,0 +1,42 @@ +package com.qidian.baseble.ble; + +import com.vise.xsnow.event.IEvent; + +public class DeviceSettingEvent implements IEvent { + private byte[] data; + private String mac; + private int flag; + + public DeviceSettingEvent() { + } + + public DeviceSettingEvent(byte[] data, String mac, int flag) { + this.data = data; + this.mac = mac; + this.flag = flag; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + public String getMac() { + return mac; + } + + public void setMac(String mac) { + this.mac = mac; + } + + public int getFlag() { + return flag; + } + + public void setFlag(int flag) { + this.flag = flag; + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/NotifyDataEvent.java b/app/src/main/java/com/qidian/baseble/ble/NotifyDataEvent.java new file mode 100644 index 0000000..86cd94c --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/NotifyDataEvent.java @@ -0,0 +1,46 @@ +package com.qidian.baseble.ble; + +import com.qidian.baseble.core.BluetoothGattChannel; +import com.qidian.baseble.model.BluetoothLeDevice; +import com.vise.xsnow.event.IEvent; + +public class NotifyDataEvent implements IEvent { + private byte[] data; + private BluetoothLeDevice bluetoothLeDevice; + private BluetoothGattChannel bluetoothGattChannel; + public String name; + + public NotifyDataEvent() { + } + + public NotifyDataEvent(String o) { + name = o; + } + + public byte[] getData() { + return data; + } + + public NotifyDataEvent setData(byte[] data) { + this.data = data; + return this; + } + + public BluetoothLeDevice getBluetoothLeDevice() { + return bluetoothLeDevice; + } + + public NotifyDataEvent setBluetoothLeDevice(BluetoothLeDevice bluetoothLeDevice) { + this.bluetoothLeDevice = bluetoothLeDevice; + return this; + } + + public BluetoothGattChannel getBluetoothGattChannel() { + return bluetoothGattChannel; + } + + public NotifyDataEvent setBluetoothGattChannel(BluetoothGattChannel bluetoothGattChannel) { + this.bluetoothGattChannel = bluetoothGattChannel; + return this; + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/ScanDataEvent.java b/app/src/main/java/com/qidian/baseble/ble/ScanDataEvent.java new file mode 100644 index 0000000..facb880 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/ScanDataEvent.java @@ -0,0 +1,30 @@ +package com.qidian.baseble.ble; + +import com.qidian.baseble.model.BluetoothLeDevice; +import com.vise.xsnow.event.IEvent; + +public class ScanDataEvent implements IEvent { + private byte[] data; + private BluetoothLeDevice bluetoothLeDevice; + + public ScanDataEvent() { + } + + public byte[] getData() { + return data; + } + + public ScanDataEvent setData(byte[] data) { + this.data = data; + return this; + } + + public BluetoothLeDevice getBluetoothLeDevice() { + return bluetoothLeDevice; + } + + public ScanDataEvent setBluetoothLeDevice(BluetoothLeDevice bluetoothLeDevice) { + this.bluetoothLeDevice = bluetoothLeDevice; + return this; + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/ScanDevice.java b/app/src/main/java/com/qidian/baseble/ble/ScanDevice.java new file mode 100644 index 0000000..7e2b0df --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/ScanDevice.java @@ -0,0 +1,20 @@ +package com.qidian.baseble.ble; + +import com.qidian.baseble.model.BluetoothLeDevice; +import com.vise.xsnow.event.IEvent; + +public class ScanDevice implements IEvent { + private BluetoothLeDevice bluetoothLeDevice; + + public ScanDevice() { + } + + public BluetoothLeDevice getBluetoothLeDevice() { + return bluetoothLeDevice; + } + + public ScanDevice setBluetoothLeDevice(BluetoothLeDevice bluetoothLeDevice) { + this.bluetoothLeDevice = bluetoothLeDevice; + return this; + } +} diff --git a/app/src/main/java/com/qidian/baseble/ble/ScanEvent.java b/app/src/main/java/com/qidian/baseble/ble/ScanEvent.java new file mode 100644 index 0000000..7311dfd --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/ble/ScanEvent.java @@ -0,0 +1,40 @@ +package com.qidian.baseble.ble; + +import com.qidian.baseble.model.BluetoothLeDeviceStore; +import com.vise.xsnow.event.IEvent; + +public class ScanEvent implements IEvent { + private boolean isScanTimeout; + private boolean isScanFinish; + private BluetoothLeDeviceStore bluetoothLeDeviceStore; + + public ScanEvent() { + } + + public boolean isScanTimeout() { + return isScanTimeout; + } + + public ScanEvent setScanTimeout(boolean scanTimeout) { + isScanTimeout = scanTimeout; + return this; + } + + public boolean isScanFinish() { + return isScanFinish; + } + + public ScanEvent setScanFinish(boolean scanFinish) { + isScanFinish = scanFinish; + return this; + } + + public BluetoothLeDeviceStore getBluetoothLeDeviceStore() { + return bluetoothLeDeviceStore; + } + + public ScanEvent setBluetoothLeDeviceStore(BluetoothLeDeviceStore bluetoothLeDeviceStore) { + this.bluetoothLeDeviceStore = bluetoothLeDeviceStore; + return this; + } +} diff --git a/app/src/main/java/com/qidian/baseble/callback/IBleCallback.java b/app/src/main/java/com/qidian/baseble/callback/IBleCallback.java new file mode 100644 index 0000000..eecf0ae --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/IBleCallback.java @@ -0,0 +1,16 @@ +package com.qidian.baseble.callback; + +import com.qidian.baseble.core.BluetoothGattChannel; +import com.qidian.baseble.exception.BleException; +import com.qidian.baseble.model.BluetoothLeDevice; + +/** + * @Description: 操作数据回调 + * @author: DAWI + * @date: 2017/10/17 19:42 + */ +public interface IBleCallback { + void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice); + + void onFailure(BleException exception); +} diff --git a/app/src/main/java/com/qidian/baseble/callback/IConnectCallback.java b/app/src/main/java/com/qidian/baseble/callback/IConnectCallback.java new file mode 100644 index 0000000..8dd3e79 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/IConnectCallback.java @@ -0,0 +1,20 @@ +package com.qidian.baseble.callback; + +import com.qidian.baseble.core.DeviceMirror; +import com.qidian.baseble.exception.BleException; + +/** + * @Description: 连接设备回调 + * @author: DAWI + * @date: 17/8/1 23:00. + */ +public interface IConnectCallback { + //连接成功 + void onConnectSuccess(DeviceMirror deviceMirror); + + //连接失败 + void onConnectFailure(BleException exception); + + //连接断开 + void onDisconnect(boolean isActive); +} diff --git a/app/src/main/java/com/qidian/baseble/callback/IRssiCallback.java b/app/src/main/java/com/qidian/baseble/callback/IRssiCallback.java new file mode 100644 index 0000000..566bf25 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/IRssiCallback.java @@ -0,0 +1,14 @@ +package com.qidian.baseble.callback; + +import com.qidian.baseble.exception.BleException; + +/** + * @Description: 获取信号值回调 + * @author: DAWI + * @date: 2017/10/19 15:19 + */ +public interface IRssiCallback { + void onSuccess(int rssi); + + void onFailure(BleException exception); +} diff --git a/app/src/main/java/com/qidian/baseble/callback/scan/IScanCallback.java b/app/src/main/java/com/qidian/baseble/callback/scan/IScanCallback.java new file mode 100644 index 0000000..ddab0d6 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/scan/IScanCallback.java @@ -0,0 +1,21 @@ +package com.qidian.baseble.callback.scan; + +import com.qidian.baseble.model.BluetoothLeDevice; +import com.qidian.baseble.model.BluetoothLeDeviceStore; + +/** + * @Description: 扫描回调 + * @author: DAWI + * @date: 17/9/10 18:14. + */ +public interface IScanCallback { + //发现设备 + void onDeviceFound(BluetoothLeDevice bluetoothLeDevice); + + //扫描完成 + void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore); + + //扫描超时 + void onScanTimeout(); + +} diff --git a/app/src/main/java/com/qidian/baseble/callback/scan/IScanFilter.java b/app/src/main/java/com/qidian/baseble/callback/scan/IScanFilter.java new file mode 100644 index 0000000..e9b7db0 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/scan/IScanFilter.java @@ -0,0 +1,12 @@ +package com.qidian.baseble.callback.scan; + +import com.qidian.baseble.model.BluetoothLeDevice; + +/** + * @Description: 扫描过滤接口,根据需要实现过滤规则 + * @author: DAWI + * @date: 17/9/10 18:19. + */ +public interface IScanFilter { + BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice); +} diff --git a/app/src/main/java/com/qidian/baseble/callback/scan/ListFilterScanCallback.java b/app/src/main/java/com/qidian/baseble/callback/scan/ListFilterScanCallback.java new file mode 100644 index 0000000..9f2c136 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/scan/ListFilterScanCallback.java @@ -0,0 +1,50 @@ +package com.qidian.baseble.callback.scan; + +import com.qidian.baseble.model.BluetoothLeDevice; + +import java.util.List; + +/** + * @Description: 指定设备集合进行过滤,一般用设备名称和Mac地址集合 + * @author: DAWI + * @date: 17/9/12 22:50. + */ +public class ListFilterScanCallback extends ScanCallback { + private List deviceNameList;//指定设备名称集合 + private List deviceMacList;//指定设备Mac地址集合 + + public ListFilterScanCallback(IScanCallback scanCallback) { + super(scanCallback); + } + + public ListFilterScanCallback setDeviceNameList(List deviceNameList) { + this.deviceNameList = deviceNameList; + return this; + } + + public ListFilterScanCallback setDeviceMacList(List deviceMacList) { + this.deviceMacList = deviceMacList; + return this; + } + + @Override + public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { + BluetoothLeDevice tempDevice = null; + if (deviceNameList != null && deviceNameList.size() > 0) { + for (String deviceName : deviceNameList) { + if (bluetoothLeDevice != null && bluetoothLeDevice.getName() != null && deviceName != null + && deviceName.equalsIgnoreCase(bluetoothLeDevice.getName().trim())) { + tempDevice = bluetoothLeDevice; + } + } + } else if (deviceMacList != null && deviceMacList.size() > 0) { + for (String deviceMac : deviceMacList) { + if (bluetoothLeDevice != null && bluetoothLeDevice.getAddress() != null && deviceMac != null + && deviceMac.equalsIgnoreCase(bluetoothLeDevice.getAddress().trim())) { + tempDevice = bluetoothLeDevice; + } + } + } + return tempDevice; + } +} diff --git a/app/src/main/java/com/qidian/baseble/callback/scan/RegularFilterScanCallback.java b/app/src/main/java/com/qidian/baseble/callback/scan/RegularFilterScanCallback.java new file mode 100644 index 0000000..90c4c6d --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/scan/RegularFilterScanCallback.java @@ -0,0 +1,58 @@ +package com.qidian.baseble.callback.scan; + +import android.text.TextUtils; + +import com.qidian.baseble.model.BluetoothLeDevice; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Description: 根据正则过滤扫描设备,这里设置的是根据一定信号范围内指定正则设备名称的过滤 + * @author: DAWI + * @date: 17/9/12 22:19. + */ +public class RegularFilterScanCallback extends ScanCallback { + private Pattern pattern; + private Matcher matcher; + private String regularDeviceName;//正则表达式表示的设备名称 + private int deviceRssi;//设备的信号 + + public RegularFilterScanCallback(IScanCallback scanCallback) { + super(scanCallback); + pattern = Pattern.compile("^[\\x00-\\xff]*$"); + } + + public RegularFilterScanCallback setRegularDeviceName(String regularDeviceName) { + this.regularDeviceName = regularDeviceName; + if (!TextUtils.isEmpty(this.regularDeviceName)) { + pattern = Pattern.compile(this.regularDeviceName); + } + return this; + } + + public RegularFilterScanCallback setDeviceRssi(int deviceRssi) { + this.deviceRssi = deviceRssi; + return this; + } + + @Override + public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { + BluetoothLeDevice tempDevice = null; + String tempName = bluetoothLeDevice.getName(); + int tempRssi = bluetoothLeDevice.getRssi(); + if (!TextUtils.isEmpty(tempName)) { + matcher = pattern.matcher(tempName); + if (this.deviceRssi < 0) { + if (matcher.matches() && tempRssi >= this.deviceRssi) { + tempDevice = bluetoothLeDevice; + } + } else { + if (matcher.matches()) { + tempDevice = bluetoothLeDevice; + } + } + } + return tempDevice; + } +} diff --git a/app/src/main/java/com/qidian/baseble/callback/scan/ScanCallback.java b/app/src/main/java/com/qidian/baseble/callback/scan/ScanCallback.java new file mode 100644 index 0000000..fd495a5 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/scan/ScanCallback.java @@ -0,0 +1,203 @@ +package com.qidian.baseble.callback.scan; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.qidian.baseble.ViseBle; +import com.qidian.baseble.common.BleConfig; +import com.qidian.baseble.model.BluetoothLeDevice; +import com.qidian.baseble.model.BluetoothLeDeviceStore; +import com.qidian.baseble.utils.HexUtil; +import com.qidian.zhongkesmart.config.EventCode; +import com.qidian.zhongkesmart.model.ActionInfo; +import com.qidian.zhongkesmart.model.DeviceInfo; +import com.qidian.zhongkesmart.utils.DataServer; +import com.qidian.zhongkesmart.utils.EventBusUtil; +import com.qidian.zhongkesmart.utils.ObjectBoxUtils; +import com.qidian.zhongkesmart.utils.TimeUtil; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.qidian.baseble.utils.HexUtil.byteArrayToInt; +import static com.qidian.baseble.utils.HexUtil.byteToInt; + +/** + * @Description: 扫描设备回调 + * @author: DAWI + * @date: 17/8/1 22:58. + */ +public class ScanCallback implements BluetoothAdapter.LeScanCallback, IScanFilter { + protected Handler handler = new Handler(Looper.myLooper()); + protected boolean isScan = true;//是否开始扫描 + protected boolean isScanning = false;//是否正在扫描 + protected BluetoothLeDeviceStore bluetoothLeDeviceStore;//用来存储扫描到的设备 + protected IScanCallback scanCallback;//扫描结果回调 + private Map deviceAction = new HashMap<>(); + + public ScanCallback(IScanCallback scanCallback) { + this.scanCallback = scanCallback; + if (scanCallback == null) { + throw new NullPointerException("this scanCallback is null!"); + } + bluetoothLeDeviceStore = new BluetoothLeDeviceStore(); + } + + public ScanCallback setScan(boolean scan) { + isScan = scan; + return this; + } + + public boolean isScanning() { + return isScanning; + } + + public void scan() { + if (isScan) { + if (isScanning) { + return; + } + Log.i("BlueTooth==","开始扫描"); + bluetoothLeDeviceStore.clear(); + if (BleConfig.getInstance().getScanTimeout() > 0) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + isScanning = false; + + if (ViseBle.getInstance().getBluetoothAdapter() != null) { + ViseBle.getInstance().getBluetoothAdapter().stopLeScan(ScanCallback.this); + } + + if (bluetoothLeDeviceStore.getDeviceMap() != null + && bluetoothLeDeviceStore.getDeviceMap().size() > 0) { + scanCallback.onScanFinish(bluetoothLeDeviceStore); + } else { + scanCallback.onScanTimeout(); + } + } + }, BleConfig.getInstance().getScanTimeout()); + }else if (BleConfig.getInstance().getScanRepeatInterval() > 0){ + //如果超时时间设置为一直扫描(即 <= 0),则判断是否设置重复扫描间隔 + handler.postDelayed(new Runnable() { + @Override + public void run() { + isScanning = false; + if (ViseBle.getInstance().getBluetoothAdapter() != null) { + ViseBle.getInstance().getBluetoothAdapter().stopLeScan(ScanCallback.this); + } + +// if (bluetoothLeDeviceStore.getDeviceMap() != null +// && bluetoothLeDeviceStore.getDeviceMap().size() > 0) { +// scanCallback.onScanFinish(bluetoothLeDeviceStore); +// } else { +// scanCallback.onScanTimeout(); +// } + isScanning = true; + if (ViseBle.getInstance().getBluetoothAdapter() != null) { + ViseBle.getInstance().getBluetoothAdapter().startLeScan(ScanCallback.this); + } + handler.postDelayed(this,BleConfig.getInstance().getScanRepeatInterval()); + } + }, BleConfig.getInstance().getScanRepeatInterval()); + } + isScanning = true; + if (ViseBle.getInstance().getBluetoothAdapter() != null) { + ViseBle.getInstance().getBluetoothAdapter().startLeScan(ScanCallback.this); + } + } else { + isScanning = false; + if (ViseBle.getInstance().getBluetoothAdapter() != null) { + Log.i("BlueTooth==","结束扫描"); + ViseBle.getInstance().getBluetoothAdapter().stopLeScan(ScanCallback.this); + } + } + } + + public ScanCallback removeHandlerMsg() { + handler.removeCallbacksAndMessages(null); + bluetoothLeDeviceStore.clear(); + return this; + } + + @Override + public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) { + BluetoothLeDevice bluetoothLeDevice = new BluetoothLeDevice(bluetoothDevice, rssi, scanRecord, System.currentTimeMillis()); + BluetoothLeDevice filterDevice = onFilter(bluetoothLeDevice); + if (filterDevice != null) { + if (DataServer.INSTANCE.getConnectMode() == 1) { + if (filterDevice.getName() != null && filterDevice.getName().startsWith("TJ")) { + bluetoothLeDeviceStore.addDevice(filterDevice); + scanCallback.onDeviceFound(filterDevice); +// Log.i( +// "BlueTooth==", +// "广播包数据" + HexUtil.encodeHexStr(scanRecord)); + if (scanRecord.length > 21 && scanRecord[15] == (byte) 0x90 && scanRecord[16] == (byte) 0x07) { + byte[] scanResult = new byte[9]; + System.arraycopy(scanRecord, 15, scanResult, 0, 9); + //同一个设备,不同的动作 + //if (!deviceAction.containsKey(bluetoothDevice.getAddress())||(!Arrays.equals(deviceAction.get(bluetoothDevice.getAddress()), new byte[]{scanResult[7], scanResult[8]})&&System.currentTimeMillis()-deviceTime.get(bluetoothDevice.getAddress())>10000)) { + //deviceTime.put(bluetoothDevice.getAddress(),System.currentTimeMillis()); + + + if (!deviceAction.containsKey(bluetoothDevice.getAddress())||!Arrays.equals(deviceAction.get(bluetoothDevice.getAddress()), new byte[]{scanResult[2], scanResult[3]})){ + deviceAction.put(bluetoothDevice.getAddress(),new byte[]{scanResult[2], scanResult[3]}); + int power = byteToInt(scanResult[6]); + int cActionDataIndex = byteArrayToInt(new byte[]{scanResult[2], scanResult[3]}); + Log.i( + "BlueTooth==", + bluetoothDevice.getAddress()+"的广播包数据" + HexUtil.encodeHexStr(scanResult)+ " index="+cActionDataIndex); + List list = ObjectBoxUtils.INSTANCE.getDeviceByMac(bluetoothDevice.getAddress()); + DeviceInfo deviceInfo; + if (list != null && list.size() > 0) { + deviceInfo = list.get(0); + deviceInfo.setPower(power + ""); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + //贴件已绑定,才保存数据 + if (deviceInfo.isBind()) { + byte[] actionData = new byte[]{scanResult[7], scanResult[8]}; + if (!Arrays.equals(actionData, new byte[]{0x00, 0x00})) { + ActionInfo actionInfo = new ActionInfo(); + actionInfo.setMac(bluetoothDevice.getAddress()); + actionInfo.setData(actionData); + actionInfo.setTime(TimeUtil.INSTANCE.getNowTime()); + ObjectBoxUtils.INSTANCE.addData(actionInfo, ActionInfo.class); + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET, bluetoothDevice.getAddress())); + } + } + } else { + deviceInfo = new DeviceInfo(); + deviceInfo.setMac(bluetoothDevice.getAddress()); + deviceInfo.setPower(power + ""); + deviceInfo.setOnline(true); + deviceInfo.setUpdateTime(TimeUtil.INSTANCE.getNowTime()); + } + ObjectBoxUtils.INSTANCE.updateData(deviceInfo, DeviceInfo.class); + } + } + } + } else { + if (filterDevice.getName() != null && filterDevice.getName().startsWith("ZJ")) { + bluetoothLeDeviceStore.addDevice(filterDevice); + scanCallback.onDeviceFound(filterDevice); + Log.i( + "BlueTooth==", + "periodScanCallback:发现设备" + filterDevice.getName() + ); + } + } + } + } + + @Override + public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { + return bluetoothLeDevice; + } + +} diff --git a/app/src/main/java/com/qidian/baseble/callback/scan/SingleFilterScanCallback.java b/app/src/main/java/com/qidian/baseble/callback/scan/SingleFilterScanCallback.java new file mode 100644 index 0000000..a45bf56 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/scan/SingleFilterScanCallback.java @@ -0,0 +1,58 @@ +package com.qidian.baseble.callback.scan; + +import com.qidian.baseble.ViseBle; +import com.qidian.baseble.model.BluetoothLeDevice; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @Description: 设置扫描指定的单个设备,一般是设备名称和Mac地址 + * @author: DAWI + * @date: 17/9/12 22:16. + */ +public class SingleFilterScanCallback extends ScanCallback { + private AtomicBoolean hasFound = new AtomicBoolean(false); + private String deviceName;//指定设备名称 + private String deviceMac;//指定设备Mac地址 + + public SingleFilterScanCallback(IScanCallback scanCallback) { + super(scanCallback); + } + + public ScanCallback setDeviceName(String deviceName) { + this.deviceName = deviceName; + return this; + } + + public SingleFilterScanCallback setDeviceMac(String deviceMac) { + this.deviceMac = deviceMac; + return this; + } + + @Override + public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { + BluetoothLeDevice tempDevice = null; + if (!hasFound.get()) { + if (bluetoothLeDevice != null && bluetoothLeDevice.getAddress() != null && deviceMac != null + && deviceMac.equalsIgnoreCase(bluetoothLeDevice.getAddress().trim())) { + hasFound.set(true); + isScanning = false; + removeHandlerMsg(); + ViseBle.getInstance().stopScan(SingleFilterScanCallback.this); + tempDevice = bluetoothLeDevice; + bluetoothLeDeviceStore.addDevice(bluetoothLeDevice); + scanCallback.onScanFinish(bluetoothLeDeviceStore); + } else if (bluetoothLeDevice != null && bluetoothLeDevice.getName() != null && deviceName != null + && deviceName.equalsIgnoreCase(bluetoothLeDevice.getName().trim())) { + hasFound.set(true); + isScanning = false; + removeHandlerMsg(); + ViseBle.getInstance().stopScan(SingleFilterScanCallback.this); + tempDevice = bluetoothLeDevice; + bluetoothLeDeviceStore.addDevice(bluetoothLeDevice); + scanCallback.onScanFinish(bluetoothLeDeviceStore); + } + } + return tempDevice; + } +} diff --git a/app/src/main/java/com/qidian/baseble/callback/scan/UuidFilterScanCallback.java b/app/src/main/java/com/qidian/baseble/callback/scan/UuidFilterScanCallback.java new file mode 100644 index 0000000..b22940e --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/callback/scan/UuidFilterScanCallback.java @@ -0,0 +1,45 @@ +package com.qidian.baseble.callback.scan; + +import android.os.ParcelUuid; + +import com.qidian.baseble.model.BluetoothLeDevice; + +import java.util.UUID; + +/** + * @Description: 根据指定uuid过滤设备 + * @author: DAWI + * @date: 17/9/12 23:20. + */ +public class UuidFilterScanCallback extends ScanCallback { + private UUID uuid;//设备uuid + + public UuidFilterScanCallback(IScanCallback scanCallback) { + super(scanCallback); + } + + public UuidFilterScanCallback setUuid(String uuid) { + this.uuid = UUID.fromString(uuid); + return this; + } + + public UuidFilterScanCallback setUuid(UUID uuid) { + this.uuid = uuid; + return this; + } + + @Override + public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { + BluetoothLeDevice tempDevice = null; + if (bluetoothLeDevice != null && bluetoothLeDevice.getDevice() != null + && bluetoothLeDevice.getDevice().getUuids() != null + && bluetoothLeDevice.getDevice().getUuids().length > 0) { + for (ParcelUuid parcelUuid : bluetoothLeDevice.getDevice().getUuids()) { + if (uuid != null && uuid == parcelUuid.getUuid()) { + tempDevice = bluetoothLeDevice; + } + } + } + return tempDevice; + } +} diff --git a/app/src/main/java/com/qidian/baseble/common/BleConfig.java b/app/src/main/java/com/qidian/baseble/common/BleConfig.java new file mode 100644 index 0000000..28155dc --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/common/BleConfig.java @@ -0,0 +1,223 @@ +package com.qidian.baseble.common; + +import static com.qidian.baseble.common.BleConstant.DEFAULT_CONN_TIME; +import static com.qidian.baseble.common.BleConstant.DEFAULT_MAX_CONNECT_COUNT; +import static com.qidian.baseble.common.BleConstant.DEFAULT_OPERATE_TIME; +import static com.qidian.baseble.common.BleConstant.DEFAULT_RETRY_COUNT; +import static com.qidian.baseble.common.BleConstant.DEFAULT_RETRY_INTERVAL; +import static com.qidian.baseble.common.BleConstant.DEFAULT_SCAN_REPEAT_INTERVAL; +import static com.qidian.baseble.common.BleConstant.DEFAULT_SCAN_TIME; + +/** + * @Description: 蓝牙通信相关配置 + * @author: DAWI + * @date: 2017/10/16 11:46 + */ +public class BleConfig { + private static BleConfig instance; + + private int scanTimeout = DEFAULT_SCAN_TIME;//扫描超时时间(毫秒) + private int connectTimeout = DEFAULT_CONN_TIME;//连接超时时间(毫秒) + private int operateTimeout = DEFAULT_OPERATE_TIME;//数据操作超时时间(毫秒) + private int connectRetryCount = DEFAULT_RETRY_COUNT;//连接重试次数 + private int connectRetryInterval = DEFAULT_RETRY_INTERVAL;//连接重试间隔(毫秒) + private int operateRetryCount = DEFAULT_RETRY_COUNT;//数据操作重试次数 + private int operateRetryInterval = DEFAULT_RETRY_INTERVAL;//数据操作重试间隔时间(毫秒) + private int maxConnectCount = DEFAULT_MAX_CONNECT_COUNT;//最大连接数量 + + //yankee + private int scanRepeatInterval = DEFAULT_SCAN_REPEAT_INTERVAL;//每隔X时间重复扫描 (毫秒) + + private BleConfig() { + } + + public static BleConfig getInstance() { + if (instance == null) { + synchronized (BleConfig.class) { + if (instance == null) { + instance = new BleConfig(); + } + } + } + return instance; + } + + /** + * 获取发送数据超时时间 + * + * @return 返回发送数据超时时间 + */ + public int getOperateTimeout() { + return operateTimeout; + } + + /** + * 设置发送数据超时时间 + * + * @param operateTimeout 发送数据超时时间 + * @return 返回ViseBle + */ + public BleConfig setOperateTimeout(int operateTimeout) { + this.operateTimeout = operateTimeout; + return this; + } + + /** + * 获取连接超时时间 + * + * @return 返回连接超时时间 + */ + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * 设置连接超时时间 + * + * @param connectTimeout 连接超时时间 + * @return 返回ViseBle + */ + public BleConfig setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + /** + * 获取扫描超时时间 + * + * @return 返回扫描超时时间 + */ + public int getScanTimeout() { + return scanTimeout; + } + + /** + * 设置扫描超时时间 + * + * @param scanTimeout 扫描超时时间 + * @return 返回ViseBle + */ + public BleConfig setScanTimeout(int scanTimeout) { + this.scanTimeout = scanTimeout; + return this; + } + + /** + * 获取连接重试次数 + * + * @return + */ + public int getConnectRetryCount() { + return connectRetryCount; + } + + /** + * 设置连接重试次数 + * + * @param connectRetryCount + * @return + */ + public BleConfig setConnectRetryCount(int connectRetryCount) { + this.connectRetryCount = connectRetryCount; + return this; + } + + /** + * 获取连接重试间隔时间 + * + * @return + */ + public int getConnectRetryInterval() { + return connectRetryInterval; + } + + /** + * 设置连接重试间隔时间 + * + * @param connectRetryInterval + * @return + */ + public BleConfig setConnectRetryInterval(int connectRetryInterval) { + this.connectRetryInterval = connectRetryInterval; + return this; + } + + /** + * 获取最大连接数量 + * + * @return + */ + public int getMaxConnectCount() { + return maxConnectCount; + } + + /** + * 设置最大连接数量 + * + * @param maxConnectCount + * @return + */ + public BleConfig setMaxConnectCount(int maxConnectCount) { + this.maxConnectCount = maxConnectCount; + return this; + } + + /** + * 获取操作数据重试次数 + * + * @return + */ + public int getOperateRetryCount() { + return operateRetryCount; + } + + /** + * 设置操作数据重试次数 + * + * @param operateRetryCount + * @return + */ + public BleConfig setOperateRetryCount(int operateRetryCount) { + this.operateRetryCount = operateRetryCount; + return this; + } + + /** + * 获取操作数据重试间隔时间 + * + * @return + */ + public int getOperateRetryInterval() { + return operateRetryInterval; + } + + /** + * 设置操作数据重试间隔时间 + * + * @param operateRetryInterval + * @return + */ + public BleConfig setOperateRetryInterval(int operateRetryInterval) { + this.operateRetryInterval = operateRetryInterval; + return this; + } + + /** + * 获取扫描间隔时间 + * @return + */ + public int getScanRepeatInterval() { + return scanRepeatInterval; + } + + /** + * 设置每隔多少时间重复扫描一次 + * 设置扫描间隔时间 (毫秒) + * @param scanRepeatInterval + * @return + */ + public BleConfig setScanRepeatInterval(int scanRepeatInterval) { + this.scanRepeatInterval = scanRepeatInterval; + return this; + } +} diff --git a/app/src/main/java/com/qidian/baseble/common/BleConstant.java b/app/src/main/java/com/qidian/baseble/common/BleConstant.java new file mode 100644 index 0000000..ff40a62 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/common/BleConstant.java @@ -0,0 +1,34 @@ +package com.qidian.baseble.common; + +/** + * @Description: BLE常量 + * @author: DAWI + * @date: 16/8/20 20:31. + */ +public class BleConstant { + public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; + + public static final int TIME_FOREVER = -1; + +// public static final int DEFAULT_SCAN_TIME = 20000;//之前的数值 + public static final int DEFAULT_SCAN_TIME = 25000; + public static final int DEFAULT_CONN_TIME = 10000; + public static final int DEFAULT_OPERATE_TIME = 5000; + + public static final int DEFAULT_RETRY_INTERVAL = 1000; + public static final int DEFAULT_RETRY_COUNT = 3; + + public static final int DEFAULT_MAX_CONNECT_COUNT = 5; + + public static final int MSG_CONNECT_TIMEOUT = 0x01; + public static final int MSG_WRITE_DATA_TIMEOUT = 0x02; + public static final int MSG_READ_DATA_TIMEOUT = 0x03; + public static final int MSG_RECEIVE_DATA_TIMEOUT = 0x04; + public static final int MSG_CONNECT_RETRY = 0x05; + public static final int MSG_WRITE_DATA_RETRY = 0x06; + public static final int MSG_READ_DATA_RETRY = 0x07; + public static final int MSG_RECEIVE_DATA_RETRY = 0x08; + + //yankee + public static final int DEFAULT_SCAN_REPEAT_INTERVAL = -1; +} diff --git a/app/src/main/java/com/qidian/baseble/common/BleExceptionCode.java b/app/src/main/java/com/qidian/baseble/common/BleExceptionCode.java new file mode 100644 index 0000000..ecf3021 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/common/BleExceptionCode.java @@ -0,0 +1,14 @@ +package com.qidian.baseble.common; + +/** + * @Description: BLE异常Code + * @author: DAWI + * @date: 16/8/14 10:43. + */ +public enum BleExceptionCode { + TIMEOUT, //超时 + CONNECT_ERR, //连接异常 + GATT_ERR, //GATT异常 + INITIATED_ERR, //初始化异常 + OTHER_ERR //其他异常 +} diff --git a/app/src/main/java/com/qidian/baseble/common/BluetoothServiceType.java b/app/src/main/java/com/qidian/baseble/common/BluetoothServiceType.java new file mode 100644 index 0000000..165bc8a --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/common/BluetoothServiceType.java @@ -0,0 +1,30 @@ +package com.qidian.baseble.common; + +import android.bluetooth.BluetoothClass; + +/** + * @Description: BLE服务类型 + * @author: DAWI + * @date: 16/8/7 22:07. + */ +public enum BluetoothServiceType { + AUDIO(BluetoothClass.Service.AUDIO), //音频服务 + CAPTURE(BluetoothClass.Service.CAPTURE), //捕捉服务 + INFORMATION(BluetoothClass.Service.INFORMATION), //信息服务 + LIMITED_DISCOVERABILITY(BluetoothClass.Service.LIMITED_DISCOVERABILITY), //有限发现服务 + NETWORKING(BluetoothClass.Service.NETWORKING), //网络服务 + OBJECT_TRANSFER(BluetoothClass.Service.OBJECT_TRANSFER), //对象传输服务 + POSITIONING(BluetoothClass.Service.POSITIONING), //定位服务 + RENDER(BluetoothClass.Service.RENDER), //给予服务 + TELEPHONY(BluetoothClass.Service.TELEPHONY); //电话服务 + + private int code; + + BluetoothServiceType(int code) { + this.code = code; + } + + public int getCode() { + return this.code; + } +} diff --git a/app/src/main/java/com/qidian/baseble/common/ConnectState.java b/app/src/main/java/com/qidian/baseble/common/ConnectState.java new file mode 100644 index 0000000..cd0906c --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/common/ConnectState.java @@ -0,0 +1,25 @@ +package com.qidian.baseble.common; + +/** + * @Description: 状态描述 + * @author: DAWI + * @date: 16/8/20 17:06. + */ +public enum ConnectState { + CONNECT_INIT(-1), //连接初始化 + CONNECT_PROCESS(0x00), //连接中 + CONNECT_SUCCESS(0x01), //连接成功 + CONNECT_FAILURE(0x02), //连接失败 + CONNECT_TIMEOUT(0x03), //连接超时 + CONNECT_DISCONNECT(0x04); //连接断开 + + private int code; + + ConnectState(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/app/src/main/java/com/qidian/baseble/common/PropertyType.java b/app/src/main/java/com/qidian/baseble/common/PropertyType.java new file mode 100644 index 0000000..ad93fbd --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/common/PropertyType.java @@ -0,0 +1,23 @@ +package com.qidian.baseble.common; + +/** + * @Description: 属性类型 + * @author: DAWI + * @date: 2017/10/17 20:27 + */ +public enum PropertyType { + PROPERTY_READ(0x01), + PROPERTY_WRITE(0x02), + PROPERTY_NOTIFY(0x04), + PROPERTY_INDICATE(0x08); + + private int propertyValue; + + PropertyType(int propertyValue) { + this.propertyValue = propertyValue; + } + + public int getPropertyValue() { + return propertyValue; + } +} diff --git a/app/src/main/java/com/qidian/baseble/core/BluetoothGattChannel.java b/app/src/main/java/com/qidian/baseble/core/BluetoothGattChannel.java new file mode 100644 index 0000000..4ac3a88 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/core/BluetoothGattChannel.java @@ -0,0 +1,138 @@ +package com.qidian.baseble.core; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; + +import com.qidian.baseble.common.PropertyType; + +import java.util.UUID; + +/** + * @Description: BluetoothGatt 相关信息 + * @author: DAWI + * @date: 2017/10/17 16:25 + */ +public class BluetoothGattChannel { + + private BluetoothGatt bluetoothGatt; + private BluetoothGattService service; + private BluetoothGattCharacteristic characteristic; + private BluetoothGattDescriptor descriptor; + private String gattInfoKey; + private PropertyType propertyType; + private UUID serviceUUID; + private UUID characteristicUUID; + private UUID descriptorUUID; + + private BluetoothGattChannel(BluetoothGatt bluetoothGatt, PropertyType propertyType, UUID serviceUUID, UUID characteristicUUID, UUID descriptorUUID) { + this.bluetoothGatt = bluetoothGatt; + this.propertyType = propertyType; + this.serviceUUID = serviceUUID; + this.characteristicUUID = characteristicUUID; + this.descriptorUUID = descriptorUUID; + StringBuilder stringBuilder = new StringBuilder(); + if (propertyType != null) { + stringBuilder.append(propertyType.getPropertyValue()); + } + if (serviceUUID != null && bluetoothGatt != null) { + service = bluetoothGatt.getService(serviceUUID); + stringBuilder.append(serviceUUID.toString()); + } + if (service != null && characteristicUUID != null) { + characteristic = service.getCharacteristic(characteristicUUID); + stringBuilder.append(characteristicUUID.toString()); + } + if (characteristic != null && descriptorUUID != null) { + descriptor = characteristic.getDescriptor(descriptorUUID); + stringBuilder.append(descriptorUUID.toString()); + } + gattInfoKey = stringBuilder.toString(); + } + + public BluetoothGatt getBluetoothGatt() { + return bluetoothGatt; + } + + public BluetoothGattCharacteristic getCharacteristic() { + return characteristic; + } + + public void setCharacteristic(BluetoothGattCharacteristic bGCharacteristic) { + this.characteristic = bGCharacteristic; + } + + public BluetoothGattDescriptor getDescriptor() { + return descriptor; + } + + public BluetoothGattService getService() { + return service; + } + + public BluetoothGattChannel setDescriptor(BluetoothGattDescriptor descriptor) { + this.descriptor = descriptor; + return this; + } + + public String getGattInfoKey() { + return gattInfoKey; + } + + public UUID getCharacteristicUUID() { + return characteristicUUID; + } + + public UUID getDescriptorUUID() { + return descriptorUUID; + } + + public PropertyType getPropertyType() { + return propertyType; + } + + public UUID getServiceUUID() { + return serviceUUID; + } + + public static class Builder { + private BluetoothGatt bluetoothGatt; + private PropertyType propertyType; + private UUID serviceUUID; + private UUID characteristicUUID; + private UUID descriptorUUID; + + public Builder() { + } + + public Builder setBluetoothGatt(BluetoothGatt bluetoothGatt) { + this.bluetoothGatt = bluetoothGatt; + return this; + } + + public Builder setCharacteristicUUID(UUID characteristicUUID) { + this.characteristicUUID = characteristicUUID; + return this; + } + + public Builder setDescriptorUUID(UUID descriptorUUID) { + this.descriptorUUID = descriptorUUID; + return this; + } + + public Builder setPropertyType(PropertyType propertyType) { + this.propertyType = propertyType; + return this; + } + + public Builder setServiceUUID(UUID serviceUUID) { + this.serviceUUID = serviceUUID; + return this; + } + + public BluetoothGattChannel builder() { + return new BluetoothGattChannel(bluetoothGatt, propertyType, serviceUUID, characteristicUUID, descriptorUUID); + } + } +} diff --git a/app/src/main/java/com/qidian/baseble/core/DeviceMirror.java b/app/src/main/java/com/qidian/baseble/core/DeviceMirror.java new file mode 100644 index 0000000..4c2d1ac --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/core/DeviceMirror.java @@ -0,0 +1,1046 @@ +package com.qidian.baseble.core; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import com.qidian.baseble.ViseBle; +import com.qidian.baseble.ble.NotifyDataEvent; +import com.qidian.baseble.callback.IBleCallback; +import com.qidian.baseble.callback.IConnectCallback; +import com.qidian.baseble.callback.IRssiCallback; +import com.qidian.baseble.common.BleConfig; +import com.qidian.baseble.common.BleConstant; +import com.qidian.baseble.common.ConnectState; +import com.qidian.baseble.common.PropertyType; +import com.qidian.baseble.exception.BleException; +import com.qidian.baseble.exception.ConnectException; +import com.qidian.baseble.exception.GattException; +import com.qidian.baseble.exception.TimeoutException; +import com.qidian.baseble.model.BluetoothLeDevice; +import com.qidian.baseble.utils.HexUtil; +import com.qidian.zhongkesmart.utils.DataServer; +import com.vise.log.ViseLog; +import com.vise.xsnow.event.BusManager; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + + +import static com.qidian.baseble.common.BleConstant.MSG_CONNECT_RETRY; +import static com.qidian.baseble.common.BleConstant.MSG_CONNECT_TIMEOUT; +import static com.qidian.baseble.common.BleConstant.MSG_READ_DATA_RETRY; +import static com.qidian.baseble.common.BleConstant.MSG_READ_DATA_TIMEOUT; +import static com.qidian.baseble.common.BleConstant.MSG_RECEIVE_DATA_RETRY; +import static com.qidian.baseble.common.BleConstant.MSG_RECEIVE_DATA_TIMEOUT; +import static com.qidian.baseble.common.BleConstant.MSG_WRITE_DATA_RETRY; +import static com.qidian.baseble.common.BleConstant.MSG_WRITE_DATA_TIMEOUT; + +//import com.md.utils.ble.NotifyDataEvent; +//import com.vise.xsnow.event.BusManager; + +/** + * @Description: 设备镜像(设备连接成功后返回的设备信息模型) + * @author: DAWI + * @date: 17/8/1 23:11. + */ +public class DeviceMirror { + private final DeviceMirror deviceMirror; + private final String uniqueSymbol;//唯一符号 + private final BluetoothLeDevice bluetoothLeDevice;//设备基础信息 + + private BluetoothGatt bluetoothGatt;//蓝牙GATT + private IRssiCallback rssiCallback;//获取信号值回调 + private IConnectCallback connectCallback;//连接回调 + private int connectRetryCount = 0;//当前连接重试次数 + private int writeDataRetryCount = 0;//当前写入数据重试次数 + private int readDataRetryCount = 0;//当前读取数据重试次数 + private int receiveDataRetryCount = 0;//当前接收数据重试次数 + private boolean isActiveDisconnect = false;//是否主动断开连接 + private boolean isIndication;//是否是指示器方式 + private boolean enable;//是否设置使能 + private byte[] writeData;//写入数据 + String mlh = ""; + private ConnectState connectState = ConnectState.CONNECT_INIT;//设备状态描述 + private volatile HashMap writeInfoMap = new HashMap<>();//写入数据GATT信息集合 + private volatile HashMap readInfoMap = new HashMap<>();//读取数据GATT信息集合 + private volatile HashMap enableInfoMap = new HashMap<>();//设置使能GATT信息集合 + private volatile HashMap bleCallbackMap = new HashMap<>();//数据操作回调集合 + private volatile HashMap receiveCallbackMap = new HashMap<>();//数据接收回调集合 + + private BluetoothGattCharacteristic writeCharacteristic; + private BluetoothGattCharacteristic readCharacteristic; + + private final Handler handler = new Handler(Looper.myLooper()) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_CONNECT_TIMEOUT) { + connectFailure(new TimeoutException()); + } else if (msg.what == MSG_CONNECT_RETRY) { +// 发现这个会让两个设备掉线后 重复连接 导致 频繁切换后 假连接的情况 战术后仰 + connect(); + } else if (msg.what == MSG_WRITE_DATA_TIMEOUT) { + writeFailure(new TimeoutException(), true); + } else if (msg.what == MSG_WRITE_DATA_RETRY) { + write(writeData, mlh); + } else if (msg.what == MSG_READ_DATA_TIMEOUT) { + readFailure(new TimeoutException(), true); + } else if (msg.what == MSG_READ_DATA_RETRY) { + read(); + } else if (msg.what == MSG_RECEIVE_DATA_TIMEOUT) { + enableFailure(new TimeoutException(), true); + } else if (msg.what == MSG_RECEIVE_DATA_RETRY) { + enable(enable, isIndication); + } + } + }; + + /** + * 蓝牙所有相关操作的核心回调类 + */ + private BluetoothGattCallback coreGattCallback = new BluetoothGattCallback() { + + /** + * 连接状态改变,主要用来分析设备的连接与断开 + * @param gatt GATT + * @param status 改变前状态 + * @param newState 改变后状态 + */ + @Override + public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { + bluetoothGatt = gatt; + if (newState == BluetoothGatt.STATE_CONNECTED) { + gatt.discoverServices(); + } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { + if (bluetoothGatt != null) { + bluetoothGatt.disconnect(); + refreshDeviceCache(); + bluetoothGatt.close(); + } + + if (connectCallback != null) { + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + ViseBle.getInstance().getDeviceMirrorPool().removeDeviceMirror(deviceMirror); + if (status == BluetoothGatt.GATT_SUCCESS) { + connectState = ConnectState.CONNECT_DISCONNECT; + connectCallback.onDisconnect(isActiveDisconnect); + } else { + connectState = ConnectState.CONNECT_FAILURE; + connectCallback.onConnectFailure(new ConnectException(gatt, status)); + } + } + } else if (newState == BluetoothGatt.STATE_CONNECTING) { + connectState = ConnectState.CONNECT_PROCESS; + } + } + + /** + * 发现服务,主要用来获取设备支持的服务列表 + * @param gatt GATT + * @param status 当前状态 + */ + @Override + public void onServicesDiscovered(final BluetoothGatt gatt, final int status) { + if (handler != null) { + handler.removeMessages(MSG_CONNECT_TIMEOUT); + } + if (status == 0) { + bluetoothGatt = gatt; + connectState = ConnectState.CONNECT_SUCCESS; + if (connectCallback != null) { + isActiveDisconnect = false; + ViseBle.getInstance().getDeviceMirrorPool().addDeviceMirror(deviceMirror); + + List supportedGattServices = gatt.getServices(); + for (BluetoothGattService gattService : supportedGattServices) { + List gattCharacteristics = gattService.getCharacteristics(); + for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { + if (DataServer.INSTANCE.getUUID2().toString().equals(gattCharacteristic.getUuid().toString())) { + writeCharacteristic = gattCharacteristic; + Log.i("BlueTooth==", "写入服务"); + } else if (DataServer.INSTANCE.getUUID3().toString().equals(gattCharacteristic.getUuid().toString())) { + Log.i("BlueTooth==", "监听服务"); + readCharacteristic = gattCharacteristic; + } + } + } + + connectCallback.onConnectSuccess(deviceMirror); + } + } else { + connectFailure(new ConnectException(gatt, status)); + } + } + + /** + * 读取特征值,主要用来读取该特征值包含的可读信息 + * @param gatt GATT + * @param characteristic 特征值 + * @param status 当前状态 + */ + @Override + public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + handleSuccessData(readInfoMap, characteristic.getValue(), status, true); + } else { + readFailure(new GattException(status), true); + } + } + + /** + * 写入特征值,主要用来发送数据到设备 + * @param gatt GATT + * @param characteristic 特征值 + * @param status 当前状态 + */ + @Override + public void onCharacteristicWrite(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { + + if (status == BluetoothGatt.GATT_SUCCESS) { + handleSuccessData(writeInfoMap, characteristic.getValue(), status, false); + } else { + writeFailure(new GattException(status), true); + } + } + + /** + * 特征值改变,主要用来接收设备返回的数据信息 + * @param gatt GATT + * @param characteristic 特征值 + */ + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + ViseLog.i("onCharacteristicChanged data:" + HexUtil.encodeHexStr(characteristic.getValue()) + + " ,thread: " + Thread.currentThread()); + for (Map.Entry receiveEntry : receiveCallbackMap.entrySet()) { + String receiveKey = receiveEntry.getKey(); + IBleCallback receiveValue = receiveEntry.getValue(); + for (Map.Entry gattInfoEntry : enableInfoMap.entrySet()) { + String bluetoothGattInfoKey = gattInfoEntry.getKey(); + BluetoothGattChannel bluetoothGattInfoValue = gattInfoEntry.getValue(); + if (receiveKey.equals(bluetoothGattInfoKey)) { + receiveValue.onSuccess(characteristic.getValue(), bluetoothGattInfoValue, bluetoothLeDevice); + } + } + } + } + + /** + * 读取属性描述值,主要用来获取设备当前属性描述的值 + * @param gatt GATT + * @param descriptor 属性描述 + * @param status 当前状态 + */ + @Override + public void onDescriptorRead(BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + handleSuccessData(readInfoMap, descriptor.getValue(), status, true); + } else { + readFailure(new GattException(status), true); + } + } + + /** + * 写入属性描述值,主要用来根据当前属性描述值写入数据到设备 + * @param gatt GATT + * @param descriptor 属性描述值 + * @param status 当前状态 + */ + @Override + public void onDescriptorWrite(BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + handleSuccessData(writeInfoMap, descriptor.getValue(), status, false); + } else { + writeFailure(new GattException(status), true); + } + if (status == BluetoothGatt.GATT_SUCCESS) { + handleSuccessData(enableInfoMap, descriptor.getValue(), status, false); + } else { + enableFailure(new GattException(status), true); + } + } + + /** + * 阅读设备信号值 + * @param gatt GATT + * @param rssi 设备当前信号 + * @param status 当前状态 + */ + @Override + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + if (rssiCallback != null) { + rssiCallback.onSuccess(rssi); + } + } else { + if (rssiCallback != null) { + rssiCallback.onFailure(new GattException(status)); + } + } + } + }; + + public DeviceMirror(BluetoothLeDevice bluetoothLeDevice) { + deviceMirror = this; + this.bluetoothLeDevice = bluetoothLeDevice; + this.uniqueSymbol = bluetoothLeDevice.getAddress() + bluetoothLeDevice.getName(); + } + + /** + * 连接设备 + * + * @param connectCallback + */ + public synchronized void connect(IConnectCallback connectCallback) { + if (connectState == ConnectState.CONNECT_SUCCESS || connectState == ConnectState.CONNECT_PROCESS + || (connectState == ConnectState.CONNECT_INIT && connectRetryCount != 0)) { + return; + } + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + this.connectCallback = connectCallback; + connectRetryCount = 0; + connect(); + + } + + /** + * 绑定一个具备读写或可通知能力的通道,设置需要操作数据的相关信息,包含:数据操作回调,数据操作类型,数据通道建立所需的UUID。 + * + * @param bleCallback + * @param bluetoothGattChannel + */ + public synchronized void bindChannel(IBleCallback bleCallback, BluetoothGattChannel bluetoothGattChannel) { + if (bleCallback != null && bluetoothGattChannel != null) { + String key = bluetoothGattChannel.getGattInfoKey(); + PropertyType propertyType = bluetoothGattChannel.getPropertyType(); + if (!bleCallbackMap.containsKey(key)) { + bleCallbackMap.put(key, bleCallback); + } + if (propertyType == PropertyType.PROPERTY_READ) { + if (!readInfoMap.containsKey(key)) { + readInfoMap.put(key, bluetoothGattChannel); + } + } else if (propertyType == PropertyType.PROPERTY_WRITE) { + if (!writeInfoMap.containsKey(key)) { + writeInfoMap.put(key, bluetoothGattChannel); + } + } else if (propertyType == PropertyType.PROPERTY_NOTIFY) { + if (!enableInfoMap.containsKey(key)) { + enableInfoMap.put(key, bluetoothGattChannel); + } + } else if (propertyType == PropertyType.PROPERTY_INDICATE) { + if (!enableInfoMap.containsKey(key)) { + enableInfoMap.put(key, bluetoothGattChannel); + } + } + } + } + + /** + * 解绑通道 + * + * @param bluetoothGattChannel + */ + public synchronized void unbindChannel(BluetoothGattChannel bluetoothGattChannel) { + if (bluetoothGattChannel != null) { + String key = bluetoothGattChannel.getGattInfoKey(); + if (bleCallbackMap.containsKey(key)) { + bleCallbackMap.remove(key); + } + if (readInfoMap.containsKey(key)) { + readInfoMap.remove(key); + } else if (writeInfoMap.containsKey(key)) { + writeInfoMap.remove(key); + } else if (enableInfoMap.containsKey(key)) { + enableInfoMap.remove(key); + } + } + } + + /** + * 写入数据 + * + * @param data + */ + public void writeData(byte[] data, String ml) { + if (data == null || data.length > 20) { + return; + } + if (!checkBluetoothGattInfo(writeInfoMap)) { + return; + } + if (handler != null) { + handler.removeMessages(MSG_WRITE_DATA_TIMEOUT); + handler.removeMessages(MSG_WRITE_DATA_RETRY); + } + writeDataRetryCount = 0; + writeData = data; + mlh = ml; + write(data, ml); + } + + /** + * 读取数据 + */ + public void readData() { + if (!checkBluetoothGattInfo(readInfoMap)) { + return; + } + if (handler != null) { + handler.removeMessages(MSG_READ_DATA_TIMEOUT); + handler.removeMessages(MSG_READ_DATA_RETRY); + } + readDataRetryCount = 0; + read(); + } + + /** + * 获取设备信号值 + * + * @param rssiCallback + */ + public void readRemoteRssi(IRssiCallback rssiCallback) { + this.rssiCallback = rssiCallback; + if (bluetoothGatt != null) { + bluetoothGatt.readRemoteRssi(); + } + } + + /** + * 注册获取数据通知 + * + * @param isIndication + */ + public void registerNotify(boolean isIndication) { + if (!checkBluetoothGattInfo(enableInfoMap)) { + return; + } + if (handler != null) { + handler.removeMessages(MSG_RECEIVE_DATA_TIMEOUT); + handler.removeMessages(MSG_RECEIVE_DATA_RETRY); + } + receiveDataRetryCount = 0; + enable = true; + this.isIndication = isIndication; + enable(enable, this.isIndication); + } + + /** + * 取消获取数据通知 + * + * @param isIndication + */ + public void unregisterNotify(boolean isIndication) { + if (!checkBluetoothGattInfo(enableInfoMap)) { + return; + } + if (handler != null) { + handler.removeMessages(MSG_RECEIVE_DATA_TIMEOUT); + handler.removeMessages(MSG_RECEIVE_DATA_RETRY); + } + enable = false; + this.isIndication = isIndication; + enable(enable, this.isIndication); + } + + /** + * 设置接收数据监听 + * + * @param key 接收数据回调key,由serviceUUID+characteristicUUID+descriptorUUID组成 + * @param receiveCallback 接收数据回调 + */ + public void setNotifyListener(String key, IBleCallback receiveCallback) { + receiveCallbackMap.put(key, receiveCallback); + } + + /** + * 获取当前连接失败重试次数 + * + * @return + */ + public int getConnectRetryCount() { + return connectRetryCount; + } + + /** + * 获取当前读取数据失败重试次数 + * + * @return + */ + public int getReadDataRetryCount() { + return readDataRetryCount; + } + + /** + * 获取当前使能数据失败重试次数 + * + * @return + */ + public int getReceiveDataRetryCount() { + return receiveDataRetryCount; + } + + /** + * 获取当前写入数据失败重试次数 + * + * @return + */ + public int getWriteDataRetryCount() { + return writeDataRetryCount; + } + + /** + * 获取设备唯一标识 + * + * @return + */ + public String getUniqueSymbol() { + return uniqueSymbol; + } + + /** + * 获取蓝牙GATT + * + * @return 返回蓝牙GATT + */ + public BluetoothGatt getBluetoothGatt() { + return bluetoothGatt; + } + + /** + * 获取设备连接状态 + * + * @return 返回设备连接状态 + */ + public ConnectState getConnectState() { + return connectState; + } + + /** + * 获取服务列表 + * + * @return + */ + public List getGattServiceList() { + if (bluetoothGatt != null) { + return bluetoothGatt.getServices(); + } + return null; + } + + /** + * 根据服务UUID获取指定服务 + * + * @param serviceUuid + * @return + */ + public BluetoothGattService getGattService(UUID serviceUuid) { + if (bluetoothGatt != null && serviceUuid != null) { + return bluetoothGatt.getService(serviceUuid); + } + return null; + } + + /** + * 获取某个服务的特征值列表 + * + * @param serviceUuid + * @return + */ + public List getGattCharacteristicList(UUID serviceUuid) { + if (getGattService(serviceUuid) != null && serviceUuid != null) { + return getGattService(serviceUuid).getCharacteristics(); + } + return null; + } + + /** + * 根据特征值UUID获取某个服务的指定特征值 + * + * @param serviceUuid + * @param characteristicUuid + * @return + */ + public BluetoothGattCharacteristic getGattCharacteristic(UUID serviceUuid, UUID characteristicUuid) { + if (getGattService(serviceUuid) != null && serviceUuid != null && characteristicUuid != null) { + return getGattService(serviceUuid).getCharacteristic(characteristicUuid); + } + return null; + } + + /** + * 获取某个特征值的描述属性列表 + * + * @param serviceUuid + * @param characteristicUuid + * @return + */ + public List getGattDescriptorList(UUID serviceUuid, UUID characteristicUuid) { + if (getGattCharacteristic(serviceUuid, characteristicUuid) != null && serviceUuid != null && characteristicUuid != null) { + return getGattCharacteristic(serviceUuid, characteristicUuid).getDescriptors(); + } + return null; + } + + /** + * 根据描述属性UUID获取某个特征值的指定属性值 + * + * @param serviceUuid + * @param characteristicUuid + * @param descriptorUuid + * @return + */ + public BluetoothGattDescriptor getGattDescriptor(UUID serviceUuid, UUID characteristicUuid, UUID descriptorUuid) { + if (getGattCharacteristic(serviceUuid, characteristicUuid) != null && serviceUuid != null && characteristicUuid != null && descriptorUuid != null) { + return getGattCharacteristic(serviceUuid, characteristicUuid).getDescriptor(descriptorUuid); + } + return null; + } + + /** + * 获取设备详细信息 + * + * @return + */ + public BluetoothLeDevice getBluetoothLeDevice() { + return bluetoothLeDevice; + } + + /** + * 设备是否连接 + * + * @return + */ + public boolean isConnected() { + return connectState == ConnectState.CONNECT_SUCCESS; + } + + /** + * 移除数据操作回调 + * + * @param key + */ + public synchronized void removeBleCallback(String key) { + if (bleCallbackMap.containsKey(key)) { + bleCallbackMap.remove(key); + } + } + + /** + * 移除接收数据回调 + * + * @param key + */ + public synchronized void removeReceiveCallback(String key) { + if (receiveCallbackMap.containsKey(key)) { + receiveCallbackMap.remove(key); + } + } + + /** + * 移除所有回调 + */ + public synchronized void removeAllCallback() { + bleCallbackMap.clear(); + receiveCallbackMap.clear(); + } + + /** + * 刷新设备缓存 + * + * @return 返回是否刷新成功 + */ + public synchronized boolean refreshDeviceCache() { + try { + final Method refresh = BluetoothGatt.class.getMethod("refresh"); + if (refresh != null && bluetoothGatt != null) { + final boolean success = (Boolean) refresh.invoke(getBluetoothGatt()); + return success; + } + } catch (Exception e) { + } + return false; + } + + /** + * 主动断开设备连接 + */ + public synchronized void disconnect() { + connectState = ConnectState.CONNECT_INIT; + connectRetryCount = 0; + if (bluetoothGatt != null) { + isActiveDisconnect = true; + bluetoothGatt.disconnect(); + } + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + } + + /** + * 关闭GATT + */ + public synchronized void close() { + if (bluetoothGatt != null) { + bluetoothGatt.close(); + } + } + + @Override + public String toString() { + return "DeviceMirror{" + + "bluetoothLeDevice=" + bluetoothLeDevice + + ", uniqueSymbol='" + uniqueSymbol + '\'' + + '}'; + } + + /** + * 清除设备资源,在不使用该设备时调用 + */ + public synchronized void clear() { + disconnect(); + refreshDeviceCache(); + close(); + if (bleCallbackMap != null) { + bleCallbackMap.clear(); + } + if (receiveCallbackMap != null) { + receiveCallbackMap.clear(); + } + if (writeInfoMap != null) { + writeInfoMap.clear(); + } + if (readInfoMap != null) { + readInfoMap.clear(); + } + if (enableInfoMap != null) { + enableInfoMap.clear(); + } + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + } + + /** + * UUID转换 + * + * @param uuid + * @return 返回UUID + */ + private UUID formUUID(String uuid) { + return uuid == null ? null : UUID.fromString(uuid); + } + + /** + * 检查BluetoothGattChannel集合是否有值 + * + * @param bluetoothGattInfoHashMap + * @return + */ + private boolean checkBluetoothGattInfo(HashMap bluetoothGattInfoHashMap) { + if (bluetoothGattInfoHashMap == null || bluetoothGattInfoHashMap.size() == 0) { + return false; + } + return true; + } + + /** + * 连接设备 + */ + private synchronized void connect() { + if (handler != null) { + handler.removeMessages(MSG_CONNECT_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_CONNECT_TIMEOUT, BleConfig.getInstance().getConnectTimeout()); + } + connectState = ConnectState.CONNECT_PROCESS;//CONNECT_PROCESS + if (bluetoothLeDevice != null && bluetoothLeDevice.getDevice() != null) { + bluetoothLeDevice.getDevice().connectGatt(ViseBle.getInstance().getContext(), false, coreGattCallback); + } + } + + /** + * 设置使能 + * + * @param enable 是否具备使能 + * @param isIndication 是否是指示器方式 + * @return + */ + private synchronized boolean enable(boolean enable, boolean isIndication) { + if (handler != null) { + handler.removeMessages(MSG_RECEIVE_DATA_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_RECEIVE_DATA_TIMEOUT, BleConfig.getInstance().getOperateTimeout()); + } + boolean success = false; + for (Map.Entry entry : enableInfoMap.entrySet()) { + String bluetoothGattInfoKey = entry.getKey(); + BluetoothGattChannel bluetoothGattInfoValue = entry.getValue(); + if(bluetoothGattInfoValue.getCharacteristic()==null&& readCharacteristic!=null){ + bluetoothGattInfoValue.setCharacteristic(readCharacteristic); + } + if (bluetoothGatt != null && bluetoothGattInfoValue.getCharacteristic() != null) { + success = bluetoothGatt.setCharacteristicNotification(bluetoothGattInfoValue.getCharacteristic(), enable); + } + BluetoothGattDescriptor bluetoothGattDescriptor = null; + if (bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() != null) { + bluetoothGattDescriptor = bluetoothGattInfoValue.getDescriptor(); + } else if (bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() == null) { + if (bluetoothGattInfoValue.getCharacteristic().getDescriptors() != null + && bluetoothGattInfoValue.getCharacteristic().getDescriptors().size() == 1) { + bluetoothGattDescriptor = bluetoothGattInfoValue.getCharacteristic().getDescriptors().get(0); + } else { + bluetoothGattDescriptor = bluetoothGattInfoValue.getCharacteristic() + .getDescriptor(UUID.fromString(BleConstant.CLIENT_CHARACTERISTIC_CONFIG)); + } + } + if (bluetoothGattDescriptor != null) { + bluetoothGattInfoValue.setDescriptor(bluetoothGattDescriptor); + if (isIndication) { + if (enable) { + bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); + } else { + bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); + } + } else { + if (enable) { + bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + } else { + bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); + } + } + if (bluetoothGatt != null) { + bluetoothGatt.writeDescriptor(bluetoothGattDescriptor); + } + } + } + return success; + } + + /** + * 读取数据 + * + * @return + */ + private synchronized boolean read() { + if (handler != null) { + handler.removeMessages(MSG_READ_DATA_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_READ_DATA_TIMEOUT, BleConfig.getInstance().getOperateTimeout()); + } + boolean success = false; + for (Map.Entry entry : readInfoMap.entrySet()) { + String bluetoothGattInfoKey = entry.getKey(); + BluetoothGattChannel bluetoothGattInfoValue = entry.getValue(); + if (bluetoothGatt != null && bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() != null) { + success = bluetoothGatt.readDescriptor(bluetoothGattInfoValue.getDescriptor()); + } else if (bluetoothGatt != null && bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() == null) { + success = bluetoothGatt.readCharacteristic(bluetoothGattInfoValue.getCharacteristic()); + } + } + return success; + } + + /** + * 写入数据 + * + * @param data + * @return + */ + private synchronized boolean write(final byte[] data, final String ml) { + if (handler != null) { + handler.removeMessages(MSG_WRITE_DATA_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_WRITE_DATA_TIMEOUT, BleConfig.getInstance().getOperateTimeout()); + } + boolean success = false; + for (Map.Entry entry : writeInfoMap.entrySet()) { + String bluetoothGattInfoKey = entry.getKey(); + BluetoothGattChannel bluetoothGattInfoValue = entry.getValue(); + if(bluetoothGattInfoValue.getCharacteristic()==null&& writeCharacteristic!=null){ + bluetoothGattInfoValue.setCharacteristic(writeCharacteristic); + } + if (bluetoothGatt != null && bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() != null) { + bluetoothGattInfoValue.getDescriptor().setValue(data); + success = bluetoothGatt.writeDescriptor(bluetoothGattInfoValue.getDescriptor()); + } else if (bluetoothGatt != null && bluetoothGattInfoValue.getCharacteristic() != null && bluetoothGattInfoValue.getDescriptor() == null) { + bluetoothGattInfoValue.getCharacteristic().setValue(data); + success = bluetoothGatt.writeCharacteristic(bluetoothGattInfoValue.getCharacteristic()); + } + } + if (success) { + //写入成功 + BusManager.getBus().post(new NotifyDataEvent(ml)); + Log.i("BlueTooth==", "写入成功"+ml); + } else { + Log.i("BlueTooth==", "写入失败"); + } +// if (!success) { +// new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { +// @Override +// public void run() { +// write(data, ml); +// } +// }, 100); +// } + return success; + } + + /** + * 连接失败处理 + * + * @param bleException 回调异常 + */ + private void connectFailure(BleException bleException) { + if (connectRetryCount < BleConfig.getInstance().getConnectRetryCount()) { + connectRetryCount++; + if (handler != null) { + handler.removeMessages(MSG_CONNECT_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_CONNECT_RETRY, BleConfig.getInstance().getConnectRetryInterval()); + } + } else { + if (bleException instanceof TimeoutException) { + connectState = ConnectState.CONNECT_TIMEOUT; + } else { + connectState = ConnectState.CONNECT_FAILURE; + } + close(); + if (connectCallback != null) { + connectCallback.onConnectFailure(bleException); + } + } + } + + /** + * 使能失败 + * + * @param bleException + * @param isRemoveCall + */ + private void enableFailure(BleException bleException, boolean isRemoveCall) { + if (receiveDataRetryCount < BleConfig.getInstance().getOperateRetryCount()) { + receiveDataRetryCount++; + if (handler != null) { + handler.removeMessages(MSG_RECEIVE_DATA_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_RECEIVE_DATA_RETRY, BleConfig.getInstance().getOperateRetryInterval()); + } + } else { + handleFailureData(enableInfoMap, bleException, isRemoveCall); + } + } + + /** + * 读取数据失败 + * + * @param bleException + * @param isRemoveCall + */ + private void readFailure(BleException bleException, boolean isRemoveCall) { + if (readDataRetryCount < BleConfig.getInstance().getOperateRetryCount()) { + readDataRetryCount++; + if (handler != null) { + handler.removeMessages(MSG_READ_DATA_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_READ_DATA_RETRY, BleConfig.getInstance().getOperateRetryInterval()); + } + } else { + handleFailureData(readInfoMap, bleException, isRemoveCall); + } + } + + /** + * 写入数据失败 + * + * @param bleException + * @param isRemoveCall + */ + private void writeFailure(BleException bleException, boolean isRemoveCall) { + if (writeDataRetryCount < BleConfig.getInstance().getOperateRetryCount()) { + writeDataRetryCount++; + if (handler != null) { + handler.removeMessages(MSG_WRITE_DATA_TIMEOUT); + handler.sendEmptyMessageDelayed(MSG_WRITE_DATA_RETRY, BleConfig.getInstance().getOperateRetryInterval()); + } + } else { + handleFailureData(writeInfoMap, bleException, isRemoveCall); + } + } + + /** + * 处理数据发送成功 + * + * @param bluetoothGattInfoHashMap + * @param value 待发送数据 + * @param status 发送数据状态 + * @param isRemoveCall 是否需要移除回调 + */ + private synchronized void handleSuccessData(HashMap bluetoothGattInfoHashMap, byte[] value, int status, + boolean isRemoveCall) { + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + String removeBleCallbackKey = null; + String removeBluetoothGattInfoKey = null; + for (Map.Entry callbackEntry : bleCallbackMap.entrySet()) { + String bleCallbackKey = callbackEntry.getKey(); + IBleCallback bleCallbackValue = callbackEntry.getValue(); + for (Map.Entry gattInfoEntry : bluetoothGattInfoHashMap.entrySet()) { + String bluetoothGattInfoKey = gattInfoEntry.getKey(); + BluetoothGattChannel bluetoothGattInfoValue = gattInfoEntry.getValue(); + if (bleCallbackKey.equals(bluetoothGattInfoKey)) { + bleCallbackValue.onSuccess(value, bluetoothGattInfoValue, bluetoothLeDevice); + removeBleCallbackKey = bleCallbackKey; + removeBluetoothGattInfoKey = bluetoothGattInfoKey; + } + } + } + synchronized (bleCallbackMap) { + if (isRemoveCall && removeBleCallbackKey != null && removeBluetoothGattInfoKey != null) { + bleCallbackMap.remove(removeBleCallbackKey); + bluetoothGattInfoHashMap.remove(removeBluetoothGattInfoKey); + } + } + } + + /** + * 处理数据发送失败 + * + * @param bluetoothGattInfoHashMap + * @param bleExceprion 回调异常 + * @param isRemoveCall 是否需要移除回调 + */ + private synchronized void handleFailureData(HashMap bluetoothGattInfoHashMap, BleException bleExceprion, + boolean isRemoveCall) { + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + String removeBleCallbackKey = null; + String removeBluetoothGattInfoKey = null; + for (Map.Entry callbackEntry : bleCallbackMap.entrySet()) { + String bleCallbackKey = callbackEntry.getKey(); + IBleCallback bleCallbackValue = callbackEntry.getValue(); + for (Map.Entry gattInfoEntry : bluetoothGattInfoHashMap.entrySet()) { + String bluetoothGattInfoKey = gattInfoEntry.getKey(); + if (bleCallbackKey.equals(bluetoothGattInfoKey)) { + bleCallbackValue.onFailure(bleExceprion); + removeBleCallbackKey = bleCallbackKey; + removeBluetoothGattInfoKey = bluetoothGattInfoKey; + } + } + } + synchronized (bleCallbackMap) { + if (isRemoveCall && removeBleCallbackKey != null && removeBluetoothGattInfoKey != null) { + bleCallbackMap.remove(removeBleCallbackKey); + bluetoothGattInfoHashMap.remove(removeBluetoothGattInfoKey); + } + } + } +} diff --git a/app/src/main/java/com/qidian/baseble/core/DeviceMirrorPool.java b/app/src/main/java/com/qidian/baseble/core/DeviceMirrorPool.java new file mode 100644 index 0000000..f1719f7 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/core/DeviceMirrorPool.java @@ -0,0 +1,227 @@ +package com.qidian.baseble.core; + +import com.qidian.baseble.common.BleConfig; +import com.qidian.baseble.common.ConnectState; +import com.qidian.baseble.model.BluetoothLeDevice; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * @Description: 设备镜像池,用来管理多个设备连接后的操作 + * @author: DAWI + * @date: 17/8/1 23:18. + */ +public class DeviceMirrorPool { + private final LruHashMap DEVICE_MIRROR_MAP; + + public DeviceMirrorPool() { + DEVICE_MIRROR_MAP = new LruHashMap<>(BleConfig.getInstance().getMaxConnectCount()); + } + + public DeviceMirrorPool(int deviceMirrorSize) { + DEVICE_MIRROR_MAP = new LruHashMap<>(deviceMirrorSize); + } + + /** + * 添加设备镜像 + * + * @param bluetoothLeDevice + */ + public synchronized void addDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { + if (bluetoothLeDevice == null) { + return; + } + String key = bluetoothLeDevice.getAddress() + bluetoothLeDevice.getName(); + if (!DEVICE_MIRROR_MAP.containsKey(key)) { + DEVICE_MIRROR_MAP.put(key, new DeviceMirror(bluetoothLeDevice)); + } + } + + /** + * 添加设备镜像 + * + * @param deviceMirror + */ + public synchronized void addDeviceMirror(DeviceMirror deviceMirror) { + if (deviceMirror == null) { + return; + } + if (!DEVICE_MIRROR_MAP.containsKey(deviceMirror.getUniqueSymbol())) { + DEVICE_MIRROR_MAP.put(deviceMirror.getUniqueSymbol(), deviceMirror); + } + } + + /** + * 删除设备镜像 + * + * @param bluetoothLeDevice + */ + public synchronized void removeDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { + if (bluetoothLeDevice == null) { + return; + } + String key = bluetoothLeDevice.getAddress() + bluetoothLeDevice.getName(); + if (DEVICE_MIRROR_MAP.containsKey(key)) { + DEVICE_MIRROR_MAP.remove(key); + } + } + + /** + * 删除设备镜像 + * + * @param deviceMirror + */ + public synchronized void removeDeviceMirror(DeviceMirror deviceMirror) { + if (deviceMirror == null) { + return; + } + if (DEVICE_MIRROR_MAP.containsKey(deviceMirror.getUniqueSymbol())) { + deviceMirror.clear(); + DEVICE_MIRROR_MAP.remove(deviceMirror.getUniqueSymbol()); + } + } + + /** + * 判断是否包含设备镜像 + * + * @param deviceMirror + * @return + */ + public synchronized boolean isContainDevice(DeviceMirror deviceMirror) { + if (deviceMirror == null || !DEVICE_MIRROR_MAP.containsKey(deviceMirror.getUniqueSymbol())) { + return false; + } + return true; + } + + /** + * 判断是否包含设备镜像 + * + * @param bluetoothLeDevice + * @return + */ + public synchronized boolean isContainDevice(BluetoothLeDevice bluetoothLeDevice) { + if (bluetoothLeDevice == null || !DEVICE_MIRROR_MAP.containsKey(bluetoothLeDevice.getAddress() + + bluetoothLeDevice.getName())) { + return false; + } + return true; + } + /** + * 判断是否连接设备 + * 自添加 2022 + * @return + */ + public synchronized boolean isConnectDevice( ) { + if (DEVICE_MIRROR_MAP.size()==0) { + return false; + } + return true; + } + + /** + * 获取连接池中该设备镜像的连接状态,如果没有连接则返回CONNECT_DISCONNECT。 + * + * @param bluetoothLeDevice + * @return + */ + public synchronized ConnectState getConnectState(BluetoothLeDevice bluetoothLeDevice) { + DeviceMirror deviceMirror = getDeviceMirror(bluetoothLeDevice); + if (deviceMirror != null) { + return deviceMirror.getConnectState(); + } + return ConnectState.CONNECT_DISCONNECT; + } + + /** + * 获取连接池中的设备镜像,如果没有连接则返回空 + * + * @param bluetoothLeDevice + * @return + */ + public synchronized DeviceMirror getDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { + if (bluetoothLeDevice != null) { + String key = bluetoothLeDevice.getAddress() + bluetoothLeDevice.getName(); + if (DEVICE_MIRROR_MAP.containsKey(key)) { + return DEVICE_MIRROR_MAP.get(key); + } + } + return null; + } + + /** + * 断开连接池中某一个设备 + * + * @param bluetoothLeDevice + */ + public synchronized void disconnect(BluetoothLeDevice bluetoothLeDevice) { + if (isContainDevice(bluetoothLeDevice)) { + getDeviceMirror(bluetoothLeDevice).disconnect(); + } + } + + /** + * 断开连接池中所有设备 + */ + public synchronized void disconnect() { + for (Map.Entry stringDeviceMirrorEntry : DEVICE_MIRROR_MAP.entrySet()) { + stringDeviceMirrorEntry.getValue().disconnect(); + } + DEVICE_MIRROR_MAP.clear(); + } + + /** + * 清除连接池 + */ + public synchronized void clear() { + for (Map.Entry stringDeviceMirrorEntry : DEVICE_MIRROR_MAP.entrySet()) { + stringDeviceMirrorEntry.getValue().clear(); + } + DEVICE_MIRROR_MAP.clear(); + } + + /** + * 获取连接池设备镜像Map集合 + * + * @return + */ + public Map getDeviceMirrorMap() { + return DEVICE_MIRROR_MAP; + } + + /** + * 获取连接池设备镜像List集合 + * + * @return + */ + public synchronized List getDeviceMirrorList() { + final List deviceMirrors = new ArrayList<>(DEVICE_MIRROR_MAP.values()); + Collections.sort(deviceMirrors, new Comparator() { + @Override + public int compare(final DeviceMirror lhs, final DeviceMirror rhs) { + return lhs.getUniqueSymbol().compareToIgnoreCase(rhs.getUniqueSymbol()); + } + }); + return deviceMirrors; + } + + /** + * 获取连接池设备详细信息List集合 + * + * @return + */ + public synchronized List getDeviceList() { + final List deviceList = new ArrayList<>(); + for (DeviceMirror deviceMirror : getDeviceMirrorList()) { + if (deviceMirror != null) { + deviceList.add(deviceMirror.getBluetoothLeDevice()); + } + } + return deviceList; + } + +} diff --git a/app/src/main/java/com/qidian/baseble/core/LruHashMap.java b/app/src/main/java/com/qidian/baseble/core/LruHashMap.java new file mode 100644 index 0000000..0937571 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/core/LruHashMap.java @@ -0,0 +1,34 @@ +package com.qidian.baseble.core; + +import java.util.LinkedHashMap; + +/** + * @Description: Lru算法实现的HashMap + * @author: DAWI + * @date: 2017/9/28 19:57 + */ +public class LruHashMap extends LinkedHashMap { + private final int MAX_SAVE_SIZE; + + public LruHashMap(int saveSize) { + super((int) Math.ceil(saveSize / 0.75) + 1, 0.75f, true); + MAX_SAVE_SIZE = saveSize; + } + + @Override + protected boolean removeEldestEntry(Entry eldest) { + if (size() > MAX_SAVE_SIZE && eldest.getValue() instanceof DeviceMirror) { + ((DeviceMirror) eldest.getValue()).disconnect(); + } + return size() > MAX_SAVE_SIZE; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Entry entry : entrySet()) { + sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue())); + } + return sb.toString(); + } +} diff --git a/app/src/main/java/com/qidian/baseble/exception/BleException.java b/app/src/main/java/com/qidian/baseble/exception/BleException.java new file mode 100644 index 0000000..b3fee72 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/BleException.java @@ -0,0 +1,46 @@ +package com.qidian.baseble.exception; + +import com.qidian.baseble.common.BleExceptionCode; + +import java.io.Serializable; + +/** + * @Description: BLE异常基类 + * @author: DAWI + * @date: 16/8/14 10:28. + */ +public class BleException implements Serializable { + private BleExceptionCode code; + private String description; + + public BleException(BleExceptionCode code, String description) { + this.code = code; + this.description = description; + } + + public BleExceptionCode getCode() { + return code; + } + + public BleException setCode(BleExceptionCode code) { + this.code = code; + return this; + } + + public String getDescription() { + return description; + } + + public BleException setDescription(String description) { + this.description = description; + return this; + } + + @Override + public String toString() { + return "BleException{" + + "code=" + code + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/qidian/baseble/exception/ConnectException.java b/app/src/main/java/com/qidian/baseble/exception/ConnectException.java new file mode 100644 index 0000000..ffe7f7c --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/ConnectException.java @@ -0,0 +1,47 @@ +package com.qidian.baseble.exception; + +import android.bluetooth.BluetoothGatt; + +import com.qidian.baseble.common.BleExceptionCode; + +/** + * @Description: 连接异常 + * @author: DAWI + * @date: 16/8/14 10:29. + */ +public class ConnectException extends BleException { + private BluetoothGatt bluetoothGatt; + private int gattStatus; + + public ConnectException(BluetoothGatt bluetoothGatt, int gattStatus) { + super(BleExceptionCode.CONNECT_ERR, "Connect Exception Occurred! "); + this.bluetoothGatt = bluetoothGatt; + this.gattStatus = gattStatus; + } + + public int getGattStatus() { + return gattStatus; + } + + public ConnectException setGattStatus(int gattStatus) { + this.gattStatus = gattStatus; + return this; + } + + public BluetoothGatt getBluetoothGatt() { + return bluetoothGatt; + } + + public ConnectException setBluetoothGatt(BluetoothGatt bluetoothGatt) { + this.bluetoothGatt = bluetoothGatt; + return this; + } + + @Override + public String toString() { + return "ConnectException{" + + "gattStatus=" + gattStatus + + ", bluetoothGatt=" + bluetoothGatt + + "} " + super.toString(); + } +} diff --git a/app/src/main/java/com/qidian/baseble/exception/GattException.java b/app/src/main/java/com/qidian/baseble/exception/GattException.java new file mode 100644 index 0000000..ce76500 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/GattException.java @@ -0,0 +1,33 @@ +package com.qidian.baseble.exception; + +import com.qidian.baseble.common.BleExceptionCode; + +/** + * @Description: Gatt异常 + * @author: DAWI + * @date: 16/8/14 10:30. + */ +public class GattException extends BleException { + private int gattStatus; + + public GattException(int gattStatus) { + super(BleExceptionCode.GATT_ERR, "Gatt Exception Occurred! "); + this.gattStatus = gattStatus; + } + + public int getGattStatus() { + return gattStatus; + } + + public GattException setGattStatus(int gattStatus) { + this.gattStatus = gattStatus; + return this; + } + + @Override + public String toString() { + return "GattException{" + + "gattStatus=" + gattStatus + + '}' + super.toString(); + } +} diff --git a/app/src/main/java/com/qidian/baseble/exception/InitiatedException.java b/app/src/main/java/com/qidian/baseble/exception/InitiatedException.java new file mode 100644 index 0000000..35f59bb --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/InitiatedException.java @@ -0,0 +1,14 @@ +package com.qidian.baseble.exception; + +import com.qidian.baseble.common.BleExceptionCode; + +/** + * @Description: 初始化异常 + * @author: DAWI + * @date: 16/8/14 10:30. + */ +public class InitiatedException extends BleException { + public InitiatedException() { + super(BleExceptionCode.INITIATED_ERR, "Initiated Exception Occurred! "); + } +} diff --git a/app/src/main/java/com/qidian/baseble/exception/OtherException.java b/app/src/main/java/com/qidian/baseble/exception/OtherException.java new file mode 100644 index 0000000..87f3a7e --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/OtherException.java @@ -0,0 +1,14 @@ +package com.qidian.baseble.exception; + +import com.qidian.baseble.common.BleExceptionCode; + +/** + * @Description: 其他异常 + * @author: DAWI + * @date: 16/8/14 10:32. + */ +public class OtherException extends BleException { + public OtherException(String description) { + super(BleExceptionCode.OTHER_ERR, description); + } +} diff --git a/app/src/main/java/com/qidian/baseble/exception/TimeoutException.java b/app/src/main/java/com/qidian/baseble/exception/TimeoutException.java new file mode 100644 index 0000000..6dc1e15 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/TimeoutException.java @@ -0,0 +1,14 @@ +package com.qidian.baseble.exception; + +import com.qidian.baseble.common.BleExceptionCode; + +/** + * @Description: 超时异常 + * @author: DAWI + * @date: 16/8/14 10:29. + */ +public class TimeoutException extends BleException { + public TimeoutException() { + super(BleExceptionCode.TIMEOUT, "Timeout Exception Occurred! "); + } +} diff --git a/app/src/main/java/com/qidian/baseble/exception/handler/BleExceptionHandler.java b/app/src/main/java/com/qidian/baseble/exception/handler/BleExceptionHandler.java new file mode 100644 index 0000000..47e3079 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/handler/BleExceptionHandler.java @@ -0,0 +1,57 @@ +package com.qidian.baseble.exception.handler; + +import com.qidian.baseble.exception.BleException; +import com.qidian.baseble.exception.ConnectException; +import com.qidian.baseble.exception.GattException; +import com.qidian.baseble.exception.InitiatedException; +import com.qidian.baseble.exception.OtherException; +import com.qidian.baseble.exception.TimeoutException; + +/** + * @Description: 异常处理 + * @author: DAWI + * @date: 16/8/14 10:35. + */ +public abstract class BleExceptionHandler { + public BleExceptionHandler handleException(BleException exception) { + if (exception != null) { + if (exception instanceof ConnectException) { + onConnectException((ConnectException) exception); + } else if (exception instanceof GattException) { + onGattException((GattException) exception); + } else if (exception instanceof TimeoutException) { + onTimeoutException((TimeoutException) exception); + } else if (exception instanceof InitiatedException) { + onInitiatedException((InitiatedException) exception); + } else { + onOtherException((OtherException) exception); + } + } + return this; + } + + /** + * connect failed + */ + protected abstract void onConnectException(ConnectException e); + + /** + * gatt error status + */ + protected abstract void onGattException(GattException e); + + /** + * operation timeout + */ + protected abstract void onTimeoutException(TimeoutException e); + + /** + * operation inititiated error + */ + protected abstract void onInitiatedException(InitiatedException e); + + /** + * other exceptions + */ + protected abstract void onOtherException(OtherException e); +} diff --git a/app/src/main/java/com/qidian/baseble/exception/handler/DefaultBleExceptionHandler.java b/app/src/main/java/com/qidian/baseble/exception/handler/DefaultBleExceptionHandler.java new file mode 100644 index 0000000..b30f023 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/exception/handler/DefaultBleExceptionHandler.java @@ -0,0 +1,40 @@ +package com.qidian.baseble.exception.handler; + +import com.qidian.baseble.exception.ConnectException; +import com.qidian.baseble.exception.GattException; +import com.qidian.baseble.exception.InitiatedException; +import com.qidian.baseble.exception.OtherException; +import com.qidian.baseble.exception.TimeoutException; +import com.vise.log.ViseLog; + +/** + * @Description: 异常默认处理 + * @author: DAWI + * @date: 16/8/14 10:35. + */ +public class DefaultBleExceptionHandler extends BleExceptionHandler { + @Override + protected void onConnectException(ConnectException e) { + ViseLog.e(e.getDescription()); + } + + @Override + protected void onGattException(GattException e) { + ViseLog.e(e.getDescription()); + } + + @Override + protected void onTimeoutException(TimeoutException e) { + ViseLog.e(e.getDescription()); + } + + @Override + protected void onInitiatedException(InitiatedException e) { + ViseLog.e(e.getDescription()); + } + + @Override + protected void onOtherException(OtherException e) { + ViseLog.e(e.getDescription()); + } +} diff --git a/app/src/main/java/com/qidian/baseble/model/BluetoothLeDevice.java b/app/src/main/java/com/qidian/baseble/model/BluetoothLeDevice.java new file mode 100644 index 0000000..935e168 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/model/BluetoothLeDevice.java @@ -0,0 +1,426 @@ +package com.qidian.baseble.model; + +import android.bluetooth.BluetoothDevice; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import com.qidian.baseble.common.BluetoothServiceType; +import com.qidian.baseble.model.adrecord.AdRecordStore; +import com.qidian.baseble.model.resolver.BluetoothClassResolver; +import com.qidian.baseble.utils.AdRecordUtil; +import com.vise.utils.convert.HexUtil; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * @Description: 设备信息 + * @author: DAWI + * @date: 16/8/5 20:44. + */ +public class BluetoothLeDevice implements Parcelable { + + /** + * The Constant CREATOR. + */ + public static final Creator CREATOR = new Creator() { + public BluetoothLeDevice createFromParcel(final Parcel in) { + return new BluetoothLeDevice(in); + } + + public BluetoothLeDevice[] newArray(final int size) { + return new BluetoothLeDevice[size]; + } + }; + protected static final int MAX_RSSI_LOG_SIZE = 10; + private static final String PARCEL_EXTRA_BLUETOOTH_DEVICE = "bluetooth_device"; + private static final String PARCEL_EXTRA_CURRENT_RSSI = "current_rssi"; + private static final String PARCEL_EXTRA_CURRENT_TIMESTAMP = "current_timestamp"; + private static final String PARCEL_EXTRA_DEVICE_RSSI_LOG = "device_rssi_log"; + private static final String PARCEL_EXTRA_DEVICE_SCANRECORD = "device_scanrecord"; + private static final String PARCEL_EXTRA_DEVICE_SCANRECORD_STORE = "device_scanrecord_store"; + private static final String PARCEL_EXTRA_FIRST_RSSI = "device_first_rssi"; + private static final String PARCEL_EXTRA_FIRST_TIMESTAMP = "first_timestamp"; + private static final long LOG_INVALIDATION_THRESHOLD = 10 * 1000; + private final AdRecordStore mRecordStore; + private final BluetoothDevice mDevice; + private final Map mRssiLog; + private final byte[] mScanRecord; + private final int mFirstRssi; + private final long mFirstTimestamp; + private int mCurrentRssi; + private long mCurrentTimestamp; + private transient Set mServiceSet; + + /** + * Instantiates a new Bluetooth LE device. + * + * @param device a standard android Bluetooth device + * @param rssi the RSSI value of the Bluetooth device + * @param scanRecord the scan record of the device + * @param timestamp the timestamp of the RSSI reading + */ + public BluetoothLeDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) { + mDevice = device; + mFirstRssi = rssi; + mFirstTimestamp = timestamp; + mRecordStore = new AdRecordStore(AdRecordUtil.parseScanRecordAsSparseArray(scanRecord)); + mScanRecord = scanRecord; + mRssiLog = new LinkedHashMap<>(MAX_RSSI_LOG_SIZE); + updateRssiReading(timestamp, rssi); + } + + /** + * Instantiates a new Bluetooth LE device. + * + * @param device the device + */ + public BluetoothLeDevice(final BluetoothLeDevice device) { + mCurrentRssi = device.getRssi(); + mCurrentTimestamp = device.getTimestamp(); + mDevice = device.getDevice(); + mFirstRssi = device.getFirstRssi(); + mFirstTimestamp = device.getFirstTimestamp(); + mRecordStore = new AdRecordStore(AdRecordUtil.parseScanRecordAsSparseArray(device.getScanRecord())); + mRssiLog = device.getRssiLog(); + mScanRecord = device.getScanRecord(); + } + + /** + * Instantiates a new bluetooth le device. + * + * @param in the in + */ + protected BluetoothLeDevice(final Parcel in) { + final Bundle b = in.readBundle(getClass().getClassLoader()); + + mCurrentRssi = b.getInt(PARCEL_EXTRA_CURRENT_RSSI, 0); + mCurrentTimestamp = b.getLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, 0); + mDevice = b.getParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE); + mFirstRssi = b.getInt(PARCEL_EXTRA_FIRST_RSSI, 0); + mFirstTimestamp = b.getLong(PARCEL_EXTRA_FIRST_TIMESTAMP, 0); + mRecordStore = b.getParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE); + mRssiLog = (Map) b.getSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG); + mScanRecord = b.getByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD); + } + + /** + * Adds the to rssi log. + * + * @param timestamp the timestamp + * @param rssiReading the rssi reading + */ + private void addToRssiLog(final long timestamp, final int rssiReading) { + synchronized (mRssiLog) { + if (timestamp - mCurrentTimestamp > LOG_INVALIDATION_THRESHOLD) { + mRssiLog.clear(); + } + + mCurrentRssi = rssiReading; + mCurrentTimestamp = timestamp; + mRssiLog.put(timestamp, rssiReading); + } + } + + /* (non-Javadoc) + * @see android.os.Parcelable#describeContents() + */ + @Override + public int describeContents() { + return 0; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final BluetoothLeDevice other = (BluetoothLeDevice) obj; + if (mCurrentRssi != other.mCurrentRssi) return false; + if (mCurrentTimestamp != other.mCurrentTimestamp) return false; + if (mDevice == null) { + if (other.mDevice != null) return false; + } else if (!mDevice.equals(other.mDevice)) return false; + if (mFirstRssi != other.mFirstRssi) return false; + if (mFirstTimestamp != other.mFirstTimestamp) return false; + if (mRecordStore == null) { + if (other.mRecordStore != null) return false; + } else if (!mRecordStore.equals(other.mRecordStore)) return false; + if (mRssiLog == null) { + if (other.mRssiLog != null) return false; + } else if (!mRssiLog.equals(other.mRssiLog)) return false; + if (Arrays.equals(mScanRecord, other.mScanRecord)) { + return true; + } + return false; + } + + /** + * Gets the ad record store. + * + * @return the ad record store + */ + public AdRecordStore getAdRecordStore() { + return mRecordStore; + } + + /** + * Gets the address. + * + * @return the address + */ + public String getAddress() { + return mDevice.getAddress(); + } + + /** + * Gets the bluetooth device bond state. + * + * @return the bluetooth device bond state + */ + public String getBluetoothDeviceBondState() { + return resolveBondingState(mDevice.getBondState()); + } + + /** + * Gets the bluetooth device class name. + * + * @return the bluetooth device class name + */ + public String getBluetoothDeviceClassName() { + return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass()); + } + + public Set getBluetoothDeviceKnownSupportedServices() { + if (mServiceSet == null) { + synchronized (this) { + if (mServiceSet == null) { + final Set serviceSet = new HashSet<>(); + for (final BluetoothServiceType service : BluetoothServiceType.values()) { + + if (mDevice.getBluetoothClass().hasService(service.getCode())) { + serviceSet.add(service); + } + } + mServiceSet = Collections.unmodifiableSet(serviceSet); + } + } + } + + return mServiceSet; + } + + /** + * Gets the bluetooth device major class name. + * + * @return the bluetooth device major class name + */ + public String getBluetoothDeviceMajorClassName() { + return BluetoothClassResolver.resolveMajorDeviceClass(mDevice.getBluetoothClass().getMajorDeviceClass()); + } + + /** + * Gets the device. + * + * @return the device + */ + public BluetoothDevice getDevice() { + return mDevice; + } + + /** + * Gets the first rssi. + * + * @return the first rssi + */ + public int getFirstRssi() { + return mFirstRssi; + } + + /** + * Gets the first timestamp. + * + * @return the first timestamp + */ + public long getFirstTimestamp() { + return mFirstTimestamp; + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return mDevice.getName(); + } + + /** + * Gets the rssi. + * + * @return the rssi + */ + public int getRssi() { + return mCurrentRssi; + } + + /** + * Gets the rssi log. + * + * @return the rssi log + */ + protected Map getRssiLog() { + synchronized (mRssiLog) { + return mRssiLog; + } + } + + /** + * Gets the running average rssi. + * + * @return the running average rssi + */ + public double getRunningAverageRssi() { + int sum = 0; + int count = 0; + + synchronized (mRssiLog) { + + for (final Long aLong : mRssiLog.keySet()) { + count++; + sum += mRssiLog.get(aLong); + } + } + + if (count > 0) { + return sum / count; + } else { + return 0; + } + + } + + /** + * 获取major + * @return major + */ +// public long getMajor(){ +// return HexUtil.byteToLong(HexUtilu.subBytes(mScanRecord,25,2),0,2,true); +// } + + /** + * 获取minor + * @return minor + */ +// public long minor(){ +// return HexUtil.byteToLong(HexUtil.subBytes(mScanRecord,27,2),0,2,true); +// } + + /** + * Gets the scan record. + * + * @return the scan record + */ + public byte[] getScanRecord() { + return mScanRecord; + } + + /** + * Gets the timestamp. + * + * @return the timestamp + */ + public long getTimestamp() { + return mCurrentTimestamp; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mCurrentRssi; + result = prime * result + (int) (mCurrentTimestamp ^ (mCurrentTimestamp >>> 32)); + result = prime * result + ((mDevice == null) ? 0 : mDevice.hashCode()); + result = prime * result + mFirstRssi; + result = prime * result + (int) (mFirstTimestamp ^ (mFirstTimestamp >>> 32)); + result = prime * result + ((mRecordStore == null) ? 0 : mRecordStore.hashCode()); + result = prime * result + ((mRssiLog == null) ? 0 : mRssiLog.hashCode()); + result = prime * result + Arrays.hashCode(mScanRecord); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "BluetoothLeDevice [mDevice=" + mDevice + ", " + + "mRssi=" + mFirstRssi + ", mScanRecord=" + HexUtil.encodeHexStr(mScanRecord) + + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + + getBluetoothDeviceClassName() + "]"; + } + + /** + * Update rssi reading. + * + * @param timestamp the timestamp + * @param rssiReading the rssi reading + */ + public void updateRssiReading(final long timestamp, final int rssiReading) { + addToRssiLog(timestamp, rssiReading); + } + + /* (non-Javadoc) + * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) + */ + @Override + public void writeToParcel(final Parcel parcel, final int arg1) { + final Bundle b = new Bundle(getClass().getClassLoader()); + + b.putByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD, mScanRecord); + + b.putInt(PARCEL_EXTRA_FIRST_RSSI, mFirstRssi); + b.putInt(PARCEL_EXTRA_CURRENT_RSSI, mCurrentRssi); + + b.putLong(PARCEL_EXTRA_FIRST_TIMESTAMP, mFirstTimestamp); + b.putLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, mCurrentTimestamp); + + b.putParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE, mDevice); + b.putParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE, mRecordStore); + b.putSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG, (Serializable) mRssiLog); + + parcel.writeBundle(b); + } + + /** + * Resolve bonding state. + * + * @param bondState the bond state + * @return the string + */ + private static String resolveBondingState(final int bondState) { + switch (bondState) { + case BluetoothDevice.BOND_BONDED://已配对 + return "Paired"; + case BluetoothDevice.BOND_BONDING://配对中 + return "Pairing"; + case BluetoothDevice.BOND_NONE://未配对 + return "UnBonded"; + default: + return "Unknown";//未知状态 + } + } +} diff --git a/app/src/main/java/com/qidian/baseble/model/BluetoothLeDeviceStore.java b/app/src/main/java/com/qidian/baseble/model/BluetoothLeDeviceStore.java new file mode 100644 index 0000000..9f602f9 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/model/BluetoothLeDeviceStore.java @@ -0,0 +1,71 @@ +package com.qidian.baseble.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description: 设备信息集合 + * @author: DAWI + * @date: 16/8/21 16:48. + */ +public class BluetoothLeDeviceStore { + + private final Map mDeviceMap; + + public BluetoothLeDeviceStore() { + mDeviceMap = new HashMap<>(); + } + + public void addDevice(BluetoothLeDevice device) { + if (device == null) { + return; + } + if (mDeviceMap.containsKey(device.getAddress())) { + mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi()); + } else { + mDeviceMap.put(device.getAddress(), device); + } + } + + public void removeDevice(BluetoothLeDevice device) { + if (device == null) { + return; + } + if (mDeviceMap.containsKey(device.getAddress())) { + mDeviceMap.remove(device.getAddress()); + } + } + + public void clear() { + mDeviceMap.clear(); + } + + public Map getDeviceMap() { + return mDeviceMap; + } + + public List getDeviceList() { + final List methodResult = new ArrayList<>(mDeviceMap.values()); + + Collections.sort(methodResult, new Comparator() { + + @Override + public int compare(final BluetoothLeDevice arg0, final BluetoothLeDevice arg1) { + return arg0.getAddress().compareToIgnoreCase(arg1.getAddress()); + } + }); + + return methodResult; + } + + @Override + public String toString() { + return "BluetoothLeDeviceStore{" + + "DeviceList=" + getDeviceList() + + '}'; + } +} diff --git a/app/src/main/java/com/qidian/baseble/model/adrecord/AdRecord.java b/app/src/main/java/com/qidian/baseble/model/adrecord/AdRecord.java new file mode 100644 index 0000000..9f93b40 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/model/adrecord/AdRecord.java @@ -0,0 +1,185 @@ +package com.qidian.baseble.model.adrecord; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * @Description: 广播包解析model + * 参考:https://www.bluetooth.com/zh-cn/specifications/assigned-numbers/generic-access-profile + * @author: DAWI + * @date: 16/8/7 21:53. + */ +public class AdRecord implements Parcelable { + + public static final int BLE_GAP_AD_TYPE_FLAGS = 0x01;//< Flags for discoverAbility. + public static final int BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE = 0x02;//< Partial list of 16 bit service UUIDs. + public static final int BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE = 0x03;//< Complete list of 16 bit service UUIDs. + public static final int BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE = 0x04;//< Partial list of 32 bit service UUIDs. + public static final int BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE = 0x05;//< Complete list of 32 bit service UUIDs. + public static final int BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE = 0x06;//< Partial list of 128 bit service UUIDs. + public static final int BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE = 0x07;//< Complete list of 128 bit service UUIDs. + public static final int BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME = 0x08;//< Short local device name. + public static final int BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME = 0x09;//< Complete local device name. + public static final int BLE_GAP_AD_TYPE_TX_POWER_LEVEL = 0x0A;//< Transmit power level. + public static final int BLE_GAP_AD_TYPE_CLASS_OF_DEVICE = 0x0D;//< Class of device. + public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C = 0x0E;//< Simple Pairing Hash C. + public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F;//< Simple Pairing Randomizer R. + public static final int BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE = 0x10;//< Security Manager TK Value. + public static final int BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11;//< Security Manager Out Of Band Flags. + public static final int BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE = 0x12;//< Slave Connection Interval Range. + public static final int BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT = 0x14;//< List of 16-bit Service Solicitation UUIDs. + public static final int BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT = 0x15;//< List of 128-bit Service Solicitation UUIDs. + public static final int BLE_GAP_AD_TYPE_SERVICE_DATA = 0x16;//< Service Data - 16-bit UUID. + public static final int BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS = 0x17;//< Public Target Address. + public static final int BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS = 0x18;//< Random Target Address. + public static final int BLE_GAP_AD_TYPE_APPEARANCE = 0x19;//< Appearance. + public static final int BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL = 0x1A;//< Advertising Interval. + public static final int BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B;//< LE Bluetooth Device Address. + public static final int BLE_GAP_AD_TYPE_LE_ROLE = 0x1C;//< LE Role. + public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256 = 0x1D;//< Simple Pairing Hash C-256. + public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256 = 0x1E;//< Simple Pairing Randomizer R-256. + public static final int BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID = 0x20;//< Service Data - 32-bit UUID. + public static final int BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID = 0x21;//< Service Data - 128-bit UUID. + public static final int BLE_GAP_AD_TYPE_3D_INFORMATION_DATA = 0x3D;//< 3D Information Data. + public static final int BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;//< Manufacturer Specific Data. + + public static final Creator CREATOR = new Creator() { + public AdRecord createFromParcel(final Parcel in) { + return new AdRecord(in); + } + + public AdRecord[] newArray(final int size) { + return new AdRecord[size]; + } + }; + private static final String PARCEL_RECORD_DATA = "record_data"; + private static final String PARCEL_RECORD_TYPE = "record_type"; + private static final String PARCEL_RECORD_LENGTH = "record_length"; + /* Model Object Definition */ + private final int mLength; + private final int mType; + private final byte[] mData; + + public AdRecord(final int length, final int type, final byte[] data) { + mLength = length; + mType = type; + mData = data; + } + + public AdRecord(final Parcel in) { + final Bundle b = in.readBundle(getClass().getClassLoader()); + mLength = b.getInt(PARCEL_RECORD_LENGTH); + mType = b.getInt(PARCEL_RECORD_TYPE); + mData = b.getByteArray(PARCEL_RECORD_DATA); + } + + @Override + public int describeContents() { + return 0; + } + + public byte[] getData() { + return mData; + } + + public String getHumanReadableType() { + return getHumanReadableAdType(mType); + } + + public int getLength() { + return mLength; + } + + public int getType() { + return mType; + } + + @Override + public String toString() { + return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + + getHumanReadableType() + "]"; + } + + @Override + public void writeToParcel(final Parcel parcel, final int arg1) { + final Bundle b = new Bundle(getClass().getClassLoader()); + + b.putInt(PARCEL_RECORD_LENGTH, mLength); + b.putInt(PARCEL_RECORD_TYPE, mType); + b.putByteArray(PARCEL_RECORD_DATA, mData); + + parcel.writeBundle(b); + } + + private static String getHumanReadableAdType(final int type) { + switch (type) { + case BLE_GAP_AD_TYPE_FLAGS: + return "Flags for discoverAbility."; + case BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE: + return "Partial list of 16 bit service UUIDs."; + case BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE: + return "Complete list of 16 bit service UUIDs."; + case BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE: + return "Partial list of 32 bit service UUIDs."; + case BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE: + return "Complete list of 32 bit service UUIDs."; + case BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE: + return "Partial list of 128 bit service UUIDs."; + case BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE: + return "Complete list of 128 bit service UUIDs."; + case BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME: + return "Short local device name."; + case BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME: + return "Complete local device name."; + case BLE_GAP_AD_TYPE_TX_POWER_LEVEL: + return "Transmit power level."; + case BLE_GAP_AD_TYPE_CLASS_OF_DEVICE: + return "Class of device."; + case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C: + return "Simple Pairing Hash C."; + case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R: + return "Simple Pairing Randomizer R."; + case BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE: + return "Security Manager TK Value."; + case BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS: + return "Security Manager Out Of Band Flags."; + case BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE: + return "Slave Connection Interval Range."; + case BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT: + return "List of 16-bit Service Solicitation UUIDs."; + case BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT: + return "List of 128-bit Service Solicitation UUIDs."; + case BLE_GAP_AD_TYPE_SERVICE_DATA: + return "Service Data - 16-bit UUID."; + case BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS: + return "Public Target Address."; + case BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS: + return "Random Target Address."; + case BLE_GAP_AD_TYPE_APPEARANCE: + return "Appearance."; + case BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL: + return "Advertising Interval."; + case BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS: + return "LE Bluetooth Device Address."; + case BLE_GAP_AD_TYPE_LE_ROLE: + return "LE Role."; + case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256: + return "Simple Pairing Hash C-256."; + case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256: + return "Simple Pairing Randomizer R-256."; + case BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID: + return "Service Data - 32-bit UUID."; + case BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID: + return "Service Data - 128-bit UUID."; + case BLE_GAP_AD_TYPE_3D_INFORMATION_DATA: + return "3D Information Data."; + case BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA: + return "Manufacturer Specific Data."; + default: + return "Unknown AdRecord Structure: " + type; + } + } +} diff --git a/app/src/main/java/com/qidian/baseble/model/adrecord/AdRecordStore.java b/app/src/main/java/com/qidian/baseble/model/adrecord/AdRecordStore.java new file mode 100644 index 0000000..70e2505 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/model/adrecord/AdRecordStore.java @@ -0,0 +1,156 @@ +package com.qidian.baseble.model.adrecord; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +import com.qidian.baseble.utils.AdRecordUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * @Description: 广播包解析仓库 + * @author: DAWI + * @date: 16/8/7 21:54. + */ +public class AdRecordStore implements Parcelable { + + public static final Creator CREATOR = new Creator() { + public AdRecordStore createFromParcel(final Parcel in) { + return new AdRecordStore(in); + } + + public AdRecordStore[] newArray(final int size) { + return new AdRecordStore[size]; + } + }; + private static final String RECORDS_ARRAY = "records_array"; + private static final String LOCAL_NAME_COMPLETE = "local_name_complete"; + private static final String LOCAL_NAME_SHORT = "local_name_short"; + private final SparseArray mAdRecords; + private final String mLocalNameComplete; + private final String mLocalNameShort; + + public AdRecordStore(final Parcel in) { + final Bundle b = in.readBundle(getClass().getClassLoader()); + mAdRecords = b.getSparseParcelableArray(RECORDS_ARRAY); + mLocalNameComplete = b.getString(LOCAL_NAME_COMPLETE); + mLocalNameShort = b.getString(LOCAL_NAME_SHORT); + } + + /** + * Instantiates a new Bluetooth LE device Ad Record Store. + * + * @param adRecords the ad records + */ + public AdRecordStore(final SparseArray adRecords) { + mAdRecords = adRecords; + mLocalNameComplete = AdRecordUtil.getRecordDataAsString(mAdRecords.get(AdRecord.BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME)); + mLocalNameShort = AdRecordUtil.getRecordDataAsString(mAdRecords.get(AdRecord.BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME)); + + } + + /* (non-Javadoc) + * @see android.os.Parcelable#describeContents() + */ + @Override + public int describeContents() { + return 0; + } + + /** + * Gets the short local device name. + * + * @return the local name complete + */ + public String getLocalNameComplete() { + return mLocalNameComplete; + } + + /** + * Gets the complete local device name. + * + * @return the local name short + */ + public String getLocalNameShort() { + return mLocalNameShort; + } + + /** + * retrieves an individual record. + * + * @param record the record + * @return the record + */ + public AdRecord getRecord(final int record) { + return mAdRecords.get(record); + } + + /** + * Gets the record data as string. + * + * @param record the record + * @return the record data as string + */ + public String getRecordDataAsString(final int record) { + return AdRecordUtil.getRecordDataAsString(mAdRecords.get(record)); + } + + /** + * Gets the record as collection. + * + * @return the records as collection + */ + public Collection getRecordsAsCollection() { + return Collections.unmodifiableCollection(asList(mAdRecords)); + } + + /** + * Checks if is record present. + * + * @param record the record + * @return true, if is record present + */ + public boolean isRecordPresent(final int record) { + return mAdRecords.indexOfKey(record) >= 0; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "AdRecordStore [mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]"; + } + + /* (non-Javadoc) + * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) + */ + @Override + public void writeToParcel(final Parcel parcel, final int arg1) { + final Bundle b = new Bundle(); + b.putString(LOCAL_NAME_COMPLETE, mLocalNameComplete); + b.putString(LOCAL_NAME_SHORT, mLocalNameShort); + b.putSparseParcelableArray(RECORDS_ARRAY, mAdRecords); + parcel.writeBundle(b); + } + + /** + * As list. + * + * @param the generic type + * @param sparseArray the sparse array + * @return the collection + */ + public static Collection asList(final SparseArray sparseArray) { + if (sparseArray == null) return null; + final Collection arrayList = new ArrayList<>(sparseArray.size()); + for (int i = 0; i < sparseArray.size(); i++) { + arrayList.add(sparseArray.valueAt(i)); + } + return arrayList; + } +} diff --git a/app/src/main/java/com/qidian/baseble/model/resolver/BluetoothClassResolver.java b/app/src/main/java/com/qidian/baseble/model/resolver/BluetoothClassResolver.java new file mode 100644 index 0000000..6592fab --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/model/resolver/BluetoothClassResolver.java @@ -0,0 +1,146 @@ +package com.qidian.baseble.model.resolver; + +import android.bluetooth.BluetoothClass; + +/** + * @Description: 蓝牙设备类别 + * @author: DAWI + * @date: 16/8/7 21:48. + */ +public class BluetoothClassResolver { + public static String resolveDeviceClass(final int btClass) { + switch (btClass) { + case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: + return "A/V, Camcorder"; + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return "A/V, Car Audio"; + case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: + return "A/V, Handsfree"; + case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: + return "A/V, Headphones"; + case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: + return "A/V, HiFi Audio"; + case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: + return "A/V, Loudspeaker"; + case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE: + return "A/V, Microphone"; + case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: + return "A/V, Portable Audio"; + case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX: + return "A/V, Set Top Box"; + case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED: + return "A/V, Uncategorized"; + case BluetoothClass.Device.AUDIO_VIDEO_VCR: + return "A/V, VCR"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA: + return "A/V, Video Camera"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING: + return "A/V, Video Conferencing"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: + return "A/V, Video Display and Loudspeaker"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY: + return "A/V, Video Gaming Toy"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR: + return "A/V, Video Monitor"; + case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: + return "A/V, Video Wearable Headset"; + case BluetoothClass.Device.COMPUTER_DESKTOP: + return "Computer, Desktop"; + case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA: + return "Computer, Handheld PC/PDA"; + case BluetoothClass.Device.COMPUTER_LAPTOP: + return "Computer, Laptop"; + case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA: + return "Computer, Palm Size PC/PDA"; + case BluetoothClass.Device.COMPUTER_SERVER: + return "Computer, Server"; + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + return "Computer, Uncategorized"; + case BluetoothClass.Device.COMPUTER_WEARABLE: + return "Computer, Wearable"; + case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE: + return "Health, Blood Pressure"; + case BluetoothClass.Device.HEALTH_DATA_DISPLAY: + return "Health, Data Display"; + case BluetoothClass.Device.HEALTH_GLUCOSE: + return "Health, Glucose"; + case BluetoothClass.Device.HEALTH_PULSE_OXIMETER: + return "Health, Pulse Oximeter"; + case BluetoothClass.Device.HEALTH_PULSE_RATE: + return "Health, Pulse Rate"; + case BluetoothClass.Device.HEALTH_THERMOMETER: + return "Health, Thermometer"; + case BluetoothClass.Device.HEALTH_UNCATEGORIZED: + return "Health, Uncategorized"; + case BluetoothClass.Device.HEALTH_WEIGHING: + return "Health, Weighting"; + case BluetoothClass.Device.PHONE_CELLULAR: + return "Phone, Cellular"; + case BluetoothClass.Device.PHONE_CORDLESS: + return "Phone, Cordless"; + case BluetoothClass.Device.PHONE_ISDN: + return "Phone, ISDN"; + case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY: + return "Phone, Modem or Gateway"; + case BluetoothClass.Device.PHONE_SMART: + return "Phone, Smart"; + case BluetoothClass.Device.PHONE_UNCATEGORIZED: + return "Phone, Uncategorized"; + case BluetoothClass.Device.TOY_CONTROLLER: + return "Toy, Controller"; + case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE: + return "Toy, Doll/Action Figure"; + case BluetoothClass.Device.TOY_GAME: + return "Toy, Game"; + case BluetoothClass.Device.TOY_ROBOT: + return "Toy, Robot"; + case BluetoothClass.Device.TOY_UNCATEGORIZED: + return "Toy, Uncategorized"; + case BluetoothClass.Device.TOY_VEHICLE: + return "Toy, Vehicle"; + case BluetoothClass.Device.WEARABLE_GLASSES: + return "Wearable, Glasses"; + case BluetoothClass.Device.WEARABLE_HELMET: + return "Wearable, Helmet"; + case BluetoothClass.Device.WEARABLE_JACKET: + return "Wearable, Jacket"; + case BluetoothClass.Device.WEARABLE_PAGER: + return "Wearable, Pager"; + case BluetoothClass.Device.WEARABLE_UNCATEGORIZED: + return "Wearable, Uncategorized"; + case BluetoothClass.Device.WEARABLE_WRIST_WATCH: + return "Wearable, Wrist Watch"; + default: + return "Unknown, Unknown (class=" + btClass + ")"; + } + } + + public static String resolveMajorDeviceClass(final int majorBtClass) { + switch (majorBtClass) { + case BluetoothClass.Device.Major.AUDIO_VIDEO: + return "Audio/ Video"; + case BluetoothClass.Device.Major.COMPUTER: + return "Computer"; + case BluetoothClass.Device.Major.HEALTH: + return "Health"; + case BluetoothClass.Device.Major.IMAGING: + return "Imaging"; + case BluetoothClass.Device.Major.MISC: + return "Misc"; + case BluetoothClass.Device.Major.NETWORKING: + return "Networking"; + case BluetoothClass.Device.Major.PERIPHERAL: + return "Peripheral"; + case BluetoothClass.Device.Major.PHONE: + return "Phone"; + case BluetoothClass.Device.Major.TOY: + return "Toy"; + case BluetoothClass.Device.Major.UNCATEGORIZED: + return "Uncategorized"; + case BluetoothClass.Device.Major.WEARABLE: + return "Wearable"; + default: + return "Unknown (" + majorBtClass + ")"; + } + } +} diff --git a/app/src/main/java/com/qidian/baseble/model/resolver/CompanyIdentifierResolver.java b/app/src/main/java/com/qidian/baseble/model/resolver/CompanyIdentifierResolver.java new file mode 100644 index 0000000..10d2649 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/model/resolver/CompanyIdentifierResolver.java @@ -0,0 +1,650 @@ +package com.qidian.baseble.model.resolver; + +import android.util.SparseArray; + +/** + * @Description: 公司标识符 + * 参考:https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers + * @author: DAWI + * @date: 16/8/7 21:49. + */ +public class CompanyIdentifierResolver { + public static final int ERICSSON_TECHNOLOGY_LICENSING = 0x0000; + public static final int NOKIA_MOBILE_PHONES = 0x0001; + public static final int INTEL_CORP = 0x0002; + public static final int IBM_CORP = 0x0003; + public static final int TOSHIBA_CORP = 0x0004; + public static final int THREE_COM = 0x0005; + public static final int MICROSOFT = 0x0006; + public static final int LUCENT = 0x0007; + public static final int MOTOROLA = 0x0008; + public static final int INFINEON_TECHNOLOGIES_AG = 0x0009; + public static final int CAMBRIDGE_SILICON_RADIO = 0x000A; + public static final int SILICON_WAVE = 0x000B; + public static final int DIGIANSWER_A_S = 0x000C; + public static final int TEXAS_INSTRUMENTS_INC = 0x000D; + public static final int CEVA_INC_FORMERLY_PARTHUS_TECHNOLOGIES_INC = 0x000E; + public static final int BROADCOM_CORPORATION = 0x000F; + public static final int MITEL_SEMICONDUCTOR = 0x0010; + public static final int WIDCOMM_INC = 0x0011; + public static final int ZEEVO_INC = 0x0012; + public static final int ATMEL_CORPORATION = 0x0013; + public static final int MITSUBISHI_ELECTRIC_CORPORATION = 0x0014; + public static final int RTX_TELECOM_A_S = 0x0015; + public static final int KC_TECHNOLOGY_INC = 0x0016; + public static final int NEWLOGIC = 0x0017; + public static final int TRANSILICA_INC = 0x0018; + public static final int ROHDE_SCHWARZ_GMBH_CO_KG = 0x0019; + public static final int TTPCOM_LIMITED = 0x001A; + public static final int SIGNIA_TECHNOLOGIES_INC = 0x001B; + public static final int CONEXANT_SYSTEMS_INC = 0x001C; + public static final int QUALCOMM = 0x001D; + public static final int INVENTEL = 0x001E; + public static final int AVM_BERLIN = 0x001F; + public static final int BANDSPEED_INC = 0x0020; + public static final int MANSELLA_LTD = 0x0021; + public static final int NEC_CORPORATION = 0x0022; + public static final int WAVEPLUS_TECHNOLOGY_CO_LTD = 0x0023; + public static final int ALCATEL = 0x0024; + public static final int PHILIPS_SEMICONDUCTORS = 0x0025; + public static final int C_TECHNOLOGIES = 0x0026; + public static final int OPEN_INTERFACE = 0x0027; + public static final int R_F_MICRO_DEVICES = 0x0028; + public static final int HITACHI_LTD = 0x0029; + public static final int SYMBOL_TECHNOLOGIES_INC = 0x002A; + public static final int TENOVIS = 0x002B; + public static final int MACRONIX_INTERNATIONAL_CO_LTD = 0x002C; + public static final int GCT_SEMICONDUCTOR = 0x002D; + public static final int NORWOOD_SYSTEMS = 0x002E; + public static final int MEWTEL_TECHNOLOGY_INC = 0x002F; + public static final int ST_MICROELECTRONICS = 0x0030; + public static final int SYNOPSIS = 0x0031; + public static final int REDM_COMMUNICATIONS_LTD = 0x0032; + public static final int COMMIL_LTD = 0x0033; + public static final int COMPUTER_ACCESS_TECHNOLOGY_CORPORATION_CATC = 0x0034; + public static final int ECLIPSE_HQ_ESPANA_SL = 0x0035; + public static final int RENESAS_TECHNOLOGY_CORP = 0x0036; + public static final int MOBILIAN_CORPORATION = 0x0037; + public static final int TERAX = 0x0038; + public static final int INTEGRATED_SYSTEM_SOLUTION_CORP = 0x0039; + public static final int MATSUSHITA_ELECTRIC_INDUSTRIAL_CO_LTD = 0x003A; + public static final int GENNUM_CORPORATION = 0x003B; + public static final int RESEARCH_IN_MOTION = 0x003C; + public static final int IPEXTREME_INC = 0x003D; + public static final int SYSTEMS_AND_CHIPS_INC = 0x003E; + public static final int BLUETOOTH_SIG_INC = 0x003F; + public static final int SEIKO_EPSON_CORPORATION = 0x0040; + public static final int INTEGRATED_SILICON_SOLUTION_TAIWAN_INC = 0x0041; + public static final int CONWISE_TECHNOLOGY_CORPORATION_LTD = 0x0042; + public static final int PARROT_SA = 0x0043; + public static final int SOCKET_MOBILE = 0x0044; + public static final int ATHEROS_COMMUNICATIONS_INC = 0x0045; + public static final int MEDIATEK_INC = 0x0046; + public static final int BLUEGIGA = 0x0047; + public static final int MARVELL_TECHNOLOGY_GROUP_LTD = 0x0048; + public static final int THREE_DSP_CORPORATION = 0x0049; + public static final int ACCEL_SEMICONDUCTOR_LTD = 0x004A; + public static final int CONTINENTAL_AUTOMOTIVE_SYSTEMS = 0x004B; + public static final int APPLE_INC = 0x004C; + public static final int STACCATO_COMMUNICATIONS_INC = 0x004D; + public static final int AVAGO_TECHNOLOGIES = 0x004E; + public static final int APT_LICENSING_LTD = 0x004F; + public static final int SIRF_TECHNOLOGY = 0x0050; + public static final int TZERO_TECHNOLOGIES_INC = 0x0051; + public static final int JM_CORPORATION = 0x0052; + public static final int FREE2MOVE_AB = 0x0053; + public static final int THREE_DIJOY_CORPORATION = 0x0054; + public static final int PLANTRONICS_INC = 0x0055; + public static final int SONY_ERICSSON_MOBILE_COMMUNICATIONS = 0x0056; + public static final int HARMAN_INTERNATIONAL_INDUSTRIES_INC = 0x0057; + public static final int VIZIO_INC = 0x0058; + public static final int NORDIC_SEMICONDUCTOR_ASA = 0x0059; + public static final int EM_MICROELECTRONICMARIN_SA = 0x005A; + public static final int RALINK_TECHNOLOGY_CORPORATION = 0x005B; + public static final int BELKIN_INTERNATIONAL_INC = 0x005C; + public static final int REALTEK_SEMICONDUCTOR_CORPORATION = 0x005D; + public static final int STONESTREET_ONE_LLC = 0x005E; + public static final int WICENTRIC_INC = 0x005F; + public static final int RIVIERAWAVES_SAS = 0x0060; + public static final int RDA_MICROELECTRONICS = 0x0061; + public static final int GIBSON_GUITARS = 0x0062; + public static final int MICOMMAND_INC = 0x0063; + public static final int BAND_XI_INTERNATIONAL_LLC = 0x0064; + public static final int HEWLETTPACKARD_COMPANY = 0x0065; + public static final int NINE_SOLUTIONS_OY = 0x0066; + public static final int GN_NETCOM_A_S = 0x0067; + public static final int GENERAL_MOTORS = 0x0068; + public static final int AD_ENGINEERING_INC = 0x0069; + public static final int MINDTREE_LTD = 0x006A; + public static final int POLAR_ELECTRO_OY = 0x006B; + public static final int BEAUTIFUL_ENTERPRISE_CO_LTD = 0x006C; + public static final int BRIARTEK_INC = 0x006D; + public static final int SUMMIT_DATA_COMMUNICATIONS_INC = 0x006E; + public static final int SOUND_ID = 0x006F; + public static final int MONSTER_LLC = 0x0070; + public static final int CONNECTBLUE_AB = 0x0071; + public static final int SHANGHAI_SUPER_SMART_ELECTRONICS_CO_LTD = 0x0072; + public static final int GROUP_SENSE_LTD = 0x0073; + public static final int ZOMM_LLC = 0x0074; + public static final int SAMSUNG_ELECTRONICS_CO_LTD = 0x0075; + public static final int CREATIVE_TECHNOLOGY_LTD = 0x0076; + public static final int LAIRD_TECHNOLOGIES = 0x0077; + public static final int NIKE_INC = 0x0078; + public static final int LESSWIRE_AG = 0x0079; + public static final int MSTAR_SEMICONDUCTOR_INC = 0x007A; + public static final int HANLYNN_TECHNOLOGIES = 0x007B; + public static final int A_R_CAMBRIDGE = 0x007C; + public static final int SEERS_TECHNOLOGY_CO_LTD = 0x007D; + public static final int SPORTS_TRACKING_TECHNOLOGIES_LTD = 0x007E; + public static final int AUTONET_MOBILE = 0x007F; + public static final int DELORME_PUBLISHING_COMPANY_INC = 0x0080; + public static final int WUXI_VIMICRO = 0x0081; + public static final int SENNHEISER_COMMUNICATIONS_A_S = 0x0082; + public static final int TIMEKEEPING_SYSTEMS_INC = 0x0083; + public static final int LUDUS_HELSINKI_LTD = 0x0084; + public static final int BLUERADIOS_INC = 0x0085; + public static final int EQUINOX_AG = 0x0086; + public static final int GARMIN_INTERNATIONAL_INC = 0x0087; + public static final int ECOTEST = 0x0088; + public static final int GN_RESOUND_A_S = 0x0089; + public static final int JAWBONE = 0x008A; + public static final int TOPCORN_POSITIONING_SYSTEMS_LLC = 0x008B; + public static final int QUALCOMM_RETAIL_SOLUTIONS_INC_FORMERLY_QUALCOMM_LABS_INC = 0x008C; + public static final int ZSCAN_SOFTWARE = 0x008D; + public static final int QUINTIC_CORP = 0x008E; + public static final int STOLLMAN_EV_GMBH = 0x008F; + public static final int FUNAI_ELECTRIC_CO_LTD = 0x0090; + public static final int ADVANCED_PANMOBIL_SYSTEMS_GMBH_CO_KG = 0x0091; + public static final int THINKOPTICS_INC = 0x0092; + public static final int UNIVERSAL_ELECTRONICS_INC = 0x0093; + public static final int AIROHA_TECHNOLOGY_CORP = 0x0094; + public static final int NEC_LIGHTING_LTD = 0x0095; + public static final int ODM_TECHNOLOGY_INC = 0x0096; + public static final int CONNECTEDEVICE_LTD = 0x0097; + public static final int ZER01TV_GMBH = 0x0098; + public static final int ITECH_DYNAMIC_GLOBAL_DISTRIBUTION_LTD = 0x0099; + public static final int ALPWISE = 0x009A; + public static final int JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS_CO_LTD = 0x009B; + public static final int COLORFY_INC = 0x009C; + public static final int GEOFORCE_INC = 0x009D; + public static final int BOSE_CORPORATION = 0x009E; + public static final int SUUNTO_OY = 0x009F; + public static final int KENSINGTON_COMPUTER_PRODUCTS_GROUP = 0x00A0; + public static final int SRMEDIZINELEKTRONIK = 0x00A1; + public static final int VERTU_CORPORATION_LIMITED = 0x00A2; + public static final int META_WATCH_LTD = 0x00A3; + public static final int LINAK_A_S = 0x00A4; + public static final int OTL_DYNAMICS_LLC = 0x00A5; + public static final int PANDA_OCEAN_INC = 0x00A6; + public static final int VISTEON_CORPORATION = 0x00A7; + public static final int ARP_DEVICES_LIMITED = 0x00A8; + public static final int MAGNETI_MARELLI_SPA = 0x00A9; + public static final int CAEN_RFID_SRL = 0x00AA; + public static final int INGENIEURSYSTEMGRUPPE_ZAHN_GMBH = 0x00AB; + public static final int GREEN_THROTTLE_GAMES = 0x00AC; + public static final int PETER_SYSTEMTECHNIK_GMBH = 0x00AD; + public static final int OMEGAWAVE_OY = 0x00AE; + public static final int CINETIX = 0x00AF; + public static final int PASSIF_SEMICONDUCTOR_CORP = 0x00B0; + public static final int SARIS_CYCLING_GROUP_INC = 0x00B1; + public static final int BEKEY_A_S = 0x00B2; + public static final int CLARINOX_TECHNOLOGIES_PTY_LTD = 0x00B3; + public static final int BDE_TECHNOLOGY_CO_LTD = 0x00B4; + public static final int SWIRL_NETWORKS = 0x00B5; + public static final int MESO_INTERNATIONAL = 0x00B6; + public static final int TRELAB_LTD = 0x00B7; + public static final int QUALCOMM_INNOVATION_CENTER_INC_QUIC = 0x00B8; + public static final int JOHNSON_CONTROLS_INC = 0x00B9; + public static final int STARKEY_LABORATORIES_INC = 0x00BA; + public static final int SPOWER_ELECTRONICS_LIMITED = 0x00BB; + public static final int ACE_SENSOR_INC = 0x00BC; + public static final int APLIX_CORPORATION = 0x00BD; + public static final int AAMP_OF_AMERICA = 0x00BE; + public static final int STALMART_TECHNOLOGY_LIMITED = 0x00BF; + public static final int AMICCOM_ELECTRONICS_CORPORATION = 0x00C0; + public static final int SHENZHEN_EXCELSECU_DATA_TECHNOLOGY_COLTD = 0x00C1; + public static final int GENEQ_INC = 0x00C2; + public static final int ADIDAS_AG = 0x00C3; + public static final int LG_ELECTRONICS = 0x00C4; + public static final int ONSET_COMPUTER_CORPORATION = 0x00C5; + public static final int SELFLY_BV = 0x00C6; + public static final int QUUPPA_OY = 0x00C7; + public static final int GELO_INC = 0x00C8; + public static final int EVLUMA = 0x00C9; + public static final int MC10 = 0x00CA; + public static final int BINAURIC_SE = 0x00CB; + public static final int BEATS_ELECTRONICS = 0x00CC; + public static final int MICROCHIP_TECHNOLOGY_INC = 0x00CD; + public static final int ELGATO_SYSTEMS_GMBH = 0x00CE; + public static final int ARCHOS_SA = 0x00CF; + public static final int DEXCOM_INC = 0x00D0; + public static final int POLAR_ELECTRO_EUROPE_BV = 0x00D1; + public static final int DIALOG_SEMICONDUCTOR_BV = 0x00D2; + public static final int TAIXINGBANG_TECHNOLOGY_HK_CO_LTD = 0x00D3; + public static final int KAWANTECH = 0x00D4; + public static final int AUSTCO_COMMUNICATION_SYSTEMS = 0x00D5; + public static final int TIMEX_GROUP_USA_INC = 0x00D6; + public static final int QUALCOMM_TECHNOLOGIES_INC = 0x00D7; + public static final int QUALCOMM_CONNECTED_EXPERIENCES_INC = 0x00D8; + public static final int VOYETRA_TURTLE_BEACH = 0x00D9; + public static final int TXTR_GMBH = 0x00DA; + public static final int BIOSENTRONICS = 0x00DB; + public static final int PROCTER_GAMBLE = 0x00DC; + public static final int HOSIDEN_CORPORATION = 0x00DD; + public static final int MUZIK_LLC = 0x00DE; + public static final int MISFIT_WEARABLES_CORP = 0x00DF; + public static final int GOOGLE = 0x00E0; + public static final int DANLERS_LTD = 0x00E1; + public static final int SEMILINK_INC = 0x00E2; + public static final int INMUSIC_BRANDS_INC = 0x00E3; + public static final int LS_RESEARCH_INC = 0x00E4; + public static final int EDEN_SOFTWARE_CONSULTANTS_LTD = 0x00E5; + public static final int FRESHTEMP = 0x00E6; + public static final int KS_TECHNOLOGIES = 0x00E7; + public static final int ACTS_TECHNOLOGIES = 0x00E8; + public static final int VTRACK_SYSTEMS = 0x00E9; + public static final int NIELSENKELLERMAN_COMPANY = 0x00EA; + public static final int SERVER_TECHNOLOGY_INC = 0x00EB; + public static final int BIORESEARCH_ASSOCIATES = 0x00EC; + public static final int JOLLY_LOGIC_LLC = 0x00ED; + public static final int ABOVE_AVERAGE_OUTCOMES_INC = 0x00EE; + public static final int BITSPLITTERS_GMBH = 0x00EF; + public static final int PAYPAL_INC = 0x00F0; + public static final int WITRON_TECHNOLOGY_LIMITED = 0x00F1; + public static final int MORSE_PROJECT_INC = 0x00F2; + public static final int KENT_DISPLAYS_INC = 0x00F3; + public static final int NAUTILUS_INC = 0x00F4; + public static final int SMARTIFIER_OY = 0x00F5; + public static final int ELCOMETER_LIMITED = 0x00F6; + public static final int VSN_TECHNOLOGIES_INC = 0x00F7; + public static final int ACEUNI_CORP_LTD = 0x00F8; + public static final int STICKNFIND = 0x00F9; + public static final int CRYSTAL_CODE_AB = 0x00FA; + public static final int KOUKAAM_AS = 0x00FB; + public static final int DELPHI_CORPORATION = 0x00FC; + public static final int VALENCETECH_LIMITED = 0x00FD; + public static final int RESERVED = 0x00FE; + public static final int TYPO_PRODUCTS_LLC = 0x00FF; + public static final int TOMTOM_INTERNATIONAL_BV = 0x0100; + public static final int FUGOO_INC = 0x0101; + public static final int KEISER_CORPORATION = 0x0102; + public static final int BANG_OLUFSEN_A_S = 0x0103; + public static final int PLUS_LOCATIONS_SYSTEMS_PTY_LTD = 0x0104; + public static final int UBIQUITOUS_COMPUTING_TECHNOLOGY_CORPORATION = 0x0105; + public static final int INNOVATIVE_YACHTTER_SOLUTIONS = 0x0106; + public static final int WILLIAM_DEMANT_HOLDING_A_S = 0x0107; + public static final int CHICONY_ELECTRONICS_CO_LTD = 0x0108; + public static final int ATUS_BV = 0x0109; + public static final int CODEGATE_LTD = 0x010A; + public static final int ERI_INC = 0x010B; + public static final int TRANSDUCERS_DIRECT_LLC = 0x010C; + public static final int FUJITSU_TEN_LIMITED = 0x010D; + public static final int AUDI_AG = 0x010E; + public static final int HISILICON_TECHNOLOGIES_CO_LTD = 0x010F; + public static final int NIPPON_SEIKI_CO_LTD = 0x0110; + public static final int STEELSERIES_APS = 0x0111; + public static final int VYZYBL_INC = 0x0112; + public static final int OPENBRAIN_TECHNOLOGIES_CO_LTD = 0x0113; + public static final int XENSR = 0x0114; + public static final int ESOLUTIONS = 0x0115; + public static final int ONE_OAK_TECHNOLOGIES = 0x0116; + public static final int WIMOTO_TECHNOLOGIES_INC = 0x0117; + public static final int RADIUS_NETWORKS_INC = 0x0118; + public static final int WIZE_TECHNOLOGY_CO_LTD = 0x0119; + public static final int QUALCOMM_LABS_INC = 0x011A; + public static final int ARUBA_NETWORKS = 0x011B; + public static final int BAIDU = 0x011C; + public static final int ARENDI_AG = 0x011D; + public static final int SKODA_AUTO_AS = 0x011E; + public static final int VOLKSWAGON_AG = 0x011F; + public static final int PORSCHE_AG = 0x0120; + public static final int SINO_WEALTH_ELECTRONIC_LTD = 0x0121; + public static final int AIRTURN_INC = 0x0122; + public static final int KINSA_INC = 0x0123; + public static final int HID_GLOBAL = 0x0124; + public static final int SEAT_ES = 0x0125; + public static final int PROMETHEAN_LTD = 0x0126; + public static final int SALUTICA_ALLIED_SOLUTIONS = 0x0127; + public static final int GPSI_GROUP_PTY_LTD = 0x0128; + public static final int NIMBLE_DEVICES_OY = 0x0129; + public static final int CHANGZHOU_YONGSE_INFOTECH_CO_LTD = 0x012A; + public static final int SPORTIQ = 0x012B; + public static final int TEMEC_INSTRUMENTS_BV = 0x012C; + public static final int SONY_CORPORATION = 0x012D; + public static final int ASSA_ABLOY = 0x012E; + public static final int CLARION_CO_LTD = 0x012F; + public static final int WAREHOUSE_INNOVATIONS = 0x0130; + public static final int CYPRESS_SEMICONDUCTOR_CORPORATION = 0x0131; + public static final int MADS_INC = 0x0132; + public static final int BLUE_MAESTRO_LIMITED = 0x0133; + public static final int RESOLUTION_PRODUCTS_INC = 0x0134; + public static final int AIREWEAR_LLC = 0x0135; + public static final int ETC_SP_ZOO = 0x0136; + public static final int PRESTIGIO_PLAZA_LTD = 0x0137; + + private static final SparseArray COMPANY_NAME_MAP = populateCompanyNameMap(); + + public static String getCompanyName(final int companyId, final String fallback) { + final String name = COMPANY_NAME_MAP.get(companyId); + return name == null ? fallback : name; + } + + private static SparseArray populateCompanyNameMap() { + final SparseArray map = new SparseArray<>(); + + map.put(ERICSSON_TECHNOLOGY_LICENSING, "Ericsson Technology Licensing"); + map.put(NOKIA_MOBILE_PHONES, "Nokia Mobile Phones"); + map.put(INTEL_CORP, "Intel Corp."); + map.put(IBM_CORP, "IBM Corp."); + map.put(TOSHIBA_CORP, "Toshiba Corp."); + map.put(THREE_COM, "3Com"); + map.put(MICROSOFT, "Microsoft"); + map.put(LUCENT, "Lucent"); + map.put(MOTOROLA, "Motorola"); + map.put(INFINEON_TECHNOLOGIES_AG, "Infineon Technologies AG"); + map.put(CAMBRIDGE_SILICON_RADIO, "Cambridge Silicon Radio"); + map.put(SILICON_WAVE, "Silicon Wave"); + map.put(DIGIANSWER_A_S, "Digianswer A/S"); + map.put(TEXAS_INSTRUMENTS_INC, "Texas Instruments Inc."); + map.put(CEVA_INC_FORMERLY_PARTHUS_TECHNOLOGIES_INC, "Ceva, Inc. (formerly Parthus Technologies, Inc.)"); + map.put(BROADCOM_CORPORATION, "Broadcom Corporation"); + map.put(MITEL_SEMICONDUCTOR, "Mitel Semiconductor"); + map.put(WIDCOMM_INC, "Widcomm, Inc"); + map.put(ZEEVO_INC, "Zeevo, Inc."); + map.put(ATMEL_CORPORATION, "Atmel Corporation"); + map.put(MITSUBISHI_ELECTRIC_CORPORATION, "Mitsubishi Electric Corporation"); + map.put(RTX_TELECOM_A_S, "RTX Telecom A/S"); + map.put(KC_TECHNOLOGY_INC, "KC Technology Inc."); + map.put(NEWLOGIC, "NewLogic"); + map.put(TRANSILICA_INC, "Transilica, Inc."); + map.put(ROHDE_SCHWARZ_GMBH_CO_KG, "Rohde & Schwarz GmbH & Co. KG"); + map.put(TTPCOM_LIMITED, "TTPCom Limited"); + map.put(SIGNIA_TECHNOLOGIES_INC, "Signia Technologies, Inc."); + map.put(CONEXANT_SYSTEMS_INC, "Conexant Systems Inc."); + map.put(QUALCOMM, "Qualcomm"); + map.put(INVENTEL, "Inventel"); + map.put(AVM_BERLIN, "AVM Berlin"); + map.put(BANDSPEED_INC, "BandSpeed, Inc."); + map.put(MANSELLA_LTD, "Mansella Ltd"); + map.put(NEC_CORPORATION, "NEC Corporation"); + map.put(WAVEPLUS_TECHNOLOGY_CO_LTD, "WavePlus Technology Co., Ltd."); + map.put(ALCATEL, "Alcatel"); + map.put(PHILIPS_SEMICONDUCTORS, "Philips Semiconductors"); + map.put(C_TECHNOLOGIES, "C Technologies"); + map.put(OPEN_INTERFACE, "Open Interface"); + map.put(R_F_MICRO_DEVICES, "R F Micro Devices"); + map.put(HITACHI_LTD, "Hitachi Ltd"); + map.put(SYMBOL_TECHNOLOGIES_INC, "Symbol Technologies, Inc."); + map.put(TENOVIS, "Tenovis"); + map.put(MACRONIX_INTERNATIONAL_CO_LTD, "Macronix International Co. Ltd."); + map.put(GCT_SEMICONDUCTOR, "GCT Semiconductor"); + map.put(NORWOOD_SYSTEMS, "Norwood Systems"); + map.put(MEWTEL_TECHNOLOGY_INC, "MewTel Technology Inc."); + map.put(ST_MICROELECTRONICS, "ST Microelectronics"); + map.put(SYNOPSIS, "Synopsis"); + map.put(REDM_COMMUNICATIONS_LTD, "Red-M (Communications) Ltd"); + map.put(COMMIL_LTD, "Commil Ltd"); + map.put(COMPUTER_ACCESS_TECHNOLOGY_CORPORATION_CATC, "Computer Access Technology Corporation (CATC)"); + map.put(ECLIPSE_HQ_ESPANA_SL, "Eclipse (HQ Espana) S.L."); + map.put(RENESAS_TECHNOLOGY_CORP, "Renesas Technology Corp."); + map.put(MOBILIAN_CORPORATION, "Mobilian Corporation"); + map.put(TERAX, "Terax"); + map.put(INTEGRATED_SYSTEM_SOLUTION_CORP, "Integrated System Solution Corp."); + map.put(MATSUSHITA_ELECTRIC_INDUSTRIAL_CO_LTD, "Matsushita Electric Industrial Co., Ltd."); + map.put(GENNUM_CORPORATION, "Gennum Corporation"); + map.put(RESEARCH_IN_MOTION, "Research In Motion"); + map.put(IPEXTREME_INC, "IPextreme, Inc."); + map.put(SYSTEMS_AND_CHIPS_INC, "Systems and Chips, Inc."); + map.put(BLUETOOTH_SIG_INC, "Bluetooth SIG, Inc."); + map.put(SEIKO_EPSON_CORPORATION, "Seiko Epson Corporation"); + map.put(INTEGRATED_SILICON_SOLUTION_TAIWAN_INC, "Integrated Silicon Solution Taiwan, Inc."); + map.put(CONWISE_TECHNOLOGY_CORPORATION_LTD, "CONWISE Technology Corporation Ltd"); + map.put(PARROT_SA, "PARROT SA"); + map.put(SOCKET_MOBILE, "Socket Mobile"); + map.put(ATHEROS_COMMUNICATIONS_INC, "Atheros Communications, Inc."); + map.put(MEDIATEK_INC, "MediaTek, Inc."); + map.put(BLUEGIGA, "Bluegiga"); + map.put(MARVELL_TECHNOLOGY_GROUP_LTD, "Marvell Technology Group Ltd."); + map.put(THREE_DSP_CORPORATION, "3DSP Corporation"); + map.put(ACCEL_SEMICONDUCTOR_LTD, "Accel Semiconductor Ltd."); + map.put(CONTINENTAL_AUTOMOTIVE_SYSTEMS, "Continental Automotive Systems"); + map.put(APPLE_INC, "Apple, Inc."); + map.put(STACCATO_COMMUNICATIONS_INC, "Staccato Communications, Inc."); + map.put(AVAGO_TECHNOLOGIES, "Avago Technologies"); + map.put(APT_LICENSING_LTD, "APT Licensing Ltd."); + map.put(SIRF_TECHNOLOGY, "SiRF Technology"); + map.put(TZERO_TECHNOLOGIES_INC, "Tzero Technologies, Inc."); + map.put(JM_CORPORATION, "J&M Corporation"); + map.put(FREE2MOVE_AB, "Free2move AB"); + map.put(THREE_DIJOY_CORPORATION, "3DiJoy Corporation"); + map.put(PLANTRONICS_INC, "Plantronics, Inc."); + map.put(SONY_ERICSSON_MOBILE_COMMUNICATIONS, "Sony Ericsson Mobile Communications"); + map.put(HARMAN_INTERNATIONAL_INDUSTRIES_INC, "Harman International Industries, Inc."); + map.put(VIZIO_INC, "Vizio, Inc."); + map.put(NORDIC_SEMICONDUCTOR_ASA, "Nordic Semiconductor ASA"); + map.put(EM_MICROELECTRONICMARIN_SA, "EM Microelectronic-Marin SA"); + map.put(RALINK_TECHNOLOGY_CORPORATION, "Ralink Technology Corporation"); + map.put(BELKIN_INTERNATIONAL_INC, "Belkin International, Inc."); + map.put(REALTEK_SEMICONDUCTOR_CORPORATION, "Realtek Semiconductor Corporation"); + map.put(STONESTREET_ONE_LLC, "Stonestreet One, LLC"); + map.put(WICENTRIC_INC, "Wicentric, Inc."); + map.put(RIVIERAWAVES_SAS, "RivieraWaves S.A.S"); + map.put(RDA_MICROELECTRONICS, "RDA Microelectronics"); + map.put(GIBSON_GUITARS, "Gibson Guitars"); + map.put(MICOMMAND_INC, "MiCommand Inc."); + map.put(BAND_XI_INTERNATIONAL_LLC, "Band XI International, LLC"); + map.put(HEWLETTPACKARD_COMPANY, "Hewlett-Packard Company"); + map.put(NINE_SOLUTIONS_OY, "9Solutions Oy"); + map.put(GN_NETCOM_A_S, "GN Netcom A/S"); + map.put(GENERAL_MOTORS, "General Motors"); + map.put(AD_ENGINEERING_INC, "A&D Engineering, Inc."); + map.put(MINDTREE_LTD, "MindTree Ltd."); + map.put(POLAR_ELECTRO_OY, "Polar Electro OY"); + map.put(BEAUTIFUL_ENTERPRISE_CO_LTD, "Beautiful Enterprise Co., Ltd."); + map.put(BRIARTEK_INC, "BriarTek, Inc."); + map.put(SUMMIT_DATA_COMMUNICATIONS_INC, "Summit Data Communications, Inc."); + map.put(SOUND_ID, "Sound ID"); + map.put(MONSTER_LLC, "Monster, LLC"); + map.put(CONNECTBLUE_AB, "connectBlue AB"); + map.put(SHANGHAI_SUPER_SMART_ELECTRONICS_CO_LTD, "ShangHai Super Smart Electronics Co. Ltd."); + map.put(GROUP_SENSE_LTD, "Group Sense Ltd."); + map.put(ZOMM_LLC, "Zomm, LLC"); + map.put(SAMSUNG_ELECTRONICS_CO_LTD, "Samsung Electronics Co. Ltd."); + map.put(CREATIVE_TECHNOLOGY_LTD, "Creative Technology Ltd."); + map.put(LAIRD_TECHNOLOGIES, "Laird Technologies"); + map.put(NIKE_INC, "Nike, Inc."); + map.put(LESSWIRE_AG, "lesswire AG"); + map.put(MSTAR_SEMICONDUCTOR_INC, "MStar Semiconductor, Inc."); + map.put(HANLYNN_TECHNOLOGIES, "Hanlynn Technologies"); + map.put(A_R_CAMBRIDGE, "A & R Cambridge"); + map.put(SEERS_TECHNOLOGY_CO_LTD, "Seers Technology Co. Ltd"); + map.put(SPORTS_TRACKING_TECHNOLOGIES_LTD, "Sports Tracking Technologies Ltd."); + map.put(AUTONET_MOBILE, "Autonet Mobile"); + map.put(DELORME_PUBLISHING_COMPANY_INC, "DeLorme Publishing Company, Inc."); + map.put(WUXI_VIMICRO, "WuXi Vimicro"); + map.put(SENNHEISER_COMMUNICATIONS_A_S, "Sennheiser Communications A/S"); + map.put(TIMEKEEPING_SYSTEMS_INC, "TimeKeeping Systems, Inc."); + map.put(LUDUS_HELSINKI_LTD, "Ludus Helsinki Ltd."); + map.put(BLUERADIOS_INC, "BlueRadios, Inc."); + map.put(EQUINOX_AG, "equinox AG"); + map.put(GARMIN_INTERNATIONAL_INC, "Garmin International, Inc."); + map.put(ECOTEST, "Ecotest"); + map.put(GN_RESOUND_A_S, "GN ReSound A/S"); + map.put(JAWBONE, "Jawbone"); + map.put(TOPCORN_POSITIONING_SYSTEMS_LLC, "Topcorn Positioning Systems, LLC"); + map.put(QUALCOMM_RETAIL_SOLUTIONS_INC_FORMERLY_QUALCOMM_LABS_INC, "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, Inc.)"); + map.put(ZSCAN_SOFTWARE, "Zscan Software"); + map.put(QUINTIC_CORP, "Quintic Corp."); + map.put(STOLLMAN_EV_GMBH, "Stollman E+V GmbH"); + map.put(FUNAI_ELECTRIC_CO_LTD, "Funai Electric Co., Ltd."); + map.put(ADVANCED_PANMOBIL_SYSTEMS_GMBH_CO_KG, "Advanced PANMOBIL Systems GmbH & Co. KG"); + map.put(THINKOPTICS_INC, "ThinkOptics, Inc."); + map.put(UNIVERSAL_ELECTRONICS_INC, "Universal Electronics, Inc."); + map.put(AIROHA_TECHNOLOGY_CORP, "Airoha Technology Corp."); + map.put(NEC_LIGHTING_LTD, "NEC Lighting, Ltd."); + map.put(ODM_TECHNOLOGY_INC, "ODM Technology, Inc."); + map.put(CONNECTEDEVICE_LTD, "ConnecteDevice Ltd."); + map.put(ZER01TV_GMBH, "zer01.tv GmbH"); + map.put(ITECH_DYNAMIC_GLOBAL_DISTRIBUTION_LTD, "i.Tech Dynamic Global Distribution Ltd."); + map.put(ALPWISE, "Alpwise"); + map.put(JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS_CO_LTD, "Jiangsu Toppower Automotive Electronics Co., Ltd."); + map.put(COLORFY_INC, "Colorfy, Inc."); + map.put(GEOFORCE_INC, "Geoforce Inc."); + map.put(BOSE_CORPORATION, "Bose Corporation"); + map.put(SUUNTO_OY, "Suunto Oy"); + map.put(KENSINGTON_COMPUTER_PRODUCTS_GROUP, "Kensington Computer Products Group"); + map.put(SRMEDIZINELEKTRONIK, "SR-Medizinelektronik"); + map.put(VERTU_CORPORATION_LIMITED, "Vertu Corporation Limited"); + map.put(META_WATCH_LTD, "Meta Watch Ltd."); + map.put(LINAK_A_S, "LINAK A/S"); + map.put(OTL_DYNAMICS_LLC, "OTL Dynamics LLC"); + map.put(PANDA_OCEAN_INC, "Panda Ocean Inc."); + map.put(VISTEON_CORPORATION, "Visteon Corporation"); + map.put(ARP_DEVICES_LIMITED, "ARP Devices Limited"); + map.put(MAGNETI_MARELLI_SPA, "Magneti Marelli S.p.A"); + map.put(CAEN_RFID_SRL, "CAEN RFID srl"); + map.put(INGENIEURSYSTEMGRUPPE_ZAHN_GMBH, "Ingenieur-Systemgruppe Zahn GmbH"); + map.put(GREEN_THROTTLE_GAMES, "Green Throttle Games"); + map.put(PETER_SYSTEMTECHNIK_GMBH, "Peter Systemtechnik GmbH"); + map.put(OMEGAWAVE_OY, "Omegawave Oy"); + map.put(CINETIX, "Cinetix"); + map.put(PASSIF_SEMICONDUCTOR_CORP, "Passif Semiconductor Corp"); + map.put(SARIS_CYCLING_GROUP_INC, "Saris Cycling Group, Inc"); + map.put(BEKEY_A_S, "Bekey A/S"); + map.put(CLARINOX_TECHNOLOGIES_PTY_LTD, "Clarinox Technologies Pty. Ltd."); + map.put(BDE_TECHNOLOGY_CO_LTD, "BDE Technology Co., Ltd."); + map.put(SWIRL_NETWORKS, "Swirl Networks"); + map.put(MESO_INTERNATIONAL, "Meso international"); + map.put(TRELAB_LTD, "TreLab Ltd"); + map.put(QUALCOMM_INNOVATION_CENTER_INC_QUIC, "Qualcomm Innovation Center, Inc. (QuIC)"); + map.put(JOHNSON_CONTROLS_INC, "Johnson Controls, Inc."); + map.put(STARKEY_LABORATORIES_INC, "Starkey Laboratories Inc."); + map.put(SPOWER_ELECTRONICS_LIMITED, "S-Power Electronics Limited"); + map.put(ACE_SENSOR_INC, "Ace Sensor Inc"); + map.put(APLIX_CORPORATION, "Aplix Corporation"); + map.put(AAMP_OF_AMERICA, "AAMP of America"); + map.put(STALMART_TECHNOLOGY_LIMITED, "Stalmart Technology Limited"); + map.put(AMICCOM_ELECTRONICS_CORPORATION, "AMICCOM Electronics Corporation"); + map.put(SHENZHEN_EXCELSECU_DATA_TECHNOLOGY_COLTD, "Shenzhen Excelsecu Data Technology Co.,Ltd"); + map.put(GENEQ_INC, "Geneq Inc."); + map.put(ADIDAS_AG, "adidas AG"); + map.put(LG_ELECTRONICS, "LG Electronics"); + map.put(ONSET_COMPUTER_CORPORATION, "Onset Computer Corporation"); + map.put(SELFLY_BV, "Selfly BV"); + map.put(QUUPPA_OY, "Quuppa Oy."); + map.put(GELO_INC, "GeLo Inc"); + map.put(EVLUMA, "Evluma"); + map.put(MC10, "MC10"); + map.put(BINAURIC_SE, "Binauric SE"); + map.put(BEATS_ELECTRONICS, "Beats Electronics"); + map.put(MICROCHIP_TECHNOLOGY_INC, "Microchip Technology Inc."); + map.put(ELGATO_SYSTEMS_GMBH, "Elgato Systems GmbH"); + map.put(ARCHOS_SA, "ARCHOS SA"); + map.put(DEXCOM_INC, "Dexcom, Inc."); + map.put(POLAR_ELECTRO_EUROPE_BV, "Polar Electro Europe B.V."); + map.put(DIALOG_SEMICONDUCTOR_BV, "Dialog Semiconductor B.V."); + map.put(TAIXINGBANG_TECHNOLOGY_HK_CO_LTD, "Taixingbang Technology (HK) Co,. LTD."); + map.put(KAWANTECH, "Kawantech"); + map.put(AUSTCO_COMMUNICATION_SYSTEMS, "Austco Communication Systems"); + map.put(TIMEX_GROUP_USA_INC, "Timex Group USA, Inc."); + map.put(QUALCOMM_TECHNOLOGIES_INC, "Qualcomm Technologies, Inc."); + map.put(QUALCOMM_CONNECTED_EXPERIENCES_INC, "Qualcomm Connected Experiences, Inc."); + map.put(VOYETRA_TURTLE_BEACH, "Voyetra Turtle Beach"); + map.put(TXTR_GMBH, "txtr GmbH"); + map.put(BIOSENTRONICS, "Biosentronics"); + map.put(PROCTER_GAMBLE, "Procter & Gamble"); + map.put(HOSIDEN_CORPORATION, "Hosiden Corporation"); + map.put(MUZIK_LLC, "Muzik LLC"); + map.put(MISFIT_WEARABLES_CORP, "Misfit Wearables Corp"); + map.put(GOOGLE, "Google"); + map.put(DANLERS_LTD, "Danlers Ltd"); + map.put(SEMILINK_INC, "Semilink Inc"); + map.put(INMUSIC_BRANDS_INC, "inMusic Brands, Inc"); + map.put(LS_RESEARCH_INC, "L.S. Research Inc."); + map.put(EDEN_SOFTWARE_CONSULTANTS_LTD, "Eden Software Consultants Ltd."); + map.put(FRESHTEMP, "Freshtemp"); + map.put(KS_TECHNOLOGIES, "KS Technologies"); + map.put(ACTS_TECHNOLOGIES, "ACTS Technologies"); + map.put(VTRACK_SYSTEMS, "Vtrack Systems"); + map.put(NIELSENKELLERMAN_COMPANY, "Nielsen-Kellerman Company"); + map.put(SERVER_TECHNOLOGY_INC, "Server Technology, Inc."); + map.put(BIORESEARCH_ASSOCIATES, "BioResearch Associates"); + map.put(JOLLY_LOGIC_LLC, "Jolly Logic, LLC"); + map.put(ABOVE_AVERAGE_OUTCOMES_INC, "Above Average Outcomes, Inc."); + map.put(BITSPLITTERS_GMBH, "Bitsplitters GmbH"); + map.put(PAYPAL_INC, "PayPal, Inc."); + map.put(WITRON_TECHNOLOGY_LIMITED, "Witron Technology Limited"); + map.put(MORSE_PROJECT_INC, "Morse Project Inc."); + map.put(KENT_DISPLAYS_INC, "Kent Displays Inc."); + map.put(NAUTILUS_INC, "Nautilus Inc."); + map.put(SMARTIFIER_OY, "Smartifier Oy"); + map.put(ELCOMETER_LIMITED, "Elcometer Limited"); + map.put(VSN_TECHNOLOGIES_INC, "VSN Technologies Inc."); + map.put(ACEUNI_CORP_LTD, "AceUni Corp., Ltd."); + map.put(STICKNFIND, "StickNFind"); + map.put(CRYSTAL_CODE_AB, "Crystal Code AB"); + map.put(KOUKAAM_AS, "KOUKAAM a.s."); + map.put(DELPHI_CORPORATION, "Delphi Corporation"); + map.put(VALENCETECH_LIMITED, "ValenceTech Limited"); + map.put(RESERVED, "Reserved"); + map.put(TYPO_PRODUCTS_LLC, "Typo Products, LLC"); + map.put(TOMTOM_INTERNATIONAL_BV, "TomTom International BV"); + map.put(FUGOO_INC, "Fugoo, Inc"); + map.put(KEISER_CORPORATION, "Keiser Corporation"); + map.put(BANG_OLUFSEN_A_S, "Bang & Olufsen A/S"); + map.put(PLUS_LOCATIONS_SYSTEMS_PTY_LTD, "PLUS Locations Systems Pty Ltd"); + map.put(UBIQUITOUS_COMPUTING_TECHNOLOGY_CORPORATION, "Ubiquitous Computing Technology Corporation"); + map.put(INNOVATIVE_YACHTTER_SOLUTIONS, "Innovative Yachtter Solutions"); + map.put(WILLIAM_DEMANT_HOLDING_A_S, "William Demant Holding A/S"); + map.put(CHICONY_ELECTRONICS_CO_LTD, "Chicony Electronics Co., Ltd."); + map.put(ATUS_BV, "Atus BV"); + map.put(CODEGATE_LTD, "Codegate Ltd."); + map.put(ERI_INC, "ERi, Inc."); + map.put(TRANSDUCERS_DIRECT_LLC, "Transducers Direct, LLC"); + map.put(FUJITSU_TEN_LIMITED, "Fujitsu Ten Limited"); + map.put(AUDI_AG, "Audi AG"); + map.put(HISILICON_TECHNOLOGIES_CO_LTD, "HiSilicon Technologies Co., Ltd."); + map.put(NIPPON_SEIKI_CO_LTD, "Nippon Seiki Co., Ltd."); + map.put(STEELSERIES_APS, "Steelseries ApS"); + map.put(VYZYBL_INC, "vyzybl Inc."); + map.put(OPENBRAIN_TECHNOLOGIES_CO_LTD, "Openbrain Technologies, Co., Ltd."); + map.put(XENSR, "Xensr"); + map.put(ESOLUTIONS, "e.solutions"); + map.put(ONE_OAK_TECHNOLOGIES, "1OAK Technologies"); + map.put(WIMOTO_TECHNOLOGIES_INC, "Wimoto Technologies Inc"); + map.put(RADIUS_NETWORKS_INC, "Radius Networks, Inc."); + map.put(WIZE_TECHNOLOGY_CO_LTD, "Wize Technology Co., Ltd."); + map.put(QUALCOMM_LABS_INC, "Qualcomm Labs, Inc."); + map.put(ARUBA_NETWORKS, "Aruba Networks"); + map.put(BAIDU, "Baidu"); + map.put(ARENDI_AG, "Arendi AG"); + map.put(SKODA_AUTO_AS, "Skoda Auto a.s."); + map.put(VOLKSWAGON_AG, "Volkswagon AG"); + map.put(PORSCHE_AG, "Porsche AG"); + map.put(SINO_WEALTH_ELECTRONIC_LTD, "Sino Wealth Electronic Ltd."); + map.put(AIRTURN_INC, "AirTurn, Inc."); + map.put(KINSA_INC, "Kinsa, Inc."); + map.put(HID_GLOBAL, "HID Global"); + map.put(SEAT_ES, "SEAT es"); + map.put(PROMETHEAN_LTD, "Promethean Ltd."); + map.put(SALUTICA_ALLIED_SOLUTIONS, "Salutica Allied Solutions"); + map.put(GPSI_GROUP_PTY_LTD, "GPSI Group Pty Ltd"); + map.put(NIMBLE_DEVICES_OY, "Nimble Devices Oy"); + map.put(CHANGZHOU_YONGSE_INFOTECH_CO_LTD, "Changzhou Yongse Infotech Co., Ltd"); + map.put(SPORTIQ, "SportIQ"); + map.put(TEMEC_INSTRUMENTS_BV, "TEMEC Instruments B.V."); + map.put(SONY_CORPORATION, "Sony Corporation"); + map.put(ASSA_ABLOY, "ASSA ABLOY"); + map.put(CLARION_CO_LTD, "Clarion Co., Ltd."); + map.put(WAREHOUSE_INNOVATIONS, "Warehouse Innovations"); + map.put(CYPRESS_SEMICONDUCTOR_CORPORATION, "Cypress Semiconductor Corporation"); + map.put(MADS_INC, "MADS Inc"); + map.put(BLUE_MAESTRO_LIMITED, "Blue Maestro Limited"); + map.put(RESOLUTION_PRODUCTS_INC, "Resolution Products, Inc."); + map.put(AIREWEAR_LLC, "Airewear LLC"); + map.put(ETC_SP_ZOO, "ETC sp. z.o.o."); + map.put(PRESTIGIO_PLAZA_LTD, "Prestigio Plaza Ltd."); + + return map; + } +} diff --git a/app/src/main/java/com/qidian/baseble/model/resolver/GattAttributeResolver.java b/app/src/main/java/com/qidian/baseble/model/resolver/GattAttributeResolver.java new file mode 100644 index 0000000..ba33c56 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/model/resolver/GattAttributeResolver.java @@ -0,0 +1,378 @@ +package com.qidian.baseble.model.resolver; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * @Description: Bluetooth服务发现协议(SDP)规格 + * 参考:https://www.bluetooth.com/zh-cn/specifications/assigned-numbers/service-discovery + * @author: DAWI + * @date: 16/8/7 21:49. + */ +public class GattAttributeResolver { + public static final String BASE_GUID = "00000000-0000-1000-8000-00805f9b34fb"; + public static final String SERVICE_DISCOVERY_PROTOCOL_SDP = "00000001-0000-1000-8000-00805f9b34fb"; + public static final String USER_DATAGRAM_PROTOCOL_UDP = "00000002-0000-1000-8000-00805f9b34fb"; + public static final String RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM = "00000003-0000-1000-8000-00805f9b34fb"; + public static final String TCP = "00000004-0000-1000-8000-00805f9b34fb"; + public static final String TCSBIN = "00000005-0000-1000-8000-00805f9b34fb"; + public static final String TCSAT = "00000006-0000-1000-8000-00805f9b34fb"; + public static final String OBJECT_EXCHANGE_PROTOCOL_OBEX = "00000008-0000-1000-8000-00805f9b34fb"; + public static final String IP = "00000009-0000-1000-8000-00805f9b34fb"; + public static final String FTP = "0000000a-0000-1000-8000-00805f9b34fb"; + public static final String HTTP = "0000000c-0000-1000-8000-00805f9b34fb"; + public static final String WSP = "0000000e-0000-1000-8000-00805f9b34fb"; + public static final String BNEP_SVC = "0000000f-0000-1000-8000-00805f9b34fb"; + public static final String UPNP_PROTOCOL = "00000010-0000-1000-8000-00805f9b34fb"; + public static final String HIDP = "00000011-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_CONTROL_CHANNEL_PROTOCOL = "00000012-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_DATA_CHANNEL_PROTOCOL = "00000014-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_NOTIFICATION_PROTOCOL = "00000016-0000-1000-8000-00805f9b34fb"; + public static final String VCTP_PROTOCOL = "00000017-0000-1000-8000-00805f9b34fb"; + public static final String VDTP_PROTOCOL = "00000019-0000-1000-8000-00805f9b34fb"; + public static final String CMPT_PROTOCOL = "0000001b-0000-1000-8000-00805f9b34fb"; + public static final String UDI_C_PLANE_PROTOCOL = "0000001d-0000-1000-8000-00805f9b34fb"; + public static final String MCAP_CONTROL_CHANNEL = "0000001e-0000-1000-8000-00805f9b34fb"; + public static final String MCAP_DATA_CHANNEL = "0000001f-0000-1000-8000-00805f9b34fb"; + public static final String L2CAP = "00000100-0000-1000-8000-00805f9b34fb"; + public static final String SERVICE_DISCOVERY_SERVER = "00001000-0000-1000-8000-00805f9b34fb"; + public static final String BROWSE_GROUP_DESCRIPTOR = "00001001-0000-1000-8000-00805f9b34fb"; + public static final String PUBLIC_BROWSE_GROUP = "00001002-0000-1000-8000-00805f9b34fb"; + public static final String SPP = "00001101-0000-1000-8000-00805f9b34fb"; + public static final String LAN_ACCESS_USING_PPP = "00001102-0000-1000-8000-00805f9b34fb"; + public static final String DUN_GW = "00001103-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_SYNC = "00001104-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_OBJECT_PUSH = "00001105-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_FILE_TRANSFER = "00001106-0000-1000-8000-00805f9b34fb"; + public static final String IRMC_SYNC_COMMAND = "00001107-0000-1000-8000-00805f9b34fb"; + public static final String HSP_HS = "00001108-0000-1000-8000-00805f9b34fb"; + public static final String CORDLESS_TELEPHONY = "00001109-0000-1000-8000-00805f9b34fb"; + public static final String AUDIO_SOURCE = "0000110a-0000-1000-8000-00805f9b34fb"; + public static final String AUDIO_SINK = "0000110b-0000-1000-8000-00805f9b34fb"; + public static final String AV_REMOTE_CONTROL_TARGET = "0000110c-0000-1000-8000-00805f9b34fb"; + public static final String ADVANCED_AUDIO = "0000110d-0000-1000-8000-00805f9b34fb"; + public static final String AVRCP_REMOTE = "0000110e-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_CONFERENCING = "0000110f-0000-1000-8000-00805f9b34fb"; + public static final String INTERCOM = "00001110-0000-1000-8000-00805f9b34fb"; + public static final String FAX = "00001111-0000-1000-8000-00805f9b34fb"; + public static final String HEADSET_PROFILE_HSP_AUDIO_GATEWAY = "00001112-0000-1000-8000-00805f9b34fb"; + public static final String WAP = "00001113-0000-1000-8000-00805f9b34fb"; + public static final String WAP_CLIENT = "00001114-0000-1000-8000-00805f9b34fb"; + public static final String PANU = "00001115-0000-1000-8000-00805f9b34fb"; + public static final String NAP = "00001116-0000-1000-8000-00805f9b34fb"; + public static final String GN = "00001117-0000-1000-8000-00805f9b34fb"; + public static final String DIRECT_PRINTING = "00001118-0000-1000-8000-00805f9b34fb"; + public static final String REFERENCE_PRINTING = "00001119-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING = "0000111a-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING_RESPONDER = "0000111b-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING_AUTOMATIC_ARCHIVE = "0000111c-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING_REFERENCE_OBJECTS = "0000111d-0000-1000-8000-00805f9b34fb"; + public static final String HANDS_FREE_PROFILE_HFP = "0000111e-0000-1000-8000-00805f9b34fb"; + public static final String HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY = "0000111f-0000-1000-8000-00805f9b34fb"; + public static final String DIRECT_PRINTING_REFERENCE_OBJECTS = "00001120-0000-1000-8000-00805f9b34fb"; + public static final String REFLECTED_UI = "00001121-0000-1000-8000-00805f9b34fb"; + public static final String BASIC_PRINTING = "00001122-0000-1000-8000-00805f9b34fb"; + public static final String PRINTING_STATUS = "00001123-0000-1000-8000-00805f9b34fb"; + public static final String HID = "00001124-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_CABLE_REPLACEMENT = "00001125-0000-1000-8000-00805f9b34fb"; + public static final String HCR_PRINT = "00001126-0000-1000-8000-00805f9b34fb"; + public static final String HCR_SCAN = "00001127-0000-1000-8000-00805f9b34fb"; + public static final String COMMON_ISDN_ACCESS = "00001128-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_CONFERENCING_GATEWAY = "00001129-0000-1000-8000-00805f9b34fb"; + public static final String UDIMT = "0000112a-0000-1000-8000-00805f9b34fb"; + public static final String UDITA = "0000112b-0000-1000-8000-00805f9b34fb"; + public static final String AUDIO_VIDEO = "0000112c-0000-1000-8000-00805f9b34fb"; + public static final String SIM_ACCESS = "0000112d-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_PCE = "0000112e-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_PSE = "0000112f-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_PBAP = "00001130-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_MAS = "00001132-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_MNS = "00001133-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_MAP = "00001134-0000-1000-8000-00805f9b34fb"; + public static final String PNP = "00001200-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_NETWORKING = "00001201-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_FILE_TRANSFER = "00001202-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_AUDIO = "00001203-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_TELEPHONY = "00001204-0000-1000-8000-00805f9b34fb"; + public static final String UPNP = "00001205-0000-1000-8000-00805f9b34fb"; + public static final String UPNP_IP = "00001206-0000-1000-8000-00805f9b34fb"; + public static final String ESDP_UPNP_IP_PAN = "00001300-0000-1000-8000-00805f9b34fb"; + public static final String ESDP_UPNP_IP_LAP = "00001301-0000-1000-8000-00805f9b34fb"; + public static final String ESDP_UPNP_L2CAP = "00001302-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE = "00001303-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SINK = "00001304-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_DISTRIBUTION_PROFILE_VDP = "00001305-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_DEVICE_PROFILE_HDP = "00001400-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_DEVICE_PROFILE_HDP_SOURCE = "00001401-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_DEVICE_PROFILE_HDP_SINK = "00001402-0000-1000-8000-00805f9b34fb"; + public static final String GAP = "00001800-0000-1000-8000-00805f9b34fb"; + public static final String GATT = "00001801-0000-1000-8000-00805f9b34fb"; + public static final String IMMEDIATE_ALERT = "00001802-0000-1000-8000-00805f9b34fb"; + public static final String LINK_LOSS = "00001803-0000-1000-8000-00805f9b34fb"; + public static final String TX_POWER = "00001804-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_THERMOMETER = "00001809-0000-1000-8000-00805f9b34fb"; + public static final String DEVICE_INFORMATION = "0000180a-0000-1000-8000-00805f9b34fb"; + public static final String HEART_RATE = "0000180d-0000-1000-8000-00805f9b34fb"; + public static final String CYCLING_SC = "00001816-0000-1000-8000-00805f9b34fb"; + public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; + public static final String DEVICE_NAME = "00002a00-0000-1000-8000-00805f9b34fb"; + public static final String APPEARANCE = "00002a01-0000-1000-8000-00805f9b34fb"; + public static final String PERIPHERAL_PRIVACY_FLAG = "00002a02-0000-1000-8000-00805f9b34fb"; + public static final String RECONNECTION_ADDRESS = "00002a03-0000-1000-8000-00805f9b34fb"; + public static final String PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS = "00002a04-0000-1000-8000-00805f9b34fb"; + public static final String SERVICE_CHANGED = "00002a05-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_LEVEL = "00002a06-0000-1000-8000-00805f9b34fb"; + public static final String TX_POWER_LEVEL = "00002a07-0000-1000-8000-00805f9b34fb"; + public static final String DATE_TIME = "00002a08-0000-1000-8000-00805f9b34fb"; + public static final String DAY_OF_WEEK = "00002a09-0000-1000-8000-00805f9b34fb"; + public static final String DAY_DATE_TIME = "00002a0a-0000-1000-8000-00805f9b34fb"; + public static final String EXACT_TIME_256 = "00002a0c-0000-1000-8000-00805f9b34fb"; + public static final String DST_OFFSET = "00002a0d-0000-1000-8000-00805f9b34fb"; + public static final String TIME_ZONE = "00002a0e-0000-1000-8000-00805f9b34fb"; + public static final String LOCAL_TIME_INFORMATION = "00002a0f-0000-1000-8000-00805f9b34fb"; + public static final String TIME_WITH_DST = "00002a11-0000-1000-8000-00805f9b34fb"; + public static final String TIME_ACCURACY = "00002a12-0000-1000-8000-00805f9b34fb"; + public static final String TIME_SOURCE = "00002a13-0000-1000-8000-00805f9b34fb"; + public static final String REFERENCE_TIME_INFORMATION = "00002a14-0000-1000-8000-00805f9b34fb"; + public static final String TIME_UPDATE_CONTROL_POINT = "00002a16-0000-1000-8000-00805f9b34fb"; + public static final String TIME_UPDATE_STATE = "00002a17-0000-1000-8000-00805f9b34fb"; + public static final String TEMPERATURE_MEASUREMENT = "00002a1c-0000-1000-8000-00805f9b34fb"; + public static final String TEMPERATURE_TYPE = "00002a1d-0000-1000-8000-00805f9b34fb"; + public static final String INTERMEDIATE_TEMPERATURE = "00002a1e-0000-1000-8000-00805f9b34fb"; + public static final String MEASUREMENT_INTERVAL = "00002a21-0000-1000-8000-00805f9b34fb"; + public static final String SYSTEM_ID = "00002a23-0000-1000-8000-00805f9b34fb"; + public static final String MODEL_NUMBER_STRING = "00002a24-0000-1000-8000-00805f9b34fb"; + public static final String SERIAL_NUMBER_STRING = "00002a25-0000-1000-8000-00805f9b34fb"; + public static final String FIRMWARE_REVISION_STRING = "00002a26-0000-1000-8000-00805f9b34fb"; + public static final String HARDWARE_REVISION_STRING = "00002a27-0000-1000-8000-00805f9b34fb"; + public static final String SOFTWARE_REVISION_STRING = "00002a28-0000-1000-8000-00805f9b34fb"; + public static final String MANUFACTURER_NAME_STRING = "00002a29-0000-1000-8000-00805f9b34fb"; + public static final String IEEE_1107320601_REGULATORY = "00002a2a-0000-1000-8000-00805f9b34fb"; + public static final String CURRENT_TIME = "00002a2b-0000-1000-8000-00805f9b34fb"; + public static final String BLOOD_PRESSURE_MEASUREMENT = "00002a35-0000-1000-8000-00805f9b34fb"; + public static final String INTERMEDIATE_CUFF_PRESSURE = "00002a36-0000-1000-8000-00805f9b34fb"; + public static final String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb"; + public static final String BODY_SENSOR_LOCATION = "00002a38-0000-1000-8000-00805f9b34fb"; + public static final String HEART_RATE_CONTROL_POINT = "00002a39-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_STATUS = "00002a3f-0000-1000-8000-00805f9b34fb"; + public static final String RINGER_CONTROL_POINT = "00002a40-0000-1000-8000-00805f9b34fb"; + public static final String RINGER_SETTING = "00002a41-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_CATEGORY_ID_BIT_MASK = "00002a42-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_CATEGORY_ID = "00002a43-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_NOTIFICATION_CONTROL_POINT = "00002a44-0000-1000-8000-00805f9b34fb"; + public static final String UNREAD_ALERT_STATUS = "00002a45-0000-1000-8000-00805f9b34fb"; + public static final String NEW_ALERT = "00002a46-0000-1000-8000-00805f9b34fb"; + public static final String SUPPORTED_NEW_ALERT_CATEGORY = "00002a47-0000-1000-8000-00805f9b34fb"; + public static final String SUPPORTED_UNREAD_ALERT_CATEGORY = "00002a48-0000-1000-8000-00805f9b34fb"; + public static final String BLOOD_PRESSURE_FEATURE = "00002a49-0000-1000-8000-00805f9b34fb"; + public static final String PNPID = "00002a50-0000-1000-8000-00805f9b34fb"; + public static final String SC_CONTROL_POINT = "00002a55-0000-1000-8000-00805f9b34fb"; + public static final String CSC_MEASUREMENT = "00002a5b-0000-1000-8000-00805f9b34fb"; + public static final String CSC_FEATURE = "00002a5c-0000-1000-8000-00805f9b34fb"; + public static final String SENSOR_LOCATION = "00002a5d-0000-1000-8000-00805f9b34fb"; + public static final String ACTIVESYNC = "831c4071-7bc8-4a9c-a01c-15df25a4adbc"; + public static final String ESTIMOTE_SERVICE = "b9403000-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_UUID = "b9403003-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_MAJOR = "b9403001-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_MINOR = "b9403002-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_BATTERY = "b9403041-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_TEMPERATURE = "b9403021-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_POWER = "b9403011-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_ADVERTISING_INTERVAL = "b9403012-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_VERSION_SERVICE = "b9404000-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_SOFTWARE_VERSION = "b9404001-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_HARDWARE_VERSION = "b9404002-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_AUTHENTICATION_SERVICE = "b9402000-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_ADVERTISING_SEED = "b9402001-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_ADVERTISING_VECTOR = "b9402002-f5f8-466e-aff9-25556b57fe6d"; + + private final static Map sGattAttributesMap = populateGattAttributesMap(); + + public static String getAttributeName(final String uuid, final String fallback) { + final String name = sGattAttributesMap.get(uuid.toLowerCase(Locale.US)); + return name == null ? fallback : name; + } + + private static Map populateGattAttributesMap() { + final Map map = new HashMap<>(); + + map.put(BASE_GUID, "Base GUID"); + map.put(SERVICE_DISCOVERY_PROTOCOL_SDP, "Service Discovery Protocol (SDP)"); + map.put(USER_DATAGRAM_PROTOCOL_UDP, "User Datagram Protocol (UDP)"); + map.put(RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM, "Radio Frequency Communication Protocol (RFCOMM)"); + map.put(TCP, "TCP"); + map.put(TCSBIN, "TCSBIN"); + map.put(TCSAT, "TCSAT"); + map.put(OBJECT_EXCHANGE_PROTOCOL_OBEX, "Object Exchange Protocol (OBEX)"); + map.put(IP, "IP"); + map.put(FTP, "FTP"); + map.put(HTTP, "HTTP"); + map.put(WSP, "WSP"); + map.put(BNEP_SVC, "BNEP_SVC"); + map.put(UPNP_PROTOCOL, "UPNP Protocol"); + map.put(HIDP, "HIDP"); + map.put(HARDCOPY_CONTROL_CHANNEL_PROTOCOL, "Hardcopy Control Channel Protocol"); + map.put(HARDCOPY_DATA_CHANNEL_PROTOCOL, "Hardcopy Data Channel Protocol"); + map.put(HARDCOPY_NOTIFICATION_PROTOCOL, "Hardcopy Notification Protocol"); + map.put(VCTP_PROTOCOL, "VCTP Protocol"); + map.put(VDTP_PROTOCOL, "VDTP Protocol"); + map.put(CMPT_PROTOCOL, "CMPT Protocol"); + map.put(UDI_C_PLANE_PROTOCOL, "UDI C Plane Protocol"); + map.put(MCAP_CONTROL_CHANNEL, "MCAP Control Channel"); + map.put(MCAP_DATA_CHANNEL, "MCAP Data Channel"); + map.put(L2CAP, "L2CAP"); + map.put(SERVICE_DISCOVERY_SERVER, "Service Discovery Server"); + map.put(BROWSE_GROUP_DESCRIPTOR, "Browse Group Descriptor"); + map.put(PUBLIC_BROWSE_GROUP, "Public Browse Group"); + map.put(SPP, "SPP"); + map.put(LAN_ACCESS_USING_PPP, "LAN Access Using PPP"); + map.put(DUN_GW, "DUN_GW"); + map.put(OBEX_SYNC, "OBEX_SYNC"); + map.put(OBEX_OBJECT_PUSH, "OBEX Object Push"); + map.put(OBEX_FILE_TRANSFER, "OBEX File Transfer"); + map.put(IRMC_SYNC_COMMAND, "IrMC Sync Command"); + map.put(HSP_HS, "HSP_HS"); + map.put(CORDLESS_TELEPHONY, "Cordless Telephony"); + map.put(AUDIO_SOURCE, "Audio Source"); + map.put(AUDIO_SINK, "Audio Sink"); + map.put(AV_REMOTE_CONTROL_TARGET, "AV Remote Control Target"); + map.put(ADVANCED_AUDIO, "ADVANCED_AUDIO"); + map.put(AVRCP_REMOTE, "AVRCP_REMOTE"); + map.put(VIDEO_CONFERENCING, "Video Conferencing"); + map.put(INTERCOM, "Intercom"); + map.put(FAX, "FAX"); + map.put(HEADSET_PROFILE_HSP_AUDIO_GATEWAY, "Headset Profile (HSP) - Audio Gateway"); + map.put(WAP, "WAP"); + map.put(WAP_CLIENT, "WAP Client"); + map.put(PANU, "PANU"); + map.put(NAP, "NAP"); + map.put(GN, "GN"); + map.put(DIRECT_PRINTING, "Direct Printing"); + map.put(REFERENCE_PRINTING, "Reference Printing"); + map.put(IMAGING, "Imaging"); + map.put(IMAGING_RESPONDER, "Imaging Responder"); + map.put(IMAGING_AUTOMATIC_ARCHIVE, "Imaging Automatic Archive"); + map.put(IMAGING_REFERENCE_OBJECTS, "Imaging Reference Objects"); + map.put(HANDS_FREE_PROFILE_HFP, "Hands Free Profile (HFP)"); + map.put(HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY, "Hands Free Profile (HFP) – Audio Gateway"); + map.put(DIRECT_PRINTING_REFERENCE_OBJECTS, "Direct Printing Reference Objects"); + map.put(REFLECTED_UI, "Reflected UI"); + map.put(BASIC_PRINTING, "Basic Printing"); + map.put(PRINTING_STATUS, "Printing Status"); + map.put(HID, "HID"); + map.put(HARDCOPY_CABLE_REPLACEMENT, "Hardcopy Cable Replacement"); + map.put(HCR_PRINT, "HCR Print"); + map.put(HCR_SCAN, "HCR Scan"); + map.put(COMMON_ISDN_ACCESS, "Common ISDN Access"); + map.put(VIDEO_CONFERENCING_GATEWAY, "Video Conferencing Gateway"); + map.put(UDIMT, "UDIMT"); + map.put(UDITA, "UDITA"); + map.put(AUDIO_VIDEO, "Audio Video"); + map.put(SIM_ACCESS, "SIM Access"); + map.put(OBEX_PCE, "OBEX PCE"); + map.put(OBEX_PSE, "OBEX PSE"); + map.put(OBEX_PBAP, "OBEX PBAP"); + map.put(OBEX_MAS, "OBEX MAS"); + map.put(OBEX_MNS, "OBEX MNS"); + map.put(OBEX_MAP, "OBEX MAP"); + map.put(PNP, "PNP"); + map.put(GENERIC_NETWORKING, "Generic Networking"); + map.put(GENERIC_FILE_TRANSFER, "Generic File Transfer"); + map.put(GENERIC_AUDIO, "Generic Audio"); + map.put(GENERIC_TELEPHONY, "Generic Telephony"); + map.put(UPNP, "UPNP"); + map.put(UPNP_IP, "UPNP IP"); + map.put(ESDP_UPNP_IP_PAN, "ESDP UPnP IP PAN"); + map.put(ESDP_UPNP_IP_LAP, "ESDP UPnP IP LAP"); + map.put(ESDP_UPNP_L2CAP, "ESDP Upnp L2CAP"); + map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE, "Video Distribution Profile (VDP) - Source"); + map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SINK, "Video Distribution Profile (VDP) - Sink"); + map.put(VIDEO_DISTRIBUTION_PROFILE_VDP, "Video Distribution Profile (VDP)"); + map.put(HEALTH_DEVICE_PROFILE_HDP, "Health Device Profile (HDP)"); + map.put(HEALTH_DEVICE_PROFILE_HDP_SOURCE, "Health Device Profile (HDP) - Source"); + map.put(HEALTH_DEVICE_PROFILE_HDP_SINK, "Health Device Profile (HDP) - Sink"); + map.put(GAP, "GAP"); + map.put(GATT, "GATT"); + map.put(IMMEDIATE_ALERT, "IMMEDIATE_ALERT"); + map.put(LINK_LOSS, "LINK_LOSS"); + map.put(TX_POWER, "TX_POWER"); + map.put(HEALTH_THERMOMETER, "Health Thermometer"); + map.put(DEVICE_INFORMATION, "Device Information"); + map.put(HEART_RATE, "HEART_RATE"); + map.put(CYCLING_SC, "CYCLING_SC"); + map.put(CLIENT_CHARACTERISTIC_CONFIG, "CLIENT_CHARACTERISTIC_CONFIG"); + map.put(DEVICE_NAME, "Device Name"); + map.put(APPEARANCE, "Appearance"); + map.put(PERIPHERAL_PRIVACY_FLAG, "Peripheral Privacy Flag"); + map.put(RECONNECTION_ADDRESS, "Reconnection Address"); + map.put(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS, "Peripheral Preferred Connection Parameters"); + map.put(SERVICE_CHANGED, "Service Changed"); + map.put(ALERT_LEVEL, "Alert Level"); + map.put(TX_POWER_LEVEL, "Tx Power Level"); + map.put(DATE_TIME, "Date Time"); + map.put(DAY_OF_WEEK, "Day of Week"); + map.put(DAY_DATE_TIME, "Day Date Time"); + map.put(EXACT_TIME_256, "Exact Time 256"); + map.put(DST_OFFSET, "DST Offset"); + map.put(TIME_ZONE, "Time Zone"); + map.put(LOCAL_TIME_INFORMATION, "Local Time Information"); + map.put(TIME_WITH_DST, "Time with DST"); + map.put(TIME_ACCURACY, "Time Accuracy"); + map.put(TIME_SOURCE, "Time Source"); + map.put(REFERENCE_TIME_INFORMATION, "Reference Time Information"); + map.put(TIME_UPDATE_CONTROL_POINT, "Time Update Control Point"); + map.put(TIME_UPDATE_STATE, "Time Update State"); + map.put(TEMPERATURE_MEASUREMENT, "Temperature Measurement"); + map.put(TEMPERATURE_TYPE, "Temperature Type"); + map.put(INTERMEDIATE_TEMPERATURE, "Intermediate Temperature"); + map.put(MEASUREMENT_INTERVAL, "Measurement Interval"); + map.put(SYSTEM_ID, "System ID"); + map.put(MODEL_NUMBER_STRING, "Model Number String"); + map.put(SERIAL_NUMBER_STRING, "Serial Number String"); + map.put(FIRMWARE_REVISION_STRING, "Firmware Revision String"); + map.put(HARDWARE_REVISION_STRING, "Hardware Revision String"); + map.put(SOFTWARE_REVISION_STRING, "Software Revision String"); + map.put(MANUFACTURER_NAME_STRING, "Manufacturer Name String"); + map.put(IEEE_1107320601_REGULATORY, "IEEE 11073-20601 Regulatory"); + map.put(CURRENT_TIME, "Current Time"); + map.put(BLOOD_PRESSURE_MEASUREMENT, "Blood Pressure Measurement"); + map.put(INTERMEDIATE_CUFF_PRESSURE, "Intermediate Cuff Pressure"); + map.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement"); + map.put(BODY_SENSOR_LOCATION, "Body Sensor Location"); + map.put(HEART_RATE_CONTROL_POINT, "Heart Rate Control Point"); + map.put(ALERT_STATUS, "Alert Status"); + map.put(RINGER_CONTROL_POINT, "Ringer Control Point"); + map.put(RINGER_SETTING, "Ringer Setting"); + map.put(ALERT_CATEGORY_ID_BIT_MASK, "Alert Category ID Bit Mask"); + map.put(ALERT_CATEGORY_ID, "Alert Category ID"); + map.put(ALERT_NOTIFICATION_CONTROL_POINT, "Alert Notification Control Point"); + map.put(UNREAD_ALERT_STATUS, "Unread Alert Status"); + map.put(NEW_ALERT, "New Alert"); + map.put(SUPPORTED_NEW_ALERT_CATEGORY, "Supported New Alert Category"); + map.put(SUPPORTED_UNREAD_ALERT_CATEGORY, "Supported Unread Alert Category"); + map.put(BLOOD_PRESSURE_FEATURE, "Blood Pressure Feature"); + map.put(PNPID, "PNPID"); + map.put(SC_CONTROL_POINT, "SC_CONTROL_POINT"); + map.put(CSC_MEASUREMENT, "CSC_MEASUREMENT"); + map.put(CSC_FEATURE, "CSC_FEATURE"); + map.put(SENSOR_LOCATION, "SENSOR_LOCATION"); + map.put(ACTIVESYNC, "ActiveSync"); + map.put(ESTIMOTE_SERVICE, "Estimote Service"); + map.put(ESTIMOTE_UUID, "Estimote UUID"); + map.put(ESTIMOTE_MAJOR, "Estimote Major"); + map.put(ESTIMOTE_MINOR, "Estimote Minor"); + map.put(ESTIMOTE_BATTERY, "Estimote Battery"); + map.put(ESTIMOTE_TEMPERATURE, "Estimote Temperature"); + map.put(ESTIMOTE_POWER, "Estimote Power"); + map.put(ESTIMOTE_ADVERTISING_INTERVAL, "Estimote Advertising Interval"); + map.put(ESTIMOTE_VERSION_SERVICE, "Estimote Version Service"); + map.put(ESTIMOTE_SOFTWARE_VERSION, "Estimote Software Version"); + map.put(ESTIMOTE_HARDWARE_VERSION, "Estimote Hardware Version"); + map.put(ESTIMOTE_AUTHENTICATION_SERVICE, "Estimote Authentication Service"); + map.put(ESTIMOTE_ADVERTISING_SEED, "Estimote Advertising Seed"); + map.put(ESTIMOTE_ADVERTISING_VECTOR, "Estimote Advertising Vector"); + + return map; + } +} diff --git a/app/src/main/java/com/qidian/baseble/utils/AdRecordUtil.java b/app/src/main/java/com/qidian/baseble/utils/AdRecordUtil.java new file mode 100644 index 0000000..86fa3fb --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/utils/AdRecordUtil.java @@ -0,0 +1,134 @@ +package com.qidian.baseble.utils; + +import android.util.SparseArray; + + +import com.qidian.baseble.model.adrecord.AdRecord; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description: 广播包解析工具类 + * @author: DAWI + * @date: 16/8/7 21:56. + */ +public class AdRecordUtil { + private AdRecordUtil() { + // TO AVOID INSTANTIATION + } + + public static String getRecordDataAsString(final AdRecord nameRecord) { + if (nameRecord == null) { + return ""; + } + return new String(nameRecord.getData()); + } + + public static byte[] getServiceData(final AdRecord serviceData) { + if (serviceData == null) { + return null; + } + if (serviceData.getType() != AdRecord.BLE_GAP_AD_TYPE_SERVICE_DATA) return null; + + final byte[] raw = serviceData.getData(); + //Chop out the uuid + return Arrays.copyOfRange(raw, 2, raw.length); + } + + public static int getServiceDataUuid(final AdRecord serviceData) { + if (serviceData == null) { + return -1; + } + if (serviceData.getType() != AdRecord.BLE_GAP_AD_TYPE_SERVICE_DATA) return -1; + + final byte[] raw = serviceData.getData(); + //Find UUID data in byte array + int uuid = (raw[1] & 0xFF) << 8; + uuid += (raw[0] & 0xFF); + + return uuid; + } + + /* + * Read out all the AD structures from the raw scan record + */ + public static List parseScanRecordAsList(final byte[] scanRecord) { + final List records = new ArrayList<>(); + + int index = 0; + while (index < scanRecord.length) { + final int length = scanRecord[index++]; + //Done once we run out of records + if (length == 0) break; + + final int type = scanRecord[index] & 0xFF; + + //Done if our record isn't a valid type + if (type == 0) break; + + final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); + + records.add(new AdRecord(length, type, data)); + + //Advance + index += length; + } + + return Collections.unmodifiableList(records); + } + + public static Map parseScanRecordAsMap(final byte[] scanRecord) { + final Map records = new HashMap<>(); + + int index = 0; + while (index < scanRecord.length) { + final int length = scanRecord[index++]; + //Done once we run out of records + if (length == 0) break; + + final int type = scanRecord[index] & 0xFF; + + //Done if our record isn't a valid type + if (type == 0) break; + + final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); + + records.put(type, new AdRecord(length, type, data)); + + //Advance + index += length; + } + + return Collections.unmodifiableMap(records); + } + + public static SparseArray parseScanRecordAsSparseArray(final byte[] scanRecord) { + final SparseArray records = new SparseArray<>(); + + int index = 0; + while (index < scanRecord.length) { + final int length = scanRecord[index++]; + //Done once we run out of records + if (length == 0) break; + + final int type = scanRecord[index] & 0xFF; + + //Done if our record isn't a valid type + if (type == 0) break; + + final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); + + records.put(type, new AdRecord(length, type, data)); + + //Advance + index += length; + } + + return records; + } +} diff --git a/app/src/main/java/com/qidian/baseble/utils/BleUtil.java b/app/src/main/java/com/qidian/baseble/utils/BleUtil.java new file mode 100644 index 0000000..4c69b75 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/utils/BleUtil.java @@ -0,0 +1,37 @@ +package com.qidian.baseble.utils; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; + +/** + * @Description: 蓝牙基础操作工具类 + * @author: DAWI + * @date: 16/8/5 20:43. + */ +public class BleUtil { + public static void enableBluetooth(Activity activity, int requestCode) { + Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + activity.startActivityForResult(intent, requestCode); + } + + public static boolean isSupportBle(Context context) { + if (context == null || !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + return false; + } + BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); + return manager.getAdapter() != null; + } + + public static boolean isBleEnable(Context context) { + if (!isSupportBle(context)) { + return false; + } + BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); + return manager.getAdapter().isEnabled(); + } + +} diff --git a/app/src/main/java/com/qidian/baseble/utils/HexUtil.java b/app/src/main/java/com/qidian/baseble/utils/HexUtil.java new file mode 100644 index 0000000..ce48fc7 --- /dev/null +++ b/app/src/main/java/com/qidian/baseble/utils/HexUtil.java @@ -0,0 +1,402 @@ +package com.qidian.baseble.utils; + +import com.vise.log.ViseLog; + +import java.io.ByteArrayOutputStream; + +/** + * @Description: 十六进制转换类 + * @author: DAWI + * @date: 16/8/7 21:57. + */ +public class HexUtil { + /** + * 用于建立十六进制字符的输出的小写字符数组 + */ + private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 用于建立十六进制字符的输出的大写字符数组 + */ + private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 将字节数组转换为十六进制字符数组 + * + * @param data byte[] + * @return 十六进制char[] + */ + public static char[] encodeHex(byte[] data) { + return encodeHex(data, true); + } + + /** + * 将字节数组转换为十六进制字符数组 + * + * @param data byte[] + * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 + * @return 十六进制char[] + */ + public static char[] encodeHex(byte[] data, boolean toLowerCase) { + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * 将字节数组转换为十六进制字符数组 + * + * @param data byte[] + * @param toDigits 用于控制输出的char[] + * @return 十六进制char[] + */ + protected static char[] encodeHex(byte[] data, char[] toDigits) { + int l = data.length; + char[] out = new char[l << 1]; + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + return out; + } + + /** + * 字节数组转16进制 + * + * @param bytes 需要转换的byte数组 + * @return 转换后的Hex字符串 + */ + public static String bytesToHex(byte[] bytes) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(bytes[i] & 0xFF); + if (hex.length() < 2) { + sb.append(0); + } + sb.append(hex); + } + return sb.toString(); + } + + /* + * @Titl: + * @Param + * @Return: + * @Description:设备数据处理流程 + * @author xundanqing + * @CreateDate: 2019/4/4 11:34 + */ + public static String byte_String(byte b) { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("%02x", b)); + return sb.toString(); + } + + public static int byteToInt(byte b) { + int x = b & 0xff; + return x; + } + + public static byte[] int2ByteArray(int i) { + byte[] result = new byte[4]; + result[0] = (byte) ((i >> 24) & 0xFF); + result[1] = (byte) ((i >> 16) & 0xFF); + result[2] = (byte) ((i >> 8) & 0xFF); + result[3] = (byte) (i & 0xFF); + return result; + } + + /* + * @Titl: + * @Param + * @Return: + * @Description:获得当前时间 + * @author xundanqing + * @CreateDate: 2019/4/4 11:34 + */ + public static byte[] GetTempTime(String timeString) { + + byte timebyte[]; + timebyte = StrToBCDBytes(timeString); + return timebyte; + + } + + /* + * @Titl: + * @Param + * @Return: + * @Description:字符串转bcd + * @author xundanqing + * @CreateDate: 2019/4/4 11:34 + */ + public static byte[] StrToBCDBytes(String s) { + if (s.length() % 2 != 0) { + s = "0" + s; + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + char[] cs = s.toCharArray(); + for (int i = 0; i < cs.length; i += 2) { + int high = cs[i] - 48; + int low = cs[i + 1] - 48; + baos.write(high << 4 | low); + } + return baos.toByteArray(); + } + + public static byte[] addBytes(byte[] data1, byte[] data2) { + byte[] data3 = new byte[data1.length + data2.length]; + System.arraycopy(data1, 0, data3, 0, data1.length); + System.arraycopy(data2, 0, data3, data1.length, data2.length); + return data3; + } + + /** + * @return int + * @Title: getInt + * @Description: 将字节数组前4字节转换为整型数值 + * @author fun + * @date 2019年3月27日 + */ + public static int byteArrayToInt(byte[] bytes) { + int value = 0; + // 由高位到低位 + for (int i = 0; i < bytes.length; i++) { + int shift = (bytes.length - 1 - i) * 8; + value += (bytes[i] & 0x000000FF) << shift;// 往高位游 + } + return value; + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param data byte[] + * @return 十六进制String + */ + public static String encodeHexStr(byte[] data) { + return encodeHexStr(data, true); + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param data byte[] + * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 + * @return 十六进制String + */ + public static String encodeHexStr(byte[] data, boolean toLowerCase) { + return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param data byte[] + * @param toDigits 用于控制输出的char[] + * @return 十六进制String + */ + protected static String encodeHexStr(byte[] data, char[] toDigits) { + if (data == null) { + ViseLog.e("this data is null."); + return ""; + } + return new String(encodeHex(data, toDigits)); + } + + /** + * 将十六进制字符串转换为字节数组 + * + * @param data + * @return + */ + public static byte[] decodeHex(String data) { + if (data == null) { + ViseLog.e("this data is null."); + return new byte[0]; + } + return decodeHex(data.toCharArray()); + } + + /** + * 将十六进制字符数组转换为字节数组 + * + * @param data 十六进制char[] + * @return byte[] + * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 + */ + public static byte[] decodeHex(char[] data) { + + int len = data.length; + + if ((len & 0x01) != 0) { + throw new RuntimeException("Odd number of characters."); + } + + byte[] out = new byte[len >> 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; j < len; i++) { + int f = toDigit(data[j], j) << 4; + j++; + f = f | toDigit(data[j], j); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + /** + * 将十六进制字符转换成一个整数 + * + * @param ch 十六进制char + * @param index 十六进制字符在字符数组中的位置 + * @return 一个整数 + * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 + */ + protected static int toDigit(char ch, int index) { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); + } + return digit; + } + + /** + * 截取字节数组 + * + * @param src byte [] 数组源 这里填16进制的 数组 + * @param begin 起始位置 源数组的起始位置。0位置有效 + * @param count 截取长度 + * @return + */ + public static byte[] subBytes(byte[] src, int begin, int count) { + byte[] bs = new byte[count]; + System.arraycopy(src, begin, bs, 0, count); // bs 目的数组 0 截取后存放的数值起始位置。0位置有效 + return bs; + } + + /** + * int转byte数组 + * + * @param bb + * @param x + * @param index 第几位开始 + * @param flag 标识高低位顺序,高位在前为true,低位在前为false + */ + public static void intToByte(byte[] bb, int x, int index, boolean flag) { + if (flag) { + bb[index + 0] = (byte) (x >> 24); + bb[index + 1] = (byte) (x >> 16); + bb[index + 2] = (byte) (x >> 8); + bb[index + 3] = (byte) (x >> 0); + } else { + bb[index + 3] = (byte) (x >> 24); + bb[index + 2] = (byte) (x >> 16); + bb[index + 1] = (byte) (x >> 8); + bb[index + 0] = (byte) (x >> 0); + } + } + + /** + * byte数组转int + * + * @param bb + * @param index 第几位开始 + * @param flag 标识高低位顺序,高位在前为true,低位在前为false + * @return + */ + public static int byteToInt(byte[] bb, int index, boolean flag) { + if (flag) { + return (int) ((((bb[index + 0] & 0xff) << 24) + | ((bb[index + 1] & 0xff) << 16) + | ((bb[index + 2] & 0xff) << 8) + | ((bb[index + 3] & 0xff) << 0))); + } else { + return (int) ((((bb[index + 3] & 0xff) << 24) + | ((bb[index + 2] & 0xff) << 16) + | ((bb[index + 1] & 0xff) << 8) + | ((bb[index + 0] & 0xff) << 0))); + } + } + + + /** + * 字节数组逆序 + * + * @param data + * @return + */ + public static byte[] reverse(byte[] data) { + byte[] reverseData = new byte[data.length]; + for (int i = 0; i < data.length; i++) { + reverseData[i] = data[data.length - 1 - i]; + } + return reverseData; + } + + /** + * 蓝牙传输 16进制 高低位 读数的 转换 + * + * @param data 截取数据源,字节数组 + * @param index 截取数据开始位置 + * @param count 截取数据长度,只能为2、4、8个字节 + * @param flag 标识高低位顺序,高位在前为true,低位在前为false + * @return + */ + public static long byteToLong(byte[] data, int index, int count, boolean flag) { + long lg = 0; + if (flag) { + switch (count) { + case 2: + lg = ((((long) data[index + 0] & 0xff) << 8) + | (((long) data[index + 1] & 0xff) << 0)); + break; + + case 4: + lg = ((((long) data[index + 0] & 0xff) << 24) + | (((long) data[index + 1] & 0xff) << 16) + | (((long) data[index + 2] & 0xff) << 8) + | (((long) data[index + 3] & 0xff) << 0)); + break; + + case 8: + lg = ((((long) data[index + 0] & 0xff) << 56) + | (((long) data[index + 1] & 0xff) << 48) + | (((long) data[index + 2] & 0xff) << 40) + | (((long) data[index + 3] & 0xff) << 32) + | (((long) data[index + 4] & 0xff) << 24) + | (((long) data[index + 5] & 0xff) << 16) + | (((long) data[index + 6] & 0xff) << 8) + | (((long) data[index + 7] & 0xff) << 0)); + break; + } + return lg; + } else { + switch (count) { + case 2: + lg = ((((long) data[index + 1] & 0xff) << 8) + | (((long) data[index + 0] & 0xff) << 0)); + break; + case 4: + lg = ((((long) data[index + 3] & 0xff) << 24) + | (((long) data[index + 2] & 0xff) << 16) + | (((long) data[index + 1] & 0xff) << 8) + | (((long) data[index + 0] & 0xff) << 0)); + break; + case 8: + lg = ((((long) data[index + 7] & 0xff) << 56) + | (((long) data[index + 6] & 0xff) << 48) + | (((long) data[index + 5] & 0xff) << 40) + | (((long) data[index + 4] & 0xff) << 32) + | (((long) data[index + 3] & 0xff) << 24) + | (((long) data[index + 2] & 0xff) << 16) + | (((long) data[index + 1] & 0xff) << 8) + | (((long) data[index + 0] & 0xff) << 0)); + break; + } + return lg; + } + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/MainActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/MainActivity.kt new file mode 100644 index 0000000..14082e2 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/MainActivity.kt @@ -0,0 +1,549 @@ + +package com.qidian.zhongkesmart + +import android.Manifest +import android.content.Intent +import android.net.Uri +import android.os.CountDownTimer +import android.os.Handler +import android.provider.Settings +import android.util.Log +import android.view.View +import com.blankj.utilcode.util.ToastUtils +import com.netease.lava.nertc.sdk.NERtcOption +import com.netease.nimlib.sdk.NIMClient +import com.netease.nimlib.sdk.Observer +import com.netease.nimlib.sdk.StatusCode +import com.netease.nimlib.sdk.auth.AuthServiceObserver +import com.netease.yunxin.nertc.ui.CallKitUI.init +import com.netease.yunxin.nertc.ui.CallKitUIOptions +import com.qidian.baseble.ViseBle +import com.qidian.zhongkesmart.activity.* +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.dialog.* +import com.qidian.zhongkesmart.model.* +import com.qidian.zhongkesmart.net.* +import com.qidian.zhongkesmart.receiver.WifiReceiver +import com.qidian.zhongkesmart.utils.* +import com.qidian.zhongkesmart.utils.TimeUtil.getNowTime +import com.qidian.zjlw.presenter.Contract +import com.qidian.zjlw.presenter.MainPresenter +import com.qidian.zxing.lib_zxing.activity.CodeUtils +import kotlinx.android.synthetic.main.activity_base.* +import kotlinx.android.synthetic.main.activity_launch.* +import kotlinx.android.synthetic.main.activity_wi_fi.* +import kotlinx.android.synthetic.main.layout_setmess.* +import kotlinx.android.synthetic.main.layout_standby.* +import kotlinx.android.synthetic.main.layout_sys_bluetooth2.* +import kotlinx.android.synthetic.main.layout_sys_netmess1.* +import kotlinx.android.synthetic.main.layout_tiepain_details.* +import pub.devrel.easypermissions.EasyPermissions + + +/** + * 1、全局监听wifi + * 2、监听蓝牙 + * 3、新增、修改、解除绑定贴片 + * 4、做扫码操作之前获取tokenbridge/device + * 5、根据蓝牙接收数据记录开关 + * 位移型处理方式: + *主要判断的是 三轴传感器的数值变化,在三轴传感器首次有值时认为该场景为开启状态(贴件首次安装均为关闭状态),再次有值时认为是关闭状态;贴件每次向网关发送数据时,网关需要查询该贴件的上次状态进行相反处理即可。 + *开启的持续时间为两次之间的差; + *震动型型处理方式: + *主要判断的是震动传感器的有值与否,当震动传感器有值的时候认为该场景为开启状态,当震动传感器X秒不在传值时认为该场景为关闭状态; + *开启的持续时间为两次之间的差; + * 5、离线任务 + * 每天00:00 时要求贴件 定时发送一次数据,当网关未收到贴件上传的数据时认为该贴件为离线状态 + * + * 6、重要::::::::设置发包后45天后app无法打开 + * + * 7、网关心跳接口 每15分钟调用一次 + */ + +class MainActivity : BaseActivity(), EasyPermissions.PermissionCallbacks, Contract.GovernmentView { + var TAG_L = "MainActivity" + private val REQUEST_CODE = 100 + var wifiReceiver: WifiReceiver? = null + + //添加新设备时候扫描出来的贴件MAC地址 + var mTJMac = "" + + var mCode = "" + var mToken = "" + var warnType = "" + + + //网关心跳计时 + var heartbeatTime = 0 + + var countDownTimer: CountDownTimer? = null + var isResume = false + //是否要重连 + var isNeedRetry = true + + private val mPresenter by lazy { MainPresenter() } + override fun getLayoutId(): Int { + return R.layout.activity_main + } + + override fun initView() { + mPresenter.attachView(this) + checkLogin() + initG2() + + } + + override fun initData() { + } + + override fun isImmersion(): Boolean { + return true + } + + + fun onClick(v: View) { + when (v.id) { + //添加设备-扫描二维码 + R.id.imv_index_add -> { + if (DataServer.mHttpToken.isNullOrEmpty()) { +// mPresenter.getHttpCode(this) + ToastUtil.showToast("您还未绑定网关") + } else { + askPermissionOfCamera() + /* mTJMac = "cf6a12483f38" + FindNewEquipmentDialog.instance!!.getShareDialog(this, + mTJMac, + object : FindNewEquipmentDialog.PopupYearWindowCallBack { + //新增 + override fun doWork(roomName: String?, type: String?) { + if (!ToolUtil.isContainsTJData( + roomName!!, + type!!, + mTJMac, + this@MainActivity + ) + ) { + mPresenter.getbindDevice( + this@MainActivity, + mTJMac, + roomName!!, + type!! + ) + + }else{ + ToastUtil.showToast("请勿重复添加") + } + } + + //修改 + override fun doChangeWork(roomName: String?, type: String?) { + if (!ToolUtil.isContainsTJData( + roomName!!, + type!!, + mTJMac, + this@MainActivity + ) + ) { + mPresenter.getChangeDevice( + this@MainActivity, + mTJMac, + roomName, + type + ) + }else{ + ToastUtil.showToast("请勿重复添加") + } + } + + //解绑 + override fun doUnBindWork() { + mPresenter.removebindDevice(this@MainActivity, mTJMac) + } + + })*/ + } + + } + //二维码弹窗 + R.id.imv_index_qrcode -> { + EquipmentStatusDialog.instance!!.getShareDialog(this, + object : EquipmentStatusDialog.PopupYearWindowCallBack { + override fun doWork(place: String?, use: String?) { + + } + + }) + } + //我的设备 + R.id.li_index_01 -> { + startActivity(Intent(this, MyEquipmentActivity::class.java)) + } + //智能分析 2022/08/22要求不可点击 + R.id.li_index_02 -> { + startActivity(Intent(this, SmartAnalysisActivity::class.java)) + } + //语音通话 + R.id.li_index_03 -> { + startActivity(Intent(this,SelectCallUserActivity::class.java)) + } + //系统设置 + R.id.li_index_04 -> { +// startActivity(Intent(this, SetLockActivity::class.java)) + startActivity(Intent(this, SettingActivity::class.java)) + } + + R.id.iv_back -> { + onBackPressed() + } + } + } + + private fun askPermissionOfCamera() { + EasyPermissions.requestPermissions( + this, + "二维码扫码,请允许获取您的摄像头权限", + DataServer.ACCESS_CAMERA_CODE, + Manifest.permission.CAMERA + ) + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult( + requestCode, + permissions, + grantResults, + this + )//1001 + } + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_CAMERA_CODE) { + val intent = + Intent(this, com.qidian.zxing.lib_zxing.activity.CaptureActivity::class.java) + startActivityForResult(intent, REQUEST_CODE) + } + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_CAMERA_CODE) { + ToastUtil.showToast("摄像头权限获取失败,扫码功能不可用!") + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", packageName, null) + startActivity(intent) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CODE) { + //处理扫描结果(在界面上显示) + if (null != data) { + val bundle = data.extras + if (bundle != null) { + if (bundle.getInt(CodeUtils.RESULT_TYPE) == CodeUtils.RESULT_SUCCESS) { + val result = bundle.getString(CodeUtils.RESULT_STRING) + Log.i("Zxing_Result", result!!) +// mTJMac = ToolUtil.formatMac(result!!) + mTJMac = result!! + FindNewEquipmentDialog.instance!!.getShareDialog(this, + mTJMac, + object : FindNewEquipmentDialog.PopupYearWindowCallBack { + //新增 + override fun doWork(roomName: String?, type: String?) { + if (!ToolUtil.isContainsTJData( + roomName!!, + type!!, + mTJMac, + this@MainActivity + ) + ) { + mPresenter.getbindDevice( + this@MainActivity, + mTJMac, + roomName!!, + type!! + ) + + } else { + ToastUtil.showToast("请勿重复添加") + } + } + + //修改 + override fun doChangeWork(roomName: String?, type: String?) { + if (!ToolUtil.isContainsTJData( + roomName!!, + type!!, + mTJMac, + this@MainActivity + ) + ) { + mPresenter.getChangeDevice( + this@MainActivity, + mTJMac, + roomName, + type + ) + } else { + ToastUtil.showToast("请勿重复添加") + } + } + + //解绑 + override fun doUnBindWork() { + mPresenter.removebindDevice(this@MainActivity, mTJMac) + } + + override fun doConnectTj() { + connectTjToSettingMode() + } + + override fun doDismiss() { + isNeedRetry = false + } + + }) + + } else if (bundle.getInt(CodeUtils.RESULT_TYPE) == CodeUtils.RESULT_FAILED) { + ToastUtil.showToast("解析二维码失败") + } + } + } + } + } + + + //修改信息 + override fun showChangeResult(messModel: Any?, mac: String, roomName: String?, type: String?) { + ToastUtil.showToast("修改成功") + FindNewEquipmentDialog.instance!!.bindSuccess() + //修改本地该条记录 + ToolUtil.ChangeHistory( + this, + DataServer.mBindHistory, + mac, BindBlueToothData( + mac, + BlueToothUtils.instance!!.getBlueToothPower(mac), + roomName!!, + type!!, TimeUtil.getNowTime()!! + ) + ) + //修改该贴件接收蓝牙数据里面的type类型 (震动型、移动型) + ToolUtil.changeMacBlueToothHistory(mac, this) + + var list = ObjectBoxUtils.getDeviceByMac(mac) + var deviceInfo:DeviceInfo?; + if (list != null && list.isNotEmpty()) { + deviceInfo = list[0] + deviceInfo!!.isBind = true + deviceInfo!!.name = roomName!! + deviceInfo!!.type = type!! + } else { + deviceInfo = DeviceInfo() + deviceInfo.mac = mac + deviceInfo.isBind = true + deviceInfo.name = roomName!! + deviceInfo.type = type!! + } + ObjectBoxUtils.updateData(deviceInfo, DeviceInfo::class.java) + } + + //新增绑定 + override fun showAddResult(messModel: Any?, mac: String, type: String?, roomName: String?) { + ToastUtil.showToast("绑定成功") + FindNewEquipmentDialog.instance!!.bindSuccess() + //存本地 + ToolUtil.addHistory( + this@MainActivity, + DataServer.mBindHistory, + BindBlueToothData( + mac, + BlueToothUtils.instance!!.getBlueToothPower(mac), + roomName!!, + type!!, TimeUtil.getNowTime()!! + ) + ) + + var list = ObjectBoxUtils.getDeviceByMac(mac) + var deviceInfo:DeviceInfo?; + if (list != null && list.isNotEmpty()) { + deviceInfo = list[0] + deviceInfo!!.isBind = true + deviceInfo!!.time = getNowTime()!! + deviceInfo!!.name = roomName!! + deviceInfo!!.type = type!! + } else { + deviceInfo = DeviceInfo() + deviceInfo.mac = mac + deviceInfo.isBind = true + deviceInfo.name = roomName!! + deviceInfo.type = type!! + deviceInfo.time = getNowTime()!! + } + ObjectBoxUtils.updateData(deviceInfo, DeviceInfo::class.java) + } + + + //解除绑定 + override fun showRemoveResult(messModel: Any?, mac: String) { + ToastUtil.showToast("解除绑定成功") + ObjectBoxUtils.deleteALLDataD(ObjectBoxUtils.getDeviceByMac(mac)) + ObjectBoxUtils.deleteALLDataA(ObjectBoxUtils.getActionByMac(mac)) + FindNewEquipmentDialog.instance!!.setDismiss() + //清除本地贴件连接的该条记录 + ToolUtil.deleteHistory( + this@MainActivity, + DataServer.mBindHistory, + mac + ) + //将本地存储的蓝牙接收数据中的该贴件的信息删除 + ToolUtil.clearMacBlueToothHistory(mac) + + + } + + //获取token + override fun showTokenResult(messModel: Any?) { + var model = messModel as GetTokenM + mToken = model.token + DataServer.mHttpToken = mToken + askPermissionOfCamera() + } + + override fun showAfter() { + } + + override fun onResume() { + super.onResume() + isResume = true + } + + override fun onPause() { + super.onPause() + isResume = false + } + + + override fun onDestroy() { + FindNewEquipmentDialog.instance!!.setDismiss() + super.onDestroy() + } + + private fun initG2() { + NIMClient.getService(AuthServiceObserver::class.java).observeOnlineStatus({ statusCode -> + if (statusCode == StatusCode.LOGINED) { + val options: CallKitUIOptions = CallKitUIOptions.Builder() // 音视频通话 sdk appKey,用于通话中使用 + .rtcAppKey(BuildConfig.APP_KEY) // 当前用户 accId + .currentUserAccId(ProfileManager.getInstance().userModel.imAccid) // 通话接听成功的超时时间单位 毫秒,默认30s + .timeOutMillisecond(30 * 1000L) // 当系统版本为 Android Q及以上时,若应用在后台系统限制不直接展示页面 + // 而是展示 notification,通过点击 notification 跳转呼叫页面 + // 此处为 notification 相关配置,如图标,提示语等。 + .notificationConfigFetcher(SelfNotificationConfigFetcher()) // 收到被叫时若 app 在后台,在恢复到前台时是否自动唤起被叫页面,默认为 true + .resumeBGInvitation(true) // 请求 rtc token 服务,若非安全模式不需设置,安全模式按照官网实现 token 服务通过如下接口设置回组件 + // .rtcTokenService(new TokenService() { + // @Override + // public void getToken(long uid, RequestCallback callback) { + // Result result = network.requestToken(uid); + // if (result.success) { + // callback.onSuccess(result.token); + // } else if (result.exception != null) { + // callback.onException(result.exception); + // } else { + // callback.onFailed(result.code); + // } + // } + // }) + // 设置初始化 rtc sdk 相关配置,按照所需进行配置 + .rtcSdkOption(NERtcOption()) // 设置用户信息 + .userInfoHelper(SelfUserInfoHelper()) + .build() + // 若重复初始化会销毁之前的初始化实例,重新初始化 + init(applicationContext, options) + } + }, true) + } + + private fun checkLogin() { + if (ProfileManager.getInstance().isLogin) { + return + } + //此处注册之后会立刻回调一次 + NIMClient.getService(AuthServiceObserver::class.java).observeOnlineStatus( + Observer { statusCode: StatusCode -> + if (statusCode == StatusCode.LOGINED) { + ProfileManager.getInstance().isLogin = true +// CallOrderManager.getInstance().init() + } + } as Observer, true) + } + + override fun receiveEvent(event: EventBusUtil.MessageEvent) { + super.receiveEvent(event) + when (event.code) { + /** + * 贴件唤醒成功 + */ + EventCode.DEVICE_WAKE_UP_SUCCEED -> { + if(isResume) { + if (WakeUpDeviceDialog.instance!!.isShowing()) { + WakeUpDeviceDialog.instance!!.setDismiss() + FindNewEquipmentDialog.instance!!.setDismiss() + countDownTimer!!.cancel() + countDownTimer = null + startActivity( + Intent( + this@MainActivity, + SettingModeActivity::class.java + ).putExtra("mac", mTJMac) + ) + } + } + } + } + } + + private fun connectTjToSettingMode(){ + if(DataServer.connectMode==2) { + if (!ViseBle.getInstance().deviceMirrorPool.isConnectDevice) { + FindNewEquipmentDialog.instance!!.connectFail() + return + } + } +// startActivity(Intent(this@MainActivity, SettingModeActivity::class.java)) + WakeUpDeviceDialog.instance!!.getShareDialog(this@MainActivity, + object : WakeUpDeviceDialog.PopupYearWindowCallBack { + override fun doCancel() { + countDownTimer!!.cancel() + countDownTimer = null + DataServer.nBlueToothVerifyMac = "" + DataServer.nBlueToothMac = "" + } + }) + //广播模式下,需要连接贴件 + if(DataServer.connectMode==1){ + DataServer.nBlueToothMac = mTJMac + } + DataServer.nBlueToothVerifyMac = mTJMac + countDownTimer = object : CountDownTimer(60 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("进入配置模式失败,请确认贴件状态,并重新进入") + WakeUpDeviceDialog.instance!!.setDismiss() + DataServer.nBlueToothVerifyMac = "" + countDownTimer = null + Handler().postDelayed({ + if(isNeedRetry&&!WakeUpDeviceDialog.instance!!.isShowing()) + connectTjToSettingMode() + },120*1000) + } + + override fun onTick(millisUntilFinished: Long) { + } + }.start() + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/BluetoothActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/BluetoothActivity.kt new file mode 100644 index 0000000..dad1afc --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/BluetoothActivity.kt @@ -0,0 +1,270 @@ +package com.qidian.zhongkesmart.activity + +import android.Manifest +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Color +import android.net.Uri +import android.net.wifi.ScanResult +import android.os.Build +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.os.Environment +import android.os.Handler +import android.provider.Settings +import android.text.TextUtils +import android.util.Log +import android.view.View +import android.widget.CheckBox +import com.qidian.baseble.ViseBle +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.baseble.ble.ConnectEvent +import com.qidian.baseble.callback.scan.IScanCallback +import com.qidian.baseble.callback.scan.ScanCallback +import com.qidian.baseble.common.PropertyType +import com.qidian.baseble.model.BluetoothLeDevice +import com.qidian.baseble.model.BluetoothLeDeviceStore +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.AppApplication +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.bluetooth.dfu.DfuService +import com.qidian.zhongkesmart.config.Config +import com.qidian.zhongkesmart.dialog.InputThresholdDialog +import com.qidian.zhongkesmart.dialog.OpenmBlueToothDialog +import com.qidian.zhongkesmart.dialog.OpenmWifiDialog +import com.qidian.zhongkesmart.dialog.WifiPswDialog +import com.qidian.zhongkesmart.receiver.WifiReceiver +import com.qidian.zhongkesmart.utils.* +import com.vise.xsnow.event.Subscribe +import kotlinx.android.synthetic.main.activity_bluetooth.* +import kotlinx.android.synthetic.main.activity_wi_fi.* +import kotlinx.android.synthetic.main.activity_wi_fi.li_nowchoose +import kotlinx.android.synthetic.main.activity_wi_fi.recycle_wifi +import kotlinx.android.synthetic.main.activity_wi_fi.tv_nowchoose_name +import kotlinx.android.synthetic.main.layout_sys_bluetooth11.* +import kotlinx.android.synthetic.main.layout_sys_bluetooth2.* +import kotlinx.android.synthetic.main.layout_tiepain_details.* +import net.idik.lib.slimadapter.SlimAdapter +import net.idik.lib.slimadapter.SlimInjector +import net.idik.lib.slimadapter.viewinjector.IViewInjector +import no.nordicsemi.android.dfu.DfuServiceInitiator +import pub.devrel.easypermissions.EasyPermissions +import java.io.File + +class BluetoothActivity : BaseActivity(), EasyPermissions.PermissionCallbacks { + var wifiReceiver: WifiReceiver? = null + //wifi状态 默认关闭 0 开启 1 连接 2 + var IsOpen = 0 + var mData_BlueTooth = ArrayList() + var mData_BlueAddress = ArrayList() + var mAdapterBluetooth: SlimAdapter? = null + var mDevice: BluetoothLeDevice? = null + override fun getLayoutId(): Int { + return R.layout.activity_bluetooth + } + + override fun initView() { + LoadUtils.setLinearLayoutManager(this, recycle_wifi, false) + mAdapterBluetooth = + SlimAdapter.create() + .register( + R.layout.item_bluetooth_activity, + object : SlimInjector { + override fun onInject( + data: BluetoothLeDevice, + injector: IViewInjector> + ) { + injector.text(R.id.tv_wifi_name, BlueToothUtils.instance!!.getName(data)) + injector.clicked(R.id.li_wifi) { + mDevice = data + SPUtil.setValue(DataServer.MRelayMac,mDevice!!.address) + SPUtil.setValue(DataServer.MRelayName,mDevice!!.name) + li_nowchoose.visibility = View.VISIBLE + tv_nowchoose_name.text = BlueToothUtils.instance!!.getName(data) + tv_nowchoose_status.text = "连接中" + Handler().postDelayed({ + tv_nowchoose_status.text = "连接成功" +// startActivity(Intent(this@BluetoothActivity, HomeActivity::class.java)) + ActivityManager.getAppManager().finishAllActivity() + AppApplication.mContext!!.startActivity( + Intent( + AppApplication.mContext, + HomeActivity::class.java + ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ) + },2000) + + + } + + } + }) + .attachTo(recycle_wifi) + mAdapterBluetooth!!.updateData(mData_BlueTooth).notifyDataSetChanged() + + //注册监听Wifi连接广播 + askPermissionOfBluetooth() + //注册蓝牙广播 + this.registerReceiver(mReceiver, makeFilter()) + } + + override fun initData() { + } + + /** + * 获取蓝牙权限 + */ + private fun askPermissionOfBluetooth() { + EasyPermissions.requestPermissions( + this, + "请允许获取您的位置权限", + DataServer.ACCESS_BlueTooth_CODE, + Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION + ) + } + + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_BlueTooth_CODE) { + aboutlueTooth() + } + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_BlueTooth_CODE) { + ToastUtil.showToast("蓝牙权限获取失败,APP功能不可用!") + } + } + + /** + * 1、蓝牙是否打开 + * 1-1、未打开-提示打开 + * + * 1-2、打开-跳转 + */ + fun aboutlueTooth() { + //1、未打开蓝牙-直接提示打开蓝牙 + if (!BlueToothUtils.instance!!.IsOpenBlueTooth()) { + BlueToothUtils.instance!!.OpenBuleTooth(this@BluetoothActivity) + } else { + //打开过 + startSearchDevices() + } + } + + //搜索设备 + fun startSearchDevices() { + mData_BlueAddress.clear() + mData_BlueTooth.clear() + mAdapterBluetooth!!.notifyDataSetChanged() + showDialog() + startScan() + } + + //注册蓝牙监听广播 + /** + * 蓝牙广播过滤器 + * 蓝牙状态改变 + * 找到设备 + * 搜索完成 + * 开始扫描 + * 状态改变 + */ + fun makeFilter(): IntentFilter? { + val filter = IntentFilter() + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED) //蓝牙状态改变的广播 + return filter + } + + //连接成功 li_bluetooth_connect.visibility = View.VISIBLE + private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + when (action) { + //蓝牙状态改变的广播 + BluetoothAdapter.ACTION_STATE_CHANGED -> { + Log.i("BlueTooth==", "ACTION_STATE_CHANGED:蓝牙状态改变") + var blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0) + when (blueState) { + //蓝牙-2、扫描 + BluetoothAdapter.STATE_ON -> { + Log.i("BlueTooth==", "蓝牙打开") + startSearchDevices() + } + //蓝牙关闭 + BluetoothAdapter.STATE_ON -> { + Log.i("BlueTooth==", "蓝牙已经关闭") + } + } + } + } + } + } + + /** + * 开始扫描 + */ + private fun startScan() { + //蓝牙相关配置修改 + ViseBle.config().scanTimeout = 12*1000 //扫描超时时间,-1设置为永久扫描 +// ViseBle.config().scanTimeout = -1 //扫描超时时间,-1设置为永久扫描 + ViseBle.getInstance().startScan(periodScanCallback) + } + + fun onClick(v: View) { + when (v.id) { + /** + * 贴片详情 + */ + R.id.iv_back -> { + onBackPressed() + } + } + } + + /** + * 扫描回调 + */ + private val periodScanCallback: ScanCallback = ScanCallback(object : IScanCallback { + override fun onDeviceFound(bluetoothLeDevice: BluetoothLeDevice) { + if(bluetoothLeDevice.name!=null&&bluetoothLeDevice.name.startsWith("ZJ",false)) { + if (!TextUtils.isEmpty(bluetoothLeDevice.address)) { + if (!mData_BlueAddress.contains(bluetoothLeDevice.address)) { + dismissDialog() + mData_BlueTooth.add(bluetoothLeDevice) + mData_BlueAddress.add(bluetoothLeDevice.address) + mAdapterBluetooth!!.notifyDataSetChanged() + Log.i( + "BlueTooth==", + "periodScanCallback:发现设备" + BlueToothUtils.instance!!.getName( + bluetoothLeDevice + ) + ) + } + } + } + } + + override fun onScanFinish(bluetoothLeDeviceStore: BluetoothLeDeviceStore?) { + Log.i("BlueTooth==", "periodScanCallback:扫描结束") + dismissDialog() + } + + override fun onScanTimeout() { + Log.i("BlueTooth==", "periodScanCallback:扫描超时") + dismissDialog() + } + + }) + + override fun onDestroy() { + if(periodScanCallback!=null) { + ViseBle.getInstance().stopScan(periodScanCallback) + } + this.unregisterReceiver(mReceiver) + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/CaptureActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/CaptureActivity.kt new file mode 100644 index 0000000..5084e6c --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/CaptureActivity.kt @@ -0,0 +1,56 @@ +package com.qidian.zhongkesmart.activity + +import android.os.Bundle +import com.qidian.zxing.lib_zxing.activity.CaptureFragment +import com.qidian.zxing.lib_zxing.activity.CodeUtils +import android.content.Intent +import android.graphics.Bitmap +import android.app.Activity +import android.hardware.Camera +import android.view.Surface +import com.gyf.immersionbar.ImmersionBar +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity + + +class CaptureActivity : BaseActivity() { + + override fun getLayoutId(): Int { + return R.layout.activity_capture + } + + override fun initView() { + ImmersionBar.with(this).reset().statusBarDarkFont(true).init() +// ImmersionBar.with(this).statusBarDarkFont(isWhiteTheme).fitsSystemWindows(true).init() + val captureFragment = CaptureFragment() + CodeUtils.setFragmentArgs(captureFragment, R.layout.my_camera) + captureFragment.analyzeCallback = analyzeCallback + supportFragmentManager.beginTransaction().replace(R.id.fl_my_container, captureFragment) + .commit() + } + + override fun initData() { + } + + private var analyzeCallback: CodeUtils.AnalyzeCallback = object : CodeUtils.AnalyzeCallback { + override fun onAnalyzeSuccess(mBitmap: Bitmap, result: String) { + val resultIntent = Intent() + val bundle = Bundle() + bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_SUCCESS) + bundle.putString(CodeUtils.RESULT_STRING, result) + resultIntent.putExtras(bundle) + this@CaptureActivity.setResult(Activity.RESULT_OK, resultIntent) + this@CaptureActivity.finish() + } + + override fun onAnalyzeFailed() { + val resultIntent = Intent() + val bundle = Bundle() + bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_FAILED) + bundle.putString(CodeUtils.RESULT_STRING, "") + resultIntent.putExtras(bundle) + this@CaptureActivity.setResult(Activity.RESULT_OK, resultIntent) + this@CaptureActivity.finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/ConnectModeActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/ConnectModeActivity.kt new file mode 100644 index 0000000..51875c7 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/ConnectModeActivity.kt @@ -0,0 +1,83 @@ +package com.qidian.zhongkesmart.activity + +import android.Manifest +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.text.TextUtils +import android.util.Log +import android.view.View +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.net.ApiUrl +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.activity_connect_mode.* +import kotlinx.android.synthetic.main.activity_service.* +import pub.devrel.easypermissions.EasyPermissions + +class ConnectModeActivity : BaseActivity(),EasyPermissions.PermissionCallbacks { + + var mode = 1 + override fun getLayoutId(): Int { + return R.layout.activity_connect_mode + } + + override fun initView() { + } + + override fun initData() { + askPermissionOfBluetooth() + } + + fun onClick(v: View) { + when (v.id) { + R.id.iv_direct -> { + mode = 1 + iv_direct.setImageResource(R.mipmap.direct_connect_mode_select_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_unselect_) + } + R.id.iv_agent -> { + mode = 2 + iv_direct.setImageResource(R.mipmap.direct_connect_mode_unselect_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_select_) + } + R.id.imv_jump -> { + SPUtil.setValue(DataServer.MConnectMode, mode) + DataServer.connectMode = mode + if(mode==2) { + var relayMac = SPUtil.getValue(DataServer.MRelayMac,String::class.java) + if(relayMac!=null&&!TextUtils.isEmpty(relayMac)) { + startActivity(Intent(this@ConnectModeActivity, HomeActivity::class.java)) + finish() + }else{ + startActivity(Intent(this@ConnectModeActivity, BluetoothActivity::class.java)) + } + }else{ + startActivity(Intent(this@ConnectModeActivity, HomeActivity::class.java)) + finish() + } + } + } + } + + /** + * 获取蓝牙权限 + */ + private fun askPermissionOfBluetooth() { + EasyPermissions.requestPermissions( + this, + "请允许获取您的位置权限", + DataServer.ACCESS_BlueTooth_CODE, + Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION + ) + } + + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_BlueTooth_CODE) { + ToastUtil.showToast("蓝牙权限获取失败,APP功能不可用!") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/HomeActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/HomeActivity.kt new file mode 100644 index 0000000..8839a95 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/HomeActivity.kt @@ -0,0 +1,1540 @@ +package com.qidian.zhongkesmart.activity + +import android.Manifest +import android.annotation.SuppressLint +import android.bluetooth.BluetoothAdapter +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Color +import android.graphics.drawable.AnimationDrawable +import android.graphics.drawable.Drawable +import android.net.ConnectivityManager +import android.net.Uri +import android.net.wifi.WifiManager +import android.os.CountDownTimer +import android.os.Handler +import android.os.Message +import android.os.SystemClock +import android.provider.Settings +import android.service.autofill.Dataset +import android.text.TextUtils +import android.util.Log +import android.view.MotionEvent +import android.view.View +import android.view.View.OnTouchListener +import androidx.vectordrawable.graphics.drawable.Animatable2Compat +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption +import com.amap.api.location.AMapLocationListener +import com.blankj.utilcode.util.TimeUtils +import com.blankj.utilcode.util.ToastUtils +import com.bumptech.glide.Glide +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.load.resource.gif.GifDrawable +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target +import com.google.gson.Gson +import com.qidian.baseble.ViseBle +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.baseble.ble.ConnectEvent +import com.qidian.baseble.ble.NotifyDataEvent +import com.qidian.baseble.callback.scan.IScanCallback +import com.qidian.baseble.callback.scan.ScanCallback +import com.qidian.baseble.common.PropertyType +import com.qidian.baseble.model.BluetoothLeDevice +import com.qidian.baseble.model.BluetoothLeDeviceStore +import com.qidian.baseble.utils.HexUtil +import com.qidian.zhongkesmart.MainActivity +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.dialog.FindNewEquipmentDialog +import com.qidian.zhongkesmart.dialog.HomeDeviceDialog +import com.qidian.zhongkesmart.dialog.WifiDisconnectDialog +import com.qidian.zhongkesmart.model.* +import com.qidian.zhongkesmart.net.ApiUrl +import com.qidian.zhongkesmart.net.HttpCallBack +import com.qidian.zhongkesmart.net.HttpRequest +import com.qidian.zhongkesmart.receiver.NetworkChange +import com.qidian.zhongkesmart.receiver.WifiReceiver +import com.qidian.zhongkesmart.utils.* +import com.qweather.sdk.bean.base.Code +import com.qweather.sdk.bean.base.Lang +import com.qweather.sdk.bean.base.Unit +import com.qweather.sdk.bean.weather.WeatherNowBean +import com.qweather.sdk.view.QWeather +import com.vise.xsnow.event.Subscribe +import kotlinx.android.synthetic.main.activity_model_init.* +import kotlinx.android.synthetic.main.activity_service.* +import kotlinx.android.synthetic.main.layout_home.* +import kotlinx.android.synthetic.main.layout_standby.* +import kotlinx.android.synthetic.main.layout_standby.imv_standby +import kotlinx.android.synthetic.main.layout_standby.tv_family_name +import kotlinx.android.synthetic.main.layout_standby.tv_standby_time +import kotlinx.android.synthetic.main.layout_standby.tv_standby_timelong +import kotlinx.android.synthetic.main.layout_standby.tv_standby_weather +import kotlinx.android.synthetic.main.layout_sys_bluetooth2.* +import kotlinx.android.synthetic.main.layout_tiepain_details.* +import pub.devrel.easypermissions.EasyPermissions + + +class HomeActivity : BaseActivity(), EasyPermissions.PermissionCallbacks { +// var flowlayoutAdapter: TagAdapter? = null +// var mStansByList = ArrayList() + var drawable01: AnimationDrawable? = null + var drawable02: AnimationDrawable? = null + var showWinkAnnimationTime = 0 +// var imv_stansby: ImageView? = null + + var getTimeNum = 0 + var warnType = "" + var wifiReceiver: WifiReceiver? = null + //网关心跳计时 + var heartbeatTime = 0 + + var mPosX:Float = 0f + var mPosY:Float = 0f + var mCurPosX:Float = 0f + var mCurPosY:Float = 0f + var mStansByList = java.util.ArrayList() + + //点击次数 + var count = 3 + + //规定的有效时间 + var time: Long = 2000 + var mHits = LongArray(count) + + var isShowing = false + + var mCode = "" + var mToken = "" + + override fun getLayoutId(): Int { + return R.layout.activity_home + } + + override fun isExitApp(): Boolean { + return true + } + + override fun initView() { + DataServer.connectMode = SPUtil.getValue(DataServer.MConnectMode,Integer::class.java).toInt() + //注册监听 + registerBroadcast() + //定位权限开启后-开启定位 + initMapLocation() + + askPermissionOfBluetooth() + registertNetConnect() + //注册蓝牙广播 + this.registerReceiver(mReceiver, makeFilter()) + DataServer.standByPageIsShow = true + handler.sendEmptyMessageDelayed(0, 1000) + tv_standby_time.text = TimeUtil.getNowTimeHourMinute() + tv_standby_timelong.text = + "${TimeUtil.getNowTimeMonthDayFormat()} ${TimeUtil.getWeekDay(System.currentTimeMillis())}"; + + ScreenUtils.getScreenHeight() + li_standby.setOnTouchListener(OnTouchListener { v, event -> // TODO Auto-generated method stub + when (event.action) { + MotionEvent.ACTION_DOWN -> { + mPosX = event.x + mPosY = event.y + } + MotionEvent.ACTION_MOVE -> { + mCurPosX = event.x + mCurPosY = event.y + } + MotionEvent.ACTION_UP -> { + if (mCurPosY - mPosY > 0 && Math.abs(mCurPosY - mPosY) > 25) { + //向下滑動 + } else if (mCurPosY - mPosY < 0 && Math.abs(mCurPosY - mPosY) > 25) { + //向上滑动 + HomeDeviceDialog.instance!!.setDismiss() + } + confirmPosition(event) + //每次点击时,数组向前移动一位 + System.arraycopy(mHits, 1, mHits, 0, mHits.size - 1) + //为数组最后一位赋值 + mHits[mHits.size - 1] = SystemClock.uptimeMillis() + if (mHits[0] >= (SystemClock.uptimeMillis() - time)) { + //数组重新初始化 + mHits=LongArray(count) + startBlinkAnimation(R.drawable.dizzy) + } + } + } + true + }) + +// imv_standby.setOnClickListener{ +// showWinkAnnimationTime = 0 +// startBlinkAnimation(R.drawable.smile) +// //每次点击时,数组向前移动一位 +// System.arraycopy(mHits, 1, mHits, 0, mHits.size - 1) +// //为数组最后一位赋值 +// mHits[mHits.size - 1] = SystemClock.uptimeMillis() +// if (mHits[0] >= (SystemClock.uptimeMillis() - time)) { +// //数组重新初始化 +// mHits=LongArray(count) +// startBlinkAnimation(R.drawable.dizzy) +// } +// } + + + ll_left.setOnClickListener{ + var dList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) + var online = 0 + dList!!.forEach{ + if(it.isBind) { + online++ + } + } + if(online>0) { + HomeDeviceDialog.instance!!.getShareDialog(this, + object : HomeDeviceDialog.PopupYearWindowCallBack { + override fun doWork() { + startActivity( + Intent( + this@HomeActivity, + MyEquipmentActivity::class.java + ) + ) + } + }) + }else{ + ToastUtil.showToast("您还未绑定网关") + // SJF 20250328 点击智能贴件,弹出未绑定网关。 + } +// var list1 = ObjectBoxUtils.getAllData(DeviceInfo::class.java) +// var list2 = ObjectBoxUtils.getAllData(ActionInfo::class.java) +// list1!!.forEach { +// Log.i("objectbox",it.toString()) +// } +// list2!!.forEach { +// Log.i("objectbox",it.toString()) +// } + } + + ll_right.setOnClickListener{ +// ObjectBoxUtils.deleteALLDataD(ObjectBoxUtils.getDeviceByMac("C5:76:AB:D0:3B:D5")) +// ObjectBoxUtils.deleteALLDataA(ObjectBoxUtils.getActionByMac("C5:76:AB:D0:3B:D5")) + startActivity(Intent(this, MainActivity::class.java)) + } + + } + + override fun initData() { + ApiUrl.setDomain(ToolUtil.getIpStr("https://ifhs.fedhealth.cn/","")) + if (DataServer.mHttpToken.isNullOrEmpty()) { + getHttpCode() + } else { + getLoginSuccess() + } + } + + + +// fun showStandByPage() { +// mStansByList.clear() +// mStansByList.addAll(ToolUtil.getHistory(DataServer.mBindHistory, this)) +// flowlayoutAdapter = object : TagAdapter(mStansByList) { +// override fun getView(parent: FlowLayout?, position: Int, s: BindBlueToothData): View? { +// val view: View = LayoutInflater.from(this@HomeActivity).inflate( +// R.layout.item_standby, null +// ) +// imv_stansby = view.findViewById(R.id.imv_stansby) +// var tv_stansby = view.findViewById(R.id.tv_stansby) +// var tv_mark = view.findViewById(R.id.tv_mark) +// tv_stansby.text = s.type +// imv_stansby!!.setImageResource(ToolUtil.getDJPageImage(s.type)) +// if (ToolUtil.getTJIsOnLineState(s.mac)) { +// tv_mark.setBackgroundResource(R.drawable.ed_yuan0fba61) +// } else { +// tv_mark.setBackgroundResource(R.drawable.ed_yuangray) +// } +// +// return view +// } +// +// override fun setSelected(position: Int, s: BindBlueToothData): Boolean { +// return s == s +// } +// } +// id_flowlayout.adapter = flowlayoutAdapter +// id_flowlayout.setOnTagClickListener { view, position, parent -> +// true +// } +// startStandByAnimation01() +// if (mStansByList.isEmpty()) { +// li_standby_background.visibility = View.GONE +// } else { +// li_standby_background.visibility = View.VISIBLE +// } +// } + + //先显示眨眼睛动画 30分钟后显示一次左右看 + fun startStandByAnimation01() { +// drawable01 = resources?.getDrawable(R.drawable.standbyanimation01) as AnimationDrawable +// // 放入imageView +// imv_standby!!.setBackgroundDrawable(drawable01) +// // 开始 +// drawable01!!.start() +// showWinkAnnimationTime = 0 + } + + /** + * 开启一条线程 + * 1、记录震动型开关 + * 震动型型处理方式: + * 主要判断的是震动传感器的有值与否,当震动传感器有值的时候认为该场景为开启状态,当震动传感器X秒不在传值时认为该场景为关闭状态; + * 开启的持续时间为两次之间的差; + */ + @SuppressLint("HandlerLeak") + var handler: Handler = object : Handler() { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + this.removeMessages(0) + sendEmptyMessageDelayed(0, 1000) + getTime() +// Log.i("Handle==", "ing") + aboutBlinkAnimation() +// aboutWindAnimation() + + if (!ToolUtil.getHistory(DataServer.mBindHistory, this@HomeActivity) + .isNullOrEmpty() + ) { + // 处理震动类型-监听开启时长-处理关闭 +// getVibrationState() + //定时任务-报警 +电量低 +// Alarm() + AlarmNew() + //离线 每天00:00 时要求贴件 定时发送一次数据,当网关未收到贴件上传的数据时认为该贴件为离线状态 +// OffLine() + OffLineNew() + } + //网关心跳 + aboutheartbeat() + } + } + + fun getTime() { + getTimeNum++ + if (getTimeNum == 30) { + tv_standby_time.text = TimeUtil.getNowTimeHourMinute() + tv_standby_timelong.text = + "${TimeUtil.getNowTimeMonthDayFormat()} ${TimeUtil.getWeekDay(System.currentTimeMillis())}"; + getTimeNum = 0 + } + } + + + fun aboutWindAnimation() { + showWinkAnnimationTime++ + if (showWinkAnnimationTime * 1000 == DataServer.mShowWinkAnnimationTimeBZ) { + showWinkAnnimationTime = 0 + startStandByAnimation02() + } + } + + //30分钟后显示一次左右看 + fun startStandByAnimation02() { +// drawable02 = resources?.getDrawable(R.drawable.standbyanimation02) as AnimationDrawable +// // 放入imageView +// imv_standby!!.setBackgroundDrawable(drawable02) +// // 开始 +// drawable02!!.start() + } + + fun aboutBlinkAnimation() { + showWinkAnnimationTime++ + if (showWinkAnnimationTime ==DataServer.mShowWinkAnnimationTime) { + startBlinkAnimation(R.drawable.blink) + } + } + + fun startBlinkAnimation(gifIndex:Int){ + iv_standby_default.visibility = View.GONE + Glide.with(this).asGif().load(gifIndex) + .addListener(object :RequestListener{ + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + return false + } + + override fun onResourceReady( + resource: GifDrawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + resource!!.setLoopCount(1) + resource!!.registerAnimationCallback(object :Animatable2Compat.AnimationCallback(){ + override fun onAnimationEnd(drawable: Drawable?) { + super.onAnimationEnd(drawable) + showWinkAnnimationTime = 0 + } + }) + return false + } + + }) + .into(imv_standby); + } + + fun confirmPosition(event:MotionEvent){ + showWinkAnnimationTime = 0 + var width = ScreenUtils.getScreenWidth() + var height = ScreenUtils.getScreenHeight() +// Log.e(TAG,"$width $height") + if(event.x<=width/4){ + if(event.y<=height/9*4){ + startBlinkAnimation(R.drawable.look_up_left) +// }else if(event.y<=height/3*2){ +// startBlinkAnimation(R.drawable.look_left) + }else{ + startBlinkAnimation(R.drawable.look_down) + } + }else if(event.x<=width/4*3){ + if(event.y<=height/9*4){ + startBlinkAnimation(R.drawable.look_up) +// }else if(event.y<=height/3*2){ +// startBlinkAnimation(R.drawable.blink2) + }else{ + startBlinkAnimation(R.drawable.smile) + } + }else{ + if(event.y<=height/9*4){ + startBlinkAnimation(R.drawable.look_up_right) +// }else if(event.y<=height/3*2){ +// startBlinkAnimation(R.drawable.look_right) + }else{ + startBlinkAnimation(R.drawable.look_down_right) + } + } + } + + override fun isImmersion(): Boolean { + return true + } + + override fun onResume() { + super.onResume() +// DataServer.MBlueToothData = ToolUtil.getBlueToothHistory() + isShowing = true + if (DataServer.mHttpToken.isNullOrEmpty()) { +// ToastUtil.showToast("您还未绑定网关") + } else { + getFamilyInfo() + } + + if(!handler.hasMessages(0)){ + handler.removeMessages(0) + handler.sendEmptyMessageDelayed(0, 1000) + Log.i("onResume", "sendEmptyMessageDelayed") + } +// getDevicesNew() + } + + override fun onPause() { + super.onPause() + isShowing = false + } + + override fun onDestroy() { + NetworkChange.unRegisterReceiver(this) + this.unregisterReceiver(mReceiver) + if(periodScanCallback!=null) { + ViseBle.getInstance().stopScan(periodScanCallback) + } + super.onDestroy() + //停止动画 +// if (drawable01 != null && drawable01!!.isRunning) { +// drawable01?.stop() +// } + //停止动画 +// if (drawable02 != null && drawable02!!.isRunning) { +// drawable02?.stop() +// } + DataServer.standByPageIsShow = false + try { + colse() + } catch (e: Exception) { + } + } + + fun colse() { + //关闭蓝牙相关 + BluetoothDeviceManager.getInstance().disconnect() + ViseBle.getInstance().clear() + //关闭wifi监听 + if (wifiReceiver != null) { + unregisterReceiver(wifiReceiver) + } + FindNewEquipmentDialog.instance!!.setDismiss() + //结束定时线程 + if (handler.hasMessages(0)) { + handler.removeMessages(0) + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult( + requestCode, + permissions, + grantResults, + this + )//1001 + } + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_WiFi_CODE) { + //定位权限开启后-开启定位 + initMapLocation() + } else if (requestCode == DataServer.ACCESS_BlueTooth_CODE ){ + //蓝牙未打开 + if (!BlueToothUtils.instance!!.IsOpenBlueTooth()) { + BlueToothUtils.instance!!.OpenBuleTooth(this@HomeActivity) + } else { + startScan() + } + } + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_WiFi_CODE) { + ToastUtil.showToast("WiFi权限获取失败,APP功能不可用!") + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", packageName, null) + startActivity(intent) + }else if (requestCode == DataServer.ACCESS_BlueTooth_CODE) { + ToastUtil.showToast("蓝牙权限获取失败,APP功能不可用!") + } + } + + fun getFamilyInfo(){ + HttpRequest.init(this).get(ApiUrl.getFamilyInfo) + .setClazz(FamilyInfo::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + var familyInfo = `object` as FamilyInfo + tv_family_name.text = familyInfo.familyName + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("$code $info") + return super.failed(code, info) + } + }) + } + + /** + * 全局监听wifi + * 注册Wifi监听广播 + */ + fun registerBroadcast() { + wifiReceiver = WifiReceiver() + val filter = IntentFilter() + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION) + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) + filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) + this.registerReceiver(wifiReceiver, filter) + } + + //声明mLocationOption对象 + var mLocationOption: AMapLocationClientOption? = null + + //声明mlocationClient对象 + var mlocationClient: AMapLocationClient? = null + + /** + * 高德地图-待机页面天气预报需要 + */ + fun initMapLocation() { + AMapLocationClient.updatePrivacyShow(application, true, true) + AMapLocationClient.updatePrivacyAgree(application, true) + mlocationClient = AMapLocationClient(this) + //初始化定位参数 + mLocationOption = AMapLocationClientOption() + //设置定位监听 + mlocationClient!!.setLocationListener(mLocationListener) + //设置定位模式为高精度模式,Battery_Saving为低功耗模式,Device_Sensors是仅设备模式 + mLocationOption!!.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + //设置只定位一次 +// mLocationOption!!.isOnceLocation = true + //设置定位间隔,单位毫秒,默认为3分钟 重新开启定位权限后能及时刷新定位数据 180000 + mLocationOption!!.interval = 180000 + //设置定位参数 + mlocationClient!!.setLocationOption(mLocationOption) + // 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗, + // 注意设置合适的定位时间的间隔(最小间隔支持为1000ms),并且在合适时间调用stopLocation()方法来取消定位请求 + // 在定位结束后,在合适的生命周期调用onDestroy()方法 + // 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除 + //启动定位 + mlocationClient!!.startLocation() + } + + private val mLocationListener = AMapLocationListener { amapLocation -> + if (amapLocation != null) { + if (amapLocation.errorCode == 0) { //定位成功回调信息,设置相关消息 +// mLatitude = amapLocation.latitude.toString()//获取纬度 +// mLongitude = amapLocation.longitude.toString()//获取经度 + Log.i("定位==", amapLocation.city) + getHFWeather("${amapLocation.longitude},${amapLocation.latitude}") + }else{ + Log.i("定位==查询失败", amapLocation.errorCode.toString()) + } + } + } + + /** + * 和风天气预报 + */ + fun getHFWeather(str:String){ + /** "CN101010100", 116.41,39.92 + * 实况天气数据 + * @param location 所查询的地区,可通过该地区ID、经纬度进行查询经纬度格式:经度,纬度 + * (英文,分隔,十进制格式,北纬东经为正,南纬西经为负) + * @param lang (选填)多语言,可以不使用该参数,默认为简体中文 + * @param unit (选填)单位选择,公制(m)或英制(i),默认为公制单位 + * @param listener 网络访问结果回调 + */ + + QWeather.getWeatherNow( + this@HomeActivity, + str, + Lang.ZH_HANS, + Unit.METRIC, + object : QWeather.OnResultWeatherNowListener { + override fun onError(e: Throwable) { + Log.i("天气预报==", "getWeather onError: $e") + } + + override fun onSuccess(weatherBean: WeatherNowBean) { + Log.i("天气预报==", "getWeather onSuccess: " + Gson().toJson(weatherBean)) + //先判断返回的status是否正确,当status正确时获取数据,若status不正确,可查看status对应的Code值找到原因 + if (Code.OK === weatherBean.code) { + val now = weatherBean.now + DataServer.mWeatherData = now.text + runOnUiThread { + tv_temp.text = weatherBean.now.temp+"℃" + tv_standby_weather.text = DataServer.mWeatherData + } + + } else { + //在此查看返回数据失败的原因 + val code: Code = weatherBean.code + Log.i("天气预报==", "failed code: $code") + } + } + }) + } + + fun getDevices(){ + mStansByList.clear() + mStansByList.addAll(ToolUtil.getHistory(DataServer.mBindHistory,this)) + var online = 0 + var offline = 0 + mStansByList.forEach{ + if (ToolUtil.getTJIsOnLineState(it.mac)) { + online++ + } else { + offline++ + } + } + tv_online.text = "在线 $online" + tv_offline.text = "离线 $offline" + } + + fun getDevicesNew(){ + var dList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) + var online = 0 + var offline = 0 + dList!!.forEach{ + if(it.isBind) { + if (it.isOnline) { + online++ + } else { + offline++ + } + } + } + tv_online.text = "在线 $online" + tv_offline.text = "离线 $offline" + } + + //网关心跳接口 + fun aboutheartbeat() { + heartbeatTime++ + if (heartbeatTime * 1000 == DataServer.mHeartbeatTime) { + HttpRequest.init(this).post(ApiUrl.heartbeat) + .setShowDialog(false) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("网关心跳==", "success") + } + + }) + heartbeatTime = 0 + } + } + + //震动型-处理关闭逻辑 x秒后没有接收到数据 +// fun getVibrationState() { +// var mDisplacementData = BlueToothUtils.instance!!.getDisplacementData() +// mDisplacementData.forEach { +// if (it.open) { +// var mOpenTime =TimeUtil.getNowTimeLong()!!- it.tomeRecord[it.tomeRecord.size - 1].openTime +// var lateDataOpenTime = it.lateDataOpenTime +// var timeLength = TimeUtil.getNowTimeLong()!! - lateDataOpenTime +// if (timeLength > DataServer.mVibrationCloseTime) {//超时则默认关闭 +// it.open = false +// it.showAlarm = false//状态改变后 就重置为默认状态 +// //更新状态变化时间 +// it.tomeRecord[it.tomeRecord.size - 1].openTime = TimeUtil.getNowTimeLong()!! +// it.tomeRecord[it.tomeRecord.size - 1].timelength =mOpenTime +// Log.i("数据推送==", "关闭") +// EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET, it.mac)) +// +// //对震动型贴件来说,当状态改变为关闭时,检查是否有报警弹窗打开;如果有的话,关闭报警弹窗 +// if(DataServer.isShowAlarmMac(it.mac)){ +// DataServer.removeAlarmMac(it.mac) +// } +// } +// } +// } +// } + + //震动型-处理关闭逻辑 x秒后没有接收到数据 + fun getVibrationStateNew() { +// var mDisplacementData = BlueToothUtils.instance!!.getDisplacementData() +// mDisplacementData.forEach { +// if (it.open) { +// var mOpenTime =TimeUtil.getNowTimeLong()!!- it.tomeRecord[it.tomeRecord.size - 1].openTime +// var lateDataOpenTime = it.lateDataOpenTime +// var timeLength = TimeUtil.getNowTimeLong()!! - lateDataOpenTime +// if (timeLength > DataServer.mVibrationCloseTime) {//超时则默认关闭 +// it.open = false +// it.showAlarm = false//状态改变后 就重置为默认状态 +// //更新状态变化时间 +// it.tomeRecord[it.tomeRecord.size - 1].openTime = TimeUtil.getNowTimeLong()!! +// it.tomeRecord[it.tomeRecord.size - 1].timelength =mOpenTime +// Log.i("数据推送==", "关闭") +// EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET, it.mac)) +// +// //对震动型贴件来说,当状态改变为关闭时,检查是否有报警弹窗打开;如果有的话,关闭报警弹窗 +// if(DataServer.isShowAlarmMac(it.mac)){ +// DataServer.removeAlarmMac(it.mac) +// } +// } +// } +// } + } + + /** + * 报警处理 + * 持续扫描开启的持续时间,当超过X秒后报警 + * 冰箱、门、水管、燃气灶都是“长时间未关闭”的报警 + * + * + * 数据推送 + * 数据推送节点 + * 1. 贴件新增数据时 + * 位移型:新增数据 + * 震动型:新增第一条数据 + * 2. 报警 + * 定时任务发现报警时发送一条数据 + * 3. 电量低 + * 20%推送一次,10%推送一次 + * 4. 离线 + * 定时任务发现离线时推送一次数据 + * + * 报警code warnType(DEVICE_WARN_3/ DEVICE_WARN_4) + */ + fun Alarm() { + DataServer.MBlueToothData.forEach { + //获取对应的贴件数据 + var tjModel = ToolUtil.getBindMessByMac(it.mac, this) + var type = tjModel!!.type + if (type == "电冰箱" || type == "门" || type == "水管" || type == "煤气灶") { + if (it.open && it.onLine) { + var tomeRecord = it.tomeRecord +// var tomeRecord = it.tomeRecord + var openTime = tomeRecord[tomeRecord.size - 1].openTime + if (TimeUtil.getTimeDifference(openTime) >= DataServer.mAlarmTime && !it.showAlarm) { + //数据推送-2.长时间未关闭报警 + Log.i("数据推送==", "长时间未关闭报警") + warnType = "DEVICE_WARN_3" + getPushData(it.mac, "ALARM") + //关于报警弹窗 + it.showAlarm = true +// showAlarmDialog(type) + showBaseAlarmDialog(type, it.mac) + } + } + + } + + var power = BlueToothUtils!!.instance!!.getBlueToothPower(it.mac) + if (power == "20" || power == "10") { + //数据推送-3.电量低 + Log.i("数据推送==", "电量低") + warnType = "DEVICE_WARN_4" + getPushData(it.mac, "ALARM") +// showAlarmDialog("电量低") + showBaseAlarmDialog("电量低", it.mac) + } + } + //报警状态-更新本地 + BlueToothUtils.instance!!.addBlueToothDataHistory() + } + + fun AlarmNew() { + var dList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) + dList!!.forEach{ + if(it.isBind){ + var type = it.type + if (type == "电冰箱" || type == "门" || type == "水管" || type == "煤气灶") { + if(it.isOnline){ + var aList = ObjectBoxUtils.getActionByMac(it.mac) + if(!aList.isNullOrEmpty()){ + if(!DataServer.isShowAlarmMac(it.mac)) { + if (BlueToothUtils.instance!!.getActionType(aList[aList.size - 1].data!!) == 0) { + var openTime = aList[aList.size - 1].time + if (TimeUtil.getTimeDifference(TimeUtils.string2Millis(openTime)) >= DataServer.mAlarmTime) { + //数据推送-2.长时间未关闭报警 + Log.i("数据推送==", "长时间未关闭报警") + warnType = "DEVICE_WARN_3" + getPushDataNew(it.mac, "ALARM") + //关于报警弹窗 +// it.showAlarm = true +// showAlarmDialog(type) + showBaseAlarmDialog(type, it.mac) + } + } + }else{ + //关闭报警弹窗 + if (BlueToothUtils.instance!!.getActionType(aList[aList.size - 1].data!!) == 1) { + DataServer.removeAlarmMac(it.mac) + } + } + } + } + } + } + } + } + + + /** + * 离线任务 + * 每天00:00 时要求贴件 定时发送一次数据,当网关未收到贴件上传的数据时认为该贴件为离线状态 + * 离线后再次收到数据即为在线 在线逻辑在getBlueToothData()方法中处理 即isOnline + */ + fun OffLine() { + DataServer.listPushOffLine.clear() + //不能做非空判断 因为有可能所有都离线的情况 +// if(!mOutputInfo.isNullOrEmpty()){ + if (TimeUtil.isZeroHour()) { + //先设置所有为离线 + BlueToothUtils.instance!!.setAllOffLine() + //本地存储的所有的接收到的蓝牙数据(已绑定的) + DataServer.MBlueToothData.forEach { + //本次接收的数据 + DataServer.MSingleBlueToothData.forEach { itSingle -> + if (it.mac == itSingle.mac) { + it.onLine = false + DataServer.listPushOffLine.add(it.mac) + Log.i("数据推送==", "离线") + getPushData(it.mac, "OFFLINE") + } + } + } + + //离线-更新本地 + BlueToothUtils.instance!!.addBlueToothDataHistory() + } + } + + fun OffLineNew() { + var deviceList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) + var online = 0 + var offline = 0 + deviceList!!.forEach { + if(it!!.frequency!=0) { + if(!TextUtils.isEmpty(it!!.updateTime)) { + if (System.currentTimeMillis() - TimeUtils.string2Millis(it!!.updateTime) > it!!.frequency * 60 * 1000) { + if (it.isOnline) { + it.isOnline = false + ObjectBoxUtils.updateData(it, DeviceInfo::class.java) + getPushDataNew(it.mac, "OFFLINE") + } + } + } + + }else{ + if(!TextUtils.isEmpty(it!!.updateTime)) { + if (System.currentTimeMillis() - TimeUtils.string2Millis(it!!.updateTime) > 1 * 60 * 1000) { + if (it.isOnline) { + it.isOnline = false + ObjectBoxUtils.updateData(it, DeviceInfo::class.java) + getPushDataNew(it.mac, "OFFLINE") + } + } + } + } + if(it.isBind) { + if (it.isOnline) { + online++ + } else { + offline++ + } + } + } + tv_online.text = "在线 $online" + tv_offline.text = "离线 $offline" + } + + + fun getPushData(mac: String, pushType: String) { + //获取对应的蓝牙数据 + var model = BlueToothUtils.instance!!.getBlueToothDataModel(mac) + var tomeRecord = model.tomeRecord[model.tomeRecord.size - 1] + //获取 时长(秒 当前开或关了多久) + var timeLength = TimeUtil.getStateContinuousTime(mac) + //获取对应的贴件数据 + var tjModel = ToolUtil.getBindMessByMac(mac, this) + //新增 + if (pushType == "ADD" && tjModel != null) { + var list = ArrayList() + var bean = PushData( + mac, + model.type, + ToolUtil.getTJTypeCode(tjModel!!.type), + tjModel.name, + model.power, + "1", + ToolUtil.getOpenFormat(model.open), + TimeUtil.getNowTime().toString(), + timeLength.toString(), + "DEVICE_WARN_1" + ) + list.add(bean) + var dataStr = ToolUtil.getJson(list).toString() + var map = HashMap() + HttpRequest.init(this).postJson(ApiUrl.pushNewGetData, map, DataUtils.getSplitStr(dataStr), true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("数据推送接口==", "开启/关闭") + } + }) + } + //报警 + if (pushType == "ALARM" && tjModel != null) { + /** + * var deviceMac: String, + var identifyType: String, + var deviceType: String, + var roomName: String, + var devicePower: String, + var deviceOnLine: String, + var deviceStatus: String, + var sendDate: String, + var timeLength: String, + var warnType: String, + */ + var list = ArrayList() + var bean = AlarmData( + mac, + model.type, + ToolUtil.getTJTypeCode(tjModel.type), + tjModel.name, + model.power, + "1", + "1", + TimeUtil.getNowTime().toString(), + DataServer.mAlarmTime.toString(), +// timeLength.toString(), + warnType + ) + list.add(bean) + var dataStr = ToolUtil.getJson(list).toString() + var map = HashMap() + HttpRequest.init(this).postJson(ApiUrl.pushWarnData, map, DataUtils.getSplitStr(dataStr),true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("数据推送接口==", "报警") + } + }) + } + //离线 + if (pushType == "OFFLINE" && tjModel != null) { + var list = ArrayList() + var bean = OfflineData( + mac, + model.type, + ToolUtil.getTJTypeCode(tjModel.type), + tjModel.name, + model.power, + "1", + TimeUtil.getNowTime().toString(), + "DEVICE_WARN_2" + ) + list.add(bean) + var dataStr = ToolUtil.getJson(list).toString() + var map = HashMap() + HttpRequest.init(this).postJson(ApiUrl.pushOffData, map, DataUtils.getSplitStr(dataStr),true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("数据推送接口==", "离线") + } + }) + } + } + + fun getPushDataNew(mac: String, pushType: String) { + var dataMac = "" + //获取对应的蓝牙数据 + if(mac.contains(":")){ + dataMac = mac + }else{ + dataMac = Utils.formatMac(mac, ":") + } + + var deviceInfo = ObjectBoxUtils.getDeviceByMac(dataMac)!![0] + //获取 时长(秒 当前开或关了多久) + var timeLength = TimeUtil.getStateContinuousTimeNew(dataMac) + //新增 + if (pushType == "ADD" && deviceInfo != null) { + var list = ArrayList() + var bean = PushData( + dataMac, + ToolUtil.getTJType(deviceInfo.type), + ToolUtil.getTJTypeCode(deviceInfo!!.type), + deviceInfo.name, + deviceInfo.power, + "1", + if(ObjectBoxUtils.getLastActionStatusByMac(dataMac)==0) "1" else "0", + TimeUtil.getNowTime().toString(), + timeLength.toString(), + "DEVICE_WARN_1" + ) + list.add(bean) + var dataStr = ToolUtil.getJson(list).toString() + var map = HashMap() + HttpRequest.init(this).postJson(ApiUrl.pushNewGetData, map, DataUtils.getSplitStr(dataStr), true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("数据推送接口==", "开启/关闭") + } + }) + } + //报警 + if (pushType == "ALARM" && deviceInfo != null) { + /** + * var deviceMac: String, + var identifyType: String, + var deviceType: String, + var roomName: String, + var devicePower: String, + var deviceOnLine: String, + var deviceStatus: String, + var sendDate: String, + var timeLength: String, + var warnType: String, + */ + var list = ArrayList() + var bean = AlarmData( + dataMac, + ToolUtil.getTJType(deviceInfo.type), + ToolUtil.getTJTypeCode(deviceInfo.type), + deviceInfo.name, + deviceInfo.power, + "1", + "1", + TimeUtil.getNowTime().toString(), + DataServer.mAlarmTime.toString(), +// timeLength.toString(), + warnType + ) + list.add(bean) + var dataStr = ToolUtil.getJson(list).toString() + var map = HashMap() + HttpRequest.init(this).postJson(ApiUrl.pushWarnData, map, DataUtils.getSplitStr(dataStr),true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("数据推送接口==", "报警") + } + }) + } + //离线 + if (pushType == "OFFLINE" && deviceInfo != null) { + var list = ArrayList() + var bean = OfflineData( + dataMac, + ToolUtil.getTJType(deviceInfo.type), + ToolUtil.getTJTypeCode(deviceInfo.type), + deviceInfo.name, + deviceInfo.power, + "1", + TimeUtil.getNowTime().toString(), + "DEVICE_WARN_2" + ) + list.add(bean) + var dataStr = ToolUtil.getJson(list).toString() + var map = HashMap() + HttpRequest.init(this).postJson(ApiUrl.pushOffData, map, DataUtils.getSplitStr(dataStr),true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("数据推送接口==", "离线") + } + }) + } + } + + override fun receiveEvent(event: EventBusUtil.MessageEvent) { + super.receiveEvent(event) + when (event.code) { + /** + * 数据推送 + */ + // 1. 贴件新增数据时 位移型:新增数据 震动型:新增第一条数据 + EventCode.PUSHDATA_NEWGET -> { +// getPushData1("ADD") + var mac = event.data.toString() + getPushDataNew(mac, "ADD") + } + + EventCode.START_SCAN -> { + startScan() + } + + } + } + + /** + * 蓝牙-3、设备连接回调 + */ + @Subscribe + fun showConnectedDevice(event: ConnectEvent?) { +// Log.i("BlueTooth==", "showConnectedDevice:连接回调") + if (event != null) { + if (event.isSuccess) { + try { + + //记录绑定的蓝牙 + DataServer.UNBUNDLE = 1 + if(DataServer.MDevice!=null) { + //蓝牙-4、设置读写 + //写入数据 + BluetoothDeviceManager.getInstance().bindChannel( + DataServer.MDevice, + PropertyType.PROPERTY_WRITE, DataServer.UUID1, DataServer.UUID2, null + ) + //读数据 + BluetoothDeviceManager.getInstance().bindChannel( + DataServer.MDevice, + PropertyType.PROPERTY_NOTIFY, DataServer.UUID1, DataServer.UUID3, null + ) + BluetoothDeviceManager.getInstance() + .registerNotify(DataServer.MDevice, false) + Log.i( + "BlueTooth==", + "showConnectedDevice:连接成功" + DataServer.MDevice!!.address + ) + if(DataServer.connectMode == 1){ + //如果DataServer.nBlueToothMac的贴件连接成功,说明贴件唤醒成功 + if(DataServer.MDevice!!.address.equals(DataServer.nBlueToothVerifyMac)) { + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.DEVICE_WAKE_UP_SUCCEED)) + } + }else if(DataServer.connectMode == 2) { + ViseBle.getInstance().stopScan(periodScanCallback) + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } else { + //广播版蓝牙断开后重新扫描广播 + if(DataServer.connectMode == 1) { + //非主动断开 + handler.removeMessages(0) + if(!DataServer.isUpdateFirmware) { + startScan() + } + }else{ + if(!DataServer.isUpdateFirmware) { + if(!BluetoothDeviceManager.getInstance().isConnectedDevice) { + Log.i("BlueTooth==", "重新连接中继") + Handler().postDelayed({ + val relayMac: String = + SPUtil.getValue(DataServer.MRelayMac, String::class.java) + if (relayMac != null && !TextUtils.isEmpty(relayMac)) { + DataServer.nBlueToothMac = relayMac + ViseBle.getInstance().stopScan(periodScanCallback) + ViseBle.config().scanTimeout = -1 //扫描超时时间,-1设置为永久扫描 + ViseBle.getInstance().startScan(periodScanCallback) + } + }, 3000) + } + } + } + //中继断开重连 + } + } + } + + /** + * 蓝牙输出数据展示 + * (0X)1F-FF-07-5D-1F-FF-33-DC-68-34-75-C5-84 + * (0X)1F-FF-07-5D-1F-FF-33-DC-68-34-75-C5-84 + * 输出数据:009c036201dc37dc683475c584 + */ + private var mOutputInfo = StringBuilder("") + //广播数据包标识 + private var outputFlag = StringBuilder("") + + @Subscribe + fun showDeviceNotifyData(event: NotifyDataEvent?) { + try { + if (DataServer.MDevice != null) { + if (event?.data != null && event.bluetoothLeDevice != null && event.bluetoothLeDevice + .address.equals(DataServer.MDevice!!.address) + ) { + mOutputInfo = StringBuilder("") + mOutputInfo.append(HexUtil.encodeHexStr(event.data)) + Log.i("BlueTooth==", "showDeviceNotifyData:输出数据:$mOutputInfo") + Log.i("BlueTooth==", String(event.data)) + if(DataServer.connectMode==1){ + + }else{ + //如果收到DataServer.nBlueToothMac的贴件数据,说明贴件唤醒成功 + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.DEVICE_WAKE_UP_SUCCEED)) + +// dealWithBluetoothData(mOutputInfo.toString()) + } + } + } + } catch (e: java.lang.Exception) { + } + } + + var index = 0 + +// @Subscribe +// fun showDeviceScanData(event: ScanDataEvent?) { +// try { +// if (event?.data != null && event.bluetoothLeDevice != null) { +// mOutputInfo = StringBuilder("") +// mOutputInfo.append(HexUtil.encodeHexStr(event.data)) +// mOutputInfo.append(Utils.formatMac(event.bluetoothLeDevice.address)) +// //过滤扫描到的重复广播数据 +// if(outputFlag.toString() == "") { +// outputFlag.append(mOutputInfo.substring(4,8)) +// Log.i("BlueTooth==", "showDeviceScanData:输出数据:$mOutputInfo") +// Log.i("BlueTooth==", String(event.data)) +// index++ +// Log.i("BlueTooth==", "接收到$index"+"条数据") +// dealWithBluetoothData(mOutputInfo.toString()) +// }else if(mOutputInfo.substring(4,8)!=outputFlag.toString()) { +// outputFlag = StringBuilder("") +// outputFlag.append(mOutputInfo.substring(4,8)) +// Log.i("BlueTooth==", "showDeviceScanData:输出数据:$mOutputInfo") +// Log.i("BlueTooth==", String(event.data)) +// index++ +// Log.i("BlueTooth==", "接收到$index"+"条数据") +// dealWithBluetoothData(mOutputInfo.toString()) +// } +// } +// } catch (e: java.lang.Exception) { +// } +// } + + //处理蓝牙数据 +// fun dealWithBluetoothData(info: String) { +// //蓝牙数据存本地 +// BlueToothUtils.instance!!.getBlueToothData(info, this) +// } + + //网络状态监听 + private fun registertNetConnect(){ + NetworkChange.registerReceiver(this, object : NetworkChange.NetStateChangeObserver { + override fun onDisconnect() { + if(!DataServer.wifiDialogIsShow&&isShowing) { + WifiDisconnectDialog.instance!!.getShareDialog(this@HomeActivity, + object : WifiDisconnectDialog.PopupYearWindowCallBack { + override fun doWork() { + } + }) + } + } + + override fun onMobileConnect() {} + override fun onWifiConnect() { + } + }) + } + + /** + * 获取蓝牙权限 + */ + private fun askPermissionOfBluetooth() { + EasyPermissions.requestPermissions( + this, + "请允许获取您的位置权限", + DataServer.ACCESS_BlueTooth_CODE, + Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION + ) + } + + /** + * 扫描回调 + */ + private val periodScanCallback: ScanCallback = ScanCallback(object : IScanCallback { + override fun onDeviceFound(bluetoothLeDevice: BluetoothLeDevice) { + if(DataServer.connectMode==1) { + if (bluetoothLeDevice.name != null && bluetoothLeDevice.name.startsWith("TJ")) { + if (!TextUtils.isEmpty(bluetoothLeDevice.address)) { + if (bluetoothLeDevice.address.equals(DataServer.nBlueToothMac)) { + Log.i( + "BlueTooth==", + "periodScanCallback:发现指定设备" + bluetoothLeDevice.address + ) + if (!BluetoothDeviceManager.getInstance() + .isConnected(bluetoothLeDevice) + ) { + DataServer.nBlueToothMac = "" + DataServer.MDevice = bluetoothLeDevice + BluetoothDeviceManager.getInstance().connect(bluetoothLeDevice) + } + } + } + } + }else{ + if (bluetoothLeDevice.name != null && bluetoothLeDevice.name.startsWith("ZJ")) { + if (!TextUtils.isEmpty(bluetoothLeDevice.address)) { + if (bluetoothLeDevice.address.equals(DataServer.nBlueToothMac)) { + Log.i( + "BlueTooth==", + "periodScanCallback:发现指定设备" + bluetoothLeDevice.address + ) + if (!BluetoothDeviceManager.getInstance() + .isConnected(bluetoothLeDevice) + ) { + DataServer.nBlueToothMac = "" + DataServer.MDevice = bluetoothLeDevice + BluetoothDeviceManager.getInstance().connect(bluetoothLeDevice) + } + } + } + } + } + } + + override fun onScanFinish(bluetoothLeDeviceStore: BluetoothLeDeviceStore?) { + Log.i("BlueTooth==", "periodScanCallback:扫描结束") + } + + override fun onScanTimeout() { + Log.i("BlueTooth==", "periodScanCallback:扫描超时") + } + + }) + + //注册蓝牙监听广播 + /** + * 蓝牙广播过滤器 + * 蓝牙状态改变 + * 找到设备 + * 搜索完成 + * 开始扫描 + * 状态改变 + */ + fun makeFilter(): IntentFilter? { + val filter = IntentFilter() + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED) //蓝牙状态改变的广播 + return filter + } + + private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + when (action) { + //蓝牙状态改变的广播 + BluetoothAdapter.ACTION_STATE_CHANGED -> { + Log.i("BlueTooth==", "ACTION_STATE_CHANGED:蓝牙状态改变") + var blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0) + when (blueState) { + //蓝牙-2、扫描 + BluetoothAdapter.STATE_ON -> { + Log.i("BlueTooth==", "蓝牙打开") + startScan() + } + //蓝牙关闭 + BluetoothAdapter.STATE_OFF -> { + Log.i("BlueTooth==", "蓝牙已经关闭") + } + } + } + } + } + } + + + /** + * 开始扫描 + * 广播模式直接扫描 + * 中继模式扫描去连接设备 + */ + private fun startScan(){ + if(DataServer.connectMode==1){ + ViseBle.getInstance().stopScan(periodScanCallback) + ViseBle.config().scanTimeout = -1 //扫描超时时间,-1设置为永久扫描 + ViseBle.getInstance().startScan(periodScanCallback) + }else{ + val relayMac: String = SPUtil.getValue(DataServer.MRelayMac,String::class.java) + if (relayMac != null && !TextUtils.isEmpty(relayMac)) { + DataServer.nBlueToothMac = relayMac + ViseBle.getInstance().stopScan(periodScanCallback) + ViseBle.config().scanTimeout = -1 //扫描超时时间,-1设置为永久扫描 + ViseBle.getInstance().startScan(periodScanCallback) + } + } + } + + + + /** + * 网关登录 + */ + //获取code + fun getHttpCode() { + Log.i("UUID==", DeviceUuidFactory.getAndroidID(this)) + var map = HashMap() + map["id"] = DeviceUuidFactory.getAndroidID(this) + HttpRequest.init(this).get(ApiUrl.getCode) + .setMap(map) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("响应数据", message!!) + mCode = `object`.toString() + getHttpLogin() + } + + override fun failed(code: String?, info: String?): Boolean { + startActivity(Intent(this@HomeActivity, ServiceActivity::class.java)) + return super.failed(code, info) + } + + }) + + } + + //网关登录 获取token存本地 这里如果绑定了就有token 不绑定就没有token 不影响后面测试网关是否连接成功 + fun getHttpLogin() { + var map = HashMap() + map["code"] = mCode + map["id"] = DeviceUuidFactory.getAndroidID(this) + HttpRequest.init(this).postJson(ApiUrl.getLogin, map,"",false) + .setClazz(GetTokenM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + var model = `object` as GetTokenM + mToken = model.token + DataServer.mCode = mCode + DataServer.mHttpToken = mToken + } + + override fun after() { + super.after() + getLoginSuccess() + } + }) + } + + //测试网关是否连接成功 + fun getLoginSuccess() { + HttpRequest.init(this).get(ApiUrl.getLoginSuccess) + .setClazz(GetCodeM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("响应数据", message!!) +// var ddList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) +// var dList = ObjectBoxUtils.getAllData(ActionInfo::class.java) +// Log.e("BlueTooth", "device:"+ddList!!.size.toString()+" action:"+dList!!.size.toString()) +//// ObjectBoxUtils.deleteALLData(ActionInfo::class.java) +//// ObjectBoxUtils.deleteALLDataD(ObjectBoxUtils.getDeviceByMac("C7:3D:41:CB:A8:36")) +//// ObjectBoxUtils.deleteALLDataA(ObjectBoxUtils.getActionByMac("C7:3D:41:CB:A8:36")) +// var mode = SPUtil.getValue(DataServer.MConnectMode,Integer::class.java) +// if(mode!=null&&mode.toInt()!=0) { +//// ObjectBoxUtils.deleteDatabase() +//// ToolUtil.clearHistory(this@ServiceActivity, DataServer.mBindHistory) +//// SPUtil.setValue(DataServer.MConnectMode, 2) +//// SPUtil.setValue(DataServer.MRelayMac,"") +//// DataServer.connectMode =1 +//// //设置连接的贴件 +//// DataServer.nBlueToothMac = "C5:76:AB:D0:3B:D5" +//// startActivity(Intent(this@ServiceActivity, HomeActivity::class.java)) +//// startActivity(Intent(this@ServiceActivity, ModelInitActivity::class.java).putExtra("mac", "D5:3B:D0:AB:76:C5")) +// if(mode.toInt()==2) { +// DataServer.connectMode = 2 +// var relayMac = SPUtil.getValue(DataServer.MRelayMac,String::class.java) +// if(relayMac!=null&&!TextUtils.isEmpty(relayMac)) { +// startActivity(Intent(this@ServiceActivity, HomeActivity::class.java)) +// }else{ +// startActivity(Intent(this@ServiceActivity, ConnectModeActivity::class.java)) +// } +// }else{ +// DataServer.connectMode = 1 +// startActivity(Intent(this@ServiceActivity, HomeActivity::class.java)) +// } +// }else{ +// DataServer.connectMode = 0 +// startActivity(Intent(this@ServiceActivity, ConnectModeActivity::class.java)) +// } +// finish() + } + + override fun failed(code: String?, info: String?): Boolean { + startActivity(Intent(this@HomeActivity, ServiceActivity::class.java)) + return super.failed(code, info) + } + + }) + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/InfoActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/InfoActivity.kt new file mode 100644 index 0000000..fcab8ed --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/InfoActivity.kt @@ -0,0 +1,23 @@ +package com.qidian.zhongkesmart.activity + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity + +class InfoActivity : BaseActivity() { + + override fun getLayoutId(): Int { + return R.layout.activity_infi + } + + override fun initView() { + } + + override fun initData() { + } + + override fun isImmersion(): Boolean { + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/LaunchActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/LaunchActivity.kt new file mode 100644 index 0000000..bfe0828 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/LaunchActivity.kt @@ -0,0 +1,103 @@ +package com.qidian.zhongkesmart.activity + +import android.content.Intent +import android.content.IntentFilter +import android.graphics.drawable.AnimationDrawable +import android.net.ConnectivityManager +import android.net.wifi.WifiManager +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.os.Handler +import android.text.TextUtils +import android.util.Log +import com.qidian.zhongkesmart.MainActivity +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.manager.BasicSettingManager +import com.qidian.zhongkesmart.model.ActionInfo +import com.qidian.zhongkesmart.model.DeviceInfo +import com.qidian.zhongkesmart.receiver.WifiReceiver +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.ObjectBoxUtils +import com.qidian.zhongkesmart.utils.SPUtil +import com.qidian.zhongkesmart.utils.WifiUtil +import kotlinx.android.synthetic.main.activity_launch.* + +/*开机页*/ +class LaunchActivity : BaseActivity() { + val handler = Handler() + val waitTime: Long = 3000 + var drawable: AnimationDrawable? = null + + override fun getLayoutId(): Int { + return R.layout.activity_launch + } + + override fun initView() { + StartAnimaton() + handler.postDelayed(Runnable { + goOnIntent() + }, waitTime) + } + + override fun initData() { + } + + + fun goOnIntent() { +// startActivity(Intent(this, MainActivity::class.java)) + var ddList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) + var dList = ObjectBoxUtils.getAllData(ActionInfo::class.java) + Log.e("BlueTooth", "device:"+ddList!!.size.toString()+" action:"+dList!!.size.toString()) + if (WifiUtil.getWifiName(this).isNullOrEmpty()) { + startActivity(Intent(this, WiFiActivity::class.java)) + } else { +// startActivity(Intent(this, ServiceActivity::class.java)) + var mode = SPUtil.getValue(DataServer.MConnectMode,Integer::class.java) + if(mode!=null&&mode.toInt()!=0) { + if(mode.toInt()==2) { + DataServer.connectMode = 2 + var relayMac = SPUtil.getValue(DataServer.MRelayMac,String::class.java) + if(relayMac!=null&&!TextUtils.isEmpty(relayMac)) { + startActivity(Intent(this@LaunchActivity, HomeActivity::class.java)) + }else{ + startActivity(Intent(this@LaunchActivity, ConnectModeActivity::class.java)) + } + }else{ + DataServer.connectMode = 1 + startActivity(Intent(this@LaunchActivity, HomeActivity::class.java)) + } + }else{ + DataServer.connectMode = 0 + startActivity(Intent(this@LaunchActivity, ConnectModeActivity::class.java)) + } + } + finish() + } + + fun StartAnimaton() { + drawable = resources?.getDrawable(R.drawable.lanchanimation) as AnimationDrawable + // 放入imageView + imv_lancher!!.setBackgroundDrawable(drawable) + // 开始 + drawable!!.start() + } + + fun StopAnimation() { + if (drawable!!.isRunning) { + drawable!!.stop() + } + } + + override fun onDestroy() { + super.onDestroy() + handler.removeCallbacksAndMessages(null) + StopAnimation() + } + + override fun isImmersion(): Boolean { + return true + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/ModelInitActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/ModelInitActivity.kt new file mode 100644 index 0000000..87055bd --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/ModelInitActivity.kt @@ -0,0 +1,835 @@ +package com.qidian.zhongkesmart.activity + +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.View +import com.blankj.utilcode.util.ToastUtils +import com.github.mikephil.charting.data.Entry +import com.netease.yunxin.kit.alog.BasicInfo +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.baseble.ble.DeviceSettingEvent +import com.qidian.baseble.utils.HexUtil +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.dialog.* +import com.qidian.zhongkesmart.model.DeviceInfo +import com.qidian.zhongkesmart.utils.* +import com.vise.xsnow.event.Subscribe +import kotlinx.android.synthetic.main.activity_model_init.* +import kotlinx.android.synthetic.main.activity_my_equipment_details.* +import java.text.SimpleDateFormat +import java.util.* +import kotlin.collections.ArrayList + +class ModelInitActivity : BaseActivity() { + override var TAG = ModelInitActivity::class.java.simpleName + var mMac="" + //0待学习 1模型一学习第一步 2模型一学习第二步 3模型二学习第一步 4模型二学习第二步 + var mStep = 0 + var deviceInfo:DeviceInfo? = null + var model1Threshold = 0 + var model2Threshold =0 + var values = ArrayList() + var xValues = ArrayList() + var countDownTimer: CountDownTimer? = null + var countDownTimerS: CountDownTimer? = null + var timeString = "" + var isShowVerifyToast = false + override fun getLayoutId(): Int { + return R.layout.activity_model_init + } + + override fun initView() { + mMac=intent.getStringExtra("mac").toString() + } + + override fun initData() { +// var myEquipmentData= ToolUtil.getHistory(DataServer.mBindHistory,this) + var myEquipmentData= ObjectBoxUtils.getDeviceByMac(mMac) + myEquipmentData!!.forEach { + if(it!!.mac==mMac){ + deviceInfo = it + tv_location.text = "贴件位置:"+it.name + } + } + tv_title.text = deviceInfo!!.type+"模型初始化" + tv_mac.text = "MAC地址:$mMac" + tv_version.text = "固件版本:${deviceInfo!!.fVersion}" + if(deviceInfo!!.model1Init){ + v_open_status.background = getDrawable(R.drawable.shape_model_init_circel_black) + tv_open_status.text = "上次学习时间:${deviceInfo!!.model1Time}" + tv_open_status.setTextColor(getColor(R.color.color_333333)) + iv_open_threshold.visibility = View.VISIBLE + }else{ + v_open_status.background = getDrawable(R.drawable.shape_model_init_circel_gray) + tv_open_status.text = "未学习" + tv_open_status.setTextColor(getColor(R.color.color_333333)) + iv_open_threshold.visibility = View.GONE + } + if(deviceInfo!!.model2Init){ + v_close_status.background = getDrawable(R.drawable.shape_model_init_circel_black) + tv_close_status.text = "上次学习时间:${deviceInfo!!.model2Time}" + tv_close_status.setTextColor(getColor(R.color.color_333333)) + iv_close_threshold.visibility = View.VISIBLE + }else{ + v_close_status.background = getDrawable(R.drawable.shape_model_init_circel_gray) + tv_close_status.text = "未学习" + tv_close_status.setTextColor(getColor(R.color.color_333333)) + iv_close_threshold.visibility = View.GONE + } + setImage() + if(iv_open_threshold.visibility == View.VISIBLE&&iv_close_threshold.visibility == View.VISIBLE){ + tv_verify.background = getDrawable(R.drawable.shape_model_init_verify_btn_blue_bg) + tv_verify.setTextColor(getColor(R.color.white)) + }else{ + tv_verify.background = getDrawable(R.drawable.shape_model_init_verify_btn_gray_bg) + tv_verify.setTextColor(Color.parseColor("#ffa1a1a1")) + } + } + + override fun onDestroy() { + if(countDownTimer!=null) { + countDownTimer!!.cancel() + countDownTimer = null + } + if(countDownTimerS!=null) { + countDownTimerS!!.cancel() + countDownTimerS = null + } + super.onDestroy() + } + + fun setImage(){ + //"门", "窗帘", "主水管", "水管", "马桶", "电视", "电冰箱", "煤气灶", "抽油烟机", "宠物", "植物", "挎包" + when (deviceInfo!!.type) { + "门" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.door_open_select_large) + iv_close.setImageResource(R.mipmap.door_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.door_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.door_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.door_open_select_small) + iv_close_study.setImageResource(R.mipmap.door_close_select_small) + } + "窗帘" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.curtain_open_select_large) + iv_close.setImageResource(R.mipmap.curtain_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.curtain_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.curtain_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.curtain_open_select_small) + iv_close_study.setImageResource(R.mipmap.curtain_close_select_small) + } + "主水管" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.stopcock_open_select_large) + iv_close.setImageResource(R.mipmap.stopcock_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.stopcock_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.stopcock_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.stopcock_open_select_small) + iv_close_study.setImageResource(R.mipmap.stopcock_close_select_small) + } + "水管" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.stopcock_open_select_large) + iv_close.setImageResource(R.mipmap.stopcock_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.stopcock_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.stopcock_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.stopcock_open_select_small) + iv_close_study.setImageResource(R.mipmap.stopcock_close_select_small) + } + "马桶" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.toilet_open_select_large) + iv_close.setImageResource(R.mipmap.toilet_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.toilet_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.toilet_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.toilet_open_select_small) + iv_close_study.setImageResource(R.mipmap.toilet_close_select_small) + } + "电视" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.tv_open_select_large) + iv_close.setImageResource(R.mipmap.tv_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.tv_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.tv_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.tv_open_select_small) + iv_close_study.setImageResource(R.mipmap.tv_close_select_small) + } + "电冰箱" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.fridge_open_select_large) + iv_close.setImageResource(R.mipmap.fridge_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.fridge_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.fridge_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.fridge_open_select_small) + iv_close_study.setImageResource(R.mipmap.fridge_close_select_small) + } + "煤气灶" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.cooker_open_select_large) + iv_close.setImageResource(R.mipmap.cooker_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.cooker_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.cooker_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.cooker_open_select_small) + iv_close_study.setImageResource(R.mipmap.cooker_close_select_small) + } + "抽油烟机" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.extractor_open_select_large) + iv_close.setImageResource(R.mipmap.extractor_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.extractor_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.extractor_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.extractor_open_select_small) + iv_close_study.setImageResource(R.mipmap.extractor_close_select_small) + } + "宠物" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.pet_open_select_large) + iv_close.setImageResource(R.mipmap.pet_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.pet_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.pet_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.pet_open_select_small) + iv_close_study.setImageResource(R.mipmap.pet_close_select_small) + } + "植物" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.plant_open_select_large) + iv_close.setImageResource(R.mipmap.plant_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.plant_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.plant_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.plant_open_select_small) + iv_close_study.setImageResource(R.mipmap.plant_close_select_small) + } + "挎包" -> { + when (mStep){ + 0 ->{ + iv_open.setImageResource(R.mipmap.bag_open_select_large) + iv_close.setImageResource(R.mipmap.bag_close_select_large) + } + 1 ->{ + iv_close.setImageResource(R.mipmap.bag_close_unselect) + } + 3 ->{ + iv_open.setImageResource(R.mipmap.bag_open_unselect) + } + } + iv_open_study.setImageResource(R.mipmap.bag_open_select_small) + iv_close_study.setImageResource(R.mipmap.bag_close_select_small) + } + } + } + + fun onClick(v: View) { + when (v.id) { + R.id.iv_back -> { + showDialog() + //广播版退出配置模式,手动断开连接 + if(DataServer.connectMode==1) { + if (BluetoothDeviceManager.getInstance() + .isConnected(DataServer.MDevice) + ) { + BluetoothDeviceManager.getInstance() + .disconnect(DataServer.MDevice) + } + } + onBackPressed() + DataServer.nBlueToothVerifyMac = "" + } + + R.id.iv_open_threshold -> { + SetThresholdDialog.instance!!.getShareDialog(this, + object : SetThresholdDialog.PopupYearWindowCallBack { + override fun doWork(value: Int) { + showDialog() + model1Threshold = value + //设置阈值 + BluetoothDeviceManager.getInstance() + .setParam(mMac, true,254,value) + } + },"打开",deviceInfo!!.model1Threshold) + } + + R.id.tv_open_next -> { + when (mStep){ + 0 ->{ + mStep = 1 + setImage() + rl_open.background = getDrawable(R.drawable.shape_model_init_blue_border_bg) + tv_open_next.text = "下一步" + tv_close_next.visibility = View.GONE + v_close_bottom.visibility = View.GONE + iv_open.visibility = View.GONE + ll_open.visibility = View.VISIBLE + tv_open_cancel.visibility = View.VISIBLE + iv_open_num.setImageResource(R.mipmap.study_num_icon) + tv_open_num.text = "准备学习" + tv_open_num.setTextColor(getColor(R.color.color_333333)) + v_open_study.background = getDrawable(R.drawable.shape_model_init_square_blue) + tv_open_des1.text = "学习前请将${deviceInfo!!.type}操作为关闭状态" + tv_open_des1.setTextColor(getColor(R.color.color_333333)) + tv_open_des2.visibility = View.GONE + v_open_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + } + 1 ->{ + mStep = 2 + rl_open.background = getDrawable(R.drawable.shape_model_init_blue_bg) + tv_open_title.setTextColor(getColor(R.color.white)) + v_open_status.background = getDrawable(R.drawable.shape_model_init_circel_green) + tv_open_status.text = "正在学习" + tv_open_status.setTextColor(getColor(R.color.white)) + tv_open_next.visibility = View.GONE + tv_open_cancel.visibility = View.GONE + tv_open_no_response.visibility = View.VISIBLE + iv_open_num.setImageResource(R.mipmap.study_num_icon2) + tv_open_num.text = "学习动作" + tv_open_num.setTextColor(getColor(R.color.white)) + v_open_study.background = getDrawable(R.drawable.shape_model_init_square_green) + tv_open_des1.text = "在贴件LED绿色闪烁后请将 ${deviceInfo!!.type} 操作为开启状态" + tv_open_des1.setTextColor(getColor(R.color.white)) + tv_open_des2.visibility = View.VISIBLE + v_open_top.setBackgroundColor(Color.parseColor("#006FDE")) + v_open_bottom.setBackgroundColor(Color.parseColor("#006FDE")) + v_open_center.setBackgroundColor(Color.parseColor("#006FDE")) + + Handler().postDelayed({ + //通知贴件开始学习 + BluetoothDeviceManager.getInstance() + .setParam(mMac, true,255,0) + countDownTimer = object : CountDownTimer(32 * 100, 100) { + //1000ms运行一次 + override fun onFinish() { + countDownTimer = null + } + + override fun onTick(millisUntilFinished: Long) { + var unit = (millisUntilFinished/100).toInt() + when (unit - 2){ + 5,10,15,20,25 ->{ + rl_open.background = getDrawable(R.drawable.shape_model_init_green_bg) + } + + 1,6,11,16,21,26 ->{ + rl_open.background = getDrawable(R.drawable.shape_model_init_blue_bg) + } + } + } + }.start() + //5秒之后结束学习 + Handler().postDelayed({ + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + val curDate = Date(System.currentTimeMillis()) //获取当前时间 + timeString = formatter.format(curDate) + BluetoothDeviceManager.getInstance() + .setParamTime(mMac, false,255,0,curDate) + countDownTimerS = object : CountDownTimer(60 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("模型学习失败") + countDownTimerS = null + } + + override fun onTick(millisUntilFinished: Long) { + } + }.start() + },5000) + }, 3000) + } + } + + } + + R.id.tv_open_cancel -> { + CancelModeInitDialog.instance!!.getShareDialog(this, + object : CancelModeInitDialog.PopupYearWindowCallBack { + override fun doWork() { + mStep = 0 + setImage() + rl_open.background = getDrawable(R.drawable.shape_model_init_gray_bg) + tv_open_title.setTextColor(getColor(R.color.color_333333)) + v_open_status.background = getDrawable(R.drawable.shape_model_init_circel_gray) + tv_open_status.text = "未学习" + tv_open_status.setTextColor(getColor(R.color.color_333333)) + tv_open_next.text = "开始学习" + tv_open_next.visibility = View.VISIBLE + tv_close_next.visibility = View.VISIBLE + v_close_bottom.visibility = View.VISIBLE + iv_open.visibility = View.VISIBLE + ll_open.visibility = View.GONE + tv_open_cancel.visibility = View.GONE + tv_open_no_response.visibility = View.GONE + v_open_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + } + }) + } + + R.id.tv_open_no_response ->{ + mStep = 0 + setImage() + rl_open.background = getDrawable(R.drawable.shape_model_init_gray_bg) + tv_open_title.setTextColor(getColor(R.color.color_333333)) + v_open_status.background = getDrawable(R.drawable.shape_model_init_circel_gray) + tv_open_status.text = "未学习" + tv_open_status.setTextColor(getColor(R.color.color_333333)) + tv_open_next.text = "开始学习" + tv_open_next.visibility = View.VISIBLE + tv_close_next.visibility = View.VISIBLE + v_close_bottom.visibility = View.VISIBLE + iv_open.visibility = View.VISIBLE + ll_open.visibility = View.GONE + tv_open_cancel.visibility = View.GONE + tv_open_no_response.visibility = View.GONE + v_open_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("消息发送失败,请确认贴件状态") + } + + R.id.iv_close_threshold -> { + SetThresholdDialog.instance!!.getShareDialog(this, + object : SetThresholdDialog.PopupYearWindowCallBack { + override fun doWork(value: Int) { + showDialog() + model2Threshold = value + //设置阈值 + BluetoothDeviceManager.getInstance() + .setParam(mMac, true,252,value) + } + },"关闭",deviceInfo!!.model2Threshold) + } + + R.id.tv_close_next -> { + when (mStep){ + 0 ->{ + mStep = 3 + setImage() + rl_close.background = getDrawable(R.drawable.shape_model_init_blue_border_bg) + tv_close_next.text = "下一步" + tv_open_next.visibility = View.GONE + v_open_bottom.visibility = View.GONE + iv_close.visibility = View.GONE + ll_close.visibility = View.VISIBLE + tv_close_cancel.visibility = View.VISIBLE + iv_close_num.setImageResource(R.mipmap.study_num_icon) + tv_close_num.text = "准备学习" + tv_close_num.setTextColor(getColor(R.color.color_333333)) + v_close_study.background = getDrawable(R.drawable.shape_model_init_square_blue) + tv_close_des1.text = "学习前请将${deviceInfo!!.type}操作为开启状态" + tv_close_des1.setTextColor(getColor(R.color.color_333333)) + tv_close_des2.visibility = View.GONE + v_close_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + } + 3 ->{ + mStep = 4 + rl_close.background = getDrawable(R.drawable.shape_model_init_blue_bg) + tv_close_title.setTextColor(getColor(R.color.white)) + v_close_status.background = getDrawable(R.drawable.shape_model_init_circel_green) + tv_close_status.text = "正在学习" + tv_close_status.setTextColor(getColor(R.color.white)) + tv_close_next.visibility = View.GONE + tv_close_cancel.visibility = View.GONE + tv_close_no_response.visibility = View.VISIBLE + iv_close_num.setImageResource(R.mipmap.study_num_icon2) + tv_close_num.text = "学习动作" + tv_close_num.setTextColor(getColor(R.color.white)) + v_close_study.background = getDrawable(R.drawable.shape_model_init_square_green) + tv_close_des1.text = "在贴件LED绿色闪烁后请将 ${deviceInfo!!.type} 操作为关闭状态" + tv_close_des1.setTextColor(getColor(R.color.white)) + tv_close_des2.visibility = View.VISIBLE + v_close_top.setBackgroundColor(Color.parseColor("#006FDE")) + v_close_bottom.setBackgroundColor(Color.parseColor("#006FDE")) + v_close_center.setBackgroundColor(Color.parseColor("#006FDE")) + Handler().postDelayed({ + //通知贴件开始学习 + BluetoothDeviceManager.getInstance() + .setParam(mMac, true,253,0) + countDownTimer = object : CountDownTimer(32 * 100, 100) { + //1000ms运行一次 + override fun onFinish() { + countDownTimer = null + } + + override fun onTick(millisUntilFinished: Long) { + var unit = (millisUntilFinished/100).toInt() + when (unit - 2){ + 5,10,15,20,25 ->{ + rl_close.background = getDrawable(R.drawable.shape_model_init_green_bg) + } + + 1,6,11,16,21,26 ->{ + rl_close.background = getDrawable(R.drawable.shape_model_init_blue_bg) + } + } + } + }.start() + //5秒之后结束学习 + Handler().postDelayed({ + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + val curDate = Date(System.currentTimeMillis()) //获取当前时间 + timeString = formatter.format(curDate) + BluetoothDeviceManager.getInstance() + .setParamTime(mMac, false,253,0,curDate) + countDownTimerS = object : CountDownTimer(60 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("模型学习失败") + countDownTimerS = null + } + + override fun onTick(millisUntilFinished: Long) { + } + }.start() + },5000) + }, 3000) + } + } + } + + R.id.tv_close_cancel -> { + CancelModeInitDialog.instance!!.getShareDialog(this, + object : CancelModeInitDialog.PopupYearWindowCallBack { + override fun doWork() { + mStep = 0 + setImage() + rl_close.background = getDrawable(R.drawable.shape_model_init_gray_bg) + tv_close_title.setTextColor(getColor(R.color.color_333333)) + v_close_status.background = getDrawable(R.drawable.shape_model_init_circel_gray) + tv_close_status.text = "未学习" + tv_close_status.setTextColor(getColor(R.color.color_333333)) + tv_close_next.text = "开始学习" + tv_close_next.visibility = View.VISIBLE + tv_open_next.visibility = View.VISIBLE + v_open_bottom.visibility = View.VISIBLE + iv_close.visibility = View.VISIBLE + ll_close.visibility = View.GONE + tv_close_cancel.visibility = View.GONE + tv_close_no_response.visibility = View.GONE + v_close_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + } + }) + } + + R.id.tv_close_no_response ->{ + mStep = 0 + setImage() + rl_close.background = getDrawable(R.drawable.shape_model_init_gray_bg) + tv_close_title.setTextColor(getColor(R.color.color_333333)) + v_close_status.background = getDrawable(R.drawable.shape_model_init_circel_gray) + tv_close_status.text = "未学习" + tv_close_status.setTextColor(getColor(R.color.color_333333)) + tv_close_next.text = "开始学习" + tv_close_next.visibility = View.VISIBLE + tv_open_next.visibility = View.VISIBLE + v_open_bottom.visibility = View.VISIBLE + iv_close.visibility = View.VISIBLE + ll_close.visibility = View.GONE + tv_close_cancel.visibility = View.GONE + tv_close_no_response.visibility = View.GONE + v_close_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("消息发送失败,请确认贴件状态") + } + + R.id.tv_verify -> { + if(iv_open_threshold.visibility == View.VISIBLE&&iv_close_threshold.visibility == View.VISIBLE){ + xValues.clear() + values.clear() + showDialog() + BluetoothDeviceManager.getInstance() + .setMode(mMac, true,2) + countDownTimerS = object : CountDownTimer(60 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("进入验证模式失败,请确认贴件状态,并重新进入") + countDownTimerS = null + } + + override fun onTick(millisUntilFinished: Long) { + } + }.start() + } + } + } + } + + @Subscribe + fun showDeviceSettingData(event: DeviceSettingEvent?) { + try { + if (event?.data != null && event.mac != null&&event.flag!=null) { + if(Utils.formatMac(event.mac, ":").equals(mMac)){ + if(event.flag==4) { + var start = HexUtil.byteToInt(event?.data[0]) + when (HexUtil.byteToInt(event?.data[1])) { + 0 -> { + when (HexUtil.byteToInt(event?.data[2])) { + 1 -> { + Log.i(TAG, event.mac + "频率设置成功") + } + 255 -> { + if(start==0) { + countDownTimerS!!.cancel() + countDownTimerS = null + Log.i(TAG, event.mac + "算法模型1学习成功") + ToastUtil.showToast("“打开”模型学习成功") +// var timeString = TimeUtil.getNowTime() + mStep = 0 + setImage() + rl_open.background = + getDrawable(R.drawable.shape_model_init_gray_bg) + tv_open_title.setTextColor(getColor(R.color.color_333333)) + v_open_status.background = + getDrawable(R.drawable.shape_model_init_circel_black) + tv_open_status.text = "上次学习时间:${timeString}" + tv_open_status.setTextColor(getColor(R.color.color_333333)) + tv_open_next.text = "开始学习" + tv_open_next.visibility = View.VISIBLE + tv_close_next.visibility = View.VISIBLE + v_close_bottom.visibility = View.VISIBLE + iv_open.visibility = View.VISIBLE + ll_open.visibility = View.GONE + tv_open_cancel.visibility = View.GONE + tv_open_no_response.visibility = View.GONE + v_open_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_open_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + iv_open_threshold.visibility = View.VISIBLE + + if (iv_open_threshold.visibility == View.VISIBLE && iv_close_threshold.visibility == View.VISIBLE) { + tv_verify.background = + getDrawable(R.drawable.shape_model_init_verify_btn_blue_bg) + tv_verify.setTextColor(getColor(R.color.white)) + } else { + tv_verify.background = + getDrawable(R.drawable.shape_model_init_verify_btn_gray_bg) + tv_verify.setTextColor(Color.parseColor("#ffa1a1a1")) + } + } + } + 254 -> { + deviceInfo!!.model1Threshold = model1Threshold + Log.i(TAG, event.mac + "算法模型1阈值设置成功") + ToastUtil.showToast("“打开”模型阈值设置成功") + dismissDialog() + } + 253 -> { + if(start==0) { + countDownTimerS!!.cancel() + countDownTimerS = null + Log.i(TAG, event.mac + "算法模型2学习成功") + ToastUtil.showToast("“关闭”模型学习成功") +// var timeString = TimeUtil.getNowTime() + mStep = 0 + setImage() + rl_close.background = + getDrawable(R.drawable.shape_model_init_gray_bg) + tv_close_title.setTextColor(getColor(R.color.color_333333)) + v_close_status.background = + getDrawable(R.drawable.shape_model_init_circel_black) + tv_close_status.text = "上次学习时间:${timeString}" + tv_close_status.setTextColor(getColor(R.color.color_333333)) + tv_close_next.text = "开始学习" + tv_close_next.visibility = View.VISIBLE + tv_open_next.visibility = View.VISIBLE + v_open_bottom.visibility = View.VISIBLE + iv_close.visibility = View.VISIBLE + ll_close.visibility = View.GONE + tv_close_cancel.visibility = View.GONE + tv_close_no_response.visibility = View.GONE + v_close_top.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_bottom.setBackgroundColor(Color.parseColor("#C6CFD9")) + v_close_center.setBackgroundColor(Color.parseColor("#C6CFD9")) + iv_close_threshold.visibility = View.VISIBLE + + if (iv_open_threshold.visibility == View.VISIBLE && iv_close_threshold.visibility == View.VISIBLE) { + tv_verify.background = + getDrawable(R.drawable.shape_model_init_verify_btn_blue_bg) + tv_verify.setTextColor(getColor(R.color.white)) + } else { + tv_verify.background = + getDrawable(R.drawable.shape_model_init_verify_btn_gray_bg) + tv_verify.setTextColor(Color.parseColor("#ffa1a1a1")) + } + } + } + 252 -> { + deviceInfo!!.model2Threshold = model2Threshold + Log.i(TAG, event.mac + "算法模型2阈值设置成功") + ToastUtil.showToast("“关闭”模型阈值设置成功") + dismissDialog() + } + } + } + 252 -> Log.i(TAG, event.mac + "设备忙碌") + 251 -> Log.i(TAG, event.mac + "保存参数失败") + 250 -> Log.i(TAG, event.mac + "算法模型学习失败") + 249 -> Log.i(TAG, event.mac + "指令无效") + } + }else if(event.flag == 5){ + when (HexUtil.byteToInt(event.data[2])) { + 0 -> { + when (HexUtil.byteToInt(event.data[1])) { + 0 -> Log.i(TAG, event.mac + "正常工作成功") + 1 -> Log.i(TAG, event.mac + "强制休眠成功") + 2 -> { + when (HexUtil.byteToInt(event.data[0])) { + 0 -> { + dismissDialog() + countDownTimerS!!.cancel() + countDownTimerS = null + Log.i(TAG, event.mac + "结束模型验证成功") + ToastUtil.showToast("退出模型验证模式") + } + 1 -> { + dismissDialog() + countDownTimerS!!.cancel() + countDownTimerS = null + Log.i(TAG, event.mac + "开启模型验证成功") + ToastUtil.showToast("进入模型验证模式") + DataServer.nBlueToothVerifyMac = mMac +// values.add(Entry(0F, 0F)) //其中两个数字对应的分别是 X轴 Y轴 +// values.add(Entry(1F, 1F)) +// values.add(Entry(2F, 0F)) +// values.add(Entry(3F, 1F)) +// values.add(Entry(4F, 0F)) + ModeInitVerifyDialog.instance!!.getShareDialog(this, + object : ModeInitVerifyDialog.PopupYearWindowCallBack { + override fun doWork() { + showDialog() + BluetoothDeviceManager.getInstance() + .setMode(mMac, false,2) + countDownTimerS = object : CountDownTimer(180 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + dismissDialog() + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("退出模型验证模式") + countDownTimerS = null + } + + override fun onTick(millisUntilFinished: Long) { + if(millisUntilFinished/1000<120) { + if (!isShowVerifyToast) { + isShowVerifyToast = true + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("退出验证模式失败,120s后自动退出") + } + } + } + }.start() + } + },values,xValues) + } + } + } + 3 -> Log.i(TAG, event.mac + "开启模型2验证成功") + } + } + 252 -> Log.i(TAG, event.mac + "设备忙碌") + 251 -> Log.i(TAG, event.mac + "保存参数失败") + 250 -> Log.i(TAG, event.mac + "算法模型学习失败") + 249 -> Log.i(TAG, event.mac + "指令无效") + } + }else if(event.flag == 6){ + xValues!!.add(TimeUtil.getNowTimeHourMinuteSecond()!!) + when (HexUtil.byteToInt(event.data[1])) { + 255 -> { + values!!.add(Entry((xValues.size-1).toFloat(), 1F)) + } + 254 -> { + values!!.add(Entry((xValues.size-1).toFloat(), 0F)) + } + } + ModeInitVerifyDialog.instance!!.updateData(xValues,values) + } + } + } + } catch (e: java.lang.Exception) { + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/MyEquipmentActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/MyEquipmentActivity.kt new file mode 100644 index 0000000..98dc013 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/MyEquipmentActivity.kt @@ -0,0 +1,498 @@ +package com.qidian.zhongkesmart.activity + +import android.Manifest +import android.content.Intent +import android.graphics.Color +import android.net.Uri +import android.os.CountDownTimer +import android.provider.Settings +import android.util.Log +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.qidian.baseble.ViseBle +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.dialog.* +import com.qidian.zhongkesmart.model.ActionInfo +import com.qidian.zhongkesmart.model.BindBlueToothData +import com.qidian.zhongkesmart.model.DeviceInfo +import com.qidian.zhongkesmart.model.GetTokenM +import com.qidian.zhongkesmart.utils.* +import com.qidian.zhongkesmart.utils.ObjectBoxUtils.addData +import com.qidian.zhongkesmart.utils.TimeUtil.getNowTime +import com.qidian.zjlw.presenter.Contract +import com.qidian.zjlw.presenter.MainPresenter +import com.qidian.zxing.lib_zxing.activity.CodeUtils +import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener +import com.scwang.smartrefresh.layout.listener.OnRefreshListener +import kotlinx.android.synthetic.main.activity_base.* +import kotlinx.android.synthetic.main.activity_my_equipment.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.item_myequipment.* +import kotlinx.android.synthetic.main.layout_list.* +import kotlinx.android.synthetic.main.view_status.* +import net.idik.lib.slimadapter.SlimAdapter +import net.idik.lib.slimadapter.SlimInjector +import net.idik.lib.slimadapter.viewinjector.IViewInjector +import pub.devrel.easypermissions.EasyPermissions + +/*我的设备*/ +class MyEquipmentActivity : BaseActivity(), EasyPermissions.PermissionCallbacks, + Contract.GovernmentView { + var mData = ArrayList() + private val mPresenter by lazy { MainPresenter() } + private val REQUEST_CODE = 100 + var mTJMac = "" + var countDownTimer: CountDownTimer? = null + var isResume = false + + override fun getLayoutId(): Int { + return R.layout.activity_my_equipment + } + + override fun initView() { + mPresenter.attachView(this) + refreshLayout_list.setOnRefreshListener(mOnRefreshListener) + refreshLayout_list.setOnLoadMoreListener(mOnLoadMoreListener) + LoadUtils.loadingasGridview(this, recycle_list, 3) + mAdapter = + SlimAdapter.create() + .register( + R.layout.item_myequipment, + object : SlimInjector { + override fun onInject( + data: DeviceInfo, + injector: IViewInjector> + ) { + injector.with(R.id.imv_myequipment) { + it.setImageResource(ToolUtil.getMyEquitmentPageImage(data)) + } + injector.text(R.id.tv_myequipment_open_status, ToolUtil.getMyEquitmentOpenStatus(data.mac)) + injector.text(R.id.tv_myequipment_name, data.type) + injector.text(R.id.tv_myequipment_type, data.name) + injector.text(R.id.tv_myequipment_bluetype, "低功耗蓝牙") + injector.with(R.id.tv_myequipment_status) { + //是否在线 + if (data.isOnline) { + injector.text(R.id.tv_myequipment_status, "正常") + it.setTextColor(Color.parseColor("#0079F5")) + } else { + injector.text(R.id.tv_myequipment_status, "离线") + it.setTextColor(Color.parseColor("#99333333")) + } + } + Log.e(TAG,data.power) +// Log.e(TAG,Integer.valueOf(data.power,16).toString()) +// injector.text(R.id.tv_myequipment_power, "电量:${data.power}%") + if(data.power == ""){ + injector.text( + R.id.tv_myequipment_power, + "0%" + ) + }else { + injector.text( + R.id.tv_myequipment_power, + "${Integer.valueOf(data.power, 10)}%" + ) + } + injector.clicked(R.id.li_myequipment) { + startActivity( + Intent( + this@MyEquipmentActivity, + MyEquipmentDetailsActivity::class.java + ).putExtra("mac", data.mac) + ) + } + injector.with(R.id.li_myequipment) { + + } + + } + }) + .attachTo(recycle_list) + getData() + //添加设备 + tv_button.setOnClickListener { + if (DataServer.mHttpToken.isNullOrEmpty()) { + ToastUtil.showToast("您还未绑定网关") + } else { + askPermissionOfCamera() + } + } + + } + + /** + * 绑定贴件信息 + */ + fun getData() { + var dList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) + var rList = ArrayList() + dList!!.forEach { + if(it.isBind){ + rList.add(it) + } + } + if (rList.isNullOrEmpty()) { + recycle_list.visibility = View.GONE + tv_myequipment_num.visibility = View.GONE + layEmpty.visibility = View.VISIBLE + } else { + mData.clear() + mData.addAll(rList) + mAdapter!!.updateData(mData).notifyDataSetChanged() + recycle_list.visibility = View.VISIBLE + tv_myequipment_num.visibility = View.VISIBLE + layEmpty.visibility = View.GONE + tv_myequipment_num.text = "${mData.size}个" + } + } + + override fun initData() { + } + + override fun isShowTitle(): Boolean { + return false + } + + /** + * 刷新 + */ + private val mOnRefreshListener = OnRefreshListener { + refreshLayout_list.finishRefresh() + } + + /** + * 加载 + */ + private val mOnLoadMoreListener = OnLoadMoreListener { + refreshLayout_list.finishLoadMore() + /*if (!isLoadingMore) { + isLoadingMore = true + currentPage++ + getData() + }*/ + + } + + fun onClick(v: View) { + when (v.id) { + R.id.tv_button -> { + recycle_list.visibility = View.VISIBLE + tv_myequipment_num.visibility = View.VISIBLE + layEmpty.visibility = View.GONE + } + + R.id.iv_back -> { + onBackPressed() + } + + R.id.imv_index_add -> { + if (DataServer.mHttpToken.isNullOrEmpty()) { + ToastUtil.showToast("您还未绑定网关") + } else { + askPermissionOfCamera() + } +// for (i in 0 until 2000) { +// val actionInfo = ActionInfo() +// actionInfo.mac = "C7:3D:41:CB:A8:36" +// if(i%2==0) { +// actionInfo.data = byteArrayOf(0x00, 0xff.toByte()) +// }else{ +// actionInfo.data = byteArrayOf(0x00, 0xfe.toByte()) +// } +// actionInfo.time = getNowTime()!! +// addData(actionInfo, ActionInfo::class.java) +// } + } + } + } + + private fun askPermissionOfCamera() { + EasyPermissions.requestPermissions( + this, + "二维码扫码,请允许获取您的摄像头权限", + DataServer.ACCESS_CAMERA_CODE, + Manifest.permission.CAMERA + ) + } + + + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_CAMERA_CODE) { + val intent = + Intent(this, com.qidian.zxing.lib_zxing.activity.CaptureActivity::class.java) + startActivityForResult(intent, REQUEST_CODE) + } + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_CAMERA_CODE) { + ToastUtil.showToast("摄像头权限获取失败,扫码功能不可用!") + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", packageName, null) + startActivity(intent) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CODE) { + //处理扫描结果(在界面上显示) + if (null != data) { + val bundle = data.extras + if (bundle != null) { + if (bundle.getInt(CodeUtils.RESULT_TYPE) == CodeUtils.RESULT_SUCCESS) { + val result = bundle.getString(CodeUtils.RESULT_STRING) + Log.i("Zxing_Result", result!!) +// var mTJMac = ToolUtil.formatMac(result!!) + mTJMac = result!! + FindNewEquipmentDialog.instance!!.getShareDialog(this, + mTJMac, + object : FindNewEquipmentDialog.PopupYearWindowCallBack { + //新增 + override fun doWork(roomName: String?, type: String?) { + if(!ToolUtil.isContainsTJData(roomName!!,type!!, mTJMac,this@MyEquipmentActivity)){ + mPresenter.getbindDevice( + this@MyEquipmentActivity, + mTJMac, + roomName!!, + type!!, + + ) + }else{ + ToastUtil.showToast("请勿重复添加") + } + + } + + //修改 + override fun doChangeWork(roomName: String?, type: String?) { + if(!ToolUtil.isContainsTJData(roomName!!,type!!, mTJMac,this@MyEquipmentActivity)) { + mPresenter.getChangeDevice( + this@MyEquipmentActivity, + mTJMac, + roomName, + type + ) + }else{ + ToastUtil.showToast("请勿重复添加") + } + } + + //解绑 + override fun doUnBindWork() { + mPresenter.removebindDevice(this@MyEquipmentActivity, mTJMac) + } + + override fun doConnectTj() { + if(DataServer.connectMode==2) { + if (!ViseBle.getInstance().deviceMirrorPool.isConnectDevice) { + FindNewEquipmentDialog.instance!!.connectFail() + return + } + } + + WakeUpDeviceDialog.instance!!.getShareDialog(this@MyEquipmentActivity, + object : WakeUpDeviceDialog.PopupYearWindowCallBack { + override fun doCancel() { + countDownTimer!!.cancel() + countDownTimer = null + DataServer.nBlueToothVerifyMac = "" + DataServer.nBlueToothMac = "" + } + }) + //广播模式下,需要连接贴件 + if(DataServer.connectMode==1){ + DataServer.nBlueToothMac = mTJMac + } + DataServer.nBlueToothVerifyMac = mTJMac + countDownTimer = object : CountDownTimer(60 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + ToastUtil.showToast("进入配置模式失败,请重新尝试") + WakeUpDeviceDialog.instance!!.setDismiss() + DataServer.nBlueToothVerifyMac = "" + DataServer.nBlueToothMac = "" + countDownTimer = null + } + + override fun onTick(millisUntilFinished: Long) { + } + }.start() + } + + override fun doDismiss() { + + } + + }) + + } else if (bundle.getInt(CodeUtils.RESULT_TYPE) == CodeUtils.RESULT_FAILED) { + ToastUtil.showToast("解析二维码失败") + } + } + } + } + } + + + //修改信息 + override fun showChangeResult(messModel: Any?, mac: String, type: String?, roomName: String?) { + ToastUtil.showToast("修改成功") + FindNewEquipmentDialog.instance!!.bindSuccess() + //修改本地该条记录 + ToolUtil.ChangeHistory( + this, + DataServer.mBindHistory, + mac, BindBlueToothData( + mac, + BlueToothUtils.instance!!.getBlueToothPower(mac), + roomName!!, + type!!, TimeUtil.getNowTime()!! + ) + ) + //修改该贴件接收蓝牙数据里面的type类型 (震动型、移动型) + ToolUtil.changeMacBlueToothHistory(mac, this) + + var list = ObjectBoxUtils.getDeviceByMac(mac) + var deviceInfo:DeviceInfo?; + if (list != null && list.isNotEmpty()) { + deviceInfo = list[0] + deviceInfo!!.isBind = true + deviceInfo!!.name = roomName!! + deviceInfo!!.type = type!! + } else { + deviceInfo = DeviceInfo() + deviceInfo.mac = mac + deviceInfo.isBind = true + deviceInfo.name = roomName!! + deviceInfo.type = type!! + } + ObjectBoxUtils.updateData(deviceInfo, DeviceInfo::class.java) + + getData() + } + + //新增绑定 + override fun showAddResult(messModel: Any?, mac: String, type: String?, roomName: String?) { + ToastUtil.showToast("绑定成功") + FindNewEquipmentDialog.instance!!.bindSuccess() + //存本地 + ToolUtil.addHistory( + this, + DataServer.mBindHistory, + BindBlueToothData( + mac, + BlueToothUtils.instance!!.getBlueToothPower(mac), + roomName!!, + type!!, TimeUtil.getNowTime()!! + ) + ) + var list = ObjectBoxUtils.getDeviceByMac(mac) + var deviceInfo: DeviceInfo?; + if (list != null && list.isNotEmpty()) { + deviceInfo = list[0] + deviceInfo!!.isBind = true + deviceInfo!!.time = TimeUtil.getNowTime()!! + deviceInfo!!.name = roomName!! + deviceInfo!!.type = type!! + } else { + deviceInfo = DeviceInfo() + deviceInfo.mac = mac + deviceInfo.isBind = true + deviceInfo.name = roomName!! + deviceInfo.type = type!! + deviceInfo.time = TimeUtil.getNowTime()!! + } + ObjectBoxUtils.updateData(deviceInfo, DeviceInfo::class.java) + getData() + } + + + //解除绑定 + override fun showRemoveResult(messModel: Any?, mac: String) { + ToastUtil.showToast("解除绑定成功") + FindNewEquipmentDialog.instance!!.setDismiss() + //清除本地该条记录 + ToolUtil.deleteHistory( + this, + DataServer.mBindHistory, + mac + ) + //将本地存储的蓝牙接收数据中的该贴件的信息删除 + ToolUtil.clearMacBlueToothHistory(mac) + ObjectBoxUtils.deleteALLDataD(ObjectBoxUtils.getDeviceByMac(mac)) + ObjectBoxUtils.deleteALLDataA(ObjectBoxUtils.getActionByMac(mac)) + getData() + } + + //获取token + override fun showTokenResult(messModel: Any?) { + var model = messModel as GetTokenM + var mToken = model.token + DataServer.mHttpToken = mToken + askPermissionOfCamera() + } + + override fun showAfter() { + } + + + override fun receiveEvent(event: EventBusUtil.MessageEvent) { + super.receiveEvent(event) + when (event.code) { + /** + * 蓝牙数据推送-正常离线状态改变时候更新数据 + */ + EventCode.BLUETOOTHDATA_Refresh -> { + if(isResume) { + mAdapter!!.notifyDataSetChanged() + } + } + + /** + * 贴件唤醒成功 + */ + EventCode.DEVICE_WAKE_UP_SUCCEED -> { + if(isResume) { + if (WakeUpDeviceDialog.instance!!.isShowing()) { + WakeUpDeviceDialog.instance!!.setDismiss() + FindNewEquipmentDialog.instance!!.setDismiss() + countDownTimer!!.cancel() + countDownTimer = null + startActivity( + Intent( + this@MyEquipmentActivity, + SettingModeActivity::class.java + ).putExtra("mac", mTJMac) + ) + } + } + } + } + } + + override fun onResume() { + super.onResume() + isResume = true + } + + override fun onPause() { + super.onPause() + isResume = false + } + + + override fun onDestroy() { + super.onDestroy() + try { + FindNewEquipmentDialog.instance!!.setDismiss() + } catch (e: Exception) { + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/MyEquipmentDetailsActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/MyEquipmentDetailsActivity.kt new file mode 100644 index 0000000..1e69afd --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/MyEquipmentDetailsActivity.kt @@ -0,0 +1,298 @@ +package com.qidian.zhongkesmart.activity + +import android.annotation.SuppressLint +import android.graphics.Color +import android.os.Handler +import android.os.Message +import android.util.Log +import android.view.View +import com.blankj.utilcode.util.TimeUtils +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet +import com.google.android.material.tabs.TabLayout +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.model.* +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.activity_my_equipment.* +import kotlinx.android.synthetic.main.activity_my_equipment_details.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.layout_barchart.* +import kotlinx.android.synthetic.main.layout_list.* + + +/*我的设备详情*/ +class MyEquipmentDetailsActivity : BaseActivity() { + var xValues = ArrayList() + val values = ArrayList() + val values1 = ArrayList() + var mTabs = arrayOf("今日", "本周", "本月", "本年") + var mMac="" + var mTodayData=ArrayList() + var mWeekData=ArrayList() + var mMonthData=ArrayList() + var mYearData=ArrayList() + var deviceInfo: DeviceInfo? = null + var tapPositon = 0 + + override fun getLayoutId(): Int { + return R.layout.activity_my_equipment_details + } + + override fun initView() { + mMac=intent.getStringExtra("mac").toString() + deviceInfo = ObjectBoxUtils.getDeviceByMac(mMac)!![0] + tv_left_title.text = "${deviceInfo!!.name}-${deviceInfo!!.type}" + //是否在线 + if(deviceInfo!!.isOnline){ + tv_myequipment_status.text="正常" + tv_myequipment_mark.setBackgroundResource(R.drawable.ed_yuan0fba61) + }else{ + tv_myequipment_status.text="离线" + tv_myequipment_mark.setBackgroundResource(R.drawable.ed_yuangray) + } + //电量 +// tv_myequipment_power.text="${BlueToothUtils.instance!!.getBlueToothPower(mMac)}%" + if(deviceInfo!!.power==""){ + tv_myequipment_power.text="0%" + }else { + tv_myequipment_power.text = + "${Integer.valueOf(deviceInfo!!.power, 10)}%" + } + //Tab + mTabs.forEach { + tablayout_type.addTab(tablayout_type.newTab().setText(it)) + } + tablayout_type.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabReselected(p0: TabLayout.Tab?) { + } + + override fun onTabUnselected(p0: TabLayout.Tab?) { + } + + override fun onTabSelected(p0: TabLayout.Tab?) { + showDialog() + tapPositon = p0!!.position + startViewThread(tapPositon) + } + + }) + } + + override fun initData() { + //查取位置用途信息 +// var myEquipmentData= ToolUtil.getHistory(DataServer.mBindHistory,this) +// myEquipmentData.forEach { +// if(it.mac==mMac){ +// +// } +// } + showDialog() + startThread() + } + + @SuppressLint("HandlerLeak") + var handler: Handler = object : Handler() { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + + + Log.e("Handle==", "ing") + Log.e("Handle==", "tapPositon "+tapPositon) + Log.e("Handle==", "xValues.size "+xValues.size) + Log.e("Handle==", "values.size "+values.size) + Log.e("Handle==", "values1.size "+values1.size) + this.removeMessages(0) + dismissDialog() + + barchart.zoom(0f, 1f, 0f, 0f) + when(tapPositon){ + 0->{//今日 + barchart.zoom(0f, 1f, 0f, 0f) + barchart.zoom(2f, 1f, 0f, 0f) + } + 1->{//本周 + barchart.zoom(0f, 1f, 0f, 0f) + barchart.zoom(1f, 1f, 0f, 0f) + } + 2->{//本月 + barchart.zoom(0f, 1f, 0f, 0f) + barchart.zoom(3f, 1f, 0f, 0f) + } + 3->{//本年 + barchart.zoom(0f, 1f, 0f, 0f) + barchart.zoom(1f, 1f, 0f, 0f) + } + } + var set = BarDataSet(values, "开启次数") + set!!.color = Color.parseColor("#516AFC") + var set1 = BarDataSet(values1, "关闭次数") + set1!!.color = Color.parseColor("#516A33") + val dataSetbar: ArrayList = ArrayList() + dataSetbar.add(set) + dataSetbar.add(set1) + var data = BarData(dataSetbar) + data.barWidth = 0.5f + //有数据时候传true 无数据时候传false + if (values.size > 0) { + ChartUtils.initBarChart( + barchart, + xValues, + data!!, + true, + false, + ) + } else { + ChartUtils.initBarChart( + barchart, + xValues, + data!!, + true, + ) + } + } + } + + fun startThread() { + Thread(object : Runnable { + override fun run() { + //读取与写入 + getData() + handler.sendEmptyMessage(0) + } + }).start() + } + + override fun isShowTitle(): Boolean { + return false + } + + @Synchronized + fun getData(){ + //查取图表信息 + var aList = ObjectBoxUtils.getActionByMac(mMac) + aList!!.forEach { + var openTime = TimeUtils.string2Millis(it.time) + if(TimeUtil.isInToday(openTime)){ + mTodayData.add(TimeDataWithTime(openTime,0,"", it.data!!)) +// showLog(mTodayData) + } + if(TimeUtil.isInWeek(openTime)){ + mWeekData.add(TimeDataWithTime(openTime,0,"", it.data!!)) +// showLog(mWeekData) + } + if(TimeUtil.isInMonth(openTime)){ + mMonthData.add(TimeDataWithTime(openTime,0,"", it.data!!)) +// showLog(mMonthData) + } + if(TimeUtil.isInYear(openTime)){ + mYearData.add(TimeDataWithTime(openTime,0,"", it.data!!)) +// showLog(mYearData) + } + } + getViewData(tapPositon) + } + + fun startViewThread(poi:Int){ + Thread(object : Runnable { + override fun run() { + //读取与写入 + getViewData(poi) + handler.sendEmptyMessage(0) + } + }).start() + } + + + @Synchronized + fun getViewData(poi: Int) { + xValues.clear() + values.clear() + values1.clear() + //设置缩放比例 +// barchart.zoom(0f, 1f, 0f, 0f) + var list=ArrayList() + when(poi){ + 0->{//今日 + list.addAll(TimeUtil.formatTodayDataNew(mTodayData)) +// barchart.zoom(0f, 1f, 0f, 0f) +// barchart.zoom(2f, 1f, 0f, 0f) + } + 1->{//本周 + list.addAll(TimeUtil.formatWeekDataNew(mWeekData)) +// barchart.zoom(0f, 1f, 0f, 0f) +// barchart.zoom(1f, 1f, 0f, 0f) + } + 2->{//本月 + list.addAll(TimeUtil.formatMonthDataNew(mMonthData)) +// barchart.zoom(0f, 1f, 0f, 0f) +// barchart.zoom(3f, 1f, 0f, 0f) + } + 3->{//本年 + list.addAll(TimeUtil.formatYearDataNew(mYearData)) +// barchart.zoom(0f, 1f, 0f, 0f) +// barchart.zoom(1f, 1f, 0f, 0f) + } + } +// showLog(list) + for(i in 0 until list.size){ + //X轴显示 + xValues.add(list[i].timeLocation) + //柱状图数据 +// values.add(BarEntry(i * 1f, (list[i].mSize).toFloat())) + values.add(BarEntry(i * 1f, (list[i].mSizeO).toFloat())) + values1.add(BarEntry(i * 1f, (list[i].mSizeC).toFloat())) + } + /* for (i in 0..10) { + //X轴显示 + xValues.add("0${i}:00") + //柱状图数据 + values.add(BarEntry(i * 1f, i * poi * 10f)) + }*/ + +// var set = BarDataSet(values, "开启次数") +// set!!.color = Color.parseColor("#516AFC") +// +// var set1 = BarDataSet(values1, "关闭次数") +// set1!!.color = Color.parseColor("#516A33") +// val dataSetbar: ArrayList = ArrayList() +// dataSetbar.add(set) +// dataSetbar.add(set1) +// var data = BarData(dataSetbar) +// data.barWidth = 0.5f +// //有数据时候传true 无数据时候传false +// if (values.size > 0) { +// ChartUtils.initBarChart( +// barchart, +// xValues, +// data!!, +// true, +// false, +// ) +// } else { +// ChartUtils.initBarChart( +// barchart, +// xValues, +// data!!, +// true, +// ) +// } + + } + + fun showLog(list: Any){ + var json = ToolUtil.getJson(list) + Log.i("我的设备数据==", json!!) + } + + fun onClick(v: View) { + when (v.id) { + R.id.iv_back -> { + onBackPressed() + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/SelectCallUserActivity.java b/app/src/main/java/com/qidian/zhongkesmart/activity/SelectCallUserActivity.java new file mode 100644 index 0000000..e472c63 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/SelectCallUserActivity.java @@ -0,0 +1,394 @@ +package com.qidian.zhongkesmart.activity; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.blankj.utilcode.util.NetworkUtils; +import com.blankj.utilcode.util.SPStaticUtils; +import com.blankj.utilcode.util.ToastUtils; +import com.qidian.zhongkesmart.R; +import com.qidian.zhongkesmart.activity.adapter.RecentUserAdapter; +import com.qidian.zhongkesmart.config.EventCode; +import com.qidian.zhongkesmart.model.CallServiceManager; +import com.qidian.zhongkesmart.model.LoginServiceManager; +import com.qidian.zhongkesmart.model.ProfileManager; +import com.qidian.zhongkesmart.model.UserCacheManager; +import com.qidian.zhongkesmart.model.UserModel; +import com.qidian.zhongkesmart.model.base.BaseService; +import com.qidian.zhongkesmart.utils.EventBusUtil; +import com.qidian.zhongkesmart.views.VerifyCodeView; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class SelectCallUserActivity extends AppCompatActivity implements View.OnClickListener { + private static final String TAG = SelectCallUserActivity.class.getSimpleName(); + @BindView(R.id.tv_base_title) + TextView tvBaseTitle; + @BindView(R.id.edt_phone_number) + EditText edtPhoneNumber; + @BindView(R.id.tv_self_number) + TextView tvSelfNumber; + @BindView(R.id.tv_empty) + TextView tvEmpty; + @BindView(R.id.rv_search_result) + RecyclerView rvSearchResult; + @BindView(R.id.tv_recently_search) + TextView tvRecentlySearch; + @BindView(R.id.rv_recent_user) + RecyclerView rvRecentUser; + @BindView(R.id.iv_clear) + ImageView ivClear; + @BindView(R.id.rl_base_title) + RelativeLayout rlBaseTitle; + @BindView(R.id.rl_back) + RelativeLayout rlBack; + @BindView(R.id.btn_search) + Button btnSearch; + + private final String UserPhoneId = "UserPhoneId"; + private static final int PHONE_NUMBER_MAX_LENGTH = 11; + private static final int SMS_CODE_LENGTH = 4; + private static final int THOUSAND_MS = 1000; + private static final int SIXTY_THOUSAND_MS = 60000; + private CountDownTimer countDownTimer; + + private RecentUserAdapter userAdapter; + private RecentUserAdapter searchAdapter; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_select_call_user); + ButterKnife.bind(this); + + initView(); + } + + private void initView() { + rlBack = findViewById(R.id.rl_back); + rlBack.setOnClickListener(this); + btnSearch = findViewById(R.id.btn_search); + btnSearch.setOnClickListener(this); + tvBaseTitle = findViewById(R.id.tv_base_title); + edtPhoneNumber = findViewById(R.id.edt_phone_number); + tvSelfNumber = findViewById(R.id.tv_self_number); + tvEmpty = findViewById(R.id.tv_empty); + rvSearchResult = findViewById(R.id.rv_search_result); + rlBaseTitle = findViewById(R.id.rl_base_title); + ivClear = findViewById(R.id.iv_clear); + ivClear.setOnClickListener(this); + rvRecentUser = findViewById(R.id.rv_recent_user); + tvRecentlySearch = findViewById(R.id.tv_recently_search); + rlBaseTitle.setVisibility(View.VISIBLE); + tvBaseTitle.setText("语音通话"); + edtPhoneNumber.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void afterTextChanged(Editable editable) { + if (!TextUtils.isEmpty(editable)) { + ivClear.setVisibility(View.VISIBLE); + } else { + ivClear.setVisibility(View.GONE); + } + } + }); + + rvRecentUser.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)); + + rvSearchResult = findViewById(R.id.rv_search_result); + rvSearchResult.setLayoutManager(new LinearLayoutManager(this)); + searchAdapter = new RecentUserAdapter(this); + rvSearchResult.setAdapter(searchAdapter); + + if (TextUtils.isEmpty(SPStaticUtils.getString("UserPhoneId"))) { + showInputPhoneDialog(); + } else { + initData(); + } + } + + private void initData() { + UserModel currentUser = ProfileManager.getInstance().getUserModel(); + tvSelfNumber.setText(String.format(getString(R.string.your_phone_number_is), currentUser.mobile)); + + UserCacheManager.getInstance().getLastSearchUser(users -> runOnUiThread(() -> { + if (userAdapter == null) { + userAdapter = new RecentUserAdapter(SelectCallUserActivity.this); + } + rvRecentUser.setAdapter(userAdapter); + userAdapter.updateUsers(users); + if (users != null && !users.isEmpty()) { + tvRecentlySearch.setVisibility(View.VISIBLE); + } + })); + } + + @Override + protected void onResume() { + super.onResume(); + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.FINISH_CALL_VIDEO)); + } + + private void showInputPhoneDialog() { + View root = View.inflate(this, R.layout.pop_input_phone_id, null); + @SuppressLint("RestrictedApi") + AlertDialog alert = new AlertDialog.Builder(this, R.style.transparentDialogDark) + .setView(root, 0, 0, 0, 0) + .setCancelable(false).create(); + + LinearLayout llClose = root.findViewById(R.id.li_close); + llClose.setOnClickListener(view -> { + finish(); + }); + + EditText mEdtPhoneNumber = root.findViewById(R.id.edt_phone_number); + + TextView tvS = root.findViewById(R.id.tv_pop_sure); + tvS.setOnClickListener(view -> { + String phoneNumber = mEdtPhoneNumber.getText().toString().trim(); + if (!TextUtils.isEmpty(phoneNumber)) { + if (phoneNumber.length() < PHONE_NUMBER_MAX_LENGTH) { + ToastUtils.showShort(R.string.login_phone_number_cant_less_than_eleven); + return; + } + if (!NetworkUtils.isConnected()) { + ToastUtils.showShort(R.string.network_connect_error_please_try_again); + return; + } + LoginServiceManager.getInstance().sendMessage(phoneNumber, new BaseService.ResponseCallBack() { + + @Override + public void onSuccess(Void response) { + showVerifyCodeDialog(phoneNumber); + alert.dismiss(); + } + + @Override + public void onFail(int code) { + + } + }); + } else { + ToastUtils.showShort(R.string.login_phone_number_cant_null); + } + // alert.dismiss(); + // showVerifyCodeDialog("18810640267"); + }); + + + alert.requestWindowFeature(Window.FEATURE_NO_TITLE); + Window window = alert.getWindow(); + window.setGravity(Gravity.CENTER); + + if (!SelectCallUserActivity.this.isFinishing()) + alert.show(); + } + + private void showVerifyCodeDialog(String phoneNumber) { + View root = View.inflate(this, R.layout.pop_verify_phone_code, null); + @SuppressLint("RestrictedApi") + AlertDialog alert = new AlertDialog.Builder(this, R.style.transparentDialogDark) + .setView(root, 0, 0, 0, 0) + .setCancelable(false).create(); + + LinearLayout llClose = root.findViewById(R.id.li_close); + llClose.setOnClickListener(view -> { + finish(); + }); + + VerifyCodeView verifyCodeView;//验证码输入框 + TextView tvMsmComment; + TextView tvTimeCountDown; + TextView tvResendMsm; + + verifyCodeView = root.findViewById(R.id.vcv_sms); + tvMsmComment = root.findViewById(R.id.tv_msm_comment); + tvTimeCountDown = root.findViewById(R.id.tv_time_discount); + tvResendMsm = root.findViewById(R.id.tv_resend_msm); + + tvMsmComment.setText(getString(R.string.login_sms_code_has_been_sent) + phoneNumber + getString(R.string.login_please_input_sms_code)); + + tvTimeCountDown.setText(R.string.sixty_second); + tvResendMsm.setVisibility(View.VISIBLE); + tvTimeCountDown.setEnabled(false); + + countDownTimer = new CountDownTimer(SIXTY_THOUSAND_MS, THOUSAND_MS) { + @Override + public void onTick(long l) { + tvTimeCountDown.setText((l / THOUSAND_MS) + getString(R.string.login_second)); + } + + @Override + public void onFinish() { + tvTimeCountDown.setText(R.string.login_resend); + tvTimeCountDown.setEnabled(true); + tvResendMsm.setVisibility(View.GONE); + } + }; + countDownTimer.start(); + + tvTimeCountDown.setOnClickListener(v -> { + if (!NetworkUtils.isConnected()) { + ToastUtils.showShort(R.string.network_connect_error_please_try_again); + return; + } + if (!TextUtils.isEmpty(phoneNumber)) { + LoginServiceManager.getInstance().sendMessage(phoneNumber, new BaseService.ResponseCallBack() { + + @Override + public void onSuccess(Void response) { + ToastUtils.showLong(R.string.login_sms_code_send_success); + tvTimeCountDown.setText(R.string.sixty_second); + tvResendMsm.setVisibility(View.VISIBLE); + tvTimeCountDown.setEnabled(false); + + countDownTimer = new CountDownTimer(SIXTY_THOUSAND_MS, THOUSAND_MS) { + @Override + public void onTick(long l) { + tvTimeCountDown.setText((l / THOUSAND_MS) + getString(R.string.login_second)); + } + + @Override + public void onFinish() { + tvTimeCountDown.setText(R.string.login_resend); + tvTimeCountDown.setEnabled(true); + tvResendMsm.setVisibility(View.GONE); + } + }; + + countDownTimer.start(); + } + + @Override + public void onFail(int code) { + ToastUtils.showLong(R.string.login_sms_code_send_fail); + } + }); + } + }); + + TextView tvS = root.findViewById(R.id.tv_pop_sure); + tvS.setOnClickListener(view -> { + if (!NetworkUtils.isConnected()) { + ToastUtils.showShort(R.string.network_connect_error_please_try_again); + return; + } + String smsCode = verifyCodeView.getResult(); + if (!TextUtils.isEmpty(smsCode) && smsCode.length() == SMS_CODE_LENGTH) { + if (!TextUtils.isEmpty(phoneNumber) && !TextUtils.isEmpty(smsCode)) { + LoginServiceManager.getInstance().loginWithSms(phoneNumber, smsCode, new BaseService.ResponseCallBack() { + @Override + public void onSuccess(Void response) { + SPStaticUtils.put("UserPhoneId", phoneNumber); + ToastUtils.showLong(R.string.login_success); + alert.dismiss(); + } + + @Override + public void onFail(int code) { + ToastUtils.showLong(R.string.login_fail); + } + }); + } + } else { + ToastUtils.showShort(R.string.login_please_input_correct_sms_code); + } + }); + + + alert.requestWindowFeature(Window.FEATURE_NO_TITLE); + Window window = alert.getWindow(); + window.setGravity(Gravity.CENTER); + + if (!SelectCallUserActivity.this.isFinishing()) + alert.show(); + + WindowManager.LayoutParams params = window.getAttributes(); + params.width = 580; + window.setAttributes(params); + } + + @OnClick({R.id.rl_back, R.id.iv_clear, R.id.btn_search}) + public void onClick(View view) { + switch (view.getId()) { + case R.id.rl_back: + onBackPressed(); + break; + case R.id.iv_clear: + edtPhoneNumber.setText(""); + break; + case R.id.btn_search: + if (!NetworkUtils.isConnected()) { + Toast.makeText(SelectCallUserActivity.this, R.string.nertc_no_network, Toast.LENGTH_SHORT).show(); + return; + } + String phoneNumber = edtPhoneNumber.getText().toString().trim(); + if (!TextUtils.isEmpty(phoneNumber)) { + CallServiceManager.getInstance().searchUserWithPhoneNumber(phoneNumber, new BaseService.ResponseCallBack() { + + @Override + public void onSuccess(UserModel response) { + hideKeyBoard(); + if (response != null) { + tvEmpty.setVisibility(View.GONE); + rvSearchResult.setVisibility(View.VISIBLE); + if (searchAdapter != null) { + searchAdapter.updateItem(response); + } + UserCacheManager.getInstance().addUser(response); + } else { + ToastUtils.showLong(R.string.nertc_cant_find_this_user); + } + } + + @Override + public void onFail(int code) { + + } + }); + } + break; + } + } + + private void hideKeyBoard() { + View view = getCurrentFocus(); + if (view != null) { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/ServiceActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/ServiceActivity.kt new file mode 100644 index 0000000..7a3a7d7 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/ServiceActivity.kt @@ -0,0 +1,249 @@ +package com.qidian.zhongkesmart.activity +/*设置服务器*/ + +import android.Manifest +import android.content.Intent +import android.graphics.Color +import android.os.Handler +import android.os.HandlerThread +import android.text.Editable +import android.text.TextUtils +import android.text.TextWatcher +import android.util.Log +import android.view.View +import com.blankj.utilcode.util.ToastUtils +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.model.ActionInfo +import com.qidian.zhongkesmart.model.DeviceInfo +import com.qidian.zhongkesmart.model.GetCodeM +import com.qidian.zhongkesmart.model.GetTokenM +import com.qidian.zhongkesmart.net.* +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.activity_service.* +import pub.devrel.easypermissions.EasyPermissions + + + +class ServiceActivity : BaseActivity(),EasyPermissions.PermissionCallbacks { + var ishaveContent = false + var mCode = "" + var mToken = "" + override fun getLayoutId(): Int { + return R.layout.activity_service + } + + override fun initView() { + ced_server_location!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int + ) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + if (s!!.isNotEmpty()) { +// InputLimitUtil.ShowEdittextStyle(ced_server_location!!, tv_woprn_01!!, "") + ced_server_location.setHintTextColor(Color.parseColor("#ffff3737")) + } + changeNextPic() + } + + }) + ced_server_portnumber!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int + ) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + if (s!!.isNotEmpty()) { + InputLimitUtil.ShowEdittextStyle2(ced_server_portnumber!!, tv_woprn_02!!, "") + } + changeNextPic() + } + + }) + } + + override fun initData() { +//测试默认值 + ced_server_location.setText("https://ifhs.fedhealth.cn/") + ced_server_location.hint="请输入服务器地址,例如:https://ifhs.fedhealth.cn/" + ced_server_portnumber.hint="请输入端口号,例如:15900" + + askPermissionOfBluetooth() + } + + /*判断是否都为空 端口号可为空*/ + fun changeNextPic() { + ishaveContent = + ced_server_location.text!!.isNotEmpty() /*&& ced_server_portnumber.text!!.isNotEmpty()*/ + if (ishaveContent) { + imv_service_jump.setBackgroundResource(R.drawable.shape_wifi_psw_join_bg) + imv_service_jump.setTextColor(Color.parseColor("#ffffffff")) + imv_service_jump.isEnabled = true + } else { + imv_service_jump.setBackgroundResource(R.drawable.shape_wifi_psw_join_gray_bg) + imv_service_jump.setTextColor(Color.parseColor("#ff333333")) + imv_service_jump.isEnabled = false + } + } + + fun onClick(v: View) { + when (v.id) { + R.id.imv_service_jump -> { + if (ced_server_location.text!!.isEmpty() /*|| ced_server_portnumber.text!!.isEmpty()*/) { + if (ced_server_location.text!!.isEmpty()) { + InputLimitUtil.ShowEdittextStyle( + ced_server_location, + tv_woprn_01, + "请输入服务器地址" + ) + } + /*if (ced_server_portnumber.text!!.isEmpty()) { + InputLimitUtil.ShowEdittextStyle2( + ced_server_portnumber, + tv_woprn_02, + "请输入端口号" + ) + }*/ + return + } + ApiUrl.setDomain(ToolUtil.getIpStr(ced_server_location.text!!.toString(),ced_server_portnumber.text!!.toString())) + Log.i("OkGo", ApiUrl.domain) + getHttpCode() +// getHttpLogin() + } + } + } + + /** + * 网关登录 + */ + //获取code + fun getHttpCode() { + Log.i("UUID==", DeviceUuidFactory.getAndroidID(this)) + var map = HashMap() + map["id"] = DeviceUuidFactory.getAndroidID(this) + HttpRequest.init(this).get(ApiUrl.getCode) + .setMap(map) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("响应数据", message!!) + mCode = `object`.toString() + getHttpLogin() + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtils.make().setBgColor(getColor(R.color.color_E1132C)).setTextColor(getColor(R.color.white)).setDurationIsLong(true).show("无法连接服务器") + Handler().postDelayed({getHttpCode()},120*1000) + return super.failed(code, info) + } + + }) + + } + + //网关登录 获取token存本地 这里如果绑定了就有token 不绑定就没有token 不影响后面测试网关是否连接成功 + fun getHttpLogin() { + var map = HashMap() + map["code"] = mCode + map["id"] = DeviceUuidFactory.getAndroidID(this) + HttpRequest.init(this).postJson(ApiUrl.getLogin, map,"",false) + .setClazz(GetTokenM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + var model = `object` as GetTokenM + mToken = model.token + DataServer.mHttpToken = mToken + } + + override fun after() { + super.after() + getLoginSuccess() + } + }) + } + + //测试网关是否连接成功 + fun getLoginSuccess() { + HttpRequest.init(this).get(ApiUrl.getLoginSuccess) + .setClazz(GetCodeM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("响应数据", message!!) +// var ddList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) +// var dList = ObjectBoxUtils.getAllData(ActionInfo::class.java) +// Log.e("BlueTooth", "device:"+ddList!!.size.toString()+" action:"+dList!!.size.toString()) +//// ObjectBoxUtils.deleteALLData(ActionInfo::class.java) +//// ObjectBoxUtils.deleteALLDataD(ObjectBoxUtils.getDeviceByMac("C7:3D:41:CB:A8:36")) +//// ObjectBoxUtils.deleteALLDataA(ObjectBoxUtils.getActionByMac("C7:3D:41:CB:A8:36")) +// var mode = SPUtil.getValue(DataServer.MConnectMode,Integer::class.java) +// if(mode!=null&&mode.toInt()!=0) { +//// ObjectBoxUtils.deleteDatabase() +//// ToolUtil.clearHistory(this@ServiceActivity, DataServer.mBindHistory) +//// SPUtil.setValue(DataServer.MConnectMode, 2) +//// SPUtil.setValue(DataServer.MRelayMac,"") +//// DataServer.connectMode =1 +//// //设置连接的贴件 +//// DataServer.nBlueToothMac = "C5:76:AB:D0:3B:D5" +//// startActivity(Intent(this@ServiceActivity, HomeActivity::class.java)) +//// startActivity(Intent(this@ServiceActivity, ModelInitActivity::class.java).putExtra("mac", "D5:3B:D0:AB:76:C5")) +// if(mode.toInt()==2) { +// DataServer.connectMode = 2 +// var relayMac = SPUtil.getValue(DataServer.MRelayMac,String::class.java) +// if(relayMac!=null&&!TextUtils.isEmpty(relayMac)) { +// startActivity(Intent(this@ServiceActivity, HomeActivity::class.java)) +// }else{ +// startActivity(Intent(this@ServiceActivity, ConnectModeActivity::class.java)) +// } +// }else{ +// DataServer.connectMode = 1 +// startActivity(Intent(this@ServiceActivity, HomeActivity::class.java)) +// } +// }else{ +// DataServer.connectMode = 0 +// startActivity(Intent(this@ServiceActivity, ConnectModeActivity::class.java)) +// } + finish() + } + + }) + } + + /** + * 获取蓝牙权限 + */ + private fun askPermissionOfBluetooth() { + EasyPermissions.requestPermissions( + this, + "请允许获取您的位置权限", + DataServer.ACCESS_BlueTooth_CODE, + Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION + ) + } + + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_BlueTooth_CODE) { + ToastUtil.showToast("蓝牙权限获取失败,APP功能不可用!") + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/SetLockActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/SetLockActivity.kt new file mode 100644 index 0000000..c2d897c --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/SetLockActivity.kt @@ -0,0 +1,70 @@ +package com.qidian.zhongkesmart.activity + +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.util.Log +import android.view.View +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.dialog.SelectFrequencyDialog +import com.qidian.zhongkesmart.dialog.SetLockDialog +import com.qidian.zhongkesmart.utils.EditUtil +import com.qidian.zhongkesmart.utils.ToastUtil +import com.tuo.customview.VerificationCodeView +import kotlinx.android.synthetic.main.activity_set_lock.* + +/*设置解锁页面*/ +class SetLockActivity : BaseActivity() { + + override fun getLayoutId(): Int { + return R.layout.activity_set_lock + } + + override fun initView() { +// icv_1.setInputCompleteListener(object : VerificationCodeView.InputCompleteListener { +// override fun inputComplete() { +// Log.i("icv_input", icv_1.inputContent) +// if(icv_1.inputContent.length==4){ +// if(icv_1.inputContent == "0000"){ +// //隐藏键盘 +// EditUtil.hideInput(this@SetLockActivity) +// startActivity(Intent(this@SetLockActivity, SettingActivity::class.java)) +// finish() +// }else{ +// ToastUtil.showToast("密码输入错误") +// } +// } +// } +// +// override fun deleteContent() { +// } +// +// }) + iv_icv.setOnClickListener(View.OnClickListener { + SetLockDialog.instance!!.getShareDialog(this, + object : SetLockDialog.PopupYearWindowCallBack { + override fun doWork() { + startActivity(Intent(this@SetLockActivity, SettingActivity::class.java)) + finish() + } + }) + }) + } + + override fun initData() { + } + + override fun isShowTitle(): Boolean { + return false + } + + fun onClick(v: View) { + when (v.id) { + R.id.iv_back -> { + onBackPressed() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/SetSeconedActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/SetSeconedActivity.kt new file mode 100644 index 0000000..0af9205 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/SetSeconedActivity.kt @@ -0,0 +1,99 @@ +package com.qidian.zhongkesmart.activity + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.viewpager.widget.ViewPager +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.fragments.SystemFragment +import com.qidian.zhongkesmart.fragments.TieJianFragment +import com.qidian.zhongkesmart.fragments.WangGuanFragment +import com.qidian.zhongkesmart.utils.DataServer +import kotlinx.android.synthetic.main.activity_set_seconed.* +import kotlinx.android.synthetic.main.base_title_layout.* + +class SetSeconedActivity : BaseActivity() { + val tabTitles = arrayOf("贴件设置", "网关设置", "系统设置") + var mJFragmentInstance: TieJianFragment? = null + var mWGFragmentInstance: WangGuanFragment? = null + var mSystemFragmentInstance: SystemFragment? = null + override fun getLayoutId(): Int { + return R.layout.activity_set_seconed + } + + override fun initView() { + DataServer.SetPageTag="" + setTitle("设置") + val fragmentArray = ArrayList() + mJFragmentInstance = TieJianFragment.newInstance() + fragmentArray.add(mJFragmentInstance!!) + mWGFragmentInstance = WangGuanFragment.newInstance() + fragmentArray.add(mWGFragmentInstance!!) + mSystemFragmentInstance = SystemFragment.newInstance() + fragmentArray.add(mSystemFragmentInstance!!) + tablayout.setViewPager(viewpager, tabTitles, this, fragmentArray) + /**解决默认第一个没加粗问题**/ + tablayout.setCurrentTab(1) + tablayout.setCurrentTab(0) + viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + if (position == 2) { + //证书信息 +// mQCFragmentInstance!!.showData(mCertificateList) + } + } + + override fun onPageScrollStateChanged(state: Int) { + + } + + }) + resetBack() + } + + override fun initData() { + + } + + /** + * SetPageTag + * 贴件设置: + * 贴件列表 1-1 + * 贴件详情 1-2 + * 贴件修改 1-3 + */ + //重写回退事件 + fun resetBack(){ + rl_back?.setOnClickListener { + when(DataServer.SetPageTag){ + "1-1"->{ + onBackPressed() + } + "1-2"->{ + mJFragmentInstance!!.showTJListPage() + } + "1-3"->{ + mJFragmentInstance!!.showTJDetailsPage() + } + } + + } + } + + override fun isImmersion(): Boolean { + return true + } + + override fun isShowTitle(): Boolean { + return true + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/SettingActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/SettingActivity.kt new file mode 100644 index 0000000..6dd2511 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/SettingActivity.kt @@ -0,0 +1,2336 @@ +package com.qidian.zhongkesmart.activity + +import android.Manifest +import android.app.NotificationManager +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Color +import android.net.ConnectivityManager +import android.net.Uri +import android.net.wifi.ScanResult +import android.net.wifi.WifiManager +import android.os.Build +import android.os.CountDownTimer +import android.os.Environment +import android.os.Handler +import android.provider.Settings +import android.text.TextUtils +import android.util.Log +import android.view.View +import android.widget.RadioGroup +import android.widget.TextView +import com.blankj.utilcode.util.AppUtils +import com.blankj.utilcode.util.FileUtils +import com.blankj.utilcode.util.GsonUtils +import com.qidian.baseble.ViseBle +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.baseble.ble.ConnectEvent +import com.qidian.baseble.ble.NotifyDataEvent +import com.qidian.baseble.callback.scan.IScanCallback +import com.qidian.baseble.callback.scan.ScanCallback +import com.qidian.baseble.model.BluetoothLeDevice +import com.qidian.baseble.model.BluetoothLeDeviceStore +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.AppApplication +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.bluetooth.dfu.DfuService +import com.qidian.zhongkesmart.config.Config +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.dialog.* +import com.qidian.zhongkesmart.model.* +import com.qidian.zhongkesmart.net.ApiUrl +import com.qidian.zhongkesmart.net.HttpCallBack +import com.qidian.zhongkesmart.net.HttpRequest +import com.qidian.zhongkesmart.receiver.WifiReceiver +import com.qidian.zhongkesmart.utils.* +import com.qidian.zhongkesmart.utils.EventBusUtil.MessageEvent +import com.qidian.zjlw.presenter.Contract +import com.qidian.zjlw.presenter.SetPresenter +import com.scwang.smartrefresh.layout.listener.OnRefreshListener +import com.vise.xsnow.event.Subscribe +import kotlinx.android.synthetic.main.activity_bluetooth.* +import kotlinx.android.synthetic.main.activity_connect_mode.* +import kotlinx.android.synthetic.main.activity_model_init.* +import kotlinx.android.synthetic.main.activity_my_equipment.* +import kotlinx.android.synthetic.main.activity_service.* +import kotlinx.android.synthetic.main.activity_setting.* +import kotlinx.android.synthetic.main.activity_smart_analysis.* +import kotlinx.android.synthetic.main.activity_wi_fi.* +import kotlinx.android.synthetic.main.base_title_layout.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.fragment_capture.* +import kotlinx.android.synthetic.main.layout_changeurl.* +import kotlinx.android.synthetic.main.layout_changewg.* +import kotlinx.android.synthetic.main.layout_connect_mode.* +import kotlinx.android.synthetic.main.layout_connect_mode.iv_agent +import kotlinx.android.synthetic.main.layout_connect_mode.iv_direct +import kotlinx.android.synthetic.main.layout_list.* +import kotlinx.android.synthetic.main.layout_setmess.* +import kotlinx.android.synthetic.main.layout_standby.* +import kotlinx.android.synthetic.main.layout_sys_bluetooth1.* +import kotlinx.android.synthetic.main.layout_sys_bluetooth11.* +import kotlinx.android.synthetic.main.layout_sys_bluetooth2.* +import kotlinx.android.synthetic.main.layout_sys_netmess1.* +import kotlinx.android.synthetic.main.layout_sys_netmess2.* +import kotlinx.android.synthetic.main.layout_tiejianmess.* +import kotlinx.android.synthetic.main.layout_tiepain_details.* +import kotlinx.android.synthetic.main.layout_tiepain_details2.* +import kotlinx.android.synthetic.main.layout_wangguanmess.* +import kotlinx.android.synthetic.main.set_title_layout.* +import net.idik.lib.slimadapter.SlimAdapter +import net.idik.lib.slimadapter.SlimInjector +import net.idik.lib.slimadapter.viewinjector.IViewInjector +import no.nordicsemi.android.dfu.DfuProgressListener +import no.nordicsemi.android.dfu.DfuProgressListenerAdapter +import no.nordicsemi.android.dfu.DfuServiceInitiator +import no.nordicsemi.android.dfu.DfuServiceListenerHelper +import pub.devrel.easypermissions.EasyPermissions +import java.io.File +import java.util.* +import kotlin.collections.ArrayList + + +/*设置*/ +class SettingActivity : BaseActivity(), EasyPermissions.PermissionCallbacks, Contract.SetView { + var mData = ArrayList() + var mData_Change = ArrayList() + var mData_BlueTooth = ArrayList() + var mData_BlueAddress = ArrayList() + var mData_Wifi = ArrayList() + var mAdapterChange: SlimAdapter? = null + var mAdapterBluetooth: SlimAdapter? = null + private val mPresenter by lazy { SetPresenter() } + + var wifiReceiver: WifiReceiver? = null + var mDevice: BluetoothLeDevice? = null + var mTJSetMac = "" + var mTJSetRoomName = "" + var mTJSetType = "" + + var mResumed = false + + //修改贴件信息后 赶回上个页面标记 + var afterChangeToPageTag = 1 + + var appVersion:VersionInfo? = null + var firmwareVersionD:VersionInfo? = null + var firmwareVersionL:VersionInfo? = null + //更新中 + var isUpdating = false + var isSamping = false + var countDownTimer:CountDownTimer? = null + + var mCode = "" + var mToken = "" + var isNeedUnregister = false + override fun getLayoutId(): Int { + return R.layout.activity_setting + } + + override fun initView() { + askPermissionOfBluetooth() + mPresenter.attachView(this) +// setTitle("设置") + if (DataServer.mHttpToken.isNotEmpty()) { + li_set_reset.visibility = View.VISIBLE + } else { + li_set_reset.visibility = View.GONE + } + radioGroup_set.setOnCheckedChangeListener(object : RadioGroup.OnCheckedChangeListener { + override fun onCheckedChanged(group: RadioGroup?, checkedId: Int) { + + when (checkedId) { + //贴件设置 + R.id.raiobutton_tiejan -> { + //贴件设置 -1 显示贴件设置 + showTJSet() + } + R.id.raiobutton_moshi -> { + //贴件设置 -1 显示贴件设置 + showConnectMode() + } + //网关 + R.id.raiobutton_wangguan -> { + setGone() + li_wangguan_mess.visibility = View.VISIBLE + aboutWifi() +// showDeviceSet() + } + //系统 + R.id.raiobutton_set -> { + showSystemSet() + } + } + } + + }) + //重写返回事件 +// rl_back.setOnClickListener { +// reSetBack() +// } + + LoadUtils.setLinearLayoutManager(this, recycle_wifi1, false) + mAdapterBluetooth = + SlimAdapter.create() + .register( + R.layout.item_bluetooth_activity, + object : SlimInjector { + override fun onInject( + data: BluetoothLeDevice, + injector: IViewInjector> + ) { + injector.text(R.id.tv_wifi_name, BlueToothUtils.instance!!.getName(data)) + injector.clicked(R.id.li_wifi) { + mDevice = data + SPUtil.setValue(DataServer.MRelayMac,mDevice!!.address) + SPUtil.setValue(DataServer.MRelayName,mDevice!!.name) + ViseBle.getInstance().stopScan(periodScanCallback) + if (!BluetoothDeviceManager.getInstance() + .isConnected(data) + ) { + DataServer.nBlueToothMac = "" + DataServer.MDevice = data + BluetoothDeviceManager.getInstance().connect(data) + } + li_now_choose.visibility = View.VISIBLE + tv_now_choose_name.text = BlueToothUtils.instance!!.getName(data) + tv_now_choose_status.text = "连接中" + ll_connectable.visibility = View.GONE + } + + } + }) + .attachTo(recycle_wifi1) + mAdapterBluetooth!!.updateData(mData_BlueTooth).notifyDataSetChanged() + } + + override fun initData() { + //关于蓝牙 + aboutBlueToothList() + /*贴片设置*/ + //贴件设置 -2 初始化 + initTJListView() + //贴件设置 -1 显示贴件设置 + showTJSet() + + getVersionInfo() + +// removebindDevice(1) + } + + /** + * 贴件设置 -1 显示贴件设置 + * 贴件设置 -2 初始化 + * 贴件设置 -3 设置数据 + * 贴件设置 -4 贴件详情 + * 贴件设置 -5 修改位置 + * 贴件设置 -6 修改用途 + */ + fun showTJSet() { + setGone() + li_tiejianmess.visibility = View.VISIBLE + //贴件数据 贴件设置 -3 + getTieJianlistData() + } + + //显示贴片详情 贴件设置 -4 贴件详情 + fun showTJDetails() { + setGone() + li_tiejian_details.visibility = View.VISIBLE + showTiePianDetails(mTJSetMac) +// mPresenter.removebindDevice(this@SettingActivity, mTJSetMac) + } + + // 贴件设置 -5 修改位置 贴件设置 -6 修改用途 + fun showChangeTJMess(type: String) { + li_tiejian_details.visibility = View.GONE + li_tiejian_changedetails.visibility = View.VISIBLE + ced_changedetails.setText("") + if (type == "1") { + tv_change_title.text = "修改位置" + ced_changedetails.hint = "请选择或输入设备位置" + ced_changedetails.isEnabled = true + } else { + tv_change_title.text = "修改用途" + ced_changedetails.hint = "请选择设备用途" + ced_changedetails.isEnabled = false + } + aboutChangeTJMess(type) + } + + fun showConnectMode(){ + setGone() + li_connect_mode.visibility = View.VISIBLE + if(DataServer.connectMode==1) { + iv_direct.setImageResource(R.mipmap.direct_connect_mode_select_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_unselect_) + ll_bluetooth_list.visibility = View.GONE + }else{ + iv_direct.setImageResource(R.mipmap.direct_connect_mode_unselect_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_select_) + ll_bluetooth_list.visibility = View.VISIBLE + + val relayMac: String = SPUtil.getValue(DataServer.MRelayMac,String::class.java) + if (relayMac != null && !TextUtils.isEmpty(relayMac)) { + if (ViseBle.getInstance().deviceMirrorPool.isConnectDevice) { + li_now_choose.visibility = View.VISIBLE + ll_connectable.visibility = View.GONE + tv_now_choose_name!!.text = SPUtil.getValue(DataServer.MRelayName,String::class.java) + tv_now_choose_status!!.text = "已连接" + }else{ + li_now_choose.visibility = View.VISIBLE + ll_connectable.visibility = View.GONE + tv_now_choose_name!!.text = SPUtil.getValue(DataServer.MRelayName,String::class.java) + tv_now_choose_status!!.text = "连接中" + } + }else{ + li_now_choose.visibility = View.GONE + ll_connectable.visibility = View.VISIBLE + //扫描蓝牙列表 + startSearchDevices() + } + } + } + + /** + * 显示网关设置 + */ + fun showDeviceSet() { + setGone() + li_wangguan_mess.visibility = View.VISIBLE + WGMess() + } + + /** + * 显示系统设置 + */ + fun showSystemSet() { + setGone() +// aboutBlueToothMo() + li_setmess.visibility = View.VISIBLE + if (DataServer.mHttpToken.isNotEmpty()) { + li_set_reset.visibility = View.VISIBLE + } else { + li_set_reset.visibility = View.GONE + } + WGMess() + //有更新 + if(appVersion!=null) { + if (Utils.compareVersion( + appVersion?.version?.substring(appVersion!!.version.indexOf("v") + 1 until appVersion!!.version.indexOf(".apk")), + AppUtils.getAppVersionName() + ) + ) { + tv_app_upgrade.text = "可更新>" + tv_app_upgrade.setTextColor(getColor(R.color.color_999999)) + } else { + tv_app_upgrade.text = "已是最新>" + tv_app_upgrade.setTextColor(getColor(R.color.color_999999)) + } + }else{ + tv_app_upgrade.text = "已是最新>" + tv_app_upgrade.setTextColor(getColor(R.color.color_999999)) + } + + tv_agent_upgrade.text = "可下载>" + tv_agent_upgrade.setTextColor(getColor(R.color.color_999999)) + tv_low_power_upgrade.text = "可下载>" + tv_low_power_upgrade.setTextColor(getColor(R.color.color_999999)) +// refreshWiFi() +// QueryBlueToothStatus() + } + + /** + * 刷新wifi状态 + */ + fun refreshWiFi() { +// if (DataServer.mWiFiName.isNotEmpty()) { +//// if (WifiUtil.isWifiEnabled(this@SettingActivity)) { +// tv_set_wifiname.text = DataServer.mWiFiName +//// tv_set_wifiname.text = WifiUtil.getWifiName(this@SettingActivity) +// } else { +// tv_set_wifiname.text = "WiFi未连接" +// } + } + + /** + * 网关信息 + */ + fun WGMess() { + if (DataServer.WGName.isEmpty()) { + tv_tpd_location.text = "名称:${ToolUtil.getDeviceName()}" + DataServer.WGName = ToolUtil.getDeviceName() + } else { + tv_tpd_location.text = "名称:${DataServer.WGName}" + } + tv_wgmess_id.text = "网关ID:${ToolUtil.getDeviceMac(this)}" + tv_wgmess_version.text = "软件版本:V${ToolUtil.getDeviceRelease()}" + tv_wgmess_typename.text = "型号名称:V${ToolUtil.getDeviceModel()}" + } + + //重写返回事件 + fun reSetBack() { + //贴件设置-详情-修改用途 + if (li_tiejian_changedetails.visibility == View.VISIBLE) { + if (tv_change_title.text == "修改位置" && ced_changedetails.text.toString() + .isNotEmpty() + ) {//位置 + mTJSetRoomName = ced_changedetails.text.toString() + afterChangeToPageTag = 1//返回贴件详情 + if (!ToolUtil.isContainsTJData(mTJSetRoomName!!, mTJSetType!!, mTJSetMac, this)) { + mPresenter.getChangeDevice(this, mTJSetMac, mTJSetRoomName, mTJSetType) + } else { + ToastUtil.showToast("请勿重复添加") + } +// tv_tpde_location.text = "位置:${ced_changedetails.text.toString()}" + } + showTJDetails() + return + } + //贴件详情 + if (li_tiejian_details.visibility == View.VISIBLE) { + //修改接口 + if (tv_tpde_location.text == "位置:") { + ToastUtil.showToast("请选择位置") + return + } + if (tv_tpd_use.text == "用途:") { + ToastUtil.showToast("请选择用途") + return + } + if(isUpdating){ + ToastUtil.showToast("固件更新中,请稍等!") + return + } + afterChangeToPageTag = 2//返回贴件列表 + if (!ToolUtil.isContainsTJData(mTJSetRoomName!!, mTJSetType!!, mTJSetMac, this)) { + mPresenter.getChangeDevice(this, mTJSetMac, mTJSetRoomName, mTJSetType) + } else { + ToastUtil.showToast("请勿重复添加") + } + + /* li_tiejian_details.visibility = View.GONE + li_tiejianmess.visibility = View.VISIBLE*/ + return + } + //网关详情-修改名称 + if (li_changewg.visibility == View.VISIBLE) { + li_changewg.visibility = View.GONE + li_setmess.visibility = View.VISIBLE + return + } + if (li_changeurl.visibility == View.VISIBLE) { + li_changeurl.visibility = View.GONE + li_wangguan_mess.visibility = View.VISIBLE + return + } + //设置 - 网络和互联网 -互联网详情 + if (li_sys_netmess2.visibility == View.VISIBLE) { + li_sys_netmess2.visibility = View.GONE + li_wangguan_mess.visibility = View.VISIBLE + return + } +// if (li_sys_netmess1.visibility == View.VISIBLE) { +// li_sys_netmess1.visibility = View.GONE +// li_setmess.visibility = View.VISIBLE +// +// return +// } + + + + if(li_setmess.visibility == View.VISIBLE && refreshDownloadStatus() !=0){ + ToastUtil.showToast("下载中,请稍等!") + return + } + + //设置-蓝牙 + if (layout_sys_bluetooth1.visibility == View.VISIBLE || layout_sys_bluetooth2.visibility == View.VISIBLE || layout_sys_bluetooth11.visibility == View.VISIBLE) { + showSysWithData() + return + } + //在一级页面 + if (li_tiejianmess.visibility == View.VISIBLE||li_connect_mode.visibility==View.VISIBLE || li_wangguan_mess.visibility == View.VISIBLE + || li_setmess.visibility == View.VISIBLE + ) { + finish() + return + } + } + + /** + * 显示系统设置以及页面 并刷新页面内容 + */ + fun showSysWithData() { + aboutBlueToothMo() + li_setmess.visibility = View.VISIBLE + refreshWiFi() + QueryBlueToothStatus() + } + + + override fun isShowTitle(): Boolean { + return false + } + + /** + * 贴件列表 + * */ + fun initTJListView() { + refreshLayout_list.setEnableLoadMore(false) + refreshLayout_list.setOnRefreshListener(mOnRefreshListener) + LoadUtils.setLinearLayoutManager(this, recycle_list, false) + mAdapter = + SlimAdapter.create() + .register( + R.layout.item_tiejian, + object : SlimInjector { + override fun onInject( + data: BindBlueToothData, + injector: IViewInjector> + ) { + injector.text(R.id.tv_set_tjname, data.type) + injector.text(R.id.tv_set_tjlocation, data.name) + //固定信息 + injector.text(R.id.tv_set_tjtype, "代理") + var deviceInfo = ObjectBoxUtils.getDeviceByMac(data.mac)?.get(0) + injector.with(R.id.tv_mark) { + //是否在线 + if (deviceInfo!!.isOnline) { + injector.text(R.id.tv_set_tjstatus, "正常") + it.setBackgroundResource(R.drawable.ed_yuan0fba61) + } else { + injector.text(R.id.tv_set_tjstatus, "离线") + it.setBackgroundResource(R.drawable.ed_yuangray) + } + } + if(deviceInfo!!.power==""){ + injector.text(R.id.tv_set_power, "电量:0%") + }else { + injector.text( + R.id.tv_set_power, + "电量:${Integer.valueOf(deviceInfo!!.power, 10)}%" + ) + } + injector.clicked(R.id.li_tp) { + mTJSetMac = data.mac + mTJSetRoomName = data.name + mTJSetType = data.type + //贴件设置 -4 贴件详情 + showTJDetails() + } + + } + }) + .attachTo(recycle_list) + + } + + fun setGone() { + //贴件列表、贴件详情、修改信息 + li_tiejianmess.visibility = View.GONE + li_tiejian_changedetails.visibility = View.GONE + li_tiejian_details.visibility = View.GONE + li_connect_mode.visibility = View.GONE + //网关详情、修改信息 + li_wangguan_mess.visibility = View.GONE + li_changewg.visibility = View.GONE + li_changeurl.visibility = View.GONE +//设置-网络 + li_setmess.visibility = View.GONE + li_wangguan_mess.visibility = View.GONE + li_sys_netmess2.visibility = View.GONE + //设置-蓝牙 + layout_sys_bluetooth1.visibility = View.GONE + layout_sys_bluetooth11.visibility = View.GONE + layout_sys_bluetooth2.visibility = View.GONE + } + + fun getTieJianlistData() { + if (ToolUtil.getHistory(DataServer.mBindHistory, this).isNullOrEmpty()) { + recycle_list.visibility = View.GONE + layEmpty.visibility = View.VISIBLE + tv_button.visibility = View.GONE + tv_emptytext.text = "您当前还有添加任何设备" + } else { + mData.clear() + mData.addAll(ToolUtil.getHistory(DataServer.mBindHistory, this)) + mAdapter!!.updateData(mData).notifyDataSetChanged() + recycle_list.visibility = View.VISIBLE + layEmpty.visibility = View.GONE + } + } + + /** + * 贴片详情 + */ + fun showTiePianDetails(mac: String) { + var deviceInfo = ObjectBoxUtils.getDeviceByMac(mac)?.get(0) + tv_device_title.text = "${deviceInfo!!.name} ${deviceInfo!!.type}" + tv_tpde_location.text = "位置:${deviceInfo!!.name}" + tv_tpd_use.text = "用途:${deviceInfo!!.type}" + //固定信息 + tv_sys_jiedian.text = "节点:Low power" + tv_sys_mac.text = "MAC:${deviceInfo!!.mac}" + tv_sys_pl.text ="频率:${deviceInfo!!.frequency}min" + tv_sys_version.text ="固件版本:${deviceInfo!!.fVersion}" + tv_sys_model1.text = if(deviceInfo!!.model1Init) "“打开”模型:已学习,上次学习时间为${deviceInfo!!.model1Time}" else "“打开”模型:未学习" + tv_sys_model1_threshold.text = "“打开”模型阈值:${deviceInfo!!.model1Threshold}" + tv_sys_model2.text = if(deviceInfo!!.model2Init) "“关闭”模型:已学习,上次学习时间为${deviceInfo!!.model2Time}" else "“关闭”模型:未学习" + tv_sys_model2_threshold.text = "“关闭”模型阈值:${deviceInfo!!.model2Threshold}" + tv_sys_addtime.text = "添加时间:${deviceInfo!!.time}" + } + + /** + * 贴件修改 + */ + fun aboutChangeTJMess(type: String) { + LoadUtils.setLinearLayoutManager(this, recycle_change, false) + mAdapterChange = + SlimAdapter.create() + .register(R.layout.item_tj_changetext, object : SlimInjector { + override fun onInject( + data: String, + injector: IViewInjector> + ) { + injector.text(R.id.tv_tjtext, data) + injector.clicked(R.id.li_tjtext) { +// ced_changedetails.setText(data) + if (type == "1") {//位置 + mTJSetRoomName = data + tv_tpde_location.text = "位置:${data}" + } else {//用途 + mTJSetType = data + tv_tpd_use.text = "用途:${data}" + } + setGone() + li_tiejian_details.visibility = View.VISIBLE + } + + } + }) + .attachTo(recycle_change) + getTjChangeData() + } + + fun getTjChangeData() { + mData_Change.clear() + if (tv_change_title.text == "修改位置") { + getData(DataServer.TJLocation) + } else { + getData(DataServer.TJUse) + } + mAdapterChange!!.updateData(mData_Change).notifyDataSetChanged() + } + + fun getData(list: Array) { + list.forEach { + mData_Change.add(it) + } + } + + + /** + * 网关信息 + */ + + + /** + * Wifi + */ + fun aboutWifi() { + showDialog() + //注册监听Wifi连接广播 + registerBroadcast() + askPermissionOfWiFi() + //初始化dialog +// OpenmWifiDialog.instance!!.getShareDialog(this, +// object : OpenmWifiDialog.PopupYearWindowCallBack { +// override fun doWork() { +//// WifiUtil.openWifiSetting(this@SettingActivity) +// //4、采用弹窗方式 应用内操作wifi +// startActivityForResult(Intent(Settings.Panel.ACTION_WIFI), 2) +// } +// +// override fun doCancel() { +// } +// +// }) + } + + /** + * 获取wifi权限 + */ + private fun askPermissionOfWiFi() { + EasyPermissions.requestPermissions( + this, + "请允许获取您的WiFi权限", + DataServer.ACCESS_WiFi_CODE, + Manifest.permission.ACCESS_FINE_LOCATION + ) + } + + /** + * 获取蓝牙权限 + */ + private fun askPermissionOfBluetooth() { + EasyPermissions.requestPermissions( + this, + "请允许获取您的位置权限", + DataServer.ACCESS_BlueTooth_CODE, + Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION + ) + } + + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_WiFi_CODE) { + //申请成功 + } + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_WiFi_CODE) { + ToastUtil.showToast("WiFi权限获取失败,APP功能不可用!") + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", packageName, null) + startActivity(intent) + } + if (requestCode == DataServer.ACCESS_BlueTooth_CODE) { + ToastUtil.showToast("蓝牙权限获取失败,APP功能不可用!") + } + } + + /** + * 注册Wifi监听广播 + */ + fun registerBroadcast() { + wifiReceiver = WifiReceiver() + val filter = IntentFilter() + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION) + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) + filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) + this.registerReceiver(wifiReceiver, filter) + isNeedUnregister = true + } + + + override fun receiveEvent(event: EventBusUtil.MessageEvent) { + super.receiveEvent(event) + dimissDialog() + when (event.code) { + /** + * WIFI + */ + EventCode.WIFI_CONFIRM_OPEN -> { + Log.i("WiFi==", "wifi开关打开") + OpenmWifiDialog.instance!!.setDismiss() + cbk_sys_open.isChecked = true + } + EventCode.WIFI_CONFIRM_OFF -> { + Log.i("WiFi==", "wifi开关关闭") + OpenmWifiDialog.instance!!.setShow() + cbk_sys_open.isChecked = false + tv_sys_netconnectstatus.text = "未连接" +// tv_set_wifiname.text = "WiFi未连接" + li_set_netname.visibility = View.GONE + recycle_set_wifi.visibility = View.GONE + dismissDialog() + if (wifiReceiver != null) { + unregisterReceiver(wifiReceiver) + isNeedUnregister = false + } + } + EventCode.WIFI_CONFIRM_CONNECT -> { + Log.i("WiFi==", "wifi连接:" + event.data) + WifiPswDialog.instance!!.setDismiss() + + li_set_netname.visibility = View.VISIBLE + tv_set_netname.text = event.data.toString() +// tv_set_wifiname.text = event.data.toString() + tv_sys_netconnectstatus.text = "已连接" + recycle_set_wifi.visibility = View.GONE + dismissDialog() + } + //列表 + EventCode.WIFI_CONFIRM_DATA -> { + Log.i("WiFi==", "wifi连接:" + event.data) + dismissDialog() + var model = event.data as List + //使用 startActivityForResult(Intent(Settings.Panel.ACTION_WIFI), num) 弹窗会触发此处 导致布局紊乱 +// if (cbk_sys_open.isChecked && tv_sys_netconnectstatus.text == "未连接") { +// showData(model) +// } + showData(model) + if (wifiReceiver != null) { + unregisterReceiver(wifiReceiver) + isNeedUnregister = false + } + } + //连接失败 + EventCode.WIFI_CONFIRM_FAIL -> { + dismissDialog() + WifiPswDialog.instance!!.showFail() + tv_sys_netconnectstatus.text = "未连接" + li_set_netname.visibility = View.GONE + } + + EventCode.DOWNLOAD_START -> { + var downloadType = event.data.toString().toInt() + when (downloadType) { + DownloadUtils.APP -> { + tv_app_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_app_upgrade.text = "开始下载" + } + + DownloadUtils.FIRMWARE_D -> { + tv_agent_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_agent_upgrade.text = "开始下载" + } + + DownloadUtils.FIRMWARE_L -> { + tv_low_power_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_low_power_upgrade.text = "开始下载" + } + } + } + + EventCode.DOWNLOAD_PROGRESS -> { + var downloadProgress = event.data as DownloadProgress + var downloadType = downloadProgress.downloadType + when (downloadType) { + DownloadUtils.APP -> { + tv_app_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_app_upgrade.text = "正在下载,进度"+downloadProgress.progress+"%" + } + + DownloadUtils.FIRMWARE_D -> { + tv_agent_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_agent_upgrade.text = "正在下载,进度"+downloadProgress.progress+"%" + } + + DownloadUtils.FIRMWARE_L-> { + tv_low_power_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_low_power_upgrade.text = "正在下载,进度"+downloadProgress.progress+"%" + } + } + } + + EventCode.DOWNLOAD_FINISH -> { + var downloadType = event.data.toString().toInt() + when (downloadType) { + DownloadUtils.APP -> { +// tv_app_upgrade.setTextColor(getColor(R.color.color_999999)) + tv_app_upgrade.text = "下载完成" + ToastUtil.showToast("下载完成,请手动点击安装!") + } + + DownloadUtils.FIRMWARE_D -> { +// tv_agent_upgrade.setTextColor(getColor(R.color.color_999999)) + tv_agent_upgrade.text = "下载完成" + } + + DownloadUtils.FIRMWARE_L -> { +// tv_low_power_upgrade.setTextColor(getColor(R.color.color_999999)) + tv_low_power_upgrade.text = "下载完成" + } + } + } + + EventCode.DOWNLOAD_ERROR -> { + var downloadType = event.data.toString().toInt() + when (downloadType) { + DownloadUtils.APP -> { + tv_app_upgrade.setTextColor(Color.parseColor("#ffee4141")) + tv_app_upgrade.text = "下载失败,请重新尝试" + } + + DownloadUtils.FIRMWARE_D -> { + tv_agent_upgrade.setTextColor(Color.parseColor("#ffee4141")) + tv_agent_upgrade.text = "下载失败,请重新尝试" + } + + DownloadUtils.FIRMWARE_L -> { + tv_low_power_upgrade.setTextColor(Color.parseColor("#ffee4141")) + tv_low_power_upgrade.text = "下载失败,请重新尝试" + } + } + } + + /** + * 贴件唤醒成功 + */ + EventCode.DEVICE_WAKE_UP_SUCCEED -> { + if(WakeUpDeviceDialog.instance!!.isShowing()) { + WakeUpDeviceDialog.instance!!.setDismiss() + countDownTimer!!.cancel() + countDownTimer = null + startActivity( + Intent( + this@SettingActivity, + SettingModeActivity::class.java + ).putExtra("mac", mTJSetMac) + ) + } + } + + } + } + + var mAdapter_Wifi: SlimAdapter? = null + fun showData(model: List) { + recycle_set_wifi.visibility = View.VISIBLE +// li_set_netname.visibility = View.GONE + mData_Wifi.clear() + model.forEach { + if (it.SSID.isNotEmpty() && !mData_Wifi.contains(it)) { + mData_Wifi.add(it) + } + } + LoadUtils.setLinearLayoutManager(this, recycle_set_wifi, false) + mAdapter_Wifi = + SlimAdapter.create() + .register(R.layout.item_wifi, object : SlimInjector { + override fun onInject( + data: ScanResult, + injector: IViewInjector> + ) { + injector.text(R.id.tv_wifi_name, data.SSID) + injector.clicked(R.id.li_wifi) { + if(tv_set_netname.text==data.SSID){ + ToastUtil.showToast("已连接") + }else { + WifiPswDialog.instance!!.getShareDialog( + this@SettingActivity, + data.SSID + ) + } + } + } + }) + .attachTo(recycle_set_wifi) + mAdapter_Wifi!!.updateData(mData_Wifi).notifyDataSetChanged() + } + + override fun onResume() { + super.onResume() + if (li_tiejian_details.visibility == View.VISIBLE) { + showTiePianDetails(mTJSetMac) + } + mResumed = true + DfuServiceListenerHelper.registerProgressListener(this, mDfuProgressListener) + } + + override fun onPause() { + super.onPause() + mResumed = true + DfuServiceListenerHelper.unregisterProgressListener(this, mDfuProgressListener) + } + + + override fun onDestroy() { + super.onDestroy() + if (isNeedUnregister&&wifiReceiver != null) { + unregisterReceiver(wifiReceiver) + } + dimissDialog() + } + + + fun dimissDialog() { + if (loadingDialog != null) loadingDialog.dismiss() + } + + /** + * 刷新 + */ + private val mOnRefreshListener = OnRefreshListener { + refreshLayout_list.finishRefresh() + } + + fun onClick(v: View) { + when (v.id) { + /** + * 贴片详情 + */ + R.id.iv_back -> { + reSetBack() + } + //解除绑定 + R.id.ll_unbind_device -> { + CancelBindDialog.instance!!.getShareDialog(this, + object : CancelBindDialog.PopupYearWindowCallBack { + override fun doWork() { + mPresenter.removebindDevice(this@SettingActivity, mTJSetMac) + } + }) + } + //贴件设置 -5 修改位置 + R.id.li_tpd_location -> { + showChangeTJMess("1") + } + //贴件设置 -6 修改用途 + R.id.li_tpd_use -> { + showChangeTJMess("2") + } + R.id.ll_set_url -> { + li_wangguan_mess.visibility = View.GONE + li_changeurl.visibility = View.VISIBLE + ced_set_change_url.setText("https://ifhs.fedhealth.cn/") + ced_set_change_url.hint="请输入服务器地址,例如:https://ifhs.fedhealth.cn/" + ced_set_change_port.hint="请输入端口号,例如:15900" + } + R.id.tv_save_url -> { + if (ced_set_change_url.text!!.isEmpty()) { + ToastUtil.showToast("请输入服务器地址") + return + } + ApiUrl.setDomain(ToolUtil.getIpStr(ced_set_change_url.text!!.toString(),ced_set_change_port.text!!.toString())) + getHttpCode() + } + /** + * 网关信息 + */ + R.id.li_wgmess_name -> { + //修改名称 + li_wangguan_mess.visibility = View.GONE + li_changewg.visibility = View.VISIBLE + ced_setchangename.setText("") + } + //保存 + R.id.tv_save -> { + if (ced_setchangename.text.toString().isEmpty()) { + ToastUtil.showToast("请输入网关名称") + return + } + DataServer.WGName = ced_setchangename.text.toString() + tv_tpd_location.text = "名称:${DataServer.WGName}" + li_wangguan_mess.visibility = View.VISIBLE + li_changewg.visibility = View.GONE + ToastUtil.showToast("修改成功") + } + /** + * 设置 + */ + //网络和互联网 +// R.id.li_set_net -> { +// li_setmess.visibility = View.GONE +// li_sys_netmess1.visibility = View.VISIBLE +// aboutWifi() +// +// //有更新 +// if(Utils.compareVersion(appVersion?.version?.substring(appVersion!!.version.indexOf("v")+1),AppUtils.getAppVersionName())){ +// tv_app_upgrade.text="可更新>" +// }else{ +// tv_app_upgrade.text="已是最新>" +// } +// +// tv_agent_upgrade.text = "可下载>" +// tv_low_power_upgrade.text = "可下载>" +// } + //已连接的设备 + R.id.li_set_bluetooth -> { + aboutlueTooth() + } + /** + * 设置-网络和互联网 + */ + //无线局域网络-开关 目前代码控制不了系统的wifi开关 + R.id.li_sys_net -> { + /*cbk_sys_open.isChecked = !cbk_sys_open.isChecked + if (cbk_sys_open.isChecked) { + WifiUtil.setWlanStatus(this, true) + } else { + WifiUtil.setWlanStatus(this, false) + li_set_netname.visibility=View.GONE + tv_sys_netconnectstatus.text="未连接" + } + val wm =getApplicationContext().getSystemService(Context.WIFI_SERVICE) as WifiManager + Log.i("WiFi==", "当前开关状态"+wm.isWifiEnabled.toString())*/ +// WifiUtil.openWifiSetting(this@SettingActivity) + //4、采用弹窗方式 应用内操作wifi +// startActivityForResult(Intent(Settings.Panel.ACTION_WIFI), 2) + showDialog() + val wm = getSystemService(Context.WIFI_SERVICE) as WifiManager + if(wm!=null) { + wm.startScan() + }else{ + dismissDialog() + } + } + R.id.li_set_netname -> { + li_wangguan_mess.visibility = View.GONE + li_sys_netmess2.visibility = View.VISIBLE + //查詢wifi信息 + getWifIDetails() + + } + //清除 + /* R.id.tv_sys_netclear -> { + reSetBack() + }*/ + /** + * 蓝牙 + */ + //与新设备配对 + R.id.li_sys_bluetooth1_pd, R.id.li_sys_bluetooth2_pd -> { + aboutBlueToothMo() + SetBlueToConnect() + } + //可用的设备-刷新蓝牙 + R.id.tv_refresh -> { + startSearchDevices() + } + //蓝牙-返回 + R.id.rl_set_back -> { + /* aboutBlueToothMo() + li_setmess.visibility = View.VISIBLE + refreshWiFi() + QueryBlueToothStatus()*/ + showSysWithData() + } + //断开蓝牙连接 + R.id.li_sys_bluetooth2_disconnect -> { +// if (BluetoothDeviceManager.getInstance().isConnected(DataServer.MDevice)) { +// DisconnectBlueDialog.instance!!.getShareDialog(this, +// object : DisconnectBlueDialog.PopupYearWindowCallBack { +// override fun doWork() { +// BluetoothDeviceManager.getInstance().disconnect(DataServer.MDevice) +// li_sys_bluetooth2_pd.visibility = View.VISIBLE +// li_sys_bluetooth2_disconnect.visibility = View.GONE +// } +// }) +// } + } + //初始化设备 + R.id.li_set_reset -> { + PopupWindowCommonCenterUtils.getInstance().getShareDialog( + this, + "提示", + "确定要初始化应用?", + "确定", + "取消", + "", + object : PopupWindowCommonCenterUtils.PopupYearWindowCallBack { + override fun doWork() { + //网关重置 + mPresenter.resetApp(this@SettingActivity) + } + + }) + + } + + //采样模式 +// R.id.li_sampling_mode -> { +// if(li_firmware_upgrade.isEnabled ) { +// li_sampling_mode.isEnabled = false +// if(!isSamping) { +// tv_sampling_mode.visibility = View.VISIBLE +// DataServer.nBlueToothMac = Utils.formatMac(mTJSetMac, ":") +// countDownTimer = object : CountDownTimer(60 * 1000, 1000) { +// //1000ms运行一次 +// override fun onFinish() { +// li_sampling_mode.isEnabled = true +// tv_sampling_mode.visibility = View.GONE +// DataServer.nBlueToothMac = "" +// countDownTimer = null +// } +// +// override fun onTick(millisUntilFinished: Long) { +// var second = millisUntilFinished / 1000 % 60 +// tv_sampling_mode.text = "请开启贴件 " + second + "s" +// } +// }.start() +// }else{ +// if(tv_sampling_mode.text.startsWith("已")) { +// //发送休眠命令 +// var sendData = "sleep" +// Log.i("BlueTooth==sendData", sendData) +// BluetoothDeviceManager.getInstance() +// .write(mDevice, sendData.toByteArray(), "First write"); +// tv_sampling_mode.visibility = View.GONE +// li_sampling_mode.isEnabled = true +// isSamping = false +// } +// } +// }else{ +// ToastUtil.showToast("请等待固件更新完成") +// } +// } + + //配置模式 + R.id.li_setting_mode -> { + if(li_firmware_upgrade.isEnabled ) { + WakeUpDeviceDialog.instance!!.getShareDialog(this@SettingActivity, + object : WakeUpDeviceDialog.PopupYearWindowCallBack { + override fun doCancel() { + countDownTimer!!.cancel() + countDownTimer = null + DataServer.nBlueToothVerifyMac = "" + DataServer.nBlueToothMac = "" + } + }) + //广播模式下,需要连接贴件 + if(DataServer.connectMode==1){ + DataServer.nBlueToothMac = mTJSetMac + } + DataServer.nBlueToothVerifyMac = mTJSetMac + countDownTimer = object : CountDownTimer(60 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + ToastUtil.showToast("进入配置模式失败,请重新尝试") + WakeUpDeviceDialog.instance!!.setDismiss() + DataServer.nBlueToothVerifyMac = "" + DataServer.nBlueToothMac = "" + countDownTimer = null + } + + override fun onTick(millisUntilFinished: Long) { + } + }.start() + }else{ + ToastUtil.showToast("请等待固件更新完成") + } + } + + //固件更新 + R.id.li_firmware_upgrade -> { + if(FileUtils.isFileExists(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.path+File.separator+Config.DOWNLOAD_FIRMWARE_L)) { +// ToastUtil.showToast("固件更新包有误,更新失败") + if (!isUpdating) { + showDialog() + isUpdating = true + DataServer.isUpdateFirmware = true + li_firmware_upgrade.isEnabled = false + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + //停止扫描 + ViseBle.getInstance().stopScan(periodScanCallback) + //先断开连接 + if (BluetoothDeviceManager.getInstance().isConnected(DataServer.MDevice)) { + BluetoothDeviceManager.getInstance().disconnect(DataServer.MDevice) + } + Handler().postDelayed({ + dismissDialog() + countDownTimer = object : CountDownTimer(60 * 1000, 1000) { + //1000ms运行一次 + override fun onFinish() { + ToastUtil.showToast("固件升级失败,请重新尝试") + isUpdating = false + DataServer.isUpdateFirmware = false + li_firmware_upgrade.isEnabled = true + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff999999")) + tv_firmware_upgrade.text = "可更新>" + DataServer.nBlueToothMac = "" + countDownTimer = null + + startScanAfterUpdateFirmware() + } + + override fun onTick(millisUntilFinished: Long) { + var second = millisUntilFinished / 1000 % 60 + tv_firmware_upgrade.text = "请开启贴件 " + second + "s" + } + }.start() + ViseBle.config().scanTimeout = 50 * 1000 + //设置connectmode为1,scancallback扫描tj + DataServer.connectMode = 1 + DataServer.nBlueToothMac = mTJSetMac + ViseBle.getInstance().startScan(periodScanCallback) + }, 3000) + } + }else{ + ToastUtil.showToast("请先下载固件更新包") + } + } + + //APK下载 + R.id.li_app_upgrade -> { + var filePath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.path+File.separator+Config.DOWNLOAD_APK + if(FileUtils.isFileExists(filePath)){ + FileUtils.delete(filePath) + } + if(tv_app_upgrade.text == "可更新>") { + val downloadIdStatus = DownloadIdStatus(0, 0, 0) + SPUtil.setValue(Config.DOWNLOAD_ID_STATUS, GsonUtils.toJson(downloadIdStatus)) + var downloadType = refreshDownloadStatus() + if (downloadType != 0) { + if (downloadType == DownloadUtils.APP) { + PopupWindowCommonCenterUtils.getInstance().getShareDialog( + this, + "提示", + "确定要停止下载吗?", + "确定", + "取消", + "", + object : PopupWindowCommonCenterUtils.PopupYearWindowCallBack { + override fun doWork() { + //停止下载 + DownloadUtils.getInstance(this@SettingActivity) + .removeDownload() + } + }) + } else { + ToastUtil.showToast("下载中,请稍等!") + } + } else { +// tv_app_upgrade_label.setTextColor(Color.parseColor("#ff516afc")) + if (appVersion != null) { + tv_app_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_app_upgrade.text = "下载中" + DownloadUtils.getInstance(this).downloadAPK( + appVersion!!.url, + Config.DOWNLOAD_APK, + DownloadUtils.APP + ) + }else{ + ToastUtil.showToast("下载失败,请重新尝试") + } + } + } + } + + //代理固件下载 + R.id.li_agent_upgrade ->{ + var downloadType = refreshDownloadStatus() + if(downloadType != 0){ + if(downloadType == DownloadUtils.FIRMWARE_D){ + PopupWindowCommonCenterUtils.getInstance().getShareDialog( + this, + "提示", + "确定要停止下载吗?", + "确定", + "取消", + "", + object : PopupWindowCommonCenterUtils.PopupYearWindowCallBack { + override fun doWork() { + //停止下载 + DownloadUtils.getInstance(this@SettingActivity) + .removeDownload() + } + }) + }else { + ToastUtil.showToast("下载中,请稍等!") + } + }else{ +// tv_agent_upgrade_label.setTextColor(Color.parseColor("#ff516afc")) + tv_agent_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_agent_upgrade.text = "下载中" + if(firmwareVersionD!=null) { + DownloadUtils.getInstance(this).downloadAPK( + firmwareVersionD!!.url, + Config.DOWNLOAD_FIRMWARE_D, + DownloadUtils.FIRMWARE_D + ) + } + } + } + + //低功耗固件下载 + R.id.li_low_power_upgrade ->{ + var filePath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.path+File.separator+Config.DOWNLOAD_FIRMWARE_L + if(FileUtils.isFileExists(filePath)){ + FileUtils.delete(filePath) + } + var downloadType = refreshDownloadStatus() + if(downloadType != 0){ + if(downloadType == DownloadUtils.FIRMWARE_L){ + PopupWindowCommonCenterUtils.getInstance().getShareDialog( + this, + "提示", + "确定要停止下载吗?", + "确定", + "取消", + "", + object : PopupWindowCommonCenterUtils.PopupYearWindowCallBack { + override fun doWork() { + //停止下载 + DownloadUtils.getInstance(this@SettingActivity) + .removeDownload() + } + }) + }else { + ToastUtil.showToast("下载中,请稍等!") + } + }else{ +// tv_low_power_upgrade_label.setTextColor(Color.parseColor("#ff516afc")) + tv_low_power_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_low_power_upgrade.text = "下载中" + if(firmwareVersionL!=null) { + DownloadUtils.getInstance(this).downloadAPK( + firmwareVersionL!!.url, + Config.DOWNLOAD_FIRMWARE_L, + DownloadUtils.FIRMWARE_L + ) + } + } + } + + R.id.iv_direct -> { + if(DataServer.connectMode==2){ + ChangeConnectModeDialog.instance!!.getShareDialog(this, + object : ChangeConnectModeDialog.PopupYearWindowCallBack { + override fun doWork() { + removebindDevice(1) + } + },"直连") + } + } + R.id.iv_agent -> { + if(DataServer.connectMode==1){ + ChangeConnectModeDialog.instance!!.getShareDialog(this, + object : ChangeConnectModeDialog.PopupYearWindowCallBack { + override fun doWork() { + removebindDevice(2) + } + },"中继") + } + } + + R.id.li_now_choose -> { + DisconnectBlueDialog.instance!!.getShareDialog(this, + object : DisconnectBlueDialog.PopupYearWindowCallBack { + override fun doWork() { + showDialog() + li_now_choose.visibility = View.GONE + ll_connectable.visibility = View.VISIBLE + SPUtil.setValue(DataServer.MRelayMac,"") + SPUtil.setValue(DataServer.MRelayName,"") + if (BluetoothDeviceManager.getInstance().isConnected(DataServer.MDevice)) { + BluetoothDeviceManager.getInstance().disconnect(DataServer.MDevice) + Handler().postDelayed({ + startSearchDevices() + }, 3000) + }else{ + startSearchDevices() + } + } + }) + } + } + } + + private val mDfuProgressListener: DfuProgressListener = object : DfuProgressListenerAdapter() { + override fun onDeviceConnecting(deviceAddress: String) { + // progress.setIndeterminate(true); + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_firmware_upgrade.setText(R.string.dfu_status_connecting) + } + + override fun onDfuProcessStarting(deviceAddress: String) { + // progress.setIndeterminate(true); + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_firmware_upgrade.setText(R.string.dfu_status_starting) + } + + override fun onEnablingDfuMode(deviceAddress: String) { + // progress.setIndeterminate(true); + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_firmware_upgrade.setText(R.string.dfu_status_switching_to_dfu) + } + + override fun onFirmwareValidating(deviceAddress: String) { + // progress.setIndeterminate(true); + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_firmware_upgrade.setText(R.string.dfu_status_validating) + } + + override fun onDeviceDisconnecting(deviceAddress: String) { + // progress.setIndeterminate(true); + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_firmware_upgrade.setText(R.string.dfu_status_disconnecting) + } + + override fun onDfuCompleted(deviceAddress: String) { + isUpdating = false + DataServer.isUpdateFirmware = false + li_firmware_upgrade.isEnabled = true + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_firmware_upgrade.setText(R.string.dfu_success) + if (mResumed) { + // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again. + Handler().postDelayed({ + ToastUtil.showToast(getString(R.string.dfu_success)) +// handler.sendEmptyMessage(com.seaway.holdonaiglasses.ui.mine.FirmwareUpdateActivity.FINISH) + // if this activity is still open and upload process was completed, cancel the notification + val manager = + getSystemService(NOTIFICATION_SERVICE) as NotificationManager + manager.cancel(DfuService.NOTIFICATION_ID) + startScanAfterUpdateFirmware() + }, 200) + } + } + + override fun onDfuAborted(deviceAddress: String) { + isUpdating = false + DataServer.isUpdateFirmware = false + li_firmware_upgrade.isEnabled = true + tv_firmware_upgrade.setTextColor(Color.parseColor("#ffee4141")) + tv_firmware_upgrade.setText(R.string.dfu_status_aborted) + // let's wait a bit until we cancel the notification. When canceled immediately it will be recreated by service again. + Handler().postDelayed({ + + ToastUtil.showToast(getString(R.string.dfu_aborted)); + Log.e(SettingActivity::class.java.simpleName, getString(R.string.dfu_aborted)) +// handler.sendEmptyMessage(com.seaway.holdonaiglasses.ui.mine.FirmwareUpdateActivity.ERROR) // if this activity is still open and upload process was completed, cancel the notification + val manager = + getSystemService(NOTIFICATION_SERVICE) as NotificationManager + manager.cancel(DfuService.NOTIFICATION_ID) + startScanAfterUpdateFirmware() + }, 200) + } + + override fun onProgressChanged( + deviceAddress: String, + percent: Int, + speed: Float, + avgSpeed: Float, + currentPart: Int, + partsTotal: Int + ) { + // progress.setIndeterminate(false); + // progress.setProgress(percent); + tv_firmware_upgrade.setTextColor(Color.parseColor("#ff516afc")) + tv_firmware_upgrade.setText("正在更新,进度$percent%") + // if (partsTotal > 1) + // mTextUploading.setText(getString(R.string.dfu_status_uploading_part, currentPart, partsTotal)); + // else + // mTextUploading.setText(R.string.dfu_status_uploading); + } + + override fun onError(deviceAddress: String, error: Int, errorType: Int, message: String) { + isUpdating = false + DataServer.isUpdateFirmware = false + li_firmware_upgrade.isEnabled = true + if (mResumed) { + Log.e(SettingActivity::class.java.simpleName, "Upload failed: $message") + tv_firmware_upgrade.setTextColor(Color.parseColor("#ffee4141")) + tv_firmware_upgrade.setText(R.string.dfu_failed) +// handler.sendEmptyMessage(com.seaway.holdonaiglasses.ui.mine.FirmwareUpdateActivity.ERROR) + ToastUtil.showToast(getString(R.string.dfu_failed)); + // LogUtil.e(TAG, deviceAddress); + // LogUtil.e(TAG, error + ""); + // LogUtil.e(TAG, errorType + ""); + // LogUtil.e(TAG, message); + + // We have to wait a bit before canceling notification. This is called before DfuService creates the last notification. + Handler().postDelayed({ + + // if this activity is still open and upload process was completed, cancel the notification + val manager = + getSystemService(NOTIFICATION_SERVICE) as NotificationManager + manager.cancel(DfuService.NOTIFICATION_ID) + startScanAfterUpdateFirmware() + }, 200) + } + } + } + + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == 1) {//网关重置 + //clear() 询问操作wifi之后就跳转至wifi页面 + ActivityManager.getAppManager().finishAllActivity() + startActivity(Intent(this, WiFiActivity::class.java)) + } + if (requestCode == 3) {//网关详情 清除 + li_sys_netmess2.visibility = View.GONE + li_wangguan_mess.visibility = View.VISIBLE + } + } + + + /** + * 查詢已連接wifi信息 + */ + fun getWifIDetails() { + if (DataServer.mWiFiName.isNotEmpty()) { +// if (WifiUtil.isWifiEnabled(this)) { + var mWifiConfiguration = WifiUtil.getConnectScanResult(this) + var mWifiInfo = WifiUtil.getConnectWifiInfo(this) + tv_netmess2_name.text = DataServer.mWiFiName +// tv_netmess2_name.text = mWifiConfiguration.SSID + //信号强度 +// tv_netmess2_xhpower.text = mWifiConfiguration.level.toString() + tv_netmess2_mac.text = mWifiConfiguration.BSSID.toString() + tv_netmess2_ip.text = mWifiInfo.ipAddress.toString() + } + tv_sys_netclear.setOnClickListener { +// WifiUtil.disConnectWifi(this) + //4、采用弹窗方式 应用内操作wifi + startActivityForResult(Intent(Settings.Panel.ACTION_WIFI), 3) + } + + } + + /** + * 设置-蓝牙-页面跳转 + */ +//二级页面 -未连接 与新设备配对 + fun SetBlueDisConnect() { + aboutBlueToothMo() + layout_sys_bluetooth1.visibility = View.VISIBLE + } + + //二级页面 -已连接 已连接名称+与新设备配对 + fun SetBlueConnect() { + aboutBlueToothMo() + layout_sys_bluetooth2.visibility = View.VISIBLE + tv_sys_bluetooth2_name.text = DataServer.MDevice!!.name + tv_set_status.visibility = View.VISIBLE + cbk_set_bluetooth.isChecked = false + } + + //三级页面 - 未连接 与新设备配对(onClick) 设备名称+可用设备 + fun SetBlueToConnect() { + aboutBlueToothMo() + layout_sys_bluetooth11.visibility = View.VISIBLE + if (BlueToothUtils.instance!!.IsOpenBlueTooth()) { + startSearchDevices() + } + } + + /** + * 关于蓝牙 + */ + fun aboutBlueToothList() { + QueryBlueToothStatus() + //注册蓝牙广播 +// this.registerReceiver(mReceiver, makeFilter()) +// LoadUtils.setLinearLayoutManager(this, recycle_bluetooth, false) +// mAdapterBluetooth = +// SlimAdapter.create() +// .register( +// R.layout.item_bluetooth, +// object : SlimInjector { +// override fun onInject( +// data: BluetoothLeDevice, +// injector: IViewInjector> +// ) { +// injector.with(R.id.checkbox_bluetooth) { +//// it.text = data.address +// it.text = BlueToothUtils.instance!!.getName(data) +// } +// injector.clicked(R.id.li_bluetooth) { +// mDevice = data +// //蓝牙-3、连接 +// if (!BluetoothDeviceManager.getInstance().isConnected(data)) { +// BluetoothDeviceManager.getInstance().connect(data) +// } +// +// } +// +// } +// }) +// .attachTo(recycle_bluetooth) +// mAdapterBluetooth!!.updateData(mData_BlueTooth).notifyDataSetChanged() + } + + + //搜索设备 + fun startSearchDevices() { + mData_BlueAddress.clear() + mData_BlueTooth.clear() + mAdapterBluetooth!!.notifyDataSetChanged() + dimissDialog() + showDialog() + startScan() + } + + /** + * 1、蓝牙是否打开 + * 1-1、未打开-提示打开 + * + * 1-2、打开-跳转 + */ + fun aboutlueTooth() { + //1、未打开蓝牙-直接提示打开蓝牙 + if (!BlueToothUtils.instance!!.IsOpenBlueTooth()) { + OpenmBlueToothDialog.instance!!.getShareDialog(this, + object : OpenmBlueToothDialog.PopupYearWindowCallBack { + override fun doWork() { + BlueToothUtils.instance!!.OpenBuleTooth(this@SettingActivity) + } + }) + } else { + //打开过 + aboutBlueToothView() + } + } + + //设置默认状态-全部隐藏 + fun aboutBlueToothMo() { + li_setmess.visibility = View.GONE + layout_sys_bluetooth1.visibility = View.GONE + layout_sys_bluetooth11.visibility = View.GONE + layout_sys_bluetooth2.visibility = View.GONE + tv_set_status.visibility = View.GONE + } + + //根据蓝牙状态来判断显示隐藏 + fun aboutBlueToothView() { + aboutBlueToothMo() + //已连接 + if (ViseBle.getInstance().deviceMirrorPool.isConnectDevice) { + SetBlueConnect() + } else { + //只有在没有连接成功且之前没有连接成功过的情况下,才显示layout_sys_bluetooth1 + //之前连接成功过,但是现在不是连接状态,直接去扫描进行连接 +// if (DataServer.CDevice!=null) { +// SetBlueConnect() +// //蓝牙相关配置修改 +// ViseBle.config().scanTimeout = -1 //扫描超时时间,-1设置为永久扫描 +// ViseBle.getInstance().startScanByDevice(DataServer.CDevice) +// }else { +// SetBlueDisConnect() +// } + SetBlueDisConnect() + } + } + + /** + * 查询蓝牙状态 + */ + fun QueryBlueToothStatus() { + //未打开 + if (!BlueToothUtils.instance!!.IsOpenBlueTooth()) { + tv_set_bluetooth.text = "未连接设备" + tv_set_bluetoothname.visibility = View.GONE + } else { + //打开-判断连接 + if (ViseBle.getInstance().deviceMirrorPool.isConnectDevice) { + tv_set_bluetooth.text = "已连接设备" + tv_set_bluetoothname.visibility = View.VISIBLE + tv_set_bluetoothname.text = DataServer.MDevice!!.name + } else { + tv_set_bluetooth.text = "未连接设备" + tv_set_bluetoothname.visibility = View.GONE + } + } + } + + +//注册蓝牙监听广播 + /** + * 蓝牙广播过滤器 + * 蓝牙状态改变 + * 找到设备 + * 搜索完成 + * 开始扫描 + * 状态改变 + */ + fun makeFilter(): IntentFilter? { + val filter = IntentFilter() + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED) //蓝牙状态改变的广播 + filter.addAction(BluetoothDevice.ACTION_FOUND) //找到设备的广播 + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED) //搜索完成的广播 + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED) //开始扫描的广播 + filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED) //状态改变 + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)//已连接 + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)//未连接 + return filter + } + + //连接成功 li_bluetooth_connect.visibility = View.VISIBLE + private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + when (action) { + //已连接 + BluetoothDevice.ACTION_ACL_CONNECTED -> { + Log.i("BlueTooth==", "ACTION_ACL_CONNECTED:已连接") + + } + //未连接 + BluetoothDevice.ACTION_ACL_DISCONNECTED -> { + Log.i("BlueTooth==", "ACTION_ACL_DISCONNECTED:未连接") + + } + //连接成功 + BluetoothDevice.ACTION_ACL_CONNECTED -> { + Log.i("BlueTooth==", "ACTION_ACL_CONNECTED:连接成功") + + } + //蓝牙状态改变的广播 + BluetoothAdapter.ACTION_STATE_CHANGED -> { + Log.i("BlueTooth==", "ACTION_STATE_CHANGED:蓝牙状态改变") + var blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0) + when (blueState) { + //蓝牙-2、扫描 + BluetoothAdapter.STATE_ON -> { + Log.i("BlueTooth==", "蓝牙打开") +// startScan() +// layout_sys_bluetooth1.visibility = View.VISIBLE +// BlueToothUtils.instance!!.startScan() + } + //蓝牙关闭 + BluetoothAdapter.STATE_ON -> { + Log.i("BlueTooth==", "蓝牙已经关闭") + //蓝牙相关配置修改 +// ViseBle.config().scanTimeout = 12*1000 //扫描超时时间,-1设置为永久扫描 +// ViseBle.getInstance().startScan(periodScanCallback) + } + } + } + // 当发现设备 + BluetoothDevice.ACTION_FOUND -> { + //把蓝牙设备对象的意图 + /*val device = intent + .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + Log.i("BlueTooth==", "ACTION_FOUND:发现设备" + device) + if (!TextUtils.isEmpty(device!!.address) || !TextUtils.isEmpty(device.name)) { + if (!mData_BlueTooth.contains(device)) { + mData_BlueTooth.add(device) + mAdapterBluetooth!!.notifyDataSetChanged() + } + Log.i("BlueTooth==", "mData_BlueTooth:" + mData_BlueTooth) + }*/ + } + // 搜索完成的广播 + BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> { + dismissDialog() + Log.i("BlueTooth==", "ACTION_DISCOVERY_FINISHED:搜索完成") + } + } + } + } + + /** + * 开始扫描 + */ + private fun startScan() { + //蓝牙相关配置修改 + ViseBle.getInstance().stopScan(periodScanCallback) +// ViseBle.config().scanTimeout = -1 //扫描超时时间,-1设置为永久扫描 + ViseBle.config().scanTimeout = 12*1000 //扫描超时时间,-1设置为永久扫描 + ViseBle.getInstance().startScan(periodScanCallback) +// invalidateOptionsMenu() //重绘菜单 + } + + /** + * 扫描回调 + */ + private val periodScanCallback: ScanCallback = ScanCallback(object : IScanCallback { + override fun onDeviceFound(bluetoothLeDevice: BluetoothLeDevice) { + dismissDialog() + if (!TextUtils.isEmpty(bluetoothLeDevice.address)) { + //选择模式时 + if(li_connect_mode.visibility == View.VISIBLE&&DataServer.connectMode==2) { + if (!mData_BlueAddress.contains(bluetoothLeDevice.address)) { + mData_BlueTooth.add(bluetoothLeDevice) + mData_BlueAddress.add(bluetoothLeDevice.address) + mAdapterBluetooth!!.notifyDataSetChanged() + Log.i( + "BlueTooth==", + "periodScanCallback:发现设备" + BlueToothUtils.instance!!.getName( + bluetoothLeDevice + ) + ) + } + } + + //固件更新时 + if(!li_firmware_upgrade.isEnabled) { + if (bluetoothLeDevice.address.equals(DataServer.nBlueToothMac)) { + Log.i( + "BlueTooth==", + "periodScanCallback:发现指定设备" + bluetoothLeDevice.address + ) + if (!BluetoothDeviceManager.getInstance() + .isConnected(bluetoothLeDevice) + ) { + DataServer.nBlueToothMac = "" + DataServer.MDevice = bluetoothLeDevice + BluetoothDeviceManager.getInstance().connect(bluetoothLeDevice) + } + } + } + } + } + + override fun onScanFinish(bluetoothLeDeviceStore: BluetoothLeDeviceStore?) { + Log.i("BlueTooth==", "periodScanCallback:扫描结束") + dimissDialog() + } + + override fun onScanTimeout() { + Log.i("BlueTooth==", "periodScanCallback:扫描超时") + dimissDialog() + } + + }) + + /** + * 蓝牙-3、设备连接回调 + */ + @Subscribe + fun showConnectedDevice(event: ConnectEvent?) { +// Log.i("BlueTooth==", "showConnectedDevice:连接回调") + if (event != null) { + if (event.isSuccess) { + if(li_connect_mode.visibility == View.VISIBLE&&DataServer.connectMode==2) { + tv_now_choose_status.text = "已连接" + ToastUtil.showToast("连接成功") + } + + try { + //固件更新 + if (isUpdating) { + Log.i("BlueTooth==", "开始固件更新") + if (countDownTimer != null) { + countDownTimer!!.cancel() + countDownTimer = null + } + if (DataServer.MDevice != null) { + Handler().postDelayed({ + var file = File( + this@SettingActivity.getExternalFilesDir( + Environment.DIRECTORY_DOWNLOADS + ), + Config.DOWNLOAD_FIRMWARE_L + ) + mDevice = DataServer.MDevice + var uri = Uri.fromFile(file) + val starter = DfuServiceInitiator(mDevice!!.address) + .setDeviceName(mDevice!!.name) + .setKeepBond(false) + .setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled( + true + ) + .setZip(uri, uri.path) + starter.start( + this@SettingActivity, + DfuService::class.java + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + DfuServiceInitiator.createDfuNotificationChannel( + this@SettingActivity + ) + } + }, 500) + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } else { + if(li_connect_mode.visibility == View.VISIBLE&&DataServer.connectMode==2) { + tv_now_choose_status.text = "连接中" + } + if(li_firmware_upgrade.isEnabled) { + ToastUtil.showToast("蓝牙已断开!") + } +// if(!li_firmware_upgrade.isEnabled){ +// if(countDownTimer!=null){ +// countDownTimer!!.cancel() +// countDownTimer = null +// } +// ToastUtil.showToast("固件升级失败,请重新尝试") +// isUpdating = false +// li_firmware_upgrade.isEnabled = true +// tv_firmware_upgrade.setTextColor(Color.parseColor("#ff999999")) +// tv_firmware_upgrade.text = "可更新>" +// DataServer.nBlueToothMac = "" +// }else { +// ToastUtil.showToast("蓝牙已断开!") +// } + + //非主动断开 +// DataServer.MDevice = null + DataServer.UNBUNDLE = 0 + Log.i("BlueTooth==", "showConnectedDevice:连接失败") +// Handler().postDelayed({ +// SetBlueDisConnect() +// if (DataServer.CDevice!=null) { +// //蓝牙相关配置修改 +// ViseBle.config().scanTimeout = -1 //扫描超时时间,-1设置为永久扫描 +// ViseBle.getInstance().startScanByDevice(DataServer.CDevice) +// } +// }, 5000) + } + } + } + + + /** + *输出数据展示 + * (0X)02-4F-1E-59-03-BF-37-DC-68-34-75-C5-84 + * + * 输出数据:1fff1fff1fff33cf6a12483f37 + */ + private var mOutputInfo = StringBuilder("") + + @Subscribe + fun showDeviceNotifyData(event: NotifyDataEvent?) { + try { + if (mDevice != null) { + if (event?.data != null && event.bluetoothLeDevice != null && event.bluetoothLeDevice + .address.equals(mDevice!!.address) + ) { +// mOutputInfo = StringBuilder("") +// mOutputInfo.append(HexUtil.encodeHexStr(event.data)) +// Log.i("BlueTooth==", "showDeviceNotifyData:输出数据:$mOutputInfo") +// BlueToothUtils.instance!!.getBlueToothData(mOutputInfo.toString(), this) + //接收到数据后,通知贴件休眠 + +// var dataString = HexUtil.encodeHexStr(event.data) +// if(dataString.length>25){ +// var sendData = dataString.substring(18) + "sleep" +// Log.i("BlueTooth==sendData", sendData) +// BluetoothDeviceManager.getInstance() +// .write(mDevice, sendData.toByteArray(), "First write"); +// } + + +// var dataString = String(event.data) +// if(!li_sampling_mode.isEnabled) { +// //采样完成 +// if(dataString == "samp complet"){ +// ToastUtil.showToast("采样完成") +// //发送休眠命令 +// var sendData = "sleep" +// Log.i("BlueTooth==sendData", sendData) +// BluetoothDeviceManager.getInstance() +// .write(mDevice, sendData.toByteArray(), "First write"); +// tv_sampling_mode.visibility = View.GONE +// li_sampling_mode.isEnabled = true +// isSamping = false +// //采样失败 +// }else if(dataString == "samp failed"){ +// ToastUtil.showToast("采样失败") +// isSamping = false +// li_sampling_mode.isEnabled = true +// tv_sampling_mode.visibility = View.GONE +// } +// } + } + } + } catch (e: java.lang.Exception) { + } + } + + //修改贴件信息 + override fun showChangeResult( + messModel: Any?, + mac: String, + locationName: String?, + type: String? + ) { + ToastUtil.showToast("修改成功") + //修改本地该条记录 + ToolUtil.ChangeHistory( + this@SettingActivity, + DataServer.mBindHistory, + mac, BindBlueToothData( + mac, + BlueToothUtils.instance!!.getBlueToothPower(mac), + locationName!!, + type!!, TimeUtil.getNowTime()!! + ) + ) + //修改该贴件接收蓝牙数据里面的type类型 (震动型、移动型) + ToolUtil.changeMacBlueToothHistory(mac, this) + var deviceInfo = ObjectBoxUtils.getDeviceByMac(mac)!![0] + deviceInfo.isBind = true + deviceInfo.name = locationName!! + deviceInfo.type = type!! + ObjectBoxUtils.updateData(deviceInfo, DeviceInfo::class.java) + } + + //解除绑定 + override fun showRemoveResult(messModel: Any?, mac: String) { + ToastUtil.showToast("解除绑定成功") +// ObjectBoxUtils.deleteDatabase() + ObjectBoxUtils.deleteALLDataD(ObjectBoxUtils.getDeviceByMac(mac)) + ObjectBoxUtils.deleteALLDataA(ObjectBoxUtils.getActionByMac(mac)) + //清除本地该条记录 + ToolUtil.deleteHistory( + this@SettingActivity, + DataServer.mBindHistory, + mac + ) + //将本地存储的蓝牙接收数据中的该贴件的信息删除 + ToolUtil.clearMacBlueToothHistory(mac) + showTJSet() + } + + //修改贴件信息后 返回上个页面 + override fun showAfter() { + //修改接口不成功不能影响返回按钮操作 1返回贴件详情 2 //返回贴件列表 + if (afterChangeToPageTag == 2) { + showTJSet() + } else { + showTJDetails() + } + + } + + //网关重置 + override fun showResetResule(messModel: Any?) { + ToastUtil.showToast("重置成功") + clear() + } + + override fun showResetFail(message: String) { + ToastUtil.showToast(message) + } + + fun clear() { + //清除本地信息 + ToolUtil.clearHistory(this, DataServer.mBindHistory) + //断开蓝牙 + if (BluetoothDeviceManager.getInstance().isConnected(DataServer.MDevice)) { + BluetoothDeviceManager.getInstance().disconnect(DataServer.MDevice) + } + //清库 + ObjectBoxUtils.deleteDatabase() + //清空相关数据 + DataServer.resetData() + //1、断开wifi连接disConnectWifi 只是断开连接 但是会记录上次连接 会主动连接 +// WifiUtil.disConnectWifi(this) + //2、测试安卓10是否支持?不支持的话就只能跳转设置页面 +// WifiUtil.setWlanStatus(this,false) + //3、跳转设置页面 +// WifiUtil.openWifiSetting(this) + //4、采用弹窗方式 应用内操作wifi + if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.R) { + startActivityForResult(Intent(Settings.Panel.ACTION_WIFI), 1) + }else{ + val intent = Intent(Settings.ACTION_WIFI_SETTINGS) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + startActivityForResult(intent,1) + } + } + + //查询下载状态 + fun refreshDownloadStatus():Int{ + var downloadIdStatus = GsonUtils.fromJson(SPUtil.getValue(Config.DOWNLOAD_ID_STATUS,String::class.java),DownloadIdStatus::class.java) + if(downloadIdStatus!=null) { + //判断是否有正在下载的任务 + if (downloadIdStatus.appDownloadId == 0L && downloadIdStatus.firmwareDDownloadId == 0L && downloadIdStatus.firmwareLDownloadId == 0L) { + return 0 + } else { + if (downloadIdStatus.appDownloadId != 0L) { + return DownloadUtils.APP + } else if (downloadIdStatus.firmwareDDownloadId != 0L) { + return DownloadUtils.FIRMWARE_D + } else { + return DownloadUtils.FIRMWARE_L + } + } + }else{ + return 0 + } + } + + private fun getVersionInfo(){ + HttpRequest.init(this).get(ApiUrl.getAPPVersionInfo) + .setClazz(VersionInfo::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + appVersion = `object` as VersionInfo + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("$code $info") + return super.failed(code, info) + } + }) + + HttpRequest.init(this).get(ApiUrl.geFirmwareDVersionInfo) + .setClazz(VersionInfo::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + firmwareVersionD = `object` as VersionInfo? + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("$code $info") + return super.failed(code, info) + } + }) + + HttpRequest.init(this).get(ApiUrl.geFirmwareLVersionInfo) + .setClazz(VersionInfo::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + firmwareVersionL = `object` as VersionInfo? + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("$code $info") + return super.failed(code, info) + } + }) + } + + fun restartApp(){ + //断开蓝牙 + if (DataServer.MDevice!=null&&BluetoothDeviceManager.getInstance().isConnected(DataServer.MDevice)) { + BluetoothDeviceManager.getInstance().disconnect(DataServer.MDevice) + } + ViseBle.getInstance().stopScan(periodScanCallback) + SPUtil.setValue(DataServer.MRelayMac, "") + DataServer.nBlueToothMac = "" + DataServer.nBlueToothVerifyMac = "" + //清理所有界面然后跳转到登录界面 + ActivityManager.getAppManager().finishAllActivity() + AppApplication.mContext!!.startActivity( + Intent( + AppApplication.mContext, + LaunchActivity::class.java + ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ) + } + + //固件更新后继续开始扫描 + fun startScanAfterUpdateFirmware(){ + EventBusUtil.sendEvent(MessageEvent(EventCode.START_SCAN, null)) + } + + /** + * 网关登录 + */ + //获取code + fun getHttpCode() { + Log.i("UUID==", DeviceUuidFactory.getAndroidID(this)) + var map = HashMap() + map["id"] = DeviceUuidFactory.getAndroidID(this) + HttpRequest.init(this).get(ApiUrl.getCode) + .setMap(map) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("响应数据", message!!) + mCode = `object`.toString() + getHttpLogin() + } + + }) + + } + + //网关登录 获取token存本地 这里如果绑定了就有token 不绑定就没有token 不影响后面测试网关是否连接成功 + fun getHttpLogin() { + var map = HashMap() + map["code"] = mCode + map["id"] = DeviceUuidFactory.getAndroidID(this) + HttpRequest.init(this).postJson(ApiUrl.getLogin, map,"",false) + .setClazz(GetTokenM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + var model = `object` as GetTokenM + mToken = model.token + DataServer.mHttpToken = mToken + } + + override fun after() { + super.after() + getLoginSuccess() + } + }) + } + + //测试网关是否连接成功 + fun getLoginSuccess() { + HttpRequest.init(this).get(ApiUrl.getLoginSuccess) + .setClazz(GetCodeM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + li_wangguan_mess.visibility = View.VISIBLE + li_changeurl.visibility = View.GONE + ToastUtil.showToast("修改成功") + } + + }) + } + + /** + * 解绑所有贴件 + */ + fun removebindDevice(index:Int) { + var map = HashMap() + var dList = ObjectBoxUtils.getAllData(DeviceInfo::class.java) + var dMac = "" + val sb = java.lang.StringBuilder() + for (i in dList!!.indices) { + if(dList[i].isBind) { + if (i == 0) { + dMac = dList[i].mac + } else if (i == dList.size - 1) { + dMac = "$dMac,${dList[i].mac}" + } + } + } + if(TextUtils.isEmpty(dMac)){ + when(index){ + 1 -> { + iv_direct.setImageResource(R.mipmap.direct_connect_mode_select_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_unselect_) + SPUtil.setValue(DataServer.MConnectMode, 1) + DataServer.connectMode = 1 + ObjectBoxUtils.deleteDatabase() + ToolUtil.clearHistory(this@SettingActivity, DataServer.mBindHistory) + restartApp() + } + + 2 -> { + iv_direct.setImageResource(R.mipmap.direct_connect_mode_select_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_unselect_) + SPUtil.setValue(DataServer.MConnectMode, 2) + DataServer.connectMode = 2 + ObjectBoxUtils.deleteDatabase() + ToolUtil.clearHistory(this@SettingActivity, DataServer.mBindHistory) + restartApp() + } + } + return + } + map["gatewayId"] = DeviceUuidFactory.getAndroidID(this) + map["deviceMacs"] = dMac + + HttpRequest.init(this).DeleteJson(ApiUrl.RemoveBindTiePian) + .setClazz(GetCodeM::class.java) + .setMap(map) +// .setModel(HttpRequest.CONTENT_TYPE_FORM_DATA) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + when(index){ + 1 -> { + iv_direct.setImageResource(R.mipmap.direct_connect_mode_select_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_unselect_) + SPUtil.setValue(DataServer.MConnectMode, 1) + DataServer.connectMode = 1 + ObjectBoxUtils.deleteDatabase() + ToolUtil.clearHistory(this@SettingActivity, DataServer.mBindHistory) + restartApp() + } + + 2 -> { + iv_direct.setImageResource(R.mipmap.direct_connect_mode_select_) + iv_agent.setImageResource(R.mipmap.agent_connect_mode_unselect_) + SPUtil.setValue(DataServer.MConnectMode, 2) + DataServer.connectMode = 2 + ObjectBoxUtils.deleteDatabase() + ToolUtil.clearHistory(this@SettingActivity, DataServer.mBindHistory) + restartApp() + } + } + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("切换模式失败,请重试") + return super.failed(code, info) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/SettingModeActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/SettingModeActivity.kt new file mode 100644 index 0000000..dab5736 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/SettingModeActivity.kt @@ -0,0 +1,109 @@ +package com.qidian.zhongkesmart.activity + +import android.content.Intent +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.View +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.baseble.ble.DeviceSettingEvent +import com.qidian.baseble.utils.HexUtil +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.dialog.CancelSettingModeDialog +import com.qidian.zhongkesmart.dialog.SelectFrequencyDialog +import com.qidian.zhongkesmart.utils.* +import com.vise.xsnow.event.Subscribe +import kotlinx.android.synthetic.main.activity_service.* +import java.text.SimpleDateFormat +import java.util.* + +class SettingModeActivity : BaseActivity() { + override var TAG = SettingModeActivity::class.java.simpleName + var mMac="" + + + override fun getLayoutId(): Int { + return R.layout.activity_setting_mode + } + + override fun initView() { + mMac=intent.getStringExtra("mac").toString() + } + + override fun initData() { + //获取模型学习时间 + BluetoothDeviceManager.getInstance().getTime(mMac,255) + Handler().postDelayed({ + BluetoothDeviceManager.getInstance().getTime(mMac,253) + },200) + } + + fun onClick(v: View) { + when (v.id) { + R.id.tv_frequency -> { + var deviceInfo = ObjectBoxUtils.getDeviceByMac(mMac)!![0].frequency + SelectFrequencyDialog.instance!!.getShareDialog(this, + object : SelectFrequencyDialog.PopupYearWindowCallBack { + override fun doWork(item: Int) { + showDialog() + //修改频率 + BluetoothDeviceManager.getInstance() + .setParam(mMac, true,1,item) + } + },deviceInfo) + } + + R.id.tv_model_init -> { + startActivity(Intent(this@SettingModeActivity, ModelInitActivity::class.java).putExtra("mac", mMac)) + finish() + } + + R.id.tv_exit -> { + CancelSettingModeDialog.instance!!.getShareDialog(this, + object : CancelSettingModeDialog.PopupYearWindowCallBack { + override fun doWork() { + //广播版退出配置模式,手动断开连接 + if(DataServer.connectMode==1) { + if (BluetoothDeviceManager.getInstance() + .isConnected(DataServer.MDevice) + ) { + BluetoothDeviceManager.getInstance() + .disconnect(DataServer.MDevice) + } + } + DataServer.nBlueToothVerifyMac = "" + finish() + } + }) + } + } + } + + @Subscribe + fun showDeviceSettingData(event: DeviceSettingEvent?) { + try { + if (event?.data != null && event.mac != null&&event.flag!=null&&event.flag==4) { + if(Utils.formatMac(event.mac, ":").equals(mMac)){ + when (HexUtil.byteToInt(event?.data[1])) { + 0 -> { + when (HexUtil.byteToInt(event?.data[2])) { + 1 -> { + dismissDialog() + ToastUtil.showToast("频率设置成功") + SelectFrequencyDialog.instance!!.setDismiss() + Log.i(TAG, event.mac + "频率设置成功") + } + } + } + 252 -> Log.i(TAG, event.mac + "设备忙碌") + 251 -> Log.i(TAG, event.mac + "保存参数失败") + 250 -> Log.i(TAG, event.mac + "算法模型学习失败") + 249 -> Log.i(TAG, event.mac + "指令无效") + } + } + } + } catch (e: java.lang.Exception) { + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/SmartAnalysisActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/SmartAnalysisActivity.kt new file mode 100644 index 0000000..ae873eb --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/SmartAnalysisActivity.kt @@ -0,0 +1,289 @@ +package com.qidian.zhongkesmart.activity + +import android.graphics.Color +import android.util.Log +import android.view.View +import android.widget.RadioGroup +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.google.android.material.tabs.TabLayout +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.model.DeviceInfo +import com.qidian.zhongkesmart.model.LineViewData +import com.qidian.zhongkesmart.model.TimeData +import com.qidian.zhongkesmart.model.TimeDataWithTime +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.activity_smart_analysis.* +import kotlinx.android.synthetic.main.layout_barchart.* + +/*智能分析*/ +class SmartAnalysisActivity : BaseActivity() { + var xValues = ArrayList() + val values = ArrayList() + var data: BarData? = null + var set: BarDataSet? = null + var mTabs= arrayOf("今日","本周","本月","本年") + var sleepMac = "" + var sportMac = "" + var mTodayData=ArrayList() + var mWeekData=ArrayList() + var mMonthData=ArrayList() + var mYearData=ArrayList() + var tapPositon = 0 + override fun getLayoutId(): Int { + return R.layout.activity_smart_analysis + } + + override fun initView() { +// setTitle("智能分析") + tv_yaxis_title.text = "时长(小时)" +// getData(0) + getData() + getBasicData(sleepMac) + getBarData(sleepMac) + //Tab + mTabs.forEach { + tablayout_type1.addTab(tablayout_type1.newTab().setText(it)) + } + tablayout_type1.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabReselected(p0: TabLayout.Tab?) { + } + + override fun onTabUnselected(p0: TabLayout.Tab?) { + } + + override fun onTabSelected(p0: TabLayout.Tab?) { + tapPositon = p0!!.position + getViewData(tapPositon) + } + + }) + + radioGroup.setOnCheckedChangeListener(object : RadioGroup.OnCheckedChangeListener { + override fun onCheckedChanged(group: RadioGroup?, checkedId: Int) { + when (checkedId) { + R.id.raiobutton_cgq -> { + getBasicData(sleepMac) + getBarData(sleepMac) + } + R.id.raiobutton_healthycondition -> { + getBasicData(sportMac) + getBarData(sportMac) + } + R.id.raiobutton_advice -> { + } + } + } + + }) + } + + override fun initData() { + } + + fun getData(poi:Int) { + xValues.clear() + values.clear() + when(poi){ + 0 ->{ + for (i in 0..23) { + //X轴显示 + xValues.add(if (i<10) "0${i}:00" else "${i}:00") + //柱状图数据 + values.add(BarEntry(i * 1f, (1..20).random()*1f)) + } + } + 1 ->{ + for (i in 0..6) { + //X轴显示 + xValues.add("星期${i+1}") + //柱状图数据 + values.add(BarEntry(i * 1f, (60..200).random()*1f)) + } + } + 2 ->{ + for (i in 0..29) { + //X轴显示 + xValues.add("${i+1}日") + //柱状图数据 + values.add(BarEntry(i * 1f, (60..200).random()*1f)) + } + } + 3 ->{ + for (i in 0..11) { + //X轴显示 + xValues.add("${i+1}月") + //柱状图数据 + values.add(BarEntry(i * 1f, (500..1000).random()*1f)) + } + } + } + + var set = BarDataSet(values, "") + set!!.color = Color.parseColor("#516AFC") + var data = BarData(set) + //有数据时候传true 无数据时候传false + if (values.size > 0) { + ChartUtils.initBarChart( + barchart, + xValues, + data!!, + false, + ) + } else { + ChartUtils.initBarChart( + barchart, + xValues, + data!!, + true, + ) + } + + } + override fun isShowTitle(): Boolean { + return false + } + + /** + * 查找贴件信息 + */ + fun getData() { + var list =ObjectBoxUtils.getAllData(DeviceInfo::class.java) + if (list.isNullOrEmpty()) { + sleepMac = "" + sportMac = "" + } else { + list.forEach{ + if(it.name=="卧室"&&it.type=="窗帘"){ + sleepMac = it.mac + } + if(it.name=="客厅"&&it.type=="门"){ + sportMac = it.mac + } + } + } + } + + fun getBasicData(mMac:String){ + if(!mMac.isNullOrEmpty()) { + var deviceInfo = ObjectBoxUtils.getDeviceByMac(mMac)!![0] + //是否在线 + if (deviceInfo.isOnline) { + tv_myequipment_status.text = "正常" + tv_myequipment_mark.setBackgroundResource(R.drawable.ed_yuan0fba61) + } else { + tv_myequipment_status.text = "离线" + tv_myequipment_mark.setBackgroundResource(R.drawable.ed_yuangray) + } + //电量 + if (deviceInfo.power == "") { + tv_myequipment_power.text = "0%" + } else { + tv_myequipment_power.text = + "${Integer.valueOf(deviceInfo.power, 10)}%" + } + }else{ + tv_myequipment_status.text = "离线" + tv_myequipment_mark.setBackgroundResource(R.drawable.ed_yuangray) + tv_myequipment_power.text = "0%" + } + } + + fun getBarData(mMac: String){ + if(!mMac.isNullOrEmpty()) { + mTodayData.clear() + mWeekData.clear() + mMonthData.clear() + mYearData.clear() + //查取图表信息 + var aList = Utils.refreshActionData(ObjectBoxUtils.getActionByMac(mMac)) + aList!!.forEach { + if (TimeUtil.isInToday(it.openTime)) { + mTodayData.add(it) + showLog(mTodayData) + } + if (TimeUtil.isInWeek(it.openTime)) { + mWeekData.add(it) + showLog(mWeekData) + } + if (TimeUtil.isInMonth(it.openTime)) { + mMonthData.add(it) + showLog(mMonthData) + } + if (TimeUtil.isInYear(it.openTime)) { + mYearData.add(it) + showLog(mYearData) + } + } + } + getViewData(tapPositon) + } + fun getViewData(poi: Int) { + xValues.clear() + values.clear() + //设置缩放比例 + barchart.zoom(0f, 1f, 0f, 0f) + var list=ArrayList() + when(poi){ + 0->{//今日 + list.addAll(TimeUtil.formatTodayDurationData(mTodayData)) + barchart.zoom(2f, 1f, 0f, 0f) + } + 1->{//本周 + list.addAll(TimeUtil.formatWeekDurationData(mWeekData)) + barchart.zoom(0f, 0f, 0f, 0f) + } + 2->{//本月 + list.addAll(TimeUtil.formatMonthDurationData(mMonthData)) + barchart.zoom(3f, 1f, 0f, 0f) + } + 3->{//本年 + list.addAll(TimeUtil.formatYearDurationData(mYearData)) + barchart.zoom(0.05f, 1f, 0f, 0f) + } + } + showLog(list) + for(i in 0 until list.size){ + //X轴显示 + xValues.add(list[i].timeLocation) + //柱状图数据 + values.add(BarEntry(i * 1f, (list[i].mSize).toFloat())) + } + + var set = BarDataSet(values, "") + set!!.color = Color.parseColor("#516AFC") + var data = BarData(set) + data.barWidth = 0.5f + //有数据时候传true 无数据时候传false + if (values.size > 0) { + ChartUtils.initBarChart( + barchart, + xValues, + data!!, + false, + ) + } else { + ChartUtils.initBarChart( + barchart, + xValues, + data!!, + true, + ) + } + } + + fun showLog(list: Any){ + var json = ToolUtil.getJson(list) + Log.i("我的设备数据==", json!!) + } + + fun onClick(v: View) { + when (v.id) { + R.id.iv_back -> { + onBackPressed() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/StandByActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/StandByActivity.kt new file mode 100644 index 0000000..7b696f8 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/StandByActivity.kt @@ -0,0 +1,236 @@ +package com.qidian.zhongkesmart.activity + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.drawable.AnimationDrawable +import android.os.Handler +import android.os.Message +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import com.amap.api.services.weather.LocalWeatherForecastResult +import com.amap.api.services.weather.LocalWeatherLiveResult +import com.amap.api.services.weather.WeatherSearch +import com.amap.api.services.weather.WeatherSearchQuery +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.model.BindBlueToothData +import com.qidian.zhongkesmart.model.FamilyInfo +import com.qidian.zhongkesmart.model.GetCodeM +import com.qidian.zhongkesmart.net.ApiUrl +import com.qidian.zhongkesmart.net.HttpCallBack +import com.qidian.zhongkesmart.net.HttpRequest +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.TimeUtil +import com.qidian.zhongkesmart.utils.ToastUtil +import com.qidian.zhongkesmart.utils.ToolUtil +import com.zhy.view.flowlayout.FlowLayout +import com.zhy.view.flowlayout.TagAdapter +import kotlinx.android.synthetic.main.layout_standby.* + + +class StandByActivity : BaseActivity()/*, WeatherSearch.OnWeatherSearchListener */{ + var flowlayoutAdapter: TagAdapter? = null + var mStansByList = ArrayList() + var drawable01: AnimationDrawable? = null + var drawable02: AnimationDrawable? = null + var showWinkAnnimationTime = 0 + var imv_stansby: ImageView? = null + + var getTimeNum = 0 + + override fun getLayoutId(): Int { + return R.layout.activity_stand_by + } + + override fun initView() { + DataServer.standByPageIsShow = true + handler.sendEmptyMessageDelayed(0, 1000) + li_santdby.setOnClickListener { + onBackPressed() + } + tv_standby_time.text = TimeUtil.getNowTimeHourMinute() + tv_standby_timelong.text = + "${TimeUtil.getNowTimeMonthDayFormat()} ${TimeUtil.getWeekDay(System.currentTimeMillis())}"; + tv_standby_weather.text=DataServer.mWeatherData + + tv_go_setting.setOnClickListener{ + startActivity(Intent(this, SetLockActivity::class.java)) + finish() + } + + } + + override fun initData() { + getFamilyInfo() + showStandByPage() + } + + fun showStandByPage() { + mStansByList.clear() + mStansByList.addAll(ToolUtil.getHistory(DataServer.mBindHistory, this)) + flowlayoutAdapter = object : TagAdapter(mStansByList) { + override fun getView(parent: FlowLayout?, position: Int, s: BindBlueToothData): View? { + val view: View = LayoutInflater.from(this@StandByActivity).inflate( + R.layout.item_standby, null + ) + imv_stansby = view.findViewById(R.id.imv_stansby) + var tv_stansby = view.findViewById(R.id.tv_stansby) + var tv_mark = view.findViewById(R.id.tv_mark) + tv_stansby.text = s.type + imv_stansby!!.setImageResource(ToolUtil.getDJPageImage(s.type)) + if (ToolUtil.getTJIsOnLineState(s.mac)) { + tv_mark.setBackgroundResource(R.drawable.ed_yuan0fba61) + } else { + tv_mark.setBackgroundResource(R.drawable.ed_yuangray) + } + + return view + } + + override fun setSelected(position: Int, s: BindBlueToothData): Boolean { + return s == s + } + } + id_flowlayout.adapter = flowlayoutAdapter + id_flowlayout.setOnTagClickListener { view, position, parent -> + true + } + startStandByAnimation01() + if (mStansByList.isEmpty()) { + li_standby_background.visibility = View.GONE + } else { + li_standby_background.visibility = View.VISIBLE + } + } + + //先显示眨眼睛动画 30分钟后显示一次左右看 + fun startStandByAnimation01() { + drawable01 = resources?.getDrawable(R.drawable.standbyanimation01) as AnimationDrawable + // 放入imageView + imv_standby!!.setBackgroundDrawable(drawable01) + // 开始 + drawable01!!.start() + showWinkAnnimationTime = 0 + } + + @SuppressLint("HandlerLeak") + var handler: Handler = object : Handler() { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + this.removeMessages(0) + sendEmptyMessageDelayed(0, 1000) + getTime() + Log.i("Handle==", "ing") + aboutWindAnimation() + } + } + + fun getTime() { + getTimeNum++ + if (getTimeNum == 30) { + tv_standby_time.text = TimeUtil.getNowTimeHourMinute() + tv_standby_timelong.text = + "${TimeUtil.getNowTimeMonthDayFormat()} ${TimeUtil.getWeekDay(System.currentTimeMillis())}"; + getTimeNum = 0 + } + } + + + fun aboutWindAnimation() { + showWinkAnnimationTime++ + if (showWinkAnnimationTime * 1000 == DataServer.mShowWinkAnnimationTimeBZ) { + showWinkAnnimationTime = 0 + startStandByAnimation02() + } + } + + //30分钟后显示一次左右看 + fun startStandByAnimation02() { + drawable02 = resources?.getDrawable(R.drawable.standbyanimation02) as AnimationDrawable + // 放入imageView + imv_standby!!.setBackgroundDrawable(drawable02) + // 开始 + drawable02!!.start() + } + + override fun isImmersion(): Boolean { + return true + } + + override fun onDestroy() { + super.onDestroy() + //停止动画 + if (drawable01 != null && drawable01!!.isRunning) { + drawable01?.stop() + } + //停止动画 + if (drawable02 != null && drawable02!!.isRunning) { + drawable02?.stop() + } + //结束定时线程 + if (handler.hasMessages(0)) { + handler.removeMessages(0) + } + DataServer.standByPageIsShow = false + } + + fun getFamilyInfo(){ + HttpRequest.init(this).get(ApiUrl.getFamilyInfo) + .setClazz(FamilyInfo::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + var familyInfo = `object` as FamilyInfo + tv_family_name.text = familyInfo.familyName + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("$code $info") + return super.failed(code, info) + } + }) + } + + /** + * 高德 + * 获取天气 + */ + /* var mquery: WeatherSearchQuery? = null + var mweathersearch: WeatherSearch? = null + fun getWeatherData() { +//检索参数为城市和天气类型,实况天气为WEATHER_TYPE_LIVE、天气预报为WEATHER_TYPE_FORECAST + //检索参数为城市和天气类型,实况天气为WEATHER_TYPE_LIVE、天气预报为WEATHER_TYPE_FORECAST + mquery = WeatherSearchQuery("北京", WeatherSearchQuery.WEATHER_TYPE_LIVE) + mweathersearch = WeatherSearch(this) + mweathersearch!!.setOnWeatherSearchListener(this) + mweathersearch!!.query = mquery + mweathersearch!!.searchWeatherAsyn() //异步搜索 + + + } + + override fun onWeatherLiveSearched(weatherLiveResult: LocalWeatherLiveResult?, rCode: Int) { + if (rCode == 1000) { + if (weatherLiveResult != null && weatherLiveResult.getLiveResult() != null) { + var weatherlive = weatherLiveResult.getLiveResult(); +// reporttime1.setText(weatherlive.getReportTime()+"发布"); +// weather.setText(weatherlive.getWeather()); +// Temperature.setText(weatherlive.getTemperature()+"°"); +// wind.setText(weatherlive.getWindDirection()+"风 "+weatherlive.getWindPower()+"级"); +// humidity.setText("湿度 "+weatherlive.getHumidity()+"%"); + Log.i("天气预报==", weatherlive.getWeather()) + } else { + Log.i("天气预报==", "暂无结果") + } + } else { + Log.i("天气预报==", "查询失败${rCode}") + } + } + + override fun onWeatherForecastSearched(p0: LocalWeatherForecastResult?, p1: Int) { + } +*/ +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/WiFiActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/activity/WiFiActivity.kt new file mode 100644 index 0000000..44b1204 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/WiFiActivity.kt @@ -0,0 +1,254 @@ +package com.qidian.zhongkesmart.activity +/*设置Wifi页面*/ + +import android.Manifest +import android.app.Dialog +import android.content.Intent +import android.content.IntentFilter +import android.net.ConnectivityManager +import android.net.Uri +import android.net.wifi.ScanResult +import android.net.wifi.WifiManager +import android.provider.Settings +import android.text.TextUtils +import android.util.Log +import android.view.View +import com.qidian.zhongkesmart.MainActivity +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.base.BaseActivity +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.dialog.* +import com.qidian.zhongkesmart.manager.BasicSettingManager +import com.qidian.zhongkesmart.receiver.WifiReceiver +import com.qidian.zhongkesmart.utils.* +import com.scwang.smartrefresh.layout.listener.OnRefreshListener +import kotlinx.android.synthetic.main.activity_wi_fi.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.layout_list.* +import net.idik.lib.slimadapter.SlimAdapter +import net.idik.lib.slimadapter.SlimInjector +import net.idik.lib.slimadapter.viewinjector.IViewInjector +import pub.devrel.easypermissions.EasyPermissions +import java.util.* +import kotlin.collections.ArrayList + + +class WiFiActivity : BaseActivity(), EasyPermissions.PermissionCallbacks { + var mData = ArrayList() + var wifiReceiver: WifiReceiver? = null + //wifi状态 默认关闭 0 开启 1 连接 2 + var IsOpen = 0 + var isShowHome = false + override fun getLayoutId(): Int { + return R.layout.activity_wi_fi + } + + override fun initView() { + //注册监听Wifi连接广播 + registerBroadcast() + askPermissionOfWiFi() + LoadUtils.setLinearLayoutManager(this, recycle_wifi, false) + mAdapter = + SlimAdapter.create() + .register(R.layout.item_wifi1, object : SlimInjector { + override fun onInject( + data: ScanResult, + injector: IViewInjector> + ) { + injector.text(R.id.tv_wifi_name, data.SSID) + injector.clicked(R.id.li_wifi) { + WifiPswDialog.instance!!.getShareDialog(this@WiFiActivity, data.SSID) + } + } + }) + .attachTo(recycle_wifi) +//初始化dialog + OpenmWifiDialog.instance!!.getShareDialog(this, + object : OpenmWifiDialog.PopupYearWindowCallBack { + override fun doWork() { +// WifiUtil.openWifiSetting(this@WiFiActivity) + //4、采用弹窗方式 应用内操作wifi + startActivityForResult(Intent(Settings.Panel.ACTION_WIFI), 0) + } + + override fun doCancel() { + } + + }) + } + + + /*关于Wifi*/ + fun getWifiStatus() { + if(WifiUtil.isWifiEnabled(this)){ + Log.i("WiFi==", "wifi连接:" + WifiUtil.getWifiName(this)) + IsOpen = 2 + li_nowchoose.visibility = View.VISIBLE + tv_nowchoose_name.text=WifiUtil.getWifiName(this) + }else{ + + } + + } + + + override fun initData() { + } + + /** + * 获取wifi权限 + */ + private fun askPermissionOfWiFi() { + EasyPermissions.requestPermissions( + this, + "请允许获取您的WiFi权限", + DataServer.ACCESS_WiFi_CODE, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.CHANGE_WIFI_STATE, + Manifest.permission.BLUETOOTH_SCAN, + Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.BLUETOOTH_ADVERTISE, + + ) + } + + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_WiFi_CODE) { + //申请成功 + registerBroadcast() + } + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == DataServer.ACCESS_WiFi_CODE) { + ToastUtil.showToast("WiFi权限获取失败,APP功能不可用!") + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", packageName, null) + startActivity(intent) + } + } + + /** + * 注册Wifi监听广播 + */ + fun registerBroadcast() { + wifiReceiver = WifiReceiver() + val filter = IntentFilter() + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION) + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) + filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) + this.registerReceiver(wifiReceiver, filter) + } + + + + override fun receiveEvent(event: EventBusUtil.MessageEvent) { + super.receiveEvent(event) + when (event.code) { + EventCode.WIFI_CONFIRM_OPEN -> { + Log.i("WiFi==", "wifi开关打开") + OpenmWifiDialog.instance!!.setDismiss() + IsOpen = 1 + showDialog() + } + EventCode.WIFI_CONFIRM_OFF -> { + Log.i("WiFi==", "wifi开关关闭") + li_nowchoose.visibility = View.GONE + OpenmWifiDialog.instance!!.setShow() + IsOpen = 0 + dimissDialog() + } + EventCode.WIFI_CONFIRM_CONNECT -> { + Log.i("WiFi==", "wifi连接:" + event.data) + IsOpen = 2 + li_nowchoose.visibility = View.VISIBLE + tv_nowchoose_name.text=event.data.toString() + WifiPswDialog.instance!!.setDismiss() + if(!isShowHome) { + isShowHome = true + var mode = SPUtil.getValue(DataServer.MConnectMode,Integer::class.java) + if(mode!=null&&mode.toInt()!=0) { + if(mode.toInt()==2) { + DataServer.connectMode = 2 + var relayMac = SPUtil.getValue(DataServer.MRelayMac,String::class.java) + if(relayMac!=null&&!TextUtils.isEmpty(relayMac)) { + startActivity(Intent(this@WiFiActivity, HomeActivity::class.java)) + }else{ + startActivity(Intent(this@WiFiActivity, ConnectModeActivity::class.java)) + } + }else{ + DataServer.connectMode = 1 + startActivity(Intent(this@WiFiActivity, HomeActivity::class.java)) + } + }else{ + DataServer.connectMode = 0 + startActivity(Intent(this@WiFiActivity, ConnectModeActivity::class.java)) + } + finish() + } + } + //列表 + EventCode.WIFI_CONFIRM_DATA -> { + dimissDialog() + Log.i("WiFi==", "wifi连接:" + event.data) + var model=event.data as List + showData(model) + } + //连接失败 + EventCode.WIFI_CONFIRM_FAIL-> { + WifiPswDialog.instance!!.showFail() + } + } + aboutViews() + } + + /*显示wifi列表*/ + fun showData(model:List){ + mData.clear() +// mData.addAll(model) + model.forEach{ + if(it.SSID.isNotEmpty()){ + mData.add(it) + } + + } + mAdapter!!.updateData(mData).notifyDataSetChanged() + } + + /*关于不同连接状态界面不同展示*/ + fun aboutViews() { + when (IsOpen) { + //关闭 + 0 -> { + recycle_wifi.visibility = View.GONE + layEmpty.visibility = View.VISIBLE + tv_emptytext.text = "您还未打开wifi或者附近暂无可用网络" + tv_button.visibility = View.GONE + } + //开启 + 1 -> { + recycle_wifi.visibility = View.VISIBLE + layEmpty.visibility = View.GONE + } + 2 -> { + recycle_wifi.visibility = View.VISIBLE + layEmpty.visibility = View.GONE + } + } + + } + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(wifiReceiver) + dimissDialog() + } + + + + fun dimissDialog(){ + if (loadingDialog != null) loadingDialog.dismiss() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/activity/adapter/RecentUserAdapter.java b/app/src/main/java/com/qidian/zhongkesmart/activity/adapter/RecentUserAdapter.java new file mode 100644 index 0000000..7e2b66d --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/activity/adapter/RecentUserAdapter.java @@ -0,0 +1,123 @@ +package com.qidian.zhongkesmart.activity.adapter; + +import android.content.Context; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.blankj.utilcode.util.NetworkUtils; +import com.blankj.utilcode.util.ToastUtils; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; +import com.netease.nimlib.sdk.avsignalling.constant.ChannelType; +import com.netease.yunxin.nertc.ui.CallKitUI; +import com.netease.yunxin.nertc.ui.base.CallParam; +import com.qidian.zhongkesmart.R; +import com.qidian.zhongkesmart.config.EventCode; +import com.qidian.zhongkesmart.model.ProfileManager; +import com.qidian.zhongkesmart.model.UserModel; +import com.qidian.zhongkesmart.utils.EventBusUtil; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import androidx.recyclerview.widget.RecyclerView; + +public class RecentUserAdapter extends RecyclerView.Adapter { + + private final List mUsers = new ArrayList<>(); + + private final Context mContext; + + //创建ViewHolder + public static class ViewHolder extends RecyclerView.ViewHolder { + public ImageView ivUser; + public TextView tvNickname; + public TextView tvCall; + + public ViewHolder(View v) { + super(v); + ivUser = v.findViewById(R.id.iv_user); + tvNickname = v.findViewById(R.id.tv_nickname); + tvCall = v.findViewById(R.id.tv_call); + } + } + + public RecentUserAdapter(Context context) { + this.mContext = context; + } + + public void updateUsers(List users) { + if (users == null) { + return; + } + this.mUsers.clear(); + mUsers.addAll(users); + notifyDataSetChanged(); + } + + public void updateItem(UserModel user) { + if (user == null) { + return; + } + this.mUsers.clear(); + mUsers.add(user); + notifyDataSetChanged(); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + if (mUsers != null) { + holder.tvNickname.setText(mUsers.get(position).mobile); + Glide.with(mContext).load(mUsers.get(position).avatar).apply(RequestOptions.bitmapTransform(new RoundedCorners(7))).into(holder.ivUser); + holder.itemView.setOnClickListener(view -> { + UserModel currentUser = ProfileManager.getInstance().getUserModel(); + if (currentUser == null || TextUtils.isEmpty(currentUser.imAccid)) { + Toast.makeText(mContext, "当前用户登录存在问题,请注销后重新登录", Toast.LENGTH_SHORT).show(); + return; + } + UserModel searchedUser = mUsers.get(position); + if (currentUser.imAccid.equals(searchedUser.imAccid) || currentUser.mobile.equals(searchedUser.mobile)) { + Toast.makeText(mContext, "不能呼叫自己!", Toast.LENGTH_SHORT).show(); + return; + } + if (NetworkUtils.isConnected()) { + // 自定义透传字段,被叫用户在收到呼叫邀请时通过参数进行解析 + JSONObject extraInfo = new JSONObject(); + + try { + extraInfo.putOpt("key", "call"); + extraInfo.putOpt("value", "testValue"); + extraInfo.putOpt("userName", currentUser.mobile); + } catch (JSONException e) { + e.printStackTrace(); + } + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.START_CALL_VIDEO)); + CallKitUI.startSingleCall(mContext, + CallParam.createSingleCallParam(ChannelType.VIDEO.getValue(), currentUser.imAccid, searchedUser.imAccid, extraInfo.toString())); + } else { + ToastUtils.showShort(R.string.network_connect_error_please_try_again); + } + }); + } + } + + @Override + public int getItemCount() { + return mUsers.size(); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_item_layout, parent, false); + return new ViewHolder(v); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/base/AppApplication.kt b/app/src/main/java/com/qidian/zhongkesmart/base/AppApplication.kt new file mode 100644 index 0000000..a0e0d16 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/base/AppApplication.kt @@ -0,0 +1,161 @@ +package com.qidian.zhongkesmart.base + +import android.app.Application +import android.content.Context +import android.os.Process +import android.text.TextUtils +import android.util.DisplayMetrics +import android.util.Log +import com.netease.nimlib.sdk.NIMClient +import com.netease.nimlib.sdk.SDKOptions +import com.netease.nimlib.sdk.auth.LoginInfo +import com.qidian.baseble.ViseBle +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.zhongkesmart.BuildConfig +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.model.MyObjectBox +import com.qidian.zhongkesmart.model.ProfileManager +import com.qidian.zhongkesmart.model.UserModel +import com.qidian.zhongkesmart.utils.BlueToothUtils +import com.qidian.zxing.lib_zxing.activity.ZXingLibrary +import com.qweather.sdk.view.HeConfig +import com.scwang.smartrefresh.layout.SmartRefreshLayout +import com.scwang.smartrefresh.layout.footer.ClassicsFooter +import com.scwang.smartrefresh.layout.header.ClassicsHeader +import com.tencent.bugly.crashreport.CrashReport +import io.objectbox.BoxStore +import io.objectbox.android.AndroidObjectBrowser +import java.io.BufferedReader +import java.io.FileReader +import java.io.IOException + + +class AppApplication : Application() { + override fun onCreate() { + super.onCreate() + mContext = applicationContext + initSmartRefreshLayout() + //获取屏幕宽高 + val dm: DisplayMetrics = this.resources.displayMetrics + widthPixels = dm.widthPixels + heightPixels = dm.heightPixels + + val context = applicationContext + // 获取当前包名 + val packageName = context.packageName + // 获取当前进程名 + val processName = + getProcessName(Process.myPid()) +//二维码 + ZXingLibrary.initDisplayOpinion(this) + //蓝牙 +// BluetoothContext.set(this) + BlueToothUtils.instance!!.init(this) +//蓝牙相关配置修改 + + + //蓝牙相关配置修改 + ViseBle.config() + .setScanTimeout(12*1000) //扫描超时时间,-1设置为永久扫描 + .setScanRepeatInterval(7 * 1000) //扫描间隔5秒 + .setConnectTimeout(5 * 1000) //连接超时时间 + .setOperateTimeout(5 * 1000) //设置数据操作超时时间 + // 这个地方设置的无效了 具体在这个地方 DeviceMirror line 80 + .setConnectRetryCount(0) //设置连接失败重试次数 + .setConnectRetryInterval(1000) //设置连接失败重试间隔时间 + .setOperateRetryCount(0) //设置数据操作失败重试次数 + .setOperateRetryInterval(1000).maxConnectCount = 1 //设置最大连接设备数量 + +//蓝牙信息初始化,全局唯一,必须在应用初始化时调用 + //蓝牙信息初始化,全局唯一,必须在应用初始化时调用 + ViseBle.getInstance().init(this) + BluetoothDeviceManager.getInstance().init(this) + + //和风天气预报 + HeConfig.init("HE2208261412031552", "ab9f3f03140047d586a036e1b6fefaf0") +//切换至开发版服务 + HeConfig.switchToDevService() + + CrashReport.initCrashReport(getApplicationContext(), "836dfa24ad", false); + + NIMClient.init(this, loginInfo(), options()) + + initObjectBox() + } + + companion object { + @JvmField + var mContext: Context? = null + var widthPixels = 0 + var heightPixels = 0 + //数据库 + var boxStore: BoxStore? = null + } + + /** + * 刷新加载相关 + */ + private fun initSmartRefreshLayout() { + SmartRefreshLayout.setDefaultRefreshHeaderCreator { context, layout -> +// layout.setPrimaryColorsId(R.color.white, R.color.black)//全局设置主题颜色 + layout.setPrimaryColorsId(R.color.tran, R.color.gray1);//全局设置主题颜色 + ClassicsHeader(context)//指定为经典Header,默认是 贝塞尔雷达Header + } + SmartRefreshLayout.setDefaultRefreshFooterCreator { context, layout -> + //指定为经典Footer,默认是 BallPulseFooter + ClassicsFooter(context).setDrawableSize(20f) + } + } + + private fun getProcessName(pid: Int): String? { + var reader: BufferedReader? = null + try { + reader = BufferedReader(FileReader("/proc/$pid/cmdline")) + var processName: String = reader.readLine() + if (!TextUtils.isEmpty(processName)) { + processName = processName.trim { it <= ' ' } + } + return processName + } catch (throwable: Throwable) { + throwable.printStackTrace() + } finally { + try { + if (reader != null) { + reader.close() + } + } catch (exception: IOException) { + exception.printStackTrace() + } + } + return null + } + + // 如果返回值为 null,则全部使用默认参数。 + private fun options(): SDKOptions? { + val options = SDKOptions() + //此处仅设置appkey,其他设置请自行参看信令文档设置 :https://dev.yunxin.163.com/docs/product/信令/SDK开发集成/Android开发集成/初始化 + options.appKey = BuildConfig.APP_KEY + return options + } + + // 如果已经存在用户登录信息,返回LoginInfo,否则返回null即可 + private fun loginInfo(): LoginInfo? { + val userModel: UserModel? = ProfileManager.getInstance().getUserModel() + return if (userModel != null && !TextUtils.isEmpty(userModel.imToken) && !TextUtils.isEmpty(userModel.imAccid)) { + LoginInfo(java.lang.String.valueOf(userModel.imAccid), userModel.imToken) + } else null + } + + /** + * 初始化数据库 + */ + private fun initObjectBox() { + //MyObjectBox找不到,必须先创建一个对象,然后重新编译,才可以 + boxStore = MyObjectBox.builder().androidContext(this).build() +// if (BuildConfig.DEBUG) { +// // 开启一个浏览服务 +// val started = AndroidObjectBrowser(boxStore).start(this) +// Log.i("ObjectBrowser", "Started: $started") +// } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/base/BaseActivity.kt b/app/src/main/java/com/qidian/zhongkesmart/base/BaseActivity.kt new file mode 100644 index 0000000..5fcd698 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/base/BaseActivity.kt @@ -0,0 +1,242 @@ +package com.qidian.zhongkesmart.base + +import android.app.Activity +import android.app.Dialog +import android.os.Bundle +import android.text.TextUtils +import android.util.Log +import android.view.* +import androidx.appcompat.app.AppCompatActivity +import com.gyf.immersionbar.ImmersionBar +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.dialog.AlarmAnimationDialog +import com.qidian.zhongkesmart.dialog.AlarmAnimationDialog2 +import com.qidian.zhongkesmart.receiver.WinCallback +import com.qidian.zhongkesmart.utils.* +import com.vise.xsnow.event.BusManager +import kotlinx.android.synthetic.main.activity_base.* +import kotlinx.android.synthetic.main.base_title_layout.* +import net.idik.lib.slimadapter.SlimAdapter +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + + +abstract class BaseActivity : AppCompatActivity() { + var mAdapter: SlimAdapter? = null + val loadingDialog: Dialog by lazy { DialogUtil.loadingDialog(this) } + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + //activity压栈 + ActivityManager.getAppManager().addActivity(this) + setContentView(R.layout.activity_base) + //Eventbus + EventBusUtil.register(this) + + BusManager.getBus().register(this) + val inflate = LayoutInflater.from(this).inflate(getLayoutId(), ll_content, false) + ll_content.addView(inflate, ViewGroup.LayoutParams.MATCH_PARENT) + layInit() + initView() + initData() + initStatusBar()//设置状态栏 +// getUse() + } + + open fun layInit() { + rl_back?.setOnClickListener { + onBackPressed() + } + + } + + /** + * 设置页面标题 + * @param title 标题 + */ + fun setTitle(title: String) { + if (!TextUtils.isEmpty(title)) { + rl_base_title.visibility = View.VISIBLE + tv_base_title.text = title + } else { + rl_base_title.visibility = View.GONE + } + } + + //设置状态栏 + fun initStatusBar() { + if (isImmersion()) { + view_status_base.visibility = View.GONE + setStatusBar(isDarkBar()) + } else { + view_status_base.visibility = View.VISIBLE + StatusBarUtil.setHeight(this, view_status_base) + setStatusBar(true) + } + } + + fun setStatusBar(isDark: Boolean) { + ImmersionBar.with(this).reset().statusBarDarkFont(isDark).init() + } + + override fun onStart() { + super.onStart() + if (isShowTitle()) { + rl_base_title.visibility = View.VISIBLE + } else { + rl_base_title.visibility = View.GONE + } + } + + //页面布局 + abstract fun getLayoutId(): Int + + //view初始化 + abstract fun initView() + + //数据初始化 + abstract fun initData() + + /** + * 沉浸式显示 不显示标头 + * 默认false ture为显示沉浸式 + */ + open fun isImmersion(): Boolean { + return false + } + + /** + * 沉浸式显示 状态栏文字颜色 + * 默认ture 黑色 false为白色 + */ + open fun isDarkBar(): Boolean { + return true + } + + /** + * 标题头是否显示 + * 默认true显示 false为不显示 + */ + open fun isShowTitle(): Boolean { + return false + } + + open fun onRightBtnClickListener() {} + + override fun onDestroy() { + super.onDestroy() + ActivityManager.getAppManager().finishActivity(this) + EventBusUtil.unregister(this) + BusManager.getBus().unregister(this) + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } + + fun showDialog() { + if (loadingDialog != null && !loadingDialog.isShowing) { + loadingDialog.show() + } + } + + fun dismissDialog() { + if (loadingDialog != null && loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } + + //是否退出app + protected open fun isExitApp(): Boolean { + return false + } + + private var current_time: Long = 0 + override fun onBackPressed() { + if (isExitApp()) { + if (current_time > 0 && System.currentTimeMillis() - current_time <= 3000) { + super.onBackPressed() + } else { + ToastUtil.showToast("再按一次退出") + current_time = System.currentTimeMillis() + } + } else { + super.onBackPressed() + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventBusCome(event: EventBusUtil.MessageEvent) { + if (event != null) { + receiveEvent(event) + } + } + + /** + * 接收到分发到事件 * * @param event 事件 + */ + open fun receiveEvent(event: EventBusUtil.MessageEvent) {} + + + + + override fun onTouchEvent(event: MotionEvent?): Boolean { +// Log.e( +// "屏幕操作监听==", +// "onTouchEvent: 屏幕被操作" +// ) + + return super.onTouchEvent(event) + } + + fun showBaseAlarmDialog(type: String?,mac:String) { + if(!DataServer.isShowAlarmMac(mac)){ + AlarmAnimationDialog2.instance!!.getShareDialog( + ActivityManager.getAppManager().currentActivity(), + type,mac, + object : AlarmAnimationDialog2.PopupYearWindowCallBack { + override fun doWork() { + //解除警报 + } + + }) + } + } + + open var TAG = "屏幕操作监听==" + + //监听app是否被操作 + fun getUse() { + val activity: Activity = ActivityManager.getAppManager().currentActivity() ?: return + val win: Window = activity.getWindow() +// Log.e( +// TAG, +// "touchOnclick: activity=$activity" +// ) + win.callback = object : WinCallback(win.callback) { + override fun dispatchTouchEvent(event: MotionEvent): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> { + DataServer.ChangeFocusMark = 0 +// Log.e( +// TAG, +// "dispatchTouchEvent:activity窗口被触摸" +// +// ) + } + MotionEvent.ACTION_UP -> { + DataServer.ChangeFocusMark = 0 +// Log.e( +// TAG, +// "dispatchTouchEvent:activity窗口被触摸" +// +// ) + } + } + return super.dispatchTouchEvent(event) + } + } + + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/bluetooth/ClientManager.java b/app/src/main/java/com/qidian/zhongkesmart/bluetooth/ClientManager.java new file mode 100644 index 0000000..e496d14 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/bluetooth/ClientManager.java @@ -0,0 +1,22 @@ +package com.qidian.zhongkesmart.bluetooth; + +import com.qidian.zhongkesmart.base.AppApplication; + +/** + * Created by dingjikerbo on 2016/8/27. + */ +public class ClientManager { + + /* private static BluetoothClient mClient; + + public static BluetoothClient getClient() { + if (mClient == null) { + synchronized (ClientManager.class) { + if (mClient == null) { + mClient = new BluetoothClient(AppApplication.mContext); + } + } + } + return mClient; + }*/ +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/bluetooth/dfu/DfuService.java b/app/src/main/java/com/qidian/zhongkesmart/bluetooth/dfu/DfuService.java new file mode 100644 index 0000000..43c0c2c --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/bluetooth/dfu/DfuService.java @@ -0,0 +1,48 @@ +package com.qidian.zhongkesmart.bluetooth.dfu; + +import android.app.Activity; + +import com.qidian.zhongkesmart.config.Config; + +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import no.nordicsemi.android.dfu.DfuBaseService; + +public class DfuService extends DfuBaseService { + + @Override + protected Class getNotificationTarget() { + /* + * As a target activity the NotificationActivity is returned, not the MainActivity. This is because + * the notification must create a new task: + * + * intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + * + * when you press it. You can use NotificationActivity to check whether the new activity + * is a root activity (that means no other activity was open earlier) or that some + * other activity is already open. In the latter case the NotificationActivity will just be + * closed. The system will restore the previous activity. However, if the application has been + * closed during upload and you click the notification, a NotificationActivity will + * be launched as a root activity. It will create and start the main activity and + * terminate itself. + * + * This method may be used to restore the target activity in case the application + * was closed or is open. It may also be used to recreate an activity history using + * startActivities(...). + */ + return NotificationActivity.class; + } + + @Override + protected boolean isDebug() { + // Here return true if you want the service to print more logs in LogCat. + // Library's BuildConfig in current version of Android Studio is always set to DEBUG=false, so + // make sure you return true or your.app.BuildConfig.DEBUG here. + return Config.DebugMode; + } + + @Override + protected void updateForegroundNotification(@NonNull final NotificationCompat.Builder builder) { + // Customize the foreground service notification here. + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/bluetooth/dfu/NotificationActivity.java b/app/src/main/java/com/qidian/zhongkesmart/bluetooth/dfu/NotificationActivity.java new file mode 100644 index 0000000..134a473 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/bluetooth/dfu/NotificationActivity.java @@ -0,0 +1,26 @@ +package com.qidian.zhongkesmart.bluetooth.dfu; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +import com.qidian.zhongkesmart.activity.SettingActivity; + +public class NotificationActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // If this activity is the root activity of the task, the app is not running + if (isTaskRoot()) { + // Start the app before finishing + final Intent intent = new Intent(this, SettingActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtras(getIntent().getExtras()); // copy all extras + startActivity(intent); + } + + // Now finish, which will drop you to the activity at which you were at the top of the task stack + finish(); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/config/Config.java b/app/src/main/java/com/qidian/zhongkesmart/config/Config.java new file mode 100644 index 0000000..626cf0d --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/config/Config.java @@ -0,0 +1,41 @@ +package com.qidian.zhongkesmart.config; + +public class Config { + + /** + * 开发环境 + * 发布到 Market 时, 必须要将其置为false, 不显示log + */ + public static boolean DebugMode = true; + + /** + * 下载的安装包文件 + */ + public static final String DOWNLOAD_APK= "smartspeaker.apk"; + + /** + * 下载的代理固件 + */ + public static final String DOWNLOAD_FIRMWARE_D= "firmware_d.zip"; + + /** + * 下载的低功耗固件 + */ + public static final String DOWNLOAD_FIRMWARE_L= "firmware_l.zip"; + + /** + * downloadId保存本地 + */ + public static final String DOWNLOAD_ID_STATUS= "DownloadIdStatus"; + + /** + * 代理固件下载url + */ + public static final String DOWNLOAD_FIRMWARE_D_URL= "https://ifhs.fedhealth.cn/gate/SDK1702_d_1.0.4.zip"; + + /** + * 低功耗固件下载url + */ + public static final String DOWNLOAD_FIRMWARE_L_URL= "https://ifhs.fedhealth.cn/gate/SDK1702_l_1.0.1.zip"; + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/config/EventCode.java b/app/src/main/java/com/qidian/zhongkesmart/config/EventCode.java new file mode 100644 index 0000000..87faedd --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/config/EventCode.java @@ -0,0 +1,85 @@ +package com.qidian.zhongkesmart.config; + +public class EventCode { + public final static int WIFI_CONFIRM_OFF = 0x0001; + public final static int WIFI_CONFIRM_OPEN = 0x0002; + public final static int WIFI_CONFIRM_CONNECT = 0x0003; + public final static int WIFI_CONFIRM_DATA = 0x0004; + public final static int WIFI_CONFIRM_FAIL = 0x0005; + public final static int PERMISSION_ACCESS_COARSE_LOCATION = 0x0006; + + //蓝牙 + public final static int BLUE_CONFIRM_SUCCESS = 0x0007; + public final static int BLUE_CONFIRM_FAIL = 0x0008; + public final static int BLUE_CONFIRM_DiSCONNECT = 0x0009; + + /** + * 数据推送节点 + */ + //新增数据 1. 贴件新增数据时 * 位移型:新增数据 * 震动型:新增第一条数据 + public final static int PUSHDATA_NEWGET = 0x0010; + //定时任务发现报警时发送一条数据 + public final static int PUSHDATA_TIMING_ALARM = 0x0011; + //电量低 * 20%推送一次,10%推送一次 + public final static int PUSHDATA_LOW_POWER = 0x0012; + //定时任务发现离线时推送一次数据 + public final static int PUSHDATA_OFFLINE = 0x0013; + //蓝牙数据改变-开关、报警、正常离线 + public final static int BLUETOOTHDATA_Refresh = 0x0013; + /** + * 焦点变化标志 + */ + public final static int FOCUS_CHANGE = 0x0014; + + /** + * 接受到BDBD数据,更新数据条数 + */ + public final static int PUSHDATA_BDBD = 0x0015; + + /** + * 贴件唤醒成功,可以进入配置模式 + */ + public final static int DEVICE_WAKE_UP_SUCCEED = 0x0016; + + /** + * 开始通话 + */ + public final static int START_CALL_VIDEO = 0x0020; + + /** + * 结束通话 + */ + public final static int FINISH_CALL_VIDEO = 0x0021; + + /** + * 开始下载 + */ + public final static int DOWNLOAD_START = 0x0031; + + /** + * 下载完成 + */ + public final static int DOWNLOAD_FINISH = 0x0032; + + /** + * 下载出错 + */ + public final static int DOWNLOAD_ERROR = 0x0033; + + /** + * 下载进度 + */ + public final static int DOWNLOAD_PROGRESS = 0x0034; + + + /** + * 通知homeactivity开启扫描 + */ + public final static int START_SCAN = 0x0040; + + + + + + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/AlarmAnimationDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/AlarmAnimationDialog.kt new file mode 100644 index 0000000..6ef53f5 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/AlarmAnimationDialog.kt @@ -0,0 +1,141 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.drawable.AnimationDrawable +import android.view.Gravity +import android.view.View +import android.view.Window +import android.view.WindowManager +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.ToolUtil + +/** + * 报警动画 + */ +class AlarmAnimationDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + private var mType: String? = null + private var mMac: String? = null + var dialog: CustomCommonDialog? = null + var drawable: AnimationDrawable? = null + fun getShareDialog(context: Context?, type: String?,mac:String, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + mType = type + mMac = mac + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(1f) + dialog!!.heightScale(1f) + dialog!!.show() + dialog!!.setCanceledOnTouchOutside(false) + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + interface PopupYearWindowCallBack { + fun doWork() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_animation_cancel: TextView? = null + var tv_animation_text: TextView? = null + var imv_animation: ImageView? = null + var li_close: LinearLayout? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_animation, null) + /*inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + )*/ + imv_animation = inflate.findViewById(R.id.imv_animation) as ImageView + tv_animation_text = inflate.findViewById(R.id.tv_animation_text) as TextView + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + tv_animation_cancel = inflate.findViewById(R.id.tv_animation_cancel) as TextView + + StartAnimaton() + return inflate + } + + override fun setUiBeforShow() { + li_close!!.setOnClickListener { + StopAnimation() + callBack!!.doWork() + dismiss () + } + //解除警报 + tv_animation_cancel!!.setOnClickListener { + StopAnimation() + callBack!!.doWork() + dismiss() + } + } + + fun StartAnimaton() { + //该条贴件数据 + var bindBean = ToolUtil.getBindMessByMac(mMac!!, activity!!) + + when (mType) { + "门" -> { + tv_animation_text!!.text="${bindBean!!.name}${bindBean.type}贴件长时间未关闭" + drawable = + activity?.resources?.getDrawable(R.drawable.dooranimation) as AnimationDrawable + } + "水管" -> { + tv_animation_text!!.text="${bindBean!!.name}${bindBean.type}贴件长时间未关闭" + drawable = + activity?.resources?.getDrawable(R.drawable.sganimation) as AnimationDrawable + } + "电冰箱" -> { + tv_animation_text!!.text="${bindBean!!.name}${bindBean.type}贴件长时间未关闭" + drawable = + activity?.resources?.getDrawable(R.drawable.iceanimation) as AnimationDrawable + } + "煤气灶" -> { + tv_animation_text!!.text="${bindBean!!.name}${bindBean.type}贴件长时间未关闭" + drawable = + activity?.resources?.getDrawable(R.drawable.rqzanimation) as AnimationDrawable + } + "电量低" -> { + tv_animation_text!!.text="${bindBean!!.name}${bindBean.type}贴件电量过低" + drawable = + activity?.resources?.getDrawable(R.drawable.poweranimation) as AnimationDrawable + } + } + // 放入imageView + imv_animation!!.setBackgroundDrawable(drawable) + // 开始 + drawable!!.start() + } + + fun StopAnimation(){ + if (drawable!!.isRunning) { + drawable!!.stop() + } + } + + } + + + + companion object { + private var popupWindowPrivinceListUtils: AlarmAnimationDialog? = null + +// @get:Synchronized + val instance: AlarmAnimationDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = AlarmAnimationDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/AlarmAnimationDialog2.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/AlarmAnimationDialog2.kt new file mode 100644 index 0000000..c545b74 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/AlarmAnimationDialog2.kt @@ -0,0 +1,221 @@ +package com.qidian.zhongkesmart.dialog + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.AnimationDrawable +import android.util.Log +import android.view.* +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.ActivityManager +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.ToolUtil + +/** + * 报警动画 + */ +class AlarmAnimationDialog2 { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + private var mType: String? = null + private var mMac: String? = null + var dialog: Dialog? = null + var drawable: AnimationDrawable? = null + + /* var tv_animation_cancel: TextView? = null + var tv_animation_text: TextView? = null + var imv_animation: ImageView? = null + var li_close: LinearLayout? = null*/ + + fun getShareDialog( + context: Context?, + type: String?, + mac: String, + callBack: PopupYearWindowCallBack? + ) { + activity = context + this.callBack = callBack + mType = type + mMac = mac + showDialog(mac) + DataServer.listAlarmMac.add(mac!!) + DataServer.listAlarmDialog.add(dialog!!) + Log.i("报警弹窗Mac==", DataServer.listAlarmMac.toString()) + } + + /*fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + DataServer.removeAlarmMac(mMac!!) + } + }*/ + + fun showDialog(mac:String) { + // 构建Dialog +// dialog = Dialog(activity!!) + dialog = Dialog(activity!!, R.style.dialog_fullscreen_menu) +// dialog!!.window!!.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + val inflate: View = + LayoutInflater.from(activity!!).inflate(R.layout.pop_animation, null) + + + var imv_animation = inflate.findViewById(R.id.imv_animation) as ImageView + var tv_animation_text = inflate.findViewById(R.id.tv_animation_text) as TextView + var li_close = inflate.findViewById(R.id.li_close) as LinearLayout + var tv_animation_cancel = inflate.findViewById(R.id.tv_animation_cancel) as TextView + + li_close!!.setOnClickListener { +// StopAnimation() + if (drawable!!.isRunning) { + drawable!!.stop() + } + callBack!!.doWork() + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + DataServer.removeAlarmMac(mac!!) + } + } + //解除警报 + tv_animation_cancel!!.setOnClickListener { +// StopAnimation() + if (drawable!!.isRunning) { + drawable!!.stop() + } + var a = mMac + callBack!!.doWork() + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + DataServer.removeAlarmMac(mac!!) + } + } + + dialog!!.setContentView(inflate) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.setCancelable(false) + + + val window: Window? = dialog!!.window + window?.let { + window.decorView.setPadding(0, 0, 0, 0) // 设置弹窗跟原始padding为0 + + val lp: WindowManager.LayoutParams? = window.attributes + lp?.let { + it.gravity = Gravity.CENTER + it.width = WindowManager.LayoutParams.MATCH_PARENT + it.height = WindowManager.LayoutParams.MATCH_PARENT + it.horizontalMargin = 0F + window.attributes = it + + } + window.decorView.minimumWidth = activity!!.resources.displayMetrics.widthPixels + window.decorView.setBackgroundColor(Color.TRANSPARENT) // 颜色要设置,不然不能全屏,所以设置透明 + } + + + dialog!!.show() +// StartAnimaton() + //该条贴件数据 + var bindBean = ToolUtil.getBindMessByMac(mMac!!, activity!!) + + when (mType) { + "门" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.dooranimation) as AnimationDrawable + } + "水管" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.sganimation) as AnimationDrawable + } + "电冰箱" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.iceanimation) as AnimationDrawable + } + "煤气灶" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.rqzanimation) as AnimationDrawable + } + "电量低" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件电量过低" + drawable = + activity?.resources?.getDrawable(R.drawable.poweranimation) as AnimationDrawable + } + } + // 放入imageView + imv_animation!!.setBackgroundDrawable(drawable) + // 开始 + drawable!!.start() + } + + interface PopupYearWindowCallBack { + fun doWork() + } + + fun setUiBeforShow() { + + } + + /* fun StartAnimaton() { + //该条贴件数据 + var bindBean = ToolUtil.getBindMessByMac(mMac!!, activity!!) + + when (mType) { + "门" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.dooranimation) as AnimationDrawable + } + "水管" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.sganimation) as AnimationDrawable + } + "电冰箱" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.iceanimation) as AnimationDrawable + } + "煤气灶" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件开启时间过长" + drawable = + activity?.resources?.getDrawable(R.drawable.rqzanimation) as AnimationDrawable + } + "电量低" -> { + tv_animation_text!!.text = "${bindBean!!.name}${bindBean.type}贴件电量过低" + drawable = + activity?.resources?.getDrawable(R.drawable.poweranimation) as AnimationDrawable + } + } + // 放入imageView + imv_animation!!.setBackgroundDrawable(drawable) + // 开始 + drawable!!.start() + } +*/ + fun StopAnimation() { + if (drawable!!.isRunning) { + drawable!!.stop() + } + } + + + companion object { + private var popupWindowPrivinceListUtils: AlarmAnimationDialog2? = null + + // @get:Synchronized + val instance: AlarmAnimationDialog2? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = AlarmAnimationDialog2() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelBindDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelBindDialog.kt new file mode 100644 index 0000000..d6d6143 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelBindDialog.kt @@ -0,0 +1,89 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*取消绑定*/ +class CancelBindDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_content: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_cancelbind, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + pop_cancnel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + pop_ok = inflate.findViewById(R.id.tv_pop_sure) as TextView + tv_content + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + li_close!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: CancelBindDialog? = null + +// @get:Synchronized + val instance: CancelBindDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = CancelBindDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelModeInitDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelModeInitDialog.kt new file mode 100644 index 0000000..30b7804 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelModeInitDialog.kt @@ -0,0 +1,89 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*取消绑定*/ +class CancelModeInitDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_content: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_cancel_mode_init, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + pop_cancnel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + pop_ok = inflate.findViewById(R.id.tv_pop_sure) as TextView + tv_content + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + li_close!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: CancelModeInitDialog? = null + +// @get:Synchronized + val instance: CancelModeInitDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = CancelModeInitDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelSettingModeDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelSettingModeDialog.kt new file mode 100644 index 0000000..f6d00af --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/CancelSettingModeDialog.kt @@ -0,0 +1,89 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*取消绑定*/ +class CancelSettingModeDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_content: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_cancel_setting_mode, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + pop_cancnel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + pop_ok = inflate.findViewById(R.id.tv_pop_sure) as TextView + tv_content + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + li_close!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: CancelSettingModeDialog? = null + +// @get:Synchronized + val instance: CancelSettingModeDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = CancelSettingModeDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/ChangeConnectModeDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/ChangeConnectModeDialog.kt new file mode 100644 index 0000000..09f116a --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/ChangeConnectModeDialog.kt @@ -0,0 +1,90 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*取消绑定*/ +class ChangeConnectModeDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var mTitle = "" + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?,title:String) { + activity = context + this.callBack = callBack + mTitle = title + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var li_close: LinearLayout? = null + var tv_title: TextView? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_change_connect_mode, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + pop_cancnel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + pop_ok = inflate.findViewById(R.id.tv_pop_sure) as TextView + tv_title = inflate.findViewById(R.id.tv_title) as TextView + return inflate + } + + override fun setUiBeforShow() { + tv_title!!.text = "修改网关的连接模式为${mTitle}吗?点击确定将会重启应用" + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + li_close!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: ChangeConnectModeDialog? = null + +// @get:Synchronized + val instance: ChangeConnectModeDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = ChangeConnectModeDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/ConnectBluetoothDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/ConnectBluetoothDialog.kt new file mode 100644 index 0000000..ffbff09 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/ConnectBluetoothDialog.kt @@ -0,0 +1,87 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*蓝牙配对*/ +class ConnectBluetoothDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var mTitle="" + var mCode="" + + fun getShareDialog(context: Context?, title:String,code:String,callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + this.mTitle = title + this.mCode = code + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_title: TextView? = null + var tv_concent: TextView? = null + var pop_ok: TextView? = null + + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_connectbluetooth, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + tv_title = inflate.findViewById(R.id.tv_title) as TextView + tv_concent = inflate.findViewById(R.id.tv_concent) as TextView + pop_ok = inflate.findViewById(R.id.tv_pop_sure) as TextView + tv_title!!.text=mTitle + tv_concent!!.text=""+mCode + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + } + } + + companion object { + private var popupWindowPrivinceListUtils: ConnectBluetoothDialog? = null + + @get:Synchronized + val instance: ConnectBluetoothDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = ConnectBluetoothDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/DisconnectBlueDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/DisconnectBlueDialog.kt new file mode 100644 index 0000000..e29d4b4 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/DisconnectBlueDialog.kt @@ -0,0 +1,92 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*提示是否断开蓝牙*/ +class DisconnectBlueDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + setShow() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + fun setShow(){ + if (dialog != null && dialog!!.isShowing) { + return + } + dialog!!.show() + } + + interface PopupYearWindowCallBack { + fun doWork() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_content: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_openwifi, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + tv_content = inflate.findViewById(R.id.tv_content) as TextView + pop_cancnel = inflate.findViewById(R.id.pop_cancnel) as TextView + pop_ok = inflate.findViewById(R.id.pop_ok) as TextView + tv_content!!.text="是否要断开与该蓝牙的连接?" + pop_ok!!.text="确定" + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: DisconnectBlueDialog? = null + + @get:Synchronized + val instance: DisconnectBlueDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = DisconnectBlueDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/EquipmentStatusDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/EquipmentStatusDialog.kt new file mode 100644 index 0000000..04131f8 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/EquipmentStatusDialog.kt @@ -0,0 +1,329 @@ +package com.qidian.zhongkesmart.dialog + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.os.Handler +import android.os.Message +import android.util.Log +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.model.GetCodeM +import com.qidian.zhongkesmart.model.GetLoginM +import com.qidian.zhongkesmart.model.GetTokenM +import com.qidian.zhongkesmart.net.* +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.layout_list.* +import java.util.* + +/*设备状态弹窗*/ +class EquipmentStatusDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var mLoopTime = 180 //默认轮训三分钟 +// var mLoopTime = 10 //默认轮训三分钟 + var isLoading = false + var mTAG = "设备绑定==" + var mCode = "" + var li_equipment_need: LinearLayout? = null + var li_close: LinearLayout? = null + var tv_equipment_status: TextView? = null + var tv_equipment_bind: TextView? = null + var tv_equipment_text1: TextView? = null + var tv_equipment_text2: TextView? = null + var tv_needtext: TextView? = null + var imv_equipment: ImageView? = null + //是否绑定成功了,绑定成功后 二维码type为2 + var isDeviceStatus = false + var isBinding = false + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.6f) + dialog!!.show() + dialog!!.setCanceledOnTouchOutside(false) + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + if (handler.hasMessages(0)) { + handler.removeMessages(0) + } + } + + interface PopupYearWindowCallBack { + fun doWork(place: String?, use: String?) + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + + + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_equipmentstatus, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) + li_equipment_need = inflate.findViewById(R.id.li_equipment_need) + tv_needtext = inflate.findViewById(R.id.tv_needtext) + tv_equipment_status = inflate.findViewById(R.id.tv_equipment_status) + tv_equipment_bind = inflate.findViewById(R.id.tv_equipment_bind) + tv_equipment_text1 = inflate.findViewById(R.id.tv_equipment_text1) + tv_equipment_text2 = inflate.findViewById(R.id.tv_equipment_text2) + imv_equipment = inflate.findViewById(R.id.imv_equipment) + if (DataServer.mHttpToken.isNullOrEmpty()) { + isDeviceStatus = false + showNeedBind("请先绑定网关") + li_equipment_need!!.visibility = View.VISIBLE + //开始轮训 + StartLoop() + } else { + li_equipment_need!!.visibility = View.GONE + isDeviceStatus = true + Log.i(mTAG, "mCode=${DataServer.mCode}") + if(!DataServer.mCode.isNullOrEmpty()) + createErCode(DataServer.mCode)//已绑定 直接生成 包含id + } + + return inflate + } + + /** + * 设备绑定 + * 设备绑定的二维码包含:当前设备ID和绑定状态等信息 + 微信小程序通过扫码获取信息后像服务端发起绑定申请 + 当用户打开该弹窗时,APP需要先判断自己本地是否有token,如果没有则先向服务端申请Code + 再使用该code轮询调用服务端接口获取token + 轮询每秒一次,code的有效期为3分钟,3分钟后停止轮询,当获取到token后弹出吐司提示“绑定成功”,停止轮询 + 当超过3分钟后,二维显示“请重新刷新二维码”,点击后刷新后重复上述过程 + */ + /*点击事件处理*/ + override fun setUiBeforShow() { + //设别状态 + tv_equipment_status!!.setOnClickListener { + tv_equipment_status!!.setTextColor(activity!!.resources.getColor(R.color.white)) + tv_equipment_bind!!.setTextColor(activity!!.resources.getColor(R.color.color_333333)) + tv_equipment_status!!.setBackgroundResource(R.drawable.ed_0_516afc_full) + tv_equipment_bind!!.setBackgroundResource(R.drawable.ed_0_ffc5cbd7_line) + tv_equipment_text1!!.text = "获取全部设备状态" + tv_equipment_text2!!.text = "使用小程序扫描二维码获取全部设备状态至小程序" + if (DataServer.mHttpToken.isNullOrEmpty()) { + showNeedBind("请先绑定网关") + } else { + li_equipment_need!!.visibility = View.GONE + } +// StopLoop() + } + //设别绑定 + tv_equipment_bind!!.setOnClickListener { + tv_equipment_bind!!.setTextColor(activity!!.resources.getColor(R.color.white)) + tv_equipment_status!!.setTextColor(activity!!.resources.getColor(R.color.color_333333)) + tv_equipment_bind!!.setBackgroundResource(R.drawable.ed_0_516afc_full) + tv_equipment_status!!.setBackgroundResource(R.drawable.ed_0_ffc5cbd7_line) + tv_equipment_text1!!.text = "使用小程序绑定该设备" + tv_equipment_text2!!.text = "使用小程序扫描二维码" + li_equipment_need!!.visibility = View.GONE + if (DataServer.mHttpToken.isNullOrEmpty()) { + //开始轮训 + StartLoop() + } + + } + li_close!!.setOnClickListener { + setDismiss() + StopLoop() + } + li_equipment_need!!.setOnClickListener { + if (tv_needtext!!.text == "请刷新二维码") { + li_equipment_need!!.visibility=View.GONE + //刷新二维码 再次开始轮训 + StartLoop() + } + } + } + + + } + + //显示网关提示覆盖布局 + fun showNeedBind(str: String) { + li_equipment_need!!.visibility = View.VISIBLE + tv_needtext!!.text = str + } + + //开始轮训 + fun StartLoop() { + + if (handler.hasMessages(0)) { + handler.removeMessages(0) + } + Log.i(mTAG, "开始轮训") + //开始轮训token + mLoopTime = 180 + mCode = "" + handler.sendEmptyMessageDelayed(0, 1000) + } + + //结束轮训 + fun StopLoop() { + if (handler.hasMessages(0)) { + handler.removeMessages(0) + } + Log.i(mTAG, "停止轮训") + //开始轮训token + mLoopTime = 180 + mCode = "" + } + + + @SuppressLint("HandlerLeak") + var handler: Handler = object : Handler() { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + Log.d("-Tag111", "handler定时") + this.removeMessages(0) + if (mLoopTime > 0) { + mLoopTime-- + sendEmptyMessageDelayed(0, 1000) + if (!isLoading) { + //code有效期内不需要再重新获取 + if (mCode.isNullOrEmpty()) { + isLoading = true + getHttpCode() + } else { + isLoading = true + getBindingStatus(mCode) + } + } + } else { + //结束一轮轮训 + removeMessages(0) + Log.i(mTAG, "结束轮训") + //显示刷新 + showNeedBind("请刷新二维码") + } + + } + } + + + /** + * 网关登录 + */ + //获取code + fun getHttpCode() { + var map = HashMap() + map["id"] = DeviceUuidFactory.getAndroidID(activity) + HttpRequest.init(activity).get(ApiUrl.getCode) + .setMap(map) + .setShowDialog(false) +// .setClazz(GetCodeM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("getHttpCode", message!!) + mCode = `object`.toString() + createErCode(mCode)//未绑定 直接生成 包含id+code + if(!isBinding) { + getBindingStatus(mCode) + }else{ + getToken((mCode)) + } + } + }) + } + + //获取网关绑定状态 + fun getBindingStatus(mCode: String) { + var map = HashMap() + map["code"] = mCode + map["id"] = DeviceUuidFactory.getAndroidID(activity) + HttpRequest.init(activity).postJson(ApiUrl.getBindingStatus,map,"",false) + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("getBindingStatus", message!!) + getHttpCode() + isBinding = true + this@EquipmentStatusDialog.mCode = "" + } + + override fun failed(code: String?, info: String?): Boolean { + Log.e("getBindingStatus", code!!) + return super.failed(code, info) + } + + override fun after() { + super.after() + isLoading = false + } + }) + } + + //网关登录 获取token存本地 这里如果绑定了就有token 不绑定就没有token 不影响后面测试网关是否连接成功 + fun getToken(mCode: String) { + var map = HashMap() + map["code"] = mCode + map["id"] = DeviceUuidFactory.getAndroidID(activity) + HttpRequest.init(activity).postJson(ApiUrl.getLogin, map, "",false) + .setClazz(GetTokenM::class.java) + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + var model = `object` as GetTokenM + var mToken = model.token + Log.i("SJF:model", mToken) + + DataServer.mHttpToken = mToken + DataServer.mCode = mCode + if (!DataServer.mHttpToken.isNullOrEmpty()) { + li_equipment_need!!.visibility = View.GONE + } + isDeviceStatus = true + //createErCode(mCode) + StopLoop() +// handler.removeMessages(0) + ToastUtil.showToast("绑定成功") + } + + override fun failed(code: String?, info: String?): Boolean { + this@EquipmentStatusDialog.mCode = "" + return super.failed(code, info) + } + + override fun after() { + super.after() + isLoading = false + } + + }) + } + + companion object { + private var popupWindowPrivinceListUtils: EquipmentStatusDialog? = null + + @get:Synchronized + val instance: EquipmentStatusDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = EquipmentStatusDialog() + } + return popupWindowPrivinceListUtils + } + } + + //生成二维码 + fun createErCode(inputcode:String){ + var btimap=ToolUtil.createEwm(DeviceUuidFactory.getAndroidID(activity),inputcode,if(isDeviceStatus) 2 else 1) + imv_equipment!!.setImageBitmap(btimap) + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/FindNewEquipmentDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/FindNewEquipmentDialog.kt new file mode 100644 index 0000000..e65dd0d --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/FindNewEquipmentDialog.kt @@ -0,0 +1,459 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.EditText +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.activity.HomeActivity +import com.qidian.zhongkesmart.activity.SettingModeActivity +import com.qidian.zhongkesmart.model.CommonMLocation +import com.qidian.zhongkesmart.model.CommonMUse +import com.qidian.zhongkesmart.utils.* +import com.scwang.smartrefresh.layout.util.DensityUtil.dp2px +import kotlinx.android.synthetic.main.layout_list.* +import net.idik.lib.slimadapter.SlimAdapter +import net.idik.lib.slimadapter.SlimInjector +import net.idik.lib.slimadapter.viewinjector.IViewInjector +import org.w3c.dom.Text +import java.util.* +import kotlin.collections.ArrayList + +/*发现新设备弹窗*/ +class FindNewEquipmentDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var mChooseType = 0//默认显示设备信息 + var mMac = "" + var ll_setting_mode: LinearLayout? = null + var tv_pop_sure: TextView? = null + var tv_pop_relievebind: TextView? = null + var tv_pop_cancel: TextView? = null + var tv_connect_status:TextView? = null + fun getShareDialog(context: Context?, mac: String, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + this.mMac = mac + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.8f) + dialog!!.show() + dialog!!.setCanceledOnTouchOutside(false) + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + callBack!!.doDismiss() + } + } + + fun bindSuccess(){ + if (dialog != null && dialog!!.isShowing) { +// ll_setting_mode!!.visibility = View.VISIBLE + tv_pop_relievebind!!.visibility = View.VISIBLE + tv_pop_cancel!!.visibility = View.GONE + tv_pop_sure!!.text = "下一步" + } + } + + fun connectSuccess(){ + if (dialog != null && dialog!!.isShowing) { + tv_connect_status!!.visibility = View.GONE + } + } + + fun connectFail(){ + if (dialog != null && dialog!!.isShowing) { + tv_connect_status!!.visibility = View.VISIBLE + } + } + + fun goSettingMode(){ + setDismiss() + activity!!.startActivity(Intent(activity, SettingModeActivity::class.java)) + } + + interface PopupYearWindowCallBack { + fun doWork(roomName: String?, type: String?) + fun doChangeWork(roomName: String?, type: String?) + fun doUnBindWork() + fun doConnectTj() + fun doDismiss() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var li_close: LinearLayout? = null + var li_new_place: LinearLayout? = null + var li_new_use: LinearLayout? = null + var li_equipment: LinearLayout? = null + var tv_pop_title: TextView? = null + var tv_new_name: TextView? = null + var tv_place_custom: TextView? = null + var tv_place_worm: TextView? = null + var tv_use_custom: TextView? = null + var tv_use_worm: TextView? = null + var imv_pic: ImageView? = null + var tv_new_place: EditText? = null + var tv_new_use: EditText? = null + var recycle_options: RecyclerView? = null + var mAdapter: SlimAdapter? = null + var mData = ArrayList() + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_findnew, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) + li_new_place = inflate.findViewById(R.id.li_new_place) + li_new_use = inflate.findViewById(R.id.li_new_use) + tv_pop_title = inflate.findViewById(R.id.tv_pop_title) + tv_new_name = inflate.findViewById(R.id.tv_new_name) + imv_pic = inflate.findViewById(R.id.imv_pic) + tv_new_place = inflate.findViewById(R.id.tv_new_place) + tv_place_custom = inflate.findViewById(R.id.tv_place_custom) + tv_place_worm = inflate.findViewById(R.id.tv_place_worm) + tv_new_use = inflate.findViewById(R.id.tv_new_use) + tv_use_custom = inflate.findViewById(R.id.tv_use_custom) + tv_use_worm = inflate.findViewById(R.id.tv_use_worm) + recycle_options = inflate.findViewById(R.id.recycle_options) + li_equipment = inflate.findViewById(R.id.li_equipment) + //按钮 + tv_pop_cancel = inflate.findViewById(R.id.tv_pop_cancel) + tv_pop_relievebind = inflate.findViewById(R.id.tv_pop_relievebind) + tv_pop_sure = inflate.findViewById(R.id.tv_pop_sure) + ll_setting_mode = inflate.findViewById(R.id.ll_setting_mode) + tv_connect_status = inflate.findViewById(R.id.tv_connect_status) + //固定内容 + tv_new_name!!.text = "低功耗节点Low Power" + /*进入默认不能输入*/ + setEdit(false, tv_new_place!!) + setEdit(false, tv_new_use!!) + aboutList() + if (ToolUtil.isBind(activity!!, DataServer.mBindHistory, mMac)) { + var model = ToolUtil.getBindBlueToothData(activity!!, DataServer.mBindHistory, mMac) + tv_new_place!!.setText(model.name) + tv_new_use!!.setText(model.type) + } + isBind() + return inflate + } + + /** + * 是否绑定 + */ + fun isBind() { + if (ToolUtil.isBind(activity!!, DataServer.mBindHistory, mMac)) { + tv_pop_relievebind!!.visibility = View.VISIBLE + tv_pop_cancel!!.visibility = View.GONE + tv_pop_sure!!.text = "确定" + tv_pop_title!!.text = "已绑定设备" + } else { + tv_pop_relievebind!!.visibility = View.GONE + tv_pop_cancel!!.visibility = View.VISIBLE + tv_pop_sure!!.text = "立即绑定" + tv_pop_title!!.text = "发现新设备" + } + } + + /*设置是否可编辑*/ + fun setEdit(isCan: Boolean, edittext: EditText) { + edittext.isCursorVisible = isCan + edittext.isFocusable = isCan + edittext.setTextIsSelectable(isCan) + } + + /*点击事件处理*/ + override fun setUiBeforShow() { + //选择位置 + tv_new_place!!.setOnClickListener { + mChooseType = 1 + getTypeList() + showChooseList(true) + } + tv_new_place!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int + ) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + if (s!!.isNotEmpty()) { + InputLimitUtil.ShowEdittextStyle3( + tv_new_place!!, + li_new_place!!, + tv_place_worm!!, + "" + ) + } + } + + }) + //自定义 + tv_place_custom!!.setOnClickListener { + if (tv_place_custom!!.text.toString() == "自定义") { + tv_place_custom!!.text = "选择" + tv_new_place!!.hint = "请输入放置位置" + setEdit(true, tv_new_place!!) + } else { + tv_place_custom!!.text = "自定义" + setEdit(false, tv_new_place!!) + tv_new_place!!.hint = "请选择放置位置" + } + } + //选择用途 + tv_new_use!!.setOnClickListener { + mChooseType = 2 + showChooseList(true) + getLocationData(tv_new_place!!.text.toString()) + } + tv_new_use!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int + ) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + if (s!!.isNotEmpty()) { + InputLimitUtil.ShowEdittextStyle3( + tv_new_use!!, + li_new_use!!, + tv_use_worm!!, + "" + ) + } + } + + }) + //自定义 + tv_use_custom!!.setOnClickListener { + if (tv_use_custom!!.text.toString() == "自定义") { + tv_use_custom!!.text = "选择" + setEdit(true, tv_new_use!!) + tv_new_use!!.hint = "请输入设备用途" + } else { + tv_use_custom!!.text = "自定义" + tv_new_use!!.isEnabled = false + setEdit(false, tv_new_use!!) + tv_new_use!!.hint = "请选择设备用途" + } + } + //立即绑定+确定 + tv_pop_sure!!.setOnClickListener { + if (tv_pop_sure!!.text == "立即绑定") { + if (tv_new_place!!.text!!.isEmpty() || tv_new_use!!.text!!.isEmpty()) { + if (tv_new_place!!.text!!.isEmpty()) { + InputLimitUtil.ShowEdittextStyle3( + tv_new_place!!, + li_new_place!!, + tv_place_worm!!, + "请选择放置位置" + ) + } + if (tv_new_use!!.text!!.isEmpty()) { + InputLimitUtil.ShowEdittextStyle3( + tv_new_use!!, + li_new_use!!, + tv_use_worm!!, + "请选择设备用途" + ) + } + return@setOnClickListener + } + callBack!!.doWork(tv_new_place!!.text.toString(), tv_new_use!!.text.toString()) + }else if(tv_pop_sure!!.text == "返回"){ + callBack!!.doDismiss() + dismiss() + }else if(tv_pop_sure!!.text == "下一步"){ + callBack!!.doConnectTj() + } else {//确定 修改 + if (tv_new_place!!.text!!.isEmpty() || tv_new_use!!.text!!.isEmpty()) { + if (tv_new_place!!.text!!.isEmpty()) { + InputLimitUtil.ShowEdittextStyle3( + tv_new_place!!, + li_new_place!!, + tv_place_worm!!, + "请选择放置位置" + ) + } + if (tv_new_use!!.text!!.isEmpty()) { + InputLimitUtil.ShowEdittextStyle3( + tv_new_use!!, + li_new_use!!, + tv_use_worm!!, + "请选择设备用途" + ) + } + return@setOnClickListener + } + callBack!!.doChangeWork(tv_new_place!!.text.toString(), tv_new_use!!.text.toString()) +// dismiss() + } + } + li_close!!.setOnClickListener { + if (mChooseType != 0) { + showChooseList(false) + } else { + callBack!!.doDismiss() + dismiss() + } + } + //取消 + tv_pop_cancel!!.setOnClickListener { + if (mChooseType != 0) { + showChooseList(false) + } else { + callBack!!.doDismiss() + dismiss() + } + } + //解除绑定 + tv_pop_relievebind!!.setOnClickListener { + callBack!!.doUnBindWork() + } + //配置模式 + ll_setting_mode!!.setOnClickListener{ + callBack!!.doConnectTj() + } + } + + /*选择位置+用途*/ + fun aboutList() { + LoadUtils.setLinearLayoutManager(activity, recycle_options, false) + mAdapter = + SlimAdapter.create() + //位置 + .register( + R.layout.item_text, + object : SlimInjector { + override fun onInject( + data: CommonMLocation, + injector: IViewInjector> + ) { + injector.text(R.id.tv_text, data.name) + injector.with(R.id.tv_text) { + //选中变色效果暂未实现 + if (data.check == 1) { + it.setBackgroundColor(activity!!.resources.getColor(R.color.color_516AFC)) + it.setTextColor(activity!!.resources.getColor(R.color.white)) + } else { + it.setBackgroundColor(activity!!.resources.getColor(R.color.white)) + it.setTextColor(activity!!.resources.getColor(R.color.color_999999)) + } + } + injector.clicked(R.id.li_text) { + if (mChooseType == 1) { + tv_new_place!!.setText(data.name) +// tv_new_use!!.setText("") + } else if (mChooseType == 2) { + tv_new_use!!.setText(data.name) + } + showChooseList(false) + } + + } + }) + .attachTo(recycle_options) + + } + + //位置数据 + fun getTypeList() { + mData.clear() + DataServer.TJLocation.forEach { + var bean = CommonMLocation(it, 0) + mData.add(bean) + } + mAdapter!!.updateData(mData).notifyDataSetChanged() + } + + //用途数据 + fun getLocationData(type: String) { + mData.clear() + DataServer.TJUse.forEach { + var bean = CommonMLocation(it, 0) + mData.add(bean) + } + mAdapter!!.updateData(mData).notifyDataSetChanged() + } + + fun getData(list: Array) { + list.forEach { + var bean = CommonMLocation(it, 0) + mData.add(bean) + } + } + + /*显示界面按断*/ + fun showChooseList(isShow: Boolean) { + if (isShow) { + //按钮 + tv_pop_cancel!!.visibility = View.VISIBLE + tv_pop_sure!!.visibility = View.VISIBLE + tv_pop_sure!!.text = "确定" + tv_pop_relievebind!!.visibility = View.GONE + //布局样式 + recycle_options!!.visibility = View.VISIBLE + li_equipment!!.visibility = View.GONE + } else { + recycle_options!!.visibility = View.GONE + li_equipment!!.visibility = View.VISIBLE + //按钮--现在只考虑未绑定状态 + isBind() + /* tv_pop_cancel!!.visibility = View.GONE + tv_pop_sure!!.visibility = View.VISIBLE + tv_pop_sure!!.text = "立即绑定" + tv_pop_relievebind!!.visibility = View.GONE*/ + mChooseType = 0 + } + when (mChooseType) { + 0 -> { + tv_pop_title!!.text = "发现新设备" + } + 1 -> { + tv_pop_title!!.text = "选择位置" + } + 2 -> { + tv_pop_title!!.text = "选择用途" + } + } + } + } + + + companion object { + private var popupWindowPrivinceListUtils: FindNewEquipmentDialog? = null + + @get:Synchronized + val instance: FindNewEquipmentDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = FindNewEquipmentDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/HomeDeviceDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/HomeDeviceDialog.kt new file mode 100644 index 0000000..4fc841b --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/HomeDeviceDialog.kt @@ -0,0 +1,133 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.animation.SlideEnter.SlideTopEnter +import com.flyco.animation.SlideExit.SlideTopExit +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.activity.InfoActivity +import com.qidian.zhongkesmart.model.BindBlueToothData +import com.qidian.zhongkesmart.model.DeviceInfo +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.utils.ObjectBoxUtils +import com.qidian.zhongkesmart.utils.ToolUtil +import com.qidian.zhongkesmart.views.ClearEditText +import com.zhy.view.flowlayout.FlowLayout +import com.zhy.view.flowlayout.TagAdapter +import com.zhy.view.flowlayout.TagFlowLayout +import kotlinx.android.synthetic.main.layout_standby.* +import java.util.* + +/*取消绑定*/ +class HomeDeviceDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var flowlayoutAdapter: TagAdapter? = null + var mStansByList = ArrayList() + var id_flow_layout:TagFlowLayout? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.8f) + dialog!!.setCanceledOnTouchOutside(true) + dialog!!.show() + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var li_standby_background: LinearLayout? = null + + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_home_devices, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_standby_background = inflate.findViewById(R.id.li_standby_background) as LinearLayout + id_flow_layout = inflate.findViewById(R.id.id_flow_layout) as TagFlowLayout + return inflate + } + + override fun setUiBeforShow() { + showAnim(SlideTopEnter()) + dismissAnim(SlideTopExit()) + showStandByPage() + + id_flow_layout!!.setOnSelectListener(){ + callBack!!.doWork() + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: HomeDeviceDialog? = null + +// @get:Synchronized + val instance: HomeDeviceDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = HomeDeviceDialog() + } + return popupWindowPrivinceListUtils + } + } + + fun showStandByPage() { + mStansByList.clear() + mStansByList.addAll(ObjectBoxUtils.getAllData(DeviceInfo::class.java)!!) + flowlayoutAdapter = object : TagAdapter(mStansByList) { + override fun getView(parent: FlowLayout?, position: Int, s: DeviceInfo): View? { + val view: View = LayoutInflater.from(activity).inflate( + R.layout.item_standby, null + ) + var imv_stansby = view.findViewById(R.id.imv_stansby) + var tv_stansby = view.findViewById(R.id.tv_stansby) + var tv_mark = view.findViewById(R.id.tv_mark) + tv_stansby.text = s.type + imv_stansby!!.setImageResource(ToolUtil.getDJPageImage(s.type)) + if (s.isOnline) { + tv_mark.setBackgroundResource(R.drawable.ed_yuan0fba61) + } else { + tv_mark.setBackgroundResource(R.drawable.ed_yuangray) + } + return view + } + + override fun setSelected(position: Int, s: DeviceInfo): Boolean { + return s == s + } + } + id_flow_layout!!.adapter = flowlayoutAdapter + id_flow_layout!!.setOnTagClickListener { view, position, parent -> + true + } +// if (mStansByList.isEmpty()) { +// li_standby_background.visibility = View.GONE +// } else { +// li_standby_background.visibility = View.VISIBLE +// } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/InputNicknameDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/InputNicknameDialog.kt new file mode 100644 index 0000000..02df429 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/InputNicknameDialog.kt @@ -0,0 +1,121 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.utils.EventBusUtil +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.utils.WifiUtil +import com.qidian.zhongkesmart.views.ClearEditText +import kotlinx.android.synthetic.main.activity_wi_fi.* +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import pub.devrel.easypermissions.EasyPermissions +import java.util.* + +/*输入昵称*/ +class InputNicknameDialog { + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var tv_pop_sure: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + fun getShareDialog(context: Context?, ssid:String/*,callBack: PopupYearWindowCallBack?*/) { + activity = context + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.6f) + dialog!!.show() + dialog!!.setCanceledOnTouchOutside(false) +// EventBusUtil.register(activity) + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_input_nickname, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + ced_pop = inflate.findViewById(R.id.ced_pop) as ClearEditText + tv_woprn = inflate.findViewById(R.id.tv_woprn) as TextView + tv_pop_sure = inflate.findViewById(R.id.tv_pop_sure) as TextView + return inflate + } + + override fun setUiBeforShow() { + ced_pop!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int + ) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable?) { + if(s!!.isNotEmpty()){ + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "") + } + } + + }) + tv_pop_sure!!.setOnClickListener(View.OnClickListener { //请输入密码+Wi-Fi密码错误 + if (ced_pop!!.text.toString().isEmpty()) { + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "请输入密码") + return@OnClickListener + } +// WifiUtil.connectWiFi(activity!!,mSsid,ced_pop!!.text.toString()!!) + }) + li_close!!.setOnClickListener { dismiss() } + } + } + + companion object { + private var popupWindowPrivinceListUtils: InputNicknameDialog? = null + + @get:Synchronized + val instance: InputNicknameDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = InputNicknameDialog() + } + return popupWindowPrivinceListUtils + } + } + + /* @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventBusCome(event: EventBusUtil.MessageEvent) { + if (event != null) { + when(event.code){ + EventCode.WIFI_CONFIRM_CONNECT -> { + setDismiss() + } + EventCode.WIFI_CONFIRM_FAIL-> { + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "Wi-Fi密码错误") + } + } + + } + }*/ +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/InputThresholdDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/InputThresholdDialog.kt new file mode 100644 index 0000000..a9f0496 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/InputThresholdDialog.kt @@ -0,0 +1,115 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.utils.EventBusUtil +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.utils.WifiUtil +import com.qidian.zhongkesmart.views.ClearEditText +import kotlinx.android.synthetic.main.activity_wi_fi.* +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import pub.devrel.easypermissions.EasyPermissions +import java.util.* + +/*输入昵称*/ +class InputThresholdDialog { + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var tv_pop_sure: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: EditText? = null + private var callBack: PopupYearWindowCallBack? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.6f) + dialog!!.show() + dialog!!.setCanceledOnTouchOutside(false) + this.callBack = callBack +// EventBusUtil.register(activity) + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + interface PopupYearWindowCallBack { + fun doWork(roomName: String?) + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_input_threshold, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + ced_pop = inflate.findViewById(R.id.ced_pop) as EditText + tv_woprn = inflate.findViewById(R.id.tv_woprn) as TextView + tv_pop_sure = inflate.findViewById(R.id.tv_pop_sure) as TextView + return inflate + } + + override fun setUiBeforShow() { + ced_pop!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int + ) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable?) { + if(s!!.isNotEmpty()){ + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "请输入采样阈值,0-100") + } + } + + }) + tv_pop_sure!!.setOnClickListener(View.OnClickListener { //请输入密码+Wi-Fi密码错误 + if (ced_pop!!.text.toString().isEmpty()) { + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "请输入采样阈值,0-100") + }else{ + callBack!!.doWork(ced_pop!!.text.toString()) + dismiss() + } +// WifiUtil.connectWiFi(activity!!,mSsid,ced_pop!!.text.toString()!!) + }) + li_close!!.setOnClickListener { dismiss() } + } + } + + companion object { + private var popupWindowPrivinceListUtils: InputThresholdDialog? = null + + @get:Synchronized + val instance: InputThresholdDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = InputThresholdDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/ModeInitVerifyDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/ModeInitVerifyDialog.kt new file mode 100644 index 0000000..d3b2a86 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/ModeInitVerifyDialog.kt @@ -0,0 +1,112 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineScatterCandleRadarDataSet +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.ChartUtils +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import kotlinx.android.synthetic.main.layout_barchart.* +import kotlinx.android.synthetic.main.pop_model_init_verify.* +import kotlinx.coroutines.flow.callbackFlow +import java.util.* +import kotlin.collections.ArrayList + +/*取消绑定*/ +class ModeInitVerifyDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var values:ArrayList? = null + var xValues: ArrayList? = null +// var index = 0 + var line_Chart:LineChart? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?,lineData: ArrayList,xData:ArrayList) { + activity = context + this.callBack = callBack + values = lineData + xValues = xData + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.8f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + fun isShowing(): Boolean { + return dialog != null && dialog!!.isShowing + } + + fun updateData(xData:ArrayList,lineData: ArrayList){ + if(isShowing()) { + ChartUtils.initLineChart( + line_Chart!!, + xData!!, + lineData!!, + true + ) + } + } + + + interface PopupYearWindowCallBack { + fun doWork() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var li_close: LinearLayout? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_model_init_verify, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + line_Chart = inflate.findViewById(R.id.linechart) as LineChart + return inflate + } + + override fun setUiBeforShow() { + li_close!!.setOnClickListener { + callBack!!.doWork() + dismiss() + } + + ChartUtils.initLineChart( + line_Chart!!, + xValues!!, + values!!, + false + ) + } + } + + companion object { + private var popupWindowPrivinceListUtils: ModeInitVerifyDialog? = null + +// @get:Synchronized + val instance: ModeInitVerifyDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = ModeInitVerifyDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/OpenmBlueToothDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/OpenmBlueToothDialog.kt new file mode 100644 index 0000000..a1f022b --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/OpenmBlueToothDialog.kt @@ -0,0 +1,92 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*提示是否打开蓝牙*/ +class OpenmBlueToothDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + setShow() + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + fun setShow(){ + if (dialog != null && dialog!!.isShowing) { + return + } + dialog!!.show() + } + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_content: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_openwifi, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + tv_content = inflate.findViewById(R.id.tv_content) as TextView + pop_cancnel = inflate.findViewById(R.id.pop_cancnel) as TextView + pop_ok = inflate.findViewById(R.id.pop_ok) as TextView + tv_content!!.text="您的蓝牙暂未开启,是否现在开启?" + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: OpenmBlueToothDialog? = null + + @get:Synchronized + val instance: OpenmBlueToothDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = OpenmBlueToothDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/OpenmWifiDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/OpenmWifiDialog.kt new file mode 100644 index 0000000..19aab5d --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/OpenmWifiDialog.kt @@ -0,0 +1,91 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*提示是否打开wifi*/ +class OpenmWifiDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + fun setShow(){ + if (dialog != null && dialog!!.isShowing) { + return + } + dialog!!.show() + } + + interface PopupYearWindowCallBack { + fun doWork() + fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_content: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_openwifi, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + tv_content = inflate.findViewById(R.id.tv_content) as TextView + pop_cancnel = inflate.findViewById(R.id.pop_cancnel) as TextView + pop_ok = inflate.findViewById(R.id.pop_ok) as TextView + pop_cancnel!!.visibility=View.GONE + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: OpenmWifiDialog? = null + + @get:Synchronized + val instance: OpenmWifiDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = OpenmWifiDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/PopupWindowCommonCenterUtils.java b/app/src/main/java/com/qidian/zhongkesmart/dialog/PopupWindowCommonCenterUtils.java new file mode 100644 index 0000000..c8ebc99 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/PopupWindowCommonCenterUtils.java @@ -0,0 +1,113 @@ +package com.qidian.zhongkesmart.dialog; + +import android.content.Context; +import android.graphics.Color; +import android.view.View; +import android.widget.TextView; + +import com.flyco.dialog.utils.CornerUtils; +import com.flyco.dialog.widget.base.BaseDialog; +import com.qidian.zhongkesmart.R; + +import java.util.ArrayList; + + +@SuppressWarnings("unused") +public class PopupWindowCommonCenterUtils { + private PopupYearWindowCallBack callBack; + private Context activity; + private String mTitle; + private String mContent; + private String mSuretext; + private String mCancelText; + private static PopupWindowCommonCenterUtils popupWindowPrivinceListUtils; + ArrayList listname = new ArrayList<>(); + private int ChoosePosition; + private String mMobile; + private Boolean isNeedCancel; + private String mId; + + public static synchronized PopupWindowCommonCenterUtils getInstance() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = new PopupWindowCommonCenterUtils(); + } + return popupWindowPrivinceListUtils; + } + + CustomCommonDialog dialog; + + public void getShareDialog(Context context, String title, String content, String suretext, String cancel, String id, PopupYearWindowCallBack callBack) { + activity = context; + this.callBack = callBack; + this.mTitle = title; + this.mCancelText = cancel; + this.mContent = content; + this.mId = id; + this.mSuretext = suretext; + dialog = new CustomCommonDialog(activity); + dialog.widthScale(0.5f); + dialog.show(); + dialog.setCanceledOnTouchOutside(true); +// dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + public void setDismiss() { + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + } + + public interface PopupYearWindowCallBack { + public void doWork(); + } + + class CustomCommonDialog extends BaseDialog { + TextView pop_ok, tv_cancel, tv_content, tv_title, pop_cancnel; + View view_line; + + public CustomCommonDialog(Context context) { + super(context); + } + + @Override + public View onCreateView() { + View inflate = View.inflate(mContext, R.layout.pop_centerbutton, null); + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10))); + tv_title = (TextView) inflate.findViewById(R.id.tv_title); + tv_content = (TextView) inflate.findViewById(R.id.tv_content); + pop_ok = (TextView) inflate.findViewById(R.id.pop_ok); + pop_cancnel = (TextView) inflate.findViewById(R.id.pop_cancnel); + tv_cancel = (TextView) inflate.findViewById(R.id.pop_cancnel); + view_line = (View) inflate.findViewById(R.id.view_line); + tv_title.setText(mTitle); + tv_content.setText(mContent); + pop_ok.setText(mSuretext); + pop_cancnel.setText(mCancelText); + return inflate; + } + + @Override + public void setUiBeforShow() { + pop_ok.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + callBack.doWork(); + } + }); + tv_cancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + } + }); + } + } + + +} + + + + diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/SelectFrequencyDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/SelectFrequencyDialog.kt new file mode 100644 index 0000000..c2d5cfc --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/SelectFrequencyDialog.kt @@ -0,0 +1,190 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.baseble.ble.BluetoothDeviceManager +import com.qidian.baseble.utils.HexUtil +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.utils.ToastUtil +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*取消绑定*/ +class SelectFrequencyDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var defaultItem = 1 + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?,lastValue:Int) { + activity = context + this.callBack = callBack + defaultItem = lastValue + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork(item:Int) +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var li_close: LinearLayout? = null + var tv_item1:TextView? = null + var tv_item2:TextView? = null + var tv_item3:TextView? = null + var tv_item4:TextView? = null + var tv_item5:TextView? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_select_frequency, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + pop_cancnel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + pop_ok = inflate.findViewById(R.id.tv_pop_sure) as TextView + tv_item1 = inflate.findViewById(R.id.tv_item1) as TextView + tv_item2 = inflate.findViewById(R.id.tv_item2) as TextView + tv_item3 = inflate.findViewById(R.id.tv_item3) as TextView + tv_item4 = inflate.findViewById(R.id.tv_item4) as TextView + tv_item5 = inflate.findViewById(R.id.tv_item5) as TextView + + return inflate + } + + override fun setUiBeforShow() { + when (defaultItem) { + 1 -> { + tv_item1!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item1!!.setTextColor(context.getColor(R.color.white)) + } + 5 -> { + tv_item2!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item2!!.setTextColor(context.getColor(R.color.white)) + } + 10 -> { + tv_item3!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item3!!.setTextColor(context.getColor(R.color.white)) + } + 30 -> { + tv_item4!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item4!!.setTextColor(context.getColor(R.color.white)) + } + 60 -> { + tv_item5!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item5!!.setTextColor(context.getColor(R.color.white)) + } + } + + tv_item1!!.setOnClickListener(View.OnClickListener { + defaultItem = 1 + tv_item1!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item2!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item3!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item4!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item5!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item1!!.setTextColor(context.getColor(R.color.white)) + tv_item2!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item3!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item4!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item5!!.setTextColor(context.getColor(R.color.color_333333)) + }) + tv_item2!!.setOnClickListener(View.OnClickListener { + defaultItem = 5 + tv_item1!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item2!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item3!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item4!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item5!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item1!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item2!!.setTextColor(context.getColor(R.color.white)) + tv_item3!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item4!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item5!!.setTextColor(context.getColor(R.color.color_333333)) + }) + tv_item3!!.setOnClickListener(View.OnClickListener { + defaultItem = 10 + tv_item1!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item2!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item3!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item4!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item5!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item1!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item2!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item3!!.setTextColor(context.getColor(R.color.white)) + tv_item4!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item5!!.setTextColor(context.getColor(R.color.color_333333)) + }) + tv_item4!!.setOnClickListener(View.OnClickListener { + defaultItem = 30 + tv_item1!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item2!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item3!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item4!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item5!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item1!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item2!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item3!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item4!!.setTextColor(context.getColor(R.color.white)) + tv_item5!!.setTextColor(context.getColor(R.color.color_333333)) + }) + tv_item5!!.setOnClickListener(View.OnClickListener { + defaultItem = 60 + tv_item1!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item2!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item3!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item4!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_gray_bg) + tv_item5!!.background = context.getDrawable(R.drawable.shape_select_frequency_item_blue_bg) + tv_item1!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item2!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item3!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item4!!.setTextColor(context.getColor(R.color.color_333333)) + tv_item5!!.setTextColor(context.getColor(R.color.white)) + }) + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork(defaultItem) + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + li_close!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: SelectFrequencyDialog? = null + +// @get:Synchronized + val instance: SelectFrequencyDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = SelectFrequencyDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/SetLockDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/SetLockDialog.kt new file mode 100644 index 0000000..e0f6a26 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/SetLockDialog.kt @@ -0,0 +1,112 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.Gravity +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.flyco.dialog.widget.base.TopBaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.activity.SettingActivity +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.utils.EditUtil +import com.qidian.zhongkesmart.utils.EventBusUtil +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.utils.ToastUtil +import com.qidian.zhongkesmart.utils.WifiUtil +import com.qidian.zhongkesmart.views.ClearEditText +import com.qidian.zhongkesmart.views.VerifyCodeView +import com.tuo.customview.VerificationCodeView +import kotlinx.android.synthetic.main.activity_set_lock.* +import kotlinx.android.synthetic.main.activity_wi_fi.* +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import pub.devrel.easypermissions.EasyPermissions +import java.util.* + +/*输入密码*/ +class SetLockDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var li_close: LinearLayout? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.6f) + dialog!!.show() + dialog!!.setCanceledOnTouchOutside(false) + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + interface PopupYearWindowCallBack { + fun doWork() + } + + inner class CustomCommonDialog(context: Context?) : TopBaseDialog(context) { + var vcv: VerificationCodeView? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_set_lock, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + vcv = inflate.findViewById(R.id.vcv) as VerificationCodeView + return inflate + } + + override fun setUiBeforShow() { + vcv!!.setInputCompleteListener(object : VerificationCodeView.InputCompleteListener { + override fun inputComplete() { + if(vcv!!.inputContent.length==4){ + if(vcv!!.inputContent == "0000"){ + dismiss() + //隐藏键盘 + EditUtil.hideInput(activity) + callBack!!.doWork() + }else{ + ToastUtil.showToast("密码输入错误") + } + } + } + + override fun deleteContent() { + } + + }) + li_close!!.setOnClickListener { + //隐藏键盘 + EditUtil.hideInput(activity) + dismiss() + } + + vcv!!.requestFocus() + } + } + + companion object { + private var popupWindowPrivinceListUtils: SetLockDialog? = null + + @get:Synchronized + val instance: SetLockDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = SetLockDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/SetThresholdDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/SetThresholdDialog.kt new file mode 100644 index 0000000..f691360 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/SetThresholdDialog.kt @@ -0,0 +1,120 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.SeekBar +import android.widget.TextView +import com.flyco.animation.BaseAnimatorSet +import com.flyco.animation.FlipEnter.FlipBottomEnter +import com.flyco.animation.SlideEnter.SlideTopEnter +import com.flyco.animation.SlideExit.SlideBottomExit +import com.flyco.animation.SlideExit.SlideTopExit +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*取消绑定*/ +class SetThresholdDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var mType = "打开" + var mValue = 50 + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?,type:String?,lastValue:Int) { + activity = context + this.callBack = callBack + mType = type!! + mValue = lastValue + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doWork(value:Int) +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tvTitle: TextView? = null + var tvDes: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var li_close: LinearLayout? = null + var seekBar:SeekBar? = null + var tvProgress:TextView? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_set_threshold, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + pop_cancnel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + pop_ok = inflate.findViewById(R.id.tv_pop_sure) as TextView + tvTitle = inflate.findViewById(R.id.tv_title) as TextView + tvDes = inflate.findViewById(R.id.tv_des) as TextView + tvProgress = inflate.findViewById(R.id.tv_progress) as TextView + seekBar = inflate.findViewById(R.id.sb_distance) as SeekBar + return inflate + } + + override fun setUiBeforShow() { + tvTitle!!.text = "模型“${mType}”模式阈值设置" + tvDes!!.text = "设置模型“${mType}”模式的阈值大小" + tvProgress!!.text = mValue.toString() + seekBar!!.progress = mValue + seekBar!!.setOnSeekBarChangeListener(object :SeekBar.OnSeekBarChangeListener{ + override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) { + mValue = p1 + tvProgress!!.text = p1.toString() + } + + override fun onStartTrackingTouch(p0: SeekBar?) { + } + + override fun onStopTrackingTouch(p0: SeekBar?) { + } + + }) + pop_ok!!.setOnClickListener(View.OnClickListener { + callBack!!.doWork(mValue) + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + li_close!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: SetThresholdDialog? = null + +// @get:Synchronized + val instance: SetThresholdDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = SetThresholdDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/WakeUpDeviceDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/WakeUpDeviceDialog.kt new file mode 100644 index 0000000..500e192 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/WakeUpDeviceDialog.kt @@ -0,0 +1,78 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*取消绑定*/ +class WakeUpDeviceDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.show() + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun isShowing(): Boolean { + return dialog != null && dialog!!.isShowing + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + + interface PopupYearWindowCallBack { + fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var pop_cancnel: TextView? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_wake_up_device, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + pop_cancnel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + return inflate + } + + override fun setUiBeforShow() { + pop_cancnel!!.setOnClickListener { + callBack!!.doCancel() + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: WakeUpDeviceDialog? = null + +// @get:Synchronized + val instance: WakeUpDeviceDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = WakeUpDeviceDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/WifiDisconnectDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/WifiDisconnectDialog.kt new file mode 100644 index 0000000..7f0ca8e --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/WifiDisconnectDialog.kt @@ -0,0 +1,96 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.content.DialogInterface +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.views.ClearEditText +import java.util.* + +/*WIFI断开连接*/ +class WifiDisconnectDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + fun getShareDialog(context: Context?, callBack: PopupYearWindowCallBack?) { + activity = context + this.callBack = callBack + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.5f) + dialog!!.setCanceledOnTouchOutside(false) + dialog!!.setOnDismissListener { + DataServer.wifiDialogIsShow = false + } + setShow() + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + fun setShow(){ + if (dialog != null && dialog!!.isShowing) { + return + } + DataServer.wifiDialogIsShow = true + dialog!!.show() + } + + interface PopupYearWindowCallBack { + fun doWork() +// fun doCancel() + } + + inner class CustomCommonDialog(context: Context?) : BaseDialog(context) { + var tv_content: TextView? = null + var pop_ok: TextView? = null + var pop_cancnel: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_wifi_disconnect, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + pop_cancnel = inflate.findViewById(R.id.pop_cancel) as TextView + pop_ok = inflate.findViewById(R.id.pop_sure) as TextView + return inflate + } + + override fun setUiBeforShow() { + pop_ok!!.setOnClickListener(View.OnClickListener { + DataServer.wifiDialogIsShow = false + callBack!!.doWork() + dismiss() + }) + pop_cancnel!!.setOnClickListener { + dismiss() + } + } + } + + companion object { + private var popupWindowPrivinceListUtils: WifiDisconnectDialog? = null + + @get:Synchronized + val instance: WifiDisconnectDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = WifiDisconnectDialog() + } + return popupWindowPrivinceListUtils + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/dialog/WifiPswDialog.kt b/app/src/main/java/com/qidian/zhongkesmart/dialog/WifiPswDialog.kt new file mode 100644 index 0000000..abe82bb --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/dialog/WifiPswDialog.kt @@ -0,0 +1,141 @@ +package com.qidian.zhongkesmart.dialog + +import android.content.Context +import android.graphics.Color +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.Gravity +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.flyco.dialog.utils.CornerUtils +import com.flyco.dialog.widget.base.BaseDialog +import com.flyco.dialog.widget.base.TopBaseDialog +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.utils.EventBusUtil +import com.qidian.zhongkesmart.utils.InputLimitUtil.ShowEdittextStyle +import com.qidian.zhongkesmart.utils.WifiUtil +import com.qidian.zhongkesmart.views.ClearEditText +import kotlinx.android.synthetic.main.activity_wi_fi.* +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import pub.devrel.easypermissions.EasyPermissions +import java.util.* + +/*wifi输入密码*/ +class WifiPswDialog { + private var callBack: PopupYearWindowCallBack? = null + private var activity: Context? = null + var dialog: CustomCommonDialog? = null + var mSsid="" + var tv_pop_cancel: TextView? = null + var tv_pop_sure: TextView? = null + var tv_woprn: TextView? = null + var li_close: LinearLayout? = null + var ced_pop: ClearEditText? = null + fun getShareDialog(context: Context?, ssid:String/*,callBack: PopupYearWindowCallBack?*/) { + activity = context +// this.callBack = callBack + this.mSsid = ssid + dialog = CustomCommonDialog(activity) + dialog!!.widthScale(0.6f) + dialog!!.show() + dialog!!.setCanceledOnTouchOutside(false) +// EventBusUtil.register(activity) + // dialog.getWindow().setBackgroundDrawable( CornerUtils.cornerDrawable(Color.parseColor("#00000000"), dp2px(0))); + } + + fun setDismiss() { + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + } + + fun showFail(){ + if (dialog != null && dialog!!.isShowing) { + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "Wi-Fi密码错误") + } + } + + interface PopupYearWindowCallBack { + fun doWork(psw: String?) + } + + inner class CustomCommonDialog(context: Context?) : TopBaseDialog(context) { + + override fun onCreateView(): View { + val inflate = View.inflate(mContext, R.layout.pop_wifipsw, null) + inflate.setBackgroundDrawable( + CornerUtils.cornerDrawable(Color.parseColor("#ffffff"), dp2px(10f).toFloat()) + ) + li_close = inflate.findViewById(R.id.li_close) as LinearLayout + ced_pop = inflate.findViewById(R.id.ced_pop) as ClearEditText + tv_woprn = inflate.findViewById(R.id.tv_woprn) as TextView + tv_pop_cancel = inflate.findViewById(R.id.tv_pop_cancel) as TextView + tv_pop_sure = inflate.findViewById(R.id.tv_pop_sure) as TextView + return inflate + } + + override fun setUiBeforShow() { + ced_pop!!.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int + ) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable?) { + if(s!!.isNotEmpty()){ + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "") + } + } + + }) + tv_pop_sure!!.setOnClickListener(View.OnClickListener { //请输入密码+Wi-Fi密码错误 + if (ced_pop!!.text.toString().isEmpty()) { + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "请输入密码") + return@OnClickListener + } + WifiUtil.connectWiFi(activity!!,mSsid,ced_pop!!.text.toString()!!) + }) + tv_pop_cancel!!.setOnClickListener { dismiss() } + li_close!!.setOnClickListener { dismiss() } + ced_pop!!.requestFocus() + } + } + + companion object { + private var popupWindowPrivinceListUtils: WifiPswDialog? = null + + @get:Synchronized + val instance: WifiPswDialog? + get() { + if (popupWindowPrivinceListUtils == null) { + popupWindowPrivinceListUtils = WifiPswDialog() + } + return popupWindowPrivinceListUtils + } + } + + /* @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventBusCome(event: EventBusUtil.MessageEvent) { + if (event != null) { + when(event.code){ + EventCode.WIFI_CONFIRM_CONNECT -> { + setDismiss() + } + EventCode.WIFI_CONFIRM_FAIL-> { + ShowEdittextStyle(ced_pop!!, tv_woprn!!, "Wi-Fi密码错误") + } + } + + } + }*/ +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/fragments/BaseFragment.kt b/app/src/main/java/com/qidian/zhongkesmart/fragments/BaseFragment.kt new file mode 100644 index 0000000..f3bc02f --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/fragments/BaseFragment.kt @@ -0,0 +1,64 @@ +package com.qidian.zhongkesmart.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.qidian.zhongkesmart.utils.EventBusUtil +import net.idik.lib.slimadapter.SlimAdapter +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +abstract class BaseFragment : Fragment() { + var baseView:View? = null + var mAdapter: SlimAdapter? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + EventBusUtil.register(this) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + if(baseView == null) { + baseView = inflater.inflate(getLayoutId(), container, false) + }else{ + if (baseView!!.getParent() != null) { + (baseView!!.getParent() as ViewGroup).removeView(baseView) + } + } + return baseView + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initView(view) + initData() + } + + abstract fun getLayoutId(): Int + + abstract fun initView(view: View?) + + abstract fun initData() + + + override fun onDestroy() { + super.onDestroy() + EventBusUtil.unregister(this) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventBusCome(event: EventBusUtil.MessageEvent) { + if (event != null) { + receiveEvent(event) + } + } + + /** + * 接收到分发到事件 * * @param event 事件 + */ + open fun receiveEvent(event: EventBusUtil.MessageEvent) {} +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/fragments/SystemFragment.kt b/app/src/main/java/com/qidian/zhongkesmart/fragments/SystemFragment.kt new file mode 100644 index 0000000..5887ffd --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/fragments/SystemFragment.kt @@ -0,0 +1,27 @@ +package com.qidian.zhongkesmart.fragments + +import android.os.Bundle +import android.view.View +import com.qidian.zhongkesmart.R + +class SystemFragment : BaseFragment() { + companion object { + fun newInstance(): SystemFragment { + val args = Bundle() + val fragment = SystemFragment() + fragment.arguments = args + return fragment + } + } + + override fun getLayoutId(): Int { + return R.layout.fragment_tiejian + } + + override fun initView(view: View?) { + } + + override fun initData() { + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/fragments/TieJianFragment.kt b/app/src/main/java/com/qidian/zhongkesmart/fragments/TieJianFragment.kt new file mode 100644 index 0000000..7c28b2d --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/fragments/TieJianFragment.kt @@ -0,0 +1,250 @@ +package com.qidian.zhongkesmart.fragments + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.View +import android.widget.TextView +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.activity.SetSeconedActivity +import com.qidian.zhongkesmart.model.BindBlueToothData +import com.qidian.zhongkesmart.utils.ActivityManager +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.LoadUtils +import com.qidian.zhongkesmart.utils.ToolUtil +import kotlinx.android.synthetic.main.base_title_layout.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.layout_list.* +import kotlinx.android.synthetic.main.layout_tiejianmess.* +import kotlinx.android.synthetic.main.layout_tiepain_details.* +import kotlinx.android.synthetic.main.layout_tiepain_details2.* +import net.idik.lib.slimadapter.SlimAdapter +import net.idik.lib.slimadapter.SlimInjector +import net.idik.lib.slimadapter.viewinjector.IViewInjector + +class TieJianFragment : BaseFragment(), View.OnClickListener { + var mData = ArrayList() + var mTJSetMac = "" + var mTJSetRoomName = "" + var mTJSetType = "" + var mAdapterChange: SlimAdapter? = null + var mData_Change = ArrayList() + var showPage = 1//显示页面标记 1 贴件列表 2贴件详情 3 修改 + + companion object { + fun newInstance(): TieJianFragment { + val args = Bundle() + val fragment = TieJianFragment() + fragment.arguments = args + return fragment + } + } + + override fun getLayoutId(): Int { + return R.layout.fragment_tiejian + } + + override fun initView(view: View?) { + initListener() + refreshLayout_list.setEnableLoadMore(false) + refreshLayout_list.setEnableRefresh(false) + LoadUtils.setLinearLayoutManager(context, recycle_list, false) + mAdapter = + SlimAdapter.create() + .register( + R.layout.item_tiejian, + object : SlimInjector { + override fun onInject( + data: BindBlueToothData, + injector: IViewInjector> + ) { + injector.text(R.id.tv_set_tjname, data.type) + injector.text(R.id.tv_set_tjlocation, data.name) + //固定信息 + injector.text(R.id.tv_set_tjtype, "代理") + injector.with(R.id.tv_mark) { + //是否在线 + if (ToolUtil.getTJIsOnLineState(data.mac)) { + injector.text(R.id.tv_set_tjstatus, "正常") + it.setBackgroundResource(R.drawable.ed_yuan0fba61) + } else { + injector.text(R.id.tv_set_tjstatus, "离线") + it.setBackgroundResource(R.drawable.ed_yuangray) + } + } + injector.text(R.id.tv_set_power, "电量:${data.power}%") + injector.clicked(R.id.li_tp) { + mTJSetMac = data.mac + mTJSetRoomName = data.name + mTJSetType = data.type + //贴件设置 -4 贴件详情 + showTJDetailsPage() + } + + } + }) + .attachTo(recycle_list) + + } + + override fun initData() { + getTieJianlistData() + } + + fun initListener() { + li_tpd_location.setOnClickListener(this) + li_tpd_use.setOnClickListener(this) + } + + @SuppressLint("UseRequireInsteadOfGet") + fun getTieJianlistData() { + if (ToolUtil.getHistory(DataServer.mBindHistory, context!!).isNullOrEmpty()) { + recycle_list.visibility = View.GONE + layEmpty.visibility = View.VISIBLE + tv_button.visibility = View.GONE + tv_emptytext.text = "您当前还有添加任何设备" + } else { + mData.clear() + mData.addAll(ToolUtil.getHistory(DataServer.mBindHistory, context!!)) + mAdapter!!.updateData(mData).notifyDataSetChanged() + recycle_list.visibility = View.VISIBLE + layEmpty.visibility = View.GONE + } + } + + + /** + * 贴片详情 + */ + fun showTiePianDetails(mac: String) { + var model = ToolUtil.getBindBlueToothData(requireContext(), DataServer.mBindHistory, mac) + tv_tpde_location.text = "位置:${model.name}" + tv_tpd_use.text = "用途:${model.type}" + //固定信息 + tv_sys_jiedian.text = "节点:Low power" + tv_sys_mac.text = "MAC:${model.mac}" + //版本+频率 无法获取 待定 + tv_sys_addtime.text = "添加时间:${model.time}" + } + + override fun onClick(p0: View?) { + when (p0!!.id) { + //修改位置 + R.id.li_tpd_location -> { + showChangeTJMess("1") + } + //修改用途 + R.id.li_tpd_use -> { + showChangeTJMess("2") + } + /* //返回 + R.id.rl_back -> { + when(showPage){ + 1->{ + ActivityManager.getAppManager().finishActivity(SetSeconedActivity::class.java) + } + 2->{ + showTJListPage() + } + 3->{ + showTJDetails() + } + } + }*/ + } + } + + /** + * 显示修改页面 + */ + + // 贴件设置 -5 修改位置 贴件设置 -6 修改用途 + fun showChangeTJMess(type: String) { + showChangePage() + if (type == "1") { + tv_change_title.text = "修改位置" + ced_changedetails.hint = "请选择或输入设备位置" + ced_changedetails.isEnabled = true + } else { + tv_change_title.text = "修改用途" + ced_changedetails.hint = "请选择设备用途" + ced_changedetails.isEnabled = false + } + aboutChangeTJMess(type) + } + //显示贴件列表 + fun showTJListPage(){ + DataServer.SetPageTag="1-1" + setGone() + li_tiejian_details.visibility = View.VISIBLE + showTiePianDetails(mTJSetMac) + } + + //显示贴片详情 贴件设置 贴件详情 + fun showTJDetailsPage() { + DataServer.SetPageTag="1-2" + setGone() + li_tiejian_details.visibility = View.VISIBLE + showTiePianDetails(mTJSetMac) + } + + // 显示贴件修改页面 + fun showChangePage() { + DataServer.SetPageTag="1-3" + setGone() + li_tiejian_changedetails.visibility = View.VISIBLE + } + + fun setGone() { + li_tiejianmess.visibility = View.GONE + li_tiejian_details.visibility = View.GONE + li_tiejian_changedetails.visibility = View.GONE + } + + /** + * 贴件修改 + */ + fun aboutChangeTJMess(type: String) { + LoadUtils.setLinearLayoutManager(context, recycle_change, false) + mAdapterChange = + SlimAdapter.create() + .register(R.layout.item_tj_changetext, object : SlimInjector { + override fun onInject( + data: String, + injector: IViewInjector> + ) { + injector.text(R.id.tv_tjtext, data) + injector.clicked(R.id.li_tjtext) { + ced_changedetails.setText(data) + if (type == "1") {//位置 + mTJSetRoomName = data + tv_tpde_location.text = "位置:${data}" + } else {//用途 + mTJSetType = data + tv_tpd_use.text = "用途:${data}" + } + showTJDetailsPage() + } + + } + }) + .attachTo(recycle_change) + getTjChangeData() + } + + fun getTjChangeData() { + mData_Change.clear() + if (tv_change_title.text == "修改位置") { + getData(DataServer.TJLocation) + } else { + getData(DataServer.TJUse) + } + mAdapterChange!!.updateData(mData_Change).notifyDataSetChanged() + } + + fun getData(list: Array) { + list.forEach { + mData_Change.add(it) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/fragments/WangGuanFragment.kt b/app/src/main/java/com/qidian/zhongkesmart/fragments/WangGuanFragment.kt new file mode 100644 index 0000000..0a02b5c --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/fragments/WangGuanFragment.kt @@ -0,0 +1,27 @@ +package com.qidian.zhongkesmart.fragments + +import android.os.Bundle +import android.view.View +import com.qidian.zhongkesmart.R + +class WangGuanFragment : BaseFragment() { + companion object { + fun newInstance(): WangGuanFragment { + val args = Bundle() + val fragment = WangGuanFragment() + fragment.arguments = args + return fragment + } + } + + override fun getLayoutId(): Int { + return R.layout.fragment_wangguan + } + + override fun initView(view: View?) { + } + + override fun initData() { + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/manager/BasicSettingManager.java b/app/src/main/java/com/qidian/zhongkesmart/manager/BasicSettingManager.java new file mode 100644 index 0000000..54812a0 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/manager/BasicSettingManager.java @@ -0,0 +1,22 @@ +package com.qidian.zhongkesmart.manager; + +import android.text.TextUtils; + + +import com.qidian.zhongkesmart.utils.DataServer; + +public class BasicSettingManager { + private static BasicSettingManager instance; + + public static BasicSettingManager getInstance() { + if (instance == null) { + instance = new BasicSettingManager(); + } + return instance; + } + + + + + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/ActionInfo.kt b/app/src/main/java/com/qidian/zhongkesmart/model/ActionInfo.kt new file mode 100644 index 0000000..943130a --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/ActionInfo.kt @@ -0,0 +1,16 @@ +package com.qidian.zhongkesmart.model + +import io.objectbox.annotation.Entity +import io.objectbox.annotation.Id + +@Entity +class ActionInfo { + @Id + var id: Long = 0 + var mac: String = "" + var data: ByteArray? = null + var time: String = "" + override fun toString(): String { + return "ActionInfo(id=$id, mac='$mac', data=${data?.contentToString()}, time='$time')" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/BlueToothData.kt b/app/src/main/java/com/qidian/zhongkesmart/model/BlueToothData.kt new file mode 100644 index 0000000..f41b8ed --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/BlueToothData.kt @@ -0,0 +1,108 @@ +package com.qidian.zhongkesmart.model + +import com.alibaba.fastjson.annotation.JSONField + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2022/7/19 0019 10:47 + */ +//接收蓝牙信息 +data class BlueToothDataM ( + var list:ArrayList, +) +data class BlueToothData ( + var mac: String, + var power: String, + var onLine:Boolean,//是否在线 + var type: String,//类型:震动、位移 + var open: Boolean,//记录开关状态 + var showAlarm: Boolean,//状态未改变的情况下 是否提示过报警弹窗 + var lateDataOpenTime:Long,//震动型-每次接收数据的时间点记录 用于判断计算关闭 + var tomeRecord: ArrayList, +) +data class TimeData( + var openTime:Long,//开启或者关闭的时间点 + var timelength: Long,//关闭的时候记录该次使用时长 +) +data class TimeDataWithTime( + var openTime:Long,//开启的时间点 + var timelength: Long,//关闭的时候记录该次使用时长 + var timeLocation: String,//所处时间段 + var data:ByteArray +) +//处理到最后的柱状图数据 +data class LineViewData( + var mSize:Int,//同时段的个数 + var timeLocation: String//所处时间段 +) + +data class LineViewData1( + var mSizeO:Int,//同时段的打开个数 + var mSizeC:Int,//同时段的关闭个数 + var timeLocation: String//所处时间段 +) + +//已绑定贴片信息 +data class BindBlueToothM ( + var list:ArrayList, +) +data class BindBlueToothData( + var mac: String, + var power: String, + var name: String, + var type: String, + var time: String + //mtjType 震动型、位移型 +// var mtjType:String +) + +/** + * 接口提交数据样式 + */ +//同步接口提交的数据样式 +data class PushData ( + var deviceMac: String, + var identifyType: String, + var deviceType: String, + var roomName: String, + var devicePower: String, + var deviceOnLine: String, + var deviceStatus: String, + var sendDate: String, + var timeLength: String, + var warnType: String +) +//绑定新设备 +data class BindData ( + var gatewayId: String, + var deviceMac: String, + var roomName: String, + var typeCode: String +) +//离线 +data class OfflineData ( + var deviceMac: String, + var identifyType: String, + var deviceType: String, + var roomName: String, + var devicePower: String, + var deviceOnLine: String, + var sendDate: String, + var warnType: String, +) +//报警 +data class AlarmData ( + var deviceMac: String, + var identifyType: String, + var deviceType: String, + var roomName: String, + var devicePower: String, + var deviceOnLine: String, + var deviceStatus: String, + var sendDate: String, + var timeLength: String, + var warnType: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/BlueToothM.kt b/app/src/main/java/com/qidian/zhongkesmart/model/BlueToothM.kt new file mode 100644 index 0000000..2de2287 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/BlueToothM.kt @@ -0,0 +1,20 @@ +package com.qidian.zhongkesmart.model + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * getName() 获取蓝牙名称 +getType() 获取蓝牙类型 +getBondState() 获取蓝牙的绑定状态 +getAddress() 获取蓝牙设备的硬件地址 +getUuids() 返回远程设备支持的功能 (UUID) + * @Date : 2021/10/21 0021 16:46 + */ +data class BlueToothM( + var name: String, + var address: String +) + + diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/CallServiceManager.java b/app/src/main/java/com/qidian/zhongkesmart/model/CallServiceManager.java new file mode 100644 index 0000000..cfb9d75 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/CallServiceManager.java @@ -0,0 +1,85 @@ +package com.qidian.zhongkesmart.model; + + +import com.netease.yunxin.kit.alog.ALog; +import com.qidian.zhongkesmart.model.base.BaseService; + +import org.json.JSONException; +import org.json.JSONObject; + +import okhttp3.MediaType; +import okhttp3.RequestBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.http.Body; +import retrofit2.http.POST; + +public class CallServiceManager { + + private static final String LOG_TAG = "CallServiceManager"; + + private final CallApi mApi; + + private Call> searchUserCall; + + private CallServiceManager() { + mApi = BaseService.getInstance().getRetrofit().create(CallApi.class); + } + + public static CallServiceManager getInstance() { + return new CallServiceManager(); + } + + + /** + * 网络访问接口 + */ + private interface CallApi { + @POST("/p2pVideoCall/caller/searchSubscriber") + Call> searchUserWithPhoneNumber(@Body RequestBody body); + } + + /** + * 根据手机号码精确搜索用户 + * + * @param phoneNumber + * @param callBack + */ + public void searchUserWithPhoneNumber(String phoneNumber, final BaseService.ResponseCallBack callBack) { + if (searchUserCall != null && searchUserCall.isExecuted()) { + ALog.e(LOG_TAG, "searchUserCall is executed"); + searchUserCall.cancel(); + } + JSONObject result = new JSONObject(); + try { + result.put("mobile", phoneNumber); + } catch (JSONException e) { + e.printStackTrace(); + } + RequestBody body = RequestBody.create(MediaType.parse("application/json"), result.toString()); + searchUserCall = mApi.searchUserWithPhoneNumber(body); + searchUserCall.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (callBack != null) { + BaseService.ResponseEntity responseEntity = response.body(); + if (responseEntity.code == 200) { + callBack.onSuccess(responseEntity.data); + } else { + callBack.onFail(responseEntity.code); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + if (callBack != null) { + callBack.onFail(BaseService.ERROR_CODE_UNKNOWN); + } + } + }); + + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/CommonM.kt b/app/src/main/java/com/qidian/zhongkesmart/model/CommonM.kt new file mode 100644 index 0000000..693fc5c --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/CommonM.kt @@ -0,0 +1,24 @@ +package com.qidian.zhongkesmart.model + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2021/10/21 0021 16:46 + */ +data class CommonMLocation( + var name: String, + var check: Int, +) +data class CommonMUse( + var name: String, + var check: Int, +) +data class CommonM( + var code: String, + var message: String, + var success: Boolean +) + + diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/DeviceInfo.kt b/app/src/main/java/com/qidian/zhongkesmart/model/DeviceInfo.kt new file mode 100644 index 0000000..6f91e3b --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/DeviceInfo.kt @@ -0,0 +1,30 @@ +package com.qidian.zhongkesmart.model + +import io.objectbox.annotation.Entity +import io.objectbox.annotation.Id + +@Entity +class DeviceInfo { + @Id + var id: Long = 0 + var mac: String = "" + var isBind:Boolean = false + var name: String = "" + var type: String = "" + var time: String = "" + var power: String = "" + var hVersion:String = "" + var fVersion:String = "" + var frequency:Int = 0 + var model1Init:Boolean = false + var model1Time:String = "" + var model1Threshold:Int = 0 + var model2Init:Boolean = false + var model2Time:String = "" + var model2Threshold:Int = 0 + var isOnline:Boolean = false + var updateTime:String = "" + override fun toString(): String { + return "DeviceInfo(id=$id, mac='$mac', isBind=$isBind, name='$name', type='$type', time='$time', power='$power', hVersion='$hVersion', fVersion='$fVersion', frequency=$frequency, model1Init=$model1Init, model1Time='$model1Time', model1Threshold=$model1Threshold, model2Init=$model2Init, model2Time='$model2Time', model2Threshold=$model2Threshold, isOnline=$isOnline, updateTime='$updateTime')" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/DownloadIdStatus.java b/app/src/main/java/com/qidian/zhongkesmart/model/DownloadIdStatus.java new file mode 100644 index 0000000..d07d507 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/DownloadIdStatus.java @@ -0,0 +1,37 @@ +package com.qidian.zhongkesmart.model; + +public class DownloadIdStatus { + private long appDownloadId; + private long firmwareDDownloadId; + private long firmwareLDownloadId; + + public DownloadIdStatus(long appDownloadId, long firmwareDDownloadId, long firmwareLDownloadId) { + this.appDownloadId = appDownloadId; + this.firmwareDDownloadId = firmwareDDownloadId; + this.firmwareLDownloadId = firmwareLDownloadId; + } + + public long getAppDownloadId() { + return appDownloadId; + } + + public void setAppDownloadId(long appDownloadId) { + this.appDownloadId = appDownloadId; + } + + public long getFirmwareDDownloadId() { + return firmwareDDownloadId; + } + + public void setFirmwareDDownloadId(long firmwareDDownloadId) { + this.firmwareDDownloadId = firmwareDDownloadId; + } + + public long getFirmwareLDownloadId() { + return firmwareLDownloadId; + } + + public void setFirmwareLDownloadId(long firmwareLDownloadId) { + this.firmwareLDownloadId = firmwareLDownloadId; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/DownloadProgress.java b/app/src/main/java/com/qidian/zhongkesmart/model/DownloadProgress.java new file mode 100644 index 0000000..de8b146 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/DownloadProgress.java @@ -0,0 +1,35 @@ +package com.qidian.zhongkesmart.model; + +public class DownloadProgress { + private int downloadType; + private int progress; + + public DownloadProgress(int downloadType, int progress) { + this.downloadType = downloadType; + this.progress = progress; + } + + public int getDownloadType() { + return downloadType; + } + + public void setDownloadType(int downloadType) { + this.downloadType = downloadType; + } + + public int getProgress() { + return progress; + } + + public void setProgress(int progress) { + this.progress = progress; + } + + @Override + public String toString() { + return "DownloadProgress{" + + "downloadType=" + downloadType + + ", progress=" + progress + + '}'; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/FamilyInfo.kt b/app/src/main/java/com/qidian/zhongkesmart/model/FamilyInfo.kt new file mode 100644 index 0000000..a23c717 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/FamilyInfo.kt @@ -0,0 +1,5 @@ +package com.qidian.zhongkesmart.model + +data class FamilyInfo ( + val familyName: String +) \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/GetCodeM.kt b/app/src/main/java/com/qidian/zhongkesmart/model/GetCodeM.kt new file mode 100644 index 0000000..857e486 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/GetCodeM.kt @@ -0,0 +1,14 @@ +package com.qidian.zhongkesmart.model +/*{ + "code":"0", + "data":"2BRWDQ2", + "message":"成功", + "success":true + }*/ + +data class GetCodeM( + var code: String, + var data: String, + var message: String, + var success: String +) \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/GetLoginM.kt b/app/src/main/java/com/qidian/zhongkesmart/model/GetLoginM.kt new file mode 100644 index 0000000..b21f3ba --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/GetLoginM.kt @@ -0,0 +1,16 @@ +package com.qidian.zhongkesmart.model + + +data class GetLoginM( + val code: String, + val `data`: Data, + val message: String, + val success: Boolean +) + +data class Data( + val accountId: String, + val gatewayId: String, + val loginTime: String, + val token: String +) \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/GetTokenM.kt b/app/src/main/java/com/qidian/zhongkesmart/model/GetTokenM.kt new file mode 100644 index 0000000..3c0da31 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/GetTokenM.kt @@ -0,0 +1,20 @@ +package com.qidian.zhongkesmart.model + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * getName() 获取蓝牙名称 +getType() 获取蓝牙类型 +getBondState() 获取蓝牙的绑定状态 +getAddress() 获取蓝牙设备的硬件地址 +getUuids() 返回远程设备支持的功能 (UUID) + * @Date : 2021/10/21 0021 16:46 + */ +data class GetTokenM( + val accountId: String, + val gatewayId: String, + val loginTime: String, + val token: String +) \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/LocalFirmwareVersion.java b/app/src/main/java/com/qidian/zhongkesmart/model/LocalFirmwareVersion.java new file mode 100644 index 0000000..fb8e846 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/LocalFirmwareVersion.java @@ -0,0 +1,37 @@ +package com.qidian.zhongkesmart.model; + +public class LocalFirmwareVersion { + private String appVersion; + private long firmwareDVersion; + private long firmwareLVersion; + + public LocalFirmwareVersion(String appVersion, long firmwareDVersion, long firmwareLVersion) { + this.appVersion = appVersion; + this.firmwareDVersion = firmwareDVersion; + this.firmwareLVersion = firmwareLVersion; + } + + public String getAppVersion() { + return appVersion; + } + + public void setAppVersion(String appVersion) { + this.appVersion = appVersion; + } + + public long getFirmwareDVersion() { + return firmwareDVersion; + } + + public void setFirmwareDVersion(long firmwareDVersion) { + this.firmwareDVersion = firmwareDVersion; + } + + public long getFirmwareLVersion() { + return firmwareLVersion; + } + + public void setFirmwareLVersion(long firmwareLVersion) { + this.firmwareLVersion = firmwareLVersion; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/LoginServiceManager.java b/app/src/main/java/com/qidian/zhongkesmart/model/LoginServiceManager.java new file mode 100644 index 0000000..51b0cb5 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/LoginServiceManager.java @@ -0,0 +1,283 @@ +package com.qidian.zhongkesmart.model; + +import android.text.TextUtils; + +import com.netease.nimlib.sdk.NIMClient; +import com.netease.nimlib.sdk.RequestCallback; +import com.netease.nimlib.sdk.auth.LoginInfo; +import com.netease.nimlib.sdk.uinfo.UserService; +import com.netease.nimlib.sdk.uinfo.constant.UserInfoFieldEnum; +import com.qidian.zhongkesmart.model.base.BaseService; +import com.qidian.zhongkesmart.model.base.CommonDataManager; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +import okhttp3.MediaType; +import okhttp3.RequestBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.http.Body; +import retrofit2.http.Headers; +import retrofit2.http.POST; + +import static com.qidian.zhongkesmart.model.base.BaseService.ERROR_CODE_UNKNOWN; + + +/** + * 登录服务管理 + */ +public class LoginServiceManager { + + private static final LoginServiceManager mOurInstance = new LoginServiceManager(); + + private final Api mApi; + + private Call> sendMessageCall; + private Call> msmLoginCall; + private Call> tokenLoginCall; + private Call> logoutCall; + + public static LoginServiceManager getInstance() { + return mOurInstance; + } + + private LoginServiceManager() { + mApi = BaseService.getInstance().getRetrofit().create(Api.class); + } + + /** + * 网络访问接口 + */ + private interface Api { + @POST("/auth/sendLoginSmsCode") + @Headers("Content-Type: application/json") + Call> sendLoginSmsCode(@Body RequestBody body); + + + @POST("/auth/loginBySmsCode") + @Headers("Content-Type: application/json") + Call> loginBySmsCode(@Body RequestBody body); + + @POST("/auth/loginByToken") + @Headers("Content-Type: application/json") + Call> loginByToken(); + + @POST("/auth/logout") + @Headers("Content-Type: application/json") + Call> logout(); + + } + + + /** + * 发送验证码短信 + * + * @param phoneNumber + * @param callBack + */ + public void sendMessage(String phoneNumber, BaseService.ResponseCallBack callBack) { + if (sendMessageCall != null && sendMessageCall.isExecuted()) { + sendMessageCall.cancel(); + } + JSONObject result = new JSONObject(); + try { + result.put("mobile", phoneNumber); + } catch (JSONException e) { + e.printStackTrace(); + } + RequestBody body = RequestBody.create(MediaType.parse("application/json"), result.toString()); + sendMessageCall = mApi.sendLoginSmsCode(body); + sendMessageCall.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (callBack != null) { + BaseService.ResponseEntity responseEntity = response.body(); + if (responseEntity != null) { + if (responseEntity.code == 200) { + callBack.onSuccess(null); + } else { + callBack.onFail(responseEntity.code); + } + } else { + callBack.onFail(response.code()); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + if (callBack != null) { + callBack.onFail(ERROR_CODE_UNKNOWN); + } + } + }); + } + + /** + * 短信验证码登录 + * + * @param phoneNumber + * @param smsCode + * @param callBack + */ + public void loginWithSms(String phoneNumber, String smsCode, BaseService.ResponseCallBack callBack) { + if (msmLoginCall != null && msmLoginCall.isExecuted()) { + msmLoginCall.cancel(); + } + JSONObject result = new JSONObject(); + try { + result.put("mobile", phoneNumber); + result.put("smsCode", smsCode); + } catch (JSONException e) { + e.printStackTrace(); + } + RequestBody body = RequestBody.create(MediaType.parse("application/json"), result.toString()); + msmLoginCall = mApi.loginBySmsCode(body); + msmLoginCall.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (callBack != null) { + BaseService.ResponseEntity responseEntity = response.body(); + if (responseEntity.code == 200) { + saveUserModel(responseEntity.data); + UserModel userModel = responseEntity.data; + loginNim(userModel, callBack); + } else { + callBack.onFail(responseEntity.code); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + if (callBack != null) { + callBack.onFail(ERROR_CODE_UNKNOWN); + } + } + }); + } + + /** + * 业务token登录接口,IM在初始化的时候设置已有userInfo 可以自动登录 + * + * @param callBack + */ + public void loginWithToken(BaseService.ResponseCallBack callBack) { + if (tokenLoginCall != null && tokenLoginCall.isExecuted()) { + tokenLoginCall.cancel(); + } + tokenLoginCall = mApi.loginByToken(); + tokenLoginCall.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (callBack != null) { + BaseService.ResponseEntity responseEntity = response.body(); + if (responseEntity.code == 200) { + saveUserModel(responseEntity.data); + UserModel userModel = responseEntity.data; + loginNim(userModel, callBack); + } else { + callBack.onFail(responseEntity.code); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + if (callBack != null) { + callBack.onFail(ERROR_CODE_UNKNOWN); + } + } + }); + } + + /** + * 退出登录 + * + * @param callBack + */ + public void logout(BaseService.ResponseCallBack callBack) { + if (logoutCall != null && logoutCall.isExecuted()) { + logoutCall.cancel(); + } + logoutCall = mApi.logout(); + logoutCall.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + if (callBack != null) { + BaseService.ResponseEntity responseEntity = response.body(); + if (responseEntity.code == 200) { + clearLoginInfo(); + ProfileManager.getInstance().logout(); + callBack.onSuccess(null); + } else { + callBack.onFail(responseEntity.code); + } + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + if (callBack != null) { + callBack.onFail(ERROR_CODE_UNKNOWN); + } + } + }); + } + + /** + * 登录IM + * + * @param userModel + * @param callBack + */ + private void loginNim(UserModel userModel, BaseService.ResponseCallBack callBack) { + LoginInfo loginInfo = new LoginInfo(String.valueOf(userModel.imAccid), userModel.imToken); + ProfileManager.getInstance().login(loginInfo, new RequestCallback() { + @Override + public void onSuccess(LoginInfo param) { + callBack.onSuccess(null); + ProfileManager.getInstance().setLogin(true);//登录IM成功 + ProfileManager.getInstance().updateUserInfo(userModel); + Map fields = new HashMap<>(1); + fields.put(UserInfoFieldEnum.Name, userModel.mobile); + NIMClient.getService(UserService.class).updateUserInfo(fields); + } + + @Override + public void onFailed(int code) { + callBack.onFail(code); + } + + @Override + public void onException(Throwable exception) { + callBack.onFail(-1); + } + }); + } + + private void saveUserModel(UserModel userModel) { + if (userModel != null) { + ProfileManager.getInstance().setUserModel(userModel); + if (!TextUtils.isEmpty(userModel.accessToken)) { + ProfileManager.getInstance().setAccessToken(userModel.accessToken); + } + if (!TextUtils.isEmpty(userModel.imToken)) { + CommonDataManager.getInstance().setIMToken(userModel.imToken); + } + } + } + + private void clearLoginInfo() { + ProfileManager.getInstance().setLogin(false); + ProfileManager.getInstance().setUserModel(null); + ProfileManager.getInstance().setAccessToken(null); + CommonDataManager.getInstance().setIMToken(null); + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/MyNERtcCallExtension.java b/app/src/main/java/com/qidian/zhongkesmart/model/MyNERtcCallExtension.java new file mode 100644 index 0000000..b904e32 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/MyNERtcCallExtension.java @@ -0,0 +1,29 @@ +package com.qidian.zhongkesmart.model; + +import com.netease.lava.nertc.sdk.NERtcConstants; +import com.netease.lava.nertc.sdk.NERtcEx; +import com.netease.lava.nertc.sdk.video.NERtcEncodeConfig; +import com.netease.lava.nertc.sdk.video.NERtcVideoConfig; +import com.netease.yunxin.nertc.nertcvideocall.model.NERtcCallExtension; + +public class MyNERtcCallExtension extends NERtcCallExtension { + + @Override + protected int joinChannel(String token, String channelName, long rtcUid) { + //设置一个默认的videoConfig + NERtcVideoConfig videoConfig = new NERtcVideoConfig(); + // 帧率:15 + videoConfig.frameRate = NERtcEncodeConfig.NERtcVideoFrameRate.FRAME_RATE_FPS_15; + // 用户修改分辨率不要使用 profile,应该使用width/height修改,两种方式都存在时,profile 方式失效。 + // 分辨率:360*640(注意此处需要设置宽大于高不用考虑具体角度旋转) + videoConfig.width = 640; + videoConfig.height = 360; + videoConfig.orientationMode = NERtcVideoConfig.NERtcVideoOutputOrientationMode.VIDEO_OUTPUT_ORIENTATION_MODE_FIXED_LANDSCAPE; + NERtcEx.getInstance().setLocalVideoConfig(videoConfig); + // 音频设置 standard + speech + NERtcEx.getInstance().setAudioProfile(NERtcConstants.AudioProfile.STANDARD, NERtcConstants.AudioScenario.SPEECH); + // 设置 channel profile + NERtcEx.getInstance().setChannelProfile(NERtcConstants.RTCChannelProfile.COMMUNICATION); + return NERtcEx.getInstance().joinChannel(token, channelName, rtcUid); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/ProfileManager.java b/app/src/main/java/com/qidian/zhongkesmart/model/ProfileManager.java new file mode 100644 index 0000000..ac7f7e4 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/ProfileManager.java @@ -0,0 +1,152 @@ +package com.qidian.zhongkesmart.model; + +import android.text.TextUtils; + +import com.blankj.utilcode.util.GsonUtils; +import com.blankj.utilcode.util.SPUtils; +import com.netease.nimlib.sdk.AbortableFuture; +import com.netease.nimlib.sdk.NIMClient; +import com.netease.nimlib.sdk.RequestCallback; +import com.netease.nimlib.sdk.auth.AuthService; +import com.netease.nimlib.sdk.auth.LoginInfo; +import com.netease.nimlib.sdk.uinfo.UserService; +import com.netease.nimlib.sdk.uinfo.constant.UserInfoFieldEnum; +import com.netease.yunxin.nertc.nertcvideocall.model.UserInfoInitCallBack; +import com.qidian.zhongkesmart.model.base.CommonDataManager; + +import java.util.HashMap; +import java.util.Map; + +public final class ProfileManager implements UserInfoInitCallBack { + private static final ProfileManager instance = new ProfileManager(); + + public static ProfileManager getInstance() { + return instance; + } + + private final static String PER_USER_MODEL = "per_user_model"; + + private UserModel userModel; + private String token; + private boolean isLogin = false; + + private AbortableFuture loginRequest; + + private ProfileManager() { + } + + public boolean isLogin() { + return isLogin; + } + + public void setLogin(boolean login) { + isLogin = login; + } + + public UserModel getUserModel() { + if (userModel == null) { + loadUserModel(); + } + return userModel; + } + + /** + * 是否是本用户 + * + * @param imAccId + * @return + */ + public boolean isCurrentUser(String imAccId) { + if (getUserModel() == null) { + return false; + } + return TextUtils.equals(getUserModel().imAccid,imAccId); + } + + /** + * 音视频uid 判断 + * @param g2Uid + * @return + */ + public boolean isCurrentUser(long g2Uid) { + if (getUserModel() == null) { + return false; + } + return getUserModel().avRoomUid == g2Uid; + } + + public String getAccessToken() { + if (token == null) { + loadAccessToken(); + } + return token; + } + + public void setUserModel(UserModel model) { + userModel = model; + saveUserModel(); + } + + public void setAccessToken(String token) { + this.token = token; + CommonDataManager.getInstance().setAccessToken(token); + } + + private void loadAccessToken() { + token = CommonDataManager.getInstance().getAccessToken(); + } + + private void loadUserModel() { + try { + String json = SPUtils.getInstance(CommonDataManager.PER_DATA).getString(PER_USER_MODEL); + userModel = GsonUtils.fromJson(json, UserModel.class); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void saveUserModel() { + try { + if (userModel != null) { + SPUtils.getInstance(CommonDataManager.PER_DATA).put(PER_USER_MODEL, GsonUtils.toJson(userModel)); + } else { + SPUtils.getInstance(CommonDataManager.PER_DATA).put(PER_USER_MODEL, ""); + } + } catch (Exception e) { + } + } + + public void login(LoginInfo loginInfo, RequestCallback callback) { + loginRequest = NIMClient.getService(AuthService.class).login(loginInfo); + loginRequest.setCallback(callback); + } + + public void updateUserInfo(UserModel userModel) { + Map userInfo = new HashMap<>(); + userInfo.put(UserInfoFieldEnum.AVATAR, userModel.avatar); + userInfo.put(UserInfoFieldEnum.MOBILE, userModel.mobile); + NIMClient.getService(UserService.class).updateUserInfo(userInfo); + } + + public void logout() { + if (!isLogin) { + return; + } + isLogin = false; + userModel = null; + token = null; + NIMClient.getService(AuthService.class).logout(); + SPUtils.getInstance(CommonDataManager.PER_DATA).put(PER_USER_MODEL, ""); + } + + @Override + public void onUserLoginToIm(String imAccId, String imToken) { + UserModel userModel = getUserModel(); + if (userModel==null){ + userModel=new UserModel(); + } + userModel.imAccid = imAccId; + userModel.imToken = imToken; + setUserModel(userModel); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/SelfNotificationConfigFetcher.java b/app/src/main/java/com/qidian/zhongkesmart/model/SelfNotificationConfigFetcher.java new file mode 100644 index 0000000..4ed15c8 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/SelfNotificationConfigFetcher.java @@ -0,0 +1,34 @@ +package com.qidian.zhongkesmart.model; + +import com.netease.nimlib.sdk.NIMClient; +import com.netease.nimlib.sdk.uinfo.UserService; +import com.netease.nimlib.sdk.uinfo.model.NimUserInfo; +import com.netease.yunxin.kit.alog.ALog; +import com.netease.yunxin.nertc.nertcvideocall.bean.InvitedInfo; +import com.netease.yunxin.nertc.ui.CallKitNotificationConfig; +import com.qidian.zhongkesmart.R; + +import org.json.JSONObject; + +import kotlin.jvm.functions.Function1; + +public class SelfNotificationConfigFetcher implements Function1 { + @Override + public CallKitNotificationConfig invoke(InvitedInfo invitedInfo) { + String name; + NimUserInfo userInfo = NIMClient.getService(UserService.class).getUserInfo(invitedInfo.invitor); + if (userInfo != null) { + name = userInfo.getName(); + } else { + String extraInfo = invitedInfo.attachment; + try { + JSONObject object = new JSONObject(extraInfo); + name = object.optString("userName"); + } catch (Exception exception) { + ALog.e("SelfNotificationConfigFetcher", "parse inviteInfo extra error.", exception); + name = ""; + } + } + return new CallKitNotificationConfig(R.mipmap.ic_launcher, null, "您有新的来电", name + "邀请您进行【网络通话】"); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/SelfUserInfoHelper.java b/app/src/main/java/com/qidian/zhongkesmart/model/SelfUserInfoHelper.java new file mode 100644 index 0000000..b52c8ad --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/SelfUserInfoHelper.java @@ -0,0 +1,50 @@ +package com.qidian.zhongkesmart.model; + +import android.content.Context; +import android.widget.ImageView; + +import com.netease.nimlib.sdk.NIMClient; +import com.netease.nimlib.sdk.RequestCallbackWrapper; +import com.netease.nimlib.sdk.uinfo.UserService; +import com.netease.nimlib.sdk.uinfo.model.NimUserInfo; +import com.netease.yunxin.nertc.ui.base.UserInfoHelper; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +public class SelfUserInfoHelper implements UserInfoHelper { + @Override + public boolean fetchNickname(@NotNull String accId, @NotNull Function1 function1) { + UserModel userModel = UserCacheManager.getInstance().getUserModelFromAccId(accId); + if (userModel != null) { + function1.invoke(userModel.mobile + ""); + } else { + NIMClient.getService(UserService.class).fetchUserInfo(Collections.singletonList(accId)).setCallback(new RequestCallbackWrapper>() { + @Override + public void onResult(int code, List result, Throwable exception) { + if (result == null || result.isEmpty()) { + function1.invoke(accId); + return; + } + function1.invoke(result.get(0).getMobile() + ""); + } + }); + } + return true; + } + + @Override + public boolean fetchNicknameByTeam(@NotNull String s, @NotNull String s1, @NotNull Function1 function1) { + return false; + } + + @Override + public boolean loadAvatar(@NotNull Context context, @NotNull String s, @NotNull ImageView imageView) { + return false; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/UserCacheManager.java b/app/src/main/java/com/qidian/zhongkesmart/model/UserCacheManager.java new file mode 100644 index 0000000..f91d985 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/UserCacheManager.java @@ -0,0 +1,95 @@ +package com.qidian.zhongkesmart.model; + +import com.blankj.utilcode.util.GsonUtils; +import com.blankj.utilcode.util.SPUtils; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + + +public class UserCacheManager { + + private List lastSearchUser; + + private final Map userModelMap = new HashMap<>(); + + private static final String LAST_SEARCH_USER = "last_search_users"; + + private static final String USER_LIST = "user_list"; + + private static final int MAX_SIZE = 3; + + public static UserCacheManager getInstance() { + return new UserCacheManager(); + } + + private UserCacheManager() { + + } + + public void getLastSearchUser(final GetUserCallBack callBack) { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + executorService.submit(new Runnable() { + @Override + public void run() { + if (lastSearchUser == null || lastSearchUser.size() == 0) { + loadUsers(); + } + callBack.getUser(lastSearchUser); + } + }); + } + + public void addUser(final UserModel userModel) { + if (userModel != null) { + userModelMap.put(userModel.imAccid, userModel); + } + ExecutorService executorService = Executors.newSingleThreadExecutor(); + executorService.submit(new Runnable() { + @Override + public void run() { + if (lastSearchUser == null || lastSearchUser.size() == 0) { + loadUsers(); + } + if (lastSearchUser != null) { + if (!lastSearchUser.contains(userModel)) { + if (lastSearchUser.size() >= MAX_SIZE) { + lastSearchUser.remove(lastSearchUser.get(0)); + } + lastSearchUser.add(userModel); + } + } else { + lastSearchUser = new LinkedList<>(); + lastSearchUser.add(userModel); + } + uploadUsers(); + } + }); + } + + public UserModel getUserModelFromAccId(String accId){ + return userModelMap.get(accId); + } + + private void loadUsers() { + UserModel currentUser = ProfileManager.getInstance().getUserModel(); + String userStr = SPUtils.getInstance(LAST_SEARCH_USER + currentUser.mobile).getString(USER_LIST); + Type type = GsonUtils.getListType(UserModel.class); + lastSearchUser = GsonUtils.fromJson(userStr, type); + } + + private void uploadUsers() { + UserModel currentUser = ProfileManager.getInstance().getUserModel(); + String userStr = GsonUtils.toJson(lastSearchUser); + SPUtils.getInstance(LAST_SEARCH_USER + currentUser.mobile).put(USER_LIST, userStr); + } + + public interface GetUserCallBack { + void getUser(List users); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/UserModel.java b/app/src/main/java/com/qidian/zhongkesmart/model/UserModel.java new file mode 100644 index 0000000..9cd04fa --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/UserModel.java @@ -0,0 +1,26 @@ +package com.qidian.zhongkesmart.model; + +import android.text.TextUtils; + +import java.io.Serializable; + +/** + * 业务用户数据 + */ +public final class UserModel implements Serializable { + public String mobile;//String 登录的手机号 + public String accessToken;//String 登录令牌,重新生成的新令牌,过期时间重新计算 + public String imAccid;//long IM账号 + public String imToken;//String IM令牌,重新生成的新令牌 + public long avRoomUid;//String 音视频房间内成员编号 + public String avatar;//String 头像地址 + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UserModel userModel = (UserModel) o; + return TextUtils.equals(this.mobile, userModel.mobile); + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/VersionInfo.kt b/app/src/main/java/com/qidian/zhongkesmart/model/VersionInfo.kt new file mode 100644 index 0000000..8083557 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/VersionInfo.kt @@ -0,0 +1,7 @@ +package com.qidian.zhongkesmart.model + +data class VersionInfo ( + val time: String, + val version: String, + val url: String +) \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/base/BaseService.java b/app/src/main/java/com/qidian/zhongkesmart/model/base/BaseService.java new file mode 100644 index 0000000..62b5ddf --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/base/BaseService.java @@ -0,0 +1,78 @@ +package com.qidian.zhongkesmart.model.base; + +import android.text.TextUtils; +import android.util.Log; + +import com.qidian.zhongkesmart.BuildConfig; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class BaseService { + + private final Retrofit mRetrofit; + + public static final int ERROR_CODE_UNKNOWN = -1; + + private static final String BASE_URL = BuildConfig.BASE_URL; + + public static BaseService getInstance() { + return RetrofitHolder.retrofit; + } + + static class RetrofitHolder { + static BaseService retrofit = new BaseService(); + } + + private BaseService() { + HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(message -> Log.d("======>>>", message)); + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .addInterceptor(interceptor) + .addInterceptor(new Interceptor() { + @Override + public okhttp3.Response intercept(Chain chain) throws IOException { + Request original = chain.request(); + + // Request customization: add request headers + + Request.Builder requestBuilder = original.newBuilder(); + if (!TextUtils.isEmpty(CommonDataManager.getInstance().getAccessToken())) { + requestBuilder.header("accessToken", CommonDataManager.getInstance().getAccessToken()); + } + + requestBuilder.addHeader("appkey", BuildConfig.APP_KEY); + + + Request request = requestBuilder.build(); + return chain.proceed(request); + } + }); + mRetrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .client(builder.build()) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + } + + public Retrofit getRetrofit() { + return mRetrofit; + } + + public static class ResponseEntity { + public int code; + public T data; + } + + public interface ResponseCallBack { + void onSuccess(T response); + + void onFail(int code); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/model/base/CommonDataManager.java b/app/src/main/java/com/qidian/zhongkesmart/model/base/CommonDataManager.java new file mode 100644 index 0000000..80a3227 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/model/base/CommonDataManager.java @@ -0,0 +1,56 @@ +package com.qidian.zhongkesmart.model.base; + +import com.blankj.utilcode.util.SPUtils; + +public class CommonDataManager { + + private static final CommonDataManager instance = new CommonDataManager(); + + public static CommonDataManager getInstance() { + return instance; + } + + public final static String PER_DATA = "per_profile_manager"; + private static final String PER_ACCESS_TOKEN = "per_access_token"; + private static final String PER_IM_TOKEN = "per_im_token"; + + private String token; + + private String imToken; + + private CommonDataManager() { + } + + public String getAccessToken() { + if (token == null) { + loadAccessToken(); + } + return token; + } + + public void setAccessToken(String token) { + this.token = token; + SPUtils.getInstance(PER_DATA).put(PER_ACCESS_TOKEN, this.token); + } + + private void loadAccessToken() { + token = SPUtils.getInstance(PER_DATA).getString(PER_ACCESS_TOKEN, ""); + } + + public String getIMToken() { + if (imToken == null) { + loadAccessToken(); + } + return imToken; + } + + public void setIMToken(String imToken) { + this.imToken = imToken; + SPUtils.getInstance(PER_DATA).put(PER_IM_TOKEN, this.imToken); + } + + private void loadIMToken() { + imToken = SPUtils.getInstance(PER_DATA).getString(PER_IM_TOKEN, ""); + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/ApiUrl.kt b/app/src/main/java/com/qidian/zhongkesmart/net/ApiUrl.kt new file mode 100644 index 0000000..2efc0da --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/ApiUrl.kt @@ -0,0 +1,99 @@ +package com.qidian.zhongkesmart.net + +class ApiUrl { + //线上地址 + // public final static String domain = "http://58.56.0.66:15900/";//本地 +// var domain = "" + // public static String domain = "https://ifhs.fedhealth.cn/"; + // public final static String domain = "https://114.116.43.24:30009/";//线上 + + + + + companion object { + + var domain: String = "" + + @JvmName("setDomain1") + fun setDomain(mdomain:String){ + domain=mdomain + getCode = domain + "fhh/v2/gatewaySession/code" + getLogin = domain + "fhh/v2/gatewaySession/login" + getBindingStatus = domain + "fhh/v2/gatewaySession/isBingding" + getLoginSuccess = domain + "fhh/v2/gatewaySession/ping" + BindTiePian = domain + "fhh/v2/bridge/device" + RemoveBindTiePian = domain + "fhh/v2/bridge/gatewayDevice" + resetApp = domain + "fhh/v2/gatewayInfo" + pushNewGetData = domain + "fhh/v2/bridge/data" + //离线 + pushOffData = domain + "fhh/v2/bridge/data/off" + //报警 + pushWarnData = domain + "fhh/v2/bridge/data/warn" + //网关心跳 + heartbeat = domain + "fhh/v2/bridge/gatewayInfo/heartbeat" // //新增贴片信息 + //获取家庭信息 + getFamilyInfo = domain +"fhh/v2/bridge/familyInfo" + getAPPVersionInfo = domain + "fhh/v2/hi?type=a" + geFirmwareDVersionInfo = domain + "fhh/v2/hi?type=d" + geFirmwareLVersionInfo = domain + "fhh/v2/hi?type=l" + } + /** + * 网关 + */ + //获取code + var getCode = domain + "fhh/v2/gatewaySession/code" + + //网关登录 + var getLogin = domain + "fhh/v2/gatewaySession/login" + + //网关绑定状态 + var getBindingStatus = domain + "fhh/v2/gatewaySession/isBingding" + + //测试网关是否连接成功 + var getLoginSuccess = domain + "fhh/v2/gatewaySession/ping" + + /** + * 贴片相关 + */ + //新增贴片信息 + var BindTiePian = domain + "fhh/v2/bridge/device" + + //解除绑定贴片信息 + var RemoveBindTiePian = domain + "fhh/v2/bridge/gatewayDevice" + + //重置网关 + var resetApp = domain + "fhh/v2/gatewayInfo" + + /** + * 数据推送 + */ + //新增贴片信息 + var pushNewGetData = domain + "fhh/v2/bridge/data" + + //离线 + var pushOffData = domain + "fhh/v2/bridge/data/off" + + //报警 + var pushWarnData = domain + "fhh/v2/bridge/data/warn" + + //网关心跳 + var heartbeat = domain + "fhh/v2/bridge/gatewayInfo/heartbeat" // //新增贴片信息 + // public final static String BindTiePian = domain + "fhh/v2/bridge/patch"; + // //解除绑定贴片信息 + // public final static String RemoveBindTiePian = domain + "fhh/v2/bridge/gatewayPatch"; + // //重置网关 + // public final static String resetApp = domain + "fhh/v2/gatewayInfo"; + + //获取家庭信息 + var getFamilyInfo = domain + "fhh/v2/bridge/familyInfo" + + //获取版本信息 + var getAPPVersionInfo = domain + "fhh/v2/hi?type=a" + + var geFirmwareDVersionInfo = domain + "fhh/v2/hi?type=d" + + var geFirmwareLVersionInfo = domain + "fhh/v2/hi?type=l" + + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/BaseBean.java b/app/src/main/java/com/qidian/zhongkesmart/net/BaseBean.java new file mode 100644 index 0000000..c338eeb --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/BaseBean.java @@ -0,0 +1,36 @@ +package com.qidian.zhongkesmart.net; + +import com.google.gson.JsonElement; + +public class BaseBean { + //code + String code = ""; + //message + String message; + //数据 + JsonElement data; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public JsonElement getRecords() { + return data; + } + + public void setRecords(JsonElement records) { + this.data = records; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/BaseBeanGetCode.java b/app/src/main/java/com/qidian/zhongkesmart/net/BaseBeanGetCode.java new file mode 100644 index 0000000..6e27432 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/BaseBeanGetCode.java @@ -0,0 +1,36 @@ +package com.qidian.zhongkesmart.net; + +import com.google.gson.JsonElement; + +public class BaseBeanGetCode { + //code + String code = ""; + //message + String message; + //数据 + String data; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getRecords() { + return data; + } + + public void setRecords(String records) { + this.data = records; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/HttpCallBack.java b/app/src/main/java/com/qidian/zhongkesmart/net/HttpCallBack.java new file mode 100644 index 0000000..c89e9b2 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/HttpCallBack.java @@ -0,0 +1,9 @@ +package com.qidian.zhongkesmart.net; + +public abstract class HttpCallBack { + public abstract void success(String message,Object object); + public boolean failed(String code, String info){return true;} + public boolean invalidLogin(){return false;}//登录失效 + public void befor(){} + public void after(){} +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/HttpHeader.java b/app/src/main/java/com/qidian/zhongkesmart/net/HttpHeader.java new file mode 100644 index 0000000..cc274e9 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/HttpHeader.java @@ -0,0 +1,21 @@ +package com.qidian.zhongkesmart.net; + +import com.lzy.okgo.model.HttpHeaders; + +import java.util.Map; + +public abstract class HttpHeader { + + public HttpHeaders getHttpHeaders() { + HttpHeaders httpHeader = new HttpHeaders(); + Map params = getParams(); + if (params != null && params.size() > 0) { + for (Map.Entry entry : params.entrySet()) { + httpHeader.put(entry.getKey(), entry.getValue()); + } + } + return httpHeader; + } + + public abstract Map getParams(); +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/HttpRequest.java b/app/src/main/java/com/qidian/zhongkesmart/net/HttpRequest.java new file mode 100644 index 0000000..4529b38 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/HttpRequest.java @@ -0,0 +1,312 @@ +package com.qidian.zhongkesmart.net; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; + +import com.lzy.okgo.OkGo; +import com.lzy.okgo.callback.StringCallback; +import com.lzy.okgo.model.HttpHeaders; +import com.lzy.okgo.model.Response; +import com.lzy.okgo.request.base.Request; +import com.qidian.zhongkesmart.utils.DataServer; +import com.qidian.zhongkesmart.utils.DialogUtil; +import com.qidian.zhongkesmart.utils.JsonUtils; +import com.qidian.zhongkesmart.utils.ToastUtil; +import com.qidian.zhongkesmart.utils.ToolUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HttpRequest { + public final static String POST = "post"; + public final static String GET = "get"; + public final static String PUT = "put"; + public final static String DELETE = "delete"; + public final static String POST_JSON = "post_json"; + public final static String UPLOAD_FILE = "upload_file"; + + public final static String CONTENT_TYPE_JSON = "application/json"; + public final static String CONTENT_TYPE_X_WWW = "application/x-www-form-urlencoded"; + public final static String CONTENT_TYPE_FORM_DATA = "form-data"; + + private Class clazz = BaseBean.class; + private String apiurl = ""; + private String mapString = ""; + private Map map = new HashMap<>(); + private Map objMap = new HashMap<>(); + private String MODEl = POST; + private static HttpRequest request; + private Context mContext; + private boolean isShowDialog = true; + private Dialog loadingDialog = null; + private String contentType = "application/json"; + private HttpHeaders headers = new HttpHeaders(); + private List files = new ArrayList<>(); + /** + * true:[{"gatewayId": "5c39250bb12730e6","patchMac": "cf6a12483f37","roomName": "卧室","type": "2"}] + * false:{"gatewayId": "5c39250bb12730e6","patchMac": "cf6a12483f37","roomName": "卧室","type": "2"} + */ + private Boolean isJsonList = false; + + public static HttpRequest init(Context context) { + request = new HttpRequest(context); + return request; + } + + public HttpRequest(Context context) { + this.mContext = context; + loadingDialog = DialogUtil.INSTANCE.loadingDialog(context); + } + + public HttpRequest setApiUrl(String apiurl) { + this.apiurl = apiurl; + return this; + } + + public HttpRequest setContentType(String contentType) { + this.contentType = contentType; + return this; + } + + public HttpRequest setAuthHeaders() { + headers.put("Authorization", "Bearer " + DataServer.INSTANCE.getMHttpToken()); + Log.i("Authorization==", "Bearer " + DataServer.INSTANCE.getMHttpToken()); + return this; + } + + public HttpRequest setHeaders(HttpHeaders headers) { + this.headers = headers; + return this; + } + + public HttpRequest setModel(String model) { + this.MODEl = model; + return this; + } + + public HttpRequest get(String apiurl) { + MODEl = GET; + this.apiurl = apiurl; + return this; + } + + public HttpRequest post(String apiurl) { + MODEl = POST; + this.apiurl = apiurl; + return this; + } + + public HttpRequest put(String apiurl, Map map) { + MODEl = PUT; + this.apiurl = apiurl; + this.objMap.putAll(map);//外层不包裹params + return this; + } + + public HttpRequest postJson(String apiurl, Map map, String mapStr ,Boolean misJsonList) { + MODEl = POST_JSON; + this.apiurl = apiurl; + this.isJsonList = misJsonList; + if(isJsonList){ + this.mapString=mapStr;//外层不包裹params + }else { + this.objMap.putAll(map);//外层不包裹params + } + return this; + } + + + public HttpRequest DeleteJson(String apiurl) { + MODEl = DELETE; + this.apiurl = apiurl; + return this; + } + + public HttpRequest setObjMap(Map map) { + MODEl = POST_JSON; + this.objMap = map; + return this; + } + + public HttpRequest uploadFile(String apiurl, List files) { + MODEl = UPLOAD_FILE; + this.apiurl = apiurl; + this.files = files; + return this; + } + + public HttpRequest setClazz(Class clazz) { + this.clazz = clazz; + return this; + } + + public HttpRequest setMap(Map map) { + this.map = map; + return this; + } + + public HttpRequest setShowDialog(boolean showDialog) { + isShowDialog = showDialog; + return this; + } + + public void excute(HttpCallBack callback) { + request(MODEl, callback); + } + + private void request(String model, final HttpCallBack callback) { + StringCallback stringCallback = new StringCallback() { + @Override + public void onSuccess(Response response) { + Log.d("json", response.body()); + if (response != null) { + requestDealWith(response.body(), clazz, callback); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + if (response != null && response.body() != null) { + Log.d("json", response.body()); + requestDealWith(response.body(), clazz, callback); + }else { + ToastUtil.showToast("联网失败"); +// loadingDialog.dismiss(); + if (mContext instanceof Activity) { + if (!((Activity) mContext).isFinishing()) { + if(loadingDialog!=null&&loadingDialog.isShowing()) + loadingDialog.dismiss(); + } + } + } + + } + + @Override + public void onStart(Request request) { + super.onStart(request); + if (mContext != null && loadingDialog != null && isShowDialog && !loadingDialog.isShowing()) { + if (mContext instanceof Activity) { + if (!((Activity) mContext).isFinishing()) { + loadingDialog.show(); + } + } + } + callback.befor(); + } + + @Override + public void onFinish() { + super.onFinish(); + if (mContext instanceof Activity) { + if (!((Activity) mContext).isFinishing()) { + if (loadingDialog != null && loadingDialog.isShowing()) + loadingDialog.dismiss(); + } + } + callback.after(); + + } + }; + + Log.e("http-url", apiurl); + if (model.equals(POST)) { + Log.e("http-map", map.toString()); + if (contentType.equals(CONTENT_TYPE_JSON)) { + String jsonStr = JsonUtils.Companion.getUtil().mapToJson(map); + OkGoUtil.postJson(apiurl, jsonStr, headers, stringCallback); + } else if (contentType.equals(CONTENT_TYPE_X_WWW)) { + OkGoUtil.post(apiurl, map, headers, stringCallback); + } else if (contentType.equals(CONTENT_TYPE_FORM_DATA)) { + OkGoUtil.postFromData(apiurl, map, headers, stringCallback); + } + } else if (model.equals(GET)) { + Log.e("http-map", map.toString()); + headers.put("Content-Type", contentType); + OkGoUtil.get(apiurl, map, headers, stringCallback); + } else if (model.equals(POST_JSON)) { + String jsonStr = ""; + if (isJsonList) { + jsonStr = mapString; +// jsonStr = ToolUtil.INSTANCE.JsonToListJson(JsonUtils.Companion.getUtil().mapToJson(objMap)); + } else { + jsonStr = JsonUtils.Companion.getUtil().mapToJson(objMap); + } + Log.e("http-json", jsonStr); + OkGoUtil.postJson(apiurl, jsonStr, headers, stringCallback); + } else if (model.equals(UPLOAD_FILE)) { + OkGoUtil.uploadFile(apiurl, files, stringCallback); + } else if (model.equals(DELETE)) { + Log.e("http-map", map.toString()); + headers.put("Content-Type", contentType); + String jsonStr = JsonUtils.Companion.getUtil().mapToJson(map); + Log.e("http-json", jsonStr); + OkGoUtil.delete(apiurl, map, headers, stringCallback); + } else if (model.equals(PUT)) { + String jsonStr = ""; + jsonStr = ToolUtil.INSTANCE.JsonToListJson(JsonUtils.Companion.getUtil().mapToJson(objMap)); + Log.e("http-json", jsonStr); +// OkGoUtil.putJson(apiurl, jsonStr, headers, stringCallback); +// OkGoUtil.put(apiurl, map, headers, stringCallback); + OkGoUtil.put(apiurl, jsonStr, headers, stringCallback); + } + + } + + //请求后处理 + public void requestDealWith(String s, Class clazz, HttpCallBack callback) { + if (TextUtils.isEmpty(s)) { + Log.d("http_request", "没有json返回,请检查请求方式"); + callback.failed("10001", "服务器异常,请联系管理员"); + return; + } + BaseBean resultBean = (BaseBean) JsonUtils.Companion.getUtil().jsonParser(s, BaseBean.class); + if (resultBean.getCode().equals(HttpSetting.SUCCESS_CODE)) { + if (clazz != null && clazz != BaseBean.class) { + if (resultBean.getRecords() != null && !TextUtils.isEmpty(resultBean.getRecords().toString()) && + !resultBean.getRecords().toString().equals("null")) { + Log.i("OkGo", "resultBean.getRecords==" + resultBean.getRecords()); + //records 存在直接是个布尔类型 + if (resultBean.getRecords().toString().equals("false") || resultBean.getRecords().toString().equals("true")) { + callback.success(resultBean.getMessage(), Boolean.valueOf(resultBean.getRecords().toString())); + } else { + Object object = JsonUtils.Companion.getUtil().jsonParser(resultBean.getRecords().toString(), clazz); + callback.success(resultBean.getMessage(), object); + } + } else { + callback.success(resultBean.getMessage(), null); + } + } else { + if (resultBean.getRecords() != null) { + callback.success(resultBean.getMessage(), resultBean.getRecords().toString().replaceAll("\"", "")); + } else { + callback.success(resultBean.getMessage(), null); + } + } + } else { + /* if (Arrays.binarySearch(HttpSetting.LOGIN_FAILD_CODE, resultBean.getCode()) > -1) { + callback.failed(resultBean.getCode(), resultBean.getMessage()); + if (!callback.invalidLogin()) { + //登录过期 +// LoginManager.getInstance().loginOutLogic(mContext, true); + } + } else {*/ +// if (callback.failed(resultBean.getCode(), resultBean.getMessage())) { +// ToastUtil.showToast(resultBean.getMessage()); + callback.failed(resultBean.getMessage(), null); +// } +// } + } + } + + public static void cancleRequest(String tag) { + OkGo.getInstance().cancelTag(tag); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/HttpSetting.java b/app/src/main/java/com/qidian/zhongkesmart/net/HttpSetting.java new file mode 100644 index 0000000..1887ea7 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/HttpSetting.java @@ -0,0 +1,24 @@ +package com.qidian.zhongkesmart.net; + + +import java.util.HashMap; +import java.util.Map; + +public class HttpSetting { + //成功的状态码 + public static String SUCCESS_CODE = "0"; + + //请求头 + public static HttpHeader headers =new HttpHeader() { + @Override + public Map getParams() { + Map map = new HashMap<>(); +// map.put("token", DataServer.INSTANCE.getMUserToken()); + return map; + } + }; + + //登录失败状态码 + public static String[] LOGIN_FAILD_CODE = new String[]{"901"}; + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/OkGoUtil.java b/app/src/main/java/com/qidian/zhongkesmart/net/OkGoUtil.java new file mode 100644 index 0000000..ba7af77 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/OkGoUtil.java @@ -0,0 +1,95 @@ +package com.qidian.zhongkesmart.net; + +import com.lzy.okgo.OkGo; +import com.lzy.okgo.cache.CacheMode; +import com.lzy.okgo.callback.StringCallback; +import com.lzy.okgo.model.HttpHeaders; + +import java.io.File; +import java.util.List; +import java.util.Map; + +public class OkGoUtil { + public static void get(String url, Map map, HttpHeaders httpHeader, StringCallback stringCallback) { + OkGo.get(url) + .tag(url) + .cacheKey("cacheGetKey") + .cacheMode(CacheMode.NO_CACHE) + .headers(HttpSetting.headers.getHttpHeaders()) + .headers(httpHeader) + .params(map) + .execute(stringCallback); + } + public static void delete(String url, Map/*String*/ map, HttpHeaders httpHeader, StringCallback stringCallback) { + OkGo.delete(url) + .isMultipart(true) + .tag(url) + .cacheKey("cacheGetKey") +// .cacheMode(CacheMode.NO_CACHE) + .headers(HttpSetting.headers.getHttpHeaders()) + .headers(httpHeader) +// .upString(map) + .params(map) + .execute(stringCallback); + } + + public static void post(String url, Map map, HttpHeaders httpHeader, StringCallback stringCallback) { + OkGo.post(url) + .tag(url) + .cacheKey("cachePostKey") + .headers(HttpSetting.headers.getHttpHeaders()) + .headers(httpHeader) + .params(map) + .execute(stringCallback); + } + + public static void postJson(String url, String jsonStr, HttpHeaders httpHeader, StringCallback stringCallback) { + OkGo.post(url) + .tag(url) + .cacheKey("cachePostKey") + .headers(HttpSetting.headers.getHttpHeaders()) + .headers(httpHeader) + .upJson(jsonStr) + .execute(stringCallback); + } + + public static void put(String url, String jsonStr/*, Map map*/,HttpHeaders httpHeader, StringCallback stringCallback) { + OkGo.put(url) + .tag(url) + .cacheKey("cachePostKey") + .headers(HttpSetting.headers.getHttpHeaders()) + .headers(httpHeader) +// .params(map) + .upJson(jsonStr) + .execute(stringCallback); + } + + public static void postFromData(String url, Map map, HttpHeaders httpHeader, StringCallback stringCallback) { + OkGo.post(url) + .isMultipart(true) + .tag(url) + .cacheKey("cachePostKey") + .headers(HttpSetting.headers.getHttpHeaders()) + .headers(httpHeader) + .params(map) + .execute(stringCallback); + } + + /** + * 上传图片 + * + * @param url + * @param stringCallback + */ + public static void uploadFile(String url,List files, StringCallback stringCallback) { + OkGo.post(url) + .isMultipart(true) + .tag(url) + .cacheKey("cachePostKey") + .cacheMode(CacheMode.NO_CACHE) + .headers(HttpSetting.headers.getHttpHeaders()) + .addFileParams("files", files) + .execute(stringCallback); + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/OkHttpUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/net/OkHttpUtils.kt new file mode 100644 index 0000000..d0f5396 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/OkHttpUtils.kt @@ -0,0 +1,299 @@ +package com.qidian.zhongkesmart.net + +import android.os.Handler +import android.util.Log +import com.qidian.zhongkesmart.utils.JsonUtils +import com.qidian.zhongkesmart.utils.JsonUtils.Companion.util +import com.qidian.zhongkesmart.utils.ToastUtil +import okhttp3.* +import org.json.JSONException +import java.io.IOException +import java.util.concurrent.TimeUnit + +/** + * Description : OkHttp网络连接封装工具类 + * Author : + * Date : 2020年7月1日15:51:20 + */ +object OkHttpUtils { + private const val TAG = "OkHttpUtils" + + //handler主要用于异步请求数据之后更新UI + private val handler = Handler() + fun getAsync( + url: String, + params: Map?, + clazz: Class<*>, + responseCallBack: ResponseCallBack + ) { + val client = OkHttpClient() + Log.i(TAG, "请求地址===》$url") + var reallyUrl = url + if (params != null) { + reallyUrl = getUrl(url, params) + } + val request = + Request.Builder() + // .addHeader("Cookie","token="+ HttpUrlVo.TOKEN) + .url(reallyUrl) + .build() + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.e(TAG, "响应失败===》" + e.message) + handler.post { responseCallBack.error(e.message) } + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + val respBody = response.body()!!.string() + Log.i(TAG, "响应成功===》$respBody") + handler.post { + try { + var objectbean = JsonUtils.util.jsonParser(respBody, clazz) + responseCallBack.success(respBody, objectbean); + /* val resultBean = + util.jsonParser(respBody, clazz::class.java) as BaseBean<*> + if (resultBean.code == "0") { + responseCallBack.success(respBody, resultBean) + } else { + responseCallBack.error(respBody) + ToastUtil.showToast(resultBean.getMessage()) + }*/ + } catch (e: JSONException) { + e.printStackTrace() + Log.i("网络okhttp", "程序出现异常:" + e.message) + } + } + } + }) + } + + fun getUrl( + url: String?, + params: Map? + ): String { + val builder = HttpUrl.parse(url)!!.newBuilder() + if (params != null && params.size > 0) { + for (key in params.keys) { + builder.addQueryParameter(key, params[key]) + } + } + return builder.build().toString() + } + + /** + * 表单提交数据 + * @param url 请求地址 + * @param formData 表单回调 + * @param responseCallBack 响应回调 + */ + fun postAsyncFormData( + url: String, + formData: Map, + clazz: Class<*>?, + responseCallBack: ResponseCallBack + ) { + val client = OkHttpClient().newBuilder().connectTimeout(30, TimeUnit.SECONDS) + .build() + val builder = FormBody.Builder() + val showData = StringBuffer() + for (key in formData.keys) { + builder.add(key, formData[key]) + showData.append(" " + key + ":" + formData[key]) + } + val formBody = builder + .build() + val request = Request.Builder() + .get() // .addHeader("Cookie","token="+ HttpUrlVo.TOKEN) + .url(url) + .post(formBody) + .build() + Log.i(TAG, "开始发送请求:请求地址【$url】,请求参数==>$showData") + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.e(TAG, "响应失败===》" + e.message) + handler.post { responseCallBack.error(e.message) } + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + val respBody = response.body()!!.string() + Log.i(TAG, "响应成功===》$respBody") + handler.post { + try { +// responseCallBack.success(respBody); +// Object object = JsonUtils.Companion.getUtil().jsonParser(respBody, clazz); +// responseCallBack.success(respBody,object); + val resultBean = + util.jsonParser(respBody, BaseBean::class.java) as BaseBean<*> + if (resultBean.code == "0") { + responseCallBack.success(respBody, resultBean) + } else { + responseCallBack.error(respBody) + ToastUtil.showToast(resultBean.getMessage()) + } + } catch (e: JSONException) { + Log.i("网络okhttp", "程序出现异常:" + e.message) + } + } + } + }) + } + + /** + * json提交数据 + * @param url 请求地址 + * @param json json数据 + * @param responseCallBack 响应回调 + */ + fun postAsyncJson( + url: String, + json: String, + clazz: Class<*>?, + responseCallBack: ResponseCallBack + ) { + val client = OkHttpClient() + val requestBody = RequestBody.create(MediaType.parse("application/json"), json) + val request = Request.Builder() + .url(url) +// .addHeader("Cookie", "Authorization=" + "Bearer " + DataServer.mHttpToken) + .post(requestBody) + .build() + Log.i(TAG, "开始发送请求:请求地址【$url】,请求参数==>$json") + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.e(TAG, "响应失败===》" + e.message) + handler.post { responseCallBack.error(e.message) } + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + val respBody = response.body()!!.string() + Log.i(TAG, "响应成功===》$respBody") + handler.post { + try { + val resultBean = + util.jsonParser(respBody, BaseBean::class.java) as BaseBean<*> + if (resultBean.code == "0") { + responseCallBack.success(respBody, resultBean) + } else { + responseCallBack.error(resultBean.message) +// ToastUtil.showToast(resultBean.getMessage()) + } + // Object object = JsonUtils.Companion.getUtil().jsonParser(respBody, clazz); + } catch (e: JSONException) { + Log.i("网络okhttp", "程序出现异常:" + e.message) + } + } + } + }) + } + + /** + * json提交数据 + * @param url 请求地址 + * @param json json数据 + * @param responseCallBack 响应回调 + */ + fun putAsyncJson( + url: String, + json: String, + clazz: Class<*>?, + responseCallBack: ResponseCallBack + ) { + val client = OkHttpClient() + val requestBody = RequestBody.create(MediaType.parse("application/json"), json) + val request = Request.Builder() + .url(url) // .addHeader("Cookie","token="+ HttpUrlVo.TOKEN) + .put(requestBody) + .build() + Log.i(TAG, "开始发送请求:请求地址【$url】,请求参数==>$json") + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.e(TAG, "响应失败===》" + e.message) + handler.post { responseCallBack.error(e.message) } + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + val respBody = response.body()!!.string() + Log.i(TAG, "响应成功===》$respBody") + handler.post { + try { +// responseCallBack.success(respBody); + val resultBean = + util.jsonParser(respBody, BaseBean::class.java) as BaseBean<*> + if (resultBean.code == "0") { + responseCallBack.success(respBody, resultBean) + } else { + responseCallBack.error(respBody) + ToastUtil.showToast(resultBean.getMessage()) + } + } catch (e: JSONException) { + Log.i("网络okhttp", "程序出现异常:" + e.message) + } + } + } + }) + } + + /** + * json提交数据 + * @param url 请求地址 + * @param formData 表单数据 + * @param responseCallBack 响应回调 + */ + fun putAsyncForm( + url: String, + formData: Map, + clazz: Class<*>?, + responseCallBack: ResponseCallBack + ) { + val client = OkHttpClient().newBuilder().connectTimeout( + 30, + TimeUnit.SECONDS + ) // callTimeout(30, TimeUnit.SECONDS) + .build() + val builder = FormBody.Builder() + val showData = StringBuffer() + for (key in formData.keys) { + builder.add(key, formData[key]) + showData.append(" " + key + ":" + formData[key]) + } + val formBody = builder + .build() + val request = + Request.Builder() // .addHeader("Cookie","token="+ HttpUrlVo.TOKEN) + .url(url) + .put(formBody) + .build() + Log.i(TAG, "开始发送请求:请求地址【$url】,请求参数==>$showData") + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.e(TAG, "响应失败===》" + e.message) + handler.post { responseCallBack.error(e.message) } + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + val respBody = response.body()!!.string() + Log.i(TAG, "响应成功===》$respBody") + handler.post { + try { +// responseCallBack.success(respBody); + val resultBean = + util.jsonParser(respBody, BaseBean::class.java) as BaseBean<*> + if (resultBean.code == "0") { + responseCallBack.success(respBody, resultBean) + } else { + responseCallBack.error(respBody) + ToastUtil.showToast(resultBean.getMessage()) + } + } catch (e: JSONException) { + Log.i("网络okhttp", "程序出现异常:" + e.message) + } + } + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/net/ResponseCallBack.java b/app/src/main/java/com/qidian/zhongkesmart/net/ResponseCallBack.java new file mode 100644 index 0000000..f3e3442 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/net/ResponseCallBack.java @@ -0,0 +1,12 @@ +package com.qidian.zhongkesmart.net; +import org.json.JSONException; + +/** + * 自定义回调 + */ +public interface ResponseCallBack { + + void success(String json,Object object) throws JSONException; + + void error(String json); +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/presenter/BasePresenter.kt b/app/src/main/java/com/qidian/zhongkesmart/presenter/BasePresenter.kt new file mode 100644 index 0000000..8593f5b --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/presenter/BasePresenter.kt @@ -0,0 +1,40 @@ +package com.qidian.government_client.ui.base.presenter + +import com.qidian.zjlw.presenter.IView +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable + +/** + * Create by Lee5ins on 2019/5/18 + */ +open class BasePresenter : IPresenter { + + var mRootView: V? = null + + private var compositeDisposable = CompositeDisposable() + + override fun attachView(view: V) { + this.mRootView = view + } + + override fun detachView() { + mRootView = null + //保证activity结束时取消所有正在执行的订阅 + if (!compositeDisposable.isDisposed) { + compositeDisposable.clear() + } + } + + private val isViewAttached: Boolean + get() = mRootView != null + + fun checkViewAttached() { + if (!isViewAttached) throw MvpViewNotAttachedException() + } + + fun addSubscription(disposable: Disposable) { + compositeDisposable.add(disposable) + } + + private class MvpViewNotAttachedException internal constructor() : RuntimeException("Please call IPresenter.attachView(IBaseView) before" + " requesting data to the IPresenter") +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/presenter/Contract.kt b/app/src/main/java/com/qidian/zhongkesmart/presenter/Contract.kt new file mode 100644 index 0000000..28baa49 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/presenter/Contract.kt @@ -0,0 +1,112 @@ +package com.qidian.zjlw.presenter + +import android.content.Context + +interface Contract { + + interface GovernmentView : IView { + fun showChangeResult( + messModel: Any?, mac: String, + type: String?, + locationName: String? + ) + + fun showAddResult( + messModel: Any?, mac: String, + type: String?, + locationName: String? + ) + + fun showRemoveResult(messModel: Any?, mac: String) + fun showTokenResult(messModel: Any?) + fun showAfter() + } + + interface SetView : IView { + fun showChangeResult( + messModel: Any?, mac: String, + type: String?, + locationName: String? + ) + + fun showRemoveResult(messModel: Any?, mac: String) + fun showAfter() + fun showResetResule(messModel: Any?) + fun showResetFail(message:String) + } + + + /** + * 首页 + */ + interface MainPagePresenter { + fun getbindDevice(context: Context, mac: String, type: String, locationName: String) + fun removebindDevice(context: Context, mac: String) + fun getHttpCode(context: Context) + fun getHttpLogin(context: Context, mCode: String) + fun getChangeDevice(context: Context, mac: String, roomName: String?, type: String?) + fun pushNewData( + context: Context, + mapStr: String, + ) +/* + fun pushNewData( + context: Context, + deviceMac: String, + identifyType: String, + deviceType: String?, + roomName: String?, + devicePower: String?, + timeLength: Int?, + open: String?, + ) +*/ + + fun pushOffData( + context: Context, + deviceMac: String, + /* identifyType: String, + deviceType: String?, + roomName: String?, + devicePower: String?, + timeLength: Int?,*/ + ) + + fun pushAlarmData( + context: Context, + deviceMac: String, + /* identifyType: String, + deviceType: String?, + roomName: String?, + devicePower: String?, + timeLength: Int?, + warnType: String?,*/ + ) + + fun heartbeat( + context: Context + ) + } + + /** + * 我的设备 + */ + interface MyEquipmentPagePresenter { + fun getbindDevice(context: Context, mac: String, type: String, locationName: String) + fun removebindDevice(context: Context, mac: String) + fun getHttpCode(context: Context) + fun getHttpLogin(context: Context, mCode: String) + fun getChangeDevice(context: Context, mac: String, roomName: String?, type: String?) + } + + /** + * 设置 + */ + interface SetPagePresenter { + fun removebindDevice(context: Context, mac: String) + fun resetApp(context: Context) + fun getChangeDevice(context: Context, mac: String, roomName: String?, type: String?) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/presenter/IPresenter.kt b/app/src/main/java/com/qidian/zhongkesmart/presenter/IPresenter.kt new file mode 100644 index 0000000..abc2e23 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/presenter/IPresenter.kt @@ -0,0 +1,15 @@ +package com.qidian.government_client.ui.base.presenter + +import com.qidian.zjlw.presenter.IView + +/** + * Create by Lee5ins + * on 2019/5/17 + */ +interface IPresenter { + + fun attachView(view: V) + + fun detachView() + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/presenter/IView.kt b/app/src/main/java/com/qidian/zhongkesmart/presenter/IView.kt new file mode 100644 index 0000000..1beaf18 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/presenter/IView.kt @@ -0,0 +1,8 @@ +package com.qidian.zjlw.presenter + +/** + * Create by Lee5ins + * on 2019/5/17 + */ +interface IView { +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/presenter/MainPresenter.kt b/app/src/main/java/com/qidian/zhongkesmart/presenter/MainPresenter.kt new file mode 100644 index 0000000..f20edbb --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/presenter/MainPresenter.kt @@ -0,0 +1,245 @@ +package com.qidian.zjlw.presenter + +import android.content.Context +import android.util.Log +import android.view.View +import com.qidian.government_client.ui.base.presenter.BasePresenter +import com.qidian.zhongkesmart.dialog.FindNewEquipmentDialog +import com.qidian.zhongkesmart.model.BindBlueToothData +import com.qidian.zhongkesmart.model.GetCodeM +import com.qidian.zhongkesmart.model.GetTokenM +import com.qidian.zhongkesmart.net.ApiUrl +import com.qidian.zhongkesmart.net.HttpCallBack +import com.qidian.zhongkesmart.net.HttpRequest +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.layout_list.* + +/** + * 首页页面接口 + */ +class MainPresenter : BasePresenter(), + Contract.MainPagePresenter { + + //新增绑定 + override fun getbindDevice( + context: Context, + mac: String, + roomName: String, + type: String + ) { + checkViewAttached() + var map = HashMap() + var mapStr = DataUtils.getBindDataStr(context, mac, roomName, ToolUtil.getTJTypeCode(type)) + HttpRequest.init(context).postJson(ApiUrl.BindTiePian, map, mapStr,true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showAddResult( + `object`, mac, + type, + roomName + ) + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("$code $info") + return super.failed(code, info) + } + }) + } + + //解除绑定 + override fun removebindDevice(context: Context, mac: String) { + checkViewAttached() + var map = HashMap() +// map["gatewayMacs"] = "5c39250bb12730e6" + map["gatewayId"] = DeviceUuidFactory.getAndroidID(context) + map["deviceMacs"] = mac + + HttpRequest.init(context).DeleteJson(ApiUrl.RemoveBindTiePian) + .setClazz(GetCodeM::class.java) + .setMap(map) +// .setModel(HttpRequest.CONTENT_TYPE_FORM_DATA) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showRemoveResult(`object`, mac) + } + }) + } + + override fun getHttpCode(context: Context) { + checkViewAttached() + Log.i("UUID==", DeviceUuidFactory.getAndroidID(context)) + var map = HashMap() + map["id"] = DeviceUuidFactory.getAndroidID(context) + HttpRequest.init(context).get(ApiUrl.getCode) + .setMap(map) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("响应数据", message!!) + var mCode = `object`.toString() + getHttpLogin(context, mCode) + } + + }) + + } + + override fun getHttpLogin(context: Context, mCode: String) { + checkViewAttached() + var map = HashMap() + map["code"] = mCode + map["id"] = DeviceUuidFactory.getAndroidID(context) + HttpRequest.init(context).postJson(ApiUrl.getLogin, map, "",false) + .setClazz(GetTokenM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showTokenResult(`object`) + } + + override fun failed(code: String?, info: String?): Boolean { + ToastUtil.showToast("您还未绑定网关") + return super.failed(code, info) + } + }) + } + + //修改贴件信息 + override fun getChangeDevice(context: Context, mac: String, roomName: String?, type: String?) { + var map = java.util.HashMap() + map["deviceMac"] = mac + map["typeCode"] = ToolUtil.getTJTypeCode(type!!) + map["roomName"] = roomName!! + HttpRequest.init(context).put(ApiUrl.BindTiePian, map) + .setClazz(GetCodeM::class.java) +// .setMap(map) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showChangeResult(`object`, mac, roomName, type) + } + + }) + } + + /** + * 数据推送 + */ + //新增贴片信息表-数据 (包括位移型或震动型,状态改变时触发-开关或设备重新连接等) + override fun pushNewData( + context: Context, + mapStr: String, + ) { + checkViewAttached() + var map = HashMap() + HttpRequest.init(context).postJson(ApiUrl.pushNewGetData, map, mapStr, true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { +// ToastUtil.showToast("数据推送成功") + Log.i("数据推送接口==", "开启/关闭") + } + }) + } + + //离线 + override fun pushOffData( + context: Context, + mapStr: String, + /* identifyType: String, + deviceType: String?, + roomName: String?, + devicePower: String?, + timeLength: Int?,*/ + ) { + checkViewAttached() + var map = HashMap() + /*map["deviceMac"] = deviceMac + //识别类型(VB:震动型: DP:位移型) + map["identifyType"] = identifyType + //贴片类型(DEVICE_CATEGORY_1等) + map["deviceType"] = deviceType.toString() + map["roomName"] = roomName.toString() + map["devicePower"] = devicePower.toString() + //是否离线 0:离线;1:在线(固定值1) + map["deviceOnLine"] = "1" + //数据发送时间(网关)yyyy-MM-dd HH:mm:ss + map["sendDate"] = TimeUtil.getNowTime().toString() + //报警code (固定值DEVICE_WARN_2) + map["warnType"] = "DEVICE_WARN_2"*/ + HttpRequest.init(context).postJson(ApiUrl.pushOffData, map, mapStr,true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { +// ToastUtil.showToast("离线数据推送成功") + } + }) + } + + //报警 (包含长时间未关闭、电量低等类型报警时触发) + override fun pushAlarmData( + context: Context, + mapStr: String, + /* identifyType: String, + deviceType: String?, + roomName: String?, + devicePower: String?, + timeLength: Int?, + warnType: String?,*/ + ) { + checkViewAttached() + var map = HashMap() + /* map["deviceMac"] = deviceMac + //识别类型(VB:震动型: DP:位移型) + map["identifyType"] = identifyType + //贴片类型(DEVICE_CATEGORY_1等) + map["deviceType"] = deviceType.toString() + map["roomName"] = roomName.toString() + map["devicePower"] = devicePower.toString() + //是否离线 0:离线;1:在线(固定值1) + map["deviceOnLine"] = "1" + //贴片状态 0:关;1:开 + map["deviceStatus"] = "1" + //数据发送时间(网关)yyyy-MM-dd HH:mm:ss + map["sendDate"] = TimeUtil.getNowTime().toString() + //时长(秒 当前开或关了多久) + map["timeLength"] = timeLength.toString() + //报警code(DEVICE_WARN_3/ DEVICE_WARN_4) + map["warnType"] = warnType.toString()*/ + HttpRequest.init(context).postJson(ApiUrl.pushWarnData, map, mapStr,true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .setShowDialog(false) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { +// ToastUtil.showToast("报警数据推送成功") + Log.i("数据推送接口==", "报警") + } + }) + } + + //网关心跳 + override fun heartbeat( + context: Context, + ) { + checkViewAttached() + var map = HashMap() + HttpRequest.init(context).post(ApiUrl.heartbeat) + .setShowDialog(false) +// .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.i("网关心跳==", "success") + } + + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/presenter/MyEquipmentPresenter.kt b/app/src/main/java/com/qidian/zhongkesmart/presenter/MyEquipmentPresenter.kt new file mode 100644 index 0000000..d570880 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/presenter/MyEquipmentPresenter.kt @@ -0,0 +1,130 @@ +package com.qidian.zjlw.presenter + +import android.content.Context +import android.util.Log +import android.view.View +import com.qidian.government_client.ui.base.presenter.BasePresenter +import com.qidian.zhongkesmart.dialog.FindNewEquipmentDialog +import com.qidian.zhongkesmart.model.BindBlueToothData +import com.qidian.zhongkesmart.model.GetCodeM +import com.qidian.zhongkesmart.model.GetTokenM +import com.qidian.zhongkesmart.net.ApiUrl +import com.qidian.zhongkesmart.net.HttpCallBack +import com.qidian.zhongkesmart.net.HttpRequest +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.layout_list.* + +/** + * 我的设备页面接口 + */ +class MyEquipmentPresenter : BasePresenter(), + Contract.MyEquipmentPagePresenter { + + //新增绑定 + override fun getbindDevice( + context: Context, + mac: String, + roomName: String, + type: String + ) { + checkViewAttached() + var map = HashMap() + /* map["gatewayId"] = DeviceUuidFactory.getAndroidID(context) + map["deviceMac"] = ToolUtil.formatMac(mac) + map["roomName"] = roomName!! + map["typeCode"] =ToolUtil.getTJTypeCode(type!!) + Log.i("okGo", "type==${type},roomName==" + roomName)*/ + var mapStr = DataUtils.getBindDataStr(context, mac, roomName, ToolUtil.getTJTypeCode(type!!)) + HttpRequest.init(context).postJson(ApiUrl.BindTiePian, map, mapStr,true) + .setClazz(GetCodeM::class.java) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showAddResult( + `object`, mac, + type, + roomName + ) + + } + }) + } + + //解除绑定 + override fun removebindDevice(context: Context, mac: String) { + checkViewAttached() + var map = HashMap() +// map["gatewayMacs"] = "5c39250bb12730e6" + map["gatewayId"] = DeviceUuidFactory.getAndroidID(context) + map["deviceMacs"] = mac + + HttpRequest.init(context).DeleteJson(ApiUrl.RemoveBindTiePian) + .setClazz(GetCodeM::class.java) + .setMap(map) +// .setModel(HttpRequest.CONTENT_TYPE_FORM_DATA) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showRemoveResult(`object`,mac) + } + }) + } + + override fun getHttpCode(context: Context) { + checkViewAttached() + Log.i("UUID==", DeviceUuidFactory.getAndroidID(context)) + var map = HashMap() + map["id"] = DeviceUuidFactory.getAndroidID(context) + HttpRequest.init(context).get(ApiUrl.getCode) + .setMap(map) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + Log.e("响应数据", message!!) + var mCode = `object`.toString() + getHttpLogin(context,mCode) + } + + }) + + } + + override fun getHttpLogin(context: Context,mCode:String) { + checkViewAttached() + var map = HashMap() + map["code"] = mCode + map["id"] = DeviceUuidFactory.getAndroidID(context) + HttpRequest.init(context).postJson(ApiUrl.getLogin, map, "",false) + .setClazz(GetTokenM::class.java) + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showTokenResult(`object`) + } + + override fun failed(code: String?, info: String?): Boolean { + return super.failed(code, info) + ToastUtil.showToast(code) + } + }) + } + + //修改贴件信息 + override fun getChangeDevice(context: Context,mac:String,roomName:String?,type:String?) { + var map = java.util.HashMap() + map["gatewayId"] = DeviceUuidFactory.getAndroidID(context) + map["patchs"] = mac + map["typeCode"] =ToolUtil.getTJTypeCode(type!!) + map["roomName"] = roomName!! + HttpRequest.init(context).put(ApiUrl.BindTiePian, map) + .setClazz(GetCodeM::class.java) +// .setMap(map) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showChangeResult(`object`,mac,roomName,type) + } + + }) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/presenter/SetPresenter.kt b/app/src/main/java/com/qidian/zhongkesmart/presenter/SetPresenter.kt new file mode 100644 index 0000000..4fd24ea --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/presenter/SetPresenter.kt @@ -0,0 +1,91 @@ +package com.qidian.zjlw.presenter + +import android.content.Context +import android.util.Log +import android.view.View +import com.qidian.government_client.ui.base.presenter.BasePresenter +import com.qidian.zhongkesmart.dialog.FindNewEquipmentDialog +import com.qidian.zhongkesmart.model.BindBlueToothData +import com.qidian.zhongkesmart.model.GetCodeM +import com.qidian.zhongkesmart.model.GetTokenM +import com.qidian.zhongkesmart.net.ApiUrl +import com.qidian.zhongkesmart.net.HttpCallBack +import com.qidian.zhongkesmart.net.HttpRequest +import com.qidian.zhongkesmart.utils.* +import kotlinx.android.synthetic.main.empty_layout.* +import kotlinx.android.synthetic.main.layout_list.* + +/** + * 设置页面接口 + */ +class SetPresenter : BasePresenter(), + Contract.SetPagePresenter { + + + //解除绑定 + override fun removebindDevice(context: Context, mac: String) { + checkViewAttached() + var map = HashMap() +// map["gatewayMacs"] = "5c39250bb12730e6" + map["gatewayId"] = DeviceUuidFactory.getAndroidID(context) + map["deviceMacs"] = mac + + HttpRequest.init(context).DeleteJson(ApiUrl.RemoveBindTiePian) + .setClazz(GetCodeM::class.java) + .setMap(map) +// .setModel(HttpRequest.CONTENT_TYPE_FORM_DATA) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showRemoveResult(`object`,mac) + } + }) + } + + + //修改贴件信息 + override fun getChangeDevice(context: Context,mac:String,roomName:String?,type:String?) { + var map = java.util.HashMap() + map["deviceMac"] = mac + map["typeCode"] = ToolUtil.getTJTypeCode(type!!) + map["roomName"] = roomName!! + HttpRequest.init(context).put(ApiUrl.BindTiePian, map) + .setClazz(GetCodeM::class.java) +// .setMap(map) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showChangeResult(`object`,mac,roomName,type) + } + + override fun after() { + super.after() + mRootView?.showAfter() + } + + }) + + } + + //重置网关 + override fun resetApp(context: Context) { + var map = java.util.HashMap() + map["id"] = DeviceUuidFactory.getAndroidID(context) + HttpRequest.init(context).DeleteJson(ApiUrl.resetApp) +// .setClazz(GetCodeM::class.java) + .setMap(map) +// .setModel(HttpRequest.CONTENT_TYPE_FORM_DATA) + .setAuthHeaders() + .excute(object : HttpCallBack() { + override fun success(message: String?, `object`: Any?) { + mRootView?.showResetResule(`object`) + } + + override fun failed(code: String?, info: String?): Boolean { + mRootView?.showResetFail(code!!) + return super.failed(code, info) + } + }) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/receiver/NetworkChange.java b/app/src/main/java/com/qidian/zhongkesmart/receiver/NetworkChange.java new file mode 100644 index 0000000..324d439 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/receiver/NetworkChange.java @@ -0,0 +1,62 @@ +package com.qidian.zhongkesmart.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.util.Log; + + +public class NetworkChange { + public static final String TAG = "NetworkChange"; + private static boolean isRegister = false; + private static NetStateChangeObserver listener; + private static BroadcastReceiver Receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); + if (null != activeNetwork) { + if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) { + if (listener != null) listener.onWifiConnect(); + } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) { + if (listener != null) listener.onMobileConnect(); + } else { + if (listener != null) listener.onDisconnect(); + } + } else { + if (listener != null) listener.onDisconnect(); + } + } + } + }; + + public static void registerReceiver(Context context, NetStateChangeObserver lis) { + listener = lis; + IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + context.getApplicationContext().registerReceiver(Receiver, intentFilter); + isRegister = true; + } + + public static void unRegisterReceiver(Context context) { + try { + if (isRegister) { + context.getApplicationContext().unregisterReceiver(Receiver); + } + } catch (Exception e) { + Log.e(TAG, "网络注册监听——解除监听报错" + e.toString()); + } + } + + public interface NetStateChangeObserver { + + void onDisconnect(); + + void onMobileConnect(); + + void onWifiConnect(); + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/receiver/WifiReceiver.kt b/app/src/main/java/com/qidian/zhongkesmart/receiver/WifiReceiver.kt new file mode 100644 index 0000000..687c0f9 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/receiver/WifiReceiver.kt @@ -0,0 +1,85 @@ +package com.qidian.zhongkesmart.receiver + +import android.R.attr +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.net.NetworkInfo +import android.net.wifi.ScanResult +import android.net.wifi.WifiManager +import android.util.Log +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.utils.EventBusUtil +import android.R.attr.action +import com.qidian.zhongkesmart.utils.DataServer +import com.qidian.zhongkesmart.utils.ToolUtil + + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2022/6/21 0021 17:22 + */ +class WifiReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == WifiManager.RSSI_CHANGED_ACTION) { + Log.i(TAG, "wifi信号强度变化") + } + //wifi连接上与否 + if (intent.action == WifiManager.NETWORK_STATE_CHANGED_ACTION) { + val info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO) + if (info!!.state == NetworkInfo.State.DISCONNECTED) { + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.WIFI_CONFIRM_OFF)) + DataServer.mWiFiName="" + Log.i(TAG, "wifi断开DataServer.mWiFiName=="+DataServer.mWiFiName) + } else if (info.state == NetworkInfo.State.CONNECTED) { + val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager + val wifiInfo = wifiManager.connectionInfo + //获取当前wifi名称 + DataServer.mWiFiName=ToolUtil.getWifiName(wifiInfo.ssid).toString() + Log.i(TAG, "连接到网络 DataServer.mWiFiName==" + DataServer.mWiFiName) + EventBusUtil.sendEvent( + EventBusUtil.MessageEvent( + EventCode.WIFI_CONFIRM_CONNECT, + ToolUtil.getWifiName(wifiInfo.ssid) + ) + ) + } + } + //wifi打开与否 + if (intent.action == WifiManager.WIFI_STATE_CHANGED_ACTION) { + val wifistate = + intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED) + if (wifistate == WifiManager.WIFI_STATE_DISABLED) { + Log.i(TAG, "系统关闭wifi") + DataServer.mWiFiName="" + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.WIFI_CONFIRM_OFF)) + } else if (wifistate == WifiManager.WIFI_STATE_ENABLED) { + Log.i(TAG, "系统开启wifi") + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.WIFI_CONFIRM_OPEN)) + } + } + //wifi列表 + if(intent.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION){ + val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager + val results: List = wifiManager.scanResults + if (results != null) { + Log.d(TAG, "WiFi==" + "results size:"+results.toString()) + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.WIFI_CONFIRM_DATA,results)) + } + } + if (intent.action==WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) { + /*Log.i("WiFi==","wifi密码错误广播1") + val linkWifiResult = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 123) + if (linkWifiResult == WifiManager.ERROR_AUTHENTICATING) { + Log.i("WiFi==","wifi密码错误广播") + }*/ + } + } + + companion object { + private const val TAG = "wifiReceiver" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/receiver/WinCallback.java b/app/src/main/java/com/qidian/zhongkesmart/receiver/WinCallback.java new file mode 100644 index 0000000..6e9d983 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/receiver/WinCallback.java @@ -0,0 +1,150 @@ +package com.qidian.zhongkesmart.receiver; + +import android.annotation.TargetApi; +import android.os.Build; +import android.view.ActionMode; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.SearchEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; + +import androidx.annotation.Nullable; + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2022/8/18 0018 15:04 + */ +public class WinCallback implements Window.Callback { + Window.Callback callback; + + public WinCallback(Window.Callback callback) { + this.callback = callback; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return callback.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + return callback.dispatchKeyShortcutEvent(event); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + return callback.dispatchTouchEvent(event); + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent event) { + return callback.dispatchTrackballEvent(event); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) + @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + return callback.dispatchGenericMotionEvent(event); + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + return callback.dispatchPopulateAccessibilityEvent(event); + + } + + @Override + public View onCreatePanelView(int featureId) { + return callback.onCreatePanelView(featureId); + } + + @Override + public boolean onCreatePanelMenu(int featureId, Menu menu) { + return callback.onCreatePanelMenu(featureId, menu); + } + + @Override + public boolean onPreparePanel(int featureId, View view, Menu menu) { + return callback.onPreparePanel(featureId, view, menu); + } + + @Override + public boolean onMenuOpened(int featureId, Menu menu) { + return callback.onMenuOpened(featureId, menu); + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + return callback.onMenuItemSelected(featureId, item); + } + + @Override + public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) { + callback.onWindowAttributesChanged(attrs); + } + + @Override + public void onContentChanged() { + callback.onContentChanged(); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + callback.onWindowFocusChanged(hasFocus); + } + + @Override + public void onAttachedToWindow() { + callback.onAttachedToWindow(); + } + + @Override + public void onDetachedFromWindow() { + callback.onDetachedFromWindow(); + } + + @Override + public void onPanelClosed(int featureId, Menu menu) { + callback.onPanelClosed(featureId, menu); + } + + @Override + public boolean onSearchRequested() { + return callback.onSearchRequested(); + } + + @Override + public boolean onSearchRequested(SearchEvent searchEvent) { + return false; + } + + @Override + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { + return this.callback.onWindowStartingActionMode(callback); + } + + @Nullable + @Override + public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) { + return null; + } + + @Override + public void onActionModeStarted(ActionMode mode) { + callback.onActionModeStarted(mode); + } + + @Override + public void onActionModeFinished(ActionMode mode) { + callback.onActionModeFinished(mode); + } +} + diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/ActivityManager.java b/app/src/main/java/com/qidian/zhongkesmart/utils/ActivityManager.java new file mode 100644 index 0000000..2679c44 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/ActivityManager.java @@ -0,0 +1,189 @@ +package com.qidian.zhongkesmart.utils; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.os.Build; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.RequiresApi; + + +import com.qidian.zhongkesmart.base.BaseActivity; + +import java.util.List; +import java.util.Stack; + +public class ActivityManager { + private static Stack activityStack; + private static ActivityManager instance; + + private ActivityManager() { + } + + /** + * 单实例 , UI无需考虑多线程同步问题 + */ + public static ActivityManager getAppManager() { + if (instance == null) { + instance = new ActivityManager(); + } + return instance; + } + + /** + * 添加Activity到栈 + */ + public void addActivity(BaseActivity activity) { + if (activityStack == null) { + activityStack = new Stack(); + } + activityStack.add(activity); + } + + /** + * 获取当前Activity(栈顶Activity) + */ + public BaseActivity currentActivity() { + if (activityStack == null || activityStack.isEmpty()) { + return null; + } + BaseActivity activity = activityStack.lastElement(); + return activity; + } + + /** + * 获取当前Activity(栈顶Activity) 没有找到则返回null + */ + public BaseActivity findActivity(Class cls) { + BaseActivity activity = null; + for (BaseActivity aty : activityStack) { + if (aty.getClass().equals(cls)) { + activity = aty; + break; + } + } + return activity; + } + + /** + * 获取当前栈内activity + */ + public void getAllTaskActivity() { + for (BaseActivity aty : activityStack) { + Log.d("activity=====", aty.getComponentName().getClassName()); + } + } + + /** + * 结束当前Activity(栈顶Activity) + */ + public void finishActivity() { + BaseActivity activity = activityStack.lastElement(); + finishActivity(activity); + } + + /** + * 结束指定的Activity(重载) + */ + public void finishActivity(Activity activity) { + if (activity != null) { + // 为与系统Activity栈保持一致,且考虑到手机设置项里的"不保留活动"选项引起的Activity生命周期调用onDestroy()方法所带来的问题,此处需要作出如下修正 + if (!activity.isFinishing()) { + Log.e("===finish",activity.getComponentName().getClassName()); + activity.finish(); + } + activityStack.remove(activity); + activity = null; + } + + } + + /** + * 结束指定的Activity(重载) + */ + public void finishActivity(Class cls) { + for (int i = 0; i < activityStack.size(); i++) { + BaseActivity activity = activityStack.get(i); + if (activity.getClass().equals(cls)) { + finishActivity(activity); + } + } + } + + /** + * 结束指定的某些activity + * @param clss + */ + public void finishActivtys(Class... clss){ + if(clss == null)return; + for (Class clz : clss){ + finishActivity(clz); + } + } + + /** + * 关闭除了指定activity以外的全部activity 如果cls不存在于栈中,则栈全部清空 + * + * @param cls + */ + public void finishOthersActivity(Class cls) { + while (activityStack != null && activityStack.size() > 0){ + BaseActivity activity = activityStack.lastElement(); + if (!(activity.getClass().equals(cls))) { + finishActivity(activity); + } + } + } + + /** + * 结束所有Activity + */ + public void finishAllActivity() { + for (int i = 0, size = activityStack.size(); i < size; i++) { + if (null != activityStack.get(i)) { + activityStack.get(i).finish(); + } + } + activityStack.clear(); + } + + /** + * 判断某个界面是否在前台 + * + * @param activity 要判断的Activity + * @return 是否在前台显示 + */ + @RequiresApi(api = Build.VERSION_CODES.Q) + public boolean isForeground(Activity activity) { +// Log.d("acc--1",activity.getClass().getName()); +// if(activity.getClass().getName().equals(currentActivity().getComponentName().getClassName())) +// return true; +// +// return false; + return isForeground(activity, activity.getClass().getName()); + } + + /** + * 判断某个界面是否在前台 + * + * @param context Context + * @param className 界面的类名 + * @return 是否在前台显示 + */ + @RequiresApi(api = Build.VERSION_CODES.Q) + public boolean isForeground(Context context, String className) { + if (context == null || TextUtils.isEmpty(className)) + return false; + android.app.ActivityManager am = (android.app.ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List list = am.getRunningTasks(1); + if (list != null && list.size() > 0) { + ComponentName cpn = list.get(0).topActivity; + if (className.equals(cpn.getClassName())) { + return true; + } + } + return false; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/BlueToothConnectUtil.java b/app/src/main/java/com/qidian/zhongkesmart/utils/BlueToothConnectUtil.java new file mode 100644 index 0000000..42db364 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/BlueToothConnectUtil.java @@ -0,0 +1,56 @@ +package com.qidian.zhongkesmart.utils; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.text.TextUtils; +import android.util.Log; + +import java.lang.reflect.Method; +import java.util.Set; + +public class BlueToothConnectUtil { + + /** + * 判断给定的设备mac地址是否已连接经典蓝牙 + * + * @param macAddress 设备mac地址,例如"78:02:B7:01:01:16" + * @return 1C6BCE1526B5 + */ + public static boolean isConnectClassicBT(String macAddress) { + if (TextUtils.isEmpty(macAddress)) { + return false; + } + final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + Class bluetoothAdapterClass = BluetoothAdapter.class;//得到BluetoothAdapter的Class对象 + try { + //是否存在连接的蓝牙设备 + Method method = bluetoothAdapterClass.getDeclaredMethod("getConnectionState", (Class[]) null); + //打开权限 + method.setAccessible(true); + int state = (int) method.invoke(bluetoothAdapter, (Object[]) null); + if (state == BluetoothAdapter.STATE_CONNECTED) { + Log.d("test", "BluetoothAdapter.STATE_CONNECTED"); + Set devices = bluetoothAdapter.getBondedDevices(); + Log.d("test", "devices:" + devices.size()); + for (BluetoothDevice device : devices) { + Method isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected", (Class[]) null); + method.setAccessible(true); + boolean isConnected = (boolean) isConnectedMethod.invoke(device, (Object[]) null); + if (isConnected) { + { + return macAddress.contains(device.getAddress()); + } + } else { + Log.d("test", device.getName() + " connect false(" + device.getAddress() + ")"); + } + } + + } + return false; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/BlueToothUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/BlueToothUtils.kt new file mode 100644 index 0000000..6be690d --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/BlueToothUtils.kt @@ -0,0 +1,574 @@ +package com.qidian.zhongkesmart.utils + +import android.Manifest +import android.app.Activity +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothSocket +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.text.TextUtils +import android.util.Log +import android.widget.Toast +import com.github.mikephil.charting.data.Entry +import com.google.gson.Gson +import com.qidian.baseble.model.BluetoothLeDevice +import com.qidian.baseble.utils.HexUtil +import com.qidian.zhongkesmart.config.EventCode +import com.qidian.zhongkesmart.model.ActionInfo +import com.qidian.zhongkesmart.model.BlueToothData +import com.qidian.zhongkesmart.model.PushData +import com.qidian.zhongkesmart.model.TimeData +import pub.devrel.easypermissions.EasyPermissions +import java.lang.reflect.Method +import java.net.NetworkInterface +import java.util.* +import kotlin.collections.ArrayList + + +/** + * @Description: + * @QQ : 蓝牙工具类 + * @Author : 文阿花 + * @Email : + * @Date : 2022/7/14 0014 15:06 + */ +class BlueToothUtils { + var bluetoothmanger: BluetoothManager? = null + var bluetoothadapter: BluetoothAdapter? = null + private var context: Context? = null + + + companion object { + private var mBlueToothUtils: BlueToothUtils? = null + + @get:Synchronized + val instance: BlueToothUtils? + get() { + if (mBlueToothUtils == null) { + mBlueToothUtils = BlueToothUtils() + + } + return mBlueToothUtils + } + } + + /** + * 初始化 + * + * @param context 上下文 + */ + fun init(context: Context?) { + Log.i("BlueTooth==", "init") + if (this.context == null && context != null) { + this.context = context.applicationContext + bluetoothmanger = + context!!.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + bluetoothadapter = bluetoothmanger!!.adapter + } + } + + /** + * 判断设备是否支持蓝牙 + */ + fun IsSupportBlueTooth(): Boolean { + var isSupport = true + if (bluetoothadapter == null) { + isSupport = false + } + Log.i("BlueTooth==", "否支持蓝牙:${isSupport}") + return isSupport + } + + /** + * 判断蓝牙是否开启 + * + * @return + */ + fun IsOpenBlueTooth(): Boolean { + Log.i("BlueTooth==", "是否开启蓝牙:${bluetoothadapter!!.isEnabled}") + return bluetoothadapter!!.isEnabled + } + + /** + * 打开蓝牙连接设备 + */ + fun OpenBuleTooth(activity: Activity?) { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + //校验是否已具有模糊定位权限 + if (!hasPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION)) { + Log.i("BlueTooth==", "打开蓝牙:无权限") + //申请权限 + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.PERMISSION_ACCESS_COARSE_LOCATION)) + } else { + enableBluetooth(activity!!, DataServer.ACCESS_BlueTooth_CODE) + } + } else { + enableBluetooth(activity!!, DataServer.ACCESS_BlueTooth_CODE) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + /** + * 检查是否具有某种权限 + */ + private fun hasPermission(context: Context?, permission: String): Boolean { + return EasyPermissions.hasPermissions(context!!, permission) + } + + /** + * 打开蓝牙 + */ + fun enableBluetooth(activity: Activity, requestCode: Int) { + val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) + activity.startActivityForResult(intent, requestCode) + Log.i("BlueTooth==", "打开蓝牙") + } + + + /** + * 获取设备mac地址 + */ + fun getMac(): String? { + try { + val all: List = + Collections.list(NetworkInterface.getNetworkInterfaces()) + for (nif in all) { + if (!"wlan0".equals(nif.getName(), ignoreCase = true)) { + continue + } + val macBytes: ByteArray = nif.getHardwareAddress() + if (macBytes == null || macBytes.size == 0) { + continue + } + val result = StringBuilder() + for (b in macBytes) { +// result.append(b) + result.append(String.format("%02X", b)) + } + return result.toString() + } + } catch (x: java.lang.Exception) { + x.printStackTrace() + } + return "" + } + + + /** + * 获取搜索到的设备名称 + * 有的name为空 + */ + fun getName(device: BluetoothLeDevice): String { + var name = "" + if (device.name.isNullOrEmpty()) { + name = device.address + } else { + name = device.name + } + return name + } + + /** + * 处理蓝牙返回数据 + * (0X)02-4F-1E-59-03-BF-37-DC-68-34-75-C5-84 (0X)1F-FF-07-5D-1F-FF-33-DC-68-34-75-C5-84 + * x轴加速:1F-FF y轴加速:07-5D z轴加速:1F-FF + * 电量:33 + * 贴件mac:DC-68-34-75-C5-84 + * 蓝牙返回数据只展示已绑定贴件的信息 + * + * + * 数据推送节点 + * 1. 贴件新增数据时 + * 位移型:新增数据 + * 震动型:新增第一条数据 + * + * 识别类型(VB:震动型: DP:位移型) + * 009c036201dc37dc683475c584 + * + * 00000000050064ed19ea82a725 + * bdbd0000050064ed19ea82a725 + * Info[4]:OTA版本 05 + * Info[5]:节点检测动作(0xff为检测到,否则为0)00 + * Info[6]:电量 64 + * Info[7-12]:完整的mac ed19ea82a725 + */ +// fun getBlueToothData(info: String, context: Context?) { +// //数据推送-贴件新增数据时 数组容器 +// DataServer.listPushADD.clear() +// if (!info.isNullOrEmpty()) { +// var listTJ = ToolUtil.getBlueToothSingleMac(info) +// var mBindTJList = ToolUtil.getHistory(DataServer.mBindHistory, context!!) +// listTJ.forEach { +// if (it.isNotEmpty()) { +// var mac = getMacstr(it) +// //筛选已绑定的贴件信息 +// mBindTJList.forEach { itBind -> +// if (mac == itBind.mac) { +// //类型判断 +// if (ToolUtil.getTJTypeByMac(mac, context) == "DP") { +// Log.i("贴件类型==", "DP:位移型") +// //位移型 +// displacementData(it, context) +// } else if (ToolUtil.getTJTypeByMac(mac, context) == "VB") { +// Log.i("贴件类型==", "VB:震动型") +// //震动型处理-此处处理接收到数据-即开启状态信息更新 由于关闭涉及到定时 所以在MianActivity中的定时操作中处理关闭 +// vibrationData(it, context) +// } +// } +// } +// } +// } +// //推送 +// /* if(DataServer.listPushADD.isNotEmpty()){ +// EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET)) +// }*/ +// //获取本次接收到的数据 - 离线逻辑用 --每次都要同步到服务器 +// getBlueToothSingleData(info, context) +// } +// } + + /** + * 处理本次蓝牙返回数据 + * 024F1E5903BF37DC683475C5841FFF075D1FFF33DC683475C584 + * x轴加速:1F-FF y轴加速:07-5D z轴加速:1F-FF + * 电量:33 + * 贴件mac:DC-68-34-75-C5-84 + */ + fun getBlueToothSingleData(info: String, context: Context) { + DataServer.MSingleBlueToothData.clear() + var listTJ = ToolUtil.getBlueToothSingleMac(info) + listTJ.forEach { itlist -> + var mac = getMacstr(itlist) + DataServer.MBlueToothData.forEach { + if (mac == it.mac) { + DataServer.MSingleBlueToothData.add(it) + } + } + } + //处理数据 同步接口用 +// DataServer.MSingleBlueToothMap = getPushDataStr(context) + } + + /** + * 处理本次蓝牙返回数据 + * 使其满足同步接口样式 + * override fun pushNewData( + context: Context, + deviceMac: String, + identifyType: String, + deviceType: String?, + roomName: String?, + devicePower: String?, + timeLength: Int?, + open: String?, + ) + */ + fun getPushDataStr(context: Context): String { + var dataStr = "" + var list = ArrayList() + DataServer.MSingleBlueToothData.forEach { + var mac = it.mac + list.add(getPushDataBean(mac, context)) + } + dataStr = ToolUtil.getJson(list).toString() + return DataUtils.getSplitStr(dataStr) + } + + /** + * 处理本次蓝牙返回数据 + * 使其满足同步接口样式 + * 获取bean + */ + fun getPushDataBean(mac: String, context: Context): PushData { + //获取对应的蓝牙数据 + var model = BlueToothUtils.instance!!.getBlueToothDataModel(mac) + var tomeRecord = model.tomeRecord[model.tomeRecord.size - 1] + //获取 时长(秒 当前开或关了多久) + var timeLength = TimeUtil.getStateContinuousTime(mac) + //获取对应的贴件数据 + var tjModel = ToolUtil.getBindMessByMac(mac, context) + var bean = PushData( + mac, + model.type, + ToolUtil.getTJTypeCode(tjModel!!.type), + tjModel.name, + model.power, + "1", + ToolUtil.getOpenFormat(model.open), + TimeUtil.getNowTime().toString(), + timeLength.toString(), + "DEVICE_WARN_1" + ) + return bean + } + + + + /** + * 获取震动型的数据 + * 识别类型(VB:震动型: DP:位移型) + */ + fun getDisplacementData(): ArrayList { + var MBlueToothData = ArrayList() + DataServer.MBlueToothData.forEach { + if (it.type == "VB") { + MBlueToothData.add(it) + } + } + return MBlueToothData + } + + + /** + * 获取该次蓝牙推送数据的mac列表 + */ + fun getBlueToothMacData(info: String): ArrayList { + var listBlueToothData = ArrayList() + var listTJ = info.replace("-", "").split("(0X)") + listTJ.forEach { + var mac = getMacstr(it) + listBlueToothData.add(mac) + } + return listBlueToothData + } + + fun getMacstr(model: String): String { + return model.substring(14).toLowerCase() + } + + fun getPowerstr(model: String): String { + return model.substring(12, 14) + } + + /** + * 位移型处理方式:类型标记蓝牙返回 暂未返回 自定义位移型type=1 + *主要判断的是 三轴传感器的数值变化,在三轴传感器首次有值时认为该场景为开启状态(贴件首次安装均为关闭状态), + *再次有值时认为是关闭状态;贴件每次向网关发送数据时,网关需要查询该贴件的上次状态进行相反处理即可。 + *开启的持续时间为两次之间的差; + * 1FFF075D1FFF33DC683475C584 + * 记录数据: + * 1、开关状态 + * 2、打开时长 + * 3、打开次数+时间(我的设备详情里面需要展示) + * isOnline : 离线后只要再收到数据就是在线 + * + *识别类型(VB:震动型: DP:位移型) + * + * 1. 贴件新增数据时 + * 位移型:新增数据 + * 震动型:新增第一条数据 + * + * timelength变化规律:开启时候记录的是0,结束的时候是结束当前时间-该次操作的openTime 即记录每次开启的时间 + *isOpen、isShowAlarm、isOnLine 0关闭 1开启 + */ +// fun displacementData(model: String, context: Context?) { +// var mac = getMacstr(model) +// var power = getPowerstr(model) +// if (!isHavaReceived(mac)) {//第一次接受到值 默认开启 +// var timeBean = ArrayList() +// timeBean.add(TimeData(TimeUtil.getNowTimeLong()!!, 0L)) +// var bean = BlueToothData(mac, power, true, "DP", true, false, TimeUtil.getNowTimeLong()!!,timeBean) +// DataServer.MBlueToothData.add(bean) +// Log.i("数据推送==", "位移型:新增数据-开") +// } else {//判断是开启还是关闭 +// var model = getBlueToothDataModel(mac) +// if (model.open) {//状态:开启 进行关闭操作 +// var timeBean = model.tomeRecord +// var bean = timeBean[timeBean.size - 1] +// //更新最新一条的时间差 记录该次开启的时长 +// bean.timelength = TimeUtil.getTimeDifference(bean.openTime)!! +// //更新状态变化时间 +// bean.openTime = TimeUtil.getNowTimeLong()!! +// model.tomeRecord = timeBean +// model.open = false//修改为关闭状态 +// Log.i("数据推送==", "位移型:新增数据-关") +// +// //对位移型贴件来说,当状态改变为关闭时,检查是否有报警弹窗打开;如果有的话,关闭报警弹窗 +// if(DataServer.isShowAlarmMac(mac)){ +// DataServer.removeAlarmMac(mac) +// } +// } else {//状态:关闭 进行开启操作 +// var timeBean = model.tomeRecord +// timeBean.add(TimeData(TimeUtil.getNowTimeLong()!!, 0L)) +// model.tomeRecord = timeBean +// model.open = true//修改为开启状态 +// Log.i("数据推送==", "位移型:新增数据-开") +// } +// model.power = power +// model.showAlarm = false//状态改变后 就重置为默认状态 +// model.onLine = true +// } +// //更新绑定贴件的电量信息 +// ToolUtil.changeBindTjPower(mac, context!!) +// //更新 +// addBlueToothDataHistory() +// //该条数据需要推送 +// //数据推送-1. 位移型:新增数据 +//// Log.i("数据推送==", "位移型:新增数据") +//// DataServer.listPushADD.add(mac) +// EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET, mac)) +// } + + + /** + * 保存蓝牙接收数据 + */ + fun addBlueToothDataHistory() { + var json = ToolUtil.getJson(DataServer.MBlueToothData) +// Log.i("BlueTooth==", json!!) + //蓝牙返回数据-存储本地 + SPUtil.setValue(DataServer.MBlueToothLocalData, json) +// Log.i("接收蓝牙数据历史记录", SPUtil.getValue(DataServer.MBlueToothLocalData, String::class.java)) + //发送数据更新广播提示贴片展示信息更新 + EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.BLUETOOTHDATA_Refresh)) + } + + /** + * 震动型型处理方式:有震动才会推数据 类型标记蓝牙返回 暂未返回 + * 主要判断的是震动传感器的有值与否, + * 当震动传感器有值的时候认为该场景为开启状态, + * 当震动传感器X秒不在传值时认为该场景为关闭状态; + * 开启的持续时间为两次之间的差; + * 只要能接收到数据就是在线 + * + * 识别类型(VB:震动型: DP:位移型) + * + * 1. 贴件新增数据时 + * 位移型:新增数据 + * 震动型:新增第一条数据 状态由关到开也需要同步一次数据 + * + */ +// fun vibrationData(model: String, context: Context?) { +// var mac = getMacstr(model) +// var power = getPowerstr(model) +// if (!isHavaReceived(mac)) {//第一次接受到值 默认开启 +// var timeBean = ArrayList() +// timeBean.add( +// TimeData( +// TimeUtil.getNowTimeLong()!!, +// DataServer.mVibrationCloseTime.toLong() +// ) +// ) +// var bean = BlueToothData(mac, power, true, "VB", true, false,TimeUtil.getNowTimeLong()!!, timeBean) +// DataServer.MBlueToothData.add(bean) +// //数据推送-1.震动型:新增第一条数据 +// Log.i("数据推送==", "震动型:新增第一条数据${mac}") +// DataServer.listPushADD.add(mac) +// EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET, mac)) +// } else {//再次接收到即为开启 +// DataServer.MBlueToothData.forEach { +// if (it.mac == mac) { +// it.onLine = true +// it.power = power +// //更新接收最新一条蓝牙数据的时间 用于监听计算震动型关闭 +// it.lateDataOpenTime=TimeUtil.getNowTimeLong()!! +// if(!it.open){ +// it.showAlarm = false//状态改变后 就重置为默认状态 +//// Log.i("数据推送==", "震动型:关闭后开启") +// var timeBean = it.tomeRecord +// timeBean.add( +// TimeData( +// TimeUtil.getNowTimeLong()!!, +// DataServer.mVibrationCloseTime.toLong() +// ) +// ) +// it.tomeRecord = timeBean +// it.open = true +// //该条数据需要推送 +// Log.i("数据推送==", "震动型:状态由关到开") +//// DataServer.listPushADD.add(mac) +// EventBusUtil.sendEvent(EventBusUtil.MessageEvent(EventCode.PUSHDATA_NEWGET, mac)) +// } +// it.open = true +// +// } +// } +// } +// //更新绑定贴件的电量信息 +// ToolUtil.changeBindTjPower(mac, context!!) +// } + + fun isHavaReceived(mac: String): Boolean { + var isHave = false + DataServer.MBlueToothData.forEach { + if (it.mac == mac) { + isHave = true + } + } + return isHave + } + + fun getBlueToothDataModel(mac: String): BlueToothData { + var model: BlueToothData? = null + DataServer.MBlueToothData.forEach { + if (it.mac == mac) { + model = it + } + } + return model!! + } + + /** + * 绑定的贴件是否接收有的蓝牙数据 + */ + + fun isGetBlueToothData(mac: String): Boolean { + var ishave = false + DataServer.MBlueToothData.forEach { + if (it.mac == mac) { + ishave = true + } + } + return ishave + } + + + //获取电量 + fun getBlueToothPower(mac: String): String { + var power = "" + if (DataServer.MBlueToothData.size == 0) { + return "" + } + DataServer.MBlueToothData.forEach { + if (it.mac == mac) { + power = it.power + } + } + return power + } + + /** + * 设置所有贴件为离线 + */ + fun setAllOffLine() { + DataServer.MBlueToothData.forEach { + it.onLine = false + } + } + + + /** + * 解析动作数据 + * 0开 1关 -1无效 + */ + fun getActionType(data:ByteArray):Int{ + when (HexUtil.byteToInt(data[1])) { + 255 -> { + return 0 + } + 254 -> { + return 1 + } + } + return -1 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/ChartUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/ChartUtils.kt new file mode 100644 index 0000000..9f95765 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/ChartUtils.kt @@ -0,0 +1,555 @@ +package com.qidian.zhongkesmart.utils + +import android.graphics.Color +import android.graphics.DashPathEffect +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.animation.Easing.EaseInOutQuad +import com.github.mikephil.charting.charts.BarChart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.charts.PieChart +import com.github.mikephil.charting.components.AxisBase +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.data.* +import com.github.mikephil.charting.formatter.ValueFormatter + +/** + * Create by Lee5ins on 2019/5/21 + * 表格工具类(饼状图,柱状图设置) + */ +object ChartUtils { + + private const val groupSpace = 0.10f + private const val barSpace = 0.05f // x4 DataSet + private const val barWidth = 0.25f // x4 DataSet + // (0.25 + 0.05) * 3 + 0.10 = 1.00 -> interval per "group" + /** + * 饼状图设置 + * @param mChart 饼状图对象 + * @param pieDataSet 数据集合 + * @param colors 颜色集合 res中的color id + * @param isRefreshing 是否是刷新后显示 + */ + fun initPieChart( + mChart: PieChart, + pieDataSet: PieDataSet, + colors: ArrayList, + isEmpty: Boolean, + isWhite: Boolean + ) { + mChart.description.isEnabled = false// 不显示描述数据 + mChart.legend.isEnabled = false //不显示图例 + mChart.rotationAngle = 0f + mChart.holeRadius = 70f + mChart.isRotationEnabled = true//设置pieChart图表是否可以手动旋转 + mChart.setDrawEntryLabels(false)//设置pieChart是否饼图上显示文字(true:下面属性才有效果) + mChart.setUsePercentValues(false) + mChart.setDrawCenterText(true)//是否绘制PieChart内部中心文本 + + mChart.animateY(1400, Easing.EaseInOutQuad)// 设置pieChart图表展示动画效果 + if (!isEmpty) { + pieDataSet.selectionShift = 6f //设置饼状Item被选中时变化的距离 + pieDataSet.colors = colors//设置饼状Item颜色 + val pieData = PieData(pieDataSet) + pieData.setDrawValues(true) + pieData.setValueTextSize(10f) + pieData.setValueFormatter(object : ValueFormatter() { + override fun getFormattedValue(value: Float): String { + return if (value > 0) { + value.toInt().toString() + } else { + "" + } + } + }) + if (isWhite) { + pieData.setValueTextColor(Color.parseColor("#333333")) + mChart.setCenterTextColor(Color.parseColor("#333333")) + } else { + pieData.setValueTextColor(Color.parseColor("#FFFFFF")) + mChart.setCenterTextColor(Color.parseColor("#FFFFFF")) + } + mChart.data = pieData + } else { + //空数据 + val pieData = PieData(pieDataSet) + + pieData.setDrawValues(false) + pieDataSet.color = Color.parseColor("#D2D2D2") + mChart.data = pieData + mChart.centerText = "暂无数据" //设置中间文字 + if (isWhite) { + mChart.setCenterTextColor(Color.parseColor("#333333")) //中间问题的颜色 + } else { + mChart.setCenterTextColor(Color.parseColor("#BDBFCA")) //中间问题的颜色 + } + mChart.setCenterTextSizePixels(32f) //中间文字的大小px + } + mChart.invalidate() + } + + /** + * 柱状图设置 + * @param mChart 柱状图对象 + * @param xValues x轴数据集合 + * @param data 所有数据集合 + * @param isGroup 是否是多组 + * @param isRefreshing 是否是刷新后显示 + */ + fun initBarChart( + mChart: BarChart, + barSet: BarDataSet, + xValues: ArrayList, + data: BarData, + isGroup: Boolean, + isEmpty: Boolean, + + ) { + mChart.isDragEnabled = false// 设置是否可以用手指移动图表 + mChart.description.isEnabled = false// 不显示描述数据 + mChart.axisRight.isEnabled = false //是否显示右侧y轴 + mChart.isHighlightFullBarEnabled = true//是否整个柱高亮 + mChart.legend.isEnabled = false //是否显示图例 + val xAxis = mChart.xAxis + xAxis.setDrawAxisLine(true) // 是否显示x轴线 + xAxis.position = XAxis.XAxisPosition.BOTTOM // 设置x轴数据的位置 + xAxis.axisLineColor = Color.parseColor("#333333") // 设置x轴线的颜色 + + xAxis.setDrawGridLines(false) // 是否绘制垂直x方向网格线 + xAxis.textSize = 8f // 设置x轴文字的大小 + + xAxis.textColor = Color.parseColor("#333333") + xAxis.setDrawLabels(true) + xAxis.granularity = 1f//粒度 + if (xValues.isNotEmpty()) { + //设置x轴文字 + xAxis.valueFormatter = object : ValueFormatter() { + override fun getFormattedValue(value: Float): String? { + val index = value.toInt() + return if (index >= 0 && index < xValues.size) { + xValues[value.toInt()] + } else { + "" + } + } + } + } + val yAxis = mChart.axisLeft + yAxis.axisMinimum = 0f + yAxis.setDrawAxisLine(true) // 不显示y轴 + yAxis.setDrawLabels(true) + yAxis.setDrawGridLines(true) // 从y轴发出横向直线 + yAxis.setGridDashedLine(DashPathEffect(floatArrayOf(10f, 10f), 0f)) + yAxis.gridColor = Color.parseColor("#333333") + barSet.color = Color.parseColor("#516AFC") + yAxis.textColor = Color.parseColor("#333333") + if (isEmpty) { + data.setDrawValues(false) + data.isHighlightEnabled = false + } else { + mChart.setDrawValueAboveBar(true) + data.setDrawValues(true) + data.isHighlightEnabled = true + } + mChart.extraBottomOffset = 3f//整体距底边15f + mChart.setNoDataText("暂无数据") + + + mChart.setNoDataTextColor(Color.parseColor("#333333")) + +// mChart.clearValues() + mChart.data = data//设置数据 +// data.notifyDataChanged() + data.setValueTextColor(Color.parseColor("#333333")) + + mChart.isScaleXEnabled = true + mChart.isDragXEnabled = true + mChart.isScaleYEnabled = false + data.barWidth = barWidth + //是否分组 + if (isGroup) { + xAxis.setCenterAxisLabels(true)//label居中 + // 设置是否可以缩放图表 + mChart.isScaleYEnabled = false + mChart.isScaleXEnabled = true + mChart.isDragXEnabled = true + data.barWidth = barWidth + mChart.xAxis.axisMinimum = 0f + mChart.xAxis.axisMaximum = 7f + mChart.groupBars(0f, groupSpace, barSpace) + } else { + // 设置是否可以缩放图表 + mChart.isScaleYEnabled = false + mChart.isScaleXEnabled = false + data.barWidth = 0.5f + } + // x轴执行动画 + mChart.animateY(2000, EaseInOutQuad) + mChart.invalidate() + } + + fun initBarChart( + mChart: BarChart, + xValues: ArrayList, + data: BarData, + isEmpty: Boolean, + ) { + mChart.isDragEnabled = true// 设置是否可以用手指移动图表 + mChart.description.isEnabled = false// 不显示描述数据 + mChart.axisRight.isEnabled = false //是否显示右侧y轴 + mChart.isHighlightFullBarEnabled = true//是否整个柱高亮 + mChart.legend.isEnabled = false //是否显示图例 + mChart.setFitBars(true) + val xAxis = mChart.xAxis + xAxis.setDrawAxisLine(true) // 是否显示x轴线 + xAxis.position = XAxis.XAxisPosition.BOTTOM // 设置x轴数据的位置 + xAxis.axisLineColor = Color.parseColor("#333333") // 设置x轴线的颜色 + + xAxis.setDrawGridLines(false) // 是否绘制垂直x方向网格线 + xAxis.textSize = 18f // 设置x轴文字的大小 + + xAxis.textColor = Color.parseColor("#333333") + xAxis.setDrawLabels(true) + xAxis.granularity = 1f//粒度 + if (xValues.isNotEmpty()) { + //设置x轴文字 + xAxis.valueFormatter = object : ValueFormatter() { + override fun getFormattedValue(value: Float): String? { + val index = value.toInt() + return if (index >= 0 && index < xValues.size) { + xValues[value.toInt()] + } else { + "" + } + } + } + } + + val yAxis = mChart.axisLeft + yAxis.axisMinimum = 0f + yAxis.setDrawAxisLine(true) // 不显示y轴 + yAxis.setDrawLabels(true) + yAxis.setDrawGridLines(true) // 从y轴发出横向直线 + yAxis.setGridDashedLine(DashPathEffect(floatArrayOf(10f, 10f), 0f)) + yAxis.gridColor = Color.parseColor("#333333") + yAxis.textColor = Color.parseColor("#333333") + yAxis.textSize = 18f // 设置x轴文字的大小 + if (isEmpty) { + data.setDrawValues(false) + data.isHighlightEnabled = false + } else { + mChart.setDrawValueAboveBar(true) + data.setDrawValues(true) + data.isHighlightEnabled = true + } + mChart.extraBottomOffset = 3f//整体距底边15f + mChart.setNoDataText("暂无数据") + + + mChart.setNoDataTextColor(Color.parseColor("#333333")) + //设置缩放比例 + /* mChart.zoom(0f, 1f, 0f, 0f) + mChart.zoom(1f, 1f, 0f, 0f)*/ + mChart.data = data//设置数据 + data.setValueTextColor(Color.parseColor("#333333")) + // 设置是否可以缩放图表 + mChart.isScaleYEnabled = false + mChart.isScaleXEnabled = false +// data.barWidth = barWidth + // x轴执行动画 +// mChart.animateY(2000, EaseInOutQuad) + mChart.notifyDataSetChanged() + } + + fun initBarChart( + mChart: BarChart, + xValues: ArrayList, + data: BarData, + isGroup: Boolean, + isEmpty: Boolean, + ) { + mChart.isDragEnabled = true// 设置是否可以用手指移动图表 + mChart.description.isEnabled = false// 不显示描述数据 + mChart.axisRight.isEnabled = false //是否显示右侧y轴 + mChart.isHighlightFullBarEnabled = true//是否整个柱高亮 + mChart.legend.isEnabled = true //是否显示图例 + mChart.legend.setDrawInside(false) + mChart.legend.horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT + mChart.legend.verticalAlignment = Legend.LegendVerticalAlignment.TOP + mChart.legend.formSize = 10f + // 图例描述文字大小 + mChart.legend.textSize = 20f + mChart.setFitBars(true) + val xAxis = mChart.xAxis + xAxis.setDrawAxisLine(true) // 是否显示x轴线 + xAxis.position = XAxis.XAxisPosition.BOTTOM // 设置x轴数据的位置 + xAxis.axisLineColor = Color.parseColor("#333333") // 设置x轴线的颜色 + + xAxis.setDrawGridLines(false) // 是否绘制垂直x方向网格线 + xAxis.textSize = 18f // 设置x轴文字的大小 + + xAxis.textColor = Color.parseColor("#333333") + xAxis.setDrawLabels(true) + xAxis.granularity = 1f//粒度 + if (xValues.isNotEmpty()) { + //设置x轴文字 + xAxis.valueFormatter = object : ValueFormatter() { + override fun getFormattedValue(value: Float): String? { + val index = value.toInt() + return if (index >= 0 && index < xValues.size) { + xValues[value.toInt()] + } else { + "" + } + } + } + } + + val yAxis = mChart.axisLeft + yAxis.axisMinimum = 0f + yAxis.setDrawAxisLine(true) // 不显示y轴 + yAxis.setDrawLabels(true) + yAxis.setDrawGridLines(true) // 从y轴发出横向直线 + yAxis.setGridDashedLine(DashPathEffect(floatArrayOf(10f, 10f), 0f)) + yAxis.gridColor = Color.parseColor("#333333") + yAxis.textColor = Color.parseColor("#333333") + yAxis.textSize = 18f // 设置x轴文字的大小 + if (isEmpty) { + data.setDrawValues(false) + data.isHighlightEnabled = false + } else { + mChart.setDrawValueAboveBar(true) + data.setDrawValues(true) + data.isHighlightEnabled = true + } + mChart.extraBottomOffset = 3f//整体距底边15f + mChart.setNoDataText("暂无数据") + + + mChart.setNoDataTextColor(Color.parseColor("#333333")) + //设置缩放比例 + /* mChart.zoom(0f, 1f, 0f, 0f) + mChart.zoom(1f, 1f, 0f, 0f)*/ + mChart.data = data//设置数据 + data.setValueTextColor(Color.parseColor("#333333")) + // 设置是否可以缩放图表 +// mChart.isScaleYEnabled = false +// mChart.isScaleXEnabled = false + //是否分组 + if (isGroup) { + xAxis.setCenterAxisLabels(true)//label居中 + // 设置是否可以缩放图表 + mChart.isScaleYEnabled = false + mChart.isScaleXEnabled = false + mChart.xAxis.axisMinimum = 0f + mChart.xAxis.axisMaximum = xValues.size.toFloat() + xAxis.setLabelCount(xValues.size, false) + //关键的地方在于 需要满足该等式,标签才能居中: + //(barSpace + barWidth) * 柱形图数量(比如我的是每组两个) + groupSpace = 1 + + val groupSpace = 0.2f + val barSpace = 0f + val barWidth = 0.4f + data.barWidth = barWidth + mChart.groupBars(0f, groupSpace, barSpace) + } else { + // 设置是否可以缩放图表 + mChart.isScaleYEnabled = false + mChart.isScaleXEnabled = false + data.barWidth = 0.5f + } + data.setValueFormatter(object : ValueFormatter() { + override fun getFormattedValue(value: Float): String? { + val index = value.toInt() + return index.toString() + } + }) +// data.barWidth = barWidth + // x轴执行动画 +// mChart.animateY(2000, EaseInOutQuad) +// mChart.invalidate() + mChart.notifyDataSetChanged() + } + /** + * 设置单组 BarChart 数据 + */ + /*fun T.setSingleData( + entries: List, + colorList: List, + isHighlight: Boolean = true, + barWidthRatio: Float = 0.4f, + valueSize: Float = 10f, + valueColor: Int = Color.parseColor("#000000"), + formatter: IValueFormatter? = null + ) { + if (data != null && data.dataSetCount > 0) { + (data.getDataSetByIndex(0) as BarDataSet).entries = entries + (data.getDataSetByIndex(0) as BarDataSet).colors = colorList + data.notifyDataChanged() + notifyDataSetChanged() + } else { + data = BarData( + BarDataSet(entries, "") + .setProperty( + colorList = colorList, + isHighlight = isHighlight, + valueSize = valueSize, + valueColor = valueColor, + formatter = formatter + ) + ).apply { + barWidth = barWidthRatio //柱块宽度比例 + } + } + + // 设置为可以左右滑动,放大x轴倍数 + // viewPortHandler.refresh(Matrix().apply { postScale(1.5f, 1f) }, this, false) + }*/ + /** + * 初始化 BarChart + */ + /*fun T.initChart( + isLegend: Boolean = true, + isFitBars: Boolean = false, + touchDistance: Float = 5f, + isCenterLabel: Boolean = false, + isXGridLine: Boolean = false, + isYGridLine: Boolean = true, + xLabelAngle: Float = 0f, + xLabelCount: Int = 7, + yLabelCount: Int = 6, + xAxisMinimum: Float = -1f, + yAxisMinimum: Float = -1f, + xSpaceEntry: Float = 30f, + ySpaceEntry: Float = 5f + ) { + extraBottomOffset = 15f //底部偏移量 + description.isEnabled = false //是否显示描述组件 + isScaleXEnabled = true //启用/禁用x轴上的缩放 + isScaleYEnabled = true //启用/禁用y轴上的缩放 + isDoubleTapToZoomEnabled = false //启用/禁用通过双击放大图表 + setDrawBarShadow(false) //是否显示阴影 + setDrawValueAboveBar(true) //设置Bar值在顶部上方或下方 + setDrawGridBackground(false) //启用/禁用图表背景色 + setPinchZoom(false) //启用/禁用捏合缩放。如果禁用,x轴和y轴可以单独缩放 + setMaxVisibleValueCount(31) //同时显示Bar值的个数,超过不再显示 + setFitBars(isFitBars) //X轴是否自适应所有柱形图 + maxHighlightDistance = touchDistance //触发highlight点击偏离距离 + + // 无数据时设置 + setNoDataText("暂无相关数据") + setNoDataTextColor(context.getColorEx(R.color.light)) + + // 设置x轴属性 + xAxis.also { + it.position = XAxis.XAxisPosition.BOTTOM //x轴位置 + it.setDrawAxisLine(true) //是否显示轴条 + it.labelCount = xLabelCount //设置轴标签个数 + it.textColor = context.getColorEx(R.color.black) //文本颜色 + it.axisLineColor = context.getColorEx(R.color.gray) //线条颜色 + it.labelRotationAngle = xLabelAngle //标签旋转角度 + it.granularity = 1f //轴值之间的最小间隔 + it.setCenterAxisLabels(isCenterLabel) //设置柱子(柱子组)居中对齐x轴上的点 + it.setDrawGridLines(isXGridLine) //是否显示表格线条 + it.enableGridDashedLine(5f, 5f, 0f) //网格线虚线模式 + if (xAxisMinimum >= 0) it.axisMinimum = xAxisMinimum //x轴最小值 + } + }*/ + + fun initLineChart( + mChart: LineChart, + xValues: ArrayList, + values: List, + isUpdate: Boolean, + ) { + if (!isUpdate) { + mChart.isDragEnabled = true// 设置是否可以用手指移动图表 + mChart.description.isEnabled = false// 不显示描述数据 + mChart.axisRight.isEnabled = false //是否显示右侧y轴 + mChart.legend.isEnabled = false //是否显示图例 + val xAxis = mChart.xAxis + xAxis.setDrawAxisLine(true) // 是否显示x轴线 + xAxis.position = XAxis.XAxisPosition.BOTTOM // 设置x轴数据的位置 + xAxis.axisLineColor = Color.parseColor("#333333") // 设置x轴线的颜色 + xAxis.setDrawGridLines(false) // 是否绘制垂直x方向网格线 + xAxis.textSize = 18f // 设置x轴文字的大小 + xAxis.textColor = Color.parseColor("#333333") + xAxis.setDrawLabels(true) + xAxis.granularity = 1f//粒度 + //设置x轴文字 + if (xValues.isNotEmpty()) { + //设置x轴文字 + xAxis.valueFormatter = object : ValueFormatter() { + override fun getFormattedValue(value: Float): String? { + val index = value.toInt() + return if (index >= 0 && index < xValues.size) { + xValues[value.toInt()] + } else { + "" + } + } + } + } + + val yAxis = mChart.axisLeft + yAxis.axisMinimum = 0f + yAxis.setDrawAxisLine(true) // 不显示y轴 + yAxis.setDrawLabels(true) + yAxis.setDrawGridLines(true) // 从y轴发出横向直线 + yAxis.setGridDashedLine(DashPathEffect(floatArrayOf(10f, 10f), 0f)) + yAxis.axisMinimum = 0f // y轴最小值 + yAxis.axisMaximum = 1f // y轴最大值 + yAxis.setLabelCount(2,true) + yAxis.gridColor = Color.parseColor("#333333") + yAxis.textColor = Color.parseColor("#333333") + yAxis.textSize = 18f // 设置x轴文字的大小 + yAxis.valueFormatter = object : ValueFormatter() { + override fun getAxisLabel(value: Float, axis: AxisBase?): String { + val index = value.toInt() + return if(index==1) "打开" else "关闭" + } + } + val lineDataSet = LineDataSet(values, "") + val lineData = LineData(lineDataSet) + + lineData.setDrawValues(false) + lineData.isHighlightEnabled = false + mChart.extraBottomOffset = 3f//整体距底边15f + mChart.extraRightOffset = 40f + mChart.setNoDataText("暂无数据") + mChart.setNoDataTextColor(Color.parseColor("#333333")) + //设置缩放比例 + /* mChart.zoom(0f, 1f, 0f, 0f) + mChart.zoom(1f, 1f, 0f, 0f)*/ + mChart.data = lineData//设置数据 + // 设置是否可以缩放图表 + mChart.isScaleYEnabled = false + mChart.isScaleXEnabled = true +// data.barWidth = barWidth + // x轴执行动画 + mChart.animateY(2000, EaseInOutQuad) + mChart.invalidate() + }else { + val xAxis = mChart.xAxis + //设置x轴文字 + if (xValues.isNotEmpty()) { + //设置x轴文字 + xAxis.valueFormatter = object : ValueFormatter() { + override fun getFormattedValue(value: Float): String? { + val index = value.toInt() + return if (index >= 0 && index < xValues.size) { + xValues[value.toInt()] + } else { + "" + } + } + } + } + val lineDataSet = LineDataSet(values, "") + val lineData = LineData(lineDataSet) + lineData.setDrawValues(false) + mChart.data = lineData//设置数据 + mChart.invalidate() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/DataServer.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/DataServer.kt new file mode 100644 index 0000000..f34534a --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/DataServer.kt @@ -0,0 +1,277 @@ +package com.qidian.zhongkesmart.utils + +import android.app.Dialog +import android.util.Log +import com.qidian.baseble.model.BluetoothLeDevice + +import com.qidian.zhongkesmart.model.BlueToothData +import java.util.* +import kotlin.collections.ArrayList + +/** + * 考虑到app开机自启 开机时间一致在app内 不存在退出app的情况 所以不适合存本地 + */ +object DataServer { + /** + * 网关登录 + */ + //获取code + var mCode by SPUtils("USER_CODE", "") + + /** + * 测试数据 + * 已绑定token + * code 2WPMNAC + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOiIyN2RhMmE2NzA0MDE0NmMxYTU4YjMzMmE0OWJhNTE5NSIsImxvZ2luVGltZSI6IjIwMjItMDgtMTggMDk6MDg6NDAiLCJ0eXBlIjoiR1cifQ.wylUKfXrgU4DXGCGrgOxwivBlxwV4iay93c0qWw4WDE */ + var mHttpToken by SPUtils("USER_TOKEN", "") +// var mHttpToken = +// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOiI1MTVlY2U0ZWJiYjU0Njc2OTIxNmMyMjdiY2UyYWQyNiIsImx" + +// "vZ2luVGltZSI6IjIwMjItMDgtMTYgMTU6Mzc6MTEiLCJ0eXBlIjoiR1cifQ.YfNBqE2S1fsw-nc5xSooxzrmgX1nqph8BJQkGBmQ1YQ" + + /** + * Wifi + */ + var mWiFiName = "" + + + /** + * 蓝牙 + */ + //蓝牙交互所需要的三个UUID 服务UUID 读UUID 写UUID + val UUID1 = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e") //UUID1 + val UUID2 = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e") //UUID2 + val UUID3 = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e") //UUID3 + + //记录已连接的蓝牙信息 + var MDevice: BluetoothLeDevice? = null + var UNBUNDLE = 0 //0未连接蓝牙 1连接蓝牙 + + //蓝牙返回数据 记录的所有的绑定中的贴件的数据 + var MBlueToothData = ArrayList() + + //蓝牙返回数据 单次返回的数据 用于判断离线 所以只用记录Mac即可 + var MSingleBlueToothData = ArrayList() + + //蓝牙返回数据 单次返回的数据 用于同步数据 mapString + var MSingleBlueToothMap = "" + + //蓝牙返回数据存本地 + var MBlueToothLocalData = "BlueToothDataHistory" + + //绑定设备存本地 {"list":[{"mac":"cf6a12483f37","name":"我还差还","power":"","time":"2022-08-18 10:57:15","type":"门"}]} + var mBindHistory = "BindHistory" + + //记录需要连接的设备MAC,有可能是中继,也可能是贴件 + var nBlueToothMac = "" + + //连接模式 0未设置 1广播 2中继 + var MConnectMode = "MConnectMode" + var connectMode = 0 + var isUpdateFirmware = false + + //绑定的中继mac + var MRelayMac = "MRelayMac" + var MRelayName = "MRelayName" + + /** + * 记录需要验证的设备MAC,验证唤醒或者验证动作数据 + */ + var nBlueToothVerifyMac = "" + + + const val ACCESS_CAMERA_CODE = 1002 + const val ACCESS_WiFi_CODE = 1001 + const val ACCESS_Location_CODE = 1001 + const val ACCESS_BlueTooth_CODE = 1003 + + /** + * 日期 + */ + var mTodayTime = arrayOf( + "0点", + "1点", + "2点", + "3点", + "4点", + "5点", + "6点", + "7点", + "8点", + "9点", + "10点", + "11点", + "12点", + "13点", + "14点", + "15点", + "16点", + "17点", + "18点", + "19点", + "20点", + "21点", + "22点", + "23点", + "24点" + ) + var mWeekTime = arrayOf("星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日") + var mMonthTime = arrayOf("第一周", "第二周", "第三周", "第四周") + var mYearTime = + arrayOf("1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月") + + + /** + * 重置网关 + */ + fun resetData() { + mCode = "" + mHttpToken = "" + SPUtil.setValue("USER_TOKEN", "") + SPUtil.setValue("USER_CODE", "") + SPUtil.setValue(MBlueToothLocalData, "") + SPUtil.setValue(mBindHistory, "") + SPUtil.setValue(MConnectMode, 0) + SPUtil.setValue(MRelayMac,"") + connectMode =0 + MDevice = null + UNBUNDLE = 0 + MBlueToothData.clear() + MSingleBlueToothData.clear() + ChangeFocusMark = 0 + showWinkAnnimationTime = 0 + WGName = "" + listAlarmMac.clear() + listAlarmDialog.clear() + } + + /** + * 设备绑定相关数据 + * 2022-8-1 + */ + var TJLocation = arrayOf( + "客厅", + "厨房", + "卧室", + "浴室", + "卫生间", + "衣帽间", + "餐厅", + "书房", + "主卧", + "次卧", + "其他房间", + "宠物", + "植物", + "随身", + "进水主水管" + ) + var TJUse = arrayOf( + "门", "窗帘", "主水管", "水管", "马桶", "电视", "电冰箱", "煤气灶", "抽油烟机", + "宠物", "植物", "挎包" + ) + + /** + * 报警时间 + * 持续扫描开启的持续时间,当超过X秒后报警 + * 冰箱、门、水管、燃气灶都是“长时间未关闭”的报警 + * 对不同类型 不同时间设置 + * 暂时都默认30分钟 30*60*1000 1*60*1000 + */ +// var mAlarmTime = 1*60*1000 + var mAlarmTime=1800*1000 + + //震动型 X秒后没震动后 设置默认关闭 40*60*1000 30*1000 + var mVibrationCloseTime = 30*1000 + + /** + * 待机页面 + */ + //焦点变化标志 用于待机页面显示判断 + var ChangeFocusMark = 0 + + //n秒后未操作显示待机页面 10*60*1000 +// var noFocusTime = 1 * 60 * 1000 + var noFocusTime = 10 * 60 * 1000 + + //三十分钟后显示眨眼动画30*60*1000 标准 + var mShowWinkAnnimationTimeBZ = 1 * 60 * 1000 + + //10s后显示眨眼动画 + var mShowWinkAnnimationTime = 10 + + //记录时间 + var showWinkAnnimationTime = 0 + + //记录是否在显示 + var standByPageIsShow = false + + var wifiDialogIsShow = false + + //天气预报使用-城市名称记录 + var mWeatherData = "晴" + + /** + * 网关心跳间隔时间 默认15分钟 15*60*1000 + */ + var mHeartbeatTime = 15 * 60 * 1000 + + + /** + * 设置页面 返回按钮tag 记录显示页面 + */ + var SetPageTag = "" + + //数据推送相关 + var listPushADD = ArrayList() + var listPushOffLine = ArrayList() + var listPushAlarm_Open = ArrayList() + var listPushAlarm_LowPower = ArrayList() + + + /** + * 设置-网关设置-网关名称 + */ + var WGName = "" + + + /** + * 报警弹窗 + * 一个贴件对应一个mac 一个mac同一时间只能显示一个 + */ + //弹窗mac记录 + var listAlarmMac = ArrayList() + var listAlarmDialog = ArrayList() + + + /** + * 移除报警mac,如果弹窗未关闭,关闭报警弹窗 + */ + fun removeAlarmMac(mac: String) { + var isContain = false + listAlarmMac.forEach { + if (it == mac) { + isContain = true + } + } + if (isContain) { + var indexMac = listAlarmMac.indexOf(mac) + var dialog = listAlarmDialog[indexMac] + if (dialog != null && dialog!!.isShowing) { + dialog!!.dismiss() + } + listAlarmDialog.removeAt(listAlarmMac.indexOf(mac)) + listAlarmMac.remove(mac) + } + Log.i("报警弹窗Mac==", listAlarmMac.toString()) + } + + fun isShowAlarmMac(mac: String): Boolean { + var isContain = false + listAlarmMac.forEach { + if (it == mac) { + isContain = true + } + } + return isContain + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/DataUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/DataUtils.kt new file mode 100644 index 0000000..8656ece --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/DataUtils.kt @@ -0,0 +1,52 @@ +package com.qidian.zhongkesmart.utils + +import android.content.Context +import android.util.Log +import com.qidian.zhongkesmart.model.BindData + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2022/8/24 0024 17:46 + */ +object DataUtils { + /** + * 接口数据样式处理 + */ + + //绑定 + fun getBindDataStr( + context: Context, + mac: String, + roomName: String, + type: String + ): String { + var str = "" + var list = ArrayList() + var bean = BindData( + DeviceUuidFactory.getAndroidID(context), + mac, + roomName, + type + ) + list.add(bean) + str = ToolUtil.getJson(list).toString() + return getSplitStr(str) + } + + //离线 + fun getOfflineDataStr(){ + + } + + //截取 + fun getSplitStr(dataStr: String): String { + if (dataStr.isNotEmpty()) { + return dataStr.substring(8, dataStr.length - 1) + } + Log.i("同步接口数据==", dataStr) + return dataStr + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/DeviceUuidFactory.java b/app/src/main/java/com/qidian/zhongkesmart/utils/DeviceUuidFactory.java new file mode 100644 index 0000000..6c84596 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/DeviceUuidFactory.java @@ -0,0 +1,186 @@ +package com.qidian.zhongkesmart.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Environment; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +public class DeviceUuidFactory { + + + private static final String DEVICE_UUID_FILE_NAME = ".tobindevid.conf"; + private static final String DEVICE_IMEI_FILE_NAME = ".tobinimei.conf"; + private static final String PREFS_FILE = "tobindevice_id.xml"; + private static final String PREFS_DEVICE_ID = "device_id"; + private static final String PREFS_IMEI = "imei"; + private static UUID uuid; + private static String imei; + + private static DeviceUuidFactory deviceUuidFactory; + public static DeviceUuidFactory getInstance(Context context){ + if (deviceUuidFactory == null){ + deviceUuidFactory = new DeviceUuidFactory(context); + } + return deviceUuidFactory; + } + + private DeviceUuidFactory(Context context) { + + if (uuid == null) { + synchronized (DeviceUuidFactory.class) { + if (uuid == null) { + final SharedPreferences prefs = context.getSharedPreferences(PREFS_FILE, 0); + String id = prefs.getString(PREFS_DEVICE_ID, null); + String imeiStr = prefs.getString(PREFS_IMEI, null); + + if (TextUtils.isEmpty(id)) { + if (checkPermission(context, permission.WRITE_EXTERNAL_STORAGE)) { + id = recoverFromSD(DEVICE_UUID_FILE_NAME); + } + } + if (TextUtils.isEmpty(imeiStr)) { + if (checkPermission(context, permission.WRITE_EXTERNAL_STORAGE)) { + imeiStr = recoverFromSD(DEVICE_IMEI_FILE_NAME); + } + } + if (!TextUtils.isEmpty(imeiStr)) { + imei = imeiStr; + prefs.edit().putString(PREFS_IMEI, imei).commit(); + if (checkPermission(context, permission.WRITE_EXTERNAL_STORAGE)) { + saveToSD(imei, DEVICE_IMEI_FILE_NAME); + } + } + if (id != null) { + // Use the ids previously computed and stored in the prefs file + uuid = UUID.fromString(id); + prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString()).commit(); + if (checkPermission(context, permission.WRITE_EXTERNAL_STORAGE)) { + saveToSD(uuid.toString(), DEVICE_UUID_FILE_NAME); + } + + } else { + final String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); + String tmDevice, tmSerial; + tmDevice = ""; + tmSerial = ""; + + try { + if (checkPermission(context, permission.READ_PHONE_STATE)) { + TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + tmDevice = tm.getDeviceId(); + tmSerial = tm.getSimSerialNumber(); + } + } catch (SecurityException ex) { + ex.printStackTrace(); + } + + if (TextUtils.isEmpty(tmDevice) || TextUtils.isEmpty(tmSerial) || tmDevice.equals("000000000000000")) { + if (!TextUtils.isEmpty(androidId) && !"9774d56d682e549c".equals(androidId) && Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) { + uuid = UUID.nameUUIDFromBytes(androidId.getBytes()); + } else { + uuid = UUID.randomUUID(); + } + + } else { + uuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode()); + } + if (!TextUtils.isEmpty(tmDevice)) { + imei = tmDevice; + prefs.edit().putString(PREFS_IMEI, imei).commit(); + if (checkPermission(context, permission.WRITE_EXTERNAL_STORAGE)) { + saveToSD(imei, DEVICE_IMEI_FILE_NAME); + } + + } + + // Write the value out to the prefs file + prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString()).commit(); + + if (checkPermission(context, permission.WRITE_EXTERNAL_STORAGE)) { + saveToSD(uuid.toString(), DEVICE_UUID_FILE_NAME); + } + } + } + } + } + } + + public UUID getDeviceUuid() { + return uuid; + } + + private static void saveToSD(String uuid, String fileName) { + String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath(); + File targetFile = new File(dirPath, fileName); + if (targetFile != null) { + if (!targetFile.exists()) { + OutputStreamWriter osw; + try { + osw = new OutputStreamWriter(new FileOutputStream(targetFile), "utf-8"); + try { + osw.write(uuid); + osw.flush(); + osw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + } + } + + private static String recoverFromSD(String fileName) { + try { + String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath(); + File dir = new File(dirPath); + File uuidFile = new File(dir, fileName); + if (!dir.exists() || !uuidFile.exists()) { + return null; + } + FileReader fileReader = new FileReader(uuidFile); + StringBuilder sb = new StringBuilder(); + char[] buffer = new char[100]; + int readCount; + while ((readCount = fileReader.read(buffer)) > 0) { + sb.append(buffer, 0, readCount); + } + fileReader.close(); + return sb.toString(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private boolean checkPermission(Context context, permission permName) { + int perm = context.checkCallingOrSelfPermission("android.permission." + permName.toString()); + return perm == PackageManager.PERMISSION_GRANTED; + } + + private enum permission { + READ_PHONE_STATE, + WRITE_EXTERNAL_STORAGE + } + public static String getAndroidID(Context context) { + String id = Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID); + return id == null ? "" : id; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/DialogUtil.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/DialogUtil.kt new file mode 100644 index 0000000..dabda21 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/DialogUtil.kt @@ -0,0 +1,20 @@ +package com.qidian.zhongkesmart.utils + +import android.app.Dialog +import android.content.Context +import android.view.Gravity +import com.qidian.zhongkesmart.R + +object DialogUtil { + + fun loadingDialog(context: Context): Dialog { + val dialog = Dialog(context, R.style.dialog2) + dialog.setContentView(R.layout.dialog_loading) + dialog.setCancelable(true) + dialog.setCanceledOnTouchOutside(false) + dialog.window?.setGravity(Gravity.CENTER) + return dialog + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/DownloadUtils.java b/app/src/main/java/com/qidian/zhongkesmart/utils/DownloadUtils.java new file mode 100644 index 0000000..f0b6166 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/DownloadUtils.java @@ -0,0 +1,362 @@ +package com.qidian.zhongkesmart.utils; + +import android.annotation.SuppressLint; +import android.app.DownloadManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.text.TextUtils; +import android.util.Log; + +import com.blankj.utilcode.util.GsonUtils; +import com.qidian.zhongkesmart.config.Config; +import com.qidian.zhongkesmart.config.EventCode; +import com.qidian.zhongkesmart.model.DownloadIdStatus; +import com.qidian.zhongkesmart.model.DownloadProgress; + +import java.io.File; +import java.io.IOException; +import java.text.NumberFormat; + +import androidx.core.content.FileProvider; + + +public class DownloadUtils { + //下载器 + private DownloadManager downloadManager; + private Context mContext; + //下载的ID + private long downloadId; + private String name; + //安装包下载路径 + private String pathstr=""; +// private IDownloadCallBack myDownLoadCallBack; + private static DownloadUtils instance; + + //应用 + public final static int APP = 1; + //代理固件 + public final static int FIRMWARE_D = 2; + //低功耗固件 + public final static int FIRMWARE_L= 3; + //当前下载的类型 + private int currentDownloadType; + + /** + * 下载状态0未下载 1下载中 2下载成功 待安装 3下载暂停 4下载失败 + */ + public int downLoadStatus; + + public static DownloadUtils getInstance(Context context) { + if (instance == null) { + Class var0 = DownloadUtils.class; + synchronized (DownloadUtils.class) { + if (instance == null) { + instance = new DownloadUtils(context); + } + } + } + return instance; + } + + public DownloadUtils(Context context){ + this.mContext = context; + if (downloadManager == null) + downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); + } + + public interface IDownloadCallBack { + void downLoading(); + void downLoadProgress(int pSoFar,int pAll); + void downLoadSuccess(); + void downLoadFail(); + } + + private final QueryRunnable mQueryProgressRunnable = new QueryRunnable(); + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + if (msg.what == 1001) { + Log.i("download",msg.arg1+"h"); + Log.i("download",msg.arg2+"h"); + NumberFormat numberFormat = NumberFormat.getInstance(); + numberFormat.setMaximumFractionDigits(0); + DownloadProgress downProgress = new DownloadProgress(currentDownloadType,Integer.parseInt(numberFormat.format((float)msg.arg1/(float) msg.arg2*100))); + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.DOWNLOAD_PROGRESS,downProgress)); + Log.i("download",downProgress.toString()); + } + } + }; + + //下载apk + public void downloadAPK(String url, String name,int downloadType) { + this.name = name; +// this.myDownLoadCallBack = iDownloadCallBack; + this.currentDownloadType = downloadType; + //创建下载任务 + DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); + //移动网络情况下是否允许漫游 + request.setAllowedOverRoaming(false); + //在通知栏中显示,默认就是显示的 + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); + request.setTitle("SeawaySports"); + request.setDescription("安装包下载中..."); + request.setVisibleInDownloadsUi(true); + + //设置下载的路径 + File file = new File(mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), name); + request.setDestinationUri(Uri.fromFile(file)); + if(downloadType==APP) { + pathstr = file.getAbsolutePath(); + } + //获取DownloadManager + if (downloadManager == null) + downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); + //将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等 + if (downloadManager != null) { + downloadId = downloadManager.enqueue(request); + DownloadIdStatus downloadIdStatus = new DownloadIdStatus(0,0,0); + if(downloadType == APP){ + downloadIdStatus.setAppDownloadId(downloadId); + }else if(downloadType == FIRMWARE_D){ + downloadIdStatus.setFirmwareDDownloadId(downloadId); + }else if(downloadType == FIRMWARE_L){ + downloadIdStatus.setFirmwareLDownloadId(downloadId); + } + SPUtil.setValue(Config.DOWNLOAD_ID_STATUS,GsonUtils.toJson(downloadIdStatus)); + downLoadStatus=1; +// if (myDownLoadCallBack != null) { +// myDownLoadCallBack.downLoading(); +// } + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.DOWNLOAD_START,downloadType)); + } + + //注册广播接收者,监听下载状态 + mContext.registerReceiver(receiver, + new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); + + + startQuery(); + } + + //广播监听下载的各个状态 + private BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + checkStatus(); + } + }; + + //检查下载状态 + private void checkStatus() { + DownloadManager.Query query = new DownloadManager.Query(); + //通过下载的id查找 + query.setFilterById(downloadId); + Cursor cursor = downloadManager.query(query); + if (cursor.moveToFirst()) { + @SuppressLint("Range") int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); + switch (status) { + //下载暂停 + case DownloadManager.STATUS_PAUSED: + downLoadStatus = 3; + Log.e("DownloadUtils","STATUS_PAUSED"); + break; + //下载延迟 + case DownloadManager.STATUS_PENDING: + Log.e("DownloadUtils","STATUS_PENDING"); + break; + //正在下载 + case DownloadManager.STATUS_RUNNING: + downLoadStatus = 1; +// if (myDownLoadCallBack != null) { +// myDownLoadCallBack.downLoading(); +// } + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.DOWNLOAD_START,currentDownloadType)); + Log.e("DownloadUtils","STATUS_RUNNING"); + break; + //下载完成 + case DownloadManager.STATUS_SUCCESSFUL: + downLoadStatus = 2; +// if (myDownLoadCallBack != null) { +// myDownLoadCallBack.downLoadSuccess(); +// } + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.DOWNLOAD_FINISH,currentDownloadType)); + Log.e("DownloadUtils","STATUS_SUCCESSFUL"); + //下载完成安装APK + if(currentDownloadType==APP) { + installAPK(); + } + cursor.close(); + stopQuery(); + mContext.unregisterReceiver(receiver); + resetLocalDownloadStatus(); + break; + //下载失败 + case DownloadManager.STATUS_FAILED: + downLoadStatus = 4; + Log.e("DownloadUtils","STATUS_FAILED"); +// if (myDownLoadCallBack != null) { +// myDownLoadCallBack.downLoadFail(); +// } + EventBusUtil.sendEvent(new EventBusUtil.MessageEvent(EventCode.DOWNLOAD_ERROR,currentDownloadType)); + cursor.close(); + stopQuery(); + mContext.unregisterReceiver(receiver); + resetLocalDownloadStatus(); + break; + } + } + } + + + public int checkStatus(long id){ + DownloadManager.Query query = new DownloadManager.Query(); + //通过下载的id查找 + query.setFilterById(id); + //获取DownloadManager + if (downloadManager == null) + downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); + Cursor cursor = downloadManager.query(query); + int result = 0; + if (cursor.moveToFirst()) { + @SuppressLint("Range") int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); + switch (status) { + //下载暂停 + case DownloadManager.STATUS_PAUSED: + downLoadStatus = 3; + result = 3; + break; + //下载延迟 + case DownloadManager.STATUS_PENDING: + break; + //正在下载 + case DownloadManager.STATUS_RUNNING: + downLoadStatus = 1; + result = 1; + break; + //下载完成 + case DownloadManager.STATUS_SUCCESSFUL: + downLoadStatus = 2; + result = 2; + break; + //下载失败 + case DownloadManager.STATUS_FAILED: + downLoadStatus = 4; + result = 4; + break; + } + } + cursor.close(); + return result; + } + + public void installAPK() { + if(TextUtils.isEmpty(pathstr.trim())){ + File file = new File(mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), Config.DOWNLOAD_APK); + pathstr = file.getAbsolutePath(); + } + setPermission(pathstr); + Intent intent = new Intent(Intent.ACTION_VIEW); + // 由于没有在Activity环境下启动Activity,设置下面的标签 + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + //Android 7.0以上要使用FileProvider + if (Build.VERSION.SDK_INT >= 24) { + File file = (new File(pathstr)); + //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致 参数3 共享的文件 + Uri apkUri = FileProvider.getUriForFile(mContext, "com.qidian.zhongkesmart.fileProvider", file); + //添加这一句表示对目标应用临时授权该Uri所代表的文件 + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); + } else { + intent.setDataAndType(Uri.fromFile(new File(Environment.DIRECTORY_DOWNLOADS, name)), "application/vnd.android.package-archive"); + } + mContext.startActivity(intent); + } + + //修改文件权限 + private void setPermission(String absolutePath) { + String command = "chmod " + "777" + " " + absolutePath; + Runtime runtime = Runtime.getRuntime(); + try { + runtime.exec(command); + } catch (IOException e) { + e.printStackTrace(); + } + } + + //更新下载进度 + private void startQuery() { + if (downloadId != 0) { + mHandler.post(mQueryProgressRunnable); + } + } + //查询下载进度 + private class QueryRunnable implements Runnable { + @Override + public void run() { + queryState(); + mHandler.postDelayed(mQueryProgressRunnable,100); + } + } + //查询下载进度 + private void queryState() { + // 通过ID向下载管理查询下载情况,返回一个cursor + Cursor c = downloadManager.query(new DownloadManager.Query().setFilterById(downloadId)); + if (c == null) { + return; + } else { // 以下是从游标中进行信息提取 + if (!c.moveToFirst()) { + if(!c.isClosed()) { + c.close(); + } + return; + } + @SuppressLint("Range") int mDownload_so_far = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + @SuppressLint("Range") int mDownload_all = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); + Log.i("download",mDownload_so_far+"q"); + Log.i("download",mDownload_all+"q"); + Message msg= Message.obtain(); + if(mDownload_all>0) { + msg.what = 1001; + msg.arg1=mDownload_so_far; + msg.arg2=mDownload_all; + mHandler.sendMessage(msg); + } + if(!c.isClosed()){ + c.close(); + } + } + } + //停止查询下载进度 + private void stopQuery() { + mHandler.removeCallbacks(mQueryProgressRunnable); + } + + + //下载停止同时删除下载文件 + public void removeDownload() { + if(downloadManager!=null&&downloadId!=0){ + downloadManager.remove(downloadId); + DownloadIdStatus downloadIdStatus = new DownloadIdStatus(0,0,0); + SPUtil.setValue(Config.DOWNLOAD_ID_STATUS, GsonUtils.toJson(downloadIdStatus)); + } + } + + private void resetLocalDownloadStatus(){ + DownloadIdStatus downloadIdStatus = new DownloadIdStatus(0,0,0); + SPUtil.setValue(Config.DOWNLOAD_ID_STATUS, GsonUtils.toJson(downloadIdStatus)); + } + + //重新设置为0 + public void resetDownloadStatus(){ + downLoadStatus = 0; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/EditUtil.java b/app/src/main/java/com/qidian/zhongkesmart/utils/EditUtil.java new file mode 100644 index 0000000..8dbf689 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/EditUtil.java @@ -0,0 +1,67 @@ +package com.qidian.zhongkesmart.utils; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.text.Editable; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.TextView; + +import static android.content.Context.INPUT_METHOD_SERVICE; + +public class EditUtil { + public static String getEditText(EditText editText){ + return editText.getText().toString().trim(); + } + public static String getEditText(TextView editText){ + return editText.getText().toString().trim(); + } + public static String getEditText(Editable editable){ + return editable.toString().trim(); + } + + /** + * 显示键盘 + * + * @param et 输入焦点 + */ + public static void showInput(Context context, final EditText et) { + et.requestFocus(); + InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE); + imm.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT); + } + + /** + * 隐藏键盘 + */ + public static void hideInput(Context context) { + View view = ((Activity)context).getWindow().peekDecorView(); + if (view != null) { + InputMethodManager inputmanger = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + inputmanger.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + /** + * 隐藏键盘 dialog + */ + public static void hideDialogInput(Context context, Dialog dialog) { + View view =dialog.getWindow().peekDecorView();//注意:这里要根据EditText位置来获取 + if (view != null) { + InputMethodManager inputmanger = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + inputmanger.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + + /** + * view显示软键盘 + */ + public static void showKeyboard(View view){ + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if(imm != null){ + view.requestFocus(); + imm.showSoftInput(view,0); + } + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/EventBusUtil.java b/app/src/main/java/com/qidian/zhongkesmart/utils/EventBusUtil.java new file mode 100644 index 0000000..c7f1872 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/EventBusUtil.java @@ -0,0 +1,45 @@ +package com.qidian.zhongkesmart.utils; + +import org.greenrobot.eventbus.EventBus; + +public class EventBusUtil { + public static void register(Object subscriber) { + if (!EventBus.getDefault().isRegistered(subscriber)) { + EventBus.getDefault().register(subscriber); + } + } + public static void unregister(Object subscriber) { + if (EventBus.getDefault().isRegistered(subscriber)) { + EventBus.getDefault().unregister(subscriber); + } + } + public static void sendEvent(MessageEvent event) { + EventBus.getDefault().post(event); + } + public static void sendStickyEvent(MessageEvent event) { + EventBus.getDefault().postSticky(event); + } + + public static class MessageEvent { + private int code; + private Object data; + public MessageEvent(int code) { + this.code = code; + } + public MessageEvent(int code, Object data) { + this.code = code; this.data = data; + } + public int getCode() { + return code; + } + public void setCode(int code) { + this.code = code; + } + public Object getData() { + return data; + } + public void setData(Object data) { + this.data = data; + } + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/InputLimitUtil.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/InputLimitUtil.kt new file mode 100644 index 0000000..ccc2ae0 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/InputLimitUtil.kt @@ -0,0 +1,65 @@ +package com.qidian.zhongkesmart.utils + +import android.content.Context +import android.view.View +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.TextView +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.views.ClearEditText + +object InputLimitUtil { + + + /*输入不符合要求时候展示不通效果*/ + fun ShowEdittextStyle( + medit: EditText, + mtext: TextView, + wormStr: String + ) { + medit.hint="" + if (wormStr.isEmpty()) { + medit.setBackgroundResource(R.drawable.ed_6_c5cbd7_line) + mtext.visibility = View.INVISIBLE + } else { + medit.setBackgroundResource(R.drawable.ed_6_ee4141_line) + mtext.visibility = View.VISIBLE + mtext.text = wormStr + } + } + + /*输入不符合要求时候展示不通效果 + * 设置服务器用*/ + fun ShowEdittextStyle2( + medit: EditText, + mtext: TextView, + wormStr: String + ) { + medit.hint="" + if (wormStr.isEmpty()) { + medit.setBackgroundResource(R.drawable.ed_6_c5cbd7_line) + mtext.visibility = View.GONE + } else { + medit.setBackgroundResource(R.drawable.ed_6_ee4141_line) + mtext.visibility = View.VISIBLE + mtext.text = wormStr + } + } + fun ShowEdittextStyle3( + medit: EditText, + linear: LinearLayout, + mtext: TextView, + wormStr: String + ) { + medit.hint="" + if (wormStr.isEmpty()) { + linear.setBackgroundResource(R.drawable.ed_6_c5cbd7_line) + mtext.visibility = View.INVISIBLE + } else { + linear.setBackgroundResource(R.drawable.ed_6_ee4141_line) + mtext.visibility = View.VISIBLE + mtext.text = wormStr + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/JsonUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/JsonUtils.kt new file mode 100644 index 0000000..1f79035 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/JsonUtils.kt @@ -0,0 +1,88 @@ +package com.qidian.zhongkesmart.utils + +import com.alibaba.fastjson.JSON +import com.google.gson.Gson +import com.google.gson.JsonParseException +import com.google.gson.JsonSyntaxException +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import org.json.JSONTokener +import java.lang.Exception +import java.lang.reflect.Type + +class JsonUtils{ + companion object{ + val util by lazy(LazyThreadSafetyMode.SYNCHRONIZED){ + JsonUtils() + } + } + + fun jsonParser(jsonStr: String,clazz:Class):Object{ + var relt: Object? = null + if (isJsonArray(jsonStr)) { + relt = parseArrayList(jsonStr, clazz!!) as Object + } else { + relt = Gson().fromJson(jsonStr, clazz) as Object + } + return relt + } + /** + * map集合转json + * @param map + * @return + */ + fun mapToJson(map: Map): String { + val jsonStr = com.alibaba.fastjson.JSONObject.parseObject(JSON.toJSONString(map)) + return jsonStr.toJSONString() + } + fun listToJson(list:ArrayList):String{ + return JSON.toJSONString(list) + } + + //判断是不是json + private fun isJson(json:String?):Boolean{ + if(json.isNullOrEmpty()){ + return false + } + try { + JSONObject(json) + }catch (e : JsonSyntaxException){ + return false + }catch (e : JsonParseException){ + return false + }catch (e : Exception){ + return false + } + return true + } + + //json 解析数组 + fun parseArrayList(json: String, clazz: Class): ArrayList { + val type = + com.google.gson.internal.`$Gson$Types`.newParameterizedTypeWithOwner( + null, + ArrayList::class.java, + clazz + ) as Type + return Gson().fromJson(json, type) as ArrayList + } + + //是不是json 数组 + private fun isJsonArray(jsonStr: String): Boolean { + var typeObject: Any? = null + try { + typeObject = JSONTokener(jsonStr).nextValue() + } catch (e: JSONException) { + e.printStackTrace() + } + + if (typeObject is JSONArray) { + return true + } else if (typeObject is JSONObject) { + return false + } + return false + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/LoadUtils.java b/app/src/main/java/com/qidian/zhongkesmart/utils/LoadUtils.java new file mode 100644 index 0000000..963a3d3 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/LoadUtils.java @@ -0,0 +1,115 @@ +package com.qidian.zhongkesmart.utils; + +import android.content.Context; + +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.scwang.smartrefresh.layout.SmartRefreshLayout; + + +/** + * Created by Monkey on 2018/1/20. + */ + +public class LoadUtils { + private static LinearLayoutManager layoutManager; + private static GridLayoutManager layoutManager_gv; + private boolean isLoading = false; + + public static interface WindowCallBack { + public void doWork(); + } + + public static void setLinearLayoutManager(final Context context, RecyclerView recycle, boolean ishorizon){ + if (ishorizon) { + layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); + recycle.setLayoutManager(layoutManager); + } else { + layoutManager = new LinearLayoutManager(context); + recycle.setLayoutManager(layoutManager); + } + } + public static void setGridviewLayoutManager(final Context context, RecyclerView recycle, int num){ + layoutManager_gv = new GridLayoutManager(context, num); + recycle.setLayoutManager(layoutManager_gv); + } + + + public static void loading(final Context context, RecyclerView recycle, boolean ishorizon, final WindowCallBack callBack) { + if (ishorizon) { + layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); + recycle.setLayoutManager(layoutManager); + } else { + layoutManager = new LinearLayoutManager(context); + recycle.setLayoutManager(layoutManager); + } + + if (callBack != null) { + recycle.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + int total = layoutManager.getItemCount(); + int lastVisibleItem = layoutManager.findLastVisibleItemPosition(); + if (lastVisibleItem >= total - 1 && dy > 0) { + callBack.doWork(); + } + } + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (context == null) return; + switch (newState) { + case RecyclerView.SCROLL_STATE_DRAGGING: +// Glide.with(context).pauseRequests(); + break; + case RecyclerView.SCROLL_STATE_SETTLING: +// Glide.with(context).pauseRequests(); + break; + case RecyclerView.SCROLL_STATE_IDLE: +// Glide.with(context).resumeRequests(); + break; + } + } + }); + } + } + + public static void loadingasGridview(final Context context, RecyclerView recycle, int num/*, final WindowCallBack callBack*/) { + layoutManager_gv = new GridLayoutManager(context, num); + recycle.setLayoutManager(layoutManager_gv); + /*if (callBack != null) { + recycle.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + int total = layoutManager_gv.getItemCount(); + int lastVisibleItem = layoutManager_gv.findLastVisibleItemPosition(); + if (lastVisibleItem >= total - 1 && dy > 0) callBack.doWork(); + } + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (context == null) return; + switch (newState) { + case RecyclerView.SCROLL_STATE_DRAGGING: +// Glide.with(context).pauseRequests(); + break; + case RecyclerView.SCROLL_STATE_SETTLING: +// Glide.with(context).pauseRequests(); + break; + case RecyclerView.SCROLL_STATE_IDLE: +// Glide.with(context).resumeRequests(); + break; + } + } + }); + }*/ + } + + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/ObjectBoxUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/ObjectBoxUtils.kt new file mode 100644 index 0000000..a0591ca --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/ObjectBoxUtils.kt @@ -0,0 +1,273 @@ +package com.qidian.zhongkesmart.utils + +import android.util.Log +import com.qidian.zhongkesmart.base.AppApplication +import com.qidian.zhongkesmart.model.* +import io.objectbox.Box +import io.objectbox.BoxStore +import io.objectbox.kotlin.equal + +object ObjectBoxUtils { + /** + * 初始化数据库 + */ + var boxStore: BoxStore? = null + fun init(): BoxStore? { + try { + boxStore = AppApplication.boxStore + if (boxStore == null) { + boxStore = + MyObjectBox.builder().androidContext(AppApplication.mContext).build() + AppApplication.boxStore = boxStore + } + } catch (e: Exception) { + } + return boxStore + } + + /** + * 重启数据库 + * 删除本地数据库后,需要重新创建 + */ + fun restartBoxStore(){ + boxStore = MyObjectBox.builder().androidContext(AppApplication.mContext).build() + AppApplication.boxStore = boxStore + } + + /** + * 添加数据 + */ + fun addData(o: Any, c: Class): Long { + try { + val boxStore: BoxStore? = init() + if (boxStore != null && !boxStore.isClosed) { + return boxStore.boxFor(c).put(o) + } + } catch (e: Throwable) { + Log.d("lyy", "error:${e.printStackTrace()}") + } + return 0 + } + + + /** + * 根据batchid查询 + * @param key + * @return + */ + fun getDeviceByMac(key: String?): List? { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(DeviceInfo::class.java) + return box.query(DeviceInfo_.mac.equal(key)).build().find() + } + } catch (e: java.lang.Exception) { + } + return ArrayList() + } + + /** + * 根据mac查询 + * @param key + * @return + */ + fun hasDeviceByMac(key: String?): Boolean? { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(DeviceInfo::class.java) + var list = box.query(DeviceInfo_.mac.equal(key)).build().find() + return list!=null&&list.size>0 + } + } catch (e: java.lang.Exception) { + } + return false + } + + /** + * 更新数据(如果直接插入更新的对象,有些字段没有值会覆盖掉) + * 1、先查询到数据 + * 2、对查询到的数据字段进行更新 + * 3、查询不到,直接插入 + */ + fun updateData(o: DeviceInfo, c: Class) { + try { + val boxStore: BoxStore? = init() + if (boxStore != null && !boxStore.isClosed) { + //1、先查询到数据 + val box: Box = boxStore.boxFor(DeviceInfo::class.java) + val list: MutableList = box.query().equal(DeviceInfo_.id, o.id).build().find() + var localBean = list.getOrNull(0) + //2、对查询到的数据字段进行更新 + localBean?.let { + localBean.isBind = o.isBind + localBean.name = o.name + localBean.type = o.type + localBean.time = o.time + localBean.power = o.power + localBean.hVersion = o.hVersion + localBean.fVersion = o.fVersion + localBean.frequency = o.frequency + localBean.model1Threshold = o.model1Threshold + localBean.model1Init = o.model1Init + localBean.model1Time = o.model1Time + localBean.model2Threshold = o.model2Threshold + localBean.model2Init = o.model2Init + localBean.model2Time = o.model2Time + localBean.isOnline = o.isOnline + localBean.updateTime = o.updateTime + boxStore.boxFor(c).put(localBean) + }?: kotlin.run { + //3、查询不到,直接插入 + boxStore.boxFor(c).put(o) + } + } + } catch (e: Throwable) { + Log.d("lyy", "error:${e.printStackTrace()}") + } + + } + + + /** + * 根据mac查询 + * @param key + * @return + */ + fun getActionByMac(key: String?): List? { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(ActionInfo::class.java) + return box.query(ActionInfo_.mac.equal(key)).build().find() + } + } catch (e: java.lang.Exception) { + } + return ArrayList() + } + + /** + * 解析动作数据 + * 0开 1关 -1无效 + */ + fun getLastActionStatusByMac(key: String?): Int { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(ActionInfo::class.java) + var list = box.query(ActionInfo_.mac.equal(key)).build().find() + if(list!=null&&list.size>0){ + return BlueToothUtils.instance!!.getActionType(list[list.size-1].data!!) + }else{ + return -1 + } + } + } catch (e: java.lang.Exception) { + } + return -1 + } + + + + + /** + * 获取全部对象数据 + */ + fun getAllData(clazz: Class?): List? { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(clazz) + return box.all + } + } catch (e: java.lang.Exception) { + } + return ArrayList() + } + + + + /** + * 删除数据单个数据2 + */ + fun deleteData(o: Any, clazz: Class?) { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(clazz) + box.remove(o) + } + } catch (e: java.lang.Exception) { + } + } + + /** + * 删除列表数据 + */ + fun deleteALLData(o: List?, clazz: Class?) { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(clazz) + box.remove(o) + } + } catch (e: java.lang.Exception) { + } + } + + /** + * 删除设备数据 + */ + fun deleteALLDataD(o: List?) { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(DeviceInfo::class.java) + box.remove(o) + } + } catch (e: java.lang.Exception) { + } + } + + /** + * 删除动作数据 + */ + fun deleteALLDataA(o: List?) { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(ActionInfo::class.java) + box.remove(o) + } + } catch (e: java.lang.Exception) { + } + } + + /** + * 删除列表数据 + */ + fun deleteALLData(c: Class) { + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + val box: Box = boxStore.boxFor(c) + box.removeAll() + } + } catch (e: java.lang.Exception) { + } + } + + /** + * 删除所有数据 + */ + fun deleteDatabase(){ + try { + val boxStore = init() + if (boxStore != null && !boxStore.isClosed) { + boxStore.removeAllObjects() + } + } catch (e: java.lang.Exception) { + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/PreferencesUtils.java b/app/src/main/java/com/qidian/zhongkesmart/utils/PreferencesUtils.java new file mode 100644 index 0000000..c0d8613 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/PreferencesUtils.java @@ -0,0 +1,392 @@ +package com.qidian.zhongkesmart.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Base64; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.StreamCorruptedException; +import java.util.ArrayList; +import java.util.List; + +/** + * PreferencesUtils, easy to get or put data + *
    + * Preference Name + *
  • you can change preference name by {@link #PREFERENCE_NAME}
  • + *
+ *
    + * Put Value + *
  • put string {@link #putString(Context, String, String)}
  • + *
  • put int {@link #putInt(Context, String, int)}
  • + *
  • put long {@link #putLong(Context, String, long)}
  • + *
  • put float {@link #putFloat(Context, String, float)}
  • + *
  • put boolean {@link #putBoolean(Context, String, boolean)}
  • + *
+ *
    + * Get Value + *
  • get string {@link #getString(Context, String)}, + * {@link #getString(Context, String, String)}
  • + *
  • get int {@link #getInt(Context, String)}, + * {@link #getInt(Context, String, int)}
  • + *
  • get long {@link #getLong(Context, String)}, + * {@link #getLong(Context, String, long)}
  • + *
  • get float {@link #getFloat(Context, String)}, + * {@link #getFloat(Context, String, float)}
  • + *
  • get boolean {@link #getBoolean(Context, String)}, + * {@link #getBoolean(Context, String, boolean)}
  • + *
+ *

+ */ +public class PreferencesUtils { + + public static String PREFERENCE_NAME = "VillageArrivalCommon"; + + /** + * put string preferences + * + * @param context + * @param key The name of the preference to modify + * @param value The new value for the preference + * @return True if the new values were successfully written to persistent + * storage. + */ + public static boolean putString(Context context, String key, String value) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + editor.putString(key, value); + return editor.commit(); + } + + /** + * get string preferences + * + * @param context + * @param key The name of the preference to retrieve + * @return The preference value if it exists, or null. Throws + * ClassCastException if there is a preference with this name that + * is not a string + * @see #getString(Context, String, String) + */ + public static String getString(Context context, String key) { + return getString(context, key, null); + } + + /** + * get string preferences + * + * @param context + * @param key The name of the preference to retrieve + * @param defaultValue Value to return if this preference does not exist + * @return The preference value if it exists, or defValue. Throws + * ClassCastException if there is a preference with this name that + * is not a string + */ + public static String getString(Context context, String key, + String defaultValue) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + return settings.getString(key, defaultValue); + } + + /** + * put int preferences + * + * @param context + * @param key The name of the preference to modify + * @param value The new value for the preference + * @return True if the new values were successfully written to persistent + * storage. + */ + public static boolean putInt(Context context, String key, int value) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + editor.putInt(key, value); + return editor.commit(); + } + + /** + * get int preferences + * + * @param context + * @param key The name of the preference to retrieve + * @return The preference value if it exists, or -1. Throws + * ClassCastException if there is a preference with this name that + * is not a int + * @see #getInt(Context, String, int) + */ + public static int getInt(Context context, String key) { + return getInt(context, key, -1); + } + + /** + * get int preferences + * + * @param context + * @param key The name of the preference to retrieve + * @param defaultValue Value to return if this preference does not exist + * @return The preference value if it exists, or defValue. Throws + * ClassCastException if there is a preference with this name that + * is not a int + */ + public static int getInt(Context context, String key, int defaultValue) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + return settings.getInt(key, defaultValue); + } + + /** + * put long preferences + * + * @param context + * @param key The name of the preference to modify + * @param value The new value for the preference + * @return True if the new values were successfully written to persistent + * storage. + */ + public static boolean putLong(Context context, String key, long value) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + editor.putLong(key, value); + return editor.commit(); + } + + /** + * put long preferences + * + * @param context + * @param name Desired preferences file + * @param key The name of the preference to modify + * @param value The new value for the preference + * @return True if the new values were successfully written to persistent + * storage. + */ + public static boolean putLong(Context context, String name, String key, long value) { + SharedPreferences settings = context.getSharedPreferences(name, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + editor.putLong(key, value); + return editor.commit(); + } + + /** + * get long preferences + * + * @param context + * @param key The name of the preference to retrieve + * @return The preference value if it exists, or -1. Throws + * ClassCastException if there is a preference with this name that + * is not a long + * @see #getLong(Context, String, long) + */ + public static long getLong(Context context, String key) { + return getLong(context, key, -1); + } + + /** + * get long preferences + * + * @param context + * @param key The name of the preference to retrieve + * @return The preference value if it exists, or -1. Throws + * ClassCastException if there is a preference with this name that + * is not a long + * @see #getLong(Context, String, long) + */ + public static long getLong(Context context, String name, String key) { + return getLong(context, name, key, -1); + } + + /** + * get long preferences + * + * @param context + * @param key The name of the preference to retrieve + * @param defaultValue Value to return if this preference does not exist + * @return The preference value if it exists, or defValue. Throws + * ClassCastException if there is a preference with this name that + * is not a long + */ + public static long getLong(Context context, String key, long defaultValue) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + return settings.getLong(key, defaultValue); + } + + /** + * get long preferences + * + * @param context + * @param name Desired preferences file + * @param key The name of the preference to retrieve + * @param defaultValue Value to return if this preference does not exist + * @return The preference value if it exists, or defValue. Throws + * ClassCastException if there is a preference with this name that + * is not a long + */ + public static long getLong(Context context, String name, String key, long defaultValue) { + SharedPreferences settings = context.getSharedPreferences(name, Context.MODE_PRIVATE); + return settings.getLong(key, defaultValue); + } + + /** + * put float preferences + * + * @param context + * @param key The name of the preference to modify + * @param value The new value for the preference + * @return True if the new values were successfully written to persistent + * storage. + */ + public static boolean putFloat(Context context, String key, float value) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + editor.putFloat(key, value); + return editor.commit(); + } + + /** + * get float preferences + * + * @param context + * @param key The name of the preference to retrieve + * @return The preference value if it exists, or -1. Throws + * ClassCastException if there is a preference with this name that + * is not a float + * @see #getFloat(Context, String, float) + */ + public static float getFloat(Context context, String key) { + return getFloat(context, key, -1); + } + + /** + * get float preferences + * + * @param context + * @param key The name of the preference to retrieve + * @param defaultValue Value to return if this preference does not exist + * @return The preference value if it exists, or defValue. Throws + * ClassCastException if there is a preference with this name that + * is not a float + */ + public static float getFloat(Context context, String key, float defaultValue) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + return settings.getFloat(key, defaultValue); + } + + /** + * put boolean preferences + * + * @param context + * @param key The name of the preference to modify + * @param value The new value for the preference + * @return True if the new values were successfully written to persistent + * storage. + */ + public static boolean putBoolean(Context context, String key, boolean value) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + editor.putBoolean(key, value); + return editor.commit(); + } + + /** + * get boolean preferences, default is false + * + * @param context + * @param key The name of the preference to retrieve + * @return The preference value if it exists, or false. Throws + * ClassCastException if there is a preference with this name that + * is not a boolean + * @see #getBoolean(Context, String, boolean) + */ + public static boolean getBoolean(Context context, String key) { + return getBoolean(context, key, false); + } + + /** + * get boolean preferences + * + * @param context + * @param key The name of the preference to retrieve + * @param defaultValue Value to return if this preference does not exist + * @return The preference value if it exists, or defValue. Throws + * ClassCastException if there is a preference with this name that + * is not a boolean + */ + public static boolean getBoolean(Context context, String key, + boolean defaultValue) { + SharedPreferences settings = context.getSharedPreferences( + PREFERENCE_NAME, Context.MODE_PRIVATE); + return settings.getBoolean(key, defaultValue); + } + + public static boolean putList2(Context context, String key, List value) { + String str_value = ""; + try { + str_value = SceneList2String2(value); + putString(context, key, str_value); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + public static List getList2(Context context, String key) { + + List mlist = new ArrayList(); + try { + String str_value = getString(context, key); + mlist = String2SceneList2(str_value); + return mlist; + } catch (Exception e) { + + } + + return mlist; + } + + //将list集合转换成字符串 + public static String SceneList2String2(List SceneList) throws IOException { +// 实例化一个ByteArrayOutputStream对象,用来装载压缩后的字节文件。 + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); +// 然后将得到的字符数据装载到ObjectOutputStream + ObjectOutputStream objectOutputStream = new ObjectOutputStream( + byteArrayOutputStream); +// writeObject 方法负责写入特定类的对象的状态,以便相应的 readObject 方法可以还原它 + objectOutputStream.writeObject(SceneList); +// 最后,用Base64.encode将字节文件转换成Base64编码保存在String中 + String SceneListString = new String(Base64.encode( + byteArrayOutputStream.toByteArray(), Base64.DEFAULT)); +// 关闭objectOutputStream + objectOutputStream.close(); + return SceneListString; + } + //将字符串转换成list集合 + + @SuppressWarnings("unchecked") + public static List String2SceneList2(String SceneListString) + throws StreamCorruptedException, IOException, + ClassNotFoundException { + byte[] mobileBytes = Base64.decode(SceneListString.getBytes(), + Base64.DEFAULT); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( + mobileBytes); + ObjectInputStream objectInputStream = new ObjectInputStream( + byteArrayInputStream); + List SceneList = (List) objectInputStream.readObject(); + objectInputStream.close(); + return SceneList; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/SPUtil.java b/app/src/main/java/com/qidian/zhongkesmart/utils/SPUtil.java new file mode 100644 index 0000000..54b6e48 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/SPUtil.java @@ -0,0 +1,244 @@ +package com.qidian.zhongkesmart.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Base64; +import android.util.Log; + + +import com.qidian.zhongkesmart.base.AppApplication; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.StreamCorruptedException; +import java.lang.reflect.InvocationTargetException; + +/** + * Created by sgll on 2019/1/15. + * SharedPreferences工具类 + * 保存的数据类型有: + * 1、四种基本类型数据(int,float,long,boolean)+ String + * 2、List类型的数据 + * 3、存储序列化对象 + * 4、存储Map集合 + */ +public class SPUtil { + private static String SP_NAME = "ship_sp"; + private static String TAG = "SharedPreferenceUtil"; + + /** + * 获取SharedPreference保存的value + * 值的类型:(int,float,long,boolean)+String + * + * @param key 储存值的key + * @param clazz 获取值的类型 int,float,long,boolean)+String + * @param T + * @return 值 + */ + public static T getValue(String key, Class clazz) { + SharedPreferences sp = AppApplication.mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + return getValue(key, getT(clazz), sp); + } + + /** + * 获取SharedPreference保存的value + * 值的类型:(int,float,long,boolean)+String + * + * @param key 储存值的key + * @param sp SharedPreferences + * @param T + * @return 值 + */ + private static T getValue(String key, T t, SharedPreferences sp) { +// if (t == null) { +// return null; +// } + if (t instanceof Integer) { + return (T) Integer.valueOf(sp.getInt(key, 0)); + } else if (t instanceof String) { + return (T) sp.getString(key, ""); + } else if (t instanceof Boolean) { + return (T) Boolean.valueOf(sp.getBoolean(key, false)); + } else if (t instanceof Long) { + return (T) Long.valueOf(sp.getLong(key, 0L)); + } else if (t instanceof Float) { + return (T) Float.valueOf(sp.getFloat(key, 0L)); + }else { + return getObject(sp,key); + } + } + + /** + * 通过反射创建类实例 + * 反射有两种方式创建类实例: + * 1、Class.newInstance():只能调用无参的构造函数(默认构造函数) + * 2、Constructor.newInstance():可以根据传入的参数,调用任意构造构造函数。 + * Integer、Boolean、Long、Float 没有默认构造函数,只能通过 Constructor.newInstance() 调用 + * String 是有默认构造函数的,两种方法都适用 + * @param clazz String.class、Integer.class、Boolean.class、Long.class、Float.class + * @param T + * @return T + */ + private static T getT(Class clazz) { + T t = null; + String clazzName = clazz.getName(); + try { + if ("java.lang.Integer".equals(clazzName)) { + t = clazz.getConstructor(int.class).newInstance(1); + } else if ("java.lang.String".equals(clazzName)) { + t = clazz.newInstance(); + } else if ("java.lang.Boolean".equals(clazzName)) { + t = clazz.getConstructor(boolean.class).newInstance(false); + } else if ("java.lang.Long".equals(clazzName)) { + t = clazz.getConstructor(long.class).newInstance(0L); + } else if ("java.lang.Float".equals(clazzName)) { + t = clazz.getConstructor(float.class).newInstance(0F); + } + } catch (InstantiationException e) { + Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]"); + e.printStackTrace(); + } catch (IllegalAccessException e) { + Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]"); + e.printStackTrace(); + } catch (InvocationTargetException e) { + Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]"); + e.printStackTrace(); + } catch (NoSuchMethodException e) { + Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]"); + e.printStackTrace(); + } + + return t; + } + + /** + * 使用SharedPreference保存value + * + * @param key 储存值的key + * @param value 值 + */ + public static void setValue(String key, Object value) { + SharedPreferences sp = AppApplication.mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + setValue(key, value, sp); + } + + /** + * 使用SharedPreference保存value + * + * @param key 储存值的key + * @param value 值 + * @param sp SharedPreferences + */ + private static void setValue(String key, Object value, SharedPreferences sp) { + SharedPreferences.Editor editor = sp.edit(); + if (value instanceof Integer) { + editor.putInt(key, (Integer) value); + } else if (value instanceof String) { + editor.putString(key, (String) value); + } else if (value instanceof Boolean) { + editor.putBoolean(key, (Boolean) value); + } else if (value instanceof Long) { + editor.putLong(key, (Long) value); + } else if (value instanceof Float) { + editor.putFloat(key, (Float) value); + }else { + setObject(sp,key,value); + } + editor.commit(); + } + + /** + * 使用SharedPreference保存序列化对象 + * 用Base64.encode将字节文件转换成Base64编码保存在String中 + * + * @param context 上下文 + * @param key 储存对象的key + * @param object object对象 对象必须实现Serializable序列化,否则会出问题, + * out.writeObject 无法写入 Parcelable 序列化的对象 + */ + private static void setObject(Context context, String key, Object object) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + setObject(sp,key,object); + } + + private static void setObject(SharedPreferences sp, String key, Object object) { + //创建字节输出流 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + //创建字节对象输出流 + ObjectOutputStream out = null; + try { + //然后通过将字对象进行64转码,写入sp中 + out = new ObjectOutputStream(baos); + out.writeObject(object); + String objectValue = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT)); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(key, objectValue); + editor.commit(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (baos != null) { + baos.close(); + } + + if (out != null) { + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取SharedPreference保存的对象 + * 使用Base64解密String,返回Object对象 + * + * @param context 上下文 + * @param key 储存对象的key + * @param 泛型 + * @return 返回保存的对象 + */ + private static T getObject(Context context, String key) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + return getObject(sp,key); + } + private static T getObject(SharedPreferences sp, String key) { + if (sp.contains(key)) { + String objectValue = sp.getString(key, null); + byte[] buffer = Base64.decode(objectValue, Base64.DEFAULT); + //一样通过读取字节流,创建字节流输入流,写入对象并作强制转换 + ByteArrayInputStream bais = new ByteArrayInputStream(buffer); + ObjectInputStream ois = null; + try { + ois = new ObjectInputStream(bais); + T t = (T) ois.readObject(); + return t; + } catch (StreamCorruptedException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } finally { + try { + if (bais != null) { + bais.close(); + } + + if (ois != null) { + ois.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/SPUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/SPUtils.kt new file mode 100644 index 0000000..e2fb5b1 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/SPUtils.kt @@ -0,0 +1,57 @@ +package com.qidian.zhongkesmart.utils + +import android.content.Context +import android.content.SharedPreferences +import com.qidian.zhongkesmart.base.AppApplication +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +class SPUtils(val name: String, val default: T) : ReadWriteProperty { + + companion object { + private val file_name = AppApplication.mContext!!.packageName + + private val prefs: SharedPreferences by lazy { + AppApplication.mContext!!.getSharedPreferences(file_name, Context.MODE_PRIVATE) + } + + fun clearPreference(){ + prefs.edit().clear().apply() + } + + fun clearPreference(key : String){ + prefs.edit().remove(key).apply() + } + } + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return findPreference(name, default) + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + putPreference(name, value) + } + + private fun findPreference(name: String, default: T): T = with(prefs) { + return when (default) { + is Long -> getLong(name, default) + is String -> getString(name, default) + is Int -> getInt(name, default) + is Boolean -> getBoolean(name, default) + is Float -> getFloat(name, default) + else -> throw IllegalArgumentException("Unsupport type") + } as T + } + + private fun putPreference(name: String, value: T) = with(prefs.edit()) { + when (value) { + is Long -> putLong(name, value) + is String -> putString(name, value) + is Int -> putInt(name, value) + is Boolean -> putBoolean(name, value) + is Float -> putFloat(name, value) + else -> throw IllegalArgumentException("Unsupport type") + }.apply() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/ScreenUtils.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/ScreenUtils.kt new file mode 100644 index 0000000..a6fb1b7 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/ScreenUtils.kt @@ -0,0 +1,57 @@ +package com.qidian.zhongkesmart.utils + +import android.content.res.Resources +import kotlin.math.roundToInt + +/** + * Create by Lee5ins + * on 2019/5/17 + */ +object ScreenUtils { + fun getScreenWidth(): Int { + val resources: Resources = Resources.getSystem() + val dm = resources.displayMetrics + return dm.widthPixels + } + + fun getScreenHeight(): Int { + val resources: Resources = Resources.getSystem() + val dm = resources.displayMetrics + return dm.heightPixels + } + + + fun pxToDp(px: Float): Float { + val resources: Resources = Resources.getSystem() + val displayMetrics = resources.displayMetrics + val densityDpi = displayMetrics.densityDpi.toFloat() + return px / (densityDpi / 160f) + } + + fun dpToPx(dp: Float): Int { + val resources: Resources = Resources.getSystem() + val density = resources.displayMetrics.density + return (dp * density).roundToInt() + } + + fun dpToPx(dp: Int): Int { + val resources: Resources = Resources.getSystem() + val density = resources.displayMetrics.density + return (dp * density).roundToInt() + } + + fun getStatusBarHeight(): Int { + var result = 0 + val resources: Resources = Resources.getSystem() + val resourceId: Int = + resources.getIdentifier("status_bar_height", "dimen", "android") + if (resourceId > 0) { + result = resources.getDimensionPixelSize(resourceId) + } + return result + } + + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/StatusBarUtil.java b/app/src/main/java/com/qidian/zhongkesmart/utils/StatusBarUtil.java new file mode 100644 index 0000000..e2b38c9 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/StatusBarUtil.java @@ -0,0 +1,109 @@ +package com.qidian.zhongkesmart.utils; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import androidx.fragment.app.FragmentActivity; + +import com.gyf.immersionbar.ImmersionBar; + +public class StatusBarUtil { + + //沉浸式状态栏 + public static void setImmersionBar(Context context){ + ImmersionBar.with((FragmentActivity)context) + .statusBarDarkFont(false, 0.2f) + .init(); + } + + public static void setImmersionBar(Context context,boolean isDark){ + ImmersionBar.with((FragmentActivity)context) + .reset() + .statusBarDarkFont(isDark, 0.2f) + .init(); + } + + //设置状态栏颜色 + public static void setStatusBarColor(Context context,int colorRes){ + ImmersionBar.with((FragmentActivity)context) + .statusBarDarkFont(true, 0.2f) + .fitsSystemWindows(true) + .statusBarColor(colorRes) + .keyboardEnable(false) + .init(); + } + + //设置状态栏高度 + public static void setHeight(Context context, View view){ + ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) view.getLayoutParams(); + layoutParams.height = ImmersionBar.getStatusBarHeight((Activity) context); + view.setLayoutParams(layoutParams); + } + + //获取状态栏高度 + public static int getStatusBarHeight(Context context){ + return ImmersionBar.getStatusBarHeight((Activity) context); + } + + /** 增加View上边距(MarginTop)一般是给高度为 WARP_CONTENT 的小控件用的*/ + public static void setMargin(Context context, View view) { + if (Build.VERSION.SDK_INT >= 19) { + ViewGroup.LayoutParams lp = view.getLayoutParams(); + if (lp instanceof ViewGroup.MarginLayoutParams) { + ((ViewGroup.MarginLayoutParams) lp).topMargin += getStatusBarHeight(context);//增高 + } + view.setLayoutParams(lp); + } + } + + /** 增加View的paddingTop,增加的值为状态栏高度 (智能判断,并设置高度)*/ + public static void setPaddingSmart(Context context, View view) { + if (Build.VERSION.SDK_INT >= 19) { + ViewGroup.LayoutParams lp = view.getLayoutParams(); + if (lp != null && lp.height > 0) { + lp.height += getStatusBarHeight(context);//增高 + } + view.setPadding(view.getPaddingLeft(), view.getPaddingTop() + getStatusBarHeight(context), + view.getPaddingRight(), view.getPaddingBottom()); + } + } + + // 隐藏状态栏 + public static void hideStatusBar(Activity activity) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + View decorView = activity.getWindow().getDecorView(); + activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + decorView.setSystemUiVisibility(uiOptions); + } + } + + // 恢复为不全屏状态 + public static void setDecorVisible(Activity activity) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + View decorView = activity.getWindow().getDecorView(); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + int uiOptions = View.SYSTEM_UI_FLAG_VISIBLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + decorView.setSystemUiVisibility(uiOptions); + } + } + + + public static void hideNavigationBar(Activity activity){ + int uiOptions=activity.getWindow().getDecorView().getSystemUiVisibility(); + activity.getWindow().getDecorView().setSystemUiVisibility(uiOptions|View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/TimeUtil.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/TimeUtil.kt new file mode 100644 index 0000000..e092130 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/TimeUtil.kt @@ -0,0 +1,744 @@ +package com.qidian.zhongkesmart.utils + +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi +import com.blankj.utilcode.util.TimeUtils +import com.qidian.zhongkesmart.model.* +import java.text.SimpleDateFormat +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAdjusters +import java.util.* +import kotlin.collections.ArrayList + + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2022/6/21 0021 16:13 + */ +object TimeUtil { + + /** + * 获取当前时间 + */ + fun getNowTime(): String? { + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + val curDate = Date(System.currentTimeMillis()) //获取当前时间 + return formatter.format(curDate) + } + + /** + * 获取当前时间 + */ + fun getNowTimeHourMinute(): String? { + val formatter = SimpleDateFormat("HH:mm") + val curDate = Date(System.currentTimeMillis()) //获取当前时间 + return formatter.format(curDate) + } + + /** + * 获取当前时间 + */ + fun getNowTimeHourMinuteSecond(): String? { + val formatter = SimpleDateFormat("HH:mm:ss") + val curDate = Date(System.currentTimeMillis()) //获取当前时间 + return formatter.format(curDate) + } + + /** + * 获取当前时间 + */ + fun getNowTimeMonthDay(): String? { + val formatter = SimpleDateFormat("MM月dd号") + val curDate = Date(System.currentTimeMillis()) //获取当前时间 + return formatter.format(curDate) + } + + fun getNowTimeMonthDayFormat(): String? { + var dataStr = "" + val formatter = SimpleDateFormat("MM月dd号") + val curDate = Date(System.currentTimeMillis()) //获取当前时间 + dataStr = formatter.format(curDate) + if (dataStr.startsWith("0")) { + return dataStr.substring(1) + } else { + return dataStr + } + } + + /** + * 获取当前时间 + */ + fun getNowTimeLong(): Long? { + return System.currentTimeMillis() + } + + /** + * 获取当前和某刻的时间差 + */ + fun getTimeDifference(time: Long): Long { + return System.currentTimeMillis() - time + } + + /** + * 日期转换 + */ + fun getYMD(time: Long): String { + val formatter = SimpleDateFormat("yy:MM:dd") + val curDate = Date(time) + return formatter.format(curDate) + } + + /** + * 获取该时刻对应的小时数 + */ + fun getTimeHour(time: Long): Int { + val formatter = SimpleDateFormat("HH") + return formatter.format(time).toInt() + } + + //将本日的数据按小时数切割 + fun formatTodayData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + var hour = getTimeHour(it.openTime) + for (i in 0..24) { + if (hour in i..i + 1) { +// it.timeLocation = "${i}-${i + 1}" + it.timeLocation = "${i}点" + } + } + } + //将数据转成符合柱状图展示的数据 + var num = 0 + DataServer.mTodayTime.forEach { + num = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num++ + } + } + linvList.add(LineViewData(num, it)) + } + return linvList + } + + //将本周的数据按天数切割 + fun formatWeekData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getWeekDay(it.openTime) + } + //将数据转成符合柱状图展示的数据 + var num = 0 + DataServer.mWeekTime.forEach { + num = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num++ + } + } + linvList.add(LineViewData(num, it)) + } + return linvList + } + + //将本月的数据按天数切割 + fun formatMonthData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getCurrentMonthDay(it.openTime) + "号" + } + //将数据转成符合柱状图展示的数据 + var num = 0 + var currentMonthDays = getCurrentMonthDayS() + var mMonthTime = ArrayList() + for (i in 1..currentMonthDays) { +// if (i < 10) { +// mMonthTime.add("0${i}号") +// } else { + mMonthTime.add("${i}号") +// } + } + mMonthTime.forEach { + num = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num++ + } + } + linvList.add(LineViewData(num, it)) + } + return linvList + } + + //将本年的数据按月份切割 + fun formatYearData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getCurrentMonth(it.openTime) + "月" + } + //将数据转成符合柱状图展示的数据 + var num = 0 + DataServer.mYearTime.forEach { + num = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num++ + } + } + linvList.add(LineViewData(num, it)) + } + return linvList + } + + //将本日的数据按小时数切割 + fun formatTodayDataNew(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + var hour = getTimeHour(it.openTime) + for (i in 0..24) { + if (hour in i..i + 1) { +// it.timeLocation = "${i}-${i + 1}" + it.timeLocation = "${i}点" + } + } + } + //将数据转成符合柱状图展示的数据 + DataServer.mTodayTime.forEach { + var num = 0 + var num1 = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + if(BlueToothUtils.instance!!.getActionType(itlist.data)==0){ + num++ + } + if(BlueToothUtils.instance!!.getActionType(itlist.data)==1){ + num1++ + } + } + } + linvList.add(LineViewData1(num,num1, it)) + } + return linvList + } + + //将本周的数据按天数切割 + fun formatWeekDataNew(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getWeekDay(it.openTime) + } + //将数据转成符合柱状图展示的数据 + DataServer.mWeekTime.forEach { + var num = 0 + var num1 = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + if(BlueToothUtils.instance!!.getActionType(itlist.data)==0){ + num++ + } + if(BlueToothUtils.instance!!.getActionType(itlist.data)==1){ + num1++ + } + } + } + linvList.add(LineViewData1(num,num1, it)) + } + return linvList + } + + //将本月的数据按天数切割 + fun formatMonthDataNew(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getCurrentMonthDay(it.openTime) + "号" + } + //将数据转成符合柱状图展示的数据 + var currentMonthDays = getCurrentMonthDayS() + var mMonthTime = ArrayList() + for (i in 1..currentMonthDays) { +// if (i < 10) { +// mMonthTime.add("0${i}号") +// } else { + mMonthTime.add("${i}号") +// } + } + mMonthTime.forEach { + var num = 0 + var num1 = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + if(BlueToothUtils.instance!!.getActionType(itlist.data)==0){ + num++ + } + if(BlueToothUtils.instance!!.getActionType(itlist.data)==1){ + num1++ + } + } + } + linvList.add(LineViewData1(num,num1, it)) + } + return linvList + } + + //将本年的数据按月份切割 + fun formatYearDataNew(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getCurrentMonth(it.openTime) + "月" + } + //将数据转成符合柱状图展示的数据 + DataServer.mYearTime.forEach { + var num = 0 + var num1 = 0 + list.forEach { itlist -> + if (itlist.timeLocation == it) { + if(BlueToothUtils.instance!!.getActionType(itlist.data)==0){ + num++ + } + if(BlueToothUtils.instance!!.getActionType(itlist.data)==1){ + num1++ + } + } + } + linvList.add(LineViewData1(num,num1, it)) + } + return linvList + } + + //将本日的数据按小时数切割 + fun formatTodayDurationData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + var hour = getTimeHour(it.openTime) + for (i in 0..24) { + if (hour in i..i + 1) { +// it.timeLocation = "${i}-${i + 1}" + it.timeLocation = "${i}点" + } + } + } + //将数据转成符合柱状图展示的数据 + var num = 0L + DataServer.mTodayTime.forEach { + num = 0L + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num+= itlist.timelength + } + } + linvList.add(LineViewData((num/(60*60*1000)).toInt(), it)) + } + return linvList + } + + //将本周的数据按天数切割 + fun formatWeekDurationData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getWeekDay(it.openTime) + } + //将数据转成符合柱状图展示的数据 + var num = 0L + DataServer.mWeekTime.forEach { + num = 0L + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num+= itlist.timelength + } + } + linvList.add(LineViewData((num/(60*60*1000)).toInt(), it)) + } + return linvList + } + + //将本月的数据按天数切割 + fun formatMonthDurationData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getCurrentMonthDay(it.openTime) + "号" + } + //将数据转成符合柱状图展示的数据 + var num = 0L + var currentMonthDays = getCurrentMonthDayS() + var mMonthTime = ArrayList() + for (i in 1..currentMonthDays) { +// if (i < 10) { +// mMonthTime.add("0${i}号") +// } else { + mMonthTime.add("${i}号") +// } + } + mMonthTime.forEach { + num = 0L + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num+= itlist.timelength + } + } + linvList.add(LineViewData((num/(60*60*1000)).toInt(), it)) + } + return linvList + } + + //将本年的数据按月份切割 + fun formatYearDurationData(list: ArrayList): ArrayList { + var linvList = ArrayList() + list.forEach { + it.timeLocation = getCurrentMonth(it.openTime) + "月" + } + //将数据转成符合柱状图展示的数据 + var num = 0L + DataServer.mYearTime.forEach { + num = 0L + list.forEach { itlist -> + if (itlist.timeLocation == it) { + num+= itlist.timelength + } + } + linvList.add(LineViewData((num/(60*60*1000)).toInt(), it)) + } + return linvList + } + + var TAG = "时间处理:" + + /** + * 判断某刻时间是否属于本天 + */ + fun isInToday(time: Long): Boolean { + val now: LocalDateTime = LocalDateTime.now() + val fmt: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") + val dayStart: Long = + now.with(LocalTime.MIN).atOffset(ZoneOffset.UTC).toInstant().toEpochMilli() + val dayEnd: Long = + now.with(LocalTime.MAX).atOffset(ZoneOffset.UTC).toInstant().toEpochMilli() + Log.i(TAG, "当前年的开始时间==${now.with(LocalTime.MIN).format(fmt)}") + Log.i(TAG, "当前年的结束时间==${now.with(LocalTime.MAX).format(fmt)}") + var isinToday = false + if (time in dayStart..dayEnd) { + isinToday = true + } + return isinToday + } + + /** + * 判断该如期是否为本周 + */ + fun isInWeek(time: Long): Boolean { + val now: LocalDateTime = LocalDateTime.now() + val fmt: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") + + val dayOfWeek: Int = now.dayOfWeek.value + val weekStart: Long = + now.minusDays((dayOfWeek - 1).toLong()).with(LocalTime.MIN).atOffset(ZoneOffset.UTC) + .toInstant().toEpochMilli() + val weekEnd: Long = + now.plusDays((7 - dayOfWeek).toLong()).with(LocalTime.MAX).atOffset(ZoneOffset.UTC) + .toInstant().toEpochMilli() + + Log.i( + TAG, + "当前周的开始时间==${now.minusDays((dayOfWeek - 1).toLong()).with(LocalTime.MIN).format(fmt)}" + ) + Log.i( + TAG, + "当前周的结束时间==${now.plusDays((7 - dayOfWeek).toLong()).with(LocalTime.MAX).format(fmt)}" + ) + + var isinWeek = false + if (time in weekStart..weekEnd) { + isinWeek = true + } + return isinWeek + } + + /** + * 判断某日期是否在本月 + */ + fun isInMonth(time: Long): Boolean { + val now: LocalDateTime = LocalDateTime.now() + val fmt: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") + + val monthStart: Long = + now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN) + .atOffset(ZoneOffset.UTC).toInstant().toEpochMilli() + val monthEnd: Long = + now.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX) + .atOffset(ZoneOffset.UTC).toInstant().toEpochMilli() + + Log.i( + TAG, + "当前月的开始时间==${ + now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN).format(fmt) + }" + ) + Log.i( + TAG, + "当前月的结束时间==${ + now.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX).format(fmt) + }" + ) + + + var isinMonth = false + if (time in monthStart..monthEnd) { + isinMonth = true + } + return isinMonth + } + + /** + * 判断某刻是否在本年 + */ + fun isInYear(time: Long): Boolean { + val now: LocalDateTime = LocalDateTime.now() + val fmt: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") + + val yearStart: Long = + now.with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN) + .atOffset(ZoneOffset.UTC).toInstant().toEpochMilli() + val yearEnd: Long = + now.with(TemporalAdjusters.lastDayOfYear()).with(LocalTime.MAX).atOffset(ZoneOffset.UTC) + .toInstant().toEpochMilli() + + Log.i( + TAG, + "当前年的开始时间==${ + now.with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN).format(fmt) + }" + ) + Log.i( + TAG, + "当前年的结束时间==${ + now.with(TemporalAdjusters.lastDayOfYear()).with(LocalTime.MAX).format(fmt) + }" + ) + + var isinYear = false + if (time in yearStart..yearEnd) { + isinYear = true + } + return isinYear + + + } + + /** + * 获取本周的开始日期 + */ + @RequiresApi(Build.VERSION_CODES.O) + fun getWeekStartDay() { + val now: LocalDateTime = LocalDateTime.now() + val fmt: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") + + val yearStart: LocalDateTime = + now.with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN) + val yearEnd: LocalDateTime = now.with(TemporalAdjusters.lastDayOfYear()).with(LocalTime.MAX) + System.out.println("当前年的开始时间:" + yearStart.format(fmt)) + System.out.println("当前年的结束时间:" + yearEnd.format(fmt)) + + val monthStart: LocalDateTime = + now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN) + val monthEnd: LocalDateTime = + now.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX) + System.out.println("当前月的开始时间:" + monthStart.format(fmt)) + System.out.println("当前月的结束时间:" + monthEnd.format(fmt)) + + val dayOfWeek: Int = now.getDayOfWeek().getValue() + val weekStart: LocalDateTime = now.minusDays((dayOfWeek - 1).toLong()).with(LocalTime.MIN) + val weekEnd: LocalDateTime = now.plusDays((7 - dayOfWeek).toLong()).with(LocalTime.MAX) + System.out.println("当前周的开始时间:" + weekStart.format(fmt)) + System.out.println("当前周的结束时间:" + weekEnd.format(fmt)) + + val dayStart: LocalDateTime = now.with(LocalTime.MIN) + val dayEnd: LocalDateTime = now.with(LocalTime.MAX) + System.out.println("当天的开始时间:" + dayStart.format(fmt)) + System.out.println("当天的结束时间:" + dayEnd.format(fmt)) + + } + + /** + * 获取某刻为周几 + */ + fun getWeekDay(time: Long): String { + var week = "" + val alldate2 = SimpleDateFormat("EEEE") //获取日期时间——EEEE 获取到的就是当前星期几 + week = alldate2.format(time) + return week + } + + + /** + * 取得当月天数 + */ + fun getCurrentMonthDayS(): Int { + val a = Calendar.getInstance() + a[Calendar.DATE] = 1 //把日期设置为当月第一天 + a.roll(Calendar.DATE, -1) //日期回滚一天,也就是最后一天 + return a[Calendar.DATE] + } + + /** + * 取得某刻为该月的即日 + */ + fun getCurrentMonthDay(time: Long): String { + var day = "" + val alldate2 = SimpleDateFormat("dd") + day = alldate2.format(time) + return day + } + + /** + * 取得某刻为该年的几月 + */ + fun getCurrentMonth(time: Long): String { + var Month = "" + val alldate2 = SimpleDateFormat("MM") + Month = alldate2.format(time) + if (alldate2.format(time).startsWith("0")) { + Month = alldate2.format(time).replace("0", "") + } + return Month + } + + /** + * 离线任务 + * 每天00:00 时要求贴件 定时发送一次数据,当网关未收到贴件上传的数据时认为该贴件为离线状态 + */ + //当前是否是00:00 添加延迟时间3秒 触发离线任务时间点 + fun isZeroHour(): Boolean { + var isZero = false + var nowTime = System.currentTimeMillis() + var mZeroHour = getTodayZero() + if (nowTime in mZeroHour..mZeroHour + 3000) { + isZero = true + } + return isZero + } + + //获取当天0点毫秒值 + open fun getTodayZero(): Long { + val date = Date() + val l = (24 * 60 * 60 * 1000).toLong() //每天的毫秒数 + //date.getTime()是现在的毫秒数,它 减去 当天零点到现在的毫秒数( 现在的毫秒数%一天总的毫秒数,取余。),理论上等于零点的毫秒数,不过这个毫秒数是UTC+0时区的。 + //减8个小时的毫秒值是为了解决时区的问题。 + return date.time - date.time % l - 8 * 60 * 60 * 1000 + } + + /** + * 获取贴件时长(秒 当前开或关了多久) + * 识别类型(VB:震动型: DP:位移型) + * + * 1. 贴件新增数据时 + * 位移型:新增数据 + * 震动型:新增第一条数据 + */ + fun getStateContinuousTime(mac: String): Int { + var bean: BlueToothData? = null + var continuteTime = 0 + DataServer.MBlueToothData.forEach { + if (mac == it.mac) { + bean = it + } + } + if (bean == null) { + return 0 + } + var tomeRecord = bean!!.tomeRecord + if (bean!!.type == "DP") {//移动型 + if (bean!!.open) {//此次操作为开启 + continuteTime = 0//默认每次开启时候开启时长为0 + /*if (tomeRecord.size == 1) {//第一次获取到 默认为开启 即查询上次关闭到此次开启中间时长 即关闭时长 + continuteTime = 0 + } else { + var tomeRecordlast = bean!!.tomeRecord[bean!!.tomeRecord.size - 2] + var tomeRecordEnd = bean!!.tomeRecord[bean!!.tomeRecord.size - 1] + continuteTime = + ((tomeRecordEnd.openTime!! - tomeRecordlast.openTime).toDouble() / 1000).toInt() + }*/ + } else {//此次操作为关闭 + continuteTime = (tomeRecord[tomeRecord.size - 1].timelength / 1000).toInt() + } + } else if (bean!!.type == "VB") {//震动型 + if(bean!!.open){ + continuteTime =0 + }else { + continuteTime =bean!!.tomeRecord[bean!!.tomeRecord.size - 1].timelength.toInt() + } + } + return continuteTime + } + + fun getStateContinuousTimeNew(mac: String): Int { + var continuteTime = 0 + var aList = ObjectBoxUtils.getActionByMac(mac) + if(!aList.isNullOrEmpty()&&BlueToothUtils.instance!!.getActionType(aList[aList.size-1].data!!)!=-1) { + var openTime = aList[aList.size - 1].time + continuteTime = (getTimeDifference(TimeUtils.string2Millis(openTime)) / 1000).toInt() + } + return continuteTime + } + + + /** + * 获取打开次数 + */ + fun getTurnOnFrequency(size: Int):Int{ + var frequency = 0 + if (size % 2 == 0) + frequency = size/2 + else + frequency = (size+1)/2 + + return frequency + } + + /** + * 获取关闭次数 + */ + fun getTurnOffFrequency(size: Int):Int{ + var frequency = 0 + if (size % 2 == 0) + frequency = size/2 + else + frequency = (size-1)/2 + + return frequency + } + + + /** + * 获取打开次数 + */ + fun getTurnOnFrequency(list:ArrayList):Int{ + var frequency = 0 + list.forEach{ + if(BlueToothUtils.instance!!.getActionType(it.data!!)==0){ + frequency++ + } + } + return frequency + } + + /** + * 获取关闭次数 + */ + fun getTurnOffFrequency(list:ArrayList):Int{ + var frequency = 0 + list.forEach{ + if(BlueToothUtils.instance!!.getActionType(it.data!!)==1){ + frequency++ + } + } + return frequency + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/ToastUtil.java b/app/src/main/java/com/qidian/zhongkesmart/utils/ToastUtil.java new file mode 100644 index 0000000..1af8d8d --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/ToastUtil.java @@ -0,0 +1,41 @@ +package com.qidian.zhongkesmart.utils; + +import android.content.Context; +import android.graphics.Color; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.qidian.zhongkesmart.R; +import com.qidian.zhongkesmart.base.AppApplication; + + +public class ToastUtil { + + public static void showToast(String showStr){ + showBlackBottomToast(AppApplication.mContext,showStr); + } + + /** + * 黑色背景 、白色字体 、位置:中下部 + * @param mcontext + * @param showStr + */ + public static void showBlackBottomToast(Context mcontext,String showStr){ + if(TextUtils.isEmpty(showStr))return; + Toast toast = Toast.makeText(AppApplication.mContext, showStr, Toast.LENGTH_SHORT); + TextView view = new TextView(mcontext); + view.setBackgroundResource(R.drawable.toastbackground); + view.setTextColor(Color.WHITE); + view.setText(showStr); + view.setPadding(20, 15, 20, 15); + toast.setGravity(Gravity.BOTTOM, 0, 300); + toast.setView(view); + toast.show(); + } + + +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/ToolUtil.kt b/app/src/main/java/com/qidian/zhongkesmart/utils/ToolUtil.kt new file mode 100644 index 0000000..6bd45fe --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/ToolUtil.kt @@ -0,0 +1,667 @@ +package com.qidian.zhongkesmart.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Bitmap +import android.net.wifi.WifiInfo +import android.net.wifi.WifiManager +import android.os.Build +import android.provider.ContactsContract +import android.text.TextUtils +import android.util.Log +import com.alibaba.fastjson.JSON +import com.alibaba.fastjson.serializer.SerializerFeature +import com.google.gson.Gson +import com.google.zxing.BarcodeFormat +import com.google.zxing.MultiFormatWriter +import com.google.zxing.WriterException +import com.qidian.zhongkesmart.R +import com.qidian.zhongkesmart.model.* +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.set + + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2022/6/21 0021 16:13 + */ +object ToolUtil { + + + @SuppressLint("MissingPermission") + fun getWifiName(name: String): String? { + if (name.contains("\"")) { + return name.replace("\"", "").replace("\"", ""); + } else { + return name + } + } + + /** + * 判断是否包含某些字符 + */ + fun getLimmit(name: String): Boolean { + var ishave = false + if (name == "/" || name == "." || name == ":") { + ishave = true + } + return ishave + } + + + /** + * 在jsonString 外面包裹[] 将格式改为数组类型json + */ + fun JsonToListJson(json: String): String { + return "[${json}]" + } + + /** + * mac格式处理 + * xx:xx:xx:xx:xx:xx ->cf6a12483f37 + */ + fun formatMac(mac: String): String { + var formatmac = mac + if (mac.contains(":")) { + formatmac = mac.replace(":", "") + } + return formatmac.toLowerCase() + } + + /** + * mac格式处理 + * xx:xx:xx:xx:xx:xx ->CF6A12483F37 + */ + fun formatMacUpperCase(mac: String): String { + var formatmac = mac + if (mac.contains(":")) { + formatmac = mac.replace(":", "") + } + return formatmac.toUpperCase() + } + + /** + * 数组存本地 + */ + fun addHistoryList(context: Context, historyName: String, mVals: ArrayList) { + var json = getJson(mVals) +// Log.i("绑定设备历史记录", json!!) + //贴件绑定信息-存储本地 +// PreferencesUtils.putString(context, historyName, json) + SPUtil.setValue(historyName, json) + } + + /** + * 清除本地数据 + */ + fun clearHistory(context: Context, historyName: String) { +// PreferencesUtils.putString(context, historyName, "") + SPUtil.setValue(historyName, "") + getHistory(historyName, context) + } + + fun addHistory(context: Context, historyName: String, mVals: BindBlueToothData) { + var list = getHistory(historyName, context) + /*if (list.isNullOrEmpty()) { + return + }*/ + var isContains = false + list.forEach { + if (it.mac == mVals.mac) { + isContains = true + } + } + if (!isContains) { + list.add(mVals) + addHistoryList(context, historyName, list) + var json = getJson(list) + Log.i("绑定设备历史记录", json!!) +// PreferencesUtils.putString(context, historyName, json) + SPUtil.setValue(historyName, json) + } + } + + + //把对象转化成Json类型的字符串 , SerializerFeature.WriteMapNullValue + fun getJson(`object`: Any): String? { + val hashMap = HashMap() + hashMap["list"] = `object` + return JSON.toJSONString(hashMap, SerializerFeature.WriteMapNullValue) + } + + + /** + * 绑定贴片信息 + */ + fun getHistory(historyName: String, context: Context): ArrayList { + var historyList = ArrayList() + var history = "" + if (!SPUtil.getValue(historyName, String::class.java).isNullOrEmpty()) { +// history = PreferencesUtils.getString(context, historyName) + history = SPUtil.getValue(historyName, String::class.java) +// Log.i("绑定设备历史记录", history) + if (!TextUtils.isEmpty(history)) { + val gson = Gson() + //根据id来显示列表 + val listdata: BindBlueToothM = + gson.fromJson(history, BindBlueToothM::class.java) + historyList.addAll(listdata.list) + } + } + return historyList + } + + /** + * 绑定贴片信息 + */ + //删除本地信息 + fun deleteHistory(context: Context, historyName: String, mac: String) { + var indexData: BindBlueToothData? = null + var list = getHistory(historyName, context) + list.forEach { + if (it.mac == mac) { + indexData = it + } + } + if (indexData != null) { + list.remove(indexData) + } + addHistoryList(context, historyName, list) + } + + /** + * 绑定贴片信息 + */ + //修改本地信息 + fun ChangeHistory( + context: Context, + historyName: String, + mac: String, + mVals: BindBlueToothData + ) { + var indexData: BindBlueToothData? = null + var list = getHistory(historyName, context) + list.forEach { + if (it.mac == mac) { + indexData = it + } + } + if (indexData != null) { + list.remove(indexData) + } + list.add(mVals) + addHistoryList(context, historyName, list) + } + + + /** + * 绑定贴片信息 + */ + //查询是否绑定 + fun isBind(context: Context, historyName: String, mac: String): Boolean { + var isBind = false + var list = getHistory(historyName, context) + list.forEach { + if (it.mac == mac) { + isBind = true + } + } + return isBind + } + + //根据mac查询BindBlueToothData + fun getBindBlueToothData( + context: Context, + historyName: String, + mac: String + ): BindBlueToothData { + var data: BindBlueToothData? = null + var list = getHistory(historyName, context) + list.forEach { + if (it.mac == mac) { + data = it + } + } + return data!! + } + + + /** + * 获取设备信息 + * String device_model = Build.MODEL; // 设备型号 。 + + String version_sdk = Build.VERSION.SDK; // 设备SDK版本 。 + + String version_release = Build.VERSION.RELEASE; // 设备的系统版本 。 + */ + //获取设备名称 + fun getDeviceName(): String { + return Build.DEVICE + } + + //获取设备Mac + @SuppressLint("MissingPermission") + fun getDeviceMac(context: Context): String { + val wifi: WifiManager? = context.getSystemService(Context.WIFI_SERVICE) as WifiManager? + val info: WifiInfo = wifi!!.connectionInfo + return info.macAddress + } + + //获取系统版本 + fun getDeviceRelease(): String { + return Build.VERSION.RELEASE + } + + //获取设备型号 + fun getDeviceModel(): String { + return Build.MODEL + } + + /** + * 设备ip端口号处理 + * 拼接 + * https://ifhs.fedhealth.cn/ + */ + fun getIpStr(ip: String, code: String): String { + var str = "" + str = if (code.isNullOrEmpty()) { + ip + } else { + "$ip:$code/" + } + return str + } + + /** + * 接收蓝牙数据处理 + */ + fun getBlueToothHistory(): ArrayList { + var historyList = ArrayList() + var history = "" + if (!SPUtil.getValue(DataServer.MBlueToothLocalData, String::class.java).isNullOrEmpty()) { + history = SPUtil.getValue(DataServer.MBlueToothLocalData, String::class.java) + Log.i("接收蓝牙数据历史记录", history) + if (!TextUtils.isEmpty(history)) { + val gson = Gson() + //根据id来显示列表 + val listdata: BlueToothDataM = + gson.fromJson(history, BlueToothDataM::class.java) + historyList.addAll(listdata.list) + } + } + return historyList + } + + //解绑之后 之前接收的该贴件的信息需要清除 + fun clearMacBlueToothHistory(mMac: String) { + var index: BlueToothData? = null + var model = getBlueToothHistory() + model.forEach { + if (it.mac == mMac) { + index = it + } + } + if (index != null) { + model.remove(index) + } + var json = getJson(model) + SPUtil.setValue(DataServer.MBlueToothLocalData, json) + Log.i("接收蓝牙数据历史记录", SPUtil.getValue(DataServer.MBlueToothLocalData, String::class.java)) + DataServer.MBlueToothData = getBlueToothHistory() + } + + //修改贴件信息之后 之前接收的该贴件的信息需要修改 (type:震动型、移动型) + fun changeMacBlueToothHistory(mMac: String, context: Context) { + //该条贴件数据 + var bindBean = getBindMessByMac(mMac, context) + //该条蓝牙接收数据 + if (BlueToothUtils.instance!!.isGetBlueToothData(mMac)) { + var blueToothBean = BlueToothUtils.instance!!.getBlueToothDataModel(mMac) + blueToothBean.type = getTJType(bindBean!!.type) + BlueToothUtils.instance!!.addBlueToothDataHistory() + } + } + + //贴件绑定后电量默认为空 接收蓝牙信息之后 更新绑定贴件的电量 + fun changeBindTjPower(mMac: String, context: Context) { + //绑定该贴件数据 + var bindBean = getBindMessByMac(mMac, context) + //该条蓝牙接收数据 + if (BlueToothUtils.instance!!.isGetBlueToothData(mMac)) { + //修改本地该条记录 + ChangeHistory( + context, + DataServer.mBindHistory, + mMac, BindBlueToothData( + mMac, + BlueToothUtils.instance!!.getBlueToothPower(mMac), + bindBean!!.name, + bindBean.type, TimeUtil.getNowTime()!! + ) + ) + } + } + + /** + * 获取贴片用途code + * 序号 Code 贴片类型 + 1 DEVICE_CATEGORY_1 门 + 2 DEVICE_CATEGORY_2 窗帘 + 3 DEVICE_CATEGORY_3 主水管 + 4 DEVICE_CATEGORY_4 水管 + 5 DEVICE_CATEGORY_5 马桶 + 6 DEVICE_CATEGORY_6 电视 + 7 DEVICE_CATEGORY_7 电冰箱 + 8 DEVICE_CATEGORY_8 煤气灶 + 9 DEVICE_CATEGORY_9 抽油烟机 + 10 DEVICE_CATEGORY_1 门 + 11 DEVICE_CATEGORY_10 宠物 + 12 DEVICE_CATEGORY_11 植物 + 13 DEVICE_CATEGORY_12 挎包 + */ + + fun getTJTypeCode(type: String): String { + var typeCode = "" + when (type) { + "门" -> typeCode = "DEVICE_CATEGORY_1" + "窗帘" -> typeCode = "DEVICE_CATEGORY_2" + "主水管" -> typeCode = "DEVICE_CATEGORY_3" + "水管" -> typeCode = "DEVICE_CATEGORY_4" + "马桶" -> typeCode = "DEVICE_CATEGORY_5" + "电视" -> typeCode = "DEVICE_CATEGORY_6" + "电冰箱" -> typeCode = "DEVICE_CATEGORY_7" + "煤气灶" -> typeCode = "DEVICE_CATEGORY_8" + "抽油烟机" -> typeCode = "DEVICE_CATEGORY_9" + "宠物" -> typeCode = "DEVICE_CATEGORY_10" + "植物" -> typeCode = "DEVICE_CATEGORY_11" + "挎包" -> typeCode = "DEVICE_CATEGORY_12" + } + return typeCode + } + + /** + * 获取贴件类型 + * 识别类型(VB:震动型: DP:位移型) + * 挎包: 信号强度 + * 宠物:信号强度+振动 + */ + fun getTJType(type: String): String { + var identifyType = "" + when (type) { + "门", "窗帘", "电冰箱", "植物" -> identifyType = "DP" + "主水管", "水管", "马桶", "电视", "煤气灶", "抽油烟机", "宠物" -> identifyType = "VB" + } + return identifyType + } + + /** + * 根据mac + * 获取贴件类型 + * 识别类型(VB:震动型: DP:位移型) + * 挎包: 信号强度 + * 宠物:信号强度+振动 + */ + fun getTJTypeByMac(mac: String, context: Context): String { + var identifyType = "" + //获取对应的贴件数据 + var tjModel = getBindMessByMac(mac, context) + identifyType = getTJType(tjModel!!.type) + return identifyType + } + + /** + * 根据mac获取绑定贴片信息 + */ + fun getBindMessByMac(mac: String, context: Context): BindBlueToothData? { + var bean: BindBlueToothData? = null + var mBindTJList = getHistory(DataServer.mBindHistory, context) + mBindTJList.forEach { + if (mac == it.mac) { + bean = it + } + } + return bean + } + + /** + * 根据mac查询是否在线 + */ + fun getTJIsOnLineState(mac: String): Boolean { + var isOnLine = false + //获取对应的蓝牙数据 + if (BlueToothUtils.instance!!.isGetBlueToothData(mac)) { + var model = BlueToothUtils.instance!!.getBlueToothDataModel(mac) + isOnLine = model.onLine + } + + return isOnLine + } + + /** + * 我的设备列表 根据用途获取对应的图片 + */ + fun getMyEquitmentPageImage(type: String): Int { + var image = 0 + when (type) { + "门" -> image = R.mipmap.men02 + "窗帘" -> image = R.mipmap.chaunglian02 + "主水管" -> image = R.mipmap.shuiguan02 + "水管" -> image = R.mipmap.zhushuiguan02 + "马桶" -> image = R.mipmap.matong02 + "电视" -> image = R.mipmap.dianshiji02 + "电冰箱" -> image = R.mipmap.bingxiang02 + "煤气灶" -> image = R.mipmap.ranqizao02 + "抽油烟机" -> image = R.mipmap.chouyouyanji02 + "宠物" -> image = R.mipmap.chongwu02 + "植物" -> image = R.mipmap.zhiwu02 + "挎包" -> image = R.mipmap.kuabao02 + } + return image + } + + /** + * 我的设备列表 根据用途获取对应的图片 + */ + fun getMyEquitmentPageImage(data: DeviceInfo): Int { + var image = 0 + var status = ObjectBoxUtils.getLastActionStatusByMac(data.mac) + when (data.type) { + "门" -> { + when(status){ + 0 -> image = R.mipmap.door_open_device + 1 -> image = R.mipmap.door_close_device + -1 -> image = R.mipmap.men02 + } + } + "窗帘" ->{ + when(status){ + 0 -> image = R.mipmap.curtain_open_device + 1 -> image = R.mipmap.curtain_close_device + -1 -> image = R.mipmap.chaunglian02 + } + } + "主水管" ->{ + when(status){ + 0 -> image = R.mipmap.stopcock_open_device + 1 -> image = R.mipmap.stopcock_close_device + -1 -> image = R.mipmap.shuiguan02 + } + } + "水管" ->{ + when(status){ + 0 -> image = R.mipmap.stopcock_open_device + 1 -> image = R.mipmap.stopcock_close_device + -1 -> image = R.mipmap.zhushuiguan02 + } + } + "马桶" ->{ + when(status){ + 0 -> image = R.mipmap.toilet_open_device + 1 -> image = R.mipmap.toilet_close_device + -1 -> image = R.mipmap.matong02 + } + } + "电视" ->{ + when(status){ + 0 -> image = R.mipmap.tv_open_device + 1 -> image = R.mipmap.tv_close_device + -1 -> image = R.mipmap.dianshiji02 + } + } + "电冰箱" ->{ + when(status){ + 0 -> image = R.mipmap.fridge_open_device + 1 -> image = R.mipmap.fridge_close_device + -1 -> image = R.mipmap.bingxiang02 + } + } + "煤气灶" ->{ + when(status){ + 0 -> image = R.mipmap.cooker_open_device + 1 -> image = R.mipmap.cooker_close_device + -1 -> image = R.mipmap.ranqizao02 + } + } + "抽油烟机" ->{ + when(status){ + 0 -> image = R.mipmap.extractor_open_device + 1 -> image = R.mipmap.extractor_close_device + -1 -> image = R.mipmap.chouyouyanji02 + } + } + "宠物" ->{ + when(status){ + 0 -> image = R.mipmap.pet_open_device + 1 -> image = R.mipmap.pet_close_device + -1 -> image = R.mipmap.chongwu02 + } + } + "植物" ->{ + when(status){ + 0 -> image = R.mipmap.plant_open_device + 1 -> image = R.mipmap.plant_close_device + -1 -> image = R.mipmap.zhiwu02 + } + } + "挎包" ->{ + when(status){ + 0 -> image = R.mipmap.bag_open_device + 1 -> image = R.mipmap.bag_close_device + -1 -> image = R.mipmap.kuabao02 + } + } + } + return image + } + + /** + * 我的设备列表 获取上次状态 + */ + fun getMyEquitmentOpenStatus(data: String): String { + var image = "close" + var status = ObjectBoxUtils.getLastActionStatusByMac(data) + when(status){ + 0 -> image = "Open" + 1 -> image = "Close" + -1 -> image = "Close" + } + return image + } + + /** + * 我的待机页面 根据用途获取对应的图片 + */ + fun getDJPageImage(type: String): Int { + var image = 0 + when (type) { + "门" -> image = R.mipmap.men01 + "窗帘" -> image = R.mipmap.chuanglian01 + "主水管" -> image = R.mipmap.shuiguan01 + "水管" -> image = R.mipmap.zhushuiguan01 + "马桶" -> image = R.mipmap.matong01 + "电视" -> image = R.mipmap.dianshiji01 + "电冰箱" -> image = R.mipmap.bingxiang01 + "煤气灶" -> image = R.mipmap.ranqizao01 + "抽油烟机" -> image = R.mipmap.cyyj01 + "宠物" -> image = R.mipmap.chongwu01 + "植物" -> image = R.mipmap.zhiwu01 + "挎包" -> image = R.mipmap.kuabao01 + } + return image + } + + /** + * 截取蓝牙接收数据 + * 009c036201dc37dc683475c584009c036201dc37dc683475c584009c036201dc37dc683475c584 + */ + fun getBlueToothSingleMac(info: String): List { + var list = ArrayList() + var csInfo = info + var index=0 + if (csInfo.length > 26) { + while (csInfo.length >= (26+index)) { + var splitList = info.substring(0+index, index+26) + list.add(splitList) + index+=26 +// csInfo = splitList[1].toString() + } + } else { + list.add(csInfo) + } + return list + } + + var width = 300 + var height = 300 + + /*生成二维码*/ + @Throws(WriterException::class) + fun createEwm(id: String, code: String,type: Int): Bitmap { + /*生成二维矩阵,编码时指定大小,不要生成了图片以后再进行缩放,这样会模糊导致识别失败 */ + val bitMatrix = + MultiFormatWriter().encode("{id:$id,code:$code,type:$type}", BarcodeFormat.QR_CODE, width, height) + /*转为一维数组*/ + val pixels = IntArray(width * height) + for (i in 0 until height) { + for (y in 0 until width) { + if (bitMatrix[y, i]) { + pixels[y * width + i] = -0x1000000 + } + } + } + val bitmap: Bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + bitmap.setPixels(pixels, 0, width, 0, 0, width, height) + return bitmap + // iv_37_01_ewm为ImageView + } + /** + * 获取贴件开关状态 + * 贴片状态 0:关;1:开 + */ + fun getOpenFormat(isOpen: Boolean): String { + var openFormat = "0" + if (isOpen) { + openFormat = "1" + } + return openFormat + } + + /** + * 贴片绑定防重 + */ + fun isContainsTJData(roomName:String,type:String,mac: String,context: Context):Boolean{ + var isCon=false + var mBindList=getHistory(DataServer.mBindHistory, context) + mBindList.forEach { + if(it.name==roomName&&it.type==type&&mac!=it.mac){ + isCon=true + } + } + return isCon + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/utils/Utils.java b/app/src/main/java/com/qidian/zhongkesmart/utils/Utils.java new file mode 100644 index 0000000..e431963 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/utils/Utils.java @@ -0,0 +1,131 @@ +package com.qidian.zhongkesmart.utils; + +import com.blankj.utilcode.util.TimeUtils; +import com.qidian.baseble.utils.HexUtil; +import com.qidian.zhongkesmart.model.ActionInfo; +import com.qidian.zhongkesmart.model.TimeDataWithTime; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Utils { + /** + * 比较版本号的大小,前者大则返回一个正数(true),后者大返回一个负数(false),相等则返回0(false),如果是1.x 对比1.5这种,1.x大于带数字的版本,字母会认为是全匹配 + * + * @param version1 ignore + * @param version2 ignore + * @return ignore + */ + public static boolean compareVersion(String version1, String version2) { + // 切割点 "."; + String[] versionArray1 = version1.split("\\."); + String[] versionArray2 = version2.split("\\."); + int idx = 0; + // 取最小长度值 + int minLength = Math.min(versionArray1.length, versionArray2.length); + int diff = 0; + // 先比较长度 再比较字符 + while (idx < minLength && (diff = versionArray1[idx].length() - versionArray2[idx].length()) == 0 + && (diff = versionArray1[idx].compareTo(versionArray2[idx])) == 0) { + ++idx; + } + // 如果已经分出大小,则直接返回,如果未分出大小,则再比较位数,有子版本的为大; + diff = (diff != 0) ? diff : versionArray1.length - versionArray2.length; + return diff>0; + } + + + /** + * 格式化mac地址 + * @param mac macString + * @param split 字符串 + * @return + */ + public static String formatMac(String mac, String split) { + String regex = "[0-9a-fA-F]{12}"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(mac); + if (!matcher.matches()) { + throw new IllegalArgumentException("mac format is error"); + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 12; i++) { + char c = mac.charAt(i); + sb.append(c); + if ((i & 1) == 1 && i <= 9) { + sb.append(split); + } + } + return sb.toString().toUpperCase(); + } + + /** + * mac格式处理 + * CF:6A:12:48:3F:37 ->373f48126acf + */ + public static String reverseMac(String mac) { + if (mac.contains(":")) { + String[] macList = mac.split(":"); + StringBuilder sb = new StringBuilder(); + for (int i = macList.length-1; i >-1 ; i--) { + String c = macList[i]; + sb.append(c); + } + return sb.toString().toLowerCase(); + }else{ + throw new IllegalArgumentException("mac format is error"); + } + } + + /** + * 格式化mac地址 + * @param mac macString + * @return + */ + public static String formatMac(String mac) { + String regex = "(([a-f0-9]{2}:)|([a-f0-9]{2}-)){5}[a-f0-9]{2}"; + Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(mac); + if (!matcher.matches()) { + throw new IllegalArgumentException("mac format is error"); + } + return mac.replaceAll("[:-]", ""); + } + + /** + * 解析动作数据 + * 0开 1关 -1无效 + */ + public static int getActionType(byte[] data){ + int result = HexUtil.byteToInt(data[1]); + if(result == 255){ + return 0; + }else if(result == 254){ + return 1; + }else{ + return -1; + } + } + + /** + * actionData变成开关数据 + */ + public static List refreshActionData(List list){ + List result = new ArrayList(); + for (int i = 0;i= Build.VERSION_CODES.Q) { + val specifier: NetworkSpecifier = WifiNetworkSpecifier.Builder() + .setSsidPattern(PatternMatcher(wifiName, PatternMatcher.PATTERN_PREFIX)) + .setWpa2Passphrase(wifPassword) + .build() + val request = NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .setNetworkSpecifier(specifier) + .build() + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val networkCallback: ConnectivityManager.NetworkCallback = + object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + // do success processing here.. + Log.i("WiFi==", "连接成功") + connectivityManager.bindProcessToNetwork(network) + } + + override fun onLosing(network: Network, maxMsToLive: Int) { + super.onLosing(network, maxMsToLive) + println("onLosing$network") + EventBusUtil.sendEvent( + EventBusUtil.MessageEvent( + EventCode.WIFI_CONFIRM_FAIL + ) + ) + Log.i("WiFi==", "onLosing==" + network.toString()) + } + + override fun onLost(network: Network) { + super.onLost(network) + println("onLost$network") + EventBusUtil.sendEvent( + EventBusUtil.MessageEvent( + EventCode.WIFI_CONFIRM_FAIL + ) + ) + Log.i("WiFi==", "onLost==" + network.toString()) + } + + override fun onUnavailable() { + // do failure processing here.. + EventBusUtil.sendEvent( + EventBusUtil.MessageEvent( + EventCode.WIFI_CONFIRM_FAIL + ) + ) + Log.i("WiFi==", "连接失败") + } + } + connectivityManager.requestNetwork(request, networkCallback) + // Release the request when done. + // connectivityManager.unregisterNetworkCallback(networkCallback); + } else {//小于Android Q的版本 + val wifiSSID = "\"" + wifiName + "\"" + val wifiConfiguration = WifiConfiguration().apply { + SSID = wifiSSID + preSharedKey = "\"" + wifPassword + "\"" + hiddenSSID = true + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); + allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); + allowedProtocols.set(WifiConfiguration.Protocol.RSN) + status = WifiConfiguration.Status.ENABLED + } + val wifiManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + context.getSystemService(WifiManager::class.java) + } else { + context.applicationContext.getSystemService(Context.WIFI_SERVICE) + ?.let { + it as WifiManager + } + + } + wifiManager?.run { + addNetwork(wifiConfiguration) + val config = configuredNetworks.first { + it.SSID != null && it.SSID == wifiSSID + } + disconnect() + Log.e("===", "==networkId==${config.networkId}") + val enableNetwork = enableNetwork(config.networkId, true) + Log.e("===", "==enableNetwork==$enableNetwork") + val b = reconnect() + Log.e("==", "==reconnect=====$b") + } + + } + } + /** + * 移除已连接wifi信息 + */ + @SuppressLint("MissingPermission") + fun removeWifiBySsid(wifiManager: WifiManager, targetSsid: String) { + val wifiConfigs: List = wifiManager.configuredNetworks + for (wifiConfig in wifiConfigs) { + val ssid = wifiConfig.SSID + if (ssid == targetSsid) { + wifiManager.removeNetwork(wifiConfig.networkId) + wifiManager.saveConfiguration() + } + } + } + + // TODO: 2021/9/15 获取附近wifi信号 https://www.jb51.net/article/225673.htm + fun getAroundWifiDeviceInfo(context:Context): List? { + val sInfo = StringBuffer() + val mWifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager + val mWifiInfo = mWifiManager.connectionInfo + val scanResults = mWifiManager.scanResults //搜索到的设备列表 + val newScanResultList: MutableList = ArrayList() + for (scanResult in scanResults) { + if (mWifiInfo != null && mWifiInfo.ssid.replace("\"", "") == scanResult.SSID){ + + } + val position: Int = getItemPosition(newScanResultList, scanResult) + if (position != -1) { + if (newScanResultList[position].level < scanResult.level) { + newScanResultList.removeAt(position) + newScanResultList.add(position, scanResult) + } + } else { + newScanResultList.add(scanResult) + } + } + val stringList: MutableList = ArrayList() + for (i in newScanResultList.indices) { + + val stringBuilder = StringBuilder() + stringBuilder.append( + """ + 设备名(SSID) ->${newScanResultList[i].SSID} + + """.trimIndent() + ) + stringBuilder.append( + """ + 信号强度 ->${newScanResultList[i].level} + + """.trimIndent() + ) + stringBuilder.append( + """ + BSSID ->${newScanResultList[i].BSSID} + + """.trimIndent() + ) + stringBuilder.append( + """ + level ->${newScanResultList[i].level} + + """.trimIndent() + ) + stringBuilder.append( + """ + 采集时间戳 ->${System.currentTimeMillis()} + + """.trimIndent() + ) + stringBuilder.append( + "rssi ->" + (if (mWifiInfo != null && mWifiInfo.ssid.replace("\"", "").equals( + newScanResultList[i].SSID + ) + ) mWifiInfo.rssi else null) + "\n" + ) + //是否为连接信号(1连接,默认为null) + stringBuilder.append( + "是否为连接信号 ->" + (if (mWifiInfo != null && mWifiInfo.ssid.replace("\"", "") + .equals( + newScanResultList[i].SSID + ) + ) 1 else null) + "\n" + ) + /* stringBuilder.append( + """ + 信道 - >${getCurrentChannel(mWifiManager).toString()} + + """.trimIndent() + )*/ + //1 为2.4g 2 为5g +// stringBuilder.append("频段 ->" + is24GOr5GHz(newScanResultList[i].frequency)) + stringList.add(stringBuilder.toString()) + } + Log.d("getAroundWifiDeviceInfo", sInfo.toString()) + return stringList + } + + /** + * 返回item在list中的坐标 + */ + private fun getItemPosition(list: List, item: ScanResult): Int { + for (i in list.indices) { + if (item.SSID.equals(list[i].SSID)) { + return i + } + } + return -1 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/views/BarChartView.java b/app/src/main/java/com/qidian/zhongkesmart/views/BarChartView.java new file mode 100644 index 0000000..e011c85 --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/views/BarChartView.java @@ -0,0 +1,299 @@ +package com.qidian.zhongkesmart.views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.widget.Scroller; + +import androidx.annotation.Nullable; + +import com.scwang.smartrefresh.layout.util.DensityUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +/** + * @Description: + * @QQ : 834672307 + * @Author : 文阿花 + * @Email : + * @Date : 2022/6/17 0017 10:54 + */ +public class BarChartView extends View { + + private Paint axisPaint;//画L线 + private int axisWidth= DensityUtil.dp2px(2);//线的宽度 + private int axisColor = Color.LTGRAY;//线的颜色 + + private int paddingLeft = DensityUtil.dp2px(20); + private int paddingTop = DensityUtil.dp2px(20); + private int paddingRight = DensityUtil.dp2px(20); + private int paddingBottom = DensityUtil.dp2px(20); + private int xTextHeight = DensityUtil.dp2px(20);//X轴底部文字高度 + + + private TextPaint axisTextPaint;//画坐标轴的文字 + private int axisTextSize=DensityUtil.dp2px(12);//文字大小 + private int axisTextColor = Color.BLACK; + + private TextPaint axisXTextPaint;//画坐标轴的文字 + private int axisXTextSize=DensityUtil.dp2px(10);//文字大小 + private int axisXTextColor = Color.BLACK; + + private TextPaint barTextPaint;//画坐标轴的文字 + private int barTextSize=DensityUtil.dp2px(10);//文字大小 + private int barTextColor = Color.BLACK; + private int barTextHeight = DensityUtil.dp2px(15); + + public void setBarTextColor(int barTextColor) { + this.barTextColor = barTextColor; + } + + private int barWidth = DensityUtil.dp2px(40);//柱子宽度 + private int barSpace = DensityUtil.dp2px(15);//柱子间距 + + //设置柱子宽度 + public void setBarWidth(int barWidth) { + this.barWidth = barWidth; + } + + private Paint barPaint;//画柱子 + private int barColor = Color.GREEN;//柱子颜色 + + //设置柱子颜色 + public void setBarColor(int barColor) { + this.barColor = barColor; + } + + private Paint LegendPaint; + private TextPaint legendTextPaint; + private int legendTextColor = Color.BLACK; + private int legendTextSize = DensityUtil.dp2px(10); + + + private List yList;//y轴数据 + private List xList; + + private int maxOffset; + private float lastX; + + private VelocityTracker tracker; + private Scroller scroller; + + private boolean isLegend; + private String legendText; + + //设置数据 + public void setChartData(List xList,List yList,boolean isLegend,String legendText){ + this.yList = yList; + this.xList = xList; + this.isLegend = isLegend; + this.legendText = legendText; + invalidate(); + } + + //得到Y轴最大值 + private float maxYData(List lists){ + HashSet hashSet = new HashSet<>(lists); + List list = new ArrayList<>(hashSet); + Collections.sort(list);//升序 + return list.get(list.size()-1); + } + + public BarChartView(Context context) { + this(context,null); + } + + public BarChartView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs,0); + } + + public BarChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + axisPaint = new Paint(); + axisPaint.setStrokeWidth(axisWidth); + axisPaint.setColor(axisColor); + axisPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + axisPaint.setAntiAlias(true); + + axisTextPaint = new TextPaint(); + axisTextPaint.setAntiAlias(true); + axisTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + axisTextPaint.setTextSize(axisTextSize); + axisTextPaint.setColor(axisTextColor); + + axisXTextPaint = new TextPaint(); + axisXTextPaint.setAntiAlias(true); + axisXTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + axisXTextPaint.setTextSize(axisXTextSize); + axisXTextPaint.setColor(axisXTextColor); + + barTextPaint = new TextPaint(); + barTextPaint.setAntiAlias(true); + barTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + barTextPaint.setTextSize(barTextSize); + barTextPaint.setColor(barTextColor); + + barPaint = new Paint(); + barPaint.setColor(barColor); + barPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + barPaint.setAntiAlias(true); + + LegendPaint = new Paint(); + LegendPaint.setColor(barColor); + LegendPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + LegendPaint.setAntiAlias(true); + + legendTextPaint = new TextPaint(); + legendTextPaint.setAntiAlias(true); + legendTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + legendTextPaint.setTextSize(legendTextSize); + legendTextPaint.setColor(legendTextColor); + + scroller = new Scroller(getContext()); + + } + private float ageY;//平均没等分是多少 + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onDraw(Canvas canvas) { + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + + if (yList==null || yList.size()==0)return; + int maxTextWidth = (int) axisTextPaint.measureText(maxYData(yList)+"0"); + int left = paddingLeft+axisWidth/2+maxTextWidth;//paddingLeft+线的宽度/2+Y轴值最大宽度 + int right = width - paddingRight; + int top = paddingTop; + int bottom = height-paddingBottom-axisWidth/2;//预留X轴文字高度 + canvas.save(); + canvas.translate(getScrollX(),0); + //画Legend + if (isLegend){ + Rect rect = new Rect(); + rect.left = (width-left-paddingRight)/2; + rect.top= top; + rect.right=rect.left+20; + rect.bottom= top+20; + canvas.drawRect(rect,LegendPaint); + Paint.FontMetricsInt LegendmetricsInt = legendTextPaint.getFontMetricsInt(); + int dyLegend = (LegendmetricsInt.bottom-LegendmetricsInt.top)/2-LegendmetricsInt.bottom; + float ydyLegend = dyLegend+rect.top+10; + canvas.drawText(legendText,rect.right+20,ydyLegend,legendTextPaint); // + } + + //画Y轴值 + float maxdata = maxYData(yList);//最大值 + float agedata = maxdata/8;//平均每份 + ageY = (height-paddingBottom-paddingTop)/8;//线的每等分 + for (int i=0;i<8;i++){ + if (i==0){ + canvas.drawText("0",paddingLeft,height-paddingBottom-xTextHeight,axisTextPaint);//画Y轴刻度 + }else { + Paint.FontMetricsInt metricsInt = axisTextPaint.getFontMetricsInt(); + int dy = (metricsInt.bottom-metricsInt.top)/2-metricsInt.bottom; + float y = dy+(height-paddingBottom)-ageY*i-xTextHeight; + canvas.drawText(""+i*agedata*1.2,paddingLeft,y,axisTextPaint); //画Y轴刻度 + } + + } + canvas.drawLine(left,top,left,bottom-xTextHeight,axisPaint);//画Y轴 + canvas.drawLine(left,bottom-xTextHeight,right,bottom-xTextHeight,axisPaint);//画X轴 + for (int i=0;i=right){ + continue; + } + float top0 =(float)( height-paddingBottom-(yList.get(i)*ageY/(agedata*1.2))-xTextHeight); + canvas.clipRect(left,top,right,bottom);//剪切柱状图区域 + canvas.drawRect(x0,top0,x1,bottom-xTextHeight,barPaint);//画柱状图 + //底部X轴文字 + String xtext = xList.get(i); + float xtextwidth = axisXTextPaint.measureText(xtext);//X文字宽度 + Paint.FontMetricsInt metricsInt = axisXTextPaint.getFontMetricsInt(); + int dy = (metricsInt.bottom-metricsInt.top)/2-metricsInt.bottom; + float y = height-xTextHeight-dy; + canvas.drawText(xtext,x0+(barWidth-xtextwidth)/2f,y,axisXTextPaint); + //柱状图上加文字 + String ytext = String.valueOf(yList.get(i)); + float ytextwidth = barTextPaint.measureText(ytext); + canvas.drawText(ytext,x0+(barWidth-ytextwidth)/2f,top0-barTextHeight,barTextPaint); + } + maxOffset = (yList.size() * (barWidth+barSpace)-(getMeasuredWidth()-paddingRight-paddingLeft-maxTextWidth));//计算可滑动距离 + if (maxOffset<0){ + maxOffset=0; + } + canvas.restore(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()){ + case MotionEvent.ACTION_DOWN: + if (tracker!=null){ + tracker.clear(); + } + lastX = event.getX(); + break; + case MotionEvent.ACTION_MOVE: + if (tracker==null){ + tracker = VelocityTracker.obtain(); + } + if (tracker!=null){ + tracker.addMovement(event); + } + int sX= getScrollX(); + sX += event.getX()-lastX; + sX = Math.max(Math.min(0,sX),-maxOffset); + scrollTo(sX,0); + lastX=event.getX(); + break; + case MotionEvent.ACTION_UP: + setTracker(); + break; + case MotionEvent.ACTION_CANCEL: + setTracker(); + break; + } + invalidate(); + return true; + } + + private void setTracker(){ + if (tracker!=null){ + tracker.computeCurrentVelocity(1000); + scroller.forceFinished(true); + scroller.fling(getScrollX(),0,(int) (0.5*tracker.getXVelocity()),0,-maxOffset,0,0,0); + tracker.recycle(); + } + tracker=null; + } + + @Override + public void computeScroll() { + if (scroller.computeScrollOffset()){ + scrollTo(scroller.getCurrX(),0); + postInvalidate(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zhongkesmart/views/ClearEditText.java b/app/src/main/java/com/qidian/zhongkesmart/views/ClearEditText.java new file mode 100644 index 0000000..5ce63bd --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/views/ClearEditText.java @@ -0,0 +1,139 @@ +package com.qidian.zhongkesmart.views; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import androidx.appcompat.widget.AppCompatEditText; + +import com.qidian.zhongkesmart.R; + + +/** + * Created by Administrator on 18/01/09. + */ + +public class ClearEditText extends AppCompatEditText implements View.OnFocusChangeListener, + TextWatcher { + //EditText右侧的删除按钮 + private Drawable mClearDrawable; + private boolean hasFoucs; + private OnClearEditFocusChange onClearEditFocusChange; + + public ClearEditText(Context context) { + this(context, null); + } + + public ClearEditText(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.editTextStyle); + } + + public ClearEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + // 获取EditText的DrawableRight,假如没有设置我们就使用默认的图片,获取图片的顺序是左上右下(0,1,2,3,) + mClearDrawable = getCompoundDrawables()[2]; + if (mClearDrawable == null) { + mClearDrawable = getResources().getDrawable( + R.mipmap.icon_et_clear); + } + + mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), + mClearDrawable.getIntrinsicHeight()); + // 默认设置隐藏图标 + setClearIconVisible(false); + // 设置焦点改变的监听 + setOnFocusChangeListener(this); + // 设置输入框里面内容发生改变的监听 + addTextChangedListener(this); + } + + /* @说明:isInnerWidth, isInnerHeight为ture,触摸点在删除图标之内,则视为点击了删除图标 + * event.getX() 获取相对应自身左上角的X坐标 + * event.getY() 获取相对应自身左上角的Y坐标 + * getWidth() 获取控件的宽度 + * getHeight() 获取控件的高度 + * getTotalPaddingRight() 获取删除图标左边缘到控件右边缘的距离 + * getPaddingRight() 获取删除图标右边缘到控件右边缘的距离 + * isInnerWidth: + * getWidth() - getTotalPaddingRight() 计算删除图标左边缘到控件左边缘的距离 + * getWidth() - getPaddingRight() 计算删除图标右边缘到控件左边缘的距离 + * isInnerHeight: + * distance 删除图标顶部边缘到控件顶部边缘的距离 + * distance + height 删除图标底部边缘到控件顶部边缘的距离 + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + if (getCompoundDrawables()[2] != null) { + int x = (int)event.getX(); + int y = (int)event.getY(); + Rect rect = getCompoundDrawables()[2].getBounds(); + int height = rect.height(); + int distance = (getHeight() - height)/2; + boolean isInnerWidth = x > (getWidth() - getTotalPaddingRight()) && x < (getWidth() - getPaddingRight()); + boolean isInnerHeight = y > distance && y <(distance + height); + if (isInnerWidth && isInnerHeight) { + this.setText(""); + } + } + } + return super.onTouchEvent(event); + } + + /** + * 当ClearEditText焦点发生变化的时候, + * 输入长度为零,隐藏删除图标,否则,显示删除图标 + */ + public void onFocusChange(View v, boolean hasFocus) { + this.hasFoucs = hasFocus; + if (hasFocus) { + setClearIconVisible(getText().length() > 0); + } else { + setClearIconVisible(false); + } + if(onClearEditFocusChange != null){ + onClearEditFocusChange.onFocusChange(v,hasFocus); + } + } + + protected void setClearIconVisible(boolean visible) { + Drawable right = visible ? mClearDrawable : null; + setCompoundDrawables(getCompoundDrawables()[0], + getCompoundDrawables()[1], right, getCompoundDrawables()[3]); + } + + @Override + public void onTextChanged(CharSequence s, int start, int count, int after) { + if (hasFoucs) { + setClearIconVisible(s.length() > 0); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + + } + + @Override + public void afterTextChanged(Editable s) { + + } + + public interface OnClearEditFocusChange{ + public void onFocusChange(View v, boolean hasFocus); + } + + public void setOnClearEditFocusChange(OnClearEditFocusChange onClearEditFocusChange) { + this.onClearEditFocusChange = onClearEditFocusChange; + } +} diff --git a/app/src/main/java/com/qidian/zhongkesmart/views/VerifyCodeView.java b/app/src/main/java/com/qidian/zhongkesmart/views/VerifyCodeView.java new file mode 100644 index 0000000..1a9576b --- /dev/null +++ b/app/src/main/java/com/qidian/zhongkesmart/views/VerifyCodeView.java @@ -0,0 +1,392 @@ +package com.qidian.zhongkesmart.views; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.os.Build; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.qidian.zhongkesmart.R; + +import java.lang.reflect.Field; + +public class VerifyCodeView extends LinearLayout implements TextWatcher, View.OnKeyListener, View.OnFocusChangeListener { + + private Context mContext; + private OnCodeFinishListener onCodeFinishListener; + + /** + * 输入框数量 + */ + private int mEtNumber; + + /** + * 输入框的宽度 + */ + private int mEtWidth; + + /** + * 文字颜色 + */ + private int mEtTextColor; + + /** + * 文字大小 + */ + private float mEtTextSize; + + /** + * 输入框背景 + */ + private int mEtTextBg; + + /** + * 输入框间距 + */ + private int mEtSpacing; + + /** + * 平分后的间距 + */ + private int mEtBisectSpacing; + + /** + * 判断是否平分 + */ + private boolean isBisect; + + /** + * 是否显示光标 + */ + private boolean cursorVisible; + + /** + * 光标样式 + */ + private int mCursorDrawable; + + /** + * 输入框宽度 + */ + private int mViewWidth; + + /** + * 输入框间距 + */ + private int mViewMargin; + + public OnCodeFinishListener getOnCodeFinishListener() { + return onCodeFinishListener; + } + + public void setOnCodeFinishListener(OnCodeFinishListener onCodeFinishListener) { + this.onCodeFinishListener = onCodeFinishListener; + } + + public int getmEtNumber() { + return mEtNumber; + } + + public void setmEtNumber(int mEtNumber) { + this.mEtNumber = mEtNumber; + } + + public int getmEtWidth() { + return mEtWidth; + } + + public void setmEtWidth(int mEtWidth) { + this.mEtWidth = mEtWidth; + } + + public int getmEtTextColor() { + return mEtTextColor; + } + + public void setmEtTextColor(int mEtTextColor) { + this.mEtTextColor = mEtTextColor; + } + + public float getmEtTextSize() { + return mEtTextSize; + } + + public void setmEtTextSize(float mEtTextSize) { + this.mEtTextSize = mEtTextSize; + } + + public int getmEtTextBg() { + return mEtTextBg; + } + + public void setmEtTextBg(int mEtTextBg) { + this.mEtTextBg = mEtTextBg; + } + + public int getmCursorDrawable() { + return mCursorDrawable; + } + + public void setmCursorDrawable(int mCursorDrawable) { + this.mCursorDrawable = mCursorDrawable; + } + + public VerifyCodeView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + @SuppressLint({"Recycle", "CustomViewStyleable"}) + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.vericationCodeView); + mEtNumber = typedArray.getInteger(R.styleable.vericationCodeView_vcv_et_number, 4); + mEtWidth = typedArray.getDimensionPixelSize(R.styleable.vericationCodeView_vcv_et_width, 50); + mEtTextColor = typedArray.getColor(R.styleable.vericationCodeView_vcv_et_text_color, Color.BLACK); + mEtTextSize = typedArray.getDimensionPixelSize(R.styleable.vericationCodeView_vcv_et_text_size, 16); + mEtTextBg = typedArray.getResourceId(R.styleable.vericationCodeView_vcv_et_bg, R.drawable.et_login_code); + mCursorDrawable = typedArray.getResourceId(R.styleable.vericationCodeView_vcv_et_cursor, R.drawable.et_cursor); + cursorVisible = typedArray.getBoolean(R.styleable.vericationCodeView_vcv_et_cursor_visible, true); + + isBisect = typedArray.hasValue(R.styleable.vericationCodeView_vcv_et_spacing); + if (isBisect) { + mEtSpacing = typedArray.getDimensionPixelSize(R.styleable.vericationCodeView_vcv_et_spacing, 0); + } + initView(); + //释放资源 + typedArray.recycle(); + } + + @SuppressLint("ResourceAsColor") + private void initView() { + for (int i = 0; i < mEtNumber; i++) { + EditText editText = new EditText(mContext); + initEditText(editText, i); + addView(editText); + //设置第一个editText获取焦点 + if (i == 0) { + editText.setFocusable(true); + } + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void initEditText(EditText editText, int i) { + editText.setLayoutParams(getETLayoutParams(i)); + editText.setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER); + editText.setGravity(Gravity.CENTER); + editText.setId(i); + editText.setCursorVisible(false); + editText.setMaxEms(1); + editText.setTextColor(mEtTextColor); + editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mEtTextSize); + editText.setCursorVisible(cursorVisible); + editText.setMaxLines(1); + editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)}); + editText.setInputType(InputType.TYPE_CLASS_NUMBER); + editText.setPadding(0, 0, 0, 0); + editText.setOnKeyListener(this); + editText.setBackgroundResource(mEtTextBg); + setEditTextCursorDrawable(editText); + editText.addTextChangedListener(this); + editText.setOnKeyListener(this); + editText.setOnFocusChangeListener(this); + } + + /** + * 获取EditText 的 LayoutParams + */ + public LayoutParams getETLayoutParams(int i) { + LayoutParams layoutParams = new LayoutParams(mEtWidth, mEtWidth); + if (!isBisect) { + //平分Margin,把第一个EditText跟最后一个EditText的间距同设为平分 + mEtBisectSpacing = (mViewWidth - mEtNumber * mEtWidth) / (mEtNumber + 1); + if (i == 0) { + layoutParams.leftMargin = mEtBisectSpacing; + layoutParams.rightMargin = mEtBisectSpacing / 2; + } else if (i == mEtNumber - 1) { + layoutParams.leftMargin = mEtBisectSpacing / 2; + layoutParams.rightMargin = mEtBisectSpacing; + } else { + layoutParams.leftMargin = mEtBisectSpacing / 2; + layoutParams.rightMargin = mEtBisectSpacing / 2; + } + } else { + layoutParams.leftMargin = mEtSpacing / 2; + layoutParams.rightMargin = mEtSpacing / 2; + } + + layoutParams.gravity = Gravity.CENTER; + return layoutParams; + } + + public void setEditTextCursorDrawable(EditText editText) { + //修改光标的颜色(反射) + if (cursorVisible) { + try { + Field f = TextView.class.getDeclaredField("mCursorDrawableRes"); + f.setAccessible(true); + f.set(editText, mCursorDrawable); + } catch (Exception ignored) { + } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mViewWidth = getMeasuredWidth(); + updateETMargin(); + } + + private void updateETMargin() { + for (int i = 0; i < mEtNumber; i++) { + EditText editText = (EditText) getChildAt(i); + editText.setLayoutParams(getETLayoutParams(i)); + } + } + + + @Override + public void onFocusChange(View view, boolean b) { + if (b) { + focus(); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (s.length() != 0) { + focus(); + } + if (onCodeFinishListener != null) { + onCodeFinishListener.onTextChange(this, getResult()); + //如果最后一个输入框有字符,则返回结果 + EditText lastEditText = (EditText) getChildAt(mEtNumber - 1); + if (lastEditText.getText().length() > 0) { + onCodeFinishListener.onComplete(this, getResult()); + } + } + } + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) { + backFocus(); + } + return false; + } + + @Override + public void setEnabled(boolean enabled) { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + child.setEnabled(enabled); + } + } + + /** + * 获取焦点 + */ + private void focus() { + int count = getChildCount(); + EditText editText; + //利用for循环找出还最前面那个还没被输入字符的EditText,并把焦点移交给它。 + for (int i = 0; i < count; i++) { + editText = (EditText) getChildAt(i); + if (editText.getText().length() < 1) { + if (cursorVisible) { + editText.setCursorVisible(true); + } else { + editText.setCursorVisible(false); + } + editText.requestFocus(); + return; + } else { + editText.setCursorVisible(false); + if (i == count - 1) { + editText.requestFocus(); + } + } + } + } + + private void backFocus() { + EditText editText; + //循环检测有字符的`editText`,把其置空,并获取焦点。 + for (int i = mEtNumber - 1; i >= 0; i--) { + editText = (EditText) getChildAt(i); + if (editText.getText().length() >= 1) { + editText.setText(""); + if (cursorVisible) { + editText.setCursorVisible(true); + } else { + editText.setCursorVisible(false); + } + editText.requestFocus(); + return; + } + } + } + + public String getResult() { + StringBuilder stringBuffer = new StringBuilder(); + EditText editText; + for (int i = 0; i < mEtNumber; i++) { + editText = (EditText) getChildAt(i); + stringBuffer.append(editText.getText()); + } + return stringBuffer.toString(); + } + + public interface OnCodeFinishListener { + /** + * 文本改变 + */ + void onTextChange(View view, String content); + + /** + * 输入完成 + */ + void onComplete(View view, String content); + } + + /** + * 清空验证码输入框 + */ + public void setEmpty() { + EditText editText; + for (int i = mEtNumber - 1; i >= 0; i--) { + editText = (EditText) getChildAt(i); + editText.setText(""); + if (i == 0) { + if (cursorVisible) { + editText.setCursorVisible(true); + } else { + editText.setCursorVisible(false); + } + editText.requestFocus(); + } + } + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/DisplayUtil.java b/app/src/main/java/com/qidian/zxing/lib_zxing/DisplayUtil.java new file mode 100644 index 0000000..394b55f --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/DisplayUtil.java @@ -0,0 +1,36 @@ +package com.qidian.zxing.lib_zxing; + +import android.content.Context; + +/** + * Created by aaron on 16/8/3. + */ +public class DisplayUtil +{ + + public static int screenWidthPx; //屏幕宽 px + public static int screenhightPx; //屏幕高 px + public static float density;//屏幕密度 + public static int densityDPI;//屏幕密度 + public static float screenWidthDip;// dp单位 + public static float screenHightDip;// dp单位 + + + + /** + * 根据手机的分辨率从 dp 的单位 转成为 px(像素) + */ + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + /** + * 根据手机的分辨率从 px(像素) 的单位 转成为 dp + */ + public static int px2dip(Context context, float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/ZApplication.java b/app/src/main/java/com/qidian/zxing/lib_zxing/ZApplication.java new file mode 100644 index 0000000..a9a5c54 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/ZApplication.java @@ -0,0 +1,32 @@ +package com.qidian.zxing.lib_zxing; + +import android.app.Application; +import android.util.DisplayMetrics; + +import com.qidian.zxing.lib_zxing.DisplayUtil; + +/** + * Created by aaron on 16/8/9. + */ + +public class ZApplication extends Application{ + + @Override + public void onCreate() { + super.onCreate(); + /** + * 初始化尺寸工具类 + */ + initDisplayOpinion(); + } + + private void initDisplayOpinion() { + DisplayMetrics dm = getResources().getDisplayMetrics(); + com.qidian.zxing.lib_zxing.DisplayUtil.density = dm.density; + com.qidian.zxing.lib_zxing.DisplayUtil.densityDPI = dm.densityDpi; + com.qidian.zxing.lib_zxing.DisplayUtil.screenWidthPx = dm.widthPixels; + com.qidian.zxing.lib_zxing.DisplayUtil.screenhightPx = dm.heightPixels; + com.qidian.zxing.lib_zxing.DisplayUtil.screenWidthDip = com.qidian.zxing.lib_zxing.DisplayUtil.px2dip(getApplicationContext(), dm.widthPixels); + com.qidian.zxing.lib_zxing.DisplayUtil.screenHightDip = DisplayUtil.px2dip(getApplicationContext(), dm.heightPixels); + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CaptureActivity.kt b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CaptureActivity.kt new file mode 100644 index 0000000..f940365 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CaptureActivity.kt @@ -0,0 +1,61 @@ +package com.qidian.zxing.lib_zxing.activity + +import android.content.Intent +import android.graphics.Bitmap +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import com.qidian.zhongkesmart.R +import com.qidian.zxing.lib_zxing.activity.CodeUtils.AnalyzeCallback +import kotlinx.android.synthetic.main.camera.* + +/** + * Initial the camera + * + * + * 默认的二维码扫描Activity + */ +class CaptureActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.camera) + val captureFragment = CaptureFragment() + captureFragment.analyzeCallback = analyzeCallback + supportFragmentManager.beginTransaction().replace(R.id.fl_zxing_container, captureFragment) + .commit() + captureFragment.setCameraInitCallBack { e -> + if (e == null) { + } else { + Log.e("TAG", "callBack: ", e) + } + } + rl_back.setOnClickListener { + finish() + } + } + + /** + * 二维码解析回调函数 + */ + var analyzeCallback: AnalyzeCallback = object : AnalyzeCallback { + override fun onAnalyzeSuccess(mBitmap: Bitmap, result: String) { + val resultIntent = Intent() + val bundle = Bundle() + bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_SUCCESS) + bundle.putString(CodeUtils.RESULT_STRING, result) + resultIntent.putExtras(bundle) + this@CaptureActivity.setResult(RESULT_OK, resultIntent) + finish() + } + + override fun onAnalyzeFailed() { + val resultIntent = Intent() + val bundle = Bundle() + bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_FAILED) + bundle.putString(CodeUtils.RESULT_STRING, "") + resultIntent.putExtras(bundle) + this@CaptureActivity.setResult(RESULT_OK, resultIntent) + finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CaptureFragment.java b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CaptureFragment.java new file mode 100644 index 0000000..c9a5d21 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CaptureFragment.java @@ -0,0 +1,285 @@ +package com.qidian.zxing.lib_zxing.activity; + +import android.content.res.AssetFileDescriptor; +import android.graphics.Bitmap; +import android.hardware.Camera; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.os.Bundle; +import android.os.Handler; +import android.os.Vibrator; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.Result; +import com.qidian.zhongkesmart.R; +import com.qidian.zxing.lib_zxing.camera.CameraManager; +import com.qidian.zxing.lib_zxing.decoding.CaptureActivityHandler; +import com.qidian.zxing.lib_zxing.decoding.InactivityTimer; +import com.qidian.zxing.lib_zxing.view.ViewfinderView; + +import java.io.IOException; +import java.util.Vector; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +//import CodeUtils; + +/** + * 自定义实现的扫描Fragment + */ +public class CaptureFragment extends Fragment implements SurfaceHolder.Callback { + + private CaptureActivityHandler handler; + private ViewfinderView viewfinderView; + private boolean hasSurface; + private Vector decodeFormats; + private String characterSet; + private InactivityTimer inactivityTimer; + private MediaPlayer mediaPlayer; + private boolean playBeep; + private static final float BEEP_VOLUME = 0.10f; + private boolean vibrate; + private SurfaceView surfaceView; + private SurfaceHolder surfaceHolder; + private CodeUtils.AnalyzeCallback analyzeCallback; + private Camera camera; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + + CameraManager.init(getActivity().getApplication()); + + hasSurface = false; + inactivityTimer = new InactivityTimer(this.getActivity()); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + + Bundle bundle = getArguments(); + View view = null; + if (bundle != null) { + int layoutId = bundle.getInt(CodeUtils.LAYOUT_ID); + if (layoutId != -1) { + view = inflater.inflate(layoutId, null); + } + } + + if (view == null) { + view = inflater.inflate(R.layout.fragment_capture, null); + } + + viewfinderView = (ViewfinderView) view.findViewById(R.id.viewfinder_view); + surfaceView = (SurfaceView) view.findViewById(R.id.preview_view); + surfaceHolder = surfaceView.getHolder(); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + if (hasSurface) { + initCamera(surfaceHolder); + } else { + surfaceHolder.addCallback(this); + surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + } + decodeFormats = null; + characterSet = null; + + playBeep = true; + AudioManager audioService = (AudioManager) getActivity().getSystemService(getActivity().AUDIO_SERVICE); + if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { + playBeep = false; + } + initBeepSound(); + vibrate = true; + } + + @Override + public void onPause() { + super.onPause(); + if (handler != null) { + handler.quitSynchronously(); + handler = null; + } + CameraManager.get().closeDriver(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + inactivityTimer.shutdown(); + } + + + /** + * Handler scan result + * + * @param result + * @param barcode + */ + public void handleDecode(Result result, Bitmap barcode) { + inactivityTimer.onActivity(); + playBeepSoundAndVibrate(); + + if (result == null || TextUtils.isEmpty(result.getText())) { + if (analyzeCallback != null) { + analyzeCallback.onAnalyzeFailed(); + } + } else { + if (analyzeCallback != null) { + analyzeCallback.onAnalyzeSuccess(barcode, result.getText()); + } + } + } + + private void initCamera(SurfaceHolder surfaceHolder) { + try { + CameraManager.get().openDriver(surfaceHolder); + camera = CameraManager.get().getCamera(); + } catch (Exception e) { + if (callBack != null) { + callBack.callBack(e); + } + return; + } + if (callBack != null) { + callBack.callBack(null); + } + if (handler == null) { + handler = new CaptureActivityHandler( this, decodeFormats, characterSet, viewfinderView); + +// handler.setAutoFocusCallback(new CaptureActivityHandler.autoFocusCallback() { +// @Override +// public void onAutoFocusCallBack(boolean isSuccess) { +// ToastUtil.showToast("变焦:"+isSuccess); +// } +// }); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + if (!hasSurface) { + hasSurface = true; + initCamera(holder); + } + + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + hasSurface = false; + if (camera != null) { + if (camera != null && CameraManager.get().isPreviewing()) { + if (!CameraManager.get().isUseOneShotPreviewCallback()) { + camera.setPreviewCallback(null); + } + camera.stopPreview(); + CameraManager.get().getPreviewCallback().setHandler(null, 0); + CameraManager.get().getAutoFocusCallback().setHandler(null, 0); + CameraManager.get().setPreviewing(false); + } + } + } + + public Handler getHandler() { + return handler; + } + + public void drawViewfinder() { + viewfinderView.drawViewfinder(); + + } + + private void initBeepSound() { + if (playBeep && mediaPlayer == null) { + // The volume on STREAM_SYSTEM is not adjustable, and users found it + // too loud, + // so we now play on the music stream. + getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC); + mediaPlayer = new MediaPlayer(); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mediaPlayer.setOnCompletionListener(beepListener); + + AssetFileDescriptor file = getResources().openRawResourceFd( + R.raw.beep); + try { + mediaPlayer.setDataSource(file.getFileDescriptor(), + file.getStartOffset(), file.getLength()); + file.close(); + mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); + mediaPlayer.prepare(); + } catch (IOException e) { + mediaPlayer = null; + } + } + } + + private static final long VIBRATE_DURATION = 200L; + + private void playBeepSoundAndVibrate() { + if (playBeep && mediaPlayer != null) { + mediaPlayer.start(); + } + if (vibrate) { + Vibrator vibrator = (Vibrator) getActivity().getSystemService(getActivity().VIBRATOR_SERVICE); + vibrator.vibrate(VIBRATE_DURATION); + } + } + + /** + * When the beep has finished playing, rewind to queue up another one. + */ + private final MediaPlayer.OnCompletionListener beepListener = new MediaPlayer.OnCompletionListener() { + public void onCompletion(MediaPlayer mediaPlayer) { + mediaPlayer.seekTo(0); + } + }; + + public CodeUtils.AnalyzeCallback getAnalyzeCallback() { + return analyzeCallback; + } + + public void setAnalyzeCallback(CodeUtils.AnalyzeCallback analyzeCallback) { + this.analyzeCallback = analyzeCallback; + } + + @Nullable + CameraInitCallBack callBack; + + /** + * Set callback for Camera check whether Camera init success or not. + */ + public void setCameraInitCallBack(CameraInitCallBack callBack) { + this.callBack = callBack; + } + + interface CameraInitCallBack { + /** + * Callback for Camera init result. + * @param e If is's null,means success.otherwise Camera init failed with the Exception. + */ + void callBack(Exception e); + } + + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CodeUtils.java b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CodeUtils.java new file mode 100644 index 0000000..7171467 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/CodeUtils.java @@ -0,0 +1,223 @@ +package com.qidian.zxing.lib_zxing.activity; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.hardware.Camera; +import android.os.Bundle; +import android.text.TextUtils; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatReader; +import com.google.zxing.Result; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.qidian.zxing.lib_zxing.activity.CaptureFragment; +import com.qidian.zxing.lib_zxing.camera.BitmapLuminanceSource; +import com.qidian.zxing.lib_zxing.camera.CameraManager; +import com.qidian.zxing.lib_zxing.decoding.DecodeFormatManager; + +import java.util.Hashtable; +import java.util.Objects; +import java.util.Vector; + +/** + * Created by aaron on 16/7/27. + * 二维码扫描工具类 + */ +public class CodeUtils { + + public static final String RESULT_TYPE = "result_type"; + public static final String RESULT_STRING = "result_string"; + public static final int RESULT_SUCCESS = 1; + public static final int RESULT_FAILED = 2; + + public static final String LAYOUT_ID = "layout_id"; + + + + /** + * 解析二维码图片工具类 + * @param analyzeCallback + */ + public static void analyzeBitmap(String path, AnalyzeCallback analyzeCallback) { + + /** + * 首先判断图片的大小,若图片过大,则执行图片的裁剪操作,防止OOM + */ + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; // 先获取原大小 + Bitmap mBitmap = BitmapFactory.decodeFile(path, options); + options.inJustDecodeBounds = false; // 获取新的大小 + + int sampleSize = (int) (options.outHeight / (float) 400); + + if (sampleSize <= 0) + sampleSize = 1; + options.inSampleSize = sampleSize; + mBitmap = BitmapFactory.decodeFile(path, options); + + MultiFormatReader multiFormatReader = new MultiFormatReader(); + + // 解码的参数 + Hashtable hints = new Hashtable(2); + // 可以解析的编码类型 + Vector decodeFormats = new Vector(); + if (decodeFormats == null || decodeFormats.isEmpty()) { + decodeFormats = new Vector(); + + // 这里设置可扫描的类型,我这里选择了都支持 + decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS); + decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS); + decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS); + } + hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); + // 设置继续的字符编码格式为UTF8 + // hints.put(DecodeHintType.CHARACTER_SET, "UTF8"); + // 设置解析配置参数 + multiFormatReader.setHints(hints); + + // 开始对图像资源解码 + Result rawResult = null; + try { + rawResult = multiFormatReader.decodeWithState(new BinaryBitmap(new HybridBinarizer(new BitmapLuminanceSource(mBitmap)))); + } catch (Exception e) { + e.printStackTrace(); + } + + if (rawResult != null) { + if (analyzeCallback != null) { + analyzeCallback.onAnalyzeSuccess(mBitmap, rawResult.getText()); + } + } else { + if (analyzeCallback != null) { + analyzeCallback.onAnalyzeFailed(); + } + } + } + + /** + * 生成二维码图片 + * @param text + * @param w + * @param h + * @param logo + * @return + */ + public static Bitmap createImage(String text,int w,int h,Bitmap logo) { + if (TextUtils.isEmpty(text)) { + return null; + } + try { + Bitmap scaleLogo = getScaleLogo(logo,w,h); + + int offsetX = w / 2; + int offsetY = h / 2; + + int scaleWidth = 0; + int scaleHeight = 0; + if (scaleLogo != null) { + scaleWidth = scaleLogo.getWidth(); + scaleHeight = scaleLogo.getHeight(); + offsetX = (w - scaleWidth) / 2; + offsetY = (h - scaleHeight) / 2; + } + Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + //容错级别 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + //设置空白边距的宽度 + hints.put(EncodeHintType.MARGIN, 0); + BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, w, h, hints); + int[] pixels = new int[w * h]; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if(x >= offsetX && x < offsetX + scaleWidth && y>= offsetY && y < offsetY + scaleHeight){ + int pixel = scaleLogo.getPixel(x-offsetX,y-offsetY); + if(pixel == 0){ + if(bitMatrix.get(x, y)){ + pixel = 0xff000000; + }else{ + pixel = 0xffffffff; + } + } + pixels[y * w + x] = pixel; + }else{ + if (bitMatrix.get(x, y)) { + pixels[y * w + x] = 0xff000000; + } else { + pixels[y * w + x] = 0xffffffff; + } + } + } + } + Bitmap bitmap = Bitmap.createBitmap(w, h, + Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, w, 0, 0, w, h); + return bitmap; + } catch (WriterException e) { + e.printStackTrace(); + } + return null; + } + + private static Bitmap getScaleLogo(Bitmap logo,int w,int h){ + if(logo == null)return null; + Matrix matrix = new Matrix(); + float scaleFactor = Math.min(w * 1.0f / 5 / logo.getWidth(), h * 1.0f / 5 /logo.getHeight()); + matrix.postScale(scaleFactor,scaleFactor); + Bitmap result = Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true); + return result; + } + + /** + * 解析二维码结果 + */ + public interface AnalyzeCallback{ + + public void onAnalyzeSuccess(Bitmap mBitmap, String result); + + public void onAnalyzeFailed(); + } + + + /** + * 为CaptureFragment设置layout参数 + * @param captureFragment + * @param layoutId + */ + public static void setFragmentArgs(CaptureFragment captureFragment, int layoutId) { + if (captureFragment == null || layoutId == -1) { + return; + } + + Bundle bundle = new Bundle(); + bundle.putInt(LAYOUT_ID, layoutId); + captureFragment.setArguments(bundle); + } + + public static void isLightEnable(boolean isEnable) { + if (isEnable) { + Camera camera = CameraManager.get().getCamera(); + if (camera != null) { + Camera.Parameters parameter = camera.getParameters(); + parameter.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); + camera.setParameters(parameter); + } + } else { + Camera camera = CameraManager.get().getCamera(); + if (camera != null) { + Camera.Parameters parameter = camera.getParameters(); + parameter.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); + camera.setParameters(parameter); + } + } + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/activity/ZXingLibrary.java b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/ZXingLibrary.java new file mode 100644 index 0000000..c53a91f --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/activity/ZXingLibrary.java @@ -0,0 +1,26 @@ +package com.qidian.zxing.lib_zxing.activity; + +import android.content.Context; +import android.util.DisplayMetrics; + +import com.qidian.zxing.lib_zxing.DisplayUtil; + +/** + * Created by aaron on 16/9/7. + */ + +public class ZXingLibrary { + + public static void initDisplayOpinion(Context context) { + if (context == null) { + return; + } + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + DisplayUtil.density = dm.density; + DisplayUtil.densityDPI = dm.densityDpi; + DisplayUtil.screenWidthPx = dm.widthPixels; + DisplayUtil.screenhightPx = dm.heightPixels; + DisplayUtil.screenWidthDip = DisplayUtil.px2dip(context, dm.widthPixels); + DisplayUtil.screenHightDip = DisplayUtil.px2dip(context, dm.heightPixels); + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/camera/AutoFocusCallback.java b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/AutoFocusCallback.java new file mode 100644 index 0000000..61489c5 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/AutoFocusCallback.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.camera; + +import android.hardware.Camera; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +public final class AutoFocusCallback implements Camera.AutoFocusCallback { + + private static final String TAG = AutoFocusCallback.class.getSimpleName(); + + private static final long AUTOFOCUS_INTERVAL_MS = 1500L; + + private Handler autoFocusHandler; + private int autoFocusMessage; + + public void setHandler(Handler autoFocusHandler, int autoFocusMessage) { + this.autoFocusHandler = autoFocusHandler; + this.autoFocusMessage = autoFocusMessage; + } + + public void onAutoFocus(boolean success, Camera camera) { + if (autoFocusHandler != null) { + Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success); + autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS); + autoFocusHandler = null; + } else { + Log.d(TAG, "Got auto-focus callback, but no handler for it"); + } + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/camera/BitmapLuminanceSource.java b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/BitmapLuminanceSource.java new file mode 100644 index 0000000..5ba78a2 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/BitmapLuminanceSource.java @@ -0,0 +1,41 @@ +package com.qidian.zxing.lib_zxing.camera; + +import android.graphics.Bitmap; + +import com.google.zxing.LuminanceSource; + +/** + * Created by aaron on 16/7/27. + * 自定义解析Bitmap LuminanceSource + */ +public class BitmapLuminanceSource extends LuminanceSource { + + private byte bitmapPixels[]; + + public BitmapLuminanceSource(Bitmap bitmap) { + super(bitmap.getWidth(), bitmap.getHeight()); + + // 首先,要取得该图片的像素数组内容 + int[] data = new int[bitmap.getWidth() * bitmap.getHeight()]; + this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()]; + bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight()); + + // 将int数组转换为byte数组,也就是取像素值中蓝色值部分作为辨析内容 + for (int i = 0; i < data.length; i++) { + this.bitmapPixels[i] = (byte) data[i]; + } + } + + @Override + public byte[] getMatrix() { + // 返回我们生成好的像素数据 + return bitmapPixels; + } + + @Override + public byte[] getRow(int y, byte[] row) { + // 这里要得到指定行的像素数据 + System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth()); + return row; + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/camera/CameraConfigurationManager.java b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/CameraConfigurationManager.java new file mode 100644 index 0000000..5ac5c67 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/CameraConfigurationManager.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.camera; + +import android.content.Context; +import android.graphics.Point; +import android.hardware.Camera; +import android.os.Build; +import android.util.Log; +import android.view.Display; +import android.view.WindowManager; + +import com.qidian.zxing.lib_zxing.camera.CameraManager; + +import java.util.regex.Pattern; + +final class CameraConfigurationManager { + + private static final String TAG = CameraConfigurationManager.class.getSimpleName(); + + private static final int TEN_DESIRED_ZOOM = 27; + private static final int DESIRED_SHARPNESS = 30; + + private static final Pattern COMMA_PATTERN = Pattern.compile(","); + + private final Context context; + private Point screenResolution; + private Point cameraResolution; + private int previewFormat; + private String previewFormatString; + + CameraConfigurationManager(Context context) { + this.context = context; + } + + /** + * Reads, one time, values from the camera that are needed by the app. + */ + void initFromCameraParameters(Camera camera) { + Camera.Parameters parameters = camera.getParameters(); + previewFormat = parameters.getPreviewFormat(); + previewFormatString = parameters.get("preview-format"); + Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString); + WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + Display display = manager.getDefaultDisplay(); + screenResolution = new Point(display.getWidth(), display.getHeight()); + Log.d(TAG, "Screen resolution: " + screenResolution); + + Point screenResolutionForCamera = new Point(); + screenResolutionForCamera.x = screenResolution.x; + screenResolutionForCamera.y = screenResolution.y; + // preview size is always something like 480*320, other 320*480 + if (screenResolution.x < screenResolution.y) { + screenResolutionForCamera.x = screenResolution.y; + screenResolutionForCamera.y = screenResolution.x; + } + Log.i("#########", "screenX:" + screenResolutionForCamera.x + " screenY:" + screenResolutionForCamera.y); + cameraResolution = getCameraResolution(parameters, screenResolutionForCamera); + + // cameraResolution = getCameraResolution(parameters, screenResolution); + Log.d(TAG, "Camera resolution: " + screenResolution); + } + + /** + * Sets the camera up to take preview images which are used for both preview and decoding. + * We detect the preview format here so that buildLuminanceSource() can build an appropriate + * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest, + * and the planar Y can be used for barcode scanning without a copy in some cases. + */ + void setDesiredCameraParameters(Camera camera) { + Camera.Parameters parameters = camera.getParameters(); + Log.d(TAG, "Setting preview size: " + cameraResolution); + parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); + setFlash(parameters); + setZoom(parameters); + //setSharpness(parameters); + //modify here + camera.setDisplayOrientation(180); + camera.setParameters(parameters); + } + + Point getCameraResolution() { + return cameraResolution; + } + + Point getScreenResolution() { + return screenResolution; + } + + int getPreviewFormat() { + return previewFormat; + } + + String getPreviewFormatString() { + return previewFormatString; + } + + private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) { + + String previewSizeValueString = parameters.get("preview-size-values"); + // saw this on Xperia + if (previewSizeValueString == null) { + previewSizeValueString = parameters.get("preview-size-value"); + } + + Point cameraResolution = null; + + if (previewSizeValueString != null) { + Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString); + cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution); + } + + if (cameraResolution == null) { + // Ensure that the camera resolution is a multiple of 8, as the screen may not be. + cameraResolution = new Point( + (screenResolution.x >> 3) << 3, + (screenResolution.y >> 3) << 3); + } + + return cameraResolution; + } + + private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) { + int bestX = 0; + int bestY = 0; + int diff = Integer.MAX_VALUE; + for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) { + + previewSize = previewSize.trim(); + int dimPosition = previewSize.indexOf('x'); + if (dimPosition < 0) { + Log.w(TAG, "Bad preview-size: " + previewSize); + continue; + } + + int newX; + int newY; + try { + newX = Integer.parseInt(previewSize.substring(0, dimPosition)); + newY = Integer.parseInt(previewSize.substring(dimPosition + 1)); + } catch (NumberFormatException nfe) { + Log.w(TAG, "Bad preview-size: " + previewSize); + continue; + } + + int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y); + if (newDiff == 0) { + bestX = newX; + bestY = newY; + break; + } else if (newDiff < diff) { + bestX = newX; + bestY = newY; + diff = newDiff; + } + + } + + if (bestX > 0 && bestY > 0) { + return new Point(bestX, bestY); + } + return null; + } + + private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) { + int tenBestValue = 0; + for (String stringValue : COMMA_PATTERN.split(stringValues)) { + stringValue = stringValue.trim(); + double value; + try { + value = Double.parseDouble(stringValue); + } catch (NumberFormatException nfe) { + return tenDesiredZoom; + } + int tenValue = (int) (10.0 * value); + if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) { + tenBestValue = tenValue; + } + } + return tenBestValue; + } + + private void setFlash(Camera.Parameters parameters) { + // FIXME: This is a hack to turn the flash off on the Samsung Galaxy. + // And this is a hack-hack to work around a different value on the Behold II + // Restrict Behold II check to Cupcake, per Samsung's advice + //if (Build.MODEL.contains("Behold II") && + // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) { + if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake + parameters.set("flash-value", 1); + } else { + parameters.set("flash-value", 2); + } + // This is the standard setting to turn the flash off that all devices should honor. + parameters.set("flash-mode", "off"); + } + + private void setZoom(Camera.Parameters parameters) { + + String zoomSupportedString = parameters.get("zoom-supported"); + if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) { + return; + } + + int tenDesiredZoom = TEN_DESIRED_ZOOM; + + String maxZoomString = parameters.get("max-zoom"); + if (maxZoomString != null) { + try { + int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString)); + if (tenDesiredZoom > tenMaxZoom) { + tenDesiredZoom = tenMaxZoom; + } + } catch (NumberFormatException nfe) { + Log.w(TAG, "Bad max-zoom: " + maxZoomString); + } + } + + String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max"); + if (takingPictureZoomMaxString != null) { + try { + int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString); + if (tenDesiredZoom > tenMaxZoom) { + tenDesiredZoom = tenMaxZoom; + } + } catch (NumberFormatException nfe) { + Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString); + } + } + + String motZoomValuesString = parameters.get("mot-zoom-values"); + if (motZoomValuesString != null) { + tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom); + } + + String motZoomStepString = parameters.get("mot-zoom-step"); + if (motZoomStepString != null) { + try { + double motZoomStep = Double.parseDouble(motZoomStepString.trim()); + int tenZoomStep = (int) (10.0 * motZoomStep); + if (tenZoomStep > 1) { + tenDesiredZoom -= tenDesiredZoom % tenZoomStep; + } + } catch (NumberFormatException nfe) { + // continue + } + } + + // Set zoom. This helps encourage the user to pull back. + // Some devices like the Behold have a zoom parameter + if (maxZoomString != null || motZoomValuesString != null) { + parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0)); + } + + // Most devices, like the Hero, appear to expose this zoom parameter. + // It takes on values like "27" which appears to mean 2.7x zoom + if (takingPictureZoomMaxString != null) { + parameters.set("taking-picture-zoom", tenDesiredZoom); + } + } + + public static int getDesiredSharpness() { + return DESIRED_SHARPNESS; + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/camera/CameraManager.java b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/CameraManager.java new file mode 100644 index 0000000..edb6abf --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/CameraManager.java @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2008 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.camera; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.Camera; +import android.os.Build; +import android.os.Handler; +import android.view.SurfaceHolder; + +import com.qidian.zxing.lib_zxing.camera.AutoFocusCallback; +import com.qidian.zxing.lib_zxing.camera.CameraConfigurationManager; +import com.qidian.zxing.lib_zxing.camera.FlashlightManager; +import com.qidian.zxing.lib_zxing.camera.PlanarYUVLuminanceSource; +import com.qidian.zxing.lib_zxing.camera.PreviewCallback; + +import java.io.IOException; + +/** + * This object wraps the Camera service object and expects to be the only one talking to it. The + * implementation encapsulates the steps needed to take preview-sized images, which are used for + * both preview and decoding. + */ +public final class CameraManager { + + private static final String TAG = CameraManager.class.getSimpleName(); + + public static int FRAME_WIDTH = -1; + public static int FRAME_HEIGHT = -1; + public static int FRAME_MARGINTOP = -1; + + private static CameraManager cameraManager; + + static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT + + static { + int sdkInt; + try { + sdkInt = Integer.parseInt(Build.VERSION.SDK); + } catch (NumberFormatException nfe) { + // Just to be safe + sdkInt = 10000; + } + SDK_INT = sdkInt; + } + + private final Context context; + private final CameraConfigurationManager configManager; + private Camera camera; + private Rect framingRect; + private Rect framingRectInPreview; + private boolean initialized; + private boolean previewing; + private final boolean useOneShotPreviewCallback; + /** + * Preview frames are delivered here, which we pass on to the registered handler. Make sure to + * clear the handler so it will only receive one message. + */ + private final PreviewCallback previewCallback; + /** + * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them. + */ + private final AutoFocusCallback autoFocusCallback; + + /** + * Initializes this static object with the Context of the calling Activity. + * + * @param context The Activity which wants to use the camera. + */ + public static void init(Context context) { + if (cameraManager == null) { + cameraManager = new CameraManager(context); + } + } + + /** + * Gets the CameraManager singleton instance. + * + * @return A reference to the CameraManager singleton. + */ + public static CameraManager get() { + return cameraManager; + } + + private CameraManager(Context context) { + + this.context = context; + this.configManager = new CameraConfigurationManager(context); + + // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older + // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use + // the more efficient one shot callback, as the older one can swamp the system and cause it + // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK. + //useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE; + useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3; // 3 = Cupcake + + previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback); + autoFocusCallback = new AutoFocusCallback(); + } + + /** + * Opens the camera driver and initializes the hardware parameters. + * + * @param holder The surface object which the camera will draw preview frames into. + * @throws IOException Indicates the camera driver failed to open. + */ + public void openDriver(SurfaceHolder holder) throws IOException { + if (camera == null) { + camera = Camera.open(); + if (camera == null) { + throw new IOException(); + } + camera.setPreviewDisplay(holder); + + if (!initialized) { + initialized = true; + configManager.initFromCameraParameters(camera); + } + configManager.setDesiredCameraParameters(camera); + + //FIXME + // SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + //�Ƿ�ʹ��ǰ�� +// if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) { +// FlashlightManager.enableFlashlight(); +// } + FlashlightManager.enableFlashlight(); + } + } + + /** + * Closes the camera driver if still in use. + */ + public void closeDriver() { + if (camera != null) { + FlashlightManager.disableFlashlight(); + camera.release(); + camera = null; + } + } + + /** + * Asks the camera hardware to begin drawing preview frames to the screen. + */ + public void startPreview() { + if (camera != null && !previewing) { + camera.startPreview(); + previewing = true; + } + } + + /** + * Tells the camera to stop drawing preview frames. + */ + public void stopPreview() { + if (camera != null && previewing) { + if (!useOneShotPreviewCallback) { + camera.setPreviewCallback(null); + } + camera.stopPreview(); + previewCallback.setHandler(null, 0); + autoFocusCallback.setHandler(null, 0); + previewing = false; + } + } + + /** + * A single preview frame will be returned to the handler supplied. The data will arrive as byte[] + * in the message.obj field, with width and height encoded as message.arg1 and message.arg2, + * respectively. + * + * @param handler The handler to send the message to. + * @param message The what field of the message to be sent. + */ + public void requestPreviewFrame(Handler handler, int message) { + if (camera != null && previewing) { + previewCallback.setHandler(handler, message); + if (useOneShotPreviewCallback) { + camera.setOneShotPreviewCallback(previewCallback); + } else { + camera.setPreviewCallback(previewCallback); + } + } + } + + /** + * Asks the camera hardware to perform an autofocus. + * + * @param handler The Handler to notify when the autofocus completes. + * @param message The message to deliver. + */ + public void requestAutoFocus(Handler handler, int message) { + if (camera != null && previewing) { + autoFocusCallback.setHandler(handler, message); + //Log.d(TAG, "Requesting auto-focus callback"); + camera.autoFocus(autoFocusCallback); + } + } + + /** + * Calculates the framing rect which the UI should draw to show the user where to place the + * barcode. This target helps with alignment as well as forces the user to hold the device + * far enough away to ensure the image will be in focus. + * + * @return The rectangle to draw on screen in window coordinates. + */ + public Rect getFramingRect() { + try { + Point screenResolution = configManager.getScreenResolution(); + // if (framingRect == null) { + if (camera == null) { + return null; + } + + int leftOffset = (screenResolution.x - FRAME_WIDTH) / 2; + + int topOffset; + if (FRAME_MARGINTOP != -1) { + topOffset = FRAME_MARGINTOP; + } else { + topOffset = (screenResolution.y - FRAME_HEIGHT) / 2; + } + framingRect = new Rect(leftOffset, topOffset, leftOffset + FRAME_WIDTH, topOffset + FRAME_HEIGHT); + // } + return framingRect; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * Like {@link #getFramingRect} but coordinates are in terms of the preview frame, + * not UI / screen. + */ + public Rect getFramingRectInPreview() { + if (framingRectInPreview == null) { + Rect rect = new Rect(getFramingRect()); + Point cameraResolution = configManager.getCameraResolution(); + Point screenResolution = configManager.getScreenResolution(); + //modify here +// rect.left = rect.left * cameraResolution.x / screenResolution.x; +// rect.right = rect.right * cameraResolution.x / screenResolution.x; +// rect.top = rect.top * cameraResolution.y / screenResolution.y; +// rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y; + rect.left = rect.left * cameraResolution.y / screenResolution.x; + rect.right = rect.right * cameraResolution.y / screenResolution.x; + rect.top = rect.top * cameraResolution.x / screenResolution.y; + rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y; + framingRectInPreview = rect; + } + return framingRectInPreview; + } + + /** + * Converts the result points from still resolution coordinates to screen coordinates. + * + * @param points The points returned by the Reader subclass through Result.getResultPoints(). + * @return An array of Points scaled to the size of the framing rect and offset appropriately + * so they can be drawn in screen coordinates. + */ + /* + public Point[] convertResultPoints(ResultPoint[] points) { + Rect frame = getFramingRectInPreview(); + int count = points.length; + Point[] output = new Point[count]; + for (int x = 0; x < count; x++) { + output[x] = new Point(); + output[x].x = frame.left + (int) (points[x].getX() + 0.5f); + output[x].y = frame.top + (int) (points[x].getY() + 0.5f); + } + return output; + } + */ + + /** + * A factory method to build the appropriate LuminanceSource object based on the format + * of the preview buffers, as described by Camera.Parameters. + * + * @param data A preview frame. + * @param width The width of the image. + * @param height The height of the image. + * @return A PlanarYUVLuminanceSource instance. + */ + public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) { + Rect rect = getFramingRectInPreview(); + int previewFormat = configManager.getPreviewFormat(); + String previewFormatString = configManager.getPreviewFormatString(); + switch (previewFormat) { + // This is the standard Android format which all devices are REQUIRED to support. + // In theory, it's the only one we should ever care about. + case PixelFormat.YCbCr_420_SP: + // This format has never been seen in the wild, but is compatible as we only care + // about the Y channel, so allow it. + case PixelFormat.YCbCr_422_SP: + return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, + rect.width(), rect.height()); + default: + // The Samsung Moment incorrectly uses this variant instead of the 'sp' version. + // Fortunately, it too has all the Y data up front, so we can read it. + if ("yuv420p".equals(previewFormatString)) { + return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, + rect.width(), rect.height()); + } + } + throw new IllegalArgumentException("Unsupported picture format: " + + previewFormat + '/' + previewFormatString); + } + + public Context getContext() { + return context; + } + + public Camera getCamera() { + return camera; + } + + public boolean isPreviewing() { + return previewing; + } + + public boolean isUseOneShotPreviewCallback() { + return useOneShotPreviewCallback; + } + + public PreviewCallback getPreviewCallback() { + return previewCallback; + } + + public AutoFocusCallback getAutoFocusCallback() { + return autoFocusCallback; + } + + public void setPreviewing(boolean previewing) { + this.previewing = previewing; + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/camera/FlashlightManager.java b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/FlashlightManager.java new file mode 100644 index 0000000..00897a7 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/FlashlightManager.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.camera; + +import android.os.IBinder; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * This class is used to activate the weak light on some camera phones (not flash) + * in order to illuminate surfaces for scanning. There is no official way to do this, + * but, classes which allow access to this function still exist on some devices. + * This therefore proceeds through a great deal of reflection. + *

+ * See + * http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/ and + * + * http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java. + * Thanks to Ryan Alford for pointing out the availability of this class. + */ +final class FlashlightManager { + + private static final String TAG = FlashlightManager.class.getSimpleName(); + + private static final Object iHardwareService; + private static final Method setFlashEnabledMethod; + + static { + iHardwareService = getHardwareService(); + setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService); + if (iHardwareService == null) { + Log.v(TAG, "This device does supports control of a flashlight"); + } else { + Log.v(TAG, "This device does not support control of a flashlight"); + } + } + + private FlashlightManager() { + } + + /** + * �����������ƿ��� + */ + //FIXME + static void enableFlashlight() { + setFlashlight(false); + } + + static void disableFlashlight() { + setFlashlight(false); + } + + private static Object getHardwareService() { + Class serviceManagerClass = maybeForName("android.os.ServiceManager"); + if (serviceManagerClass == null) { + return null; + } + + Method getServiceMethod = maybeGetMethod(serviceManagerClass, "getService", String.class); + if (getServiceMethod == null) { + return null; + } + + Object hardwareService = invoke(getServiceMethod, null, "hardware"); + if (hardwareService == null) { + return null; + } + + Class iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub"); + if (iHardwareServiceStubClass == null) { + return null; + } + + Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass, "asInterface", IBinder.class); + if (asInterfaceMethod == null) { + return null; + } + + return invoke(asInterfaceMethod, null, hardwareService); + } + + private static Method getSetFlashEnabledMethod(Object iHardwareService) { + if (iHardwareService == null) { + return null; + } + Class proxyClass = iHardwareService.getClass(); + return maybeGetMethod(proxyClass, "setFlashlightEnabled", boolean.class); + } + + private static Class maybeForName(String name) { + try { + return Class.forName(name); + } catch (ClassNotFoundException cnfe) { + // OK + return null; + } catch (RuntimeException re) { + Log.w(TAG, "Unexpected error while finding class " + name, re); + return null; + } + } + + private static Method maybeGetMethod(Class clazz, String name, Class... argClasses) { + try { + return clazz.getMethod(name, argClasses); + } catch (NoSuchMethodException nsme) { + // OK + return null; + } catch (RuntimeException re) { + Log.w(TAG, "Unexpected error while finding method " + name, re); + return null; + } + } + + private static Object invoke(Method method, Object instance, Object... args) { + try { + return method.invoke(instance, args); + } catch (IllegalAccessException e) { + Log.w(TAG, "Unexpected error while invoking " + method, e); + return null; + } catch (InvocationTargetException e) { + Log.w(TAG, "Unexpected error while invoking " + method, e.getCause()); + return null; + } catch (RuntimeException re) { + Log.w(TAG, "Unexpected error while invoking " + method, re); + return null; + } + } + + private static void setFlashlight(boolean active) { + if (iHardwareService != null) { + invoke(setFlashEnabledMethod, iHardwareService, active); + } + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/camera/PlanarYUVLuminanceSource.java b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/PlanarYUVLuminanceSource.java new file mode 100644 index 0000000..b260ab9 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/PlanarYUVLuminanceSource.java @@ -0,0 +1,133 @@ +/* + * Copyright 2009 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.camera; + +import android.graphics.Bitmap; + +import com.google.zxing.LuminanceSource; + +/** + * This object extends LuminanceSource around an array of YUV data returned from the camera driver, + * with the option to crop to a rectangle within the full data. This can be used to exclude + * superfluous pixels around the perimeter and speed up decoding. + *

+ * It works for any pixel format where the Y channel is planar and appears first, including + * YCbCr_420_SP and YCbCr_422_SP. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class PlanarYUVLuminanceSource extends LuminanceSource { + private final byte[] yuvData; + private final int dataWidth; + private final int dataHeight; + private final int left; + private final int top; + + public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top, + int width, int height) { + super(width, height); + + if (left + width > dataWidth || top + height > dataHeight) { + throw new IllegalArgumentException("Crop rectangle does not fit within image data."); + } + + this.yuvData = yuvData; + this.dataWidth = dataWidth; + this.dataHeight = dataHeight; + this.left = left; + this.top = top; + } + + @Override + public byte[] getRow(int y, byte[] row) { + if (y < 0 || y >= getHeight()) { + throw new IllegalArgumentException("Requested row is outside the image: " + y); + } + int width = getWidth(); + if (row == null || row.length < width) { + row = new byte[width]; + } + int offset = (y + top) * dataWidth + left; + System.arraycopy(yuvData, offset, row, 0, width); + return row; + } + + @Override + public byte[] getMatrix() { + int width = getWidth(); + int height = getHeight(); + + // If the caller asks for the entire underlying image, save the copy and give them the + // original data. The docs specifically warn that result.length must be ignored. + if (width == dataWidth && height == dataHeight) { + return yuvData; + } + + int area = width * height; + byte[] matrix = new byte[area]; + int inputOffset = top * dataWidth + left; + + // If the width matches the full width of the underlying data, perform a single copy. + if (width == dataWidth) { + System.arraycopy(yuvData, inputOffset, matrix, 0, area); + return matrix; + } + + // Otherwise copy one cropped row at a time. + byte[] yuv = yuvData; + for (int y = 0; y < height; y++) { + int outputOffset = y * width; + System.arraycopy(yuv, inputOffset, matrix, outputOffset, width); + inputOffset += dataWidth; + } + return matrix; + } + + @Override + public boolean isCropSupported() { + return true; + } + + public int getDataWidth() { + return dataWidth; + } + + public int getDataHeight() { + return dataHeight; + } + + public Bitmap renderCroppedGreyscaleBitmap() { + int width = getWidth(); + int height = getHeight(); + int[] pixels = new int[width * height]; + byte[] yuv = yuvData; + int inputOffset = top * dataWidth + left; + + for (int y = 0; y < height; y++) { + int outputOffset = y * width; + for (int x = 0; x < width; x++) { + int grey = yuv[inputOffset + x] & 0xff; + pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101); + } + inputOffset += dataWidth; + } + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return bitmap; + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/camera/PreviewCallback.java b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/PreviewCallback.java new file mode 100644 index 0000000..0970140 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/camera/PreviewCallback.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.camera; + +import android.graphics.Point; +import android.hardware.Camera; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import com.qidian.zxing.lib_zxing.camera.CameraConfigurationManager; + +public final class PreviewCallback implements Camera.PreviewCallback { + + private static final String TAG = PreviewCallback.class.getSimpleName(); + + private final CameraConfigurationManager configManager; + private final boolean useOneShotPreviewCallback; + private Handler previewHandler; + private int previewMessage; + + PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) { + this.configManager = configManager; + this.useOneShotPreviewCallback = useOneShotPreviewCallback; + } + + public void setHandler(Handler previewHandler, int previewMessage) { + this.previewHandler = previewHandler; + this.previewMessage = previewMessage; + } + + public void onPreviewFrame(byte[] data, Camera camera) { + Point cameraResolution = configManager.getCameraResolution(); + if (!useOneShotPreviewCallback) { + camera.setPreviewCallback(null); + } + if (previewHandler != null) { + Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x, + cameraResolution.y, data); + message.sendToTarget(); + previewHandler = null; + } else { + Log.d(TAG, "Got preview callback, but no handler for it"); + } + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/CaptureActivityHandler.java b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/CaptureActivityHandler.java new file mode 100644 index 0000000..567ce79 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/CaptureActivityHandler.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2008 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.decoding; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.Result; +import com.qidian.zhongkesmart.R; +import com.qidian.zxing.lib_zxing.activity.CaptureFragment; +import com.qidian.zxing.lib_zxing.camera.CameraManager; +import com.qidian.zxing.lib_zxing.view.ViewfinderResultPointCallback; +import com.qidian.zxing.lib_zxing.view.ViewfinderView; + +import java.util.Vector; + +/** + * This class handles all the messaging which comprises the state machine for capture. + */ +public final class CaptureActivityHandler extends Handler { + + private static final String TAG = CaptureActivityHandler.class.getSimpleName(); + + private final CaptureFragment fragment; + private final com.qidian.zxing.lib_zxing.decoding.DecodeThread decodeThread; + private State state; + + private enum State { + PREVIEW, + SUCCESS, + DONE + } + + public CaptureActivityHandler(CaptureFragment fragment, Vector decodeFormats, + String characterSet, ViewfinderView viewfinderView) { + this.fragment = fragment; + decodeThread = new com.qidian.zxing.lib_zxing.decoding.DecodeThread(fragment, decodeFormats, characterSet, + new ViewfinderResultPointCallback(viewfinderView)); + decodeThread.start(); + state = State.SUCCESS; + // Start ourselves capturing previews and decoding. + CameraManager.get().startPreview(); + restartPreviewAndDecode(); + } + + public interface autoFocusCallback{ + void onAutoFocusCallBack(boolean isSuccess); + } + private autoFocusCallback myAutoFocusCallback; + public void setAutoFocusCallback(autoFocusCallback autoFocusCallback){ + myAutoFocusCallback = autoFocusCallback; + } + + @Override + public void handleMessage(Message message) { + if (message.what == R.id.auto_focus) { +// if(myAutoFocusCallback!=null){ +// myAutoFocusCallback.onAutoFocusCallBack((Boolean) message.obj); +// } + //Log.d(TAG, "Got auto-focus message"); + // When one auto focus pass finishes, start another. This is the closest thing to + // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do. + if (state == State.PREVIEW) { + CameraManager.get().requestAutoFocus(this, R.id.auto_focus); + } + } else if (message.what == R.id.restart_preview) { + Log.d(TAG, "Got restart preview message"); + restartPreviewAndDecode(); + } else if (message.what == R.id.decode_succeeded) { + Log.d(TAG, "Got decode succeeded message"); + state = State.SUCCESS; + Bundle bundle = message.getData(); + + /***********************************************************************/ + Bitmap barcode = bundle == null ? null : + (Bitmap) bundle.getParcelable(com.qidian.zxing.lib_zxing.decoding.DecodeThread.BARCODE_BITMAP);//���ñ����߳� + + fragment.handleDecode((Result) message.obj, barcode);//���ؽ�� + /***********************************************************************/ + } else if (message.what == R.id.decode_failed) { + // We're decoding as fast as possible, so when one decode fails, start another. + state = State.PREVIEW; + CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode); + } else if (message.what == R.id.return_scan_result) { + Log.d(TAG, "Got return scan result message"); + fragment.getActivity().setResult(Activity.RESULT_OK, (Intent) message.obj); + fragment.getActivity().finish(); + } else if (message.what == R.id.launch_product_query) { + Log.d(TAG, "Got product query message"); + String url = (String) message.obj; + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + fragment.getActivity().startActivity(intent); + } + } + + public void quitSynchronously() { + state = State.DONE; + CameraManager.get().stopPreview(); + Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit); + quit.sendToTarget(); + try { + decodeThread.join(); + } catch (InterruptedException e) { + // continue + } + + // Be absolutely sure we don't send any queued up messages + removeMessages(R.id.decode_succeeded); + removeMessages(R.id.decode_failed); + } + + private void restartPreviewAndDecode() { + if (state == State.SUCCESS) { + state = State.PREVIEW; + CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode); + CameraManager.get().requestAutoFocus(this, R.id.auto_focus); + fragment.drawViewfinder(); + } + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeFormatManager.java b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeFormatManager.java new file mode 100644 index 0000000..de10aa3 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeFormatManager.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.decoding; + +import android.content.Intent; +import android.net.Uri; + +import com.google.zxing.BarcodeFormat; +import com.qidian.zxing.lib_zxing.decoding.Intents; + +import java.util.Arrays; +import java.util.List; +import java.util.Vector; +import java.util.regex.Pattern; + +public class DecodeFormatManager { + + private static final Pattern COMMA_PATTERN = Pattern.compile(","); + + public static final Vector PRODUCT_FORMATS; + public static final Vector ONE_D_FORMATS; + public static final Vector QR_CODE_FORMATS; + public static final Vector DATA_MATRIX_FORMATS; + + static { + PRODUCT_FORMATS = new Vector(5); + PRODUCT_FORMATS.add(BarcodeFormat.UPC_A); + PRODUCT_FORMATS.add(BarcodeFormat.UPC_E); + PRODUCT_FORMATS.add(BarcodeFormat.EAN_13); + PRODUCT_FORMATS.add(BarcodeFormat.EAN_8); + // PRODUCT_FORMATS.add(BarcodeFormat.RSS14); + ONE_D_FORMATS = new Vector(PRODUCT_FORMATS.size() + 4); + ONE_D_FORMATS.addAll(PRODUCT_FORMATS); + ONE_D_FORMATS.add(BarcodeFormat.CODE_39); + ONE_D_FORMATS.add(BarcodeFormat.CODE_93); + ONE_D_FORMATS.add(BarcodeFormat.CODE_128); + ONE_D_FORMATS.add(BarcodeFormat.ITF); + QR_CODE_FORMATS = new Vector(1); + QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE); + DATA_MATRIX_FORMATS = new Vector(1); + DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX); + } + + private DecodeFormatManager() { + } + + static Vector parseDecodeFormats(Intent intent) { + List scanFormats = null; + String scanFormatsString = intent.getStringExtra(com.qidian.zxing.lib_zxing.decoding.Intents.Scan.SCAN_FORMATS); + if (scanFormatsString != null) { + scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString)); + } + return parseDecodeFormats(scanFormats, intent.getStringExtra(com.qidian.zxing.lib_zxing.decoding.Intents.Scan.MODE)); + } + + static Vector parseDecodeFormats(Uri inputUri) { + List formats = inputUri.getQueryParameters(com.qidian.zxing.lib_zxing.decoding.Intents.Scan.SCAN_FORMATS); + if (formats != null && formats.size() == 1 && formats.get(0) != null) { + formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0))); + } + return parseDecodeFormats(formats, inputUri.getQueryParameter(com.qidian.zxing.lib_zxing.decoding.Intents.Scan.MODE)); + } + + private static Vector parseDecodeFormats(Iterable scanFormats, + String decodeMode) { + if (scanFormats != null) { + Vector formats = new Vector(); + try { + for (String format : scanFormats) { + formats.add(BarcodeFormat.valueOf(format)); + } + return formats; + } catch (IllegalArgumentException iae) { + // ignore it then + } + } + if (decodeMode != null) { + if (com.qidian.zxing.lib_zxing.decoding.Intents.Scan.PRODUCT_MODE.equals(decodeMode)) { + return PRODUCT_FORMATS; + } + if (com.qidian.zxing.lib_zxing.decoding.Intents.Scan.QR_CODE_MODE.equals(decodeMode)) { + return QR_CODE_FORMATS; + } + if (com.qidian.zxing.lib_zxing.decoding.Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) { + return DATA_MATRIX_FORMATS; + } + if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) { + return ONE_D_FORMATS; + } + } + return null; + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeHandler.java b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeHandler.java new file mode 100644 index 0000000..98ddf7e --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeHandler.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.decoding; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.MultiFormatReader; +import com.google.zxing.ReaderException; +import com.google.zxing.Result; +import com.google.zxing.common.HybridBinarizer; +import com.qidian.zhongkesmart.R; +import com.qidian.zxing.lib_zxing.activity.CaptureActivity; +import com.qidian.zxing.lib_zxing.activity.CaptureFragment; +import com.qidian.zxing.lib_zxing.camera.CameraManager; +import com.qidian.zxing.lib_zxing.camera.PlanarYUVLuminanceSource; +import com.qidian.zxing.lib_zxing.decoding.DecodeThread; + +import java.util.Hashtable; + +final class DecodeHandler extends Handler { + + private static final String TAG = DecodeHandler.class.getSimpleName(); + + private final CaptureFragment fragment; + private final MultiFormatReader multiFormatReader; + + DecodeHandler(CaptureFragment fragment, Hashtable hints) { + multiFormatReader = new MultiFormatReader(); + multiFormatReader.setHints(hints); + this.fragment = fragment; + } + + @Override + public void handleMessage(Message message) { + if (message.what == R.id.decode) { + decode((byte[]) message.obj, message.arg1, message.arg2); + } else if (message.what == R.id.quit) { + Looper.myLooper().quit(); + } + } + + /** + * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, + * reuse the same reader objects from one decode to the next. + * + * @param data The YUV preview frame. + * @param width The width of the preview frame. + * @param height The height of the preview frame. + */ + private void decode(byte[] data, int width, int height) { + long start = System.currentTimeMillis(); + Result rawResult = null; + + //modify here + byte[] rotatedData = new byte[data.length]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + rotatedData[x * height + height - y - 1] = data[x + y * width]; + } + int tmp = width; // Here we are swapping, that's the difference to #11 + width = height; + height = tmp; + + PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + try { + rawResult = multiFormatReader.decodeWithState(bitmap); + } catch (ReaderException re) { + // continue + } finally { + multiFormatReader.reset(); + } + + if (rawResult != null) { + long end = System.currentTimeMillis(); + Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString()); + Message message = Message.obtain(fragment.getHandler(), R.id.decode_succeeded, rawResult); + Bundle bundle = new Bundle(); + bundle.putParcelable(com.qidian.zxing.lib_zxing.decoding.DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap()); + message.setData(bundle); + //Log.d(TAG, "Sending decode succeeded message..."); + message.sendToTarget(); + } else { + Message message = Message.obtain(fragment.getHandler(), R.id.decode_failed); + message.sendToTarget(); + } + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeThread.java b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeThread.java new file mode 100644 index 0000000..e4a2ab2 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/DecodeThread.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.decoding; + +import android.os.Handler; +import android.os.Looper; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.DecodeHintType; +import com.google.zxing.ResultPointCallback; +import com.qidian.zxing.lib_zxing.activity.CaptureFragment; +import com.qidian.zxing.lib_zxing.decoding.DecodeFormatManager; +import com.qidian.zxing.lib_zxing.decoding.DecodeHandler; + +import java.util.Hashtable; +import java.util.Vector; +import java.util.concurrent.CountDownLatch; + +/** + * This thread does all the heavy lifting of decoding the images. + * �����߳� + */ +final class DecodeThread extends Thread { + + public static final String BARCODE_BITMAP = "barcode_bitmap"; + private final CaptureFragment fragment; + private final Hashtable hints; + private Handler handler; + private final CountDownLatch handlerInitLatch; + + DecodeThread(CaptureFragment fragment, + Vector decodeFormats, + String characterSet, + ResultPointCallback resultPointCallback) { + + this.fragment = fragment; + handlerInitLatch = new CountDownLatch(1); + + hints = new Hashtable(3); + + if (decodeFormats == null || decodeFormats.isEmpty()) { + decodeFormats = new Vector(); + decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS); + decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS); + decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS); + } + + hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); + + if (characterSet != null) { + hints.put(DecodeHintType.CHARACTER_SET, characterSet); + } + + hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback); + } + + Handler getHandler() { + try { + handlerInitLatch.await(); + } catch (InterruptedException ie) { + // continue? + } + return handler; + } + + @Override + public void run() { + Looper.prepare(); + handler = new DecodeHandler(fragment, hints); + handlerInitLatch.countDown(); + Looper.loop(); + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/FinishListener.java b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/FinishListener.java new file mode 100644 index 0000000..aedb9c5 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/FinishListener.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.decoding; + +import android.app.Activity; +import android.content.DialogInterface; + +/** + * Simple listener used to exit the app in a few cases. + */ +public final class FinishListener + implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, Runnable { + + private final Activity activityToFinish; + + public FinishListener(Activity activityToFinish) { + this.activityToFinish = activityToFinish; + } + + public void onCancel(DialogInterface dialogInterface) { + run(); + } + + public void onClick(DialogInterface dialogInterface, int i) { + run(); + } + + public void run() { + activityToFinish.finish(); + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/InactivityTimer.java b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/InactivityTimer.java new file mode 100644 index 0000000..c46277e --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/InactivityTimer.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.decoding; + +import android.app.Activity; + +import com.qidian.zxing.lib_zxing.decoding.FinishListener; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +/** + * Finishes an activity after a period of inactivity. + */ +public final class InactivityTimer { + + private static final int INACTIVITY_DELAY_SECONDS = 5 * 60; + + private final ScheduledExecutorService inactivityTimer = + Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory()); + private final Activity activity; + private ScheduledFuture inactivityFuture = null; + + public InactivityTimer(Activity activity) { + this.activity = activity; + onActivity(); + } + + public void onActivity() { + cancel(); + inactivityFuture = inactivityTimer.schedule(new FinishListener(activity), + INACTIVITY_DELAY_SECONDS, + TimeUnit.SECONDS); + } + + private void cancel() { + if (inactivityFuture != null) { + inactivityFuture.cancel(true); + inactivityFuture = null; + } + } + + public void shutdown() { + cancel(); + inactivityTimer.shutdown(); + } + + private static final class DaemonThreadFactory implements ThreadFactory { + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable); + thread.setDaemon(true); + return thread; + } + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/Intents.java b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/Intents.java new file mode 100644 index 0000000..2db435b --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/decoding/Intents.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2008 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.decoding; + +/** + * This class provides the constants to use when sending an Intent to Barcode Scanner. + * These strings are effectively API and cannot be changed. + */ +public final class Intents { + private Intents() { + } + + public static final class Scan { + /** + * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return + * the results. + */ + public static final String ACTION = "com.google.zxing.client.android.SCAN"; + + /** + * By default, sending Scan.ACTION will decode all barcodes that we understand. However it + * may be useful to limit scanning to certain formats. Use Intent.putExtra(MODE, value) with + * one of the values below ({@link #PRODUCT_MODE}, {@link #ONE_D_MODE}, {@link #QR_CODE_MODE}). + * Optional. + *

+ * Setting this is effectively shorthnad for setting explicit formats with {@link #SCAN_FORMATS}. + * It is overridden by that setting. + */ + public static final String MODE = "SCAN_MODE"; + + /** + * Comma-separated list of formats to scan for. The values must match the names of + * {@link com.google.zxing.BarcodeFormat}s, such as {@link com.google.zxing.BarcodeFormat#EAN_13}. + * Example: "EAN_13,EAN_8,QR_CODE" + *

+ * This overrides {@link #MODE}. + */ + public static final String SCAN_FORMATS = "SCAN_FORMATS"; + + /** + * @see com.google.zxing.DecodeHintType#CHARACTER_SET + */ + public static final String CHARACTER_SET = "CHARACTER_SET"; + + /** + * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get + * prices, reviews, etc. for products. + */ + public static final String PRODUCT_MODE = "PRODUCT_MODE"; + + /** + * Decode only 1D barcodes (currently UPC, EAN, Code 39, and Code 128). + */ + public static final String ONE_D_MODE = "ONE_D_MODE"; + + /** + * Decode only QR codes. + */ + public static final String QR_CODE_MODE = "QR_CODE_MODE"; + + /** + * Decode only Data Matrix codes. + */ + public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE"; + + /** + * If a barcode is found, Barcodes returns RESULT_OK to onActivityResult() of the app which + * requested the scan via startSubActivity(). The barcodes contents can be retrieved with + * intent.getStringExtra(RESULT). If the user presses Back, the result code will be + * RESULT_CANCELED. + */ + public static final String RESULT = "SCAN_RESULT"; + + /** + * Call intent.getStringExtra(RESULT_FORMAT) to determine which barcode format was found. + * See Contents.Format for possible values. + */ + public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT"; + + /** + * Setting this to false will not save scanned codes in the history. + */ + public static final String SAVE_HISTORY = "SAVE_HISTORY"; + + private Scan() { + } + } + + public static final class Encode { + /** + * Send this intent to encode a piece of data as a QR code and display it full screen, so + * that another person can scan the barcode from your screen. + */ + public static final String ACTION = "com.google.zxing.client.android.ENCODE"; + + /** + * The data to encode. Use Intent.putExtra(DATA, data) where data is either a String or a + * Bundle, depending on the type and format specified. Non-QR Code formats should + * just use a String here. For QR Code, see Contents for details. + */ + public static final String DATA = "ENCODE_DATA"; + + /** + * The type of data being supplied if the format is QR Code. Use + * Intent.putExtra(TYPE, type) with one of Contents.Type. + */ + public static final String TYPE = "ENCODE_TYPE"; + + /** + * The barcode format to be displayed. If this isn't specified or is blank, + * it defaults to QR Code. Use Intent.putExtra(FORMAT, format), where + * format is one of Contents.Format. + */ + public static final String FORMAT = "ENCODE_FORMAT"; + + private Encode() { + } + } + + public static final class SearchBookContents { + /** + * Use Google Book Search to search the contents of the book provided. + */ + public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"; + + /** + * The book to search, identified by ISBN number. + */ + public static final String ISBN = "ISBN"; + + /** + * An optional field which is the text to search for. + */ + public static final String QUERY = "QUERY"; + + private SearchBookContents() { + } + } + + public static final class WifiConnect { + /** + * Internal intent used to trigger connection to a wi-fi network. + */ + public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT"; + + /** + * The network to connect to, all the configuration provided here. + */ + public static final String SSID = "SSID"; + + /** + * The network to connect to, all the configuration provided here. + */ + public static final String TYPE = "TYPE"; + + /** + * The network to connect to, all the configuration provided here. + */ + public static final String PASSWORD = "PASSWORD"; + + private WifiConnect() { + } + } + + + public static final class Share { + /** + * Give the user a choice of items to encode as a barcode, then render it as a QR Code and + * display onscreen for a friend to scan with their phone. + */ + public static final String ACTION = "com.google.zxing.client.android.SHARE"; + + private Share() { + } + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/encoding/EncodingHandler.java b/app/src/main/java/com/qidian/zxing/lib_zxing/encoding/EncodingHandler.java new file mode 100644 index 0000000..3eef0cb --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/encoding/EncodingHandler.java @@ -0,0 +1,40 @@ +package com.qidian.zxing.lib_zxing.encoding; + +import android.graphics.Bitmap; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; + +import java.util.Hashtable; + +/** + * @author Ryan Tang + */ +public final class EncodingHandler { + private static final int BLACK = 0xff000000; + + public static Bitmap createQRCode(String str, int widthAndHeight) throws WriterException { + Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + BitMatrix matrix = new MultiFormatWriter().encode(str, + BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight); + int width = matrix.getWidth(); + int height = matrix.getHeight(); + int[] pixels = new int[width * height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (matrix.get(x, y)) { + pixels[y * width + x] = BLACK; + } + } + } + Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return bitmap; + } +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/view/ViewfinderResultPointCallback.java b/app/src/main/java/com/qidian/zxing/lib_zxing/view/ViewfinderResultPointCallback.java new file mode 100644 index 0000000..8e4b655 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/view/ViewfinderResultPointCallback.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.view; + +import com.google.zxing.ResultPoint; +import com.google.zxing.ResultPointCallback; +import com.qidian.zxing.lib_zxing.view.ViewfinderView; + +public final class ViewfinderResultPointCallback implements ResultPointCallback { + + private final com.qidian.zxing.lib_zxing.view.ViewfinderView viewfinderView; + + public ViewfinderResultPointCallback(ViewfinderView viewfinderView) { + this.viewfinderView = viewfinderView; + } + + public void foundPossibleResultPoint(ResultPoint point) { + viewfinderView.addPossibleResultPoint(point); + } + +} diff --git a/app/src/main/java/com/qidian/zxing/lib_zxing/view/ViewfinderView.java b/app/src/main/java/com/qidian/zxing/lib_zxing/view/ViewfinderView.java new file mode 100644 index 0000000..a6861a4 --- /dev/null +++ b/app/src/main/java/com/qidian/zxing/lib_zxing/view/ViewfinderView.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2008 ZXing 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 + * + * http://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. + */ + +package com.qidian.zxing.lib_zxing.view; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; + +import com.google.zxing.ResultPoint; +import com.qidian.zhongkesmart.R; +import com.qidian.zxing.lib_zxing.DisplayUtil; +import com.qidian.zxing.lib_zxing.camera.CameraManager; + +import java.util.Collection; +import java.util.HashSet; + +/** + * 自定义组件实现,扫描功能 + */ +public final class ViewfinderView extends View { + + private static final long ANIMATION_DELAY = 100L; + private static final int OPAQUE = 0xFF; + + private final Paint paint; + private Bitmap resultBitmap; + private final int maskColor; + private final int resultColor; + private final int resultPointColor; + private Collection possibleResultPoints; + private Collection lastPossibleResultPoints; + + public ViewfinderView(Context context) { + this(context, null); + } + + public ViewfinderView(Context context, AttributeSet attrs) { + this(context, attrs, -1); + + } + + public ViewfinderView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + paint = new Paint(); + Resources resources = getResources(); + maskColor = resources.getColor(R.color.white); + resultColor = resources.getColor(R.color.result_view); + resultPointColor = resources.getColor(R.color.possible_result_points); + possibleResultPoints = new HashSet<>(5); + + scanLight = BitmapFactory.decodeResource(resources, + R.drawable.scan_light); + + initInnerRect(context, attrs); + } + + /** + * 初始化内部框的大小 + * + * @param context + * @param attrs + */ + private void initInnerRect(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView); + + // 扫描框距离顶部 + float innerMarginTop = ta.getDimension(R.styleable.ViewfinderView_inner_margintop, -1); + if (innerMarginTop != -1) { + CameraManager.FRAME_MARGINTOP = (int) innerMarginTop; + } + + // 扫描框的宽度 + CameraManager.FRAME_WIDTH = (int) ta.getDimension(R.styleable.ViewfinderView_inner_width, DisplayUtil.screenWidthPx / 2); + + // 扫描框的高度 + CameraManager.FRAME_HEIGHT = (int) ta.getDimension(R.styleable.ViewfinderView_inner_height, DisplayUtil.screenWidthPx / 2); + + // 扫描框边角颜色 + innercornercolor = ta.getColor(R.styleable.ViewfinderView_inner_corner_color, Color.parseColor("#45DDDD")); + // 扫描框边角长度 + innercornerlength = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_length, 65); + // 扫描框边角宽度 + innercornerwidth = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_width, 15); + + // 扫描bitmap + Drawable drawable = ta.getDrawable(R.styleable.ViewfinderView_inner_scan_bitmap); + if (drawable != null) { + } + + // 扫描控件 + scanLight = BitmapFactory.decodeResource(getResources(), ta.getResourceId(R.styleable.ViewfinderView_inner_scan_bitmap, R.drawable.scan_light)); + // 扫描速度 + SCAN_VELOCITY = ta.getInt(R.styleable.ViewfinderView_inner_scan_speed, 5); + + isCircle = ta.getBoolean(R.styleable.ViewfinderView_inner_scan_iscircle, true); + + ta.recycle(); + } + + @Override + public void onDraw(Canvas canvas) { + Rect frame = CameraManager.get().getFramingRect(); + if (frame == null) { + return; + } + int width = canvas.getWidth(); + int height = canvas.getHeight(); + + // Draw the exterior (i.e. outside the framing rect) darkened + paint.setColor(resultBitmap != null ? resultColor : maskColor); +// canvas.drawRect(0, 0, width, frame.top, paint); +// canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); +// canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); +// canvas.drawRect(0, frame.bottom + 1, width, height, paint); + canvas.drawRect(0, 0, width, frame.top, paint); + canvas.drawRect(0, frame.top, frame.left, frame.bottom, paint); + canvas.drawRect(frame.right, frame.top, width, frame.bottom, paint); + canvas.drawRect(0, frame.bottom, width, height, paint); + + if (resultBitmap != null) { + // Draw the opaque result bitmap over the scanning rectangle + paint.setAlpha(OPAQUE); + canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint); + } else { + + drawFrameBounds(canvas, frame); + + drawScanLight(canvas, frame); + + Collection currentPossible = possibleResultPoints; + Collection currentLast = lastPossibleResultPoints; + if (currentPossible.isEmpty()) { + lastPossibleResultPoints = null; + } else { + possibleResultPoints = new HashSet(5); + lastPossibleResultPoints = currentPossible; + paint.setAlpha(OPAQUE); + paint.setColor(resultPointColor); + + if (isCircle) { + for (ResultPoint point : currentPossible) { + canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint); + } + } + } + if (currentLast != null) { + paint.setAlpha(OPAQUE / 2); + paint.setColor(resultPointColor); + + if (isCircle) { + for (ResultPoint point : currentLast) { + canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint); + } + } + } + + postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom); + } + } + + // 扫描线移动的y + private int scanLineTop; + // 扫描线移动速度 + private int SCAN_VELOCITY; + // 扫描线 + private Bitmap scanLight; + // 是否展示小圆点 + private boolean isCircle; + + /** + * 绘制移动扫描线 + * + * @param canvas + * @param frame + */ + private void drawScanLight(Canvas canvas, Rect frame) { + + if (scanLineTop == 0) { + scanLineTop = frame.top; + } + + if (scanLineTop >= frame.bottom - 30) { + scanLineTop = frame.top; + } else { + scanLineTop += SCAN_VELOCITY; + } + Rect scanRect = new Rect(frame.left, scanLineTop, frame.right, + scanLineTop + 30); + canvas.drawBitmap(scanLight, null, scanRect, paint); + } + + + // 扫描框边角颜色 + private int innercornercolor; + // 扫描框边角长度 + private int innercornerlength; + // 扫描框边角宽度 + private int innercornerwidth; + + /** + * 绘制取景框边框 + * + * @param canvas + * @param frame + */ + private void drawFrameBounds(Canvas canvas, Rect frame) { + + /*paint.setColor(Color.WHITE); + paint.setStrokeWidth(2); + paint.setStyle(Paint.Style.STROKE); + + canvas.drawRect(frame, paint);*/ + + paint.setColor(innercornercolor); + paint.setStyle(Paint.Style.FILL); + + int corWidth = innercornerwidth; + int corLength = innercornerlength; + + // 左上角 + canvas.drawRect(frame.left, frame.top, frame.left + corWidth, frame.top + + corLength, paint); + canvas.drawRect(frame.left, frame.top, frame.left + + corLength, frame.top + corWidth, paint); + // 右上角 + canvas.drawRect(frame.right - corWidth, frame.top, frame.right, + frame.top + corLength, paint); + canvas.drawRect(frame.right - corLength, frame.top, + frame.right, frame.top + corWidth, paint); + // 左下角 + canvas.drawRect(frame.left, frame.bottom - corLength, + frame.left + corWidth, frame.bottom, paint); + canvas.drawRect(frame.left, frame.bottom - corWidth, frame.left + + corLength, frame.bottom, paint); + // 右下角 + canvas.drawRect(frame.right - corWidth, frame.bottom - corLength, + frame.right, frame.bottom, paint); + canvas.drawRect(frame.right - corLength, frame.bottom - corWidth, + frame.right, frame.bottom, paint); + } + + + public void drawViewfinder() { + resultBitmap = null; + invalidate(); + } + + public void addPossibleResultPoint(ResultPoint point) { + possibleResultPoints.add(point); + } + + + /** + * 根据手机的分辨率从 dp 的单位 转成为 px(像素) + */ + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + +} diff --git a/app/src/main/res/drawable-v24/background_pager.png b/app/src/main/res/drawable-v24/background_pager.png new file mode 100644 index 0000000..c61f450 Binary files /dev/null and b/app/src/main/res/drawable-v24/background_pager.png differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/anim_loading.xml b/app/src/main/res/drawable/anim_loading.xml new file mode 100644 index 0000000..5ac1eec --- /dev/null +++ b/app/src/main/res/drawable/anim_loading.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_loading_text_dialog.xml b/app/src/main/res/drawable/bg_loading_text_dialog.xml new file mode 100644 index 0000000..7d4063c --- /dev/null +++ b/app/src/main/res/drawable/bg_loading_text_dialog.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/blink.gif b/app/src/main/res/drawable/blink.gif new file mode 100644 index 0000000..60e4483 Binary files /dev/null and b/app/src/main/res/drawable/blink.gif differ diff --git a/app/src/main/res/drawable/blink2.gif b/app/src/main/res/drawable/blink2.gif new file mode 100644 index 0000000..4dbc506 Binary files /dev/null and b/app/src/main/res/drawable/blink2.gif differ diff --git a/app/src/main/res/drawable/btn_call_bg.xml b/app/src/main/res/drawable/btn_call_bg.xml new file mode 100644 index 0000000..7ede665 --- /dev/null +++ b/app/src/main/res/drawable/btn_call_bg.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_search_bg.xml b/app/src/main/res/drawable/btn_search_bg.xml new file mode 100644 index 0000000..43c1690 --- /dev/null +++ b/app/src/main/res/drawable/btn_search_bg.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/check_box_open.xml b/app/src/main/res/drawable/check_box_open.xml new file mode 100644 index 0000000..55837ba --- /dev/null +++ b/app/src/main/res/drawable/check_box_open.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/check_box_shape.xml b/app/src/main/res/drawable/check_box_shape.xml new file mode 100644 index 0000000..13df814 --- /dev/null +++ b/app/src/main/res/drawable/check_box_shape.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/dizzy.gif b/app/src/main/res/drawable/dizzy.gif new file mode 100644 index 0000000..c2786d5 Binary files /dev/null and b/app/src/main/res/drawable/dizzy.gif differ diff --git a/app/src/main/res/drawable/djanimation.xml b/app/src/main/res/drawable/djanimation.xml new file mode 100644 index 0000000..39cfd4a --- /dev/null +++ b/app/src/main/res/drawable/djanimation.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/dooranimation.xml b/app/src/main/res/drawable/dooranimation.xml new file mode 100644 index 0000000..ebd7d2c --- /dev/null +++ b/app/src/main/res/drawable/dooranimation.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ed_0_516afc_full.xml b/app/src/main/res/drawable/ed_0_516afc_full.xml new file mode 100644 index 0000000..18c4690 --- /dev/null +++ b/app/src/main/res/drawable/ed_0_516afc_full.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_0_c5cbd7_line.xml b/app/src/main/res/drawable/ed_0_c5cbd7_line.xml new file mode 100644 index 0000000..e0e9014 --- /dev/null +++ b/app/src/main/res/drawable/ed_0_c5cbd7_line.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_0_ffc5cbd7_line.xml b/app/src/main/res/drawable/ed_0_ffc5cbd7_line.xml new file mode 100644 index 0000000..a69dd80 --- /dev/null +++ b/app/src/main/res/drawable/ed_0_ffc5cbd7_line.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_10_f3f5ff_full.xml b/app/src/main/res/drawable/ed_10_f3f5ff_full.xml new file mode 100644 index 0000000..b017055 --- /dev/null +++ b/app/src/main/res/drawable/ed_10_f3f5ff_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_10_white.xml b/app/src/main/res/drawable/ed_10_white.xml new file mode 100644 index 0000000..870b7cf --- /dev/null +++ b/app/src/main/res/drawable/ed_10_white.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_1_516afc_full.xml b/app/src/main/res/drawable/ed_1_516afc_full.xml new file mode 100644 index 0000000..d1d1cf2 --- /dev/null +++ b/app/src/main/res/drawable/ed_1_516afc_full.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_1_ffc5cbd7_line.xml b/app/src/main/res/drawable/ed_1_ffc5cbd7_line.xml new file mode 100644 index 0000000..c2d6b57 --- /dev/null +++ b/app/src/main/res/drawable/ed_1_ffc5cbd7_line.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_20_e1132c_full.xml b/app/src/main/res/drawable/ed_20_e1132c_full.xml new file mode 100644 index 0000000..9502dd3 --- /dev/null +++ b/app/src/main/res/drawable/ed_20_e1132c_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_20_ee4141_line.xml b/app/src/main/res/drawable/ed_20_ee4141_line.xml new file mode 100644 index 0000000..2234206 --- /dev/null +++ b/app/src/main/res/drawable/ed_20_ee4141_line.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_20_f3f5ff_full.xml b/app/src/main/res/drawable/ed_20_f3f5ff_full.xml new file mode 100644 index 0000000..416b7cc --- /dev/null +++ b/app/src/main/res/drawable/ed_20_f3f5ff_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_20_ff516afc_full.xml b/app/src/main/res/drawable/ed_20_ff516afc_full.xml new file mode 100644 index 0000000..518a1b6 --- /dev/null +++ b/app/src/main/res/drawable/ed_20_ff516afc_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_20_fff0f6ff_line.xml b/app/src/main/res/drawable/ed_20_fff0f6ff_line.xml new file mode 100644 index 0000000..76e108d --- /dev/null +++ b/app/src/main/res/drawable/ed_20_fff0f6ff_line.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_30_white.xml b/app/src/main/res/drawable/ed_30_white.xml new file mode 100644 index 0000000..115f839 --- /dev/null +++ b/app/src/main/res/drawable/ed_30_white.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_3_f0f6fd_top.xml b/app/src/main/res/drawable/ed_3_f0f6fd_top.xml new file mode 100644 index 0000000..db446e3 --- /dev/null +++ b/app/src/main/res/drawable/ed_3_f0f6fd_top.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_4_f3f3fa_full.xml b/app/src/main/res/drawable/ed_4_f3f3fa_full.xml new file mode 100644 index 0000000..0a04302 --- /dev/null +++ b/app/src/main/res/drawable/ed_4_f3f3fa_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_4_fffff_full.xml b/app/src/main/res/drawable/ed_4_fffff_full.xml new file mode 100644 index 0000000..aae6b68 --- /dev/null +++ b/app/src/main/res/drawable/ed_4_fffff_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_50_white.xml b/app/src/main/res/drawable/ed_50_white.xml new file mode 100644 index 0000000..bd07847 --- /dev/null +++ b/app/src/main/res/drawable/ed_50_white.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_6_55000000_full.xml b/app/src/main/res/drawable/ed_6_55000000_full.xml new file mode 100644 index 0000000..179aeb0 --- /dev/null +++ b/app/src/main/res/drawable/ed_6_55000000_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_6_c5cbd7_line.xml b/app/src/main/res/drawable/ed_6_c5cbd7_line.xml new file mode 100644 index 0000000..0f6d927 --- /dev/null +++ b/app/src/main/res/drawable/ed_6_c5cbd7_line.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_6_ee4141_line.xml b/app/src/main/res/drawable/ed_6_ee4141_line.xml new file mode 100644 index 0000000..879e726 --- /dev/null +++ b/app/src/main/res/drawable/ed_6_ee4141_line.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_6_fbfbff_full.xml b/app/src/main/res/drawable/ed_6_fbfbff_full.xml new file mode 100644 index 0000000..976ba78 --- /dev/null +++ b/app/src/main/res/drawable/ed_6_fbfbff_full.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_white_full.xml b/app/src/main/res/drawable/ed_white_full.xml new file mode 100644 index 0000000..d9ae0f4 --- /dev/null +++ b/app/src/main/res/drawable/ed_white_full.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_yuan0fba61.xml b/app/src/main/res/drawable/ed_yuan0fba61.xml new file mode 100644 index 0000000..3b1a01c --- /dev/null +++ b/app/src/main/res/drawable/ed_yuan0fba61.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ed_yuangray.xml b/app/src/main/res/drawable/ed_yuangray.xml new file mode 100644 index 0000000..29e6b75 --- /dev/null +++ b/app/src/main/res/drawable/ed_yuangray.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/et_cursor.xml b/app/src/main/res/drawable/et_cursor.xml new file mode 100644 index 0000000..19dfd04 --- /dev/null +++ b/app/src/main/res/drawable/et_cursor.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/et_login_code.xml b/app/src/main/res/drawable/et_login_code.xml new file mode 100644 index 0000000..ea9aa2d --- /dev/null +++ b/app/src/main/res/drawable/et_login_code.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/et_search_bg.xml b/app/src/main/res/drawable/et_search_bg.xml new file mode 100644 index 0000000..f23bb33 --- /dev/null +++ b/app/src/main/res/drawable/et_search_bg.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient_line.xml b/app/src/main/res/drawable/gradient_line.xml new file mode 100644 index 0000000..ae0b647 --- /dev/null +++ b/app/src/main/res/drawable/gradient_line.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/iceanimation.xml b/app/src/main/res/drawable/iceanimation.xml new file mode 100644 index 0000000..ff997fd --- /dev/null +++ b/app/src/main/res/drawable/iceanimation.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/lanchanimation.xml b/app/src/main/res/drawable/lanchanimation.xml new file mode 100644 index 0000000..730b771 --- /dev/null +++ b/app/src/main/res/drawable/lanchanimation.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/look_down.gif b/app/src/main/res/drawable/look_down.gif new file mode 100644 index 0000000..99db954 Binary files /dev/null and b/app/src/main/res/drawable/look_down.gif differ diff --git a/app/src/main/res/drawable/look_down_left.gif b/app/src/main/res/drawable/look_down_left.gif new file mode 100644 index 0000000..f21ad75 Binary files /dev/null and b/app/src/main/res/drawable/look_down_left.gif differ diff --git a/app/src/main/res/drawable/look_down_right.gif b/app/src/main/res/drawable/look_down_right.gif new file mode 100644 index 0000000..ab8aedc Binary files /dev/null and b/app/src/main/res/drawable/look_down_right.gif differ diff --git a/app/src/main/res/drawable/look_left.gif b/app/src/main/res/drawable/look_left.gif new file mode 100644 index 0000000..a31921b Binary files /dev/null and b/app/src/main/res/drawable/look_left.gif differ diff --git a/app/src/main/res/drawable/look_left2.gif b/app/src/main/res/drawable/look_left2.gif new file mode 100644 index 0000000..938ff5a Binary files /dev/null and b/app/src/main/res/drawable/look_left2.gif differ diff --git a/app/src/main/res/drawable/look_right.gif b/app/src/main/res/drawable/look_right.gif new file mode 100644 index 0000000..574068c Binary files /dev/null and b/app/src/main/res/drawable/look_right.gif differ diff --git a/app/src/main/res/drawable/look_right2.gif b/app/src/main/res/drawable/look_right2.gif new file mode 100644 index 0000000..371ffd6 Binary files /dev/null and b/app/src/main/res/drawable/look_right2.gif differ diff --git a/app/src/main/res/drawable/look_up.gif b/app/src/main/res/drawable/look_up.gif new file mode 100644 index 0000000..ff23916 Binary files /dev/null and b/app/src/main/res/drawable/look_up.gif differ diff --git a/app/src/main/res/drawable/look_up_left.gif b/app/src/main/res/drawable/look_up_left.gif new file mode 100644 index 0000000..a9a2329 Binary files /dev/null and b/app/src/main/res/drawable/look_up_left.gif differ diff --git a/app/src/main/res/drawable/look_up_right.gif b/app/src/main/res/drawable/look_up_right.gif new file mode 100644 index 0000000..f21ad75 Binary files /dev/null and b/app/src/main/res/drawable/look_up_right.gif differ diff --git a/app/src/main/res/drawable/poweranimation.xml b/app/src/main/res/drawable/poweranimation.xml new file mode 100644 index 0000000..da1454d --- /dev/null +++ b/app/src/main/res/drawable/poweranimation.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radio_bg_selector.xml b/app/src/main/res/drawable/radio_bg_selector.xml new file mode 100644 index 0000000..0ab2df6 --- /dev/null +++ b/app/src/main/res/drawable/radio_bg_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radio_button_bg_selector.xml b/app/src/main/res/drawable/radio_button_bg_selector.xml new file mode 100644 index 0000000..70a3d57 --- /dev/null +++ b/app/src/main/res/drawable/radio_button_bg_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radio_button_color_selector.xml b/app/src/main/res/drawable/radio_button_color_selector.xml new file mode 100644 index 0000000..57b1bc8 --- /dev/null +++ b/app/src/main/res/drawable/radio_button_color_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radio_button_color_selector1.xml b/app/src/main/res/drawable/radio_button_color_selector1.xml new file mode 100644 index 0000000..5978175 --- /dev/null +++ b/app/src/main/res/drawable/radio_button_color_selector1.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radio_button_icon_selector.xml b/app/src/main/res/drawable/radio_button_icon_selector.xml new file mode 100644 index 0000000..34d919c --- /dev/null +++ b/app/src/main/res/drawable/radio_button_icon_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radio_select_bg.xml b/app/src/main/res/drawable/radio_select_bg.xml new file mode 100644 index 0000000..c7401a2 --- /dev/null +++ b/app/src/main/res/drawable/radio_select_bg.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/drawable/radio_unselect_bg.xml b/app/src/main/res/drawable/radio_unselect_bg.xml new file mode 100644 index 0000000..f56b8e7 --- /dev/null +++ b/app/src/main/res/drawable/radio_unselect_bg.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/drawable/rqzanimation.xml b/app/src/main/res/drawable/rqzanimation.xml new file mode 100644 index 0000000..6a59c68 --- /dev/null +++ b/app/src/main/res/drawable/rqzanimation.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/scan_light.png b/app/src/main/res/drawable/scan_light.png new file mode 100644 index 0000000..0ed22ab Binary files /dev/null and b/app/src/main/res/drawable/scan_light.png differ diff --git a/app/src/main/res/drawable/sganimation.xml b/app/src/main/res/drawable/sganimation.xml new file mode 100644 index 0000000..39cfd4a --- /dev/null +++ b/app/src/main/res/drawable/sganimation.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_add_device_bluetooth_connect_bg.xml b/app/src/main/res/drawable/shape_add_device_bluetooth_connect_bg.xml new file mode 100644 index 0000000..c2067ce --- /dev/null +++ b/app/src/main/res/drawable/shape_add_device_bluetooth_connect_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_add_device_cancel_bg.xml b/app/src/main/res/drawable/shape_add_device_cancel_bg.xml new file mode 100644 index 0000000..620522a --- /dev/null +++ b/app/src/main/res/drawable/shape_add_device_cancel_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_add_device_location_bg.xml b/app/src/main/res/drawable/shape_add_device_location_bg.xml new file mode 100644 index 0000000..5a6fd4a --- /dev/null +++ b/app/src/main/res/drawable/shape_add_device_location_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_alarm_progress.xml b/app/src/main/res/drawable/shape_alarm_progress.xml new file mode 100644 index 0000000..3e14f52 --- /dev/null +++ b/app/src/main/res/drawable/shape_alarm_progress.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_alarm_progress_bg.xml b/app/src/main/res/drawable/shape_alarm_progress_bg.xml new file mode 100644 index 0000000..3dff57c --- /dev/null +++ b/app/src/main/res/drawable/shape_alarm_progress_bg.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_alarm_progress_thumb.xml b/app/src/main/res/drawable/shape_alarm_progress_thumb.xml new file mode 100644 index 0000000..0aed90e --- /dev/null +++ b/app/src/main/res/drawable/shape_alarm_progress_thumb.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_exit_setting_mode_btn_bg.xml b/app/src/main/res/drawable/shape_exit_setting_mode_btn_bg.xml new file mode 100644 index 0000000..6e96b1f --- /dev/null +++ b/app/src/main/res/drawable/shape_exit_setting_mode_btn_bg.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_home_left_top_gray_bg.xml b/app/src/main/res/drawable/shape_home_left_top_gray_bg.xml new file mode 100644 index 0000000..8ee8765 --- /dev/null +++ b/app/src/main/res/drawable/shape_home_left_top_gray_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_blue_bg.xml b/app/src/main/res/drawable/shape_model_init_blue_bg.xml new file mode 100644 index 0000000..2bc1fcc --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_blue_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_blue_border_bg.xml b/app/src/main/res/drawable/shape_model_init_blue_border_bg.xml new file mode 100644 index 0000000..eb6be41 --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_blue_border_bg.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_circel_black.xml b/app/src/main/res/drawable/shape_model_init_circel_black.xml new file mode 100644 index 0000000..111e035 --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_circel_black.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_circel_gray.xml b/app/src/main/res/drawable/shape_model_init_circel_gray.xml new file mode 100644 index 0000000..821d074 --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_circel_gray.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_circel_green.xml b/app/src/main/res/drawable/shape_model_init_circel_green.xml new file mode 100644 index 0000000..c32f14e --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_circel_green.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_gray_bg.xml b/app/src/main/res/drawable/shape_model_init_gray_bg.xml new file mode 100644 index 0000000..de3cabd --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_gray_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_green_bg.xml b/app/src/main/res/drawable/shape_model_init_green_bg.xml new file mode 100644 index 0000000..175e8cc --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_green_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_square_blue.xml b/app/src/main/res/drawable/shape_model_init_square_blue.xml new file mode 100644 index 0000000..999dfde --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_square_blue.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_square_green.xml b/app/src/main/res/drawable/shape_model_init_square_green.xml new file mode 100644 index 0000000..9b09037 --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_square_green.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_verify_btn_blue_bg.xml b/app/src/main/res/drawable/shape_model_init_verify_btn_blue_bg.xml new file mode 100644 index 0000000..a37bddf --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_verify_btn_blue_bg.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_model_init_verify_btn_gray_bg.xml b/app/src/main/res/drawable/shape_model_init_verify_btn_gray_bg.xml new file mode 100644 index 0000000..0193e93 --- /dev/null +++ b/app/src/main/res/drawable/shape_model_init_verify_btn_gray_bg.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_my_device_item_bg.xml b/app/src/main/res/drawable/shape_my_device_item_bg.xml new file mode 100644 index 0000000..bb048f9 --- /dev/null +++ b/app/src/main/res/drawable/shape_my_device_item_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_select_frequency_item_blue_bg.xml b/app/src/main/res/drawable/shape_select_frequency_item_blue_bg.xml new file mode 100644 index 0000000..d32340f --- /dev/null +++ b/app/src/main/res/drawable/shape_select_frequency_item_blue_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_select_frequency_item_gray_bg.xml b/app/src/main/res/drawable/shape_select_frequency_item_gray_bg.xml new file mode 100644 index 0000000..a740811 --- /dev/null +++ b/app/src/main/res/drawable/shape_select_frequency_item_gray_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_set_lock_pwd_bg.xml b/app/src/main/res/drawable/shape_set_lock_pwd_bg.xml new file mode 100644 index 0000000..dd0a750 --- /dev/null +++ b/app/src/main/res/drawable/shape_set_lock_pwd_bg.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_setting_radiogroup_select_bg.xml b/app/src/main/res/drawable/shape_setting_radiogroup_select_bg.xml new file mode 100644 index 0000000..c857711 --- /dev/null +++ b/app/src/main/res/drawable/shape_setting_radiogroup_select_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_setting_radiogroup_unselect_bg.xml b/app/src/main/res/drawable/shape_setting_radiogroup_unselect_bg.xml new file mode 100644 index 0000000..ea48680 --- /dev/null +++ b/app/src/main/res/drawable/shape_setting_radiogroup_unselect_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_threshold_textview_border.xml b/app/src/main/res/drawable/shape_threshold_textview_border.xml new file mode 100644 index 0000000..8bc57d1 --- /dev/null +++ b/app/src/main/res/drawable/shape_threshold_textview_border.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_wake_up_device_btn_bg.xml b/app/src/main/res/drawable/shape_wake_up_device_btn_bg.xml new file mode 100644 index 0000000..fd3e297 --- /dev/null +++ b/app/src/main/res/drawable/shape_wake_up_device_btn_bg.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_wifi_item_bg.xml b/app/src/main/res/drawable/shape_wifi_item_bg.xml new file mode 100644 index 0000000..a740811 --- /dev/null +++ b/app/src/main/res/drawable/shape_wifi_item_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_wifi_psw_join_bg.xml b/app/src/main/res/drawable/shape_wifi_psw_join_bg.xml new file mode 100644 index 0000000..2555ea3 --- /dev/null +++ b/app/src/main/res/drawable/shape_wifi_psw_join_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_wifi_psw_join_gray_bg.xml b/app/src/main/res/drawable/shape_wifi_psw_join_gray_bg.xml new file mode 100644 index 0000000..34c49f2 --- /dev/null +++ b/app/src/main/res/drawable/shape_wifi_psw_join_gray_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_wifi_psw_join_red_bg.xml b/app/src/main/res/drawable/shape_wifi_psw_join_red_bg.xml new file mode 100644 index 0000000..71ee621 --- /dev/null +++ b/app/src/main/res/drawable/shape_wifi_psw_join_red_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/smile.gif b/app/src/main/res/drawable/smile.gif new file mode 100644 index 0000000..c62e185 Binary files /dev/null and b/app/src/main/res/drawable/smile.gif differ diff --git a/app/src/main/res/drawable/standby_default.jpg b/app/src/main/res/drawable/standby_default.jpg new file mode 100644 index 0000000..3a93847 Binary files /dev/null and b/app/src/main/res/drawable/standby_default.jpg differ diff --git a/app/src/main/res/drawable/standbyanimation01.xml b/app/src/main/res/drawable/standbyanimation01.xml new file mode 100644 index 0000000..6c2d855 --- /dev/null +++ b/app/src/main/res/drawable/standbyanimation01.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/standbyanimation02.xml b/app/src/main/res/drawable/standbyanimation02.xml new file mode 100644 index 0000000..f7d0e8d --- /dev/null +++ b/app/src/main/res/drawable/standbyanimation02.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/toastbackground.xml b/app/src/main/res/drawable/toastbackground.xml new file mode 100644 index 0000000..61966c5 --- /dev/null +++ b/app/src/main/res/drawable/toastbackground.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_base.xml b/app/src/main/res/layout/activity_base.xml new file mode 100644 index 0000000..b5dbc45 --- /dev/null +++ b/app/src/main/res/layout/activity_base.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_bluetooth.xml b/app/src/main/res/layout/activity_bluetooth.xml new file mode 100644 index 0000000..136f0c2 --- /dev/null +++ b/app/src/main/res/layout/activity_bluetooth.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_capture.xml b/app/src/main/res/layout/activity_capture.xml new file mode 100644 index 0000000..c29b8a2 --- /dev/null +++ b/app/src/main/res/layout/activity_capture.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_connect_mode.xml b/app/src/main/res/layout/activity_connect_mode.xml new file mode 100644 index 0000000..9f10f5c --- /dev/null +++ b/app/src/main/res/layout/activity_connect_mode.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml new file mode 100644 index 0000000..aefd576 --- /dev/null +++ b/app/src/main/res/layout/activity_home.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_infi.xml b/app/src/main/res/layout/activity_infi.xml new file mode 100644 index 0000000..dab9833 --- /dev/null +++ b/app/src/main/res/layout/activity_infi.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_launch.xml b/app/src/main/res/layout/activity_launch.xml new file mode 100644 index 0000000..8333175 --- /dev/null +++ b/app/src/main/res/layout/activity_launch.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..640b1d6 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_model_init.xml b/app/src/main/res/layout/activity_model_init.xml new file mode 100644 index 0000000..ff22ce7 --- /dev/null +++ b/app/src/main/res/layout/activity_model_init.xml @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_my_equipment.xml b/app/src/main/res/layout/activity_my_equipment.xml new file mode 100644 index 0000000..91fe8bb --- /dev/null +++ b/app/src/main/res/layout/activity_my_equipment.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_my_equipment_details.xml b/app/src/main/res/layout/activity_my_equipment_details.xml new file mode 100644 index 0000000..36f12ba --- /dev/null +++ b/app/src/main/res/layout/activity_my_equipment_details.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_select_call_user.xml b/app/src/main/res/layout/activity_select_call_user.xml new file mode 100644 index 0000000..320c120 --- /dev/null +++ b/app/src/main/res/layout/activity_select_call_user.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + +