commit 034569ede1191846145db40fe6f4d6668ddf1d6a Author: 鑫Intel Date: Sat May 14 16:17:29 2022 +0800 ### 2022-05-14 V1.0版本 - *.初始版本 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..71bc179 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +### 2022-05-14 V1.0版本 + +- *.初始版本 \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.gitignore b/Client/android/MyEasyWOL/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/Client/android/MyEasyWOL/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/Client/android/MyEasyWOL/.idea/.gitignore b/Client/android/MyEasyWOL/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/Client/android/MyEasyWOL/.idea/compiler.xml b/Client/android/MyEasyWOL/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/copyright/profiles_settings.xml b/Client/android/MyEasyWOL/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..066b255 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/copyright/xuexiang.xml b/Client/android/MyEasyWOL/.idea/copyright/xuexiang.xml new file mode 100644 index 0000000..d785b8e --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/copyright/xuexiang.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/gradle.xml b/Client/android/MyEasyWOL/.idea/gradle.xml new file mode 100644 index 0000000..ac6b0ae --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/gradle.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/jarRepositories.xml b/Client/android/MyEasyWOL/.idea/jarRepositories.xml new file mode 100644 index 0000000..aa48081 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/jarRepositories.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/markdown-navigator-enh.xml b/Client/android/MyEasyWOL/.idea/markdown-navigator-enh.xml new file mode 100644 index 0000000..a8fcc84 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/markdown-navigator-enh.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/markdown-navigator.xml b/Client/android/MyEasyWOL/.idea/markdown-navigator.xml new file mode 100644 index 0000000..a2fc086 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/markdown-navigator.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/misc.xml b/Client/android/MyEasyWOL/.idea/misc.xml new file mode 100644 index 0000000..59f6372 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/runConfigurations.xml b/Client/android/MyEasyWOL/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/.idea/vcs.xml b/Client/android/MyEasyWOL/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/Client/android/MyEasyWOL/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/LICENSE b/Client/android/MyEasyWOL/LICENSE new file mode 100644 index 0000000..44cc482 --- /dev/null +++ b/Client/android/MyEasyWOL/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2018 xuexiangjys + + 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. \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/.gitignore b/Client/android/MyEasyWOL/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/Client/android/MyEasyWOL/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/Client/android/MyEasyWOL/app/build.gradle b/Client/android/MyEasyWOL/app/build.gradle new file mode 100644 index 0000000..1d5dbce --- /dev/null +++ b/Client/android/MyEasyWOL/app/build.gradle @@ -0,0 +1,140 @@ +apply plugin: 'com.android.application' +apply plugin: 'img-optimizer' +apply plugin: 'org.greenrobot.greendao' +//打包时,记得设置true启用 +if (isNeedPackage.toBoolean() && isUseBooster.toBoolean()) { + apply plugin: 'com.didiglobal.booster' +} + +android { + compileSdkVersion build_versions.target_sdk + buildToolsVersion build_versions.build_tools + + defaultConfig { + applicationId "com.itrycn.myeasywol" + minSdkVersion 17 + targetSdkVersion build_versions.target_sdk + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + multiDexEnabled true + vectorDrawables.useSupportLibrary = true + + javaCompileOptions { + annotationProcessorOptions { + arguments = [ moduleName : project.getName() ] + } + } + } + + signingConfigs { + if (isNeedPackage.toBoolean()) { + release { + storeFile file(app_release.storeFile) + storePassword app_release.storePassword + keyAlias app_release.keyAlias + keyPassword app_release.keyPassword + } + } + + debug { + storeFile file("./debug.jks") + storePassword "123456" + keyAlias "debug" + keyPassword "123456" + } + } + + buildTypes { + release { + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + if (isNeedPackage.toBoolean()) { + signingConfig signingConfigs.release + + Properties properties = new Properties() + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + def appID = properties.getProperty("APP_ID_UMENG") + if (appID != null) { + buildConfigField "String", "APP_ID_UMENG", appID + } else { + buildConfigField "String", "APP_ID_UMENG", '""' + } + } else { + signingConfig signingConfigs.debug + buildConfigField "String", "APP_ID_UMENG", '""' + } + } + + debug { + debuggable true + minifyEnabled false + + signingConfig signingConfigs.debug + buildConfigField "String", "APP_ID_UMENG", '""' + } + } + + lintOptions { + abortOnError false + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + //greendao设置相关参数 + greendao { + schemaVersion 2 + daoPackage 'com.itrycn.myeasywol.db.dao' + targetGenDir 'src/main/java' + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation deps.junit + androidTestImplementation deps.runner + androidTestImplementation deps.espresso.core + implementation 'org.greenrobot:greendao:3.3.0' + //列表侧滑菜单 + implementation 'com.yanzhenjie.recyclerview:support:1.3.2' + /** + * greendao 加密 + */ + implementation 'net.zetetic:android-database-sqlcipher:3.5.7' + //分包 + implementation deps.androidx.multidex + + implementation 'com.alibaba.android:vlayout:1.2.36' + //下拉刷新 + implementation 'com.github.xuexiangjys.SmartRefreshLayout:refresh-header:1.1.5' + implementation 'com.github.xuexiangjys.SmartRefreshLayout:refresh-layout:1.1.5' + //WebView + implementation 'com.github.xuexiangjys.AgentWeb:agentweb-core:1.0.0' + implementation 'com.github.xuexiangjys.AgentWeb:agentweb-download:1.0.0'//选填 + //腾讯的键值对存储mmkv + implementation 'com.tencent:mmkv:1.0.22' + //屏幕适配AutoSize + implementation 'me.jessyan:autosize:1.1.2' + + //预加载占位控件 + implementation 'me.samlss:broccoli:1.0.0' + + implementation 'com.zzhoujay.richtext:richtext:3.0.8' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + + //ANR异常捕获 + implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0' + + //美团多渠道打包 + implementation 'com.meituan.android.walle:library:1.1.6' +} +//自动添加X-Library依赖 +apply from: 'x-library.gradle' +//walle多渠道打包 +apply from: 'multiple-channel.gradle' + + diff --git a/Client/android/MyEasyWOL/app/channel b/Client/android/MyEasyWOL/app/channel new file mode 100644 index 0000000..10c8afd --- /dev/null +++ b/Client/android/MyEasyWOL/app/channel @@ -0,0 +1,25 @@ +# 美团 +meituan +# 三星 +samsungapps +# 小米 +xiaomi +# 91助手 +91com +# 魅族 +meizu +# 豌豆荚 +wandou +# Google Play +googleplay +# 百度 +baidu +# 360 +360cn +# 应用宝 +myapp +# 华为 +huawei +# 蒲公英 +pgyer +github \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/debug.jks b/Client/android/MyEasyWOL/app/debug.jks new file mode 100644 index 0000000..d49fb2b Binary files /dev/null and b/Client/android/MyEasyWOL/app/debug.jks differ diff --git a/Client/android/MyEasyWOL/app/multiple-channel.gradle b/Client/android/MyEasyWOL/app/multiple-channel.gradle new file mode 100644 index 0000000..5dc6dbf --- /dev/null +++ b/Client/android/MyEasyWOL/app/multiple-channel.gradle @@ -0,0 +1,10 @@ +apply plugin: 'walle' + +walle { + // 指定渠道包的输出路径 + apkOutputFolder = new File("${project.buildDir}/outputs/channels") + // 定制渠道包的APK的文件名称 + apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk' + // 渠道配置文件 + channelFile = new File("${project.getProjectDir()}/channel") +} \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/proguard-rules.pro b/Client/android/MyEasyWOL/app/proguard-rules.pro new file mode 100644 index 0000000..f1ba94b --- /dev/null +++ b/Client/android/MyEasyWOL/app/proguard-rules.pro @@ -0,0 +1,268 @@ +#=========================================基础不变的混淆配置=========================================## +#指定代码的压缩级别 +-optimizationpasses 5 +#包名不混合大小写 +-dontusemixedcaseclassnames +#不去忽略非公共的库类 +-dontskipnonpubliclibraryclasses +# 指定不去忽略非公共的库的类的成员 +-dontskipnonpubliclibraryclassmembers +#优化 不优化输入的类文件 +-dontoptimize +#预校验 +-dontpreverify +#混淆时是否记录日志 +-verbose +# 混淆时所采用的算法 +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* +#保护注解 +-keepattributes *Annotation* +#忽略警告 +-ignorewarnings + +##记录生成的日志数据,gradle build时在本项目根目录输出## +#apk 包内所有 class 的内部结构 +-dump class_files.txt +#未混淆的类和成员 +-printseeds seeds.txt +#列出从 apk 中删除的代码 +-printusage unused.txt +#混淆前后的映射 +-printmapping mapping.txt +# 并保留源文件名为"Proguard"字符串,而非原始的类名 并保留行号 +-keepattributes SourceFile,LineNumberTable +########记录生成的日志数据,gradle build时 在本项目根目录输出-end##### + +#需要保留的东西 +# 保持哪些类不被混淆 +-keep public class * extends android.app.Fragment +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class * extends android.support.v4.** +-keep public class com.android.vending.licensing.ILicensingService + +#如果有引用v4包可以添加下面这行 +-keep public class * extends android.support.v4.app.Fragment + +##########JS接口类不混淆,否则执行不了 +-dontwarn com.android.JsInterface.** +-keep class com.android.JsInterface.** {*; } + +#极光推送和百度lbs android sdk一起使用proguard 混淆的问题#http的类被混淆后,导致apk定位失败,保持apache 的http类不被混淆就好了 +-dontwarn org.apache.** +-keep class org.apache.**{ *; } + +-keep public class * extends android.view.View { + public (android.content.Context); + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); + public void set*(...); + } + +#保持 native 方法不被混淆 +-keepclasseswithmembernames class * { + native ; +} + +#保持自定义控件类不被混淆 +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet); +} + +#保持自定义控件类不被混淆 +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +#保持 Parcelable 不被混淆 +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} + +#保持 Serializable 不被混淆 +-keepnames class * implements java.io.Serializable + +#保持 Serializable 不被混淆并且enum 类也不被混淆 +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + !static !transient ; + !private ; + !private ; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +#保持枚举 enum 类不被混淆 如果混淆报错,建议直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可 +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keepclassmembers class * { + public void *ButtonClicked(android.view.View); +} + +#不混淆资源类 +-keep class **.R$* {*;} + +#===================================混淆保护自己项目的部分代码以及引用的第三方jar包library=============================####### +#如果引用了v4或者v7包 +-dontwarn android.support.** + + +# AndroidX 防止混淆 +-dontwarn com.google.android.material.** +-dontnote com.google.android.material.** +-dontwarn androidx.** +-keep class com.google.android.material.** {*;} +-keep class androidx.** {*;} +-keep public class * extends androidx.** +-keep interface androidx.** {*;} +-keepclassmembers class * { + @androidx.annotation.Keep *; +} + +# zxing +-dontwarn com.google.zxing.** +-keep class com.google.zxing.**{*;} + +#SignalR推送 +-keep class microsoft.aspnet.signalr.** { *; } + +# 极光推送混淆 +-dontoptimize +-dontpreverify +-dontwarn cn.jpush.** +-keep class cn.jpush.** { *; } +-dontwarn cn.jiguang.** +-keep class cn.jiguang.** { *; } + +# 数据库框架OrmLite +-keepattributes *DatabaseField* +-keepattributes *DatabaseTable* +-keepattributes *SerializedName* +-keep class com.j256.** +-keepclassmembers class com.j256.** { *; } +-keep enum com.j256.** +-keepclassmembers enum com.j256.** { *; } +-keep interface com.j256.** +-keepclassmembers interface com.j256.** { *; } + +#XHttp2 +-keep class com.xuexiang.xhttp2.model.** { *; } +-keep class com.xuexiang.xhttp2.cache.model.** { *; } +-keep class com.xuexiang.xhttp2.cache.stategy.**{*;} +-keep class com.xuexiang.xhttp2.annotation.** { *; } + +#okhttp +-dontwarn com.squareup.okhttp3.** +-keep class com.squareup.okhttp3.** { *;} +-dontwarn okio.** +-dontwarn javax.annotation.Nullable +-dontwarn javax.annotation.ParametersAreNonnullByDefault +-dontwarn javax.annotation.** + +#如果用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错 +-keepattributes Signature +-keep class com.google.gson.stream.** { *; } +-keepattributes EnclosingMethod +-keep class org.xz_sale.entity.**{*;} +-keep class com.google.gson.** {*;} +-keep class com.google.**{*;} +-keep class sun.misc.Unsafe { *; } +-keep class com.google.gson.stream.** { *; } +-keep class com.google.gson.examples.android.model.** { *; } + +# Glide +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$** { + **[] $VALUES; + public *; +} + +# Retrofit +-dontwarn retrofit2.** +-keep class retrofit2.** { *; } +-keepattributes Exceptions + +# RxJava RxAndroid +-dontwarn sun.misc.** +-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { + long producerIndex; + long consumerIndex; +} +-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { + rx.internal.util.atomic.LinkedQueueNode producerNode; +} +-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { + rx.internal.util.atomic.LinkedQueueNode consumerNode; +} + +-dontwarn okio.** +-dontwarn javax.annotation.Nullable +-dontwarn javax.annotation.ParametersAreNonnullByDefault +-dontwarn javax.annotation.** + +# fastjson +-dontwarn com.alibaba.fastjson.** +-keep class com.alibaba.fastjson.** { *; } +-keepattributes Signature + +# xpage +-keep class com.xuexiang.xpage.annotation.** { *; } + +# xaop +-keep @com.xuexiang.xaop.annotation.* class * {*;} +-keep @org.aspectj.lang.annotation.* class * {*;} +-keep class * { + @com.xuexiang.xaop.annotation.* ; + @org.aspectj.lang.annotation.* ; +} +-keepclassmembers class * { + @com.xuexiang.xaop.annotation.* ; + @org.aspectj.lang.annotation.* ; +} + +# xrouter +-keep public class com.xuexiang.xrouter.routes.**{*;} +-keep class * implements com.xuexiang.xrouter.facade.template.ISyringe{*;} +# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口 +-keep interface * implements com.xuexiang.xrouter.facade.template.IProvider +# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现 +-keep class * implements com.xuexiang.xrouter.facade.template.IProvider + +# xupdate +-keep class com.xuexiang.xupdate.entity.** { *; } + +# xvideo +-keep class com.xuexiang.xvideo.jniinterface.** { *; } + +# xipc +-keep @com.xuexiang.xipc.annotation.* class * {*;} +-keep class * { + @com.xuexiang.xipc.annotation.* ; +} +-keepclassmembers class * { + @com.xuexiang.xipc.annotation.* ; +} + +# umeng统计 +-keep class com.umeng.** {*;} +-keepclassmembers class * { + public (org.json.JSONObject); +} +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class com.xuexiang.xui.widget.edittext.materialedittext.** { *; } diff --git a/Client/android/MyEasyWOL/app/src/androidTest/java/com/itrycn/myeasywol/ExampleInstrumentedTest.java b/Client/android/MyEasyWOL/app/src/androidTest/java/com/itrycn/myeasywol/ExampleInstrumentedTest.java new file mode 100644 index 0000000..9f5df55 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/androidTest/java/com/itrycn/myeasywol/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.itrycn.myeasywol; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.itrycn.myeasywol", appContext.getPackageName()); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/AndroidManifest.xml b/Client/android/MyEasyWOL/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b7848eb --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/AndroidManifest.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/assets/tips.json b/Client/android/MyEasyWOL/app/src/main/assets/tips.json new file mode 100644 index 0000000..969a4c4 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/assets/tips.json @@ -0,0 +1,21 @@ +{ + "Code": 0, + "Data": [ + { + "title": "微信公众号", + "content": "获取更多资讯内容,欢迎微信搜索公众号:「我的Android开源之旅」" + }, + { + "title": "关于作者", + "content": "点击关注作者,了解最新动态!
Github
\n知乎
\n掘金
简书
\n思否
\n哔哩哔哩
\n今日头条" + }, + { + "title": "赞助作者", + "content": "你的打赏是我维护的动力,点击此处支持我吧!" + }, + { + "title": "QQ交流群", + "content": "XUI开源交流1号群
XUI开源交流2号群
AndroidGitHub开源交流群
XUpdate官方交流群" + } + ] +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/MyApp.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/MyApp.java new file mode 100644 index 0000000..6d2a0eb --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/MyApp.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol; + +import android.app.Application; +import android.content.Context; + +import androidx.multidex.MultiDex; + +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.utils.sdkinit.ANRWatchDogInit; +import com.itrycn.myeasywol.utils.sdkinit.XBasicLibInit; +import com.itrycn.myeasywol.utils.sdkinit.XUpdateInit; + +/** + * @author xuexiang + * @since 2018/11/7 下午1:12 + */ +public class MyApp extends Application { + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + //解决4.x运行崩溃的问题 + MultiDex.install(this); + } + + @Override + public void onCreate() { + super.onCreate(); + initLibs(); + initDB(); + } + /** + * 初始化数据库 + */ + private void initDB() { + DBHelper.getInstance(getApplicationContext()); + } + /** + * 初始化基础库 + */ + private void initLibs() { + XBasicLibInit.init(this); + + XUpdateInit.init(this); + XUpdateInit.checkUpdate(this,false); + //运营统计数据运行时不初始化 + + //ANR监控 + ANRWatchDogInit.init(); + } + + + /** + * @return 当前app是否是调试开发模式 + */ + public static boolean isDebug() { + return BuildConfig.DEBUG; + } + + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/activity/MainActivity.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/activity/MainActivity.java new file mode 100644 index 0000000..ab7d628 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/activity/MainActivity.java @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.activity; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.widget.Toolbar; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.navigation.NavigationView; +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseActivity; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.fragment.AboutFragment; +import com.itrycn.myeasywol.fragment.SettingsFragment; +import com.itrycn.myeasywol.fragment.news.AddPCFragment; +import com.itrycn.myeasywol.fragment.news.AddServerFragment; +import com.itrycn.myeasywol.fragment.news.PCFragment; +import com.itrycn.myeasywol.fragment.news.ServerViewFragment; +import com.itrycn.myeasywol.fragment.news.WakePCFragment; +import com.itrycn.myeasywol.fragment.profile.MsgCode; +import com.itrycn.myeasywol.fragment.profile.ProfileFragment; +import com.itrycn.myeasywol.utils.MyBroadcastReceiver; +import com.itrycn.myeasywol.utils.Utils; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.core.CoreSwitchBean; +import com.xuexiang.xui.adapter.FragmentAdapter; +import com.xuexiang.xui.utils.ResUtils; +import com.xuexiang.xui.utils.ThemeUtils; +import com.xuexiang.xutil.XUtil; +import com.xuexiang.xutil.common.ClickUtils; +import com.xuexiang.xutil.common.CollectionUtils; +import com.xuexiang.xutil.display.Colors; + +import butterknife.BindView; + +/** + * 程序主页面,只是一个简单的Tab例子 + * + * @author xuexiang + * @since 2019-07-07 23:53 + */ +public class MainActivity extends BaseActivity implements View.OnClickListener, ViewPager.OnPageChangeListener, BottomNavigationView.OnNavigationItemSelectedListener, ClickUtils.OnClick2ExitListener, Toolbar.OnMenuItemClickListener { + /** + * 广播 + */ + private MyBroadcastReceiver mBroadcastReceiver; + @BindView(R.id.toolbar) + Toolbar toolbar; + @BindView(R.id.view_pager) + ViewPager viewPager; + /** + * 底部导航栏 + */ + @BindView(R.id.bottom_navigation) + BottomNavigationView bottomNavigation; + /** + * 侧边栏 + */ + @BindView(R.id.nav_view) + NavigationView navView; + @BindView(R.id.drawer_layout) + DrawerLayout drawerLayout; + + private String[] mTitles; + + @Override + protected int getLayoutId() { + return R.layout.activity_main; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + initViews(); + + initListeners(); + mBroadcastReceiver = new MyBroadcastReceiver(); + mBroadcastReceiver.setReceiverListener(new MyBroadcastReceiver.MyReceiverListener() { + @Override + public void onReceive(Context context, final Intent intent, final int code) { + mUIHandler.post(new Runnable() { + @Override + public void run() { + handleAudioBroadcastReceiver(intent, code); + } + }); + } + + private void handleAudioBroadcastReceiver(Intent intent, int code) { + switch (code) { + case MsgCode.ACTION_CODE_EDIT_SERVER: + Bundle initBundle = intent.getBundleExtra(MyBroadcastReceiver.ACTION_BUNDLEKEY); + CoreSwitchBean page = new CoreSwitchBean(AddServerFragment.class) + .setNewActivity(true); + page.setBundle(initBundle); + openPage(page); + break; + case MsgCode.ACTION_CODE_EDIT_PC: + Bundle initBundle_PC = intent.getBundleExtra(MyBroadcastReceiver.ACTION_BUNDLEKEY); + CoreSwitchBean page_PC = new CoreSwitchBean(AddPCFragment.class) + .setNewActivity(true); + page_PC.setBundle(initBundle_PC); + openPage(page_PC); + break; + case MsgCode.ACTION_CODE_WAKE_PC: + Bundle initBundle2 = intent.getBundleExtra(MyBroadcastReceiver.ACTION_BUNDLEKEY); + CoreSwitchBean page2 = new CoreSwitchBean(WakePCFragment.class) + .setNewActivity(true); + page2.setBundle(initBundle2); + openPage(page2); + break; + } + } + }); + mBroadcastReceiver.registerReceiver(getApplicationContext()); + } + @Override + public void onDestroy() { + if (mBroadcastReceiver != null) { + mBroadcastReceiver.unregisterReceiver(getApplicationContext()); + } + super.onDestroy(); + } + @Override + protected boolean isSupportSlideBack() { + return false; + } + + private void initViews() { + mTitles = ResUtils.getStringArray(R.array.home_titles); + toolbar.setTitle(mTitles[0]); + toolbar.inflateMenu(R.menu.menu_main); + toolbar.setOnMenuItemClickListener(this); + + initHeader(); + + //主页内容填充 + BaseFragment[] fragments = new BaseFragment[]{ + new PCFragment(), + new ServerViewFragment(), + new ProfileFragment() + }; + FragmentAdapter adapter = new FragmentAdapter<>(getSupportFragmentManager(), fragments); + viewPager.setOffscreenPageLimit(mTitles.length - 1); + viewPager.setAdapter(adapter); + + //GuideTipsDialog.showTips(this); + } + private void initHeader() { + navView.setItemIconTintList(null); + View headerView = navView.getHeaderView(0); + LinearLayout navHeader = headerView.findViewById(R.id.nav_header); + //RadiusImageView ivAvatar = headerView.findViewById(R.id.iv_avatar); + TextView tvAvatar = headerView.findViewById(R.id.tv_avatar); + TextView tvSign = headerView.findViewById(R.id.tv_sign); + + if (Utils.isColorDark(ThemeUtils.resolveColor(this, R.attr.colorAccent))) { + tvAvatar.setTextColor(Colors.WHITE); + tvSign.setTextColor(Colors.WHITE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // ivAvatar.setImageTintList(ResUtils.getColors(R.color.xui_config_color_white)); + } + } else { + tvAvatar.setTextColor(ThemeUtils.resolveColor(this, R.attr.xui_config_color_title_text)); + tvSign.setTextColor(ThemeUtils.resolveColor(this, R.attr.xui_config_color_explain_text)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // ivAvatar.setImageTintList(ResUtils.getColors(R.color.xui_config_color_gray_3)); + } + } + + // TODO: 2019-10-09 初始化数据 + //ivAvatar.setImageResource(R.drawable.ic_default_head); + tvAvatar.setText(R.string.app_name); + tvSign.setText("快速便捷的远程唤醒工具"); + navHeader.setOnClickListener(this); + } + + protected void initListeners() { + ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); + drawerLayout.addDrawerListener(toggle); + toggle.syncState(); + + //侧边栏点击事件 + navView.setNavigationItemSelectedListener(menuItem -> { + if (menuItem.isCheckable()) { + drawerLayout.closeDrawers(); + return handleNavigationItemSelected(menuItem); + } else { + switch (menuItem.getItemId()) { + case R.id.nav_settings: + openNewPage(SettingsFragment.class); + break; + case R.id.nav_about: + openNewPage(AboutFragment.class); + break; + default: + XToastUtils.toast("点击了:" + menuItem.getTitle()); + break; + } + } + return true; + }); + + //主页事件监听 + viewPager.addOnPageChangeListener(this); + bottomNavigation.setOnNavigationItemSelectedListener(this); + } + + /** + * 处理侧边栏点击事件 + * + * @param menuItem + * @return + */ + private boolean handleNavigationItemSelected(@NonNull MenuItem menuItem) { + int index = CollectionUtils.arrayIndexOf(mTitles, menuItem.getTitle()); + if (index != -1) { + toolbar.setTitle(menuItem.getTitle()); + viewPager.setCurrentItem(index, false); + return true; + } + return false; + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_privacy: + if(viewPager.getCurrentItem()!=1) + {openNewPage(AddPCFragment.class);} + else if(viewPager.getCurrentItem()==1) + {openNewPage(AddServerFragment.class);} + //Utils.showPrivacyDialog(this, null); + break; + default: + break; + } + return false; + } + + @SingleClick + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.nav_header: + //XToastUtils.toast("点击头部!"); + break; + default: + break; + } + } + + //=============ViewPager===================// + + @Override + public void onPageScrolled(int i, float v, int i1) { + + } + + @Override + public void onPageSelected(int position) { + MenuItem item = bottomNavigation.getMenu().getItem(position); + toolbar.setTitle(item.getTitle()); + item.setChecked(true); + + updateSideNavStatus(item); + } + + @Override + public void onPageScrollStateChanged(int i) { + + } + + //================Navigation================// + + /** + * 底部导航栏点击事件 + * + * @param menuItem + * @return + */ + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { + int index = CollectionUtils.arrayIndexOf(mTitles, menuItem.getTitle()); + if (index != -1) { + toolbar.setTitle(menuItem.getTitle()); + viewPager.setCurrentItem(index, false); + + updateSideNavStatus(menuItem); + return true; + } + return false; + } + + /** + * 更新侧边栏菜单选中状态 + * + * @param menuItem + */ + private void updateSideNavStatus(MenuItem menuItem) { + MenuItem side = navView.getMenu().findItem(menuItem.getItemId()); + if (side != null) { + side.setChecked(true); + } + } + + /** + * 菜单、返回键响应 + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + ClickUtils.exitBy2Click(2000, this); + } + return true; + } + + @Override + public void onRetry() { + XToastUtils.toast("再按一次退出程序"); + } + + @Override + public void onExit() { + XUtil.exitApp(); + } + + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/activity/SplashActivity.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/activity/SplashActivity.java new file mode 100644 index 0000000..3e0d2b2 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/activity/SplashActivity.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.activity; + +import android.view.KeyEvent; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.utils.SettingUtils; +import com.itrycn.myeasywol.utils.TokenUtils; +import com.itrycn.myeasywol.utils.Utils; +import com.xuexiang.xui.utils.KeyboardUtils; +import com.xuexiang.xui.widget.activity.BaseSplashActivity; +import com.xuexiang.xutil.app.ActivityUtils; + +import me.jessyan.autosize.internal.CancelAdapt; + +/** + * 启动页【无需适配屏幕大小】 + * + * @author xuexiang + * @since 2019-06-30 17:32 + */ +public class SplashActivity extends BaseSplashActivity implements CancelAdapt { + + @Override + protected long getSplashDurationMillis() { + return 500; + } + + /** + * activity启动后的初始化 + */ + @Override + protected void onCreateActivity() { + initSplashView(R.drawable.xui_config_bg_splash); + startSplash(false); + } + + /** + * 启动页结束后的动作 + */ + @Override + protected void onSplashFinished() { + if (SettingUtils.isAgreePrivacy()) { + loginOrGoMainPage(); + } else { + Utils.showPrivacyDialog(this, (dialog, which) -> { + dialog.dismiss(); + SettingUtils.setIsAgreePrivacy(true); + loginOrGoMainPage(); + }); + } + } + + private void loginOrGoMainPage() { + ActivityUtils.startActivity(MainActivity.class); + finish(); + } + + /** + * 菜单、返回键响应 + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/broccoli/BroccoliRecyclerAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/broccoli/BroccoliRecyclerAdapter.java new file mode 100644 index 0000000..dde1198 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/broccoli/BroccoliRecyclerAdapter.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.base.broccoli; + +import android.view.View; + +import androidx.annotation.NonNull; + +import com.xuexiang.xui.adapter.recyclerview.BaseRecyclerAdapter; +import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder; +import com.xuexiang.xui.adapter.recyclerview.XRecyclerAdapter; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import me.samlss.broccoli.Broccoli; + +/** + * 使用Broccoli占位的基础适配器 + * + * @author XUE + * @since 2019/4/8 16:33 + */ +public abstract class BroccoliRecyclerAdapter extends BaseRecyclerAdapter { + /** + * 是否已经加载成功 + */ + private boolean mHasLoad = false; + private Map mBroccoliMap = new HashMap<>(); + + public BroccoliRecyclerAdapter(Collection collection) { + super(collection); + } + + @Override + protected void bindData(@NonNull RecyclerViewHolder holder, int position, T item) { + Broccoli broccoli = mBroccoliMap.get(holder.itemView); + if (broccoli == null) { + broccoli = new Broccoli(); + mBroccoliMap.put(holder.itemView, broccoli); + } + if (mHasLoad) { + broccoli.removeAllPlaceholders(); + + onBindData(holder, item, position); + } else { + onBindBroccoli(holder, broccoli); + broccoli.show(); + } + } + + /** + * 绑定控件 + * + * @param holder + * @param model + * @param position + */ + protected abstract void onBindData(RecyclerViewHolder holder, T model, int position); + + /** + * 绑定占位控件 + * + * @param broccoli + */ + protected abstract void onBindBroccoli(RecyclerViewHolder holder, Broccoli broccoli); + + @Override + public XRecyclerAdapter refresh(Collection collection) { + mHasLoad = true; + return super.refresh(collection); + } + + /** + * 资源释放,防止内存泄漏 + */ + public void recycle() { + for (Broccoli broccoli : mBroccoliMap.values()) { + broccoli.removeAllPlaceholders(); + } + mBroccoliMap.clear(); + clear(); + } + + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/broccoli/BroccoliSimpleDelegateAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/broccoli/BroccoliSimpleDelegateAdapter.java new file mode 100644 index 0000000..53f8b1c --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/broccoli/BroccoliSimpleDelegateAdapter.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.base.broccoli; + +import android.view.View; + +import androidx.annotation.NonNull; + +import com.alibaba.android.vlayout.LayoutHelper; +import com.itrycn.myeasywol.adapter.base.delegate.SimpleDelegateAdapter; +import com.itrycn.myeasywol.adapter.base.delegate.XDelegateAdapter; +import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import me.samlss.broccoli.Broccoli; + +/** + * 使用Broccoli占位的基础适配器 + * + * @author xuexiang + * @since 2021/1/9 4:52 PM + */ +public abstract class BroccoliSimpleDelegateAdapter extends SimpleDelegateAdapter { + + /** + * 是否已经加载成功 + */ + private boolean mHasLoad = false; + private Map mBroccoliMap = new HashMap<>(); + + public BroccoliSimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper) { + super(layoutId, layoutHelper); + } + + public BroccoliSimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper, Collection list) { + super(layoutId, layoutHelper, list); + } + + public BroccoliSimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper, T[] data) { + super(layoutId, layoutHelper, data); + } + + @Override + protected void bindData(@NonNull RecyclerViewHolder holder, int position, T item) { + Broccoli broccoli = mBroccoliMap.get(holder.itemView); + if (broccoli == null) { + broccoli = new Broccoli(); + mBroccoliMap.put(holder.itemView, broccoli); + } + if (mHasLoad) { + broccoli.removeAllPlaceholders(); + + onBindData(holder, item, position); + } else { + onBindBroccoli(holder, broccoli); + broccoli.show(); + } + } + + + /** + * 绑定控件 + * + * @param holder + * @param model + * @param position + */ + protected abstract void onBindData(RecyclerViewHolder holder, T model, int position); + + /** + * 绑定占位控件 + * + * @param holder + * @param broccoli + */ + protected abstract void onBindBroccoli(RecyclerViewHolder holder, Broccoli broccoli); + + @Override + public XDelegateAdapter refresh(Collection collection) { + mHasLoad = true; + return super.refresh(collection); + } + + /** + * 资源释放,防止内存泄漏 + */ + public void recycle() { + for (Broccoli broccoli : mBroccoliMap.values()) { + broccoli.removeAllPlaceholders(); + } + mBroccoliMap.clear(); + clear(); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/BaseDelegateAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/BaseDelegateAdapter.java new file mode 100644 index 0000000..e6e68a3 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/BaseDelegateAdapter.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.base.delegate; + +import android.view.ViewGroup; + +import androidx.annotation.NonNull; + +import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder; + +import java.util.Collection; + +/** + * 通用的DelegateAdapter适配器 + * + * @author xuexiang + * @since 2020/3/20 12:44 AM + */ +public abstract class BaseDelegateAdapter extends XDelegateAdapter { + + public BaseDelegateAdapter() { + super(); + } + + public BaseDelegateAdapter(Collection list) { + super(list); + } + + public BaseDelegateAdapter(T[] data) { + super(data); + } + + /** + * 适配的布局 + * + * @param viewType + * @return + */ + protected abstract int getItemLayoutId(int viewType); + + @NonNull + @Override + protected RecyclerViewHolder getViewHolder(@NonNull ViewGroup parent, int viewType) { + return new RecyclerViewHolder(inflateView(parent, getItemLayoutId(viewType))); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/ServerRecyclerAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/ServerRecyclerAdapter.java new file mode 100644 index 0000000..7441b09 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/ServerRecyclerAdapter.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.base.delegate; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.db.entity.ServerInfo; +import com.scwang.smartrefresh.layout.adapter.SmartRecyclerAdapter; +import com.scwang.smartrefresh.layout.adapter.SmartViewHolder; + +import java.util.Collection; + +public class ServerRecyclerAdapter extends SmartRecyclerAdapter { + public ServerRecyclerAdapter() { + super(R.layout.adapter_item_simple_list_1); + } + + public ServerRecyclerAdapter(Collection data) { + super(data, R.layout.adapter_item_simple_list_1); + } + + /** + * 绑定布局控件 + * + * @param holder + * @param model + * @param position + */ + @Override + protected void onBindViewHolder(SmartViewHolder holder, ServerInfo model, int position) { + holder.text(R.id.tv_title, model.getName()); + //holder.text(android.R.id.text2,model.getName()); + //holder.textColorId(android.R.id.text2, R.color.xui_config_color_light_blue_gray); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/SimpleDelegateAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/SimpleDelegateAdapter.java new file mode 100644 index 0000000..22a6a47 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/SimpleDelegateAdapter.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.base.delegate; + +import com.alibaba.android.vlayout.LayoutHelper; + +import java.util.Collection; + +/** + * 简易DelegateAdapter适配器 + * + * @author xuexiang + * @since 2020/3/20 12:55 AM + */ +public abstract class SimpleDelegateAdapter extends BaseDelegateAdapter { + + private int mLayoutId; + + private LayoutHelper mLayoutHelper; + + public SimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper) { + super(); + mLayoutId = layoutId; + mLayoutHelper = layoutHelper; + } + + public SimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper, Collection list) { + super(list); + mLayoutId = layoutId; + mLayoutHelper = layoutHelper; + } + + public SimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper, T[] data) { + super(data); + mLayoutId = layoutId; + mLayoutHelper = layoutHelper; + } + + @Override + protected int getItemLayoutId(int viewType) { + return mLayoutId; + } + + + @Override + public LayoutHelper onCreateLayoutHelper() { + return mLayoutHelper; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/SingleDelegateAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/SingleDelegateAdapter.java new file mode 100644 index 0000000..a200d52 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/SingleDelegateAdapter.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.base.delegate; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; + +import com.alibaba.android.vlayout.DelegateAdapter; +import com.alibaba.android.vlayout.LayoutHelper; +import com.alibaba.android.vlayout.layout.SingleLayoutHelper; +import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder; + +/** + * 单独布局的DelegateAdapter + * + * @author xuexiang + * @since 2020/3/20 1:04 AM + */ +public abstract class SingleDelegateAdapter extends DelegateAdapter.Adapter { + + private int mLayoutId; + + public SingleDelegateAdapter(int layoutId) { + mLayoutId = layoutId; + } + + @Override + public LayoutHelper onCreateLayoutHelper() { + return new SingleLayoutHelper(); + } + + /** + * 加载布局获取控件 + * + * @param parent 父布局 + * @param layoutId 布局ID + * @return + */ + protected View inflateView(ViewGroup parent, @LayoutRes int layoutId) { + return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); + } + + @NonNull + @Override + public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new RecyclerViewHolder(inflateView(parent, mLayoutId)); + } + + @Override + public int getItemCount() { + return 1; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/XDelegateAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/XDelegateAdapter.java new file mode 100644 index 0000000..7c2c907 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/base/delegate/XDelegateAdapter.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.base.delegate; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.alibaba.android.vlayout.DelegateAdapter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * 基础DelegateAdapter + * + * @author xuexiang + * @since 2020/3/20 12:17 AM + */ +public abstract class XDelegateAdapter extends DelegateAdapter.Adapter { + /** + * 数据源 + */ + protected final List mData = new ArrayList<>(); + /** + * 当前点击的条目 + */ + protected int mSelectPosition = -1; + + public XDelegateAdapter() { + + } + + public XDelegateAdapter(Collection list) { + if (list != null) { + mData.addAll(list); + } + } + + public XDelegateAdapter(T[] data) { + if (data != null && data.length > 0) { + mData.addAll(Arrays.asList(data)); + } + } + + /** + * 构建自定义的ViewHolder + * + * @param parent + * @param viewType + * @return + */ + @NonNull + protected abstract V getViewHolder(@NonNull ViewGroup parent, int viewType); + + /** + * 绑定数据 + * + * @param holder + * @param position 索引 + * @param item 列表项 + */ + protected abstract void bindData(@NonNull V holder, int position, T item); + + /** + * 加载布局获取控件 + * + * @param parent 父布局 + * @param layoutId 布局ID + * @return + */ + protected View inflateView(ViewGroup parent, @LayoutRes int layoutId) { + return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); + } + + @NonNull + @Override + public V onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return getViewHolder(parent, viewType); + } + + @Override + public void onBindViewHolder(@NonNull V holder, int position) { + bindData(holder, position, mData.get(position)); + } + + /** + * 获取列表项 + * + * @param position + * @return + */ + public T getItem(int position) { + return checkPosition(position) ? mData.get(position) : null; + } + + private boolean checkPosition(int position) { + return position >= 0 && position <= mData.size() - 1; + } + + public boolean isEmpty() { + return getItemCount() == 0; + } + + @Override + public int getItemCount() { + return mData.size(); + } + + /** + * @return 数据源 + */ + public List getData() { + return mData; + } + + /** + * 给指定位置添加一项 + * + * @param pos + * @param item + * @return + */ + public XDelegateAdapter add(int pos, T item) { + mData.add(pos, item); + notifyItemInserted(pos); + return this; + } + + /** + * 在列表末端增加一项 + * + * @param item + * @return + */ + public XDelegateAdapter add(T item) { + mData.add(item); + notifyItemInserted(mData.size() - 1); + return this; + } + + /** + * 删除列表中指定索引的数据 + * + * @param pos + * @return + */ + public XDelegateAdapter delete(int pos) { + mData.remove(pos); + notifyItemRemoved(pos); + return this; + } + + /** + * 刷新列表中指定位置的数据 + * + * @param pos + * @param item + * @return + */ + public XDelegateAdapter refresh(int pos, T item) { + mData.set(pos, item); + notifyItemChanged(pos); + return this; + } + + /** + * 刷新列表数据 + * + * @param collection + * @return + */ + public XDelegateAdapter refresh(Collection collection) { + if (collection != null) { + mData.clear(); + mData.addAll(collection); + mSelectPosition = -1; + notifyDataSetChanged(); + } + return this; + } + + /** + * 刷新列表数据 + * + * @param array + * @return + */ + public XDelegateAdapter refresh(T[] array) { + if (array != null && array.length > 0) { + mData.clear(); + mData.addAll(Arrays.asList(array)); + mSelectPosition = -1; + notifyDataSetChanged(); + } + return this; + } + + /** + * 加载更多 + * + * @param collection + * @return + */ + public XDelegateAdapter loadMore(Collection collection) { + if (collection != null) { + mData.addAll(collection); + notifyDataSetChanged(); + } + return this; + } + + /** + * 加载更多 + * + * @param array + * @return + */ + public XDelegateAdapter loadMore(T[] array) { + if (array != null && array.length > 0) { + mData.addAll(Arrays.asList(array)); + notifyDataSetChanged(); + } + return this; + } + + /** + * 添加一个 + * + * @param item + * @return + */ + public XDelegateAdapter load(T item) { + if (item != null) { + mData.add(item); + notifyDataSetChanged(); + } + return this; + } + + /** + * @return 当前列表的选中项 + */ + public int getSelectPosition() { + return mSelectPosition; + } + + /** + * 设置当前列表的选中项 + * + * @param selectPosition + * @return + */ + public XDelegateAdapter setSelectPosition(int selectPosition) { + mSelectPosition = selectPosition; + notifyDataSetChanged(); + return this; + } + + /** + * 获取当前列表选中项 + * + * @return 当前列表选中项 + */ + public T getSelectItem() { + return getItem(mSelectPosition); + } + + /** + * 清除数据 + */ + public void clear() { + if (!isEmpty()) { + mData.clear(); + mSelectPosition = -1; + notifyDataSetChanged(); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/entity/NewInfo.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/entity/NewInfo.java new file mode 100644 index 0000000..ef54192 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/adapter/entity/NewInfo.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.adapter.entity; + +/** + * 新闻信息 + * + * @author xuexiang + * @since 2019/4/7 下午12:07 + */ +public class NewInfo { + + /** + * 用户名 + */ + private String UserName = "xuexiangjys"; + /** + * 标签 + */ + private String Tag; + /** + * 标题 + */ + private String Title; + /** + * 摘要 + */ + private String Summary; + + /** + * 图片 + */ + private String ImageUrl; + /** + * 点赞数 + */ + private int Praise; + /** + * 评论数 + */ + private int Comment; + /** + * 阅读量 + */ + private int Read; + /** + * 新闻的详情地址 + */ + private String DetailUrl; + + + public NewInfo() { + + } + + public NewInfo(String userName, String tag, String title, String summary, String imageUrl, int praise, int comment, int read, String detailUrl) { + UserName = userName; + Tag = tag; + Title = title; + Summary = summary; + ImageUrl = imageUrl; + Praise = praise; + Comment = comment; + Read = read; + DetailUrl = detailUrl; + } + + + public NewInfo(String tag, String title, String summary, String imageUrl, String detailUrl) { + Tag = tag; + Title = title; + Summary = summary; + ImageUrl = imageUrl; + DetailUrl = detailUrl; + } + + + public NewInfo(String tag, String title) { + Tag = tag; + Title = title; + + Praise = (int) (Math.random() * 100 + 5); + Comment = (int) (Math.random() * 50 + 5); + Read = (int) (Math.random() * 500 + 50); + } + + + + public String getUserName() { + return UserName; + } + + public NewInfo setUserName(String userName) { + UserName = userName; + return this; + } + + public String getTag() { + return Tag; + } + + public NewInfo setTag(String tag) { + Tag = tag; + return this; + } + + public String getTitle() { + return Title; + } + + public NewInfo setTitle(String title) { + Title = title; + return this; + } + + public String getSummary() { + return Summary; + } + + public NewInfo setSummary(String summary) { + Summary = summary; + return this; + } + + public String getImageUrl() { + return ImageUrl; + } + + public NewInfo setImageUrl(String imageUrl) { + ImageUrl = imageUrl; + return this; + } + + public int getPraise() { + return Praise; + } + + public NewInfo setPraise(int praise) { + Praise = praise; + return this; + } + + public int getComment() { + return Comment; + } + + public NewInfo setComment(int comment) { + Comment = comment; + return this; + } + + public int getRead() { + return Read; + } + + public NewInfo setRead(int read) { + Read = read; + return this; + } + + public String getDetailUrl() { + return DetailUrl; + } + + public NewInfo setDetailUrl(String detailUrl) { + DetailUrl = detailUrl; + return this; + } + + @Override + public String toString() { + return "NewInfo{" + + "UserName='" + UserName + '\'' + + ", Tag='" + Tag + '\'' + + ", Title='" + Title + '\'' + + ", Summary='" + Summary + '\'' + + ", ImageUrl='" + ImageUrl + '\'' + + ", Praise=" + Praise + + ", Comment=" + Comment + + ", Read=" + Read + + ", DetailUrl='" + DetailUrl + '\'' + + '}'; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseActivity.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseActivity.java new file mode 100644 index 0000000..aacbcb7 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseActivity.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import com.xuexiang.xpage.base.XPageActivity; +import com.xuexiang.xpage.base.XPageFragment; +import com.xuexiang.xpage.core.CoreSwitchBean; +import com.xuexiang.xrouter.facade.service.SerializationService; +import com.xuexiang.xrouter.launcher.XRouter; +import com.xuexiang.xui.utils.ResUtils; +import com.xuexiang.xui.widget.slideback.SlideBack; + +import butterknife.ButterKnife; +import butterknife.Unbinder; +import io.github.inflationx.viewpump.ViewPumpContextWrapper; + +/** + * 基础容器Activity + * + * @author XUE + * @since 2019/3/22 11:21 + */ +public class BaseActivity extends XPageActivity { + + Unbinder mUnbinder; + /** + * 处理ui任务 + */ + public WeakRefHandler mUIHandler; + + @Override + protected void attachBaseContext(Context newBase) { + //注入字体 + super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase)); + } + + /** + * 是否支持侧滑返回 + */ + public static final String KEY_SUPPORT_SLIDE_BACK = "key_support_slide_back"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + initStatusBarStyle(); + super.onCreate(savedInstanceState); + mUnbinder = ButterKnife.bind(this); +//创建ui handler + mUIHandler = new WeakRefHandler(Looper.getMainLooper(), this, new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + return true; + } + }); + registerSlideBack(); + } + /** + * 初始化状态栏的样式 + */ + protected void initStatusBarStyle() { + + } + + /** + * 打开fragment + * + * @param clazz 页面类 + * @param addToBackStack 是否添加到栈中 + * @return 打开的fragment对象 + */ + public T openPage(Class clazz, boolean addToBackStack) { + CoreSwitchBean page = new CoreSwitchBean(clazz) + .setAddToBackStack(addToBackStack); + return (T) openPage(page); + } + + /** + * 打开fragment + * + * @return 打开的fragment对象 + */ + public T openNewPage(Class clazz) { + CoreSwitchBean page = new CoreSwitchBean(clazz) + .setNewActivity(true); + return (T) openPage(page); + } + + /** + * 切换fragment + * + * @param clazz 页面类 + * @return 打开的fragment对象 + */ + public T switchPage(Class clazz) { + return openPage(clazz, false); + } + + /** + * 序列化对象 + * + * @param object + * @return + */ + public String serializeObject(Object object) { + return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); + } + + @Override + protected void onRelease() { + mUnbinder.unbind(); + //移除队列任务 + if (mUIHandler != null) { + mUIHandler.removeCallbacksAndMessages(null); + } + unregisterSlideBack(); + super.onRelease(); + } + + /** + * 注册侧滑回调 + */ + protected void registerSlideBack() { + if (isSupportSlideBack()) { + SlideBack.with(this) + .haveScroll(true) + .edgeMode(ResUtils.isRtl() ? SlideBack.EDGE_RIGHT : SlideBack.EDGE_LEFT) + .callBack(this::popPage) + .register(); + } + } + + /** + * 注销侧滑回调 + */ + protected void unregisterSlideBack() { + if (isSupportSlideBack()) { + SlideBack.unregister(this); + } + } + + /** + * @return 是否支持侧滑返回 + */ + protected boolean isSupportSlideBack() { + CoreSwitchBean page = getIntent().getParcelableExtra(CoreSwitchBean.KEY_SWITCH_BEAN); + return page == null || page.getBundle() == null || page.getBundle().getBoolean(KEY_SUPPORT_SLIDE_BACK, true); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseContainerFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseContainerFragment.java new file mode 100644 index 0000000..354babd --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseContainerFragment.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core; + +import android.content.res.Configuration; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; + +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.base.XPageContainerListFragment; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xui.widget.actionbar.TitleUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.itrycn.myeasywol.core.SimpleListAdapter.KEY_SUB_TITLE; +import static com.itrycn.myeasywol.core.SimpleListAdapter.KEY_TITLE; + +/** + * 修改列表样式为主副标题显示 + * + * @author xuexiang + * @since 2018/11/22 上午11:26 + */ +public abstract class BaseContainerFragment extends XPageContainerListFragment { + + @Override + protected void initPage() { + initTitle(); + initViews(); + initListeners(); + } + + protected TitleBar initTitle() { + return TitleUtils.addTitleBarDynamic((ViewGroup) getRootView(), getPageTitle(), new View.OnClickListener() { + @Override + public void onClick(View v) { + popToBack(); + } + }); + } + + @Override + protected void initData() { + mSimpleData = initSimpleData(mSimpleData); + + List> data = new ArrayList<>(); + for (String content : mSimpleData) { + Map item = new HashMap<>(); + int index = content.indexOf("\n"); + if (index > 0) { + item.put(KEY_TITLE, String.valueOf(content.subSequence(0, index))); + item.put(KEY_SUB_TITLE, String.valueOf(content.subSequence(index + 1, content.length()))); + } else { + item.put(KEY_TITLE, content); + item.put(KEY_SUB_TITLE, ""); + } + data.add(item); + } + + getListView().setAdapter(new SimpleListAdapter(getContext(), data)); + initSimply(); + } + + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + onItemClick(view, position); + } + + @SingleClick + private void onItemClick(View view, int position) { + onItemClick(position); + } + + @Override + public void onDestroyView() { + getListView().setOnItemClickListener(null); + super.onDestroyView(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + //屏幕旋转时刷新一下title + super.onConfigurationChanged(newConfig); + ViewGroup root = (ViewGroup) getRootView(); + if (root.getChildAt(0) instanceof TitleBar) { + root.removeViewAt(0); + initTitle(); + } + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseFragment.java new file mode 100644 index 0000000..ddd4572 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseFragment.java @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core; + +import android.content.res.Configuration; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Parcelable; +import android.os.Process; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import com.itrycn.myeasywol.core.http.loader.ProgressLoader; +import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; +import com.xuexiang.xpage.base.XPageActivity; +import com.xuexiang.xpage.base.XPageFragment; +import com.xuexiang.xpage.core.PageOption; +import com.xuexiang.xpage.enums.CoreAnim; +import com.xuexiang.xpage.utils.Utils; +import com.xuexiang.xrouter.facade.service.SerializationService; +import com.xuexiang.xrouter.launcher.XRouter; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xui.widget.actionbar.TitleUtils; + +import java.io.Serializable; +import java.lang.reflect.Type; + +/** + * 基础fragment + * + * @author xuexiang + * @since 2018/5/25 下午3:44 + */ +public abstract class BaseFragment extends XPageFragment { + + private IProgressLoader mIProgressLoader; + /** + * 处理ui任务 + */ + public WeakRefHandler mUIHandler; + /** + * 子线程用于执行耗时任务 + */ + public WeakRefHandler mWorkerHandler; + //创建异步HandlerThread + private HandlerThread mHandlerThread; + @Override + protected void initPage() { + initTitle(); + initViews(); + initListeners(); + //创建ui handler + mUIHandler = new WeakRefHandler(Looper.getMainLooper(), this, new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + handleUIMessage(msg); + return true; + } + }); + //创建异步HandlerThread + mHandlerThread = new HandlerThread("loadFragmentData", Process.THREAD_PRIORITY_BACKGROUND); + //必须先开启线程 + mHandlerThread.start(); + //子线程Handler + mWorkerHandler = new WeakRefHandler(mHandlerThread.getLooper(), this, new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + handleWorkerMessage(msg); + return true; + } + }); + } + /** + * 处理UI线程worker + * + * @param msg + */ + protected abstract void handleUIMessage(Message msg); + /** + * 处理子线程worker + * + * @param msg + */ + protected abstract void handleWorkerMessage(Message msg); + protected TitleBar initTitle() { + return TitleUtils.addTitleBarDynamic((ViewGroup) getRootView(), getPageTitle(), v -> popToBack()); + } + + @Override + protected void initListeners() { + + } + + /** + * 获取进度条加载者 + * + * @return 进度条加载者 + */ + public IProgressLoader getProgressLoader() { + if (mIProgressLoader == null) { + mIProgressLoader = ProgressLoader.create(getContext()); + } + return mIProgressLoader; + } + + /** + * 获取进度条加载者 + * + * @param message + * @return 进度条加载者 + */ + public IProgressLoader getProgressLoader(String message) { + if (mIProgressLoader == null) { + mIProgressLoader = ProgressLoader.create(getContext(), message); + } else { + mIProgressLoader.updateMessage(message); + } + return mIProgressLoader; + } + + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + //屏幕旋转时刷新一下title + super.onConfigurationChanged(newConfig); + ViewGroup root = (ViewGroup) getRootView(); + if (root.getChildAt(0) instanceof TitleBar) { + root.removeViewAt(0); + initTitle(); + } + } + + @Override + public void onDestroyView() { + if (mIProgressLoader != null) { + mIProgressLoader.dismissLoading(); + } + //移除队列任务 + if (mUIHandler != null) { + mUIHandler.removeCallbacksAndMessages(null); + } + //移除队列任务 + if (mWorkerHandler != null) { + mWorkerHandler.removeCallbacksAndMessages(null); + } + + //关闭线程 + if (mHandlerThread != null) + mHandlerThread.quit(); + super.onDestroyView(); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } + + //==============================页面跳转api===================================// + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param clazz 页面的类 + * @param + * @return + */ + public Fragment openNewPage(Class clazz) { + return new PageOption(clazz) + .setNewActivity(true) + .open(this); + } + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param pageName 页面名 + * @param + * @return + */ + public Fragment openNewPage(String pageName) { + return new PageOption(pageName) + .setAnim(CoreAnim.slide) + .setNewActivity(true) + .open(this); + } + + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param clazz 页面的类 + * @param containActivityClazz 页面容器 + * @param + * @return + */ + public Fragment openNewPage(Class clazz, @NonNull Class containActivityClazz) { + return new PageOption(clazz) + .setNewActivity(true) + .setContainActivityClazz(containActivityClazz) + .open(this); + } + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openNewPage(Class clazz, String key, Object value) { + PageOption option = new PageOption(clazz).setNewActivity(true); + return openPage(option, key, value); + } + + public Fragment openPage(PageOption option, String key, Object value) { + if (value instanceof Integer) { + option.putInt(key, (Integer) value); + } else if (value instanceof Float) { + option.putFloat(key, (Float) value); + } else if (value instanceof String) { + option.putString(key, (String) value); + } else if (value instanceof Boolean) { + option.putBoolean(key, (Boolean) value); + } else if (value instanceof Long) { + option.putLong(key, (Long) value); + } else if (value instanceof Double) { + option.putDouble(key, (Double) value); + } else if (value instanceof Parcelable) { + option.putParcelable(key, (Parcelable) value); + } else if (value instanceof Serializable) { + option.putSerializable(key, (Serializable) value); + } else { + option.putString(key, serializeObject(value)); + } + return option.open(this); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param addToBackStack 是否加入回退栈 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, boolean addToBackStack, String key, String value) { + return new PageOption(clazz) + .setAddToBackStack(addToBackStack) + .putString(key, value) + .open(this); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, String key, Object value) { + return openPage(clazz, true, key, value); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param addToBackStack 是否加入回退栈 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, boolean addToBackStack, String key, Object value) { + PageOption option = new PageOption(clazz).setAddToBackStack(addToBackStack); + return openPage(option, key, value); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, String key, String value) { + return new PageOption(clazz) + .putString(key, value) + .open(this); + } + + /** + * 打开页面,需要结果返回 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param requestCode 请求码 + * @param + * @return + */ + public Fragment openPageForResult(Class clazz, String key, Object value, int requestCode) { + PageOption option = new PageOption(clazz).setRequestCode(requestCode); + return openPage(option, key, value); + } + + /** + * 打开页面,需要结果返回 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param requestCode 请求码 + * @param + * @return + */ + public Fragment openPageForResult(Class clazz, String key, String value, int requestCode) { + return new PageOption(clazz) + .setRequestCode(requestCode) + .putString(key, value) + .open(this); + } + + /** + * 打开页面,需要结果返回 + * + * @param clazz 页面的类 + * @param requestCode 请求码 + * @param + * @return + */ + public Fragment openPageForResult(Class clazz, int requestCode) { + return new PageOption(clazz) + .setRequestCode(requestCode) + .open(this); + } + + /** + * 序列化对象 + * + * @param object 需要序列化的对象 + * @return 序列化结果 + */ + public String serializeObject(Object object) { + return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); + } + + /** + * 反序列化对象 + * + * @param input 反序列化的内容 + * @param clazz 类型 + * @return 反序列化结果 + */ + public T deserializeObject(String input, Type clazz) { + return XRouter.getInstance().navigation(SerializationService.class).parseObject(input, clazz); + } + + + @Override + protected void hideCurrentPageSoftInput() { + if (getActivity() == null) { + return; + } + // 记住,要在xml的父布局加上android:focusable="true" 和 android:focusableInTouchMode="true" + Utils.hideSoftInputClearFocus(getActivity().getCurrentFocus()); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseSimpleListFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseSimpleListFragment.java new file mode 100644 index 0000000..0783515 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/BaseSimpleListFragment.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core; + +import android.content.res.Configuration; +import android.os.Parcelable; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import com.xuexiang.xpage.base.XPageActivity; +import com.xuexiang.xpage.base.XPageFragment; +import com.xuexiang.xpage.base.XPageSimpleListFragment; +import com.xuexiang.xpage.core.PageOption; +import com.xuexiang.xpage.enums.CoreAnim; +import com.xuexiang.xrouter.facade.service.SerializationService; +import com.xuexiang.xrouter.launcher.XRouter; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xui.widget.actionbar.TitleUtils; + +import java.io.Serializable; + +/** + * @author xuexiang + * @since 2018/12/29 下午12:41 + */ +public abstract class BaseSimpleListFragment extends XPageSimpleListFragment { + + @Override + protected void initPage() { + initTitle(); + initViews(); + initListeners(); + } + + protected TitleBar initTitle() { + return TitleUtils.addTitleBarDynamic((ViewGroup) getRootView(), getPageTitle(), new View.OnClickListener() { + @Override + public void onClick(View v) { + popToBack(); + } + }); + } + + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + //屏幕旋转时刷新一下title + super.onConfigurationChanged(newConfig); + ViewGroup root = (ViewGroup) getRootView(); + if (root.getChildAt(0) instanceof TitleBar) { + root.removeViewAt(0); + initTitle(); + } + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } + + //==============================页面跳转api===================================// + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param clazz 页面的类 + * @param + * @return + */ + public Fragment openNewPage(Class clazz) { + return new PageOption(clazz) + .setNewActivity(true) + .open(this); + } + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param pageName 页面名 + * @param + * @return + */ + public Fragment openNewPage(String pageName) { + return new PageOption(pageName) + .setAnim(CoreAnim.slide) + .setNewActivity(true) + .open(this); + } + + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param clazz 页面的类 + * @param containActivityClazz 页面容器 + * @param + * @return + */ + public Fragment openNewPage(Class clazz, @NonNull Class containActivityClazz) { + return new PageOption(clazz) + .setNewActivity(true) + .setContainActivityClazz(containActivityClazz) + .open(this); + } + + /** + * 打开一个新的页面【建议只在主tab页使用】 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openNewPage(Class clazz, String key, Object value) { + PageOption option = new PageOption(clazz).setNewActivity(true); + return openPage(option, key, value); + } + + public Fragment openPage(PageOption option, String key, Object value) { + if (value instanceof Integer) { + option.putInt(key, (Integer) value); + } else if (value instanceof Float) { + option.putFloat(key, (Float) value); + } else if (value instanceof String) { + option.putString(key, (String) value); + } else if (value instanceof Boolean) { + option.putBoolean(key, (Boolean) value); + } else if (value instanceof Long) { + option.putLong(key, (Long) value); + } else if (value instanceof Double) { + option.putDouble(key, (Double) value); + } else if (value instanceof Parcelable) { + option.putParcelable(key, (Parcelable) value); + } else if (value instanceof Serializable) { + option.putSerializable(key, (Serializable) value); + } else { + option.putString(key, serializeObject(value)); + } + return option.open(this); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param addToBackStack 是否加入回退栈 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, boolean addToBackStack, String key, String value) { + return new PageOption(clazz) + .setAddToBackStack(addToBackStack) + .putString(key, value) + .open(this); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, String key, Object value) { + return openPage(clazz, true, key, value); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param addToBackStack 是否加入回退栈 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, boolean addToBackStack, String key, Object value) { + PageOption option = new PageOption(clazz).setAddToBackStack(addToBackStack); + return openPage(option, key, value); + } + + /** + * 打开页面 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param + * @return + */ + public Fragment openPage(Class clazz, String key, String value) { + return new PageOption(clazz) + .putString(key, value) + .open(this); + } + + /** + * 打开页面,需要结果返回 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param requestCode 请求码 + * @param + * @return + */ + public Fragment openPageForResult(Class clazz, String key, Object value, int requestCode) { + PageOption option = new PageOption(clazz).setRequestCode(requestCode); + return openPage(option, key, value); + } + + /** + * 打开页面,需要结果返回 + * + * @param clazz 页面的类 + * @param key 入参的键 + * @param value 入参的值 + * @param requestCode 请求码 + * @param + * @return + */ + public Fragment openPageForResult(Class clazz, String key, String value, int requestCode) { + return new PageOption(clazz) + .setRequestCode(requestCode) + .putString(key, value) + .open(this); + } + + /** + * 打开页面,需要结果返回 + * + * @param clazz 页面的类 + * @param requestCode 请求码 + * @param + * @return + */ + public Fragment openPageForResult(Class clazz, int requestCode) { + return new PageOption(clazz) + .setRequestCode(requestCode) + .open(this); + } + + /** + * 序列化对象 + * + * @param object 需要序列化的对象 + * @return 序列化结果 + */ + public String serializeObject(Object object) { + return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/SimpleListAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/SimpleListAdapter.java new file mode 100644 index 0000000..cb65332 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/SimpleListAdapter.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core; + +import android.content.Context; +import android.view.View; +import android.widget.TextView; + +import com.itrycn.myeasywol.R; +import com.xuexiang.xui.adapter.listview.BaseListAdapter; +import com.xuexiang.xutil.common.StringUtils; + +import java.util.List; +import java.util.Map; + +/** + * 主副标题显示适配器 + * + * @author xuexiang + * @since 2018/12/19 上午12:19 + */ +public class SimpleListAdapter extends BaseListAdapter, SimpleListAdapter.ViewHolder> { + + public static final String KEY_TITLE = "key_title"; + public static final String KEY_SUB_TITLE = "key_sub_title"; + + public SimpleListAdapter(Context context, List> data) { + super(context, data); + } + + @Override + protected ViewHolder newViewHolder(View convertView) { + ViewHolder holder = new ViewHolder(); + holder.mTvTitle = convertView.findViewById(R.id.tv_title); + holder.mTvSubTitle = convertView.findViewById(R.id.tv_sub_title); + return holder; + } + + @Override + protected int getLayoutId() { + return R.layout.adapter_item_simple_list_2; + } + + @Override + protected void convert(ViewHolder holder, Map item, int position) { + holder.mTvTitle.setText(item.get(KEY_TITLE)); + if (!StringUtils.isEmpty(item.get(KEY_SUB_TITLE))) { + holder.mTvSubTitle.setText(item.get(KEY_SUB_TITLE)); + holder.mTvSubTitle.setVisibility(View.VISIBLE); + } else { + holder.mTvSubTitle.setVisibility(View.GONE); + } + } + + public static class ViewHolder { + /** + * 标题 + */ + public TextView mTvTitle; + /** + * 副标题 + */ + public TextView mTvSubTitle; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/WeakRefHandler.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/WeakRefHandler.java new file mode 100644 index 0000000..4688f5f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/WeakRefHandler.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import java.lang.ref.WeakReference; + +/** + * Created by zhangliangming on 2018-08-23. + */ +public class WeakRefHandler extends Handler { + private WeakReference mWeakReference; + private Callback mCallback; + + public WeakRefHandler(Looper looper, T t, Callback callback) { + super(looper); + mCallback = callback; + mWeakReference = new WeakReference<>(t); + } + + @Override + public void handleMessage(Message msg) { + if (isAlive() && mCallback != null) { + mCallback.handleMessage(msg); + } + } + + /** + * 是否还存活 + * + * @return + */ + public boolean isAlive() { + T t = mWeakReference.get(); + return t != null; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/XPageTransferActivity.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/XPageTransferActivity.java new file mode 100644 index 0000000..6238a26 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/XPageTransferActivity.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core; + +import android.os.Bundle; + +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xrouter.annotation.AutoWired; +import com.xuexiang.xrouter.annotation.Router; +import com.xuexiang.xrouter.launcher.XRouter; +import com.xuexiang.xutil.common.StringUtils; + +/** + * https://xuexiangjys.club/xpage/transfer?pageName=xxxxx&.... + * applink的中转 + * + * @author xuexiang + * @since 2019-07-06 9:37 + */ +@Router(path = "/xpage/transfer") +public class XPageTransferActivity extends BaseActivity { + + @AutoWired(name = "pageName") + String pageName; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + XRouter.getInstance().inject(this); + + if (!StringUtils.isEmpty(pageName)) { + if (openPage(pageName, getIntent().getExtras()) == null) { + XToastUtils.error("页面未找到!"); + finish(); + } + } else { + XToastUtils.error("页面未找到!"); + finish(); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/api/ApiService.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/api/ApiService.java new file mode 100644 index 0000000..14ec153 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/api/ApiService.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.api; + +import com.itrycn.myeasywol.core.http.entity.TipInfo; +import com.xuexiang.xhttp2.model.ApiResult; + +import java.util.List; + +import io.reactivex.Observable; +import retrofit2.http.GET; + +/** + * @author xuexiang + * @since 2021/1/9 7:01 PM + */ +public class ApiService { + + /** + * 使用的是retrofit的接口定义 + */ + public interface IGetService { + + /** + * 获得小贴士 + */ + @GET("/xuexiangjys/Resource/raw/master/jsonapi/tips.json") + Observable>> getTips(); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/NoTipCallBack.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/NoTipCallBack.java new file mode 100644 index 0000000..10db09f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/NoTipCallBack.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.callback; + +import com.xuexiang.xhttp2.callback.SimpleCallBack; +import com.xuexiang.xhttp2.exception.ApiException; +import com.xuexiang.xhttp2.model.XHttpRequest; +import com.xuexiang.xutil.common.StringUtils; +import com.xuexiang.xutil.common.logger.Logger; + +/** + * 不带错误提示的网络请求回调 + * + * @author xuexiang + * @since 2019-11-18 23:02 + */ +public abstract class NoTipCallBack extends SimpleCallBack { + + /** + * 记录一下请求的url,确定出错的请求是哪个请求 + */ + private String mUrl; + + public NoTipCallBack() { + + } + + public NoTipCallBack(XHttpRequest req) { + this(req.getUrl()); + } + + public NoTipCallBack(String url) { + mUrl = url; + } + + @Override + public void onError(ApiException e) { + if (!StringUtils.isEmpty(mUrl)) { + Logger.e("网络请求的url:" + mUrl, e); + } else { + Logger.e(e); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/TipCallBack.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/TipCallBack.java new file mode 100644 index 0000000..0973358 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/TipCallBack.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.callback; + +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xhttp2.callback.SimpleCallBack; +import com.xuexiang.xhttp2.exception.ApiException; +import com.xuexiang.xhttp2.model.XHttpRequest; +import com.xuexiang.xutil.common.StringUtils; +import com.xuexiang.xutil.common.logger.Logger; + +/** + * 带错误toast提示的网络请求回调 + * + * @author xuexiang + * @since 2019-11-18 23:02 + */ +public abstract class TipCallBack extends SimpleCallBack { + + /** + * 记录一下请求的url,确定出错的请求是哪个请求 + */ + private String mUrl; + + public TipCallBack() { + + } + + public TipCallBack(XHttpRequest req) { + this(req.getUrl()); + } + + public TipCallBack(String url) { + mUrl = url; + } + + @Override + public void onError(ApiException e) { + XToastUtils.error(e); + if (!StringUtils.isEmpty(mUrl)) { + Logger.e("网络请求的url:" + mUrl, e); + } else { + Logger.e(e); + } + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/TipProgressLoadingCallBack.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/TipProgressLoadingCallBack.java new file mode 100644 index 0000000..8a7f937 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/callback/TipProgressLoadingCallBack.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.callback; + +import androidx.annotation.NonNull; + +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xhttp2.callback.ProgressLoadingCallBack; +import com.xuexiang.xhttp2.exception.ApiException; +import com.xuexiang.xhttp2.model.XHttpRequest; +import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; +import com.xuexiang.xutil.common.StringUtils; +import com.xuexiang.xutil.common.logger.Logger; + +/** + * 带错误toast提示和加载进度条的网络请求回调 + * + * @author xuexiang + * @since 2019-11-18 23:16 + */ +public abstract class TipProgressLoadingCallBack extends ProgressLoadingCallBack { + /** + * 记录一下请求的url,确定出错的请求是哪个请求 + */ + private String mUrl; + + public TipProgressLoadingCallBack(BaseFragment fragment) { + super(fragment.getProgressLoader()); + } + + public TipProgressLoadingCallBack(IProgressLoader iProgressLoader) { + super(iProgressLoader); + } + + public TipProgressLoadingCallBack(@NonNull XHttpRequest req, IProgressLoader iProgressLoader) { + this(req.getUrl(), iProgressLoader); + } + + public TipProgressLoadingCallBack(String url, IProgressLoader iProgressLoader) { + super(iProgressLoader); + mUrl = url; + } + + @Override + public void onError(ApiException e) { + super.onError(e); + XToastUtils.error(e); + if (!StringUtils.isEmpty(mUrl)) { + Logger.e("网络请求的url:" + mUrl, e); + } else { + Logger.e(e); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/entity/TipInfo.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/entity/TipInfo.java new file mode 100644 index 0000000..1150be0 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/entity/TipInfo.java @@ -0,0 +1,43 @@ +package com.itrycn.myeasywol.core.http.entity; + +import androidx.annotation.Keep; + +/** + * @author xuexiang + * @since 2019-08-28 15:35 + */ +@Keep +public class TipInfo { + + /** + * title : 小贴士3 + * content :

欢迎关注我的微信公众号:我的Android开源之旅。


+ */ + + private String title; + private String content; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + @Override + public String toString() { + return "TipInfo{" + + "title='" + title + '\'' + + ", content='" + content + '\'' + + '}'; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/IProgressLoaderFactory.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/IProgressLoaderFactory.java new file mode 100644 index 0000000..b686d65 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/IProgressLoaderFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.loader; + +import android.content.Context; + +import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; + +/** + * IProgressLoader的创建工厂实现接口 + * + * @author xuexiang + * @since 2019-11-18 23:17 + */ +public interface IProgressLoaderFactory { + + + /** + * 创建进度加载者 + * + * @param context + * @return + */ + IProgressLoader create(Context context); + + + /** + * 创建进度加载者 + * + * @param context + * @param message 默认提示 + * @return + */ + IProgressLoader create(Context context, String message); +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/MiniLoadingDialogLoader.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/MiniLoadingDialogLoader.java new file mode 100644 index 0000000..c4adedd --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/MiniLoadingDialogLoader.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.loader; + +import android.content.Context; +import android.content.DialogInterface; + +import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; +import com.xuexiang.xhttp2.subsciber.impl.OnProgressCancelListener; +import com.xuexiang.xui.widget.dialog.MiniLoadingDialog; + +/** + * 默认进度加载 + * + * @author xuexiang + * @since 2019-11-18 23:07 + */ +public class MiniLoadingDialogLoader implements IProgressLoader { + /** + * 进度loading弹窗 + */ + private MiniLoadingDialog mDialog; + /** + * 进度框取消监听 + */ + private OnProgressCancelListener mOnProgressCancelListener; + + public MiniLoadingDialogLoader(Context context) { + this(context, "请求中..."); + } + + public MiniLoadingDialogLoader(Context context, String msg) { + mDialog = new MiniLoadingDialog(context, msg); + } + + @Override + public boolean isLoading() { + return mDialog != null && mDialog.isShowing(); + } + + @Override + public void updateMessage(String msg) { + if (mDialog != null) { + mDialog.updateMessage(msg); + } + } + + @Override + public void showLoading() { + if (mDialog != null && !mDialog.isShowing()) { + mDialog.show(); + } + } + + @Override + public void dismissLoading() { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + } + + @Override + public void setCancelable(boolean flag) { + mDialog.setCancelable(flag); + if (flag) { + mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialogInterface) { + if (mOnProgressCancelListener != null) { + mOnProgressCancelListener.onCancelProgress(); + } + } + }); + } + } + + @Override + public void setOnProgressCancelListener(OnProgressCancelListener listener) { + mOnProgressCancelListener = listener; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/MiniProgressLoaderFactory.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/MiniProgressLoaderFactory.java new file mode 100644 index 0000000..5e5b769 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/MiniProgressLoaderFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.loader; + +import android.content.Context; + +import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; + +/** + * 迷你加载框创建工厂 + * + * @author xuexiang + * @since 2019-11-18 23:23 + */ +public class MiniProgressLoaderFactory implements IProgressLoaderFactory { + + @Override + public IProgressLoader create(Context context) { + return new MiniLoadingDialogLoader(context); + } + + @Override + public IProgressLoader create(Context context, String message) { + return new MiniLoadingDialogLoader(context, message); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/ProgressLoader.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/ProgressLoader.java new file mode 100644 index 0000000..ae7b391 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/loader/ProgressLoader.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.loader; + +import android.content.Context; + +import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; + +/** + * 创建进度加载者 + * + * @author xuexiang + * @since 2019-07-02 12:51 + */ +public final class ProgressLoader { + + private ProgressLoader() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + private static IProgressLoaderFactory sIProgressLoaderFactory = new MiniProgressLoaderFactory(); + + public static void setIProgressLoaderFactory(IProgressLoaderFactory sIProgressLoaderFactory) { + ProgressLoader.sIProgressLoaderFactory = sIProgressLoaderFactory; + } + + /** + * 创建进度加载者 + * + * @param context + * @return + */ + public static IProgressLoader create(Context context) { + return sIProgressLoaderFactory.create(context); + } + + /** + * 创建进度加载者 + * + * @param context + * @param message 默认提示信息 + * @return + */ + public static IProgressLoader create(Context context, String message) { + return sIProgressLoaderFactory.create(context, message); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/NoTipRequestSubscriber.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/NoTipRequestSubscriber.java new file mode 100644 index 0000000..7068940 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/NoTipRequestSubscriber.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.subscriber; + +import com.xuexiang.xhttp2.exception.ApiException; +import com.xuexiang.xhttp2.model.XHttpRequest; +import com.xuexiang.xhttp2.subsciber.BaseSubscriber; +import com.xuexiang.xutil.common.StringUtils; +import com.xuexiang.xutil.common.logger.Logger; + +/** + * 不带错误toast提示的网络请求订阅,只存储错误的日志 + * + * @author xuexiang + * @since 2019-11-18 23:11 + */ +public abstract class NoTipRequestSubscriber extends BaseSubscriber { + + /** + * 记录一下请求的url,确定出错的请求是哪个请求 + */ + private String mUrl; + + public NoTipRequestSubscriber() { + + } + + public NoTipRequestSubscriber(XHttpRequest req) { + this(req.getUrl()); + } + + public NoTipRequestSubscriber(String url) { + mUrl = url; + } + + @Override + public void onError(ApiException e) { + if (!StringUtils.isEmpty(mUrl)) { + Logger.e("网络请求的url:" + mUrl, e); + } else { + Logger.e(e); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/TipProgressLoadingSubscriber.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/TipProgressLoadingSubscriber.java new file mode 100644 index 0000000..436d841 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/TipProgressLoadingSubscriber.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.subscriber; + +import androidx.annotation.NonNull; + +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xhttp2.exception.ApiException; +import com.xuexiang.xhttp2.model.XHttpRequest; +import com.xuexiang.xhttp2.subsciber.ProgressLoadingSubscriber; +import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; +import com.xuexiang.xutil.common.StringUtils; +import com.xuexiang.xutil.common.logger.Logger; + +/** + * 带错误toast提示和加载进度条的网络请求订阅 + * + * @author xuexiang + * @since 2019-11-18 23:11 + */ +public abstract class TipProgressLoadingSubscriber extends ProgressLoadingSubscriber { + + /** + * 记录一下请求的url,确定出错的请求是哪个请求 + */ + private String mUrl; + + public TipProgressLoadingSubscriber() { + super(); + } + + public TipProgressLoadingSubscriber(BaseFragment fragment) { + super(fragment.getProgressLoader()); + } + + public TipProgressLoadingSubscriber(IProgressLoader iProgressLoader) { + super(iProgressLoader); + } + + public TipProgressLoadingSubscriber(@NonNull XHttpRequest req) { + this(req.getUrl()); + } + + public TipProgressLoadingSubscriber(String url) { + super(); + mUrl = url; + } + + @Override + public void onError(ApiException e) { + super.onError(e); + XToastUtils.error(e); + if (!StringUtils.isEmpty(mUrl)) { + Logger.e("网络请求的url:" + mUrl, e); + } else { + Logger.e(e); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/TipRequestSubscriber.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/TipRequestSubscriber.java new file mode 100644 index 0000000..ccb0d41 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/http/subscriber/TipRequestSubscriber.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.http.subscriber; + + +import androidx.annotation.NonNull; + +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xhttp2.exception.ApiException; +import com.xuexiang.xhttp2.model.XHttpRequest; +import com.xuexiang.xhttp2.subsciber.BaseSubscriber; +import com.xuexiang.xutil.common.StringUtils; +import com.xuexiang.xutil.common.logger.Logger; + +/** + * 带错误toast提示的网络请求订阅 + * + * @author xuexiang + * @since 2019-11-18 23:10 + */ +public abstract class TipRequestSubscriber extends BaseSubscriber { + /** + * 记录一下请求的url,确定出错的请求是哪个请求 + */ + private String mUrl; + + public TipRequestSubscriber() { + + } + + public TipRequestSubscriber(@NonNull XHttpRequest req) { + this(req.getUrl()); + } + + public TipRequestSubscriber(String url) { + mUrl = url; + } + + + @Override + public void onError(ApiException e) { + XToastUtils.error(e); + if (!StringUtils.isEmpty(mUrl)) { + Logger.e("网络请求的url:" + mUrl, e); + } else { + Logger.e(e); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/AgentWebActivity.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/AgentWebActivity.java new file mode 100644 index 0000000..5101547 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/AgentWebActivity.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.KeyEvent; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentTransaction; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xrouter.facade.Postcard; +import com.xuexiang.xrouter.facade.callback.NavCallback; +import com.xuexiang.xrouter.launcher.XRouter; +import com.xuexiang.xui.widget.slideback.SlideBack; + +import static com.itrycn.myeasywol.core.webview.AgentWebFragment.KEY_URL; + +/** + * 壳浏览器 + * + * @author xuexiang + * @since 2019/1/5 上午12:15 + */ +public class AgentWebActivity extends AppCompatActivity { + + /** + * 请求浏览器 + * + * @param url + */ + public static void goWeb(Context context, final String url) { + Intent intent = new Intent(context, AgentWebActivity.class); + intent.putExtra(KEY_URL, url); + context.startActivity(intent); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_agent_web); + + SlideBack.with(this) + .haveScroll(true) + .callBack(this::finish) + .register(); + + Uri uri = getIntent().getData(); + if (uri != null) { + XRouter.getInstance().build(uri).navigation(this, new NavCallback() { + @Override + public void onArrival(Postcard postcard) { + finish(); + } + + @Override + public void onLost(Postcard postcard) { + loadUrl(uri.toString()); + } + }); + } else { + String url = getIntent().getStringExtra(KEY_URL); + loadUrl(url); + } + } + + private void loadUrl(String url) { + if (url != null) { + openFragment(url); + } else { + XToastUtils.error("数据出错!"); + finish(); + } + } + + private AgentWebFragment mAgentWebFragment; + + private void openFragment(String url) { + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.add(R.id.container_frame_layout, mAgentWebFragment = AgentWebFragment.getInstance(url)); + ft.commit(); + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + AgentWebFragment agentWebFragment = mAgentWebFragment; + if (agentWebFragment != null) { + if (((FragmentKeyDown) agentWebFragment).onFragmentKeyDown(keyCode, event)) { + return true; + } else { + return super.onKeyDown(keyCode, event); + } + } + return super.onKeyDown(keyCode, event); + } + + + @Override + protected void onDestroy() { + SlideBack.unregister(this); + super.onDestroy(); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/AgentWebFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/AgentWebFragment.java new file mode 100644 index 0000000..b93d385 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/AgentWebFragment.java @@ -0,0 +1,658 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.appcompat.widget.PopupMenu; +import androidx.fragment.app.Fragment; + +import com.just.agentweb.action.PermissionInterceptor; +import com.just.agentweb.core.AgentWeb; +import com.just.agentweb.core.client.MiddlewareWebChromeBase; +import com.just.agentweb.core.client.MiddlewareWebClientBase; +import com.just.agentweb.core.client.WebListenerManager; +import com.just.agentweb.core.web.AbsAgentWebSettings; +import com.just.agentweb.core.web.AgentWebConfig; +import com.just.agentweb.core.web.IAgentWebSettings; +import com.just.agentweb.download.AgentWebDownloader; +import com.just.agentweb.download.DefaultDownloadImpl; +import com.just.agentweb.download.DownloadListenerAdapter; +import com.just.agentweb.download.DownloadingService; +import com.just.agentweb.utils.LogUtils; +import com.just.agentweb.widget.IWebLayout; +import com.itrycn.myeasywol.MyApp; +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xutil.net.JsonUtil; + +import java.util.HashMap; + +/** + * 通用WebView页面 + * + * @author xuexiang + * @since 2019/1/4 下午11:13 + */ +public class AgentWebFragment extends Fragment implements FragmentKeyDown { + public static final String KEY_URL = "com.xuexiang.xuidemo.base.webview.key_url"; + + private ImageView mBackImageView; + private View mLineView; + private ImageView mFinishImageView; + private TextView mTitleTextView; + private AgentWeb mAgentWeb; + private ImageView mMoreImageView; + private PopupMenu mPopupMenu; + public static final String TAG = AgentWebFragment.class.getSimpleName(); + private DownloadingService mDownloadingService; + + public static AgentWebFragment getInstance(String url) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_URL, url); + return getInstance(bundle); + } + + public static AgentWebFragment getInstance(Bundle bundle) { + AgentWebFragment fragment = new AgentWebFragment(); + if (bundle != null) { + fragment.setArguments(bundle); + } + return fragment; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_agentweb, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mAgentWeb = AgentWeb.with(this) + //传入AgentWeb的父控件。 + .setAgentWebParent((LinearLayout) view, -1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)) + //设置进度条颜色与高度,-1为默认值,高度为2,单位为dp。 + .useDefaultIndicator(-1, 3) + //设置 IAgentWebSettings。 + .setAgentWebWebSettings(getSettings()) + //WebViewClient , 与 WebView 使用一致 ,但是请勿获取WebView调用setWebViewClient(xx)方法了,会覆盖AgentWeb DefaultWebClient,同时相应的中间件也会失效。 + .setWebViewClient(mWebViewClient) + //WebChromeClient + .setWebChromeClient(mWebChromeClient) + //设置WebChromeClient中间件,支持多个WebChromeClient,AgentWeb 3.0.0 加入。 + .useMiddlewareWebChrome(getMiddlewareWebChrome()) + //设置WebViewClient中间件,支持多个WebViewClient, AgentWeb 3.0.0 加入。 + .useMiddlewareWebClient(getMiddlewareWebClient()) + //权限拦截 2.0.0 加入。 + .setPermissionInterceptor(mPermissionInterceptor) + //严格模式 Android 4.2.2 以下会放弃注入对象 ,使用AgentWebView没影响。 + .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK) + //自定义UI AgentWeb3.0.0 加入。 + .setAgentWebUIController(new UIController(getActivity())) + //参数1是错误显示的布局,参数2点击刷新控件ID -1表示点击整个布局都刷新, AgentWeb 3.0.0 加入。 + .setMainFrameErrorView(R.layout.agentweb_error_page, -1) + .setWebLayout(getWebLayout()) + .interceptUnkownUrl() + //创建AgentWeb。 + .createAgentWeb() + .ready()//设置 WebSettings。 + //WebView载入该url地址的页面并显示。 + .go(getUrl()); + + if (MyApp.isDebug()) { + AgentWebConfig.debug(); + } + + // 得到 AgentWeb 最底层的控件 + addBackgroundChild(mAgentWeb.getWebCreator().getWebParentLayout()); + + initView(view); + + // AgentWeb 没有把WebView的功能全面覆盖 ,所以某些设置 AgentWeb 没有提供,请从WebView方面入手设置。 + mAgentWeb.getWebCreator().getWebView().setOverScrollMode(WebView.OVER_SCROLL_NEVER); + } + + protected IWebLayout getWebLayout() { + return new WebLayout(getActivity()); + } + + protected void initView(View view) { + mBackImageView = view.findViewById(R.id.iv_back); + mLineView = view.findViewById(R.id.view_line); + mFinishImageView = view.findViewById(R.id.iv_finish); + mTitleTextView = view.findViewById(R.id.toolbar_title); + mBackImageView.setOnClickListener(mOnClickListener); + mFinishImageView.setOnClickListener(mOnClickListener); + mMoreImageView = view.findViewById(R.id.iv_more); + mMoreImageView.setOnClickListener(mOnClickListener); + pageNavigator(View.GONE); + } + + protected void addBackgroundChild(FrameLayout frameLayout) { + TextView textView = new TextView(frameLayout.getContext()); + textView.setText("技术由 AgentWeb 提供"); + textView.setTextSize(16); + textView.setTextColor(Color.parseColor("#727779")); + frameLayout.setBackgroundColor(Color.parseColor("#272b2d")); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(-2, -2); + params.gravity = Gravity.CENTER_HORIZONTAL; + final float scale = frameLayout.getContext().getResources().getDisplayMetrics().density; + params.topMargin = (int) (15 * scale + 0.5f); + frameLayout.addView(textView, 0, params); + } + + + private void pageNavigator(int tag) { + mBackImageView.setVisibility(tag); + mLineView.setVisibility(tag); + } + + private View.OnClickListener mOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.iv_back: + // true表示AgentWeb处理了该事件 + if (!mAgentWeb.back()) { + AgentWebFragment.this.getActivity().finish(); + } + break; + case R.id.iv_finish: + AgentWebFragment.this.getActivity().finish(); + break; + case R.id.iv_more: + showPoPup(v); + break; + default: + break; + + } + } + + }; + + //========================================// + + /** + * 权限申请拦截器 + */ + protected PermissionInterceptor mPermissionInterceptor = new PermissionInterceptor() { + /** + * PermissionInterceptor 能达到 url1 允许授权, url2 拒绝授权的效果。 + * @param url + * @param permissions + * @param action + * @return true 该Url对应页面请求权限进行拦截 ,false 表示不拦截。 + */ + @Override + public boolean intercept(String url, String[] permissions, String action) { + Log.i(TAG, "mUrl:" + url + " permission:" + JsonUtil.toJson(permissions) + " action:" + action); + return false; + } + }; + + //=====================下载============================// + + /** + * 更新于 AgentWeb 4.0.0,下载监听 + */ + protected DownloadListenerAdapter mDownloadListenerAdapter = new DownloadListenerAdapter() { + /** + * + * @param url 下载链接 + * @param userAgent UserAgent + * @param contentDisposition ContentDisposition + * @param mimetype 资源的媒体类型 + * @param contentLength 文件长度 + * @param extra 下载配置 , 用户可以通过 Extra 修改下载icon , 关闭进度条 , 是否强制下载。 + * @return true 表示用户处理了该下载事件 , false 交给 AgentWeb 下载 + */ + @Override + public boolean onStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength, AgentWebDownloader.Extra extra) { + LogUtils.i(TAG, "onStart:" + url); + // 是否开启断点续传 + extra.setOpenBreakPointDownload(true) + //下载通知的icon + .setIcon(R.drawable.ic_file_download_black_24dp) + // 连接的超时时间 + .setConnectTimeOut(6000) + // 以8KB位单位,默认60s ,如果60s内无法从网络流中读满8KB数据,则抛出异常 + .setBlockMaxTime(10 * 60 * 1000) + // 下载的超时时间 + .setDownloadTimeOut(Long.MAX_VALUE) + // 串行下载更节省资源哦 + .setParallelDownload(false) + // false 关闭进度通知 + .setEnableIndicator(true) + // 自定义请求头 + .addHeader("Cookie", "xx") + // 下载完成自动打开 + .setAutoOpen(true) + // 强制下载,不管网络网络类型 + .setForceDownload(true); + return false; + } + + /** + * + * 不需要暂停或者停止下载该方法可以不必实现 + * @param url + * @param downloadingService 用户可以通过 DownloadingService#shutdownNow 终止下载 + */ + @Override + public void onBindService(String url, DownloadingService downloadingService) { + super.onBindService(url, downloadingService); + mDownloadingService = downloadingService; + LogUtils.i(TAG, "onBindService:" + url + " DownloadingService:" + downloadingService); + } + + /** + * 回调onUnbindService方法,让用户释放掉 DownloadingService。 + * @param url + * @param downloadingService + */ + @Override + public void onUnbindService(String url, DownloadingService downloadingService) { + super.onUnbindService(url, downloadingService); + mDownloadingService = null; + LogUtils.i(TAG, "onUnbindService:" + url); + } + + /** + * + * @param url 下载链接 + * @param loaded 已经下载的长度 + * @param length 文件的总大小 + * @param usedTime 耗时 ,单位ms + * 注意该方法回调在子线程 ,线程名 AsyncTask #XX 或者 AgentWeb # XX + */ + @Override + public void onProgress(String url, long loaded, long length, long usedTime) { + int mProgress = (int) ((loaded) / Float.valueOf(length) * 100); + LogUtils.i(TAG, "onProgress:" + mProgress); + super.onProgress(url, loaded, length, usedTime); + } + + /** + * + * @param path 文件的绝对路径 + * @param url 下载地址 + * @param throwable 如果异常,返回给用户异常 + * @return true 表示用户处理了下载完成后续的事件 ,false 默认交给AgentWeb 处理 + */ + @Override + public boolean onResult(String path, String url, Throwable throwable) { + //下载成功 + if (null == throwable) { + //do you work + } else {//下载失败 + + } + // true 不会发出下载完成的通知 , 或者打开文件 + return false; + } + }; + + /** + * @return IAgentWebSettings + */ + public IAgentWebSettings getSettings() { + return new AbsAgentWebSettings() { + private AgentWeb mAgentWeb; + + @Override + protected void bindAgentWebSupport(AgentWeb agentWeb) { + this.mAgentWeb = agentWeb; + } + + /** + * AgentWeb 4.0.0 内部删除了 DownloadListener 监听 ,以及相关API ,将 Download 部分完全抽离出来独立一个库, + * 如果你需要使用 AgentWeb Download 部分 , 请依赖上 compile 'com.just.agentweb:download:4.0.0 , + * 如果你需要监听下载结果,请自定义 AgentWebSetting , New 出 DefaultDownloadImpl,传入DownloadListenerAdapter + * 实现进度或者结果监听,例如下面这个例子,如果你不需要监听进度,或者下载结果,下面 setDownloader 的例子可以忽略。 + * @param webView + * @param downloadListener + * @return WebListenerManager + */ + @Override + public WebListenerManager setDownloader(WebView webView, android.webkit.DownloadListener downloadListener) { + return super.setDownloader(webView, + DefaultDownloadImpl + .create(getActivity(), + webView, + mDownloadListenerAdapter, + mDownloadListenerAdapter, + this.mAgentWeb.getPermissionInterceptor())); + } + }; + } + + //===================WebChromeClient 和 WebViewClient===========================// + /** + * 页面空白,请检查scheme是否加上, scheme://host:port/path?query&query 。 + * + * @return mUrl + */ + public String getUrl() { + String target = ""; + Bundle bundle = getArguments(); + if (bundle != null) { + target = bundle.getString(KEY_URL); + } + + if (TextUtils.isEmpty(target)) { + target = "https://github.com/xuexiangjys"; + } + return target; + } + + protected WebChromeClient mWebChromeClient = new WebChromeClient() { + @Override + public void onProgressChanged(WebView view, int newProgress) { + Log.i(TAG, "onProgressChanged:" + newProgress + " view:" + view); + } + + @Override + public void onReceivedTitle(WebView view, String title) { + super.onReceivedTitle(view, title); + if (mTitleTextView != null && !TextUtils.isEmpty(title)) { + if (title.length() > 10) { + title = title.substring(0, 10).concat("..."); + } + mTitleTextView.setText(title); + } + } + }; + + protected WebViewClient mWebViewClient = new WebViewClient() { + + private HashMap timer = new HashMap<>(); + + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + super.onReceivedError(view, request, error); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return shouldOverrideUrlLoading(view, request.getUrl() + ""); + } + + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + return super.shouldInterceptRequest(view, request); + } + + // + @Override + public boolean shouldOverrideUrlLoading(final WebView view, String url) { + //intent:// scheme的处理 如果返回false , 则交给 DefaultWebClient 处理 , 默认会打开该Activity , 如果Activity不存在则跳到应用市场上去. true 表示拦截 + //例如优酷视频播放 ,intent://play?...package=com.youku.phone;end; + //优酷想唤起自己应用播放该视频 , 下面拦截地址返回 true 则会在应用内 H5 播放 ,禁止优酷唤起播放该视频, 如果返回 false , DefaultWebClient 会根据intent 协议处理 该地址 , 首先匹配该应用存不存在 ,如果存在 , 唤起该应用播放 , 如果不存在 , 则跳到应用市场下载该应用 . + if (url.startsWith("intent://") && url.contains("com.youku.phone")) { + return true; + } + + return false; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + Log.i(TAG, "mUrl:" + url + " onPageStarted target:" + getUrl()); + timer.put(url, System.currentTimeMillis()); + if (url.equals(getUrl())) { + pageNavigator(View.GONE); + } else { + pageNavigator(View.VISIBLE); + } + + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + + if (timer.get(url) != null) { + long overTime = System.currentTimeMillis(); + Long startTime = timer.get(url); + Log.i(TAG, " page mUrl:" + url + " used time:" + (overTime - startTime)); + } + + } + + @Override + public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { + super.onReceivedHttpError(view, request, errorResponse); + } + + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + super.onReceivedError(view, errorCode, description, failingUrl); + } + }; + + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + } + + + //========================菜单功能================================// + + /** + * 打开浏览器 + * + * @param targetUrl 外部浏览器打开的地址 + */ + private void openBrowser(String targetUrl) { + if (TextUtils.isEmpty(targetUrl) || targetUrl.startsWith("file://")) { + XToastUtils.toast(targetUrl + " 该链接无法使用浏览器打开。"); + return; + } + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri uri = Uri.parse(targetUrl); + intent.setData(uri); + startActivity(intent); + } + + + /** + * 显示更多菜单 + * + * @param view 菜单依附在该View下面 + */ + private void showPoPup(View view) { + if (mPopupMenu == null) { + mPopupMenu = new PopupMenu(getContext(), view); + mPopupMenu.inflate(R.menu.menu_toolbar_web); + mPopupMenu.setOnMenuItemClickListener(mOnMenuItemClickListener); + } + mPopupMenu.show(); + } + + /** + * 菜单事件 + */ + private PopupMenu.OnMenuItemClickListener mOnMenuItemClickListener = new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.refresh: + if (mAgentWeb != null) { + mAgentWeb.getUrlLoader().reload(); // 刷新 + } + return true; + + case R.id.copy: + if (mAgentWeb != null) { + toCopy(getContext(), mAgentWeb.getWebCreator().getWebView().getUrl()); + } + return true; + case R.id.default_browser: + if (mAgentWeb != null) { + openBrowser(mAgentWeb.getWebCreator().getWebView().getUrl()); + } + return true; + case R.id.share: + if (mAgentWeb != null) { + shareWebUrl(mAgentWeb.getWebCreator().getWebView().getUrl()); + } + return true; + default: + return false; + } + + } + }; + + /** + * 分享网页链接 + * + * @param url 网页链接 + */ + private void shareWebUrl(String url) { + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_TEXT, url); + shareIntent.setType("text/plain"); + //设置分享列表的标题,并且每次都显示分享列表 + startActivity(Intent.createChooser(shareIntent, "分享到")); + } + + + /** + * 复制字符串 + * + * @param context + * @param text + */ + private void toCopy(Context context, String text) { + ClipboardManager manager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (manager == null) { + return; + } + manager.setPrimaryClip(ClipData.newPlainText(null, text)); + } + + //===================生命周期管理===========================// + + @Override + public void onResume() { + mAgentWeb.getWebLifeCycle().onResume();//恢复 + super.onResume(); + } + + @Override + public void onPause() { + mAgentWeb.getWebLifeCycle().onPause(); //暂停应用内所有WebView , 调用mWebView.resumeTimers();/mAgentWeb.getWebLifeCycle().onResume(); 恢复。 + super.onPause(); + } + + @Override + public boolean onFragmentKeyDown(int keyCode, KeyEvent event) { + return mAgentWeb.handleKeyEvent(keyCode, event); + } + + @Override + public void onDestroyView() { + mAgentWeb.getWebLifeCycle().onDestroy(); + super.onDestroyView(); + } + + //===================中间键===========================// + + + /** + * MiddlewareWebClientBase 是 AgentWeb 3.0.0 提供一个强大的功能, + * 如果用户需要使用 AgentWeb 提供的功能, 不想重写 WebClientView方 + * 法覆盖AgentWeb提供的功能,那么 MiddlewareWebClientBase 是一个 + * 不错的选择 。 + * + * @return + */ + protected MiddlewareWebClientBase getMiddlewareWebClient() { + return new MiddlewareWebViewClient() { + /** + * + * @param view + * @param url + * @return + */ + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + // 拦截 url,不执行 DefaultWebClient#shouldOverrideUrlLoading + if (url.startsWith("agentweb")) { + Log.i(TAG, "agentweb scheme ~"); + return true; + } + // 执行 DefaultWebClient#shouldOverrideUrlLoading + if (super.shouldOverrideUrlLoading(view, url)) { + return true; + } + // do you work + return false; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return super.shouldOverrideUrlLoading(view, request); + } + }; + } + + protected MiddlewareWebChromeBase getMiddlewareWebChrome() { + return new MiddlewareChromeClient() { + }; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/BaseWebViewFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/BaseWebViewFragment.java new file mode 100644 index 0000000..d48d48d --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/BaseWebViewFragment.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.view.KeyEvent; + +import com.just.agentweb.core.AgentWeb; +import com.itrycn.myeasywol.core.BaseFragment; + +/** + * 基础web + * + * @author xuexiang + * @since 2019/5/28 10:22 + */ +public abstract class BaseWebViewFragment extends BaseFragment { + + protected AgentWeb mAgentWeb; + + //===================生命周期管理===========================// + @Override + public void onResume() { + if (mAgentWeb != null) { + //恢复 + mAgentWeb.getWebLifeCycle().onResume(); + } + super.onResume(); + } + + @Override + public void onPause() { + if (mAgentWeb != null) { + //暂停应用内所有WebView , 调用mWebView.resumeTimers();/mAgentWeb.getWebLifeCycle().onResume(); 恢复。 + mAgentWeb.getWebLifeCycle().onPause(); + } + super.onPause(); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return mAgentWeb != null && mAgentWeb.handleKeyEvent(keyCode, event); + } + + @Override + public void onDestroyView() { + if (mAgentWeb != null) { + mAgentWeb.destroy(); + } + super.onDestroyView(); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/FragmentKeyDown.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/FragmentKeyDown.java new file mode 100644 index 0000000..92bc417 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/FragmentKeyDown.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.view.KeyEvent; + +/** + * + * + * @author xuexiang + * @since 2019/1/4 下午11:32 + */ +public interface FragmentKeyDown { + + /** + * fragment按键监听 + * @param keyCode + * @param event + * @return + */ + boolean onFragmentKeyDown(int keyCode, KeyEvent event); +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/LollipopFixedWebView.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/LollipopFixedWebView.java new file mode 100644 index 0000000..e3b23f0 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/LollipopFixedWebView.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Build; +import android.util.AttributeSet; +import android.webkit.WebView; + +/** + * 修复 Android 5.0 & 5.1 打开 WebView 闪退问题: + * 参阅 https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview + */ +@SuppressWarnings("unused") +public class LollipopFixedWebView extends WebView { + public LollipopFixedWebView(Context context) { + super(getFixedContext(context)); + } + + public LollipopFixedWebView(Context context, AttributeSet attrs) { + super(getFixedContext(context), attrs); + } + + public LollipopFixedWebView(Context context, AttributeSet attrs, int defStyleAttr) { + super(getFixedContext(context), attrs, defStyleAttr); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public LollipopFixedWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(getFixedContext(context), attrs, defStyleAttr, defStyleRes); + } + + public LollipopFixedWebView(Context context, AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) { + super(getFixedContext(context), attrs, defStyleAttr, privateBrowsing); + } + + public static Context getFixedContext(Context context) { + if (isLollipopWebViewBug()) { + // Avoid crashing on Android 5 and 6 (API level 21 to 23) + return context.createConfigurationContext(new Configuration()); + } + return context; + } + + public static boolean isLollipopWebViewBug() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT < Build.VERSION_CODES.M; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/MiddlewareChromeClient.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/MiddlewareChromeClient.java new file mode 100644 index 0000000..48424dd --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/MiddlewareChromeClient.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.util.Log; +import android.webkit.JsResult; +import android.webkit.WebView; + +import com.just.agentweb.core.client.MiddlewareWebChromeBase; + +/** + * WebChrome(WebChromeClient主要辅助WebView处理JavaScript的对话框、网站图片、网站title、加载进度等)中间件 + * 【浏览器】 + * @author xuexiang + * @since 2019/1/4 下午11:31 + */ +public class MiddlewareChromeClient extends MiddlewareWebChromeBase { + + public MiddlewareChromeClient() { + + } + + @Override + public boolean onJsAlert(WebView view, String url, String message, JsResult result) { + Log.i("Info", "onJsAlert:" + url); + return super.onJsAlert(view, url, message, result); + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + Log.i("Info", "onProgressChanged:"); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/MiddlewareWebViewClient.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/MiddlewareWebViewClient.java new file mode 100644 index 0000000..a9407e9 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/MiddlewareWebViewClient.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.net.Uri; +import android.os.Build; +import android.util.Log; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; + +import androidx.annotation.RequiresApi; + +import com.just.agentweb.core.client.MiddlewareWebClientBase; +import com.itrycn.myeasywol.R; +import com.xuexiang.xui.utils.ResUtils; + +import static com.itrycn.myeasywol.core.webview.WebViewInterceptDialog.APP_LINK_HOST; + +/** + * 【网络请求、加载】 + * WebClient(WebViewClient 这个类主要帮助WebView处理各种通知、url加载,请求时间的)中间件 + *

+ *

+ * 方法的执行顺序,例如下面用了7个中间件一个 WebViewClient + *

+ * .useMiddlewareWebClient(getMiddlewareWebClient()) // 1 + * .useMiddlewareWebClient(getMiddlewareWebClient()) // 2 + * .useMiddlewareWebClient(getMiddlewareWebClient()) // 3 + * .useMiddlewareWebClient(getMiddlewareWebClient()) // 4 + * .useMiddlewareWebClient(getMiddlewareWebClient()) // 5 + * .useMiddlewareWebClient(getMiddlewareWebClient()) // 6 + * .useMiddlewareWebClient(getMiddlewareWebClient()) // 7 + * DefaultWebClient // 8 + * .setWebViewClient(mWebViewClient) // 9 + *

+ *

+ * 典型的洋葱模型 + * 对象内部的方法执行顺序: 1->2->3->4->5->6->7->8->9->8->7->6->5->4->3->2->1 + *

+ *

+ * 中断中间件的执行, 删除super.methodName(...) 这行即可 + *

+ * 这里主要是做去广告的工作 + */ +public class MiddlewareWebViewClient extends MiddlewareWebClientBase { + + public MiddlewareWebViewClient() { + } + + private static int count = 1; + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + Log.i("Info", "MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + request.getUrl().toString() + " c:" + (count++)); + if (shouldOverrideUrlLoadingByApp(view, request.getUrl().toString())) { + return true; + } + return super.shouldOverrideUrlLoading(view, request); + + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + Log.i("Info", "MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + url + " c:" + (count++)); + if (shouldOverrideUrlLoadingByApp(view, url)) { + return true; + } + return super.shouldOverrideUrlLoading(view, url); + } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + url = url.toLowerCase(); + if (!hasAdUrl(url)) { + //正常加载 + return super.shouldInterceptRequest(view, url); + } else { + //含有广告资源屏蔽请求 + return new WebResourceResponse(null, null, null); + } + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + String url = request.getUrl().toString().toLowerCase(); + if (!hasAdUrl(url)) { + //正常加载 + return super.shouldInterceptRequest(view, request); + } else { + //含有广告资源屏蔽请求 + return new WebResourceResponse(null, null, null); + } + } + + /** + * 判断是否存在广告的链接 + * + * @param url + * @return + */ + private static boolean hasAdUrl(String url) { + return false; + } + + + /** + * 根据url的scheme处理跳转第三方app的业务,true代表拦截,false代表不拦截 + */ + private boolean shouldOverrideUrlLoadingByApp(WebView webView, final String url) { + if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp")) { + //不拦截http, https, ftp的请求 + Uri uri = Uri.parse(url); + if (uri != null && !(APP_LINK_HOST.equals(uri.getHost()) + //防止xui官网被拦截 + && url.contains("xpage"))) { + return false; + } + } + + WebViewInterceptDialog.show(url); + return true; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/UIController.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/UIController.java new file mode 100644 index 0000000..a867bec --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/UIController.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.app.Activity; +import android.os.Handler; +import android.util.Log; +import android.webkit.WebView; + +import com.just.agentweb.core.web.AgentWebUIControllerImplBase; + +import java.lang.ref.WeakReference; + +/** + * 如果你需要修改某一个AgentWeb 内部的某一个弹窗 ,请看下面的例子 + * 注意写法一定要参照 DefaultUIController 的写法 ,因为UI自由定制,但是回调的方式是固定的,并且一定要回调。 + * + * @author xuexiang + * @since 2019-10-30 23:18 + */ +public class UIController extends AgentWebUIControllerImplBase { + + private WeakReference mActivity; + + public UIController(Activity activity) { + mActivity = new WeakReference<>(activity); + } + + @Override + public void onShowMessage(String message, String from) { + super.onShowMessage(message, from); + Log.i(TAG, "message:" + message); + } + + @Override + public void onSelectItemsPrompt(WebView view, String url, String[] items, Handler.Callback callback) { + // 使用默认的UI + super.onSelectItemsPrompt(view, url, items, callback); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/WebLayout.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/WebLayout.java new file mode 100644 index 0000000..4fb2646 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/WebLayout.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.app.Activity; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.webkit.WebView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.just.agentweb.widget.IWebLayout; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.itrycn.myeasywol.R; + +/** + * 定义支持下来回弹的WebView + * + * @author xuexiang + * @since 2019/1/5 上午2:01 + */ +public class WebLayout implements IWebLayout { + + private final SmartRefreshLayout mSmartRefreshLayout; + private WebView mWebView; + + public WebLayout(Activity activity) { + mSmartRefreshLayout = (SmartRefreshLayout) LayoutInflater.from(activity).inflate(R.layout.fragment_pulldown_web, null); + mWebView = mSmartRefreshLayout.findViewById(R.id.webView); + } + + @NonNull + @Override + public ViewGroup getLayout() { + return mSmartRefreshLayout; + } + + @Nullable + @Override + public WebView getWebView() { + return mWebView; + } + + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/WebViewInterceptDialog.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/WebViewInterceptDialog.java new file mode 100644 index 0000000..42282d9 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/WebViewInterceptDialog.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xui.utils.ResUtils; +import com.xuexiang.xui.widget.dialog.DialogLoader; +import com.xuexiang.xutil.XUtil; +import com.xuexiang.xutil.app.ActivityUtils; + +import java.net.URISyntaxException; + +/** + * WebView拦截提示 + * + * @author xuexiang + * @since 2019-10-21 9:51 + */ +public class WebViewInterceptDialog extends AppCompatActivity implements DialogInterface.OnDismissListener { + + private static final String KEY_INTERCEPT_URL = "key_intercept_url"; + + // TODO: 2019-10-30 这里修改你的applink + public static final String APP_LINK_HOST = "xuexiangjys.club"; + public static final String APP_LINK_ACTION = "com.xuexiang.xui.applink"; + + + /** + * 显示WebView拦截提示 + * + * @param url 需要拦截处理的url + */ + public static void show(String url) { + ActivityUtils.startActivity(WebViewInterceptDialog.class, KEY_INTERCEPT_URL, url); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + String url = getIntent().getStringExtra(KEY_INTERCEPT_URL); + + DialogLoader.getInstance().showConfirmDialog( + this, + getOpenTitle(url), + ResUtils.getString(R.string.lab_yes), + (dialog, which) -> { + dialog.dismiss(); + if (isAppLink(url)) { + openAppLink(this, url); + } else { + openApp(url); + } + }, + ResUtils.getString(R.string.lab_no), + (dialog, which) -> dialog.dismiss() + ).setOnDismissListener(this); + + } + + private String getOpenTitle(String url) { + String scheme = getScheme(url); + if ("mqqopensdkapi".equals(scheme)) { + return "是否允许页面打开\"QQ\"?"; + } else { + return ResUtils.getString(R.string.lab_open_third_app); + } + } + + private String getScheme(String url) { + try { + Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); + return intent.getScheme(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return ""; + } + + private boolean isAppLink(String url) { + Uri uri = Uri.parse(url); + return uri != null + && APP_LINK_HOST.equals(uri.getHost()) + && (url.startsWith("http") || url.startsWith("https")); + } + + + private void openApp(String url) { + Intent intent; + try { + intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); + XUtil.getContext().startActivity(intent); + } catch (Exception e) { + XToastUtils.error("您所打开的第三方App未安装!"); + } + } + + private void openAppLink(Context context, String url) { + try { + Intent intent = new Intent(APP_LINK_ACTION); + intent.setData(Uri.parse(url)); + context.startActivity(intent); + } catch (Exception e) { + XToastUtils.error("您所打开的第三方App未安装!"); + } + } + + @Override + public void onDismiss(DialogInterface dialog) { + finish(); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/XPageWebViewFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/XPageWebViewFragment.java new file mode 100644 index 0000000..0d3a328 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/core/webview/XPageWebViewFragment.java @@ -0,0 +1,690 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.core.webview; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Message; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.appcompat.widget.PopupMenu; +import androidx.fragment.app.Fragment; + +import com.just.agentweb.action.PermissionInterceptor; +import com.just.agentweb.core.AgentWeb; +import com.just.agentweb.core.client.DefaultWebClient; +import com.just.agentweb.core.client.MiddlewareWebChromeBase; +import com.just.agentweb.core.client.MiddlewareWebClientBase; +import com.just.agentweb.core.client.WebListenerManager; +import com.just.agentweb.core.web.AbsAgentWebSettings; +import com.just.agentweb.core.web.AgentWebConfig; +import com.just.agentweb.core.web.IAgentWebSettings; +import com.just.agentweb.download.AgentWebDownloader; +import com.just.agentweb.download.DefaultDownloadImpl; +import com.just.agentweb.download.DownloadListenerAdapter; +import com.just.agentweb.download.DownloadingService; +import com.just.agentweb.widget.IWebLayout; +import com.itrycn.myeasywol.MyApp; +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xpage.base.XPageActivity; +import com.xuexiang.xpage.base.XPageFragment; +import com.xuexiang.xpage.core.PageOption; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xutil.common.logger.Logger; +import com.xuexiang.xutil.net.JsonUtil; + +import java.util.HashMap; + +import butterknife.BindView; +import butterknife.OnClick; + +import static com.itrycn.myeasywol.core.webview.AgentWebFragment.KEY_URL; + +/** + * 使用XPageFragment + * + * @author xuexiang + * @since 2019-05-26 18:15 + */ +@Page(params = {KEY_URL}) +public class XPageWebViewFragment extends BaseFragment { + + @BindView(R.id.iv_back) + AppCompatImageView mIvBack; + @BindView(R.id.view_line) + View mLineView; + @BindView(R.id.toolbar_title) + TextView mTvTitle; + + protected AgentWeb mAgentWeb; + private PopupMenu mPopupMenu; + + private DownloadingService mDownloadingService; + + /** + * 打开网页 + * + * @param xPageActivity + * @param url + * @return + */ + public static Fragment openUrl(XPageActivity xPageActivity, String url) { + return PageOption.to(XPageWebViewFragment.class) + .putString(KEY_URL, url) + .open(xPageActivity); + } + + /** + * 打开网页 + * + * @param fragment + * @param url + * @return + */ + public static Fragment openUrl(XPageFragment fragment, String url) { + return PageOption.to(XPageWebViewFragment.class) + .setNewActivity(true) + .putString(KEY_URL, url) + .open(fragment); + } + + @Override + protected void handleUIMessage(Message msg) { + + } + + @Override + protected void handleWorkerMessage(Message msg) { + + } + + @Override + protected TitleBar initTitle() { + return null; + } + + /** + * 布局的资源id + * + * @return + */ + @Override + protected int getLayoutId() { + return R.layout.fragment_agentweb; + } + + /** + * 初始化控件 + */ + @Override + protected void initViews() { + mAgentWeb = AgentWeb.with(this) + //传入AgentWeb的父控件。 + .setAgentWebParent((LinearLayout) getRootView(), -1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)) + //设置进度条颜色与高度,-1为默认值,高度为2,单位为dp。 + .useDefaultIndicator(-1, 3) + //设置 IAgentWebSettings。 + .setAgentWebWebSettings(getSettings()) + //WebViewClient , 与 WebView 使用一致 ,但是请勿获取WebView调用setWebViewClient(xx)方法了,会覆盖AgentWeb DefaultWebClient,同时相应的中间件也会失效。 + .setWebViewClient(mWebViewClient) + //WebChromeClient + .setWebChromeClient(mWebChromeClient) + //设置WebChromeClient中间件,支持多个WebChromeClient,AgentWeb 3.0.0 加入。 + .useMiddlewareWebChrome(getMiddlewareWebChrome()) + //设置WebViewClient中间件,支持多个WebViewClient, AgentWeb 3.0.0 加入。 + .useMiddlewareWebClient(getMiddlewareWebClient()) + //权限拦截 2.0.0 加入。 + .setPermissionInterceptor(mPermissionInterceptor) + //严格模式 Android 4.2.2 以下会放弃注入对象 ,使用AgentWebView没影响。 + .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK) + //自定义UI AgentWeb3.0.0 加入。 + .setAgentWebUIController(new UIController(getActivity())) + //参数1是错误显示的布局,参数2点击刷新控件ID -1表示点击整个布局都刷新, AgentWeb 3.0.0 加入。 + .setMainFrameErrorView(R.layout.agentweb_error_page, -1) + .setWebLayout(getWebLayout()) + //打开其他页面时,弹窗质询用户前往其他应用 AgentWeb 3.0.0 加入。 + .setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.DISALLOW) + //拦截找不到相关页面的Url AgentWeb 3.0.0 加入。 + .interceptUnkownUrl() + //创建AgentWeb。 + .createAgentWeb() + .ready()//设置 WebSettings。 + //WebView载入该url地址的页面并显示。 + .go(getUrl()); + + if (MyApp.isDebug()) { + AgentWebConfig.debug(); + } + + pageNavigator(View.GONE); + // 得到 AgentWeb 最底层的控件 + addBackgroundChild(mAgentWeb.getWebCreator().getWebParentLayout()); + + // AgentWeb 没有把WebView的功能全面覆盖 ,所以某些设置 AgentWeb 没有提供,请从WebView方面入手设置。 + mAgentWeb.getWebCreator().getWebView().setOverScrollMode(WebView.OVER_SCROLL_NEVER); + } + + protected IWebLayout getWebLayout() { + return new WebLayout(getActivity()); + } + + protected void addBackgroundChild(FrameLayout frameLayout) { + TextView textView = new TextView(frameLayout.getContext()); + textView.setText("技术由 AgentWeb 提供"); + textView.setTextSize(16); + textView.setTextColor(Color.parseColor("#727779")); + frameLayout.setBackgroundColor(Color.parseColor("#272b2d")); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(-2, -2); + params.gravity = Gravity.CENTER_HORIZONTAL; + final float scale = frameLayout.getContext().getResources().getDisplayMetrics().density; + params.topMargin = (int) (15 * scale + 0.5f); + frameLayout.addView(textView, 0, params); + } + + + private void pageNavigator(int tag) { + //返回的导航按钮 + mIvBack.setVisibility(tag); + mLineView.setVisibility(tag); + } + + @SingleClick + @OnClick({R.id.iv_back, R.id.iv_finish, R.id.iv_more}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.iv_back: + // true表示AgentWeb处理了该事件 + if (!mAgentWeb.back()) { + popToBack(); + } + break; + case R.id.iv_finish: + popToBack(); + break; + case R.id.iv_more: + showPoPup(view); + break; + default: + break; + } + } + + //=====================下载============================// + + /** + * 更新于 AgentWeb 4.0.0,下载监听 + */ + protected DownloadListenerAdapter mDownloadListenerAdapter = new DownloadListenerAdapter() { + /** + * + * @param url 下载链接 + * @param userAgent UserAgent + * @param contentDisposition ContentDisposition + * @param mimeType 资源的媒体类型 + * @param contentLength 文件长度 + * @param extra 下载配置 , 用户可以通过 Extra 修改下载icon , 关闭进度条 , 是否强制下载。 + * @return true 表示用户处理了该下载事件 , false 交给 AgentWeb 下载 + */ + @Override + public boolean onStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength, AgentWebDownloader.Extra extra) { + Logger.i("onStart:" + url); + // 是否开启断点续传 + extra.setOpenBreakPointDownload(true) + //下载通知的icon + .setIcon(R.drawable.ic_file_download_black_24dp) + // 连接的超时时间 + .setConnectTimeOut(6000) + // 以8KB位单位,默认60s ,如果60s内无法从网络流中读满8KB数据,则抛出异常 + .setBlockMaxTime(10 * 60 * 1000) + // 下载的超时时间 + .setDownloadTimeOut(Long.MAX_VALUE) + // 串行下载更节省资源哦 + .setParallelDownload(false) + // false 关闭进度通知 + .setEnableIndicator(true) + // 自定义请求头 + .addHeader("Cookie", "xx") + // 下载完成自动打开 + .setAutoOpen(true) + // 强制下载,不管网络网络类型 + .setForceDownload(true); + return false; + } + + /** + * + * 不需要暂停或者停止下载该方法可以不必实现 + * @param url + * @param downloadingService 用户可以通过 DownloadingService#shutdownNow 终止下载 + */ + @Override + public void onBindService(String url, DownloadingService downloadingService) { + super.onBindService(url, downloadingService); + mDownloadingService = downloadingService; + Logger.i("onBindService:" + url + " DownloadingService:" + downloadingService); + } + + /** + * 回调onUnbindService方法,让用户释放掉 DownloadingService。 + * @param url + * @param downloadingService + */ + @Override + public void onUnbindService(String url, DownloadingService downloadingService) { + super.onUnbindService(url, downloadingService); + mDownloadingService = null; + Logger.i("onUnbindService:" + url); + } + + /** + * + * @param url 下载链接 + * @param loaded 已经下载的长度 + * @param length 文件的总大小 + * @param usedTime 耗时 ,单位ms + * 注意该方法回调在子线程 ,线程名 AsyncTask #XX 或者 AgentWeb # XX + */ + @Override + public void onProgress(String url, long loaded, long length, long usedTime) { + int mProgress = (int) ((loaded) / (float) length * 100); + Logger.i("onProgress:" + mProgress); + super.onProgress(url, loaded, length, usedTime); + } + + /** + * + * @param path 文件的绝对路径 + * @param url 下载地址 + * @param throwable 如果异常,返回给用户异常 + * @return true 表示用户处理了下载完成后续的事件 ,false 默认交给AgentWeb 处理 + */ + @Override + public boolean onResult(String path, String url, Throwable throwable) { + //下载成功 + if (null == throwable) { + //do you work + } else {//下载失败 + + } + // true 不会发出下载完成的通知 , 或者打开文件 + return false; + } + }; + + /** + * 下载服务设置 + * + * @return IAgentWebSettings + */ + public IAgentWebSettings getSettings() { + return new AbsAgentWebSettings() { + private AgentWeb mAgentWeb; + + @Override + protected void bindAgentWebSupport(AgentWeb agentWeb) { + this.mAgentWeb = agentWeb; + } + + /** + * AgentWeb 4.0.0 内部删除了 DownloadListener 监听 ,以及相关API ,将 Download 部分完全抽离出来独立一个库, + * 如果你需要使用 AgentWeb Download 部分 , 请依赖上 compile 'com.just.agentweb:download:4.0.0 , + * 如果你需要监听下载结果,请自定义 AgentWebSetting , New 出 DefaultDownloadImpl,传入DownloadListenerAdapter + * 实现进度或者结果监听,例如下面这个例子,如果你不需要监听进度,或者下载结果,下面 setDownloader 的例子可以忽略。 + * @param webView + * @param downloadListener + * @return WebListenerManager + */ + @Override + public WebListenerManager setDownloader(WebView webView, android.webkit.DownloadListener downloadListener) { + return super.setDownloader(webView, + DefaultDownloadImpl + .create(getActivity(), + webView, + mDownloadListenerAdapter, + mDownloadListenerAdapter, + mAgentWeb.getPermissionInterceptor())); + } + }; + } + + //===================WebChromeClient 和 WebViewClient===========================// + + /** + * 页面空白,请检查scheme是否加上, scheme://host:port/path?query&query 。 + * + * @return mUrl + */ + public String getUrl() { + String target = ""; + Bundle bundle = getArguments(); + if (bundle != null) { + target = bundle.getString(KEY_URL); + } + + if (TextUtils.isEmpty(target)) { + target = "https://github.com/xuexiangjys"; + } + return target; + } + + /** + * 和浏览器相关,包括和JS的交互 + */ + protected WebChromeClient mWebChromeClient = new WebChromeClient() { + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + //网页加载进度 + } + @Override + public void onReceivedTitle(WebView view, String title) { + super.onReceivedTitle(view, title); + if (mTvTitle != null && !TextUtils.isEmpty(title)) { + if (title.length() > 10) { + title = title.substring(0, 10).concat("..."); + } + mTvTitle.setText(title); + } + } + }; + + /** + * 和网页url加载相关,统计加载时间 + */ + protected WebViewClient mWebViewClient = new WebViewClient() { + private HashMap mTimer = new HashMap<>(); + + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + super.onReceivedError(view, request, error); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return shouldOverrideUrlLoading(view, request.getUrl() + ""); + } + + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + return super.shouldInterceptRequest(view, request); + } + @Override + public boolean shouldOverrideUrlLoading(final WebView view, String url) { + //intent:// scheme的处理 如果返回false , 则交给 DefaultWebClient 处理 , 默认会打开该Activity , 如果Activity不存在则跳到应用市场上去. true 表示拦截 + //例如优酷视频播放 ,intent://play?...package=com.youku.phone;end; + //优酷想唤起自己应用播放该视频 , 下面拦截地址返回 true 则会在应用内 H5 播放 ,禁止优酷唤起播放该视频, 如果返回 false , DefaultWebClient 会根据intent 协议处理 该地址 , 首先匹配该应用存不存在 ,如果存在 , 唤起该应用播放 , 如果不存在 , 则跳到应用市场下载该应用 . + if (url.startsWith("intent://") && url.contains("com.youku.phone")) { + return true; + } + return false; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + mTimer.put(url, System.currentTimeMillis()); + if (url.equals(getUrl())) { + pageNavigator(View.GONE); + } else { + pageNavigator(View.VISIBLE); + } + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + if (mTimer.get(url) != null) { + long overTime = System.currentTimeMillis(); + Long startTime = mTimer.get(url); + //统计页面的使用时长 + Logger.i(" page mUrl:" + url + " used time:" + (overTime - startTime)); + } + } + + @Override + public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { + super.onReceivedHttpError(view, request, errorResponse); + } + + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + super.onReceivedError(view, errorCode, description, failingUrl); + } + }; + + //=====================菜单========================// + + /** + * 显示更多菜单 + * + * @param view 菜单依附在该View下面 + */ + private void showPoPup(View view) { + if (mPopupMenu == null) { + mPopupMenu = new PopupMenu(getContext(), view); + mPopupMenu.inflate(R.menu.menu_toolbar_web); + mPopupMenu.setOnMenuItemClickListener(mOnMenuItemClickListener); + } + mPopupMenu.show(); + } + + /** + * 菜单事件 + */ + private PopupMenu.OnMenuItemClickListener mOnMenuItemClickListener = new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.refresh: + if (mAgentWeb != null) { + mAgentWeb.getUrlLoader().reload(); // 刷新 + } + return true; + case R.id.copy: + if (mAgentWeb != null) { + toCopy(getContext(), mAgentWeb.getWebCreator().getWebView().getUrl()); + } + return true; + case R.id.default_browser: + if (mAgentWeb != null) { + openBrowser(mAgentWeb.getWebCreator().getWebView().getUrl()); + } + return true; + case R.id.share: + if (mAgentWeb != null) { + shareWebUrl(mAgentWeb.getWebCreator().getWebView().getUrl()); + } + return true; + default: + return false; + } + + } + }; + + /** + * 打开浏览器 + * + * @param targetUrl 外部浏览器打开的地址 + */ + private void openBrowser(String targetUrl) { + if (TextUtils.isEmpty(targetUrl) || targetUrl.startsWith("file://")) { + XToastUtils.toast(targetUrl + " 该链接无法使用浏览器打开。"); + return; + } + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri uri = Uri.parse(targetUrl); + intent.setData(uri); + startActivity(intent); + } + + /** + * 分享网页链接 + * + * @param url 网页链接 + */ + private void shareWebUrl(String url) { + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_TEXT, url); + shareIntent.setType("text/plain"); + //设置分享列表的标题,并且每次都显示分享列表 + startActivity(Intent.createChooser(shareIntent, "分享到")); + } + + /** + * 复制字符串 + * + * @param context + * @param text + */ + private void toCopy(Context context, String text) { + ClipboardManager manager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (manager == null) { + return; + } + manager.setPrimaryClip(ClipData.newPlainText(null, text)); + } + + //===================生命周期管理===========================// + + @Override + public void onResume() { + if (mAgentWeb != null) { + mAgentWeb.getWebLifeCycle().onResume();//恢复 + } + super.onResume(); + } + + @Override + public void onPause() { + if (mAgentWeb != null) { + mAgentWeb.getWebLifeCycle().onPause(); //暂停应用内所有WebView , 调用mWebView.resumeTimers();/mAgentWeb.getWebLifeCycle().onResume(); 恢复。 + } + super.onPause(); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return mAgentWeb != null && mAgentWeb.handleKeyEvent(keyCode, event); + } + + @Override + public void onDestroyView() { + if (mAgentWeb != null) { + mAgentWeb.destroy(); + } + super.onDestroyView(); + } + + + //===================中间键===========================// + + + /** + * MiddlewareWebClientBase 是 AgentWeb 3.0.0 提供一个强大的功能, + * 如果用户需要使用 AgentWeb 提供的功能, 不想重写 WebClientView方 + * 法覆盖AgentWeb提供的功能,那么 MiddlewareWebClientBase 是一个 + * 不错的选择 。 + * + * @return + */ + protected MiddlewareWebClientBase getMiddlewareWebClient() { + return new MiddlewareWebViewClient() { + /** + * + * @param view + * @param url + * @return + */ + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + // 拦截 url,不执行 DefaultWebClient#shouldOverrideUrlLoading + if (url.startsWith("agentweb")) { + return true; + } + // 执行 DefaultWebClient#shouldOverrideUrlLoading + if (super.shouldOverrideUrlLoading(view, url)) { + return true; + } + // do you work + return false; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return super.shouldOverrideUrlLoading(view, request); + } + }; + } + + protected MiddlewareWebChromeBase getMiddlewareWebChrome() { + return new MiddlewareChromeClient() { + }; + } + + /** + * 权限申请拦截器 + */ + protected PermissionInterceptor mPermissionInterceptor = new PermissionInterceptor() { + /** + * PermissionInterceptor 能达到 url1 允许授权, url2 拒绝授权的效果。 + * @param url + * @param permissions + * @param action + * @return true 该Url对应页面请求权限进行拦截 ,false 表示不拦截。 + */ + @Override + public boolean intercept(String url, String[] permissions, String action) { + Logger.i("mUrl:" + url + " permission:" + JsonUtil.toJson(permissions) + " action:" + action); + return false; + } + }; + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/DBHelper.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/DBHelper.java new file mode 100644 index 0000000..02f6626 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/DBHelper.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.db; + +import android.content.Context; + +import com.itrycn.myeasywol.db.dao.DaoMaster; +import com.itrycn.myeasywol.db.dao.DaoSession; + +import org.greenrobot.greendao.database.Database; +import org.greenrobot.greendao.identityscope.IdentityScopeType; + +public class DBHelper { + private final String DB_NAME = "happyplayer.db"; + private static DBHelper _DBHelper; + private static UpdateOpenHelper mDevOpenHelper; + private static DaoMaster mDaoMaster; + private static DaoSession mDaoSession; + private static String mPassword; + + private DBHelper(Context context) { + init(context); + } + + private void init(Context context) { + mPassword = "itrycn2021"; + // 初始化数据库信息 + mDevOpenHelper = new UpdateOpenHelper(context, DB_NAME, null); + getDaoMaster(); + getDaoSession(); + } + + public static DBHelper getInstance(Context context) { + if (null == _DBHelper) { + synchronized (DBHelper.class) { + if (null == _DBHelper) { + _DBHelper = new DBHelper(context); + } + } + } + return _DBHelper; + } + + /** + * 获取可写数据库 + * + * @return + */ + public Database getWritableDatabase() { + return mDevOpenHelper.getEncryptedWritableDb(mPassword); +// return mDevOpenHelper.getWritableDb(); + } + + /** + * 获取DaoMaster + * + * @return + */ + private DaoMaster getDaoMaster() { + if (null == mDaoMaster) { + synchronized (DBHelper.class) { + if (null == mDaoMaster) { + mDaoMaster = new DaoMaster(getWritableDatabase()); + } + } + } + return mDaoMaster; + } + + /** + * 获取DaoSession + * + * @return + */ + public DaoSession getDaoSession() { + if (null == mDaoSession) { + synchronized (DBHelper.class) { + mDaoSession = getDaoMaster().newSession(IdentityScopeType.None); + } + } + + return mDaoSession; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/MyAdapterItem.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/MyAdapterItem.java new file mode 100644 index 0000000..f28db43 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/MyAdapterItem.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.db; + +import android.graphics.drawable.Drawable; + +import com.xuexiang.xui.adapter.simple.AdapterItem; + +public class MyAdapterItem extends AdapterItem { + + public MyAdapterItem(CharSequence title) { + super(title); + } + public MyAdapterItem(CharSequence title, Drawable icon) { + super(title,icon); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/UpdateOpenHelper.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/UpdateOpenHelper.java new file mode 100644 index 0000000..ed29d4b --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/UpdateOpenHelper.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.db; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; + +import com.itrycn.myeasywol.db.dao.DaoMaster; + +import org.greenrobot.greendao.database.Database; + +/** + * 更新 + */ +public class UpdateOpenHelper extends DaoMaster.OpenHelper { + + public UpdateOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { + super(context, name, factory); + } + + /** + * 数据库升级 + * + * @param db + * @param oldVersion + * @param newVersion + */ + @Override + public void onUpgrade(Database db, int oldVersion, int newVersion) { + if (oldVersion < newVersion) { + //操作数据库的更新 有几个表升级都可以传入到下面 + //MigrationHelper.getInstance().migrate(db, AudioInfoDao.class); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/DaoMaster.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/DaoMaster.java new file mode 100644 index 0000000..cafc476 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/DaoMaster.java @@ -0,0 +1,99 @@ +package com.itrycn.myeasywol.db.dao; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; +import android.util.Log; + +import org.greenrobot.greendao.AbstractDaoMaster; +import org.greenrobot.greendao.database.StandardDatabase; +import org.greenrobot.greendao.database.Database; +import org.greenrobot.greendao.database.DatabaseOpenHelper; +import org.greenrobot.greendao.identityscope.IdentityScopeType; + + +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. +/** + * Master of DAO (schema version 2): knows all DAOs. + */ +public class DaoMaster extends AbstractDaoMaster { + public static final int SCHEMA_VERSION = 2; + + /** Creates underlying database table using DAOs. */ + public static void createAllTables(Database db, boolean ifNotExists) { + PCInfoDao.createTable(db, ifNotExists); + ServerInfoDao.createTable(db, ifNotExists); + } + + /** Drops underlying database table using DAOs. */ + public static void dropAllTables(Database db, boolean ifExists) { + PCInfoDao.dropTable(db, ifExists); + ServerInfoDao.dropTable(db, ifExists); + } + + /** + * WARNING: Drops all table on Upgrade! Use only during development. + * Convenience method using a {@link DevOpenHelper}. + */ + public static DaoSession newDevSession(Context context, String name) { + Database db = new DevOpenHelper(context, name).getWritableDb(); + DaoMaster daoMaster = new DaoMaster(db); + return daoMaster.newSession(); + } + + public DaoMaster(SQLiteDatabase db) { + this(new StandardDatabase(db)); + } + + public DaoMaster(Database db) { + super(db, SCHEMA_VERSION); + registerDaoClass(PCInfoDao.class); + registerDaoClass(ServerInfoDao.class); + } + + public DaoSession newSession() { + return new DaoSession(db, IdentityScopeType.Session, daoConfigMap); + } + + public DaoSession newSession(IdentityScopeType type) { + return new DaoSession(db, type, daoConfigMap); + } + + /** + * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} - + */ + public static abstract class OpenHelper extends DatabaseOpenHelper { + public OpenHelper(Context context, String name) { + super(context, name, SCHEMA_VERSION); + } + + public OpenHelper(Context context, String name, CursorFactory factory) { + super(context, name, factory, SCHEMA_VERSION); + } + + @Override + public void onCreate(Database db) { + Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); + createAllTables(db, false); + } + } + + /** WARNING: Drops all table on Upgrade! Use only during development. */ + public static class DevOpenHelper extends OpenHelper { + public DevOpenHelper(Context context, String name) { + super(context, name); + } + + public DevOpenHelper(Context context, String name, CursorFactory factory) { + super(context, name, factory); + } + + @Override + public void onUpgrade(Database db, int oldVersion, int newVersion) { + Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); + dropAllTables(db, true); + onCreate(db); + } + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/DaoSession.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/DaoSession.java new file mode 100644 index 0000000..a466934 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/DaoSession.java @@ -0,0 +1,62 @@ +package com.itrycn.myeasywol.db.dao; + +import java.util.Map; + +import org.greenrobot.greendao.AbstractDao; +import org.greenrobot.greendao.AbstractDaoSession; +import org.greenrobot.greendao.database.Database; +import org.greenrobot.greendao.identityscope.IdentityScopeType; +import org.greenrobot.greendao.internal.DaoConfig; + +import com.itrycn.myeasywol.db.entity.PCInfo; +import com.itrycn.myeasywol.db.entity.ServerInfo; + +import com.itrycn.myeasywol.db.dao.PCInfoDao; +import com.itrycn.myeasywol.db.dao.ServerInfoDao; + +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. + +/** + * {@inheritDoc} + * + * @see org.greenrobot.greendao.AbstractDaoSession + */ +public class DaoSession extends AbstractDaoSession { + + private final DaoConfig pCInfoDaoConfig; + private final DaoConfig serverInfoDaoConfig; + + private final PCInfoDao pCInfoDao; + private final ServerInfoDao serverInfoDao; + + public DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig> + daoConfigMap) { + super(db); + + pCInfoDaoConfig = daoConfigMap.get(PCInfoDao.class).clone(); + pCInfoDaoConfig.initIdentityScope(type); + + serverInfoDaoConfig = daoConfigMap.get(ServerInfoDao.class).clone(); + serverInfoDaoConfig.initIdentityScope(type); + + pCInfoDao = new PCInfoDao(pCInfoDaoConfig, this); + serverInfoDao = new ServerInfoDao(serverInfoDaoConfig, this); + + registerDao(PCInfo.class, pCInfoDao); + registerDao(ServerInfo.class, serverInfoDao); + } + + public void clear() { + pCInfoDaoConfig.clearIdentityScope(); + serverInfoDaoConfig.clearIdentityScope(); + } + + public PCInfoDao getPCInfoDao() { + return pCInfoDao; + } + + public ServerInfoDao getServerInfoDao() { + return serverInfoDao; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/PCInfoDao.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/PCInfoDao.java new file mode 100644 index 0000000..2d0ab1b --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/PCInfoDao.java @@ -0,0 +1,177 @@ +package com.itrycn.myeasywol.db.dao; + +import android.database.Cursor; +import android.database.sqlite.SQLiteStatement; + +import org.greenrobot.greendao.AbstractDao; +import org.greenrobot.greendao.Property; +import org.greenrobot.greendao.internal.DaoConfig; +import org.greenrobot.greendao.database.Database; +import org.greenrobot.greendao.database.DatabaseStatement; + +import com.itrycn.myeasywol.db.entity.PCInfo; + +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. +/** + * DAO for table "PCINFO". +*/ +public class PCInfoDao extends AbstractDao { + + public static final String TABLENAME = "PCINFO"; + + /** + * Properties of entity PCInfo.
+ * Can be used for QueryBuilder and for referencing column names. + */ + public static class Properties { + public final static Property Id = new Property(0, Long.class, "id", true, "_id"); + public final static Property Name = new Property(1, String.class, "name", false, "NAME"); + public final static Property Ip = new Property(2, String.class, "ip", false, "IP"); + public final static Property Port = new Property(3, int.class, "port", false, "PORT"); + public final static Property Mac = new Property(4, String.class, "mac", false, "MAC"); + public final static Property ServerID = new Property(5, Long.class, "serverID", false, "SERVER_ID"); + } + + + public PCInfoDao(DaoConfig config) { + super(config); + } + + public PCInfoDao(DaoConfig config, DaoSession daoSession) { + super(config, daoSession); + } + + /** Creates the underlying database table. */ + public static void createTable(Database db, boolean ifNotExists) { + String constraint = ifNotExists? "IF NOT EXISTS ": ""; + db.execSQL("CREATE TABLE " + constraint + "\"PCINFO\" (" + // + "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id + "\"NAME\" TEXT," + // 1: name + "\"IP\" TEXT," + // 2: ip + "\"PORT\" INTEGER NOT NULL ," + // 3: port + "\"MAC\" TEXT," + // 4: mac + "\"SERVER_ID\" INTEGER);"); // 5: serverID + } + + /** Drops the underlying database table. */ + public static void dropTable(Database db, boolean ifExists) { + String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"PCINFO\""; + db.execSQL(sql); + } + + @Override + protected final void bindValues(DatabaseStatement stmt, PCInfo entity) { + stmt.clearBindings(); + + Long id = entity.getId(); + if (id != null) { + stmt.bindLong(1, id); + } + + String name = entity.getName(); + if (name != null) { + stmt.bindString(2, name); + } + + String ip = entity.getIp(); + if (ip != null) { + stmt.bindString(3, ip); + } + stmt.bindLong(4, entity.getPort()); + + String mac = entity.getMac(); + if (mac != null) { + stmt.bindString(5, mac); + } + + Long serverID = entity.getServerID(); + if (serverID != null) { + stmt.bindLong(6, serverID); + } + } + + @Override + protected final void bindValues(SQLiteStatement stmt, PCInfo entity) { + stmt.clearBindings(); + + Long id = entity.getId(); + if (id != null) { + stmt.bindLong(1, id); + } + + String name = entity.getName(); + if (name != null) { + stmt.bindString(2, name); + } + + String ip = entity.getIp(); + if (ip != null) { + stmt.bindString(3, ip); + } + stmt.bindLong(4, entity.getPort()); + + String mac = entity.getMac(); + if (mac != null) { + stmt.bindString(5, mac); + } + + Long serverID = entity.getServerID(); + if (serverID != null) { + stmt.bindLong(6, serverID); + } + } + + @Override + public Long readKey(Cursor cursor, int offset) { + return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0); + } + + @Override + public PCInfo readEntity(Cursor cursor, int offset) { + PCInfo entity = new PCInfo( // + cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id + cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // name + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // ip + cursor.getInt(offset + 3), // port + cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // mac + cursor.isNull(offset + 5) ? null : cursor.getLong(offset + 5) // serverID + ); + return entity; + } + + @Override + public void readEntity(Cursor cursor, PCInfo entity, int offset) { + entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0)); + entity.setName(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1)); + entity.setIp(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); + entity.setPort(cursor.getInt(offset + 3)); + entity.setMac(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4)); + entity.setServerID(cursor.isNull(offset + 5) ? null : cursor.getLong(offset + 5)); + } + + @Override + protected final Long updateKeyAfterInsert(PCInfo entity, long rowId) { + entity.setId(rowId); + return rowId; + } + + @Override + public Long getKey(PCInfo entity) { + if(entity != null) { + return entity.getId(); + } else { + return null; + } + } + + @Override + public boolean hasKey(PCInfo entity) { + return entity.getId() != null; + } + + @Override + protected final boolean isEntityUpdateable() { + return true; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/ServerInfoDao.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/ServerInfoDao.java new file mode 100644 index 0000000..5d49558 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/dao/ServerInfoDao.java @@ -0,0 +1,157 @@ +package com.itrycn.myeasywol.db.dao; + +import android.database.Cursor; +import android.database.sqlite.SQLiteStatement; + +import org.greenrobot.greendao.AbstractDao; +import org.greenrobot.greendao.Property; +import org.greenrobot.greendao.internal.DaoConfig; +import org.greenrobot.greendao.database.Database; +import org.greenrobot.greendao.database.DatabaseStatement; + +import com.itrycn.myeasywol.db.entity.ServerInfo; + +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. +/** + * DAO for table "SERVER_INFO". +*/ +public class ServerInfoDao extends AbstractDao { + + public static final String TABLENAME = "SERVER_INFO"; + + /** + * Properties of entity ServerInfo.
+ * Can be used for QueryBuilder and for referencing column names. + */ + public static class Properties { + public final static Property Id = new Property(0, Long.class, "id", true, "_id"); + public final static Property Name = new Property(1, String.class, "name", false, "NAME"); + public final static Property Url = new Property(2, String.class, "Url", false, "URL"); + public final static Property SuccessMark = new Property(3, String.class, "SuccessMark", false, "SUCCESS_MARK"); + } + + + public ServerInfoDao(DaoConfig config) { + super(config); + } + + public ServerInfoDao(DaoConfig config, DaoSession daoSession) { + super(config, daoSession); + } + + /** Creates the underlying database table. */ + public static void createTable(Database db, boolean ifNotExists) { + String constraint = ifNotExists? "IF NOT EXISTS ": ""; + db.execSQL("CREATE TABLE " + constraint + "\"SERVER_INFO\" (" + // + "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id + "\"NAME\" TEXT," + // 1: name + "\"URL\" TEXT," + // 2: Url + "\"SUCCESS_MARK\" TEXT);"); // 3: SuccessMark + } + + /** Drops the underlying database table. */ + public static void dropTable(Database db, boolean ifExists) { + String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"SERVER_INFO\""; + db.execSQL(sql); + } + + @Override + protected final void bindValues(DatabaseStatement stmt, ServerInfo entity) { + stmt.clearBindings(); + + Long id = entity.getId(); + if (id != null) { + stmt.bindLong(1, id); + } + + String name = entity.getName(); + if (name != null) { + stmt.bindString(2, name); + } + + String Url = entity.getUrl(); + if (Url != null) { + stmt.bindString(3, Url); + } + + String SuccessMark = entity.getSuccessMark(); + if (SuccessMark != null) { + stmt.bindString(4, SuccessMark); + } + } + + @Override + protected final void bindValues(SQLiteStatement stmt, ServerInfo entity) { + stmt.clearBindings(); + + Long id = entity.getId(); + if (id != null) { + stmt.bindLong(1, id); + } + + String name = entity.getName(); + if (name != null) { + stmt.bindString(2, name); + } + + String Url = entity.getUrl(); + if (Url != null) { + stmt.bindString(3, Url); + } + + String SuccessMark = entity.getSuccessMark(); + if (SuccessMark != null) { + stmt.bindString(4, SuccessMark); + } + } + + @Override + public Long readKey(Cursor cursor, int offset) { + return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0); + } + + @Override + public ServerInfo readEntity(Cursor cursor, int offset) { + ServerInfo entity = new ServerInfo( // + cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id + cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // name + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // Url + cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3) // SuccessMark + ); + return entity; + } + + @Override + public void readEntity(Cursor cursor, ServerInfo entity, int offset) { + entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0)); + entity.setName(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1)); + entity.setUrl(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); + entity.setSuccessMark(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); + } + + @Override + protected final Long updateKeyAfterInsert(ServerInfo entity, long rowId) { + entity.setId(rowId); + return rowId; + } + + @Override + public Long getKey(ServerInfo entity) { + if(entity != null) { + return entity.getId(); + } else { + return null; + } + } + + @Override + public boolean hasKey(ServerInfo entity) { + return entity.getId() != null; + } + + @Override + protected final boolean isEntityUpdateable() { + return true; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/entity/PCInfo.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/entity/PCInfo.java new file mode 100644 index 0000000..db43035 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/entity/PCInfo.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.db.entity; + +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Generated; +@Entity +public class PCInfo { + @Id(autoincrement = true) + private Long id; + private String name; + private String ip; + private int port; + private String mac; + private Long serverID; + @Generated(hash = 867088671) + public PCInfo(Long id, String name, String ip, int port, String mac, + Long serverID) { + this.id = id; + this.name = name; + this.ip = ip; + this.port = port; + this.mac = mac; + this.serverID = serverID; + } + @Generated(hash = 932968637) + public PCInfo() { + } + public Long getId() { + return this.id; + } + public void setId(Long id) { + this.id = id; + } + public String getName() { + return this.name; + } + public void setName(String name) { + this.name = name; + } + public String getIp() { + return this.ip; + } + public void setIp(String ip) { + this.ip = ip; + } + public String getMac() { + return this.mac; + } + public void setMac(String mac) { + this.mac = mac; + } + public Long getServerID() { + return this.serverID; + } + public void setServerID(Long serverID) { + this.serverID = serverID; + } + public int getPort() { + return this.port; + } + public void setPort(int port) { + this.port = port; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/entity/ServerInfo.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/entity/ServerInfo.java new file mode 100644 index 0000000..d39ba4e --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/entity/ServerInfo.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.db.entity; + +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Generated; + +@Entity +public class ServerInfo { + @Id(autoincrement = true) + private Long id; + private String name; + private String Url; + private String SuccessMark; + @Generated(hash = 620916738) + public ServerInfo(Long id, String name, String Url, String SuccessMark) { + this.id = id; + this.name = name; + this.Url = Url; + this.SuccessMark = SuccessMark; + } + @Generated(hash = 1634164213) + public ServerInfo() { + } + public Long getId() { + return this.id; + } + public void setId(Long id) { + this.id = id; + } + public String getName() { + return this.name; + } + public void setName(String name) { + this.name = name; + } + public String getUrl() { + return this.Url; + } + public void setUrl(String Url) { + this.Url = Url; + } + public String getSuccessMark() { + return this.SuccessMark; + } + public void setSuccessMark(String SuccessMark) { + this.SuccessMark = SuccessMark; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/util/PCInfoDb.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/util/PCInfoDb.java new file mode 100644 index 0000000..a83bf39 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/util/PCInfoDb.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.db.util; + +import android.content.Context; +import android.database.Cursor; + +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.db.entity.PCInfo; +import com.itrycn.myeasywol.db.dao.PCInfoDao; + +import java.util.List; + +public class PCInfoDb { + /** + * 添加PC信息 + * + * @param context + * @param pcInfo + * @return + */ + public static boolean addPCInfo(Context context, PCInfo pcInfo) { + try { + DBHelper.getInstance(context).getDaoSession().getPCInfoDao().insert(pcInfo); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + /** + * 批量添加PC数据 + * + * @param context + * @param pcInfos + * @return + */ + public static boolean addPCInfos(Context context, List pcInfos) { + try { + DBHelper.getInstance(context).getDaoSession().getPCInfoDao().insertInTx(pcInfos); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + /** + * 更新PC信息 + */ + public static boolean updatePCInfo(Context context, PCInfo info) { + try { + + String sql = "UPDATE "; + sql += PCInfoDao.TABLENAME; + sql += " SET " + PCInfoDao.Properties.Name.columnName + " =?,"+ + PCInfoDao.Properties.Ip.columnName+"=?,"+ + PCInfoDao.Properties.Port.columnName+"="+info.getPort()+","+ + PCInfoDao.Properties.Mac.columnName+"=?,"+ + PCInfoDao.Properties.ServerID.columnName+"="+info.getServerID(); + sql += " where " + PCInfoDao.Properties.Id.columnName + "="+info.getId(); + + String args[] = {info.getName(),info.getIp(), info.getMac()}; + DBHelper.getInstance(context).getWritableDatabase().execSQL(sql, args); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + /** + * 删除PC信息 + * + * @param context + */ + public static boolean deletePCInfo(Context context,Long id) { + try { + String sql = "DELETE FROM "; + sql += PCInfoDao.TABLENAME; + sql += " where " + PCInfoDao.Properties.Id.columnName + "="+id; + DBHelper.getInstance(context).getWritableDatabase().execSQL(sql); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + /** + * 获取指定服务器下的PC数量 + * + * @param context + * @return + */ + public static int getPCCountByServer(Context context,Long ServerId) { + Cursor cursor = null; + int count = 0; + try { + //String args[] = {}; + String sql = "select count(*) from " + PCInfoDao.TABLENAME + " WHERE " + PCInfoDao.Properties.ServerID.columnName + "="+ServerId; + cursor = DBHelper.getInstance(context).getWritableDatabase().rawQuery(sql, null); + cursor.moveToFirst(); + count = cursor.getInt(0); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return count; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/util/ServerInfoDb.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/util/ServerInfoDb.java new file mode 100644 index 0000000..5f9a3bb --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/db/util/ServerInfoDb.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.db.util; + +import android.content.Context; + +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.db.dao.ServerInfoDao; +import com.itrycn.myeasywol.db.entity.ServerInfo; + +import java.util.List; + +public class ServerInfoDb { + /** + * 添加服务器信息 + * + * @param context + * @param pcInfo + * @return + */ + public static boolean addServerInfo(Context context, ServerInfo pcInfo) { + try { + DBHelper.getInstance(context).getDaoSession().getServerInfoDao().insert(pcInfo); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + /** + * 批量添加服务器数据 + * + * @param context + * @param serverInfos + * @return + */ + public static boolean addServerInfos(Context context, List serverInfos) { + try { + DBHelper.getInstance(context).getDaoSession().getServerInfoDao().insertInTx(serverInfos); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + /** + * 更新服务器数据 + */ + public static boolean updateServerInfo(Context context, ServerInfo info) { + try { + + String sql = "UPDATE "; + sql += ServerInfoDao.TABLENAME; + sql += " SET " + ServerInfoDao.Properties.Name.columnName + " =?,"+ + ServerInfoDao.Properties.Url.columnName+"=?,"+ + ServerInfoDao.Properties.SuccessMark.columnName+"=?"; + sql += " where " + ServerInfoDao.Properties.Id.columnName + "="+info.getId(); + String args[] = {info.getName(),info.getUrl(),info.getSuccessMark()}; + DBHelper.getInstance(context).getWritableDatabase().execSQL(sql, args); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + /** + * 删除服务器信息 + * + * @param context + */ + public static boolean deleteServerInfo(Context context,Long id) { + try { + String sql = "DELETE FROM "; + sql += ServerInfoDao.TABLENAME; + sql += " where " + ServerInfoDao.Properties.Id.columnName + "="+id; + DBHelper.getInstance(context).getWritableDatabase().execSQL(sql); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/AboutFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/AboutFragment.java new file mode 100644 index 0000000..f2de88c --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/AboutFragment.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment; + +import android.os.Message; +import android.widget.TextView; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xui.widget.grouplist.XUIGroupListView; +import com.xuexiang.xutil.app.AppUtils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import butterknife.BindView; + +/** + * @author xuexiang + * @since 2019-10-30 00:02 + */ +@Page(name = "关于") +public class AboutFragment extends BaseFragment { + + @BindView(R.id.tv_version) + TextView mVersionTextView; + @BindView(R.id.about_list) + XUIGroupListView mAboutGroupListView; + @BindView(R.id.tv_copyright) + TextView mCopyrightTextView; + + @Override + protected int getLayoutId() { + return R.layout.fragment_about; + } + @Override + protected void handleWorkerMessage(Message msg) { + } + @Override + protected void handleUIMessage(Message msg) { + } + @Override + protected void initViews() { + mVersionTextView.setText(String.format("版本号:%s", AppUtils.getAppVersionName())); +//.addItemView(mAboutGroupListView.createItemView(getResources().getString(R.string.about_item_homepage)), v -> AgentWebActivity.goWeb(getContext(), getString(R.string.url_project_github))) +// .addItemView(mAboutGroupListView.createItemView(getResources().getString(R.string.about_item_author_github)), v -> AgentWebActivity.goWeb(getContext(), getString(R.string.url_author_github))) + XUIGroupListView.newSection(getContext()) + .addItemView(mAboutGroupListView.createItemView(getResources().getString(R.string.about_item_homepage)), v -> XToastUtils.toast("暂无官网。")) + .addItemView(mAboutGroupListView.createItemView(getResources().getString(R.string.about_item_author_github)), v -> XToastUtils.toast("作者:zilinsoft\n邮箱:zilinsoft@qq.com")) + .addTo(mAboutGroupListView); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy", Locale.CHINA); + String currentYear = dateFormat.format(new Date()); + mCopyrightTextView.setText(String.format(getResources().getString(R.string.about_copyright), currentYear)); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/SettingsFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/SettingsFragment.java new file mode 100644 index 0000000..cc8d2e3 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/SettingsFragment.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment; + +import android.os.Message; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xui.widget.textview.supertextview.SuperTextView; + +import butterknife.BindView; + +/** + * @author xuexiang + * @since 2019-10-15 22:38 + */ +@Page(name = "设置") +public class SettingsFragment extends BaseFragment implements SuperTextView.OnSuperTextViewClickListener { + + @BindView(R.id.menu_common) + SuperTextView menuCommon; + + @Override + protected int getLayoutId() { + return R.layout.fragment_settings; + } + @Override + protected void handleWorkerMessage(Message msg) { + } + @Override + protected void handleUIMessage(Message msg) { + } + @Override + protected void initViews() { + menuCommon.setOnSuperTextViewClickListener(this); + } + + @SingleClick + @Override + public void onClick(SuperTextView superTextView) { + switch (superTextView.getId()) { + case R.id.menu_common: + XToastUtils.toast("暂无设置,无需在此处设置"); + break; + default: + break; + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/AddPCFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/AddPCFragment.java new file mode 100644 index 0000000..9372737 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/AddPCFragment.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.news; + +import android.os.Bundle; +import android.os.Message; +import android.view.View; +import android.widget.Spinner; +import android.widget.TextView; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.db.entity.PCInfo; +import com.itrycn.myeasywol.db.entity.ServerInfo; +import com.itrycn.myeasywol.db.util.PCInfoDb; +import com.itrycn.myeasywol.fragment.profile.MsgCode; +import com.itrycn.myeasywol.utils.MyBroadcastReceiver; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xui.utils.WidgetUtils; +import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText; + +import org.greenrobot.greendao.query.WhereCondition; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.OnClick; + +@Page(name = "设备管理") +public class AddPCFragment extends BaseFragment { + @BindView(R.id.et_name) + MaterialEditText etName; + @BindView(R.id.et_mac) + MaterialEditText etMac; + @BindView(R.id.et_ip) + MaterialEditText etIP; + @BindView(R.id.et_port) + MaterialEditText etPort; + @BindView(R.id.spinner_server) + Spinner spinnerServer; + @BindView(R.id.frag_name) + TextView frag_name; + Boolean isAdd=true; + Long SelectedId=0L; + @Override + protected int getLayoutId() { + return R.layout.fragment_add_pcinfo; + } + @Override + protected void handleWorkerMessage(Message msg) { + } + @Override + protected void handleUIMessage(Message msg) { + } + @Override + protected void initViews() { + List list =new ArrayList(); + ServerInfo info= new ServerInfo(); + info.setName(getString(R.string.sever_direct)); + info.setUrl("direct"); + info.setId(-1L); + list.add(info); + list.addAll(DBHelper.getInstance(getContext()).getDaoSession().getServerInfoDao().queryBuilder().list()); + WidgetUtils.initSpinnerStyle(spinnerServer); + ServerAdapter _MyAdapter=new ServerAdapter(getContext(), list); + spinnerServer.setAdapter(_MyAdapter); + Bundle bundle = getArguments(); + if (bundle != null) { + Long id= bundle.getLong(MsgCode.ACTION_DATA_KEY,-1); + if(id>=0) { + List list_pc = DBHelper.getInstance(getContext()).getDaoSession().getPCInfoDao().queryBuilder().where(new WhereCondition.StringCondition("_id=" + id)).list(); + if (list_pc.size() == 1) { + etName.setText(list_pc.get(0).getName()); + etMac.setText(list_pc.get(0).getMac()); + etIP.setText(list_pc.get(0).getIp()); + if(list_pc.get(0).getPort()>0) { + etPort.setText(String.valueOf(list_pc.get(0).getPort())); + } + SelectedId = list_pc.get(0).getId(); + isAdd = false; + frag_name.setText("修改设备"); + Long ServerId = list_pc.get(0).getServerID(); + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getId() == ServerId) { + spinnerServer.setSelection(i); + break; + } + } + } + } + } } + @SingleClick + @OnClick({R.id.btn_ok}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.btn_ok: + String Name=etName.getEditValue(); + if(Name.length()<1) + { + XToastUtils.warning("请输入名称"); + return; + } + String Mac=etMac.getEditValue(); + if(Mac.length()!=17 && Mac.length()!=12) + { + XToastUtils.warning("请输入正确的MAC"); + return; + } + ServerInfo ss=(ServerInfo)spinnerServer.getSelectedItem(); + PCInfo info=new PCInfo(); + info.setName(Name); + info.setMac(Mac); + info.setIp(etIP.getEditValue()); + if(etPort.getEditValue().length()>0) { + info.setPort(Integer.parseInt(etPort.getEditValue())); + } + else + { + info.setPort(-1); + } + info.setServerID(ss.getId()); + if(isAdd) { + PCInfoDb.addPCInfo(getContext(),info); + } + else + { + info.setId(SelectedId); + PCInfoDb.updatePCInfo(getContext(),info); + } + popToBack(); + MyBroadcastReceiver.sendReceiver(getContext().getApplicationContext(), MsgCode.ACTION_CODE_UPDATE_PCLIST); + //确定按钮 + break; + default: + break; + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/AddServerFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/AddServerFragment.java new file mode 100644 index 0000000..0f1625e --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/AddServerFragment.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.news; + +import android.os.Bundle; +import android.os.Message; +import android.view.View; +import android.widget.TextView; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.db.entity.ServerInfo; +import com.itrycn.myeasywol.db.util.ServerInfoDb; +import com.itrycn.myeasywol.fragment.profile.MsgCode; +import com.itrycn.myeasywol.utils.MyBroadcastReceiver; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText; + +import org.greenrobot.greendao.query.WhereCondition; + +import java.util.List; + +import butterknife.BindView; +import butterknife.OnClick; + +@Page(name = "服务器管理") +public class AddServerFragment extends BaseFragment { + @BindView(R.id.et_name) + MaterialEditText etName; + @BindView(R.id.et_url) + MaterialEditText etUrl; + @BindView(R.id.et_successmark) + MaterialEditText etSuccessMark; + @BindView(R.id.frag_name) + TextView frag_name; + @Override + protected int getLayoutId() { + return R.layout.fragment_add_serverinfo; + } + Boolean isAdd=true; + Long SelectedId=0L; + @Override + protected void initViews() { + Bundle bundle = getArguments(); + if (bundle != null) { + Long id= bundle.getLong(MsgCode.ACTION_DATA_KEY,-1); + if(id>=0) { + List list = DBHelper.getInstance(getContext()).getDaoSession().getServerInfoDao().queryBuilder().where(new WhereCondition.StringCondition("_id=" + id)).list(); + if (list.size() == 1) { + etName.setText(list.get(0).getName()); + etUrl.setText(list.get(0).getUrl()); + etSuccessMark.setText(list.get(0).getSuccessMark()); + SelectedId = list.get(0).getId(); + isAdd = false; + frag_name.setText("修改服务器"); + } + } + } + } + @Override + protected void handleWorkerMessage(Message msg) { + } + @Override + protected void handleUIMessage(Message msg) { + } + @SingleClick + @OnClick({R.id.btn_ok}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.btn_ok: + String Name=etName.getEditValue(); + if(Name.length()<1) + { + XToastUtils.warning("请输入名称"); + return; + } + String Url=etUrl.getEditValue(); + if(Url.length()<5) + { + XToastUtils.warning("请输入正确的Url"); + return; + } + ServerInfo info=new ServerInfo(); + info.setName(Name); + info.setUrl(Url); + info.setSuccessMark(etSuccessMark.getEditValue()); + if(isAdd) { + ServerInfoDb.addServerInfo(getContext(), info); + } + else + { + info.setId(SelectedId); + ServerInfoDb.updateServerInfo(getContext(), info); + } + popToBack(); + MyBroadcastReceiver.sendReceiver(getContext().getApplicationContext(), MsgCode.ACTION_CODE_UPDATE_SERVERLIST); + //确定按钮 + break; + default: + break; + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/PCFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/PCFragment.java new file mode 100644 index 0000000..a44d810 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/PCFragment.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.news; + +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.alibaba.android.vlayout.DelegateAdapter; +import com.alibaba.android.vlayout.VirtualLayoutManager; +import com.alibaba.android.vlayout.layout.GridLayoutHelper; +import com.alibaba.android.vlayout.layout.LinearLayoutHelper; +import com.itrycn.myeasywol.adapter.base.delegate.BaseDelegateAdapter; +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.db.dao.PCInfoDao; +import com.itrycn.myeasywol.db.entity.PCInfo; +import com.itrycn.myeasywol.db.entity.ServerInfo; +import com.itrycn.myeasywol.fragment.profile.MsgCode; +import com.itrycn.myeasywol.utils.MyBroadcastReceiver; +import com.itrycn.myeasywol.utils.RandomUtils; +import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.adapter.base.broccoli.BroccoliSimpleDelegateAdapter; +import com.itrycn.myeasywol.adapter.base.delegate.SimpleDelegateAdapter; +import com.itrycn.myeasywol.adapter.base.delegate.SingleDelegateAdapter; +import com.itrycn.myeasywol.adapter.entity.NewInfo; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.utils.Utils; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xpage.enums.CoreAnim; +import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder; +import com.xuexiang.xui.adapter.simple.AdapterItem; +import com.xuexiang.xui.utils.ResUtils; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xui.widget.banner.widget.banner.SimpleImageBanner; +import com.xuexiang.xui.widget.imageview.ImageLoader; +import com.xuexiang.xui.widget.imageview.RadiusImageView; + +import org.greenrobot.greendao.query.WhereCondition; + +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; + +import butterknife.BindView; +import me.samlss.broccoli.Broccoli; + +/** + * 首页动态 + * + * @author xuexiang + * @since 2019-10-30 00:15 + */ +@Page(anim = CoreAnim.none) +public class PCFragment extends BaseFragment { + /** + * 广播 + */ + private MyBroadcastReceiver mBroadcastReceiver; + @BindView(R.id.recyclerView) + RecyclerView recyclerView; + @BindView(R.id.refreshLayout) + SmartRefreshLayout refreshLayout; + private SimpleDelegateAdapter commonAdapter; + /** + * @return 返回为 null意为不需要导航栏 + */ + @Override + protected TitleBar initTitle() { + return null; + } + + /** + * 布局的资源id + * + * @return + */ + @Override + protected int getLayoutId() { + return R.layout.fragment_news; + } + @Override + protected void handleWorkerMessage(Message msg) { + } + @Override + protected void handleUIMessage(Message msg) { + } + private static List getPCList(Context context) { + try { + //按创建时间倒序 + List audioInfos = DBHelper.getInstance(context).getDaoSession().getPCInfoDao().queryBuilder().list(); + return audioInfos; + } catch (Exception e) { + e.printStackTrace(); + } + return new ArrayList(); + } + /** + * 初始化控件 + */ + @Override + protected void initViews() { + VirtualLayoutManager virtualLayoutManager = new VirtualLayoutManager(getContext()); + recyclerView.setLayoutManager(virtualLayoutManager); + RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool(); + recyclerView.setRecycledViewPool(viewPool); + viewPool.setMaxRecycledViews(0, 10); + mBroadcastReceiver = new MyBroadcastReceiver(); + //轮播条 +// SingleDelegateAdapter bannerAdapter = new SingleDelegateAdapter(R.layout.include_head_view_banner) { +// @Override +// public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) { +// SimpleImageBanner banner = holder.findViewById(R.id.sib_simple_usage); +// banner.setSource(DemoDataProvider.getBannerList()) +// .setOnItemClickListener((view, item, position1) -> XToastUtils.toast("headBanner position--->" + position1)).startScroll(); +// } +// }; + //九宫格菜单 + GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(4); + gridLayoutHelper.setPadding(0, 16, 0, 0); + gridLayoutHelper.setVGap(10); + gridLayoutHelper.setHGap(0); + gridLayoutHelper.setAutoExpand(false); + commonAdapter = new SimpleDelegateAdapter(R.layout.adapter_common_grid_item, gridLayoutHelper,getPCList(getContext())) { + @Override + protected void bindData(@NonNull RecyclerViewHolder holder, int position, PCInfo item) { + if (item != null) { + RadiusImageView imageView = holder.findViewById(R.id.riv_item); + imageView.setCircle(true); + Drawable[] draw=ResUtils.getDrawableArray(getContext(), R.array.grid_icons_entry); + ImageLoader.get().loadImage(imageView,draw[RandomUtils.getRandom(0,draw.length)]); + holder.text(R.id.tv_title, item.getName().toString().substring(0, 1)); + holder.text(R.id.tv_sub_title, item.getName()); + holder.click(R.id.ll_container,new View.OnClickListener() { + @Override + public void onClick(View v) { + //打开排行页面 + Bundle bundle = new Bundle(); + bundle.putLong(MsgCode.ACTION_DATA_KEY, item.getId()); + MyBroadcastReceiver.sendReceiver(getContext().getApplicationContext(), MsgCode.ACTION_CODE_WAKE_PC,MyBroadcastReceiver.ACTION_BUNDLEKEY,bundle); + //XToastUtils.toast("点击了:" + item.getMac()); + } + }); + + } + } + }; + DelegateAdapter delegateAdapter = new DelegateAdapter(virtualLayoutManager); +// delegateAdapter.addAdapter(bannerAdapter); + delegateAdapter.addAdapter(commonAdapter); + + recyclerView.setAdapter(delegateAdapter); + mBroadcastReceiver = new MyBroadcastReceiver(); + mBroadcastReceiver.setReceiverListener(new MyBroadcastReceiver.MyReceiverListener() { + @Override + public void onReceive(Context context, final Intent intent, final int code) { + mUIHandler.post(new Runnable() { + @Override + public void run() { + handleAudioBroadcastReceiver(intent, code); + } + }); + } + + private void handleAudioBroadcastReceiver(Intent intent, int code) { + switch (code) { + case MsgCode.ACTION_CODE_UPDATE_PCLIST: + refresh(); + break; + } + } + }); + mBroadcastReceiver.registerReceiver(getContext().getApplicationContext()); + } + @Override + public void onDestroyView() { + if (mBroadcastReceiver != null) { + mBroadcastReceiver.unregisterReceiver(getContext()); + } + super.onDestroyView(); + } + @Override + protected void initListeners() { + //下拉刷新 + refreshLayout.setOnRefreshListener(refreshLayout -> { + // TODO: 2020-02-25 这里只是模拟了网络请求 + refreshLayout.getLayout().post(() -> { + refresh(); + refreshLayout.finishRefresh(); + }); + }); + //refreshLayout.autoRefresh();//第一次进入触发自动刷新,演示效果 + } + private void refresh() { +// refreshLayout.setEnableRefresh(true); + loadData(); + } + + private void loadData() { + new Handler().post(() -> { + commonAdapter.refresh(getPCList(getContext())); +// if (refreshLayout != null) { +// refreshLayout.setEnableRefresh(false); +// } + }); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/ServerAdapter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/ServerAdapter.java new file mode 100644 index 0000000..70b4881 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/ServerAdapter.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.news; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.db.entity.ServerInfo; + +import java.util.List; + +public class ServerAdapter extends BaseAdapter { + private List mList; + private Context mContext; + public ServerAdapter(Context pContext, List pList) { + this.mContext = pContext; + this.mList = pList; + } + @Override + public int getCount() { + return mList.size(); + } + @Override + public Object getItem(int position) { + return mList.get(position); + } + @Override + public long getItemId(int position) { + return position; + } + /** + * 下面是重要代码 + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LayoutInflater _LayoutInflater=LayoutInflater.from(mContext); + convertView=_LayoutInflater.inflate(R.layout.xui_layout_spinner_selected_item, null); + if(convertView!=null) + { + TextView _TextView1=(TextView)convertView.findViewById(R.id.spinner_item); + _TextView1.setText(mList.get(position).getName()); + } + return convertView; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/ServerViewFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/ServerViewFragment.java new file mode 100644 index 0000000..a09b9cd --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/ServerViewFragment.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.news; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.view.ViewGroup; + +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.adapter.base.delegate.ServerRecyclerAdapter; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.db.entity.ServerInfo; +import com.itrycn.myeasywol.db.util.PCInfoDb; +import com.itrycn.myeasywol.db.util.ServerInfoDb; +import com.itrycn.myeasywol.fragment.profile.MsgCode; +import com.itrycn.myeasywol.utils.MyBroadcastReceiver; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xui.utils.WidgetUtils; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog; +import com.yanzhenjie.recyclerview.OnItemClickListener; +import com.yanzhenjie.recyclerview.OnItemMenuClickListener; +import com.yanzhenjie.recyclerview.SwipeMenuCreator; +import com.yanzhenjie.recyclerview.SwipeMenuItem; +import com.yanzhenjie.recyclerview.SwipeRecyclerView; + +import java.util.List; + +import butterknife.BindView; + +@Page(name = "服务器列表") +public class ServerViewFragment extends BaseFragment { + /** + * 广播 + */ + private MyBroadcastReceiver mBroadcastReceiver; + @BindView(R.id.refresh_layout) + SwipeRefreshLayout refreshLayout; + + @BindView(R.id.recycler_view) + SwipeRecyclerView recyclerView; + + private ServerRecyclerAdapter ServerAdapter; + @Override + protected void handleWorkerMessage(Message msg) { + } + @Override + protected void handleUIMessage(Message msg) { + } + /** + * @return 返回为 null意为不需要导航栏 + */ + @Override + protected TitleBar initTitle() { + return null; + } + /** + * 布局的资源id + * + * @return + */ + @Override + protected int getLayoutId() { + return R.layout.fragment_server; + } + /** + * 初始化控件 + */ + @Override + protected void initViews() { + WidgetUtils.initRecyclerView(recyclerView); + + //必须在setAdapter之前调用 + recyclerView.setSwipeMenuCreator(swipeMenuCreator); + //必须在setAdapter之前调用 + recyclerView.setOnItemMenuClickListener(mMenuItemClickListener); + recyclerView.setOnItemClickListener(ItemClickListener); + //List audioInfos = DBHelper.getInstance(getContext()).getDaoSession().getServerInfoDao().queryBuilder().list(); + ServerAdapter = new ServerRecyclerAdapter(); + recyclerView.setAdapter(ServerAdapter); + + refreshLayout.setColorSchemeColors(0xff0099cc, 0xffff4444, 0xff669900, 0xffaa66cc, 0xffff8800); + mBroadcastReceiver = new MyBroadcastReceiver(); + mBroadcastReceiver.setReceiverListener(new MyBroadcastReceiver.MyReceiverListener() { + @Override + public void onReceive(Context context, final Intent intent, final int code) { + mUIHandler.post(new Runnable() { + @Override + public void run() { + handleAudioBroadcastReceiver(intent, code); + } + }); + } + + private void handleAudioBroadcastReceiver(Intent intent, int code) { + switch (code) { + case MsgCode.ACTION_CODE_UPDATE_SERVERLIST: + refresh(); + break; + } + } + }); + mBroadcastReceiver.registerReceiver(getContext().getApplicationContext()); + } + @Override + public void onDestroyView() { + if (mBroadcastReceiver != null) { + mBroadcastReceiver.unregisterReceiver(getContext()); + } + super.onDestroyView(); + } + private OnItemClickListener ItemClickListener = (View itemView, int position) -> { + Bundle bundle = new Bundle(); + bundle.putLong(MsgCode.ACTION_DATA_KEY, ServerAdapter.getItem(position).getId()); + MyBroadcastReceiver.sendReceiver(getContext().getApplicationContext(), MsgCode.ACTION_CODE_EDIT_SERVER,MyBroadcastReceiver.ACTION_BUNDLEKEY,bundle); + }; + /** + * 菜单创建器,在Item要创建菜单的时候调用。 + */ + private SwipeMenuCreator swipeMenuCreator = (swipeLeftMenu, swipeRightMenu, position) -> { + int width = getResources().getDimensionPixelSize(R.dimen.dp_70); + + // 1. MATCH_PARENT 自适应高度,保持和Item一样高; + // 2. 指定具体的高,比如80; + // 3. WRAP_CONTENT,自身高度,不推荐; + int height = ViewGroup.LayoutParams.MATCH_PARENT; + + + // 添加右侧的,如果不添加,则右侧不会出现菜单。 + { + SwipeMenuItem deleteItem = new SwipeMenuItem(getContext()).setBackground(R.drawable.menu_selector_red) + .setText("删除") + .setTextColor(Color.WHITE) + .setWidth(width) + .setHeight(height); + swipeRightMenu.addMenuItem(deleteItem);// 添加菜单到右侧。 + + SwipeMenuItem addItem = new SwipeMenuItem(getContext()).setBackground(R.drawable.menu_selector_green) + .setText("编辑") + .setTextColor(Color.WHITE) + .setWidth(width) + .setHeight(height); + swipeRightMenu.addMenuItem(addItem); // 添加菜单到右侧。 + } + }; + /** + * RecyclerView的Item的Menu点击监听。 + */ + private OnItemMenuClickListener mMenuItemClickListener = (menuBridge, position) -> { + menuBridge.closeMenu(); + + int direction = menuBridge.getDirection(); // 左侧还是右侧菜单。 + int menuPosition = menuBridge.getPosition(); // 菜单在RecyclerView的Item中的Position。 + + if (direction == SwipeRecyclerView.RIGHT_DIRECTION) { + if(menuPosition==0)//删除 + { + MaterialDialog.Builder materialDialog= new MaterialDialog.Builder(getContext()); + materialDialog.content("是否确认要删除该服务器?") + .positiveText(R.string.lab_yes) + .negativeText(R.string.lab_no) + .onPositive((dialog, which) -> { + Long ServerId=ServerAdapter.getItem(position).getId(); + int count= PCInfoDb.getPCCountByServer(getContext(),ServerId); + if(count==0) { + ServerInfoDb.deleteServerInfo(getContext(), ServerId); + ServerAdapter.getListData().remove(position); + ServerAdapter.notifyDataSetChanged(); + } + else + { + new MaterialDialog.Builder(getContext()) + .iconRes(R.drawable.icon_tip) + .title("提示") + .content("当前中转服务器下还有设备,请先删除设备后再删除中转服务器。") + .positiveText("确定") + .show(); + } + }).show(); + + } + else if(menuPosition==1)//编辑 + { + Bundle bundle = new Bundle(); + bundle.putLong(MsgCode.ACTION_DATA_KEY, ServerAdapter.getItem(position).getId()); + MyBroadcastReceiver.sendReceiver(getContext().getApplicationContext(), MsgCode.ACTION_CODE_EDIT_SERVER,MyBroadcastReceiver.ACTION_BUNDLEKEY,bundle); + } + //XToastUtils.toast("list第" + position + "; 右侧菜单第" + menuPosition+"-->"+ServerAdapter.getItem(position).getName()); + } else if (direction == SwipeRecyclerView.LEFT_DIRECTION) { + //XToastUtils.toast("list第" + position + "; 左侧菜单第" + menuPosition); + } + }; + @Override + protected void initListeners() { + //下拉刷新 + refreshLayout.setOnRefreshListener(this::loadData); + refresh(); //第一次进入触发自动刷新,演示效果 + } + private void refresh() { + refreshLayout.setRefreshing(true); + loadData(); + } + + private void loadData() { + new Handler().postDelayed(() -> { + List audioInfos = DBHelper.getInstance(getContext()).getDaoSession().getServerInfoDao().queryBuilder().list(); + ServerAdapter.refresh(audioInfos); + if (refreshLayout != null) { + refreshLayout.setRefreshing(false); + } + }, 1000); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/WakePCFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/WakePCFragment.java new file mode 100644 index 0000000..dcc6d66 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/news/WakePCFragment.java @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.news; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.view.MenuItem; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.appcompat.widget.PopupMenu; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.db.DBHelper; +import com.itrycn.myeasywol.db.entity.PCInfo; +import com.itrycn.myeasywol.db.entity.ServerInfo; +import com.itrycn.myeasywol.db.util.PCInfoDb; +import com.itrycn.myeasywol.fragment.profile.MagicBoot; +import com.itrycn.myeasywol.fragment.profile.MsgCode; +import com.itrycn.myeasywol.utils.MyBroadcastReceiver; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog; + +import org.greenrobot.greendao.query.WhereCondition; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import butterknife.BindView; +import butterknife.OnClick; + +@Page(name = "唤醒设备") +public class WakePCFragment extends BaseFragment { + private PopupMenu mPopupMenu; + @BindView(R.id.iv_back) + ImageView mBackImageView; + @BindView(R.id.toolbar_title) + TextView mTitleTextView; + @BindView(R.id.iv_more) + ImageView mMoreImageView; + /** + * 唤醒成功 + */ + private final int Wake_Successs = 1; + /** + * 唤醒失败 + */ + private final int Wake_Failure = -1; + /** + * 唤醒失败,没有发现服务器 + */ + private final int Wake_UnfoundServer = -2; + /** + * 唤醒失败,发送出错 + */ + private final int Wake_Error = -3; + /** + * 广播 + */ + private MyBroadcastReceiver mBroadcastReceiver; + PCInfo pcitem; + @BindView(R.id.lblpcname) + TextView lblpcname; + @BindView(R.id.lblservername) + TextView lblservername; + @Override + protected int getLayoutId() { + return R.layout.fragment_wake_pc; + } + @Override + protected void handleWorkerMessage(Message msg) { + } + /** + * @return 返回为 null意为不需要导航栏 + */ + @Override + protected TitleBar initTitle() { + return null; + } + @Override + protected void handleUIMessage(Message msg) { + switch (msg.what) { + case Wake_Successs: + XToastUtils.success("远程唤醒数据包发送成功。"); + break; + case Wake_Failure: + XToastUtils.error( "远程唤醒数据包发送失败。\r\n请重试。"); + break; + case Wake_UnfoundServer: + XToastUtils.warning("没有发现服务器,远程唤醒失败。"); + break; + case Wake_Error: + XToastUtils.error("发送失败,出现错误。"); + break; + } + } + @Override + protected void initViews() { + mTitleTextView.setText("唤醒设备"); + Bundle bundle = getArguments(); + if (bundle != null) { + Long id= bundle.getLong(MsgCode.ACTION_DATA_KEY); + List list= DBHelper.getInstance(getContext()).getDaoSession().getPCInfoDao().queryBuilder().where(new WhereCondition.StringCondition("_id="+id)).list(); + if(list.size()==1) + { + pcitem=list.get(0); + lblpcname.setText(pcitem.getName()); + if(pcitem.getServerID()==-1) + { + lblservername.setText(R.string.sever_direct); + } + else { + List list_server = DBHelper.getInstance(getContext()).getDaoSession().getServerInfoDao().queryBuilder().where(new WhereCondition.StringCondition("_id=" + pcitem.getServerID())).list(); + if (list_server.size() == 1) { + lblservername.setText(list_server.get(0).getName()); + } + } + } + } + mBroadcastReceiver = new MyBroadcastReceiver(); + mBroadcastReceiver.setReceiverListener(new MyBroadcastReceiver.MyReceiverListener() { + @Override + public void onReceive(Context context, final Intent intent, final int code) { + mUIHandler.post(new Runnable() { + @Override + public void run() { + handleAudioBroadcastReceiver(intent, code); + } + }); + } + + private void handleAudioBroadcastReceiver(Intent intent, int code) { + switch (code) { + case MsgCode.ACTION_CODE_UPDATE_PCLIST: + Bundle bundle = getArguments(); + if (bundle != null) { + Long id= bundle.getLong(MsgCode.ACTION_DATA_KEY); + List list= DBHelper.getInstance(getContext()).getDaoSession().getPCInfoDao().queryBuilder().where(new WhereCondition.StringCondition("_id="+id)).list(); + if(list.size()==1) + { + pcitem=list.get(0); + lblpcname.setText(pcitem.getName()); + if(pcitem.getServerID()==-1) + { + lblservername.setText(R.string.sever_direct); + } + else { + List list_server = DBHelper.getInstance(getContext()).getDaoSession().getServerInfoDao().queryBuilder().where(new WhereCondition.StringCondition("_id=" + pcitem.getServerID())).list(); + if (list_server.size() == 1) { + lblservername.setText(list_server.get(0).getName()); + } + } + } + } + break; + } + } + }); + mBroadcastReceiver.registerReceiver(getContext().getApplicationContext()); + } + @Override + public void onDestroy() { + if (mBroadcastReceiver != null) { + mBroadcastReceiver.unregisterReceiver(getContext().getApplicationContext()); + } + super.onDestroy(); + } + /** + * 显示更多菜单 + * + * @param view 菜单依附在该View下面 + */ + private void showPoPup(View view) { + if (mPopupMenu == null) { + mPopupMenu = new PopupMenu(getContext(), view); + mPopupMenu.inflate(R.menu.menu_toolbar_edit); + mPopupMenu.setOnMenuItemClickListener(mOnMenuItemClickListener); + } + mPopupMenu.show(); + } + /** + * 菜单事件 + */ + private PopupMenu.OnMenuItemClickListener mOnMenuItemClickListener = new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.edit: + Bundle bundle = new Bundle(); + bundle.putLong(MsgCode.ACTION_DATA_KEY, pcitem.getId()); + MyBroadcastReceiver.sendReceiver(getContext().getApplicationContext(), MsgCode.ACTION_CODE_EDIT_PC,MyBroadcastReceiver.ACTION_BUNDLEKEY,bundle); + return true; + case R.id.delete: + MaterialDialog.Builder materialDialog= new MaterialDialog.Builder(getContext()); + materialDialog.content("是否确认要删除该设备?") + .positiveText(R.string.lab_yes) + .negativeText(R.string.lab_no) + .onPositive((dialog, which) -> { + PCInfoDb.deletePCInfo(getContext(),pcitem.getId()); + MyBroadcastReceiver.sendReceiver(getContext().getApplicationContext(), MsgCode.ACTION_CODE_UPDATE_PCLIST); + popToBack(); + }).show(); + return true; + default: + return false; + } + + } + }; + @SingleClick + @OnClick({R.id.btn_wake,R.id.iv_back,R.id.iv_more}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.btn_wake: + if(pcitem!=null) { + new Thread(new Runnable() { + @Override + public void run() { + // TODO Auto-generated method stub + Long ServerID=pcitem.getServerID(); + if(ServerID==-1)//直链唤醒 + { + new Thread(new Runnable() + { + @Override + public void run() + { + int port=pcitem.getPort(); + if(port<=0){port=20201;} + try { + boolean result = MagicBoot.SendMagic(pcitem.getMac(), pcitem.getIp(), port); + if (result) { + mUIHandler.sendEmptyMessage(Wake_Successs); + } else { + mUIHandler.sendEmptyMessage(Wake_Failure); + } + } + catch (Exception e) { + // TODO Auto-generated catch block + mUIHandler.sendEmptyMessage(Wake_Error); + e.printStackTrace(); + } + } + }).start(); //这段代码在主线程中调用,开启一个线程 + + //mUIHandler.sendEmptyMessage(Wake_UnfoundServer); + } + else { + new Thread(new Runnable() + { + @Override + public void run() + { + int port=pcitem.getPort(); + if(port<=0){port=20201;} + List list = DBHelper.getInstance(getContext()).getDaoSession().getServerInfoDao().queryBuilder().where(new WhereCondition.StringCondition("_id=" + ServerID)).list(); + if (list.size() == 1) { + SendMagic(pcitem.getMac(), pcitem.getIp(), list.get(0).getUrl(),port,list.get(0).getSuccessMark()); + } + else + { + mUIHandler.sendEmptyMessage(Wake_UnfoundServer); + } + } + }).start(); //这段代码在主线程中调用,开启一个线程 + } + } + }).start(); //这段代码在主线程中调用,开启一个线程 + } + //确定按钮 + break; + case R.id.iv_back: + popToBack(); + break; + case R.id.iv_more: + showPoPup(view); + break; + default: + break; + } + } + private void SendMagic(String Mac,String IP,String Url,int Port,String SuccessMark) + { + String result; + String _Url=Url; + if(_Url.contains("{mac}")) { + _Url = _Url.replace("{mac}", Mac); + _Url = _Url.replace("{ip}", IP); + _Url = _Url.replace("{port}", String.valueOf(Port)); + result = sendGetMessage(_Url,"utf-8"); + } + else { + Map params = new HashMap(); + params.put("a", "magic"); + params.put("mac", Mac); + params.put("ip", IP); + params.put("port", String.valueOf(Port)); + result = sendPostMessage(_Url, params, "utf-8"); + } + if(result.equals("ok") || result.contains("success") || result.contains("成功") || (SuccessMark!=null && !SuccessMark.equals("") && result.contains(SuccessMark))) + { + mUIHandler.sendEmptyMessage(Wake_Successs); + } + else + { + mUIHandler.sendEmptyMessage(Wake_Failure); + } + } + + /** + * @param params 填写的url的参数 + * @param encode 字节编码 + * @return + */ + public static String sendPostMessage(String url, Map params, String encode){ + StringBuffer buffer = new StringBuffer(); + try {//把请求的主体写入正文!! + URL uri = new URL(url); + if(params != null&&!params.isEmpty()){ + //迭代器 + //Map.Entry 是Map中的一个接口,他的用途是表示一个映射项(里面有Key和Value) + for(Map.Entry entry : params.entrySet()){ + buffer.append(entry.getKey()).append("="). + append(URLEncoder.encode(entry.getValue(),encode)). + append("&"); + } + // System.out.println(buffer.toString()); + //删除最后一个字符&,多了一个;主体设置完毕 + buffer.deleteCharAt(buffer.length()-1); + } + HttpURLConnection connection = (HttpURLConnection) uri.openConnection(); + connection.setConnectTimeout(3000); + connection.setDoInput(true);//表示从服务器获取数据 + connection.setDoOutput(true);//表示向服务器写数据 + + connection.setRequestMethod("POST"); + //是否使用缓存 + connection.setUseCaches(false); + //表示设置请求体的类型是文本类型 + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + byte[] mydata = buffer.toString().getBytes(); + connection.setRequestProperty("Content-Length", String.valueOf(mydata.length)); + connection.connect(); //连接,不写也可以。。??有待了解 + + //获得输出流,向服务器输出数据 + OutputStream outputStream = connection.getOutputStream(); + outputStream.write(mydata,0,mydata.length); + //获得服务器响应的结果和状态码 + int responseCode = connection.getResponseCode(); + if(responseCode == HttpURLConnection.HTTP_OK){ + return changeInputeStream(connection.getInputStream(),encode); + + } + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return ""; + } + public static String sendGetMessage(String url, String encode) + { + //1. URL + try + { + URL uri = new URL(url); + //2. HttpURLConnection + HttpURLConnection conn=(HttpURLConnection)uri.openConnection(); + //3. set(GET) + conn.setRequestMethod("GET"); + //4. getInputStream + InputStream is = conn.getInputStream(); + //5. 解析is,获取responseText,这里用缓冲字符 + int responseCode = conn.getResponseCode(); + if(responseCode == HttpURLConnection.HTTP_OK) { + return changeInputeStream(conn.getInputStream(), encode); + } + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return ""; + } + /** + * 将一个输入流转换成字符串 + * @param inputStream + * @param encode + * @return + */ + private static String changeInputeStream(InputStream inputStream, String encode) { + //通常叫做内存流,写在内存中的 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] data = new byte[1024]; + int len = 0; + String result = ""; + if(inputStream != null){ + try { + while((len = inputStream.read(data))!=-1){ + data.toString(); + outputStream.write(data, 0, len); + } + //result是在服务器端设置的doPost函数中的 + result = new String(outputStream.toByteArray(),encode); + outputStream.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return result; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/MagicBoot.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/MagicBoot.java new file mode 100644 index 0000000..b10421e --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/MagicBoot.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.profile; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +public class MagicBoot { + /** + * 发送开机魔术包 + */ + public static boolean SendMagic(String macAddress,String destIP) + { + return SendMagic(macAddress,destIP,20105); + } + /** + * 发送开机魔术包 + */ + public static boolean SendMagic(String macAddress,String destIP,int port) + { + try { + if(macAddress==null || destIP==null){return false;} + // String destIP = "255.255.255.255";// 广播地址 + // 检测 mac 地址,并将其转换为二进制 + byte[] destMac = getMacBytes(macAddress); + if (destMac == null) + return false; + InetAddress destHost = InetAddress.getByName(destIP); + // construct packet data + byte[] magicBytes = new byte[102]; + // 将数据包的前6位放入0xFF即 "FF"的二进制 + for (int i = 0; i < 6; i++) + magicBytes[i] = (byte) 0xFF; + // 从第7个位置开始把mac地址放入16次 + for (int i = 0; i < 16; i++) { + for (int j = 0; j < destMac.length; j++) { + magicBytes[6 + destMac.length * i + j] = destMac[j]; + } + } + // create packet + DatagramPacket dp = null; + dp = new DatagramPacket(magicBytes, magicBytes.length, destHost, port); + DatagramSocket ds = new DatagramSocket(); + ds.send(dp); + ds.close(); + return true; + } + catch (IOException e) { + e.printStackTrace(); + return false; + } + } + private static byte[] getMacBytes(String macStr) throws IllegalArgumentException { + byte[] bytes = new byte[6]; + String[] hex = macStr.split("(\\:|\\-)"); + if (hex.length != 6) { + throw new IllegalArgumentException("Invalid MAC address."); + } + try { + for (int i = 0; i < 6; i++) { + bytes[i] = (byte) Integer.parseInt(hex[i], 16); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid hex digit in MAC address."); + } + return bytes; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/MsgCode.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/MsgCode.java new file mode 100644 index 0000000..99dd0bc --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/MsgCode.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.profile; + +public class MsgCode { + /** + * 更新服务器列表 + */ + public static final int ACTION_CODE_UPDATE_SERVERLIST = 100; + /** + * 发送编辑服务器消息 + */ + public static final int ACTION_CODE_EDIT_SERVER = 101; + /** + * 获取服务器内容 + */ + public static final int ACTION_CODE_GET_SERVER = 102; + /** + * 发送唤醒消息 + */ + public static final int ACTION_CODE_WAKE_PC = 103; + /** + * 更新设备列表 + */ + public static final int ACTION_CODE_UPDATE_PCLIST = 104; + /** + * 发送编辑设备消息 + */ + public static final int ACTION_CODE_EDIT_PC = 105; + /** + * data key + */ + public static final String ACTION_DATA_KEY = "com.itryn.myeasywol.receiver.action.data.key"; +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/ProfileFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/ProfileFragment.java new file mode 100644 index 0000000..64a7171 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/ProfileFragment.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.profile; + +import android.os.Message; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.itrycn.myeasywol.fragment.AboutFragment; +import com.itrycn.myeasywol.fragment.SettingsFragment; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xpage.enums.CoreAnim; +import com.xuexiang.xui.widget.actionbar.TitleBar; +import com.xuexiang.xui.widget.textview.supertextview.SuperTextView; + +import butterknife.BindView; + +/** + * @author xuexiang + * @since 2019-10-30 00:18 + */ +@Page(anim = CoreAnim.none) +public class ProfileFragment extends BaseFragment implements SuperTextView.OnSuperTextViewClickListener { + @BindView(R.id.menu_settings) + SuperTextView menuSettings; + @BindView(R.id.menu_about) + SuperTextView menuAbout; + @BindView(R.id.menu_yijian) + SuperTextView menu_yijian; + @Override + protected void handleUIMessage(Message msg) { + + } + + @Override + protected void handleWorkerMessage(Message msg) { + + } + + /** + * @return 返回为 null意为不需要导航栏 + */ + @Override + protected TitleBar initTitle() { + return null; + } + + /** + * 布局的资源id + * + * @return + */ + @Override + protected int getLayoutId() { + return R.layout.fragment_profile; + } + + /** + * 初始化控件 + */ + @Override + protected void initViews() { + + } + + @Override + protected void initListeners() { + menuSettings.setOnSuperTextViewClickListener(this); + menuAbout.setOnSuperTextViewClickListener(this); + menu_yijian.setOnSuperTextViewClickListener(this); + } + + @SingleClick + @Override + public void onClick(SuperTextView view) { + switch(view.getId()) { + case R.id.menu_yijian: + XToastUtils.toast("请通过邮箱:zilinsoft@qq.com进行反馈。");; + break; + case R.id.menu_settings: + openNewPage(SettingsFragment.class); + break; + case R.id.menu_about: + openNewPage(AboutFragment.class); + break; + default: + break; + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/SendMsg.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/SendMsg.java new file mode 100644 index 0000000..8e85ed0 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/profile/SendMsg.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.profile; + +import android.content.Context; + +import com.itrycn.myeasywol.utils.MyBroadcastReceiver; + +public class SendMsg { + /** + * 发null广播 + * + * @param context + */ + public static void sendUpdateServer(Context context) { + MyBroadcastReceiver.sendReceiver(context, MsgCode.ACTION_CODE_UPDATE_SERVERLIST); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/trending/TrendingFragment.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/trending/TrendingFragment.java new file mode 100644 index 0000000..7729e94 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/fragment/trending/TrendingFragment.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.fragment.trending; + +import android.os.Message; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.BaseFragment; +import com.xuexiang.xpage.annotation.Page; +import com.xuexiang.xpage.enums.CoreAnim; +import com.xuexiang.xui.widget.actionbar.TitleBar; + +/** + * @author xuexiang + * @since 2019-10-30 00:19 + */ +@Page(anim = CoreAnim.none) +public class TrendingFragment extends BaseFragment { + + @Override + protected void handleUIMessage(Message msg) { + + } + + @Override + protected void handleWorkerMessage(Message msg) { + + } + + /** + * @return 返回为 null意为不需要导航栏 + */ + @Override + protected TitleBar initTitle() { + return null; + } + + /** + * 布局的资源id + * + * @return + */ + @Override + protected int getLayoutId() { + return R.layout.fragment_trending; + } + + /** + * 初始化控件 + */ + @Override + protected void initViews() { + + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/MMKVUtils.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/MMKVUtils.java new file mode 100644 index 0000000..eba62e5 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/MMKVUtils.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils; + + +import android.content.Context; +import android.os.Parcelable; + +import com.tencent.mmkv.MMKV; + +import java.util.Set; + +/** + * MMKV工具类 + * + * @author xuexiang + * @since 2019-07-04 10:20 + */ +public final class MMKVUtils { + + private MMKVUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + private static MMKV sMMKV; + + /** + * 初始化 + * + * @param context + */ + public static void init(Context context) { + MMKV.initialize(context.getApplicationContext()); + sMMKV = MMKV.defaultMMKV(); + } + + public static MMKV getsMMKV() { + if (sMMKV == null) { + sMMKV = MMKV.defaultMMKV(); + } + return sMMKV; + } + + //=======================================键值保存==================================================// + + /** + * 保存键值 + * + * @param key + * @param value + * @return + */ + public static boolean put(String key, Object value) { + if (value instanceof Integer) { + return getsMMKV().encode(key, (Integer) value); + } else if (value instanceof Float) { + return getsMMKV().encode(key, (Float) value); + } else if (value instanceof String) { + return getsMMKV().encode(key, (String) value); + } else if (value instanceof Boolean) { + return getsMMKV().encode(key, (Boolean) value); + } else if (value instanceof Long) { + return getsMMKV().encode(key, (Long) value); + } else if (value instanceof Double) { + return getsMMKV().encode(key, (Double) value); + } else if (value instanceof Parcelable) { + return getsMMKV().encode(key, (Parcelable) value); + } else if (value instanceof byte[]) { + return getsMMKV().encode(key, (byte[]) value); + } else if (value instanceof Set) { + return getsMMKV().encode(key, (Set) value); + } + return false; + } + + + //=======================================键值获取==================================================// + + /** + * 获取键值 + * + * @param key + * @param defaultValue + * @return + */ + public static Object get(String key, Object defaultValue) { + if (defaultValue instanceof Integer) { + return getsMMKV().decodeInt(key, (Integer) defaultValue); + } else if (defaultValue instanceof Float) { + return getsMMKV().decodeFloat(key, (Float) defaultValue); + } else if (defaultValue instanceof String) { + return getsMMKV().decodeString(key, (String) defaultValue); + } else if (defaultValue instanceof Boolean) { + return getsMMKV().decodeBool(key, (Boolean) defaultValue); + } else if (defaultValue instanceof Long) { + return getsMMKV().decodeLong(key, (Long) defaultValue); + } else if (defaultValue instanceof Double) { + return getsMMKV().decodeDouble(key, (Double) defaultValue); + } else if (defaultValue instanceof byte[]) { + return getsMMKV().decodeBytes(key); + } else if (defaultValue instanceof Set) { + return getsMMKV().decodeStringSet(key, (Set) defaultValue); + } + return null; + } + + + /** + * 根据key获取boolean值 + * + * @param key + * @param defValue + * @return + */ + public static boolean getBoolean(String key, boolean defValue) { + try { + return getsMMKV().getBoolean(key, defValue); + } catch (Exception e) { + e.printStackTrace(); + } + return defValue; + } + + /** + * 根据key获取long值 + * + * @param key + * @param defValue + * @return + */ + public static long getLong(String key, long defValue) { + try { + return getsMMKV().getLong(key, defValue); + } catch (Exception e) { + e.printStackTrace(); + } + return defValue; + } + + /** + * 根据key获取float值 + * + * @param key + * @param defValue + * @return + */ + public static float getFloat(String key, float defValue) { + try { + return getsMMKV().getFloat(key, defValue); + } catch (Exception e) { + e.printStackTrace(); + } + return defValue; + } + + /** + * 根据key获取String值 + * + * @param key + * @param defValue + * @return + */ + public static String getString(String key, String defValue) { + try { + return getsMMKV().getString(key, defValue); + } catch (Exception e) { + e.printStackTrace(); + } + return defValue; + } + + /** + * 根据key获取int值 + * + * @param key + * @param defValue + * @return + */ + public static int getInt(String key, int defValue) { + try { + return getsMMKV().getInt(key, defValue); + } catch (Exception e) { + e.printStackTrace(); + } + return defValue; + } + + + /** + * 根据key获取double值 + * + * @param key + * @param defValue + * @return + */ + public static double getDouble(String key, double defValue) { + try { + return getsMMKV().decodeDouble(key, defValue); + } catch (Exception e) { + e.printStackTrace(); + } + return defValue; + } + + + /** + * 获取对象 + * + * @param key + * @param tClass 类型 + * @param + * @return + */ + public static T getObject(String key, Class tClass) { + return getsMMKV().decodeParcelable(key, tClass); + } + + /** + * 获取对象 + * + * @param key + * @param tClass 类型 + * @param + * @return + */ + public static T getObject(String key, Class tClass, T defValue) { + try { + return getsMMKV().decodeParcelable(key, tClass, defValue); + } catch (Exception e) { + e.printStackTrace(); + } + return defValue; + } + + + /** + * 判断键值对是否存在 + * + * @param key 键 + * @return 键值对是否存在 + */ + public static boolean containsKey(String key) { + return getsMMKV().containsKey(key); + } + + /** + * 清除指定键值对 + * + * @param key 键 + */ + public static void remove(String key) { + getsMMKV().remove(key).apply(); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/MyBroadcastReceiver.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/MyBroadcastReceiver.java new file mode 100644 index 0000000..d110a70 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/MyBroadcastReceiver.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.text.TextUtils; + +public class MyBroadcastReceiver { + /** + * audio的receiver的action + */ + private static final String RECEIVER_ACTION = "com.itrycn.myeasywol.receiver.action"; + /** + * code key + */ + public static final String ACTION_CODE_KEY = "com.itrycn.myeasywol.action.code.key"; + /** + * bundle key + */ + public static final String ACTION_BUNDLEKEY = "com.itrycn.myeasywol.receiver.action.bundle.key"; + /** + * 动态广播自定义权限 + */ + public final static String RECEIVER_PERMISSION = "com.itrycn.myeasywol.permissions.RECEIVER"; + private android.content.BroadcastReceiver mBroadcastReceiver; + private IntentFilter mIntentFilter; + private MyReceiverListener mReceiverListener; + + public MyBroadcastReceiver() { + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(RECEIVER_ACTION); + } + /** + * 注册广播 + * + * @param context + */ + public void registerReceiver(Context context) { + + mBroadcastReceiver = new android.content.BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + + if (mReceiverListener != null) { + int code = intent.getIntExtra(ACTION_CODE_KEY, -1); + if (code != -1) { + mReceiverListener.onReceive(context, intent, code); + } + } + } + }; + context.registerReceiver(mBroadcastReceiver, mIntentFilter, RECEIVER_PERMISSION, null); + } + + /** + * 发广播 + * + * @param context + * @param code + * @param bundleKey + * @param bundleValue + */ + public static void sendReceiver(Context context, int code, String bundleKey, Bundle bundleValue) { + Intent intent = new Intent(RECEIVER_ACTION); + intent.putExtra(ACTION_CODE_KEY, code); + if (!TextUtils.isEmpty(bundleKey) && bundleValue != null) { + intent.putExtra(bundleKey, bundleValue); + } + intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent,RECEIVER_PERMISSION); + } + /** + * 发广播 + * + * @param context + * @param code + */ + public static void sendReceiver(Context context, int code) { + sendReceiver(context, code, null, null); + } + /** + * 取消注册广播 + */ + public void unregisterReceiver(Context context) { + if (mBroadcastReceiver != null) { + context.unregisterReceiver(mBroadcastReceiver); + } + } + + public interface MyReceiverListener { + void onReceive(Context context, Intent intent, int code); + } + + public void setReceiverListener(MyReceiverListener ReceiverListener) { + this.mReceiverListener = ReceiverListener; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/RandomUtils.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/RandomUtils.java new file mode 100644 index 0000000..5140d81 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/RandomUtils.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils; + +import android.graphics.Color; +import android.text.TextUtils; + +import java.util.Random; + +/** + *

+ *     desc   : Random Utils
+ *     author : xuexiang
+ *     time   : 2018/4/28 上午12:41
+ * 
+ *
    + * Shuffling algorithm + *
  • {@link #shuffle(Object[])} Shuffling algorithm, Randomly permutes the specified array using a default source of + * randomness
  • + *
  • {@link #shuffle(Object[], int)} Shuffling algorithm, Randomly permutes the specified array
  • + *
  • {@link #shuffle(int[])} Shuffling algorithm, Randomly permutes the specified int array using a default source of + * randomness
  • + *
  • {@link #shuffle(int[], int)} Shuffling algorithm, Randomly permutes the specified int array
  • + *
+ *
    + * get random int + *
  • {@link #getRandom(int)} get random int between 0 and max
  • + *
  • {@link #getRandom(int, int)} get random int between min and max
  • + *
+ *
    + * get random numbers or letters + *
  • {@link #getRandomCapitalLetters(int)} get a fixed-length random string, its a mixture of uppercase letters
  • + *
  • {@link #getRandomLetters(int)} get a fixed-length random string, its a mixture of uppercase and lowercase letters + *
  • + *
  • {@link #getRandomLowerCaseLetters(int)} get a fixed-length random string, its a mixture of lowercase letters
  • + *
  • {@link #getRandomNumbers(int)} get a fixed-length random string, its a mixture of numbers
  • + *
  • {@link #getRandomNumbersAndLetters(int)} get a fixed-length random string, its a mixture of uppercase, lowercase + * letters and numbers
  • + *
  • {@link #getRandom(String, int)} get a fixed-length random string, its a mixture of chars in source
  • + *
  • {@link #getRandom(char[], int)} get a fixed-length random string, its a mixture of chars in sourceChar
  • + *
+ * + */ +public final class RandomUtils { + + public static final String NUMBERS_AND_LETTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public static final String NUMBERS = "0123456789"; + public static final String LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public static final String LOWER_CASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"; + + /** + * Don't let anyone instantiate this class. + */ + private RandomUtils() { + throw new Error("Do not need instantiate!"); + } + + /** + * 在数字和英文字母中获取一个定长的随机字符串 + * + * @param length 长度 + * @return 随机字符串 + * @see RandomUtils#getRandom(String source, int length) + */ + public static String getRandomNumbersAndLetters(int length) { + return getRandom(NUMBERS_AND_LETTERS, length); + } + + /** + * 在数字中获取一个定长的随机字符串 + * + * @param length 长度 + * @return 随机数字符串 + * @see RandomUtils#getRandom(String source, int length) + */ + public static String getRandomNumbers(int length) { + return getRandom(NUMBERS, length); + } + + /** + * 在英文字母中获取一个定长的随机字符串 + * + * @param length 长度 + * @return 随机字母字符串 + * @see RandomUtils#getRandom(String source, int length) + */ + public static String getRandomLetters(int length) { + return getRandom(LETTERS, length); + } + + /** + * 在大写英文字母中获取一个定长的随机字符串 + * + * @param length 长度 + * @return 随机字符串 只包含大写字母 + * @see RandomUtils#getRandom(String source, int length) + */ + public static String getRandomCapitalLetters(int length) { + return getRandom(CAPITAL_LETTERS, length); + } + + /** + * 在小写英文字母中获取一个定长的随机字符串 + * + * @param length 长度 + * @return 随机字符串 只包含小写字母 + * @see RandomUtils#getRandom(String source, int length) + */ + public static String getRandomLowerCaseLetters(int length) { + return getRandom(LOWER_CASE_LETTERS, length); + } + + /** + * 在一个字符数组源中获取一个定长的随机字符串 + * + * @param source 源字符串 + * @param length 长度 + * @return
    + *
  • if source is null or empty, return null
  • + *
  • else see {@link RandomUtils#getRandom(char[] sourceChar, int length)}
  • + *
+ */ + public static String getRandom(String source, int length) { + return TextUtils.isEmpty(source) ? null : getRandom(source.toCharArray(), length); + } + + /** + * 在一个字符数组源中获取一个定长的随机字符串 + * + * @param sourceChar 字符数组源 + * @param length 长度 + * @return
    + *
  • if sourceChar is null or empty, return null
  • + *
  • if length less than 0, return null
  • + *
+ */ + public static String getRandom(char[] sourceChar, int length) { + if (sourceChar == null || sourceChar.length == 0 || length < 0) { + return null; + } + + StringBuilder str = new StringBuilder(length); + Random random = new Random(); + for (int i = 0; i < length; i++) { + str.append(sourceChar[random.nextInt(sourceChar.length)]); + } + return str.toString(); + } + + /** + * get random int between 0 and max + * + * @param max 最大随机数 + * @return
    + *
  • if max <= 0, return 0
  • + *
  • else return random int between 0 and max
  • + *
+ */ + public static int getRandom(int max) { + return getRandom(0, max); + } + + /** + * get random int between min and max + * + * @param min 最小随机数 + * @param max 最大随机数 + * @return
    + *
  • if min > max, return 0
  • + *
  • if min == max, return min
  • + *
  • else return random int between min and max
  • + *
+ */ + public static int getRandom(int min, int max) { + if (min > max) { + return 0; + } + if (min == max) { + return min; + } + return min + new Random().nextInt(max - min); + } + + /** + * 获取随机颜色 + * + * @return + */ + public static int getRandomColor() { + Random random = new Random(); + int r = random.nextInt(256); + int g = random.nextInt(256); + int b = random.nextInt(256); + return Color.rgb(r, g, b); + } + + /** + * 随机打乱数组中的内容 + * + * @param objArray + * @return + */ + public static boolean shuffle(Object[] objArray) { + if (objArray == null) { + return false; + } + + return shuffle(objArray, getRandom(objArray.length)); + } + + /** + * 随机打乱数组中的内容 + * + * @param objArray + * @param shuffleCount + * @return + */ + public static boolean shuffle(Object[] objArray, int shuffleCount) { + int length; + if (objArray == null || shuffleCount < 0 || (length = objArray.length) < shuffleCount) { + return false; + } + + for (int i = 1; i <= shuffleCount; i++) { + int random = getRandom(length - i); + Object temp = objArray[length - i]; + objArray[length - i] = objArray[random]; + objArray[random] = temp; + } + return true; + } + + /** + * 随机打乱数组中的内容 + * + * @param intArray + * @return + */ + public static int[] shuffle(int[] intArray) { + if (intArray == null) { + return null; + } + + return shuffle(intArray, getRandom(intArray.length)); + } + + /** + * 随机打乱数组中的内容 + * + * @param intArray + * @param shuffleCount + * @return + */ + public static int[] shuffle(int[] intArray, int shuffleCount) { + int length; + if (intArray == null || shuffleCount < 0 || (length = intArray.length) < shuffleCount) { + return null; + } + + int[] out = new int[shuffleCount]; + for (int i = 1; i <= shuffleCount; i++) { + int random = getRandom(length - i); + out[i - 1] = intArray[random]; + int temp = intArray[length - i]; + intArray[length - i] = intArray[random]; + intArray[random] = temp; + } + return out; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/SettingUtils.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/SettingUtils.java new file mode 100644 index 0000000..92b9562 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/SettingUtils.java @@ -0,0 +1,46 @@ +package com.itrycn.myeasywol.utils; + + +/** + * SharedPreferences管理工具基类 + * + * @author xuexiang + * @since 2018/11/27 下午5:16 + */ +public final class SettingUtils { + + private SettingUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + private static final String IS_FIRST_OPEN_KEY = "is_first_open_key"; + + private static final String IS_AGREE_PRIVACY_KEY = "is_agree_privacy_key"; + + /** + * 是否是第一次启动 + */ + public static boolean isFirstOpen() { + return MMKVUtils.getBoolean(IS_FIRST_OPEN_KEY, true); + } + + /** + * 设置是否是第一次启动 + */ + public static void setIsFirstOpen(boolean isFirstOpen) { + MMKVUtils.put(IS_FIRST_OPEN_KEY, isFirstOpen); + } + + /** + * @return 是否同意隐私政策 + */ + public static boolean isAgreePrivacy() { + return MMKVUtils.getBoolean(IS_AGREE_PRIVACY_KEY, false); + } + + public static void setIsAgreePrivacy(boolean isAgreePrivacy) { + MMKVUtils.put(IS_AGREE_PRIVACY_KEY, isAgreePrivacy); + } + + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/TokenUtils.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/TokenUtils.java new file mode 100644 index 0000000..60c7955 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/TokenUtils.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils; + +import android.content.Context; + +import com.xuexiang.xutil.app.ActivityUtils; +import com.xuexiang.xutil.common.StringUtils; + +/** + * Token管理工具 + * + * @author xuexiang + * @since 2019-11-17 22:37 + */ +public final class TokenUtils { + + private static String sToken; + + private static final String KEY_TOKEN = "com.itrycn.myeasywol.utils.KEY_TOKEN"; + + private TokenUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + private static final String KEY_PROFILE_CHANNEL = "github"; + + /** + * 初始化Token信息 + */ + public static void init(Context context) { + MMKVUtils.init(context); + sToken = MMKVUtils.getString(KEY_TOKEN, ""); + } + + public static void setToken(String token) { + sToken = token; + MMKVUtils.put(KEY_TOKEN, token); + } + + public static void clearToken() { + sToken = null; + MMKVUtils.remove(KEY_TOKEN); + } + + public static String getToken() { + return sToken; + } + + public static boolean hasToken() { + return MMKVUtils.containsKey(KEY_TOKEN); + } + + /** + * 处理登录成功的事件 + * + * @param token 账户信息 + */ + public static boolean handleLoginSuccess(String token) { + if (!StringUtils.isEmpty(token)) { + XToastUtils.success("登录成功!"); + setToken(token); + return true; + } else { + XToastUtils.error("登录失败!"); + return false; + } + } + + /** + * 处理登出的事件 + */ + public static void handleLogoutSuccess() { + //登出时,清除账号信息 + clearToken(); + XToastUtils.success("登出成功!"); + //跳转到登录页 + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/Utils.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/Utils.java new file mode 100644 index 0000000..5c73e68 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/Utils.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Color; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; + +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.webview.AgentWebActivity; +import com.xuexiang.xui.utils.ResUtils; +import com.xuexiang.xui.widget.dialog.DialogLoader; +import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction; +import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog; +import com.xuexiang.xutil.XUtil; + +import static com.itrycn.myeasywol.core.webview.AgentWebFragment.KEY_URL; + +/** + * 工具类 + * + * @author xuexiang + * @since 2020-02-23 15:12 + */ +public final class Utils { + + private Utils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + /** + * 这里填写你的应用隐私政策网页地址 + */ + private static final String PRIVACY_URL = "https://gitee.com/xuexiangjys/TemplateAppProject/raw/master/LICENSE"; + + /** + * 显示隐私政策的提示 + * + * @param context + * @param submitListener 同意的监听 + * @return + */ + public static Dialog showPrivacyDialog(Context context, MaterialDialog.SingleButtonCallback submitListener) { + MaterialDialog dialog = new MaterialDialog.Builder(context).title(R.string.title_reminder).autoDismiss(false).cancelable(false) + .positiveText(R.string.lab_agree).onPositive((dialog1, which) -> { + if (submitListener != null) { + submitListener.onClick(dialog1, which); + } else { + dialog1.dismiss(); + } + }) + .negativeText(R.string.lab_disagree).onNegative(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + dialog.dismiss(); + DialogLoader.getInstance().showConfirmDialog(context, ResUtils.getString(R.string.title_reminder), String.format(ResUtils.getString(R.string.content_privacy_explain_again), ResUtils.getString(R.string.app_name)), ResUtils.getString(R.string.lab_look_again), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + showPrivacyDialog(context, submitListener); + } + }, ResUtils.getString(R.string.lab_still_disagree), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + DialogLoader.getInstance().showConfirmDialog(context, ResUtils.getString(R.string.content_think_about_it_again), ResUtils.getString(R.string.lab_look_again), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + showPrivacyDialog(context, submitListener); + } + }, ResUtils.getString(R.string.lab_exit_app), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + XUtil.exitApp(); + } + }); + } + }); + } + }).build(); + dialog.setContent(getPrivacyContent(context)); + //开始响应点击事件 + dialog.getContentView().setMovementMethod(LinkMovementMethod.getInstance()); + dialog.show(); + return dialog; + } + + /** + * @return 隐私政策说明 + */ + private static SpannableStringBuilder getPrivacyContent(Context context) { + SpannableStringBuilder stringBuilder = new SpannableStringBuilder() + .append(" 欢迎来到").append(ResUtils.getString(R.string.app_name)).append("!\n") + .append(" 我们深知个人信息对你的重要性,也感谢你对我们的信任。\n") + .append(" 为了更好地保护你的权益,同时遵守相关监管的要求,我们将通过"); + stringBuilder.append(getPrivacyLink(context, PRIVACY_URL)) + .append("向你说明我们会如何收集、存储、保护、使用及对外提供你的信息,并说明你享有的权利。\n") + .append(" 更多详情,敬请查阅") + .append(getPrivacyLink(context, PRIVACY_URL)) + .append("全文。"); + return stringBuilder; + } + + /** + * @param context 隐私政策的链接 + * @return + */ + private static SpannableString getPrivacyLink(Context context, String privacyUrl) { + String privacyName = String.format(ResUtils.getString(R.string.lab_privacy_name), ResUtils.getString(R.string.app_name)); + SpannableString spannableString = new SpannableString(privacyName); + spannableString.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + goWeb(context, privacyUrl); + } + }, 0, privacyName.length(), Spanned.SPAN_MARK_MARK); + return spannableString; + } + + + /** + * 请求浏览器 + * + * @param url + */ + public static void goWeb(Context context, final String url) { + Intent intent = new Intent(context, AgentWebActivity.class); + intent.putExtra(KEY_URL, url); + context.startActivity(intent); + } + + + /** + * 是否是深色的颜色 + * + * @param color + * @return + */ + public static boolean isColorDark(@ColorInt int color) { + double darkness = + 1 + - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) + / 255; + return darkness >= 0.382; + } + + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/XToastUtils.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/XToastUtils.java new file mode 100644 index 0000000..11a52c7 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/XToastUtils.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils; + +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import com.xuexiang.xui.XUI; +import com.xuexiang.xui.widget.toast.XToast; + +/** + * xtoast 工具类 + * + * @author xuexiang + * @since 2019-06-30 19:04 + */ +public final class XToastUtils { + + + private XToastUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + static { + XToast.Config.get() + .setAlpha(200) + .allowQueue(false); + } + + //======普通土司=======// + + @MainThread + public static void toast(@NonNull CharSequence message) { + XToast.normal(XUI.getContext(), message).show(); + } + + @MainThread + public static void toast(@StringRes int message) { + XToast.normal(XUI.getContext(), message).show(); + } + + @MainThread + public static void toast(@NonNull CharSequence message, int duration) { + XToast.normal(XUI.getContext(), message, duration).show(); + } + + @MainThread + public static void toast(@StringRes int message, int duration) { + XToast.normal(XUI.getContext(), message, duration).show(); + } + + //======错误【红色】=======// + + @MainThread + public static void error(@NonNull Throwable throwable) { + XToast.error(XUI.getContext(), throwable.getMessage()).show(); + } + + @MainThread + public static void error(@NonNull CharSequence message) { + XToast.error(XUI.getContext(), message).show(); + } + + @MainThread + public static void error(@StringRes int message) { + XToast.error(XUI.getContext(), message).show(); + } + + @MainThread + public static void error(@NonNull CharSequence message, int duration) { + XToast.error(XUI.getContext(), message, duration).show(); + } + + @MainThread + public static void error(@StringRes int message, int duration) { + XToast.error(XUI.getContext(), message, duration).show(); + } + + //======成功【绿色】=======// + + @MainThread + public static void success(@NonNull CharSequence message) { + XToast.success(XUI.getContext(), message).show(); + } + + @MainThread + public static void success(@StringRes int message) { + XToast.success(XUI.getContext(), message).show(); + } + + @MainThread + public static void success(@NonNull CharSequence message, int duration) { + XToast.success(XUI.getContext(), message, duration).show(); + } + + @MainThread + public static void success(@StringRes int message, int duration) { + XToast.success(XUI.getContext(), message, duration).show(); + } + + //======信息【蓝色】=======// + + @MainThread + public static void info(@NonNull CharSequence message) { + XToast.info(XUI.getContext(), message).show(); + } + + @MainThread + public static void info(@StringRes int message) { + XToast.info(XUI.getContext(), message).show(); + } + + @MainThread + public static void info(@NonNull CharSequence message, int duration) { + XToast.info(XUI.getContext(), message, duration).show(); + } + + @MainThread + public static void info(@StringRes int message, int duration) { + XToast.info(XUI.getContext(), message, duration).show(); + } + + //=======警告【黄色】======// + + @MainThread + public static void warning(@NonNull CharSequence message) { + XToast.warning(XUI.getContext(), message).show(); + } + + @MainThread + public static void warning(@StringRes int message) { + XToast.warning(XUI.getContext(), message).show(); + } + + @MainThread + public static void warning(@NonNull CharSequence message, int duration) { + XToast.warning(XUI.getContext(), message, duration).show(); + } + + @MainThread + public static void warning(@StringRes int message, int duration) { + XToast.warning(XUI.getContext(), message, duration).show(); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/ANRWatchDogInit.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/ANRWatchDogInit.java new file mode 100644 index 0000000..6ad10ff --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/ANRWatchDogInit.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.sdkinit; + +import com.github.anrwatchdog.ANRWatchDog; +import com.xuexiang.xutil.common.logger.Logger; + +/** + * ANR看门狗监听器初始化 + * + * @author xuexiang + * @since 2020-02-18 15:08 + */ +public final class ANRWatchDogInit { + + private static final String TAG = "ANRWatchDog"; + + private ANRWatchDogInit() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + /** + * ANR看门狗 + */ + private static ANRWatchDog sANRWatchDog; + + /** + * ANR监听触发的时间 + */ + private static final int ANR_DURATION = 4000; + + + /** + * ANR静默处理【就是不处理,直接记录一下日志】 + */ + private final static ANRWatchDog.ANRListener SILENT_LISTENER = error -> Logger.eTag(TAG, error); + + /** + * ANR自定义处理【可以是记录日志用于上传】 + */ + private final static ANRWatchDog.ANRListener CUSTOM_LISTENER = error -> { + Logger.eTag(TAG, "Detected Application Not Responding!", error); + //这里进行ANR的捕获后的操作 + + throw error; + }; + + public static void init() { + //这里设置监听的间隔为2秒 + sANRWatchDog = new ANRWatchDog(2000); + sANRWatchDog.setANRInterceptor(duration -> { + long ret = ANR_DURATION - duration; + if (ret > 0) { + Logger.wTag(TAG, "Intercepted ANR that is too short (" + duration + " ms), postponing for " + ret + " ms."); + } + //当返回是0或者负数时,就会触发ANR监听回调 + return ret; + }).setANRListener(SILENT_LISTENER).start(); + } + + public static ANRWatchDog getANRWatchDog() { + return sANRWatchDog; + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/XBasicLibInit.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/XBasicLibInit.java new file mode 100644 index 0000000..be79932 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/XBasicLibInit.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.sdkinit; + +import android.app.Application; + +import com.itrycn.myeasywol.MyApp; +import com.itrycn.myeasywol.core.BaseActivity; +import com.itrycn.myeasywol.utils.TokenUtils; +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xaop.XAOP; +import com.xuexiang.xhttp2.XHttpSDK; +import com.xuexiang.xpage.AppPageConfig; +import com.xuexiang.xpage.PageConfig; +import com.xuexiang.xrouter.launcher.XRouter; +import com.xuexiang.xui.XUI; +import com.xuexiang.xutil.XUtil; +import com.xuexiang.xutil.common.StringUtils; + +/** + * X系列基础库初始化 + * + * @author xuexiang + * @since 2019-06-30 23:54 + */ +public final class XBasicLibInit { + + private XBasicLibInit() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + /** + * 初始化基础库SDK + */ + public static void init(Application application) { + //工具类 + initXUtil(application); + + //网络请求框架 + initXHttp2(application); + + //页面框架 + initXPage(application); + + //切片框架 + initXAOP(application); + + //UI框架 + initXUI(application); + + //路由框架 + initRouter(application); + } + + /** + * 初始化XUtil工具类 + */ + private static void initXUtil(Application application) { + XUtil.init(application); + XUtil.debug(MyApp.isDebug()); + TokenUtils.init(application); + } + + /** + * 初始化XHttp2 + */ + private static void initXHttp2(Application application) { + //初始化网络请求框架,必须首先执行 + XHttpSDK.init(application); + //需要调试的时候执行 + if (MyApp.isDebug()) { + XHttpSDK.debug(); + } +// XHttpSDK.debug(new CustomLoggingInterceptor()); //设置自定义的日志打印拦截器 + //设置网络请求的全局基础地址 + XHttpSDK.setBaseUrl("https://gitee.com/"); +// //设置动态参数添加拦截器 +// XHttpSDK.addInterceptor(new CustomDynamicInterceptor()); +// //请求失效校验拦截器 +// XHttpSDK.addInterceptor(new CustomExpiredInterceptor()); + } + + /** + * 初始化XPage页面框架 + */ + private static void initXPage(Application application) { + PageConfig.getInstance() + //页面注册 + .setPageConfiguration(context -> { + //自动注册页面,是编译时自动生成的,build一下就出来了 + return AppPageConfig.getInstance().getPages(); + }) + .debug(MyApp.isDebug() ? "PageLog" : null) + .enableWatcher(MyApp.isDebug()) + .setContainActivityClazz(BaseActivity.class) + .init(application); + } + + /** + * 初始化XAOP + */ + private static void initXAOP(Application application) { + XAOP.init(application); + XAOP.debug(MyApp.isDebug()); + //设置动态申请权限切片 申请权限被拒绝的事件响应监听 + XAOP.setOnPermissionDeniedListener(permissionsDenied -> XToastUtils.error("权限申请被拒绝:" + StringUtils.listToString(permissionsDenied, ","))); + } + + /** + * 初始化XUI框架 + */ + private static void initXUI(Application application) { + XUI.init(application); + XUI.debug(MyApp.isDebug()); + } + + /** + * 初始化路由框架 + */ + private static void initRouter(Application application) { + // 这两行必须写在init之前,否则这些配置在init过程中将无效 + if (MyApp.isDebug()) { + XRouter.openLog(); // 打印日志 + XRouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) + } + XRouter.init(application); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/XUpdateInit.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/XUpdateInit.java new file mode 100644 index 0000000..8919c9c --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/sdkinit/XUpdateInit.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.sdkinit; + +import android.app.Application; +import android.content.Context; + +import com.itrycn.myeasywol.MyApp; +import com.itrycn.myeasywol.utils.update.CustomUpdateDownloader; +import com.itrycn.myeasywol.utils.update.CustomUpdateFailureListener; +import com.itrycn.myeasywol.utils.update.XHttpUpdateHttpServiceImpl; +import com.xuexiang.xupdate.XUpdate; +import com.xuexiang.xupdate.utils.UpdateUtils; + +/** + * XUpdate 版本更新 SDK 初始化 + * + * @author xuexiang + * @since 2019-06-18 15:51 + */ +public final class XUpdateInit { + + private XUpdateInit() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + /** + * 应用版本更新的检查地址 + */ + private static final String KEY_UPDATE_URL = "http://www.itrycn.com/update/myeasywol.json"; + + public static void init(Application application) { + XUpdate.get() + .debug(MyApp.isDebug()) + //默认设置只在wifi下检查版本更新 + .isWifiOnly(false) + //默认设置使用get请求检查版本 + .isGet(true) + //默认设置非自动模式,可根据具体使用配置 + .isAutoMode(false) + //设置默认公共请求参数 + .param("versionCode", UpdateUtils.getVersionCode(application)) + .param("appKey", application.getPackageName()) + //这个必须设置!实现网络请求功能。 + .setIUpdateHttpService(new XHttpUpdateHttpServiceImpl()) + .setIUpdateDownLoader(new CustomUpdateDownloader()) + //这个必须初始化 + .init(application); + } + + /** + * 进行版本更新检查 + */ + public static void checkUpdate(Context context, boolean needErrorTip) { + XUpdate.newBuild(context).updateUrl(KEY_UPDATE_URL).update(); + XUpdate.get().setOnUpdateFailureListener(new CustomUpdateFailureListener(needErrorTip)); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/service/JsonSerializationService.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/service/JsonSerializationService.java new file mode 100644 index 0000000..ea3aa2c --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/service/JsonSerializationService.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.service; + +import android.content.Context; + +import com.xuexiang.xrouter.annotation.Router; +import com.xuexiang.xrouter.facade.service.SerializationService; +import com.xuexiang.xutil.net.JsonUtil; + +import java.lang.reflect.Type; + +/** + * @author XUE + * @since 2019/3/27 16:39 + */ +@Router(path = "/service/json") +public class JsonSerializationService implements SerializationService { + /** + * 对象序列化为json + * + * @param instance obj + * @return json string + */ + @Override + public String object2Json(Object instance) { + return JsonUtil.toJson(instance); + } + + /** + * json反序列化为对象 + * + * @param input json string + * @param clazz object type + * @return instance of object + */ + @Override + public T parseObject(String input, Type clazz) { + return JsonUtil.fromJson(input, clazz); + } + + /** + * 进程初始化的方法 + * + * @param context 上下文 + */ + @Override + public void init(Context context) { + + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateDownloader.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateDownloader.java new file mode 100644 index 0000000..b4e41bd --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateDownloader.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.update; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.xuexiang.xupdate.entity.UpdateEntity; +import com.xuexiang.xupdate.proxy.impl.DefaultUpdateDownloader; +import com.xuexiang.xupdate.service.OnFileDownloadListener; +import com.xuexiang.xutil.app.ActivityUtils; + +/** + * 重写DefaultUpdateDownloader,在取消下载时,弹出提示 + * + * @author xuexiang + * @since 2019-06-14 23:47 + */ +public class CustomUpdateDownloader extends DefaultUpdateDownloader { + + private boolean mIsStartDownload; + + @Override + public void startDownload(@NonNull UpdateEntity updateEntity, @Nullable OnFileDownloadListener downloadListener) { + super.startDownload(updateEntity, downloadListener); + mIsStartDownload = true; + + } + + @Override + public void cancelDownload() { + super.cancelDownload(); + if (mIsStartDownload) { + mIsStartDownload = false; + ActivityUtils.startActivity(UpdateTipDialog.class); + } + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateFailureListener.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateFailureListener.java new file mode 100644 index 0000000..1590bea --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateFailureListener.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.update; + +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xupdate.entity.UpdateError; +import com.xuexiang.xupdate.listener.OnUpdateFailureListener; + +/** + * 自定义版本更新提示 + * + * @author xuexiang + * @since 2019/4/15 上午12:01 + */ +public class CustomUpdateFailureListener implements OnUpdateFailureListener { + + /** + * 是否需要错误提示 + */ + private boolean mNeedErrorTip; + + public CustomUpdateFailureListener() { + this(true); + } + + public CustomUpdateFailureListener(boolean needErrorTip) { + mNeedErrorTip = needErrorTip; + } + + /** + * 更新失败 + * + * @param error 错误 + */ + @Override + public void onFailure(UpdateError error) { + if (mNeedErrorTip) { + XToastUtils.error(error); + } + if (error.getCode() == UpdateError.ERROR.DOWNLOAD_FAILED) { + UpdateTipDialog.show("Github被墙无法下载,是否考虑切换蒲公英下载?"); + } + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateParser.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateParser.java new file mode 100644 index 0000000..b2db7f4 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/CustomUpdateParser.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.update; + +import com.xuexiang.xupdate.entity.UpdateEntity; +import com.xuexiang.xupdate.proxy.impl.AbstractUpdateParser; + +/** + * 版本更新信息自定义json解析器 + * + * @author xuexiang + * @since 2020-02-18 13:01 + */ +public class CustomUpdateParser extends AbstractUpdateParser { + + @Override + public UpdateEntity parseJson(String json) throws Exception { + // TODO: 2020-02-18 这里填写你需要自定义的json格式 + return null; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/UpdateTipDialog.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/UpdateTipDialog.java new file mode 100644 index 0000000..e51dcc3 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/UpdateTipDialog.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.update; + +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.xuexiang.xupdate.XUpdate; + +/** + * 版本更新提示弹窗 + * + * @author xuexiang + * @since 2019-06-15 00:06 + */ +public class UpdateTipDialog extends AppCompatActivity implements DialogInterface.OnDismissListener { + + public static final String KEY_CONTENT = "com.itrycn.myeasywol.utils.update.KEY_CONTENT"; + + /** + * 显示版本更新重试提示弹窗 + * + * @param content + */ + public static void show(String content) { + Intent intent = new Intent(XUpdate.getContext(), UpdateTipDialog.class); + intent.putExtra(KEY_CONTENT, content); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + XUpdate.getContext().startActivity(intent); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + +// String content = getIntent().getStringExtra(KEY_CONTENT); +// if (TextUtils.isEmpty(content)) { +// content = "Github下载速度太慢了,是否考虑切换蒲公英下载?"; +// } +// +// DialogLoader.getInstance().showConfirmDialog(this, content, "是", (dialog, which) -> { +// dialog.dismiss(); +//// Utils.goWeb(UpdateTipDialog.this, "这里填写你应用下载页面的链接"); +// }, "否") +// .setOnDismissListener(this); + + } + + @Override + public void onDismiss(DialogInterface dialog) { + finish(); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/XHttpUpdateHttpServiceImpl.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/XHttpUpdateHttpServiceImpl.java new file mode 100644 index 0000000..3f85aae --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/utils/update/XHttpUpdateHttpServiceImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.utils.update; + +import androidx.annotation.NonNull; + +import com.itrycn.myeasywol.utils.XToastUtils; +import com.xuexiang.xhttp2.XHttp; +import com.xuexiang.xhttp2.XHttpSDK; +import com.xuexiang.xhttp2.callback.DownloadProgressCallBack; +import com.xuexiang.xhttp2.callback.SimpleCallBack; +import com.xuexiang.xhttp2.exception.ApiException; +import com.xuexiang.xupdate.proxy.IUpdateHttpService; +import com.xuexiang.xutil.file.FileUtils; +import com.xuexiang.xutil.net.JsonUtil; + +import java.util.Map; + +/** + * XHttp2实现的请求更新 + * + * @author xuexiang + * @since 2018/8/12 上午11:46 + */ +public class XHttpUpdateHttpServiceImpl implements IUpdateHttpService { + + @Override + public void asyncGet(@NonNull String url, @NonNull Map params, @NonNull final IUpdateHttpService.Callback callBack) { + XHttp.get(url) + .params(params) + .keepJson(true) + .execute(new SimpleCallBack() { + @Override + public void onSuccess(String response) throws Throwable { + callBack.onSuccess(response); + } + @Override + public void onError(ApiException e) { + callBack.onError(e); + } + }); + } + + @Override + public void asyncPost(@NonNull String url, @NonNull Map params, @NonNull final IUpdateHttpService.Callback callBack) { + XHttp.post(url) + .upJson(JsonUtil.toJson(params)) + .keepJson(true) + .execute(new SimpleCallBack() { + @Override + public void onSuccess(String response) throws Throwable { + callBack.onSuccess(response); + } + + @Override + public void onError(ApiException e) { + callBack.onError(e); + } + }); + } + + @Override + public void download(@NonNull String url, @NonNull String path, @NonNull String fileName, @NonNull final IUpdateHttpService.DownloadCallback callback) { + XHttpSDK.addRequest(url, XHttp.downLoad(url) + .savePath(path) + .saveName(fileName) + .isUseBaseUrl(false) + .execute(new DownloadProgressCallBack() { + @Override + public void onStart() { + callback.onStart(); + } + + @Override + public void onError(ApiException e) { + callback.onError(e); + } + + @Override + public void update(long downLoadSize, long totalSize, boolean done) { + callback.onProgress(downLoadSize / (float) totalSize, totalSize); + } + + @Override + public void onComplete(String path) { + callback.onSuccess(FileUtils.getFileByPath(path)); + } + })); + } + + @Override + public void cancelDownload(@NonNull String url) { + XToastUtils.info("已取消更新"); + XHttpSDK.cancelRequest(url); + } +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/widget/GuideTipsDialog.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/widget/GuideTipsDialog.java new file mode 100644 index 0000000..c0f1741 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/widget/GuideTipsDialog.java @@ -0,0 +1,191 @@ +package com.itrycn.myeasywol.widget; + +import android.content.Context; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatCheckBox; + +import com.xuexiang.constant.TimeConstants; +import com.itrycn.myeasywol.R; +import com.itrycn.myeasywol.core.http.api.ApiService; +import com.itrycn.myeasywol.core.http.callback.NoTipCallBack; +import com.itrycn.myeasywol.core.http.entity.TipInfo; +import com.itrycn.myeasywol.utils.MMKVUtils; +import com.xuexiang.xaop.annotation.SingleClick; +import com.xuexiang.xhttp2.XHttp; +import com.xuexiang.xhttp2.cache.model.CacheMode; +import com.xuexiang.xhttp2.request.CustomRequest; +import com.xuexiang.xui.widget.dialog.BaseDialog; +import com.xuexiang.xutil.app.AppUtils; +import com.zzhoujay.richtext.RichText; + +import java.util.List; + +/** + * 小贴士弹窗 + * + * @author xuexiang + * @since 2019-08-22 17:02 + */ +public class GuideTipsDialog extends BaseDialog implements View.OnClickListener, CompoundButton.OnCheckedChangeListener { + + private static final String KEY_IS_IGNORE_TIPS = "com.itrycn.myeasywol.widget.key_is_ignore_tips_"; + + private List mTips; + private int mIndex = -1; + + private TextView mTvPrevious; + private TextView mTvNext; + + private TextView mTvTitle; + private TextView mTvContent; + + /** + * 显示提示 + * + * @param context 上下文 + */ + public static void showTips(final Context context) { + if (!isIgnoreTips()) { + CustomRequest request = XHttp.custom().cacheMode(CacheMode.FIRST_CACHE).cacheTime(TimeConstants.DAY).cacheKey("getTips"); + request.apiCall(request.create(ApiService.IGetService.class).getTips(), new NoTipCallBack>() { + @Override + public void onSuccess(List response) throws Throwable { + if (response != null && response.size() > 0) { + new GuideTipsDialog(context, response).show(); + } + } + }); + } + } + + public GuideTipsDialog(Context context, @NonNull List tips) { + super(context, R.layout.dialog_guide_tips); + initViews(); + updateTips(tips); + } + + /** + * 初始化弹窗 + */ + private void initViews() { + mTvTitle = findViewById(R.id.tv_title); + mTvContent = findViewById(R.id.tv_content); + AppCompatCheckBox cbIgnore = findViewById(R.id.cb_ignore); + ImageView ivClose = findViewById(R.id.iv_close); + + mTvPrevious = findViewById(R.id.tv_previous); + mTvNext = findViewById(R.id.tv_next); + + if (cbIgnore != null) { + cbIgnore.setOnCheckedChangeListener(this); + } + if (ivClose != null) { + ivClose.setOnClickListener(this); + } + mTvPrevious.setOnClickListener(this); + mTvNext.setOnClickListener(this); + mTvPrevious.setEnabled(false); + mTvNext.setEnabled(true); + setCancelable(false); + setCanceledOnTouchOutside(true); + } + + /** + * 更新提示信息 + * + * @param tips 提示信息 + */ + private void updateTips(List tips) { + mTips = tips; + if (mTips != null && mTips.size() > 0 && mTvContent != null) { + mIndex = 0; + showRichText(mTips.get(mIndex)); + } + } + + /** + * 切换提示信息 + * + * @param index 索引 + */ + private void switchTipInfo(int index) { + if (mTips != null && mTips.size() > 0 && mTvContent != null) { + if (index >= 0 && index <= mTips.size() - 1) { + showRichText(mTips.get(index)); + if (index == 0) { + mTvPrevious.setEnabled(false); + mTvNext.setEnabled(true); + } else if (index == mTips.size() - 1) { + mTvPrevious.setEnabled(true); + mTvNext.setEnabled(false); + } else { + mTvPrevious.setEnabled(true); + mTvNext.setEnabled(true); + } + } + } + } + + /** + * 显示富文本 + * + * @param tipInfo 提示信息 + */ + private void showRichText(TipInfo tipInfo) { + mTvTitle.setText(tipInfo.getTitle()); + RichText.fromHtml(tipInfo.getContent()) + .bind(this) + .into(mTvContent); + } + + + @SingleClick(300) + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.iv_close: + dismiss(); + break; + case R.id.tv_previous: + if (mIndex > 0) { + mIndex--; + switchTipInfo(mIndex); + } + break; + case R.id.tv_next: + if (mIndex < mTips.size() - 1) { + mIndex++; + switchTipInfo(mIndex); + } + break; + default: + break; + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + setIsIgnoreTips(isChecked); + } + + @Override + public void onDetachedFromWindow() { + RichText.clear(this); + super.onDetachedFromWindow(); + } + + + public static boolean setIsIgnoreTips(boolean isIgnore) { + return MMKVUtils.put(KEY_IS_IGNORE_TIPS + AppUtils.getAppVersionCode(), isIgnore); + } + + public static boolean isIgnoreTips() { + return MMKVUtils.getBoolean(KEY_IS_IGNORE_TIPS + AppUtils.getAppVersionCode(), false); + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/widget/MaterialFooter.java b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/widget/MaterialFooter.java new file mode 100644 index 0000000..5a42b57 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/java/com/itrycn/myeasywol/widget/MaterialFooter.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) + * + * 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.itrycn.myeasywol.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ProgressBar; + +import androidx.annotation.NonNull; + +import com.scwang.smartrefresh.layout.api.RefreshFooter; +import com.scwang.smartrefresh.layout.api.RefreshKernel; +import com.scwang.smartrefresh.layout.api.RefreshLayout; +import com.scwang.smartrefresh.layout.constant.RefreshState; +import com.scwang.smartrefresh.layout.constant.SpinnerStyle; +import com.scwang.smartrefresh.layout.util.DensityUtil; + +/** + * Material风格的上拉加载 + * + * @author xuexiang + * @since 2019-08-03 11:14 + */ +public class MaterialFooter extends ProgressBar implements RefreshFooter { + + public MaterialFooter(Context context) { + this(context, null); + } + + public MaterialFooter(Context context, AttributeSet attrs) { + super(context, attrs); + initView(); + } + + private void initView() { + setVisibility(GONE); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); + setPadding(0, DensityUtil.dp2px(10), 0, DensityUtil.dp2px(10)); + setLayoutParams(params); + } + + @Override + public boolean setNoMoreData(boolean noMoreData) { + return false; + } + + @NonNull + @Override + public View getView() { + return this; + } + + @NonNull + @Override + public SpinnerStyle getSpinnerStyle() { + //指定为平移,不能null + return SpinnerStyle.Translate; + } + + + @Override + public void onStartAnimator(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) { + setVisibility(VISIBLE); + } + + @Override + public int onFinish(@NonNull RefreshLayout refreshLayout, boolean success) { + setVisibility(GONE); + return 100; + } + + @Override + public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) { + + } + + @Override + public void setPrimaryColors(int... colors) { + + } + + @Override + public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) { + + } + + @Override + public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) { + + } + + @Override + public void onReleased(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) { + + } + + @Override + public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) { + + } + + @Override + public boolean isSupportHorizontalDrag() { + return false; + } + +} diff --git a/Client/android/MyEasyWOL/app/src/main/res/color/selector_round_button_main_theme_color.xml b/Client/android/MyEasyWOL/app/src/main/res/color/selector_round_button_main_theme_color.xml new file mode 100644 index 0000000..16fd2c7 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/color/selector_round_button_main_theme_color.xml @@ -0,0 +1,24 @@ + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_comment.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_comment.png new file mode 100644 index 0000000..dec6ff4 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_comment.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_praise.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_praise.png new file mode 100644 index 0000000..64021e2 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_praise.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_splash_app_logo_xui.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_splash_app_logo_xui.png new file mode 100644 index 0000000..1f5c884 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_splash_app_logo_xui.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_splash_company_logo.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_splash_company_logo.png new file mode 100644 index 0000000..7d8654d Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/ic_splash_company_logo.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/yun.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/yun.png new file mode 100644 index 0000000..cefe78e Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-hdpi/yun.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v17/xui_config_bg_splash.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable-v17/xui_config_bg_splash.xml new file mode 100644 index 0000000..b0cd1bc --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable-v17/xui_config_bg_splash.xml @@ -0,0 +1,28 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v21/xui_config_bg_splash.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable-v21/xui_config_bg_splash.xml new file mode 100644 index 0000000..f502f5d --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable-v21/xui_config_bg_splash.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..1f6bb29 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_add.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_add.png new file mode 100644 index 0000000..edd8639 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_add.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_close.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_close.png new file mode 100644 index 0000000..4866a91 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_close.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_delete.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_delete.png new file mode 100644 index 0000000..875b9b3 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/ic_swipe_menu_delete.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/icon_tip.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/icon_tip.png new file mode 100644 index 0000000..cb585f3 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/icon_tip.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/menu_selector_green.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/menu_selector_green.xml new file mode 100644 index 0000000..4c39ff8 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/menu_selector_green.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/menu_selector_red.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/menu_selector_red.xml new file mode 100644 index 0000000..12de340 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable-v24/menu_selector_red.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_back.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_back.png new file mode 100644 index 0000000..8bb3cf8 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_back.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_close.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_close.png new file mode 100644 index 0000000..58a7036 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_close.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_more.png b/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_more.png new file mode 100644 index 0000000..6ecc004 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable-xxxhdpi/ic_web_more.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/bg_dialog_common_tip_corner_white.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/bg_dialog_common_tip_corner_white.xml new file mode 100644 index 0000000..dbcadad --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/bg_dialog_common_tip_corner_white.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_action_close_white.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_action_close_white.xml new file mode 100644 index 0000000..266e01f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_action_close_white.xml @@ -0,0 +1,22 @@ + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_check_normal.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_check_normal.xml new file mode 100644 index 0000000..ac01f1f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_check_normal.xml @@ -0,0 +1,9 @@ + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_checked.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_checked.xml new file mode 100644 index 0000000..414e0a8 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_checked.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_default_head.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_default_head.xml new file mode 100644 index 0000000..f68423e --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_default_head.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_launcher_background.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_login_close.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_login_close.xml new file mode 100644 index 0000000..178ac09 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_login_close.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_about.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_about.xml new file mode 100644 index 0000000..1f7d30f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_about.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_issues.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_issues.xml new file mode 100644 index 0000000..1614cdb --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_issues.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_news.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_news.xml new file mode 100644 index 0000000..9a7ddff --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_news.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_notifications.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_notifications.xml new file mode 100644 index 0000000..9c92f09 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_notifications.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_person.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_person.xml new file mode 100644 index 0000000..3bd07cf --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_person.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_privacy.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_privacy.xml new file mode 100644 index 0000000..9184e2e --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_privacy.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_search.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_search.xml new file mode 100644 index 0000000..9dba080 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_search.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_settings.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_settings.xml new file mode 100644 index 0000000..ee77a3e --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_settings.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_star.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_star.xml new file mode 100644 index 0000000..e7b7c61 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_star.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_trending.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_trending.xml new file mode 100644 index 0000000..df83f28 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_menu_trending.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_password.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_password.xml new file mode 100644 index 0000000..716e402 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_password.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_phone.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 0000000..56cf551 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,4 @@ + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/icon_arrow_right_grey.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/icon_arrow_right_grey.xml new file mode 100644 index 0000000..964e9b2 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/icon_arrow_right_grey.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/icon_checkbox.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/icon_checkbox.xml new file mode 100644 index 0000000..bd2960a --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/icon_checkbox.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/img_guide_tip_top.xml b/Client/android/MyEasyWOL/app/src/main/res/drawable/img_guide_tip_top.xml new file mode 100644 index 0000000..6c787f8 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/drawable/img_guide_tip_top.xml @@ -0,0 +1,36 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/drawable/logo.png b/Client/android/MyEasyWOL/app/src/main/res/drawable/logo.png new file mode 100644 index 0000000..46838c3 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/drawable/logo.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/activity_agent_web.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/activity_agent_web.xml new file mode 100644 index 0000000..f02c107 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/activity_agent_web.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/activity_main.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..c7efeb8 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_common_grid_item.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_common_grid_item.xml new file mode 100644 index 0000000..80688eb --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_common_grid_item.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_item_simple_list_1.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_item_simple_list_1.xml new file mode 100644 index 0000000..36743d8 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_item_simple_list_1.xml @@ -0,0 +1,39 @@ + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_item_simple_list_2.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_item_simple_list_2.xml new file mode 100644 index 0000000..923c957 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_item_simple_list_2.xml @@ -0,0 +1,38 @@ + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_news_card_view_list_item.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_news_card_view_list_item.xml new file mode 100644 index 0000000..6fdf2a7 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_news_card_view_list_item.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_title_item.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_title_item.xml new file mode 100644 index 0000000..eaf8d4b --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/adapter_title_item.xml @@ -0,0 +1,45 @@ + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/dialog_guide_tips.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/dialog_guide_tips.xml new file mode 100644 index 0000000..d18910f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/dialog_guide_tips.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_about.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_about.xml new file mode 100644 index 0000000..1a71979 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_about.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_add_pcinfo.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_add_pcinfo.xml new file mode 100644 index 0000000..582e451 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_add_pcinfo.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_add_serverinfo.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_add_serverinfo.xml new file mode 100644 index 0000000..bb9af9e --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_add_serverinfo.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_agentweb.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_agentweb.xml new file mode 100644 index 0000000..cf1b5a9 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_agentweb.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_news.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_news.xml new file mode 100644 index 0000000..9907f64 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_news.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_profile.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_profile.xml new file mode 100644 index 0000000..881a0ca --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_profile.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_pulldown_web.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_pulldown_web.xml new file mode 100644 index 0000000..d810730 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_pulldown_web.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_server.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_server.xml new file mode 100644 index 0000000..a190ac4 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_server.xml @@ -0,0 +1,22 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_settings.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_settings.xml new file mode 100644 index 0000000..f873f56 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_settings.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_trending.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_trending.xml new file mode 100644 index 0000000..98d0404 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_trending.xml @@ -0,0 +1,29 @@ + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_wake_pc.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_wake_pc.xml new file mode 100644 index 0000000..e87c7d2 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/fragment_wake_pc.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/include_head_view_banner.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/include_head_view_banner.xml new file mode 100644 index 0000000..c0e1890 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/include_head_view_banner.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/include_navigation_header.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/include_navigation_header.xml new file mode 100644 index 0000000..50232eb --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/include_navigation_header.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/include_toolbar_more.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/include_toolbar_more.xml new file mode 100644 index 0000000..f08bacd --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/include_toolbar_more.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/include_toolbar_web.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/include_toolbar_web.xml new file mode 100644 index 0000000..1393456 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/include_toolbar_web.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/layout/layout_main_content.xml b/Client/android/MyEasyWOL/app/src/main/res/layout/layout_main_content.xml new file mode 100644 index 0000000..aff2df5 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/layout/layout_main_content.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/menu/menu_drawer.xml b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_drawer.xml new file mode 100644 index 0000000..c6c8a3f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_drawer.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/menu/menu_main.xml b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..7716806 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/menu/menu_navigation_bottom.xml b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_navigation_bottom.xml new file mode 100644 index 0000000..47cfd93 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_navigation_bottom.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/menu/menu_toolbar_edit.xml b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_toolbar_edit.xml new file mode 100644 index 0000000..8283c31 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_toolbar_edit.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/menu/menu_toolbar_web.xml b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_toolbar_web.xml new file mode 100644 index 0000000..207ca50 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/menu/menu_toolbar_web.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Client/android/MyEasyWOL/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..b0662a3 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Client/android/MyEasyWOL/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..3a55530 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Client/android/MyEasyWOL/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..c82be1a Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Client/android/MyEasyWOL/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..fd7cb44 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Client/android/MyEasyWOL/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..1406319 Binary files /dev/null and b/Client/android/MyEasyWOL/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/Client/android/MyEasyWOL/app/src/main/res/values/arrays.xml b/Client/android/MyEasyWOL/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..8247934 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/values/arrays.xml @@ -0,0 +1,41 @@ + + + + + + + @string/menu_news + @string/menu_trending + @string/menu_profile + + + + + @color/app_color_theme_1 + @color/app_color_theme_2 + @color/app_color_theme_3 + @color/app_color_theme_4 + @color/app_color_theme_5 + @color/app_color_theme_6 + @color/app_color_theme_7 + @color/app_color_theme_8 + @color/app_color_theme_7 + @color/app_color_theme_8 + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/values/colors.xml b/Client/android/MyEasyWOL/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..d61f198 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/values/colors.xml @@ -0,0 +1,36 @@ + + + #299EE3 + #299EE3 + #299EE3 + + #FFF1F1F1 + + + + @color/xui_config_color_white + @color/xui_config_color_red + @color/colorAccent + #388E3C + @color/xui_config_color_waring + #353A3E + + #EF5362 + #FE6D4B + #FFCF47 + #9FD661 + #3FD0AD + #2BBDF3 + #5A9AEF + #AC8FEF + #EE85C1 + #FFFFFF + #CFCFCF + @color/app_color_theme_2 + #FF6666 + #09B378 + #56BF92 + #9932CC + #9F79EE + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/values/dimens.xml b/Client/android/MyEasyWOL/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..af2744a --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/values/dimens.xml @@ -0,0 +1,16 @@ + + + 24dp + + 16dp + 14dp + 10dp + 20dp + 8dp + 5dp + 18dp + 30dp + 12dp + 24dp + 70dp + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/values/strings.xml b/Client/android/MyEasyWOL/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..3126a03 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/values/strings.xml @@ -0,0 +1,84 @@ + + 远程开机 + 通用浏览器 + + Open navigation drawer + Close navigation drawer + + 远程开机 + 中转服务器 + 我的 + + 通知 + 问题 + 收藏 + 搜索 + 设置 + 关于 + + © %1$s itrycn All rights reserved. + 访问官网 + 关于作者 + 捐赠作者 + - + - + + + + 是否允许页面打开第三方应用? + + + 退出应用 + 同意 + 不同意 + 再次查看 + 仍不同意 + 温馨提示 + 要不要再想想 + 我们非常重视对你个人信息的保护,承诺严格按照《%s隐私权政策》保护及处理你的信息。如果你不同意该政策,很遗憾我们将无法为你提供服务 + 《%s隐私权政策》 + + 唤醒 + 确定 + 请输入设备名称 + 请输入设备的MAC地址 + 请输入设备的IP地址或网段 + 端口 + + 路由器唤醒(局域网唤醒) + 请输入服务器名称 + 请输入服务器网址 + (可选)当使用第三方服务器时需要填写 + + 登录/注册 + 获取验证码 + 登录 + 验证码登录 + 注册 + 忘记密码? + 验证码登录 + 密码登录 + 请输入手机号码 + 手机号码 + 密码 + 旧密码 + 请输入验证码 + 验证码 + 密码必须是8~18位字母和数字的组合! + 新密码必须是8~18位字母和数字的组合! + 无效的手机号! + ^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(16[6])|(17[0,1,3,5-8])|(18[0-9])|(19[8,9]))\\d{8}$ + 请输入4位数验证码 + ^\\d{4}$ + ^(?:(?=.*[a-zA-Z])(?=.*[0-9])).{8,18}$ + 重置密码 + 点击注册即表示同意 + ]]> + 是否确认退出账号? + 跳过 + 上一条 + 下一条 + 以后不再提示此类信息 + 你知道吗? + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/values/styles.xml b/Client/android/MyEasyWOL/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..aaedae8 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/values/styles.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + diff --git a/Client/android/MyEasyWOL/app/src/main/res/values/styles_widget.xml b/Client/android/MyEasyWOL/app/src/main/res/values/styles_widget.xml new file mode 100644 index 0000000..9f13c6f --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/values/styles_widget.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/main/res/xml/network_security_config.xml b/Client/android/MyEasyWOL/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..da03785 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/src/test/java/com/itrycn/myeasywol/ExampleUnitTest.java b/Client/android/MyEasyWOL/app/src/test/java/com/itrycn/myeasywol/ExampleUnitTest.java new file mode 100644 index 0000000..cbe5107 --- /dev/null +++ b/Client/android/MyEasyWOL/app/src/test/java/com/itrycn/myeasywol/ExampleUnitTest.java @@ -0,0 +1,36 @@ +package com.itrycn.myeasywol; + +import com.itrycn.myeasywol.core.http.entity.TipInfo; +import com.xuexiang.xhttp2.model.ApiResult; +import com.xuexiang.xutil.net.JsonUtil; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + + + TipInfo info = new TipInfo(); + info.setTitle("测试"); + info.setContent("谢谢大家的支持"); + List list = new ArrayList<>(); + for (int i = 0; i <5 ; i++) { + list.add(info); + } + ApiResult> result = new ApiResult<>(); + result.setData(list); + System.out.println(JsonUtil.toJson(result)); + } +} \ No newline at end of file diff --git a/Client/android/MyEasyWOL/app/x-library.gradle b/Client/android/MyEasyWOL/app/x-library.gradle new file mode 100644 index 0000000..94f6998 --- /dev/null +++ b/Client/android/MyEasyWOL/app/x-library.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.xuexiang.xaop' //引用XAOP插件 +apply plugin: 'com.xuexiang.xrouter' //引用XRouter-plugin插件实现自动注册 + +//自动添加依赖 +project.configurations.each { configuration -> + if (configuration.name == "implementation") { + //为Project加入X-Library依赖 + //XUI框架 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys:XUI:1.1.6')) + configuration.dependencies.add(getProject().dependencies.create(deps.androidx.appcompat)) + configuration.dependencies.add(getProject().dependencies.create(deps.androidx.recyclerview)) + configuration.dependencies.add(getProject().dependencies.create(deps.androidx.design)) + configuration.dependencies.add(getProject().dependencies.create(deps.glide)) + //XUtil工具类 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XUtil:xutil-core:2.0.0')) + //XAOP切片,版本号前带x的是支持androidx的版本 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XAOP:xaop-runtime:1.1.0')) + //XUpdate版本更新 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys:XUpdate:2.0.6')) + //XHttp2 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys:XHttp2:2.0.1')) + configuration.dependencies.add(getProject().dependencies.create(deps.rxjava2)) + configuration.dependencies.add(getProject().dependencies.create(deps.rxandroid)) + configuration.dependencies.add(getProject().dependencies.create('com.squareup.okhttp3:okhttp:3.10.0')) + configuration.dependencies.add(getProject().dependencies.create(deps.gson)) + //XPage + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XPage:xpage-lib:3.0.3')) + configuration.dependencies.add(getProject().dependencies.create(deps.butterknife.runtime)) + //页面路由 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XRouter:xrouter-runtime:1.0.1')) + } + + if (configuration.name == "annotationProcessor") { + //XPage + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XPage:xpage-compiler:3.0.3')) + configuration.dependencies.add(getProject().dependencies.create(deps.butterknife.compiler)) + //页面路由 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XRouter:xrouter-compiler:1.0.1')) + } + //设置泄露监测leak为始终release模式。 + configuration.dependencies.add(getProject().dependencies.create('com.squareup.leakcanary:leakcanary-android-no-op:1.6.3')) + if (configuration.name == "debugImplementation") { + //内存泄漏监测leak + //configuration.dependencies.add(getProject().dependencies.create('com.squareup.leakcanary:leakcanary-android:1.6.3')) + } + if (configuration.name == "releaseImplementation") { + //内存泄漏监测leak + //configuration.dependencies.add(getProject().dependencies.create('com.squareup.leakcanary:leakcanary-android-no-op:1.6.3')) + } + if (configuration.name == "testImplementation") { + //内存泄漏监测leak + configuration.dependencies.add(getProject().dependencies.create('com.squareup.leakcanary:leakcanary-android-no-op:1.6.3')) + } +} + diff --git a/Client/android/MyEasyWOL/build.gradle b/Client/android/MyEasyWOL/build.gradle new file mode 100644 index 0000000..0512870 --- /dev/null +++ b/Client/android/MyEasyWOL/build.gradle @@ -0,0 +1,29 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + apply from: './versions.gradle' + addRepos(repositories) //增加代码仓库 + dependencies { + classpath deps.android_gradle_plugin + classpath deps.android_maven_gradle_plugin + classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0' + classpath 'com.chenenyu:img-optimizer:1.2.0' // 图片压缩 + //美团多渠道打包 + classpath 'com.meituan.android.walle:plugin:1.1.6' + //滴滴的质量优化框架 + if (isNeedPackage.toBoolean() && isUseBooster.toBoolean()) { + classpath deps.booster.gradle_plugin + classpath deps.booster.task_processed_res + classpath deps.booster.task_resource_deredundancy + } + } +} + +allprojects { + addRepos(repositories) +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + diff --git a/Client/android/MyEasyWOL/gradle.properties b/Client/android/MyEasyWOL/gradle.properties new file mode 100644 index 0000000..951bf4b --- /dev/null +++ b/Client/android/MyEasyWOL/gradle.properties @@ -0,0 +1,25 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# 是否打包APK,打正式包时请设置为true,使用正式的签名 +isNeedPackage=false +# 是否使用booster优化APK,这里需要注意gradle的版本,对于最新的gradle版本可能存在兼容问题 +isUseBooster=false +android.precompileDependenciesResources=false + +android.useAndroidX=true +android.enableJetifier=true + +android.enableD8=true +android.injected.testOnly=false \ No newline at end of file diff --git a/Client/android/MyEasyWOL/gradle/wrapper/gradle-wrapper.jar b/Client/android/MyEasyWOL/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/Client/android/MyEasyWOL/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Client/android/MyEasyWOL/gradle/wrapper/gradle-wrapper.properties b/Client/android/MyEasyWOL/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..835cb05 --- /dev/null +++ b/Client/android/MyEasyWOL/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 28 16:23:16 CST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-bin.zip diff --git a/Client/android/MyEasyWOL/gradlew b/Client/android/MyEasyWOL/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/Client/android/MyEasyWOL/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/Client/android/MyEasyWOL/gradlew.bat b/Client/android/MyEasyWOL/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/Client/android/MyEasyWOL/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Client/android/MyEasyWOL/settings.gradle b/Client/android/MyEasyWOL/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/Client/android/MyEasyWOL/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/Client/android/MyEasyWOL/versions.gradle b/Client/android/MyEasyWOL/versions.gradle new file mode 100644 index 0000000..33dc5e6 --- /dev/null +++ b/Client/android/MyEasyWOL/versions.gradle @@ -0,0 +1,173 @@ +import java.util.regex.Matcher +import java.util.regex.Pattern + +/** + * Shared file between builds so that they can all use the same dependencies and + * maven repositories. + **/ +ext.deps = [:] +def versions = [:] +versions.android_gradle_plugin = '4.2.1' +versions.android_maven_gradle_plugin = "2.0" +versions.gradle_bintray_plugin = "1.8.0" +versions.booster = "3.1.0" +versions.booster_all = "1.1.1" +versions.support = "28.0.0" +versions.androidx = "1.1.0" +versions.junit = "4.12" +versions.espresso = "3.2.0" +versions.constraint_layout = "1.1.3" +versions.glide = "4.11.0" +versions.rxjava2 = "2.2.20" +versions.rxandroid = "2.1.1" +versions.rxbinding = "2.2.0" +versions.butterknife = "10.1.0" +versions.runner = "1.2.0" +versions.gson = "2.8.5" + +def deps = [:] + +def support = [:] +support.annotations = "com.android.support:support-annotations:$versions.support" +support.app_compat = "com.android.support:appcompat-v7:$versions.support" +support.recyclerview = "com.android.support:recyclerview-v7:$versions.support" +support.cardview = "com.android.support:cardview-v7:$versions.support" +support.design = "com.android.support:design:$versions.support" +support.v4 = "com.android.support:support-v4:$versions.support" +support.core_utils = "com.android.support:support-core-utils:$versions.support" +deps.support = support + +def androidx = [:] +androidx.annotations = "androidx.annotation:annotation:$versions.androidx" +androidx.appcompat = "androidx.appcompat:appcompat:$versions.androidx" +androidx.recyclerview = "androidx.recyclerview:recyclerview:$versions.androidx" +androidx.design = "com.google.android.material:material:$versions.androidx" +androidx.multidex = 'androidx.multidex:multidex:2.0.1' +deps.androidx = androidx + +def booster = [:] +booster.gradle_plugin = "com.didiglobal.booster:booster-gradle-plugin:$versions.booster" +booster.task_all = "com.didiglobal.booster:booster-task-all:$versions.booster_all" +booster.transform_all = "com.didiglobal.booster:booster-transform-all:$versions.booster_all" +//采用 cwebp 对资源进行压缩 +booster.task_compression_cwebp = "com.didiglobal.booster:booster-task-compression-cwebp:$versions.booster" +//采用 pngquant 对资源进行压缩 +booster.task_compression_pngquant = "com.didiglobal.booster:booster-task-compression-pngquant:$versions.booster" +//ap_ 文件压缩 +booster.task_processed_res = "com.didiglobal.booster:booster-task-compression-processed-res:$versions.booster" +//去冗余资源 +booster.task_resource_deredundancy = "com.didiglobal.booster:booster-task-resource-deredundancy:$versions.booster" +//检查 SNAPSHOT 版本 +booster.task_check_snapshot = "com.didiglobal.booster:booster-task-check-snapshot:$versions.booster" +//性能瓶颈检测 +booster.transform_lint = "com.didiglobal.booster:booster-transform-lint:$versions.booster" +//多线程优化 +booster.transform_thread = "com.didiglobal.booster:booster-transform-thread:$versions.booster" +//资源索引内联 +booster.transform_r_inline = "com.didiglobal.booster:booster-transform-r-inline:$versions.booster" +//WebView 预加载 +booster.transform_webview = "com.didiglobal.booster:booster-transform-webview:$versions.booster" +//SharedPreferences 优化 +booster.transform_shared_preferences = "com.didiglobal.booster:booster-transform-shared-preferences:$versions.booster" +//检查覆盖安装导致的 Resources 和 Assets 未加载的 Bug +booster.transform_res_check = "com.didiglobal.booster:booster-transform-res-check:$versions.booster" +//修复 Toast 在 Android 7.1 上的 Bug +booster.transform_toast = "com.didiglobal.booster:booster-transform-toast:$versions.booster" +//处理系统 Crash +booster.transform_activity_thread = "com.didiglobal.booster:booster-transform-activity-thread:$versions.booster" +deps.booster = booster + +def butterknife = [:] +butterknife.runtime = "com.jakewharton:butterknife:$versions.butterknife" +butterknife.compiler = "com.jakewharton:butterknife-compiler:$versions.butterknife" + +deps.butterknife = butterknife + +def espresso = [:] +espresso.core = "androidx.test.espresso:espresso-core:$versions.espresso" +espresso.contrib = "androidx.test.espresso:espresso-contrib:$versions.espresso" +espresso.intents = "androidx.test.espresso:espresso-intents:$versions.espresso" +deps.espresso = espresso + +deps.android_gradle_plugin = "com.android.tools.build:gradle:$versions.android_gradle_plugin" +deps.android_maven_gradle_plugin = "com.github.dcendents:android-maven-gradle-plugin:$versions.android_maven_gradle_plugin" +deps.gradle_bintray_plugin = "com.jfrog.bintray.gradle:gradle-bintray-plugin:$versions.gradle_bintray_plugin" +deps.glide = "com.github.bumptech.glide:glide:$versions.glide" +deps.constraint_layout = "androidx.constraint:constraint-layout:$versions.constraint_layout" +deps.junit = "junit:junit:$versions.junit" +deps.runner = "androidx.test:runner:$versions.runner" +deps.rxjava2 = "io.reactivex.rxjava2:rxjava:$versions.rxjava2" +deps.rxandroid = "io.reactivex.rxjava2:rxandroid:$versions.rxandroid" +deps.rxbinding = "com.jakewharton.rxbinding2:rxbinding:$versions.rxbinding" +deps.gson = "com.google.code.gson:gson:$versions.gson" + +ext.deps = deps + +def build_versions = [:] +build_versions.min_sdk = 19 +build_versions.target_sdk = 28 +build_versions.build_tools = "28.0.3" +ext.build_versions = build_versions + +def app_release = [:] +app_release.storeFile = "../keystores/android.keystore" +app_release.storePassword = "xuexiang" +app_release.keyAlias = "android.keystore" +app_release.keyPassword = "xuexiang" + +ext.app_release = app_release + +/** + * @return 是否为release + */ +def isRelease() { + Gradle gradle = getGradle() + String tskReqStr = gradle.getStartParameter().getTaskRequests().toString() + + Pattern pattern + if (tskReqStr.contains("assemble")) { + println tskReqStr + pattern = Pattern.compile("assemble(\\w*)(Release|Debug)") + } else { + pattern = Pattern.compile("generate(\\w*)(Release|Debug)") + } + Matcher matcher = pattern.matcher(tskReqStr) + + if (matcher.find()) { + String task = matcher.group(0).toLowerCase() + println("[BuildType] Current task: " + task) + return task.contains("release") + } else { + println "[BuildType] NO MATCH FOUND" + return true + } +} + +ext.isRelease = this.&isRelease + +//默认添加代码仓库路径 +static def addRepos(RepositoryHandler handler) { + handler.mavenLocal() + handler.google { url 'https://maven.aliyun.com/repository/google' } + handler.jcenter { url 'https://maven.aliyun.com/repository/jcenter' } + handler.mavenCentral { url 'https://maven.aliyun.com/repository/central' } + handler.maven { url "https://jitpack.io" } + handler.maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } + handler.maven { url "https://dl.bintray.com/umsdk/release" } + handler.maven { url 'https://oss.sonatype.org/content/repositories/public' } + //Add the Local repository + handler.maven { url 'LocalRepository' } +} + +ext.addRepos = this.&addRepos + + +//自动添加XAOP和XRouter插件 +project.buildscript.configurations.each { configuration -> + if (configuration.name == "classpath") { + //XAOP插件 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XAOP:xaop-plugin:1.1.0')) + //XRouter插件 + configuration.dependencies.add(getProject().dependencies.create('com.github.xuexiangjys.XRouter:xrouter-plugin:1.0.1')) + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..06089f6 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +### MyEasyWOL + +------ + +本工具是一套远程唤醒辅助工具集。分客户端和服务端两部分。客户端可以安装在自己的手机上,服务端可以安装在nas、机顶盒、windows电脑,然后搭配公网IP,实现外网远程唤醒电脑。本工具永久无广告、永久免费使用。 + +------ + +##### 开发本工具的起因 + +由于本人的华为路由器不支持ARP功能,所以无法用WOL软件在外网远程唤醒家里的电脑。另外华为路由器app自带的唤醒功能非常不好用,当你有一大堆设备时,每次唤醒还要在一大堆设备中找到要唤醒的设备,体验非常不好。 + +后来用了小米智能插座和向日葵的远程开机插座来实现远程唤醒。但是我的电脑,需要断电10秒之后再来电才能远程开机。小米插座倒是可以用场景来实现,但是如果电脑开机着,不小心执行了场景,就会让电脑直接断电关机。向日葵的插座却是因为它自己程序设计的原因(断电间隔太短,用户又无法自主设置)导致无法远程开机。每次都要在app里断电10秒后,再在app里点击开机才能打开电脑。体验同样非常不好。 + +之后所以就有了开发本工具的念头。利用机顶盒或nas,再搭配公网ip,就可以实现远程开机。 + +------ + +##### 开源说明 + +由于本人不太会android和php开发,所以代码里采用了很多网上的代码,抱着人人为我,我为人人的想法,所以我将本项目开源了。本项目采用比较宽松的开源协议,个人使用完全免费。商用的话,需要告知本人。目前第一阶段我开源了手机端app,后续将开源机顶盒端app、Windows端程序。 + +------ + +##### 关于app + +安卓端我是基于XUI示例程序开发的,所以源码中包含了很多示例代码,我没有做删除,有一点点冗余。app没有申请任何隐私权限,也保证不作恶,app每次启动时会去服务端检测新版本。app没有任何广告,完全免费开源。安卓端我是半路出家,代码写的乱,行家不要见笑。 + +------ + +##### 服务端(可选) + +安装服务端后,搭配公网ip,就可以将客户端发来的开机信息,转发到服务端所在的局域网中,从而实现开机。如果不安装服务端,客户端也支持局域网唤醒,以及支持arp的路由器的外网唤醒。 + +目前成品中只包含了php程序,如需Windows版、机顶盒作为服务端,请发邮件联系我。 + +------ + +##### 客户端 + +客户端是用来方便管理远程唤醒的设备的。目前支持android版本,之后会支持windows版本。由于本人不懂ios开发,所以iphone版本不打算做支持。 + +------ + +##### Nas版安装步骤 + +1. 打开Server文件夹,复制里面的php文件夹到nas里(我是复制到Web共享文件夹下)。威联通需要在设置里开启Web服务器(默认是80端口,要外网访问,需要映射成其它端口),群晖我手头没有,大家可以百度下。 + +2. 打开浏览器,输入nas绑定的域名:Web服务器端口/php/index.php,看看能不能正常访问。如果能正常访问,则执行下一步。 + +3. 进入客户端安装步骤。 + + +##### 安卓机顶盒版安装步骤 + +1. 打开Server文件夹,将【android机顶盒】文件夹里的apk文件安装到机顶盒,然后打开。 +2. 在路由器中映射机顶盒的5000端口到外网任意端口A。使用绑定的域名:A作为服务端网址。 +3. 进入客户端安装步骤。 + +##### 客户端安装步骤 + +1. 打开Client文件夹下的android文件夹,把里面的apk文件安装到手机上。 + +2. 打开手机app,选择中转服务器,点击右上角的+,然后名称随便输入,网址输入我们刚才服务端第二步的网址,成功标记留空即可,然后点击确定保存。 + +3. 然后到远程开机tab页,点击右上角的+,名称随便输入,MAC和IP就输入要唤醒的设备的,端口可留空不输入。服务器选择我们刚才保存的。然后点击确定保存。这样就完成了。 + +------ +##### 界面预览 + + ![](界面预览\1.png) + ![](界面预览\2.png) \ No newline at end of file diff --git a/Server/php/WOL.php b/Server/php/WOL.php new file mode 100644 index 0000000..b4a5b9d --- /dev/null +++ b/Server/php/WOL.php @@ -0,0 +1,90 @@ + "目标机器已经是开机状态的.", + 1 => "socket_create 方法执行失败", + 2 => "socket_set_option 方法执行失败", + 3 => "magic packet 发送成功!", + 4 => "magic packet 发送成功!" + ); + + function __construct($hostname,$mac,$port,$ip = false) + { + $this->hostname = $hostname; + $this->mac = $mac; + $this->port = $port; + if (!$ip) + { + $this->ip = $this->get_ip_from_hostname(); + } + } + + public function wake_on_wan() + { + if ($this->is_awake()) + { + return $this->msg[0]; // 如果设备已经是唤醒的就不做其它操作了 + } + else + { + $addr_byte = explode(':', $this->mac); + $hw_addr = ''; + for ($a=0; $a<6; $a++) $hw_addr .= chr(hexdec($addr_byte[$a])); + $msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255); + for ($a=1; $a<=16; $a++) $msg .= $hw_addr; + // 通过 UDP 发送数据包 + $s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + + if ($s == false) + { + return $this->msg[1]; // socket_create 执行失败 + } + + $set_opt = @socket_set_option($s, 1, 6, TRUE); + + if ($set_opt < 0) + { + return $this->msg[2]; // socket_set_option 执行失败 + } + + $sendto = @socket_sendto($s, $msg, strlen($msg), 0, $this->ip, $this->port); + + if ($sendto) + { + socket_close($s); + return $this->msg[3]; // magic packet 发送成功! + } + + return $this->msg[4]; // magic packet 发送失败! + + } + } + + private function is_awake() + { + $awake = @fsockopen($this->ip, 80, $errno, $errstr, 2); + + if ($awake) + { + fclose($awake); + } + + return $awake; + } + + private function get_ip_from_hostname() + { + return gethostbyname($this->hostname); + } + +} +?> \ No newline at end of file diff --git a/Server/php/index.php b/Server/php/index.php new file mode 100644 index 0000000..4d49a1f --- /dev/null +++ b/Server/php/index.php @@ -0,0 +1,25 @@ +wake_on_wan(); + if((strpos($status,'开机')>=0) || (strpos($status,'发送成功')>=0)) + { + echo "ok"; + } + else + { + echo $status; + } +} +else +{ + echo "0"; +} +?> \ No newline at end of file diff --git a/成品程序/Client/android/MyEasyWOL.apk b/成品程序/Client/android/MyEasyWOL.apk new file mode 100644 index 0000000..11cb634 Binary files /dev/null and b/成品程序/Client/android/MyEasyWOL.apk differ diff --git a/成品程序/README.md b/成品程序/README.md new file mode 100644 index 0000000..030e579 --- /dev/null +++ b/成品程序/README.md @@ -0,0 +1,66 @@ +### MyEasyWOL + +------ + +本工具是一套远程唤醒辅助工具集。分客户端和服务端两部分。客户端可以安装在自己的手机上,服务端可以安装在nas、机顶盒、windows电脑,然后搭配公网IP,实现外网远程唤醒电脑。本工具永久无广告、永久免费使用。 + +------ + +##### 开发本工具的起因 + +由于本人的华为路由器不支持ARP功能,所以无法用WOL软件在外网远程唤醒家里的电脑。另外华为路由器app自带的唤醒功能非常不好用,当你有一大堆设备时,每次唤醒还要在一大堆设备中找到要唤醒的设备,体验非常不好。 + +后来用了小米智能插座和向日葵的远程开机插座来实现远程唤醒。但是我的电脑,需要断电10秒之后再来电才能远程开机。小米插座倒是可以用场景来实现,但是如果电脑开机着,不小心执行了场景,就会让电脑直接断电关机。向日葵的插座却是因为它自己程序设计的原因(断电间隔太短,用户又无法自主设置)导致无法远程开机。每次都要在app里断电10秒后,再在app里点击开机才能打开电脑。体验同样非常不好。 + +之后所以就有了开发本工具的念头。利用机顶盒或nas,再搭配公网ip,就可以实现远程开机。 + +------ + +##### 开源说明 + +由于本人不太会android和php开发,所以代码里采用了很多网上的代码,抱着人人为我,我为人人的想法,所以我将本项目开源了。本项目采用比较宽松的开源协议,个人使用完全免费。商用的话,需要告知本人。如需源码,请发邮件到zilinsoft@qq.com索要。 + +------ + +##### 关于app + +安卓端我是基于XUI示例程序开发的,所以源码中包含了很多示例代码,我没有做删除,有一点点冗余。app没有申请任何隐私权限,也保证不作恶,app每次启动时会去服务端检测新版本。app没有任何广告,完全免费开源。安卓端我是半路出家,代码写的乱,行家不要见笑。 + +------ + +##### 服务端(可选) + +安装服务端后,搭配公网ip,就可以将客户端发来的开机信息,转发到服务端所在的局域网中,从而实现开机。如果不安装服务端,客户端也支持局域网唤醒,以及支持arp的路由器的外网唤醒。 + +目前成品中只包含了php程序,如需Windows版、机顶盒作为服务端,请发邮件联系我。 + +------ + +##### 客户端 + +客户端是用来方便管理远程唤醒的设备的。目前支持android版本,之后会支持windows版本。由于本人不懂ios开发,所以iphone版本不打算做支持。 + +------ + +##### Nas版安装步骤 + +1. 打开Server文件夹,复制里面的php文件夹到nas里(我是复制到Web共享文件夹下)。威联通需要在设置里开启Web服务器(默认是80端口,要外网访问,需要映射成其它端口),群晖我手头没有,大家可以百度下。 + +2. 打开浏览器,输入nas绑定的域名:Web服务器端口/php/index.php,看看能不能正常访问。如果能正常访问,则执行下一步。 + +3. 进入客户端安装步骤。 + + +##### 安卓机顶盒版安装步骤 + +1. 打开Server文件夹,将【android机顶盒】文件夹里的apk文件安装到机顶盒,然后打开。 +2. 在路由器中映射机顶盒的5000端口到外网任意端口A。使用绑定的域名:A作为服务端网址。 +3. 进入客户端安装步骤。 + +##### 客户端安装步骤 + +1. 打开Client文件夹下的android文件夹,把里面的apk文件安装到手机上。 + +2. 打开手机app,选择中转服务器,点击右上角的+,然后名称随便输入,网址输入我们刚才服务端第二步的网址,成功标记留空即可,然后点击确定保存。 + +3. 然后到远程开机tab页,点击右上角的+,名称随便输入,MAC和IP就输入要唤醒的设备的,端口可留空不输入。服务器选择我们刚才保存的。然后点击确定保存。这样就完成了。 \ No newline at end of file diff --git a/成品程序/Server/android机顶盒/BootService.apk b/成品程序/Server/android机顶盒/BootService.apk new file mode 100644 index 0000000..1072e50 Binary files /dev/null and b/成品程序/Server/android机顶盒/BootService.apk differ diff --git a/成品程序/Server/php/WOL.php b/成品程序/Server/php/WOL.php new file mode 100644 index 0000000..b4a5b9d --- /dev/null +++ b/成品程序/Server/php/WOL.php @@ -0,0 +1,90 @@ + "目标机器已经是开机状态的.", + 1 => "socket_create 方法执行失败", + 2 => "socket_set_option 方法执行失败", + 3 => "magic packet 发送成功!", + 4 => "magic packet 发送成功!" + ); + + function __construct($hostname,$mac,$port,$ip = false) + { + $this->hostname = $hostname; + $this->mac = $mac; + $this->port = $port; + if (!$ip) + { + $this->ip = $this->get_ip_from_hostname(); + } + } + + public function wake_on_wan() + { + if ($this->is_awake()) + { + return $this->msg[0]; // 如果设备已经是唤醒的就不做其它操作了 + } + else + { + $addr_byte = explode(':', $this->mac); + $hw_addr = ''; + for ($a=0; $a<6; $a++) $hw_addr .= chr(hexdec($addr_byte[$a])); + $msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255); + for ($a=1; $a<=16; $a++) $msg .= $hw_addr; + // 通过 UDP 发送数据包 + $s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + + if ($s == false) + { + return $this->msg[1]; // socket_create 执行失败 + } + + $set_opt = @socket_set_option($s, 1, 6, TRUE); + + if ($set_opt < 0) + { + return $this->msg[2]; // socket_set_option 执行失败 + } + + $sendto = @socket_sendto($s, $msg, strlen($msg), 0, $this->ip, $this->port); + + if ($sendto) + { + socket_close($s); + return $this->msg[3]; // magic packet 发送成功! + } + + return $this->msg[4]; // magic packet 发送失败! + + } + } + + private function is_awake() + { + $awake = @fsockopen($this->ip, 80, $errno, $errstr, 2); + + if ($awake) + { + fclose($awake); + } + + return $awake; + } + + private function get_ip_from_hostname() + { + return gethostbyname($this->hostname); + } + +} +?> \ No newline at end of file diff --git a/成品程序/Server/php/index.php b/成品程序/Server/php/index.php new file mode 100644 index 0000000..4d49a1f --- /dev/null +++ b/成品程序/Server/php/index.php @@ -0,0 +1,25 @@ +wake_on_wan(); + if((strpos($status,'开机')>=0) || (strpos($status,'发送成功')>=0)) + { + echo "ok"; + } + else + { + echo $status; + } +} +else +{ + echo "0"; +} +?> \ No newline at end of file diff --git a/界面预览/1.png b/界面预览/1.png new file mode 100644 index 0000000..046d88c Binary files /dev/null and b/界面预览/1.png differ diff --git a/界面预览/2.png b/界面预览/2.png new file mode 100644 index 0000000..81d5dff Binary files /dev/null and b/界面预览/2.png differ