添加安卓端基本功能
1
android/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
131
android/app/build.gradle
Normal file
@@ -0,0 +1,131 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'img-optimizer'
|
||||
//打包时,记得设置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.kerwin.wumei"
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(path: ':esptouch')
|
||||
testImplementation deps.junit
|
||||
androidTestImplementation deps.runner
|
||||
androidTestImplementation deps.espresso.core
|
||||
|
||||
//分包
|
||||
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'
|
||||
//umeng统计
|
||||
implementation 'com.umeng.umsdk:analytics:8.0.2'
|
||||
implementation 'com.umeng.umsdk:common:2.0.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'
|
||||
|
||||
|
||||
25
android/app/channel
Normal file
@@ -0,0 +1,25 @@
|
||||
# 美团
|
||||
meituan
|
||||
# 三星
|
||||
samsungapps
|
||||
# 小米
|
||||
xiaomi
|
||||
# 91助手
|
||||
91com
|
||||
# 魅族
|
||||
meizu
|
||||
# 豌豆荚
|
||||
wandou
|
||||
# Google Play
|
||||
googleplay
|
||||
# 百度
|
||||
baidu
|
||||
# 360
|
||||
360cn
|
||||
# 应用宝
|
||||
myapp
|
||||
# 华为
|
||||
huawei
|
||||
# 蒲公英
|
||||
pgyer
|
||||
github
|
||||
BIN
android/app/debug.jks
Normal file
10
android/app/multiple-channel.gradle
Normal file
@@ -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")
|
||||
}
|
||||
269
android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
#=========================================基础不变的混淆配置=========================================##
|
||||
#指定代码的压缩级别
|
||||
-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 <init>(android.content.Context);
|
||||
public <init>(android.content.Context, android.util.AttributeSet);
|
||||
public <init>(android.content.Context, android.util.AttributeSet, int);
|
||||
public void set*(...);
|
||||
}
|
||||
|
||||
#保持 native 方法不被混淆
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
#保持自定义控件类不被混淆
|
||||
-keepclasseswithmembers class * {
|
||||
public <init>(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 <fields>;
|
||||
!private <fields>;
|
||||
!private <methods>;
|
||||
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.** { *; }
|
||||
-keep class com.xuexiang.xpage.config.** { *; }
|
||||
|
||||
# xaop
|
||||
-keep @com.xuexiang.xaop.annotation.* class * {*;}
|
||||
-keep @org.aspectj.lang.annotation.* class * {*;}
|
||||
-keep class * {
|
||||
@com.xuexiang.xaop.annotation.* <fields>;
|
||||
@org.aspectj.lang.annotation.* <fields>;
|
||||
}
|
||||
-keepclassmembers class * {
|
||||
@com.xuexiang.xaop.annotation.* <methods>;
|
||||
@org.aspectj.lang.annotation.* <methods>;
|
||||
}
|
||||
|
||||
# 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.* <fields>;
|
||||
}
|
||||
-keepclassmembers class * {
|
||||
@com.xuexiang.xipc.annotation.* <methods>;
|
||||
}
|
||||
|
||||
# umeng统计
|
||||
-keep class com.umeng.** {*;}
|
||||
-keepclassmembers class * {
|
||||
public <init> (org.json.JSONObject);
|
||||
}
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keep class com.xuexiang.xui.widget.edittext.materialedittext.** { *; }
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.kerwin.wumei;
|
||||
|
||||
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.kerwin.wumei", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
147
android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.kerwin.wumei">
|
||||
|
||||
<!--进程杀死-->
|
||||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
|
||||
<application
|
||||
android:name="com.kerwin.wumei.MyApp"
|
||||
android:allowBackup="false"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden"
|
||||
tools:ignore="LockedOrientationActivity"
|
||||
tools:targetApi="n">
|
||||
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.activity.SplashActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.Launch.App"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.activity.MainActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.activity.LoginActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:launchMode="singleInstance"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.activity.AddDeviceActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:screenOrientation="portrait"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/AppTheme"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
|
||||
<!--通用浏览器-->
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.core.webview.AgentWebActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="@string/app_browser_name"
|
||||
android:theme="@style/AppTheme">
|
||||
<!-- Scheme -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="com.xuexiang.xui.applink" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="about" />
|
||||
<data android:scheme="javascript" />
|
||||
<!-- 设置自己的deeplink -->
|
||||
<!-- <data-->
|
||||
<!-- android:host="xxx.com"-->
|
||||
<!-- android:scheme="xui"/>-->
|
||||
</intent-filter>
|
||||
<!-- AppLink -->
|
||||
<intent-filter
|
||||
android:autoVerify="true"
|
||||
tools:targetApi="m">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="inline" />
|
||||
<data android:mimeType="text/html" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="application/xhtml+xml" />
|
||||
<data android:mimeType="application/vnd.wap.xhtml+xml" />
|
||||
<!-- 设置自己的applink -->
|
||||
<!-- <data-->
|
||||
<!-- android:host="xxx.com"-->
|
||||
<!-- android:scheme="http"/>-->
|
||||
<!-- <data-->
|
||||
<!-- android:host="xxx.com"-->
|
||||
<!-- android:scheme="https"/>-->
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!--fragment的页面容器-->
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.core.BaseActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
<!-- 版本更新提示-->
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.utils.update.UpdateTipDialog"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/DialogTheme" />
|
||||
<!-- Webview拦截提示弹窗-->
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.core.webview.WebViewInterceptDialog"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/DialogTheme" />
|
||||
<!-- applink的中转页面 -->
|
||||
<activity
|
||||
android:name="com.kerwin.wumei.core.XPageTransferActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
|
||||
<!--屏幕自适应设计图-->
|
||||
<meta-data
|
||||
android:name="design_width_in_dp"
|
||||
android:value="360" />
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="640" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
21
android/app/src/main/assets/tips.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Code": 0,
|
||||
"Data": [
|
||||
{
|
||||
"title": "微信公众号",
|
||||
"content": "<a href=\"https://t.1yb.co/71xZ\">获取更多资讯内容,欢迎微信搜索公众号:「我的Android开源之旅」</a>"
|
||||
},
|
||||
{
|
||||
"title": "关于作者",
|
||||
"content": "点击关注作者,了解最新动态!<br /><a href=\"https://github.com/xuexiangjys\"><font color=\"#800080\">Github</font></a><br />\n<a href=\"https://www.zhihu.com/people/xuexiangjys/posts\"><font color=\"#0000FF\">知乎</font></a><br />\n<a href=\"https://juejin.im/user/598feef55188257d592e56ed/posts\"><font color=\"#000000\">掘金</font></a><br /><a href=\"https://www.jianshu.com/u/6bf605575337\"><font color=\"#FF0000\">简书</font></a><br />\n<a href=\"https://segmentfault.com/blog/openAndroidX\"><font color=\"#008000\">思否</font></a><br />\n<a href=\"https://space.bilibili.com/483850585/video\"><font color=\"#FFA500\">哔哩哔哩</font></a><br />\n<a href=\"https://www.toutiao.com/c/user/token/MS4wLjABAAAAqD0Pe01AhT2Hgi3w7HzboVuq57gntoAJJURwAkM3Elv0-EA9WSKUSy1DujIYLAEm/\"><font color=\"#FF0000\">今日头条</font></a>"
|
||||
},
|
||||
{
|
||||
"title": "赞助作者",
|
||||
"content": "你的打赏是我维护的动力,<a href=\"https://gitee.com/xuexiangjys/Resource/blob/master/doc/sponsor.md\"><font color=\"#800080\">点击此处支持我吧!</font></a>"
|
||||
},
|
||||
{
|
||||
"title": "QQ交流群",
|
||||
"content": "<a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=i1rhgvRxlJRlJzigL0QemICtkk0H6g3J\">XUI开源交流1号群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=_0IhSCvLxdsgxAIBtTXr__bwsxZl4Eva\">XUI开源交流2号群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=GRVNTA4ehFuIUhQwqiZkjqYeQoxRBqCI\">AndroidGitHub开源交流群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=0OHp7jgMiF9SsfrzmOaxcauxMP9fZjc3\">XUpdate官方交流群</a>"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
android/app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
114
android/app/src/main/java/com/kerwin/wumei/MyApp.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.kerwin.wumei;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.location.LocationManager;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.multidex.MultiDex;
|
||||
|
||||
import com.kerwin.wumei.BuildConfig;
|
||||
import com.kerwin.wumei.utils.sdkinit.ANRWatchDogInit;
|
||||
import com.kerwin.wumei.utils.sdkinit.UMengInit;
|
||||
import com.kerwin.wumei.utils.sdkinit.XBasicLibInit;
|
||||
import com.kerwin.wumei.utils.sdkinit.XUpdateInit;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author xuexiang
|
||||
* @since 2018/11/7 下午1:12
|
||||
*/
|
||||
public class MyApp extends Application {
|
||||
|
||||
private static MyApp app;
|
||||
private MutableLiveData<String> mBroadcastData;
|
||||
|
||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (action == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case WifiManager.NETWORK_STATE_CHANGED_ACTION:
|
||||
case LocationManager.PROVIDERS_CHANGED_ACTION:
|
||||
mBroadcastData.setValue(action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
//解决4.x运行崩溃的问题
|
||||
MultiDex.install(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
initLibs();
|
||||
|
||||
app = this;
|
||||
mBroadcastData = new MutableLiveData<>();
|
||||
IntentFilter filter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
filter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
|
||||
}
|
||||
registerReceiver(mReceiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
public static MyApp getInstance() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public void observeBroadcast(LifecycleOwner owner, Observer<String> observer) {
|
||||
mBroadcastData.observe(owner, observer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化基础库
|
||||
*/
|
||||
private void initLibs() {
|
||||
XBasicLibInit.init(this);
|
||||
|
||||
XUpdateInit.init(this);
|
||||
|
||||
//运营统计数据运行时不初始化
|
||||
if (!MyApp.isDebug()) {
|
||||
UMengInit.init(this);
|
||||
}
|
||||
|
||||
//ANR监控
|
||||
ANRWatchDogInit.init();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 当前app是否是调试开发模式
|
||||
*/
|
||||
public static boolean isDebug() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
|
||||
package com.kerwin.wumei.activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.LocationManager;
|
||||
import android.net.wifi.ScanResult;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.location.LocationManagerCompat;
|
||||
|
||||
import com.espressif.iot.esptouch.EsptouchTask;
|
||||
import com.espressif.iot.esptouch.IEsptouchResult;
|
||||
import com.espressif.iot.esptouch.IEsptouchTask;
|
||||
import com.espressif.iot.esptouch.util.ByteUtil;
|
||||
import com.espressif.iot.esptouch.util.TouchNetUtil;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.adapter.entity.EspTouchViewModel;
|
||||
import com.kerwin.wumei.core.BaseActivity;
|
||||
import com.kerwin.wumei.fragment.LoginFragment;
|
||||
import com.kerwin.wumei.utils.NetUtils;
|
||||
import com.xuexiang.xui.utils.KeyboardUtils;
|
||||
import com.xuexiang.xui.utils.StatusBarUtils;
|
||||
import com.xuexiang.xutil.display.Colors;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class AddDeviceActivity extends BaseActivity {
|
||||
|
||||
// begin esptouch -------------------------------------
|
||||
private static final String TAG = AddDeviceActivity.class.getSimpleName();
|
||||
private static final int REQUEST_PERMISSION = 0x01;
|
||||
private EspTouchViewModel mViewModel;
|
||||
private EsptouchAsyncTask4 mTask;
|
||||
private WifiManager mWifiManager;
|
||||
private List<String> ssids;
|
||||
private String selectedSSID;
|
||||
|
||||
public String GetSelectedSSID(){
|
||||
return selectedSSID;
|
||||
}
|
||||
public List<String> GetSsids(){
|
||||
return ssids;
|
||||
}
|
||||
public EspTouchViewModel GetMViewModel(){
|
||||
return mViewModel;
|
||||
}
|
||||
|
||||
public void executeEsptouch() {
|
||||
EspTouchViewModel viewModel = mViewModel;
|
||||
// byte[] ssid = viewModel.ssidBytes == null ? ByteUtil.getBytesByString(viewModel.ssid): viewModel.ssidBytes;
|
||||
CharSequence ssidStr=mViewModel.ssidSpinner.getText();
|
||||
byte[] ssid= ByteUtil.getBytesByString(ssidStr.toString());
|
||||
CharSequence pwdStr = mViewModel.apPasswordEdit.getText();
|
||||
byte[] password = pwdStr == null ? null : ByteUtil.getBytesByString(pwdStr.toString());
|
||||
byte[] bssid = TouchNetUtil.parseBssid2bytes(viewModel.bssid);
|
||||
byte[] broadcast = {(byte) (mViewModel.packageModeGroup.getCheckedRadioButtonId() == R.id.packageBroadcast? 1 : 0)};
|
||||
byte[] deviceCount = "1".getBytes();
|
||||
if (mTask != null) {
|
||||
mTask.cancelEsptouch();
|
||||
}
|
||||
mTask = new EsptouchAsyncTask4(this);
|
||||
mTask.execute(ssid, bssid, password, deviceCount, broadcast);
|
||||
}
|
||||
|
||||
public void onWifiChanged() {
|
||||
StateResult stateResult = check();
|
||||
mViewModel.message = stateResult.message;
|
||||
mViewModel.ssid = stateResult.ssid;
|
||||
mViewModel.ssidBytes = stateResult.ssidBytes;
|
||||
mViewModel.bssid = stateResult.bssid;
|
||||
mViewModel.confirmEnable = false;
|
||||
if (stateResult.wifiConnected) {
|
||||
mViewModel.confirmEnable = true;
|
||||
if (stateResult.is5G) {
|
||||
mViewModel.message = getString(R.string.esptouch1_wifi_5g_message);
|
||||
}
|
||||
} else {
|
||||
if (mTask != null) {
|
||||
mTask.cancelEsptouch();
|
||||
mTask = null;
|
||||
new AlertDialog.Builder(AddDeviceActivity.this)
|
||||
.setMessage(R.string.esptouch1_configure_wifi_change_message)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
mViewModel.invalidateAll();
|
||||
}
|
||||
|
||||
protected static class StateResult {
|
||||
public CharSequence message = null;
|
||||
public boolean permissionGranted = false;
|
||||
public boolean locationRequirement = false;
|
||||
public boolean wifiConnected = false;
|
||||
public boolean is5G = false;
|
||||
public InetAddress address = null;
|
||||
public String ssid = null;
|
||||
public byte[] ssidBytes = null;
|
||||
public String bssid = null;
|
||||
}
|
||||
|
||||
private StateResult check() {
|
||||
StateResult result = checkPermission();
|
||||
if (!result.permissionGranted) {
|
||||
return result;
|
||||
}
|
||||
result = checkLocation();
|
||||
result.permissionGranted = true;
|
||||
if (result.locationRequirement) {
|
||||
return result;
|
||||
}
|
||||
result = checkWifi();
|
||||
result.permissionGranted = true;
|
||||
result.locationRequirement = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected StateResult checkWifi() {
|
||||
StateResult result = new StateResult();
|
||||
result.wifiConnected = false;
|
||||
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
|
||||
|
||||
// 获取wifi列表
|
||||
mWifiManager.startScan();
|
||||
List<ScanResult> scanWifiList = mWifiManager.getScanResults();
|
||||
List<ScanResult> wifiList = new ArrayList<>();
|
||||
ssids=new ArrayList<>();
|
||||
if (scanWifiList != null && scanWifiList.size() > 0) {
|
||||
HashMap<String, Integer> signalStrength = new HashMap<String, Integer>();
|
||||
for (int i = 0; i < scanWifiList.size(); i++) {
|
||||
ScanResult scanResult = scanWifiList.get(i);
|
||||
Log.e(TAG, "搜索的wifi-ssid:" + scanResult.SSID);
|
||||
if (!scanResult.SSID.isEmpty()) {
|
||||
String key = scanResult.SSID + " " + scanResult.capabilities;
|
||||
if (!signalStrength.containsKey(key)) {
|
||||
signalStrength.put(key, i);
|
||||
wifiList.add(scanResult);
|
||||
ssids.add(scanResult.SSID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean connected = NetUtils.isWifiConnected(mWifiManager);
|
||||
if (!connected) {
|
||||
result.message = getString(R.string.esptouch_message_wifi_connection);
|
||||
return result;
|
||||
}
|
||||
|
||||
String ssid = NetUtils.getSsidString(wifiInfo);
|
||||
selectedSSID=ssid;
|
||||
int ipValue = wifiInfo.getIpAddress();
|
||||
if (ipValue != 0) {
|
||||
result.address = NetUtils.getAddress(wifiInfo.getIpAddress());
|
||||
} else {
|
||||
result.address = NetUtils.getIPv4Address();
|
||||
if (result.address == null) {
|
||||
result.address = NetUtils.getIPv6Address();
|
||||
}
|
||||
}
|
||||
|
||||
result.wifiConnected = true;
|
||||
result.message = "";
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
result.is5G = NetUtils.is5G(wifiInfo.getFrequency());
|
||||
}
|
||||
if (result.is5G) {
|
||||
result.message = getString(R.string.esptouch_message_wifi_frequency);
|
||||
}
|
||||
result.ssid = ssid;
|
||||
result.ssidBytes = NetUtils.getRawSsidBytesOrElse(wifiInfo, ssid.getBytes());
|
||||
result.bssid = wifiInfo.getBSSID();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected StateResult checkLocation() {
|
||||
StateResult result = new StateResult();
|
||||
result.locationRequirement = true;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
LocationManager manager = getSystemService(LocationManager.class);
|
||||
boolean enable = manager != null && LocationManagerCompat.isLocationEnabled(manager);
|
||||
if (!enable) {
|
||||
result.message = getString(R.string.esptouch_message_location);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.locationRequirement = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected StateResult checkPermission() {
|
||||
StateResult result = new StateResult();
|
||||
result.permissionGranted = false;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
boolean locationGranted = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
if (!locationGranted) {
|
||||
String[] splits = getString(R.string.esptouch_message_permission).split("\n");
|
||||
if (splits.length != 2) {
|
||||
throw new IllegalArgumentException("Invalid String @RES esptouch_message_permission");
|
||||
}
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder(splits[0]);
|
||||
ssb.append('\n');
|
||||
SpannableString clickMsg = new SpannableString(splits[1]);
|
||||
ForegroundColorSpan clickSpan = new ForegroundColorSpan(0xFF0022FF);
|
||||
clickMsg.setSpan(clickSpan, 0, clickMsg.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
ssb.append(clickMsg);
|
||||
result.message = ssb;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.permissionGranted = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == REQUEST_PERMISSION) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
onWifiChanged();
|
||||
} else {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.esptouch1_location_permission_title)
|
||||
.setMessage(R.string.esptouch1_location_permission_message)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> finish())
|
||||
.show();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
public static class EsptouchAsyncTask4 extends AsyncTask<byte[], IEsptouchResult, List<IEsptouchResult>> {
|
||||
private WeakReference<AddDeviceActivity> mActivity;
|
||||
private final Object mLock = new Object();
|
||||
private ProgressDialog mProgressDialog;
|
||||
private AlertDialog mResultDialog;
|
||||
private IEsptouchTask mEsptouchTask;
|
||||
|
||||
EsptouchAsyncTask4(AddDeviceActivity activity) {
|
||||
mActivity = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
public void cancelEsptouch() {
|
||||
cancel(true);
|
||||
if (mProgressDialog != null) {
|
||||
mProgressDialog.dismiss();
|
||||
}
|
||||
if (mResultDialog != null) {
|
||||
mResultDialog.dismiss();
|
||||
}
|
||||
if (mEsptouchTask != null) {
|
||||
mEsptouchTask.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Activity activity = mActivity.get();
|
||||
mProgressDialog = new ProgressDialog(activity);
|
||||
mProgressDialog.setMessage(activity.getString(R.string.esptouch1_configuring_message));
|
||||
mProgressDialog.setCanceledOnTouchOutside(false);
|
||||
mProgressDialog.setOnCancelListener(dialog -> {
|
||||
synchronized (mLock) {
|
||||
if (mEsptouchTask != null) {
|
||||
mEsptouchTask.interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
mProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getText(android.R.string.cancel),
|
||||
(dialog, which) -> {
|
||||
synchronized (mLock) {
|
||||
if (mEsptouchTask != null) {
|
||||
mEsptouchTask.interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
mProgressDialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(IEsptouchResult... values) {
|
||||
Context context = mActivity.get();
|
||||
if (context != null) {
|
||||
IEsptouchResult result = values[0];
|
||||
Log.i(TAG, "EspTouchResult: " + result);
|
||||
String text = result.getBssid() + " is connected to the wifi";
|
||||
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<IEsptouchResult> doInBackground(byte[]... params) {
|
||||
AddDeviceActivity activity = mActivity.get();
|
||||
int taskResultCount;
|
||||
synchronized (mLock) {
|
||||
byte[] apSsid = params[0];
|
||||
byte[] apBssid = params[1];
|
||||
byte[] apPassword = params[2];
|
||||
byte[] deviceCountData = params[3];
|
||||
byte[] broadcastData = params[4];
|
||||
taskResultCount = deviceCountData.length == 0 ? -1 : Integer.parseInt(new String(deviceCountData));
|
||||
Context context = activity.getApplicationContext();
|
||||
mEsptouchTask = new EsptouchTask(apSsid, apBssid, apPassword, context);
|
||||
mEsptouchTask.setPackageBroadcast(broadcastData[0] == 1);
|
||||
mEsptouchTask.setEsptouchListener(this::publishProgress);
|
||||
}
|
||||
return mEsptouchTask.executeForResults(taskResultCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<IEsptouchResult> result) {
|
||||
AddDeviceActivity activity = mActivity.get();
|
||||
activity.mTask = null;
|
||||
mProgressDialog.dismiss();
|
||||
if (result == null) {
|
||||
mResultDialog = new AlertDialog.Builder(activity)
|
||||
.setMessage(R.string.esptouch1_configure_result_failed_port)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
mResultDialog.setCanceledOnTouchOutside(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// check whether the task is cancelled and no results received
|
||||
IEsptouchResult firstResult = result.get(0);
|
||||
if (firstResult.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// the task received some results including cancelled while
|
||||
// executing before receiving enough results
|
||||
|
||||
if (!firstResult.isSuc()) {
|
||||
mResultDialog = new AlertDialog.Builder(activity)
|
||||
.setMessage(R.string.esptouch1_configure_result_failed)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
mResultDialog.setCanceledOnTouchOutside(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<CharSequence> resultMsgList = new ArrayList<>(result.size());
|
||||
for (IEsptouchResult touchResult : result) {
|
||||
String message = activity.getString(R.string.esptouch1_configure_result_success_item,
|
||||
touchResult.getBssid(), touchResult.getInetAddress().getHostAddress());
|
||||
resultMsgList.add(message);
|
||||
}
|
||||
CharSequence[] items = new CharSequence[resultMsgList.size()];
|
||||
mResultDialog = new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.esptouch1_configure_result_success)
|
||||
.setItems(resultMsgList.toArray(items), null)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
mResultDialog.setCanceledOnTouchOutside(false);
|
||||
}
|
||||
}
|
||||
|
||||
// end esptouch ----------------------------------------
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mWifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
|
||||
mViewModel = new EspTouchViewModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSupportSlideBack() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initStatusBarStyle() {
|
||||
StatusBarUtils.initStatusBarStyle(this, false, Colors.WHITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
package com.kerwin.wumei.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.kerwin.wumei.core.BaseActivity;
|
||||
import com.kerwin.wumei.fragment.LoginFragment;
|
||||
import com.xuexiang.xui.utils.KeyboardUtils;
|
||||
import com.xuexiang.xui.utils.StatusBarUtils;
|
||||
import com.xuexiang.xutil.display.Colors;
|
||||
|
||||
|
||||
public class LoginActivity extends BaseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
openPage(LoginFragment.class, getIntent().getExtras());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSupportSlideBack() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initStatusBarStyle() {
|
||||
StatusBarUtils.initStatusBarStyle(this, false, Colors.WHITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.LocationManager;
|
||||
import android.net.wifi.ScanResult;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.location.LocationManagerCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.espressif.iot.esptouch.EsptouchTask;
|
||||
import com.espressif.iot.esptouch.IEsptouchResult;
|
||||
import com.espressif.iot.esptouch.IEsptouchTask;
|
||||
import com.espressif.iot.esptouch.util.ByteUtil;
|
||||
import com.espressif.iot.esptouch.util.TouchNetUtil;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.adapter.entity.EspTouchViewModel;
|
||||
import com.kerwin.wumei.core.BaseActivity;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.fragment.AboutFragment;
|
||||
import com.kerwin.wumei.fragment.FeedbackFragment;
|
||||
import com.kerwin.wumei.fragment.MessageFragment;
|
||||
import com.kerwin.wumei.fragment.SettingsFragment;
|
||||
import com.kerwin.wumei.fragment.device.AddDeviceFragment;
|
||||
import com.kerwin.wumei.fragment.device.GroupFragment;
|
||||
import com.kerwin.wumei.fragment.device.SceneFragment;
|
||||
import com.kerwin.wumei.fragment.device.ShareDeviceFragment;
|
||||
import com.kerwin.wumei.fragment.news.NewsFragment;
|
||||
import com.kerwin.wumei.fragment.profile.ProfileFragment;
|
||||
import com.kerwin.wumei.fragment.device.DeviceFragment;
|
||||
import com.kerwin.wumei.utils.NetUtils;
|
||||
import com.kerwin.wumei.utils.Utils;
|
||||
import com.kerwin.wumei.utils.XToastUtils;
|
||||
import com.kerwin.wumei.widget.GuideTipsDialog;
|
||||
import com.xuexiang.xaop.annotation.SingleClick;
|
||||
import com.xuexiang.xpage.core.PageOption;
|
||||
import com.xuexiang.xpage.enums.CoreAnim;
|
||||
import com.xuexiang.xui.adapter.FragmentAdapter;
|
||||
import com.xuexiang.xui.adapter.simple.AdapterItem;
|
||||
import com.xuexiang.xui.utils.ResUtils;
|
||||
import com.xuexiang.xui.utils.ThemeUtils;
|
||||
import com.xuexiang.xui.widget.imageview.RadiusImageView;
|
||||
import com.xuexiang.xui.widget.popupwindow.popup.XUISimplePopup;
|
||||
import com.xuexiang.xutil.XUtil;
|
||||
import com.xuexiang.xutil.common.ClickUtils;
|
||||
import com.xuexiang.xutil.common.CollectionUtils;
|
||||
import com.xuexiang.xutil.display.Colors;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class MainActivity extends BaseActivity implements View.OnClickListener, ViewPager.OnPageChangeListener, BottomNavigationView.OnNavigationItemSelectedListener, ClickUtils.OnClick2ExitListener, Toolbar.OnMenuItemClickListener {
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSupportSlideBack() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 DeviceFragment(),
|
||||
new SceneFragment(),
|
||||
new NewsFragment(),
|
||||
new ProfileFragment(),
|
||||
};
|
||||
FragmentAdapter<BaseFragment> 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("15208747707");
|
||||
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 -> {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.nav_add_device:
|
||||
PageOption.to(AddDeviceFragment.class) //跳转的fragment
|
||||
.setAnim(CoreAnim.slide) //页面转场动画
|
||||
.setRequestCode(100) //请求码,用于返回结果
|
||||
.setAddToBackStack(true) //是否加入堆栈
|
||||
.setNewActivity(true,AddDeviceActivity.class) //是否使用新的Activity打开
|
||||
.open(this); //打开页面进行跳转
|
||||
break;
|
||||
case R.id.nav_settings:
|
||||
openNewPage(SettingsFragment.class);
|
||||
break;
|
||||
case R.id.nav_about:
|
||||
openNewPage(AboutFragment.class);
|
||||
break;
|
||||
case R.id.nav_message:
|
||||
openNewPage(MessageFragment.class);
|
||||
break;
|
||||
case R.id.nav_share_device:
|
||||
openNewPage(ShareDeviceFragment.class);
|
||||
break;
|
||||
case R.id.nav_group:
|
||||
openNewPage(GroupFragment.class);
|
||||
break;
|
||||
default:
|
||||
XToastUtils.toast("点击了:" + menuItem.getTitle());
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
//主页事件监听
|
||||
viewPager.addOnPageChangeListener(this);
|
||||
bottomNavigation.setOnNavigationItemSelectedListener(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.add_device:
|
||||
PageOption.to(AddDeviceFragment.class) //跳转的fragment
|
||||
.setAnim(CoreAnim.slide) //页面转场动画
|
||||
.setRequestCode(100) //请求码,用于返回结果
|
||||
.setAddToBackStack(true) //是否加入堆栈
|
||||
.setNewActivity(true, AddDeviceActivity.class) //是否使用新的Activity打开
|
||||
.open(this); //打开页面进行跳转
|
||||
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);
|
||||
}
|
||||
|
||||
@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);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 菜单、返回键响应
|
||||
*/
|
||||
@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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.kerwin.wumei.activity;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.utils.SettingUtils;
|
||||
import com.kerwin.wumei.utils.TokenUtils;
|
||||
import com.kerwin.wumei.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;
|
||||
|
||||
/**
|
||||
* 启动页【无需适配屏幕大小】
|
||||
*
|
||||
*/
|
||||
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() {
|
||||
if (TokenUtils.hasToken()) {
|
||||
ActivityUtils.startActivity(MainActivity.class);
|
||||
} else {
|
||||
ActivityUtils.startActivity(LoginActivity.class);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单、返回键响应
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
@@ -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.kerwin.wumei.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<T> extends BaseRecyclerAdapter<T> {
|
||||
/**
|
||||
* 是否已经加载成功
|
||||
*/
|
||||
private boolean mHasLoad = false;
|
||||
private Map<View, Broccoli> mBroccoliMap = new HashMap<>();
|
||||
|
||||
public BroccoliRecyclerAdapter(Collection<T> 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<T> collection) {
|
||||
mHasLoad = true;
|
||||
return super.refresh(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源释放,防止内存泄漏
|
||||
*/
|
||||
public void recycle() {
|
||||
for (Broccoli broccoli : mBroccoliMap.values()) {
|
||||
broccoli.removeAllPlaceholders();
|
||||
}
|
||||
mBroccoliMap.clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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.kerwin.wumei.adapter.base.broccoli;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.alibaba.android.vlayout.LayoutHelper;
|
||||
import com.kerwin.wumei.adapter.base.delegate.SimpleDelegateAdapter;
|
||||
import com.kerwin.wumei.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<T> extends SimpleDelegateAdapter<T> {
|
||||
|
||||
/**
|
||||
* 是否已经加载成功
|
||||
*/
|
||||
private boolean mHasLoad = false;
|
||||
private Map<View, Broccoli> mBroccoliMap = new HashMap<>();
|
||||
|
||||
public BroccoliSimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper) {
|
||||
super(layoutId, layoutHelper);
|
||||
}
|
||||
|
||||
public BroccoliSimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper, Collection<T> 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<T> collection) {
|
||||
mHasLoad = true;
|
||||
return super.refresh(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源释放,防止内存泄漏
|
||||
*/
|
||||
public void recycle() {
|
||||
for (Broccoli broccoli : mBroccoliMap.values()) {
|
||||
broccoli.removeAllPlaceholders();
|
||||
}
|
||||
mBroccoliMap.clear();
|
||||
clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<T> extends XDelegateAdapter<T, RecyclerViewHolder> {
|
||||
|
||||
public BaseDelegateAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BaseDelegateAdapter(Collection<T> 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)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<T> extends BaseDelegateAdapter<T> {
|
||||
|
||||
private int mLayoutId;
|
||||
|
||||
private LayoutHelper mLayoutHelper;
|
||||
|
||||
public SimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper) {
|
||||
super();
|
||||
mLayoutId = layoutId;
|
||||
mLayoutHelper = layoutHelper;
|
||||
}
|
||||
|
||||
public SimpleDelegateAdapter(int layoutId, LayoutHelper layoutHelper, Collection<T> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<RecyclerViewHolder> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<T, V extends RecyclerView.ViewHolder> extends DelegateAdapter.Adapter<V> {
|
||||
/**
|
||||
* 数据源
|
||||
*/
|
||||
protected final List<T> mData = new ArrayList<>();
|
||||
/**
|
||||
* 当前点击的条目
|
||||
*/
|
||||
protected int mSelectPosition = -1;
|
||||
|
||||
public XDelegateAdapter() {
|
||||
|
||||
}
|
||||
|
||||
public XDelegateAdapter(Collection<T> 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<T> 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<T> 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<T> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.kerwin.wumei.adapter.entity;
|
||||
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner;
|
||||
|
||||
public class EspTouchViewModel {
|
||||
public MaterialSpinner ssidSpinner;
|
||||
public EditText apPasswordEdit;
|
||||
public EditText deviceCountEdit;
|
||||
public RadioGroup packageModeGroup;
|
||||
public TextView messageView;
|
||||
public Button confirmBtn;
|
||||
|
||||
public String ssid;
|
||||
public byte[] ssidBytes;
|
||||
public String bssid;
|
||||
|
||||
public CharSequence message;
|
||||
|
||||
public boolean confirmEnable;
|
||||
|
||||
public void invalidateAll() {
|
||||
ssidSpinner.setText(ssid);
|
||||
messageView.setText(message);
|
||||
confirmBtn.setEnabled(confirmEnable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.adapter.entity;
|
||||
|
||||
/**
|
||||
* 新闻信息
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2019/4/7 下午12:07
|
||||
*/
|
||||
public class NewInfo {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String UserName = "kerwin";
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
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;
|
||||
|
||||
@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);
|
||||
|
||||
registerSlideBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化状态栏的样式
|
||||
*/
|
||||
protected void initStatusBarStyle() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开fragment
|
||||
*
|
||||
* @param clazz 页面类
|
||||
* @param addToBackStack 是否添加到栈中
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
public <T extends XPageFragment> T openPage(Class<T> clazz, boolean addToBackStack) {
|
||||
CoreSwitchBean page = new CoreSwitchBean(clazz)
|
||||
.setAddToBackStack(addToBackStack);
|
||||
return (T) openPage(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开fragment
|
||||
*
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
public <T extends XPageFragment> T openNewPage(Class<T> clazz) {
|
||||
CoreSwitchBean page = new CoreSwitchBean(clazz)
|
||||
.setNewActivity(true);
|
||||
return (T) openPage(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换fragment
|
||||
*
|
||||
* @param clazz 页面类
|
||||
* @return 打开的fragment对象
|
||||
*/
|
||||
public <T extends XPageFragment> T switchPage(Class<T> 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();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.umeng.analytics.MobclickAgent;
|
||||
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.kerwin.wumei.core.SimpleListAdapter.KEY_SUB_TITLE;
|
||||
import static com.kerwin.wumei.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<Map<String, String>> data = new ArrayList<>();
|
||||
for (String content : mSimpleData) {
|
||||
Map<String, String> 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();
|
||||
MobclickAgent.onPageStart(getPageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
MobclickAgent.onPageEnd(getPageName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Parcelable;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.umeng.analytics.MobclickAgent;
|
||||
import com.kerwin.wumei.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;
|
||||
|
||||
@Override
|
||||
protected void initPage() {
|
||||
initTitle();
|
||||
initViews();
|
||||
initListeners();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
MobclickAgent.onPageStart(getPageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
MobclickAgent.onPageEnd(getPageName());
|
||||
}
|
||||
|
||||
//==============================页面跳转api===================================//
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(Class<T> clazz) {
|
||||
return new PageOption(clazz)
|
||||
.setNewActivity(true)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param pageName 页面名
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(String pageName) {
|
||||
return new PageOption(pageName)
|
||||
.setAnim(CoreAnim.slide)
|
||||
.setNewActivity(true)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param containActivityClazz 页面容器
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(Class<T> clazz, @NonNull Class<? extends XPageActivity> containActivityClazz) {
|
||||
return new PageOption(clazz)
|
||||
.setNewActivity(true)
|
||||
.setContainActivityClazz(containActivityClazz)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param key 入参的键
|
||||
* @param value 入参的值
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> clazz, String key, Object value) {
|
||||
return openPage(clazz, true, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开页面
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param addToBackStack 是否加入回退栈
|
||||
* @param key 入参的键
|
||||
* @param value 入参的值
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> clazz, String key, String value) {
|
||||
return new PageOption(clazz)
|
||||
.putString(key, value)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开页面,需要结果返回
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param key 入参的键
|
||||
* @param value 入参的值
|
||||
* @param requestCode 请求码
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPageForResult(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPageForResult(Class<T> clazz, String key, String value, int requestCode) {
|
||||
return new PageOption(clazz)
|
||||
.setRequestCode(requestCode)
|
||||
.putString(key, value)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开页面,需要结果返回
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param requestCode 请求码
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPageForResult(Class<T> 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> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.umeng.analytics.MobclickAgent;
|
||||
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();
|
||||
MobclickAgent.onPageStart(getPageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
MobclickAgent.onPageEnd(getPageName());
|
||||
}
|
||||
|
||||
//==============================页面跳转api===================================//
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(Class<T> clazz) {
|
||||
return new PageOption(clazz)
|
||||
.setNewActivity(true)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param pageName 页面名
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(String pageName) {
|
||||
return new PageOption(pageName)
|
||||
.setAnim(CoreAnim.slide)
|
||||
.setNewActivity(true)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param containActivityClazz 页面容器
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(Class<T> clazz, @NonNull Class<? extends XPageActivity> containActivityClazz) {
|
||||
return new PageOption(clazz)
|
||||
.setNewActivity(true)
|
||||
.setContainActivityClazz(containActivityClazz)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开一个新的页面【建议只在主tab页使用】
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param key 入参的键
|
||||
* @param value 入参的值
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openNewPage(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> clazz, String key, Object value) {
|
||||
return openPage(clazz, true, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开页面
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param addToBackStack 是否加入回退栈
|
||||
* @param key 入参的键
|
||||
* @param value 入参的值
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPage(Class<T> clazz, String key, String value) {
|
||||
return new PageOption(clazz)
|
||||
.putString(key, value)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开页面,需要结果返回
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param key 入参的键
|
||||
* @param value 入参的值
|
||||
* @param requestCode 请求码
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPageForResult(Class<T> 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPageForResult(Class<T> clazz, String key, String value, int requestCode) {
|
||||
return new PageOption(clazz)
|
||||
.setRequestCode(requestCode)
|
||||
.putString(key, value)
|
||||
.open(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开页面,需要结果返回
|
||||
*
|
||||
* @param clazz 页面的类
|
||||
* @param requestCode 请求码
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T extends XPageFragment> Fragment openPageForResult(Class<T> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kerwin.wumei.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<Map<String, String>, 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<Map<String, String>> data) {
|
||||
super(context, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewHolder newViewHolder(View convertView) {
|
||||
ViewHolder holder = new ViewHolder();
|
||||
holder.mTvTitle = convertView.findViewById(R.id.device_item_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<String, String> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.kerwin.wumei.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.kerwin.wumei.core.http.api;
|
||||
|
||||
import com.kerwin.wumei.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<ApiResult<List<TipInfo>>> getTips();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<T> extends SimpleCallBack<T> {
|
||||
|
||||
/**
|
||||
* 记录一下请求的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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core.http.callback;
|
||||
|
||||
import com.kerwin.wumei.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<T> extends SimpleCallBack<T> {
|
||||
|
||||
/**
|
||||
* 记录一下请求的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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core.http.callback;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.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<T> extends ProgressLoadingCallBack<T> {
|
||||
/**
|
||||
* 记录一下请求的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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core.http.entity;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
/**
|
||||
* @author xuexiang
|
||||
* @since 2019-08-28 15:35
|
||||
*/
|
||||
@Keep
|
||||
public class TipInfo {
|
||||
|
||||
/**
|
||||
* title : 小贴士3
|
||||
* content : <p style=";font-family:'Microsoft YaHei';font-size:15px">欢迎关注我的微信公众号:我的Android开源之旅。</p><p><br/></p>
|
||||
*/
|
||||
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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.kerwin.wumei.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);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<T> extends BaseSubscriber<T> {
|
||||
|
||||
/**
|
||||
* 记录一下请求的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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core.http.subscriber;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.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<T> extends ProgressLoadingSubscriber<T> {
|
||||
|
||||
/**
|
||||
* 记录一下请求的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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core.http.subscriber;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.kerwin.wumei.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<T> extends BaseSubscriber<T> {
|
||||
/**
|
||||
* 记录一下请求的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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.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;
|
||||
|
||||
/**
|
||||
* 壳浏览器
|
||||
*
|
||||
* @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(AgentWebFragment.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(AgentWebFragment.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.kerwin.wumei.MyApp;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.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<String, Long> 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() {
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.core.webview;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.just.agentweb.core.AgentWeb;
|
||||
import com.kerwin.wumei.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.kerwin.wumei.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:");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.kerwin.wumei.R;
|
||||
import com.xuexiang.xui.utils.ResUtils;
|
||||
|
||||
/**
|
||||
* 【网络请求、加载】
|
||||
* WebClient(WebViewClient 这个类主要帮助WebView处理各种通知、url加载,请求时间的)中间件
|
||||
* <p>
|
||||
* <p>
|
||||
* 方法的执行顺序,例如下面用了7个中间件一个 WebViewClient
|
||||
* <p>
|
||||
* .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
|
||||
* <p>
|
||||
* <p>
|
||||
* 典型的洋葱模型
|
||||
* 对象内部的方法执行顺序: 1->2->3->4->5->6->7->8->9->8->7->6->5->4->3->2->1
|
||||
* <p>
|
||||
* <p>
|
||||
* 中断中间件的执行, 删除super.methodName(...) 这行即可
|
||||
* <p>
|
||||
* 这里主要是做去广告的工作
|
||||
*/
|
||||
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) {
|
||||
String[] adUrls = ResUtils.getStringArray(R.array.adBlockUrl);
|
||||
for (String adUrl : adUrls) {
|
||||
if (url.contains(adUrl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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 && !(WebViewInterceptDialog.APP_LINK_HOST.equals(uri.getHost())
|
||||
//防止xui官网被拦截
|
||||
&& url.contains("xpage"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
WebViewInterceptDialog.show(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<Activity> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.kerwin.wumei.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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.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.kerwin.wumei.MyApp;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.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;
|
||||
|
||||
/**
|
||||
* 使用XPageFragment
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2019-05-26 18:15
|
||||
*/
|
||||
@Page(params = {AgentWebFragment.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(AgentWebFragment.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(AgentWebFragment.KEY_URL, url)
|
||||
.open(fragment);
|
||||
}
|
||||
|
||||
@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(AgentWebFragment.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<String, Long> 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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.fragment;
|
||||
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.core.webview.AgentWebActivity;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.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;
|
||||
|
||||
|
||||
@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 initViews() {
|
||||
mVersionTextView.setText(String.format("版本号:%s", AppUtils.getAppVersionName()));
|
||||
|
||||
XUIGroupListView.newSection(getContext())
|
||||
.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)))
|
||||
.addItemView(mAboutGroupListView.createItemView("版本"), v -> XToastUtils.toast("版本升级"))
|
||||
.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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.kerwin.wumei.fragment;
|
||||
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.core.webview.AgentWebActivity;
|
||||
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;
|
||||
|
||||
@Page(name = "意见反馈")
|
||||
public class FeedbackFragment extends BaseFragment {
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_feedback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.fragment;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
|
||||
import com.kerwin.wumei.activity.MainActivity;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.utils.RandomUtils;
|
||||
import com.kerwin.wumei.utils.SettingUtils;
|
||||
import com.kerwin.wumei.utils.TokenUtils;
|
||||
import com.kerwin.wumei.utils.Utils;
|
||||
import com.kerwin.wumei.utils.XToastUtils;
|
||||
import com.xuexiang.xaop.annotation.SingleClick;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xpage.enums.CoreAnim;
|
||||
import com.xuexiang.xui.utils.CountDownButtonHelper;
|
||||
import com.xuexiang.xui.utils.ResUtils;
|
||||
import com.xuexiang.xui.utils.ThemeUtils;
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar;
|
||||
import com.xuexiang.xui.widget.button.roundbutton.RoundButton;
|
||||
import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText;
|
||||
import com.xuexiang.xutil.app.ActivityUtils;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
|
||||
/**
|
||||
* 登录页面
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2019-11-17 22:15
|
||||
*/
|
||||
@Page(anim = CoreAnim.none)
|
||||
public class LoginFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.et_phone_number)
|
||||
MaterialEditText etPhoneNumber;
|
||||
@BindView(R.id.et_verify_code)
|
||||
MaterialEditText etVerifyCode;
|
||||
@BindView(R.id.btn_get_verify_code)
|
||||
RoundButton btnGetVerifyCode;
|
||||
|
||||
private CountDownButtonHelper mCountDownHelper;
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_login;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TitleBar initTitle() {
|
||||
TitleBar titleBar = super.initTitle()
|
||||
.setImmersive(true);
|
||||
titleBar.setBackgroundColor(Color.TRANSPARENT);
|
||||
titleBar.setTitle("");
|
||||
titleBar.setLeftImageDrawable(ResUtils.getVectorDrawable(getContext(), R.drawable.ic_login_close));
|
||||
titleBar.setActionTextColor(ThemeUtils.resolveColor(getContext(), R.attr.colorAccent));
|
||||
titleBar.addAction(new TitleBar.TextAction(R.string.title_jump_login) {
|
||||
@Override
|
||||
public void performAction(View view) {
|
||||
onLoginSuccess();
|
||||
}
|
||||
});
|
||||
return titleBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews() {
|
||||
mCountDownHelper = new CountDownButtonHelper(btnGetVerifyCode, 60);
|
||||
|
||||
//隐私政策弹窗
|
||||
if (!SettingUtils.isAgreePrivacy()) {
|
||||
Utils.showPrivacyDialog(getContext(), (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
SettingUtils.setIsAgreePrivacy(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
@OnClick({R.id.btn_get_verify_code, R.id.btn_login, R.id.tv_other_login, R.id.tv_forget_password, R.id.tv_user_protocol, R.id.tv_privacy_protocol})
|
||||
public void onViewClicked(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.btn_get_verify_code:
|
||||
if (etPhoneNumber.validate()) {
|
||||
getVerifyCode(etPhoneNumber.getEditValue());
|
||||
}
|
||||
break;
|
||||
case R.id.btn_login:
|
||||
if (etPhoneNumber.validate()) {
|
||||
if (etVerifyCode.validate()) {
|
||||
loginByVerifyCode(etPhoneNumber.getEditValue(), etVerifyCode.getEditValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case R.id.tv_other_login:
|
||||
XToastUtils.info("其他登录方式");
|
||||
break;
|
||||
case R.id.tv_forget_password:
|
||||
XToastUtils.info("忘记密码");
|
||||
break;
|
||||
case R.id.tv_user_protocol:
|
||||
XToastUtils.info("用户协议");
|
||||
break;
|
||||
case R.id.tv_privacy_protocol:
|
||||
XToastUtils.info("隐私政策");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
private void getVerifyCode(String phoneNumber) {
|
||||
// TODO: 2020/8/29 这里只是界面演示而已
|
||||
XToastUtils.warning("只是演示,验证码请随便输");
|
||||
mCountDownHelper.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据验证码登录
|
||||
*
|
||||
* @param phoneNumber 手机号
|
||||
* @param verifyCode 验证码
|
||||
*/
|
||||
private void loginByVerifyCode(String phoneNumber, String verifyCode) {
|
||||
// TODO: 2020/8/29 这里只是界面演示而已
|
||||
onLoginSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功的处理
|
||||
*/
|
||||
private void onLoginSuccess() {
|
||||
String token = RandomUtils.getRandomNumbersAndLetters(16);
|
||||
if (TokenUtils.handleLoginSuccess(token)) {
|
||||
popToBack();
|
||||
ActivityUtils.startActivity(MainActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (mCountDownHelper != null) {
|
||||
mCountDownHelper.recycle();
|
||||
}
|
||||
super.onDestroyView();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.kerwin.wumei.fragment;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.core.webview.AgentWebActivity;
|
||||
import com.xuexiang.xaop.annotation.SingleClick;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar;
|
||||
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;
|
||||
|
||||
|
||||
@Page(name = "消息")
|
||||
public class MessageFragment extends BaseFragment {
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TitleBar initTitle() {
|
||||
com.xuexiang.xui.widget.actionbar.TitleBar titleBar = super.initTitle();
|
||||
titleBar.setCenterClickListener(new View.OnClickListener() {
|
||||
@SingleClick
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
}
|
||||
});
|
||||
titleBar.addAction(new com.xuexiang.xui.widget.actionbar.TitleBar.TextAction("菜单") {
|
||||
@SingleClick
|
||||
@Override
|
||||
public void performAction(View view) {
|
||||
|
||||
}
|
||||
});
|
||||
return titleBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.kerwin.wumei.fragment;
|
||||
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.utils.TokenUtils;
|
||||
import com.kerwin.wumei.utils.XToastUtils;
|
||||
import com.xuexiang.xaop.annotation.SingleClick;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xui.widget.dialog.DialogLoader;
|
||||
import com.xuexiang.xui.widget.textview.supertextview.SuperTextView;
|
||||
import com.xuexiang.xutil.XUtil;
|
||||
|
||||
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;
|
||||
@BindView(R.id.menu_privacy)
|
||||
SuperTextView menuPrivacy;
|
||||
@BindView(R.id.menu_push)
|
||||
SuperTextView menuPush;
|
||||
@BindView(R.id.menu_helper)
|
||||
SuperTextView menuHelper;
|
||||
@BindView(R.id.menu_change_account)
|
||||
SuperTextView menuChangeAccount;
|
||||
@BindView(R.id.menu_logout)
|
||||
SuperTextView menuLogout;
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews() {
|
||||
menuCommon.setOnSuperTextViewClickListener(this);
|
||||
menuPrivacy.setOnSuperTextViewClickListener(this);
|
||||
menuPush.setOnSuperTextViewClickListener(this);
|
||||
menuHelper.setOnSuperTextViewClickListener(this);
|
||||
menuChangeAccount.setOnSuperTextViewClickListener(this);
|
||||
menuLogout.setOnSuperTextViewClickListener(this);
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
@Override
|
||||
public void onClick(SuperTextView superTextView) {
|
||||
switch (superTextView.getId()) {
|
||||
case R.id.menu_common:
|
||||
case R.id.menu_privacy:
|
||||
case R.id.menu_push:
|
||||
case R.id.menu_helper:
|
||||
XToastUtils.toast(superTextView.getLeftString());
|
||||
break;
|
||||
case R.id.menu_change_account:
|
||||
XToastUtils.toast(superTextView.getCenterString());
|
||||
break;
|
||||
case R.id.menu_logout:
|
||||
DialogLoader.getInstance().showConfirmDialog(
|
||||
getContext(),
|
||||
getString(R.string.lab_logout_confirm),
|
||||
getString(R.string.lab_yes),
|
||||
(dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
XUtil.getActivityLifecycleHelper().exit();
|
||||
TokenUtils.handleLogoutSuccess();
|
||||
},
|
||||
getString(R.string.lab_no),
|
||||
(dialog, which) -> dialog.dismiss()
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Build;
|
||||
import android.text.method.HideReturnsTransformationMethod;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.kerwin.wumei.MyApp;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.activity.AddDeviceActivity;
|
||||
import com.kerwin.wumei.activity.MainActivity;
|
||||
import com.kerwin.wumei.adapter.entity.EspTouchViewModel;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xpage.core.PageOption;
|
||||
import com.xuexiang.xpage.enums.CoreAnim;
|
||||
import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
|
||||
@Page(name = "WIFI网络配置")
|
||||
public class AddDeviceFragment extends BaseFragment {
|
||||
@BindView(R.id.advance_frame_layout)
|
||||
FrameLayout advanceFrameLayout;
|
||||
@BindView(R.id.advance_linear_layout)
|
||||
LinearLayout advanceLinearLayout;
|
||||
@BindView(R.id.advance_icon)
|
||||
AppCompatImageView advanceIcon;
|
||||
@BindView(R.id.wifi_password_icon)
|
||||
AppCompatImageView wifiPasswordIcon;
|
||||
|
||||
private static final String TAG = AddDeviceFragment.class.getSimpleName();
|
||||
private static final int REQUEST_PERMISSION = 0x01;
|
||||
private EspTouchViewModel mViewModel;
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_add_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@Override
|
||||
protected void initViews() {
|
||||
|
||||
//智能配网
|
||||
mViewModel = ((AddDeviceActivity)this.getActivity()).GetMViewModel();
|
||||
mViewModel.ssidSpinner = findViewById(R.id.ssid_spinner);
|
||||
mViewModel.apPasswordEdit = findViewById(R.id.wifi_password_txt);
|
||||
mViewModel.packageModeGroup = findViewById(R.id.packageModeGroup);
|
||||
mViewModel.messageView = findViewById(R.id.messageView);
|
||||
mViewModel.confirmBtn = findViewById(R.id.add_device_next_btn);
|
||||
mViewModel.confirmBtn.setOnClickListener(v ->
|
||||
{
|
||||
// ((AddDeviceActivity)this.getActivity()).executeEsptouch();
|
||||
PageOption.to(AddDeviceTwoFragment.class) //跳转的fragment
|
||||
.setAnim(CoreAnim.slide) //页面转场动画
|
||||
.setRequestCode(100) //请求码,用于返回结果
|
||||
.setAddToBackStack(true) //是否加入堆栈
|
||||
.putString("device_mac","0908070605040306")
|
||||
.open(this); //打开页面进行跳转
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION};
|
||||
requestPermissions(permissions, REQUEST_PERMISSION);
|
||||
}
|
||||
|
||||
MyApp.getInstance().observeBroadcast(this, broadcast -> {
|
||||
Log.d(TAG, "onCreate: Broadcast=" + broadcast);
|
||||
((AddDeviceActivity)this.getActivity()).onWifiChanged();
|
||||
|
||||
List<String> ssids=((AddDeviceActivity)this.getActivity()).GetSsids();
|
||||
if(ssids!=null && ssids.size()>0){
|
||||
Log.e(TAG, "进入数据绑定 " );
|
||||
mViewModel.ssidSpinner.setItems(ssids);
|
||||
// ssidSpinner.setOnItemSelectedListener((spinner, position, id, item) -> SnackbarUtils.Long(spinner, "Clicked " + item).show());
|
||||
// ssidSpinner.setOnNothingSelectedListener(spinner -> SnackbarUtils.Long(spinner, "Nothing selected").show());
|
||||
String ssid=((AddDeviceActivity)this.getActivity()).GetSelectedSSID();
|
||||
if(ssid!=null && ssid.length()>0 && ssids.contains(ssid)) {
|
||||
mViewModel.ssidSpinner.setSelectedItem(ssid);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initListeners() {
|
||||
//单击高级设置项
|
||||
advanceFrameLayout.setOnClickListener(new View.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int visible=advanceLinearLayout.getVisibility();
|
||||
if(visible!=0) {
|
||||
advanceLinearLayout.setVisibility(View.VISIBLE);
|
||||
advanceIcon.setImageDrawable(getResources().getDrawable((R.drawable.up)));
|
||||
}else{
|
||||
advanceLinearLayout.setVisibility(View.GONE);
|
||||
advanceIcon.setImageDrawable(getResources().getDrawable((R.drawable.down)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//显示和隐藏密码
|
||||
wifiPasswordIcon.setOnClickListener(new View.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(View view){
|
||||
if(wifiPasswordIcon.getTag()==null) return;
|
||||
if(wifiPasswordIcon.getTag().toString().equals("show")){
|
||||
wifiPasswordIcon.setImageDrawable(getResources().getDrawable((R.drawable.hide)));
|
||||
wifiPasswordIcon.setTag("hide");
|
||||
mViewModel.apPasswordEdit.setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||
}else{
|
||||
wifiPasswordIcon.setImageDrawable(getResources().getDrawable((R.drawable.show)));
|
||||
wifiPasswordIcon.setTag("show");
|
||||
mViewModel.apPasswordEdit.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.method.HideReturnsTransformationMethod;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.kerwin.wumei.MyApp;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.activity.AddDeviceActivity;
|
||||
import com.kerwin.wumei.activity.MainActivity;
|
||||
import com.kerwin.wumei.adapter.entity.EspTouchViewModel;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.utils.XToastUtils;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
|
||||
@Page(name = "设备信息")
|
||||
public class AddDeviceTwoFragment extends BaseFragment {
|
||||
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_add_device_two;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@Override
|
||||
protected void initViews() {
|
||||
|
||||
Bundle arguments = getArguments();
|
||||
String mac = arguments.getString("device_mac");
|
||||
XToastUtils.toast("设备MAC:" + mac);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initListeners() {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.utils.XToastUtils;
|
||||
import com.xuexiang.xaop.annotation.SingleClick;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xui.adapter.simple.AdapterItem;
|
||||
import com.xuexiang.xui.utils.WidgetUtils;
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar;
|
||||
import com.xuexiang.xui.widget.popupwindow.popup.XUISimplePopup;
|
||||
import com.xuexiang.xutil.display.ViewUtils;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import static com.google.android.material.tabs.TabLayout.MODE_SCROLLABLE;
|
||||
|
||||
@Page(name = "设备")
|
||||
public class DeviceFragment extends BaseFragment implements TabLayout.OnTabSelectedListener{
|
||||
@BindView(R.id.tab_layout)
|
||||
TabLayout tabLayout;
|
||||
@BindView(R.id.view_pager)
|
||||
ViewPager2 viewPager;
|
||||
|
||||
private boolean mIsShowNavigationView;
|
||||
private FragmentStateViewPager2Adapter mAdapter;
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
@Override
|
||||
protected TitleBar initTitle() {
|
||||
// mAdapter.addFragment(2, SimpleTabFragment.newInstance("动态加入"), "动态加入");
|
||||
// mAdapter.removeFragment(2);
|
||||
// mAdapter.notifyDataSetChanged();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@Override
|
||||
protected void initViews() {
|
||||
mAdapter = new FragmentStateViewPager2Adapter(this);
|
||||
tabLayout.setTabMode(MODE_SCROLLABLE);
|
||||
tabLayout.addOnTabSelectedListener(this);
|
||||
viewPager.setAdapter(mAdapter);
|
||||
// 设置缓存的数量
|
||||
viewPager.setOffscreenPageLimit(1);
|
||||
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText(mAdapter.getPageTitle(position))).attach();
|
||||
|
||||
// 动态加载选项卡内容
|
||||
for (String page : MultiPage.getPageNames()) {
|
||||
mAdapter.addFragment(SimpleTabFragment.newInstance(page), page);
|
||||
}
|
||||
mAdapter.notifyDataSetChanged();
|
||||
viewPager.setCurrentItem(0, false);
|
||||
WidgetUtils.setTabLayoutTextFont(tabLayout);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
XToastUtils.toast("选中了:" + tab.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
|
||||
@Page(name = "分享设备")
|
||||
public class EditDeviceFragment extends BaseFragment {
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_edit_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@Override
|
||||
protected void initViews() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author xuexiang
|
||||
* @since 2020/5/21 1:27 AM
|
||||
*/
|
||||
public class FragmentStateViewPager2Adapter extends FragmentStateAdapter {
|
||||
|
||||
private List<Fragment> mFragmentList = new ArrayList<>();
|
||||
|
||||
private List<String> mTitleList = new ArrayList<>();
|
||||
|
||||
private List<Long> mIds = new ArrayList<>();
|
||||
|
||||
private AtomicLong mAtomicLong = new AtomicLong(0);
|
||||
|
||||
public FragmentStateViewPager2Adapter(@NonNull Fragment fragment) {
|
||||
super(fragment);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
return mFragmentList.get(position);
|
||||
}
|
||||
|
||||
public FragmentStateViewPager2Adapter addFragment(Fragment fragment, String title) {
|
||||
if (fragment != null) {
|
||||
mFragmentList.add(fragment);
|
||||
mTitleList.add(title);
|
||||
mIds.add(getAtomicGeneratedId());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public FragmentStateViewPager2Adapter addFragment(int index, Fragment fragment, String title) {
|
||||
if (fragment != null && index >= 0 && index <= mFragmentList.size()) {
|
||||
mFragmentList.add(index, fragment);
|
||||
mTitleList.add(index, title);
|
||||
mIds.add(index, getAtomicGeneratedId());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public FragmentStateViewPager2Adapter removeFragment(int index) {
|
||||
if (index >= 0 && index < mFragmentList.size()) {
|
||||
mFragmentList.remove(index);
|
||||
mTitleList.remove(index);
|
||||
mIds.remove(index);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private long getAtomicGeneratedId() {
|
||||
return mAtomicLong.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mFragmentList.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mFragmentList.clear();
|
||||
mTitleList.clear();
|
||||
mIds.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return mTitleList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return mIds.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsItem(long itemId) {
|
||||
return mIds.contains(itemId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
|
||||
@Page(name = "分组管理")
|
||||
public class GroupFragment extends BaseFragment {
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_group;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@Override
|
||||
protected void initViews() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.kerwin.wumei.fragment.device;/*
|
||||
|
||||
|
||||
/**
|
||||
* @author xuexiang
|
||||
* @since 2018/12/26 下午11:49
|
||||
*/
|
||||
public enum MultiPage {
|
||||
|
||||
全部(0),
|
||||
浇灌(1),
|
||||
一楼(2),
|
||||
二楼(3),
|
||||
三楼(4),
|
||||
走廊(5);
|
||||
|
||||
private final int position;
|
||||
|
||||
MultiPage(int pos) {
|
||||
position = pos;
|
||||
}
|
||||
|
||||
public static MultiPage getPage(int position) {
|
||||
return MultiPage.values()[position];
|
||||
}
|
||||
|
||||
public static int size() {
|
||||
return MultiPage.values().length;
|
||||
}
|
||||
|
||||
public static String[] getPageNames() {
|
||||
MultiPage[] pages = MultiPage.values();
|
||||
String[] pageNames = new String[pages.length];
|
||||
for (int i = 0; i < pages.length; i++) {
|
||||
pageNames[i] = pages[i].name();
|
||||
}
|
||||
return pageNames;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Build;
|
||||
import android.text.method.HideReturnsTransformationMethod;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.kerwin.wumei.MyApp;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.activity.MainActivity;
|
||||
import com.kerwin.wumei.adapter.entity.EspTouchViewModel;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xpage.enums.CoreAnim;
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar;
|
||||
import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
|
||||
@Page(anim = CoreAnim.none)
|
||||
public class SceneFragment extends BaseFragment {
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
@Override
|
||||
protected TitleBar initTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@Override
|
||||
protected void initViews() { }
|
||||
|
||||
@Override
|
||||
protected void initListeners() { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xpage.enums.CoreAnim;
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar;
|
||||
|
||||
@Page(name = "分享设备")
|
||||
public class ShareDeviceFragment extends BaseFragment {
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_share_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@Override
|
||||
protected void initViews() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.kerwin.wumei.fragment.device;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.text.method.HideReturnsTransformationMethod;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.alibaba.android.vlayout.DelegateAdapter;
|
||||
import com.alibaba.android.vlayout.LayoutHelper;
|
||||
import com.alibaba.android.vlayout.VirtualLayoutManager;
|
||||
import com.alibaba.android.vlayout.layout.GridLayoutHelper;
|
||||
import com.alibaba.android.vlayout.layout.LinearLayoutHelper;
|
||||
import com.alibaba.android.vlayout.layout.StaggeredGridLayoutHelper;
|
||||
import com.alibaba.android.vlayout.layout.StickyLayoutHelper;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.adapter.base.broccoli.BroccoliSimpleDelegateAdapter;
|
||||
import com.kerwin.wumei.adapter.base.delegate.SimpleDelegateAdapter;
|
||||
import com.kerwin.wumei.adapter.base.delegate.SingleDelegateAdapter;
|
||||
import com.kerwin.wumei.adapter.entity.NewInfo;
|
||||
import com.kerwin.wumei.utils.DemoDataProvider;
|
||||
import com.kerwin.wumei.utils.RandomUtils;
|
||||
import com.kerwin.wumei.utils.Utils;
|
||||
import com.kerwin.wumei.utils.XToastUtils;
|
||||
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
|
||||
import com.xuexiang.xrouter.annotation.AutoWired;
|
||||
import com.xuexiang.xrouter.launcher.XRouter;
|
||||
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder;
|
||||
import com.xuexiang.xui.adapter.simple.AdapterItem;
|
||||
import com.xuexiang.xui.widget.banner.widget.banner.SimpleImageBanner;
|
||||
import com.xuexiang.xui.widget.button.SwitchIconView;
|
||||
import com.xuexiang.xui.widget.imageview.ImageLoader;
|
||||
import com.xuexiang.xui.widget.imageview.RadiusImageView;
|
||||
import com.xuexiang.xui.widget.textview.supertextview.SuperButton;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
import me.samlss.broccoli.Broccoli;
|
||||
|
||||
import static com.xuexiang.xui.utils.Utils.getScreenWidth;
|
||||
import static com.xuexiang.xutil.display.DensityUtils.dip2px;
|
||||
|
||||
/**
|
||||
* @author xuexiang
|
||||
* @since 2020/4/21 12:24 AM
|
||||
*/
|
||||
public class SimpleTabFragment extends Fragment {
|
||||
private static final String TAG = "SimpleTabFragment";
|
||||
|
||||
private static final String KEY_TITLE = "title";
|
||||
|
||||
// @BindView(R.id.tv_title)
|
||||
// TextView tvTitle;
|
||||
// @BindView(R.id.tv_explain)
|
||||
// TextView tvExplain;
|
||||
|
||||
@BindView(R.id.recyclerView)
|
||||
RecyclerView recyclerView;
|
||||
@BindView(R.id.refreshLayout)
|
||||
SmartRefreshLayout refreshLayout;
|
||||
// @BindView(R.id.item_card_view)
|
||||
// CardView itemCardView;
|
||||
|
||||
private Unbinder mUnbinder;
|
||||
|
||||
private SimpleDelegateAdapter<NewInfo> mNewsAdapter;
|
||||
|
||||
@AutoWired(name = KEY_TITLE)
|
||||
String title;
|
||||
|
||||
|
||||
public static SimpleTabFragment newInstance(String title) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_TITLE, title);
|
||||
SimpleTabFragment fragment = new SimpleTabFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
Log.e(TAG, "onAttach:" + title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
Log.e(TAG, "onDetach:" + title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Log.e(TAG, "onResume:" + title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
Log.e(TAG, "onStop:" + title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
XRouter.getInstance().inject(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_simple_tab, container, false);
|
||||
mUnbinder = ButterKnife.bind(this, view);
|
||||
initView();
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
// int randomNumber = RandomUtils.getRandom(10, 100);
|
||||
// Log.e(TAG, "initView, random number:" + randomNumber + ", " + title);
|
||||
// tvTitle.setText(String.format("这个是%s页面的内容", title));
|
||||
// tvExplain.setText(String.format("这个是页面随机生成的数字:%d", randomNumber));
|
||||
|
||||
|
||||
VirtualLayoutManager virtualLayoutManager = new VirtualLayoutManager(getContext());
|
||||
recyclerView.setLayoutManager(virtualLayoutManager);
|
||||
RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
|
||||
recyclerView.setRecycledViewPool(viewPool);
|
||||
viewPool.setMaxRecycledViews(0, 10);
|
||||
|
||||
//顶部按钮
|
||||
// SingleDelegateAdapter buttonAdapter = new SingleDelegateAdapter(R.layout.adapter_button_top_item) {
|
||||
// @Override
|
||||
// public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
|
||||
// SuperButton superButton = holder.findViewById(R.id.device_item_all_open);
|
||||
// }
|
||||
// };
|
||||
|
||||
// 设备
|
||||
FragmentActivity activity=this.getActivity();
|
||||
mNewsAdapter = new BroccoliSimpleDelegateAdapter<NewInfo>(R.layout.adapter_device_card_view_list_item, new StaggeredGridLayoutHelper(2,0), DemoDataProvider.getEmptyNewInfo()) {
|
||||
@Override
|
||||
protected void onBindData(RecyclerViewHolder holder, NewInfo model, int position) {
|
||||
|
||||
//设置item宽度,适配屏幕分辨率
|
||||
// CardView view=holder.findViewById(R.id.device_item_card_view);
|
||||
// int widthPixels = getScreenWidth(activity);
|
||||
// int space=dip2px(40); //间隙=左边距+右边距+中间间隔
|
||||
// ViewGroup.LayoutParams cardViewParams=view.getLayoutParams();
|
||||
// cardViewParams.width=(widthPixels-space)/2;
|
||||
|
||||
//设置开关按钮
|
||||
SwitchIconView switchIconView=holder.findViewById(R.id.device_item_switch_button);
|
||||
holder.click(R.id.device_item_switch_button, v -> {
|
||||
Vibrator vibrator = (Vibrator) activity.getSystemService(activity.VIBRATOR_SERVICE);
|
||||
vibrator.vibrate(100);
|
||||
switchIconView.switchState();
|
||||
});
|
||||
|
||||
AppCompatImageView stateView=holder.findViewById(R.id.device_item_state_icon);
|
||||
stateView.setImageDrawable(getResources().getDrawable((R.drawable.state_a)));
|
||||
|
||||
|
||||
if (model != null) {
|
||||
// holder.text(R.id.tv_user_name, model.getUserName());
|
||||
// holder.text(R.id.tv_tag, model.getTag());
|
||||
// holder.text(R.id.tv_title, model.getTitle());
|
||||
// holder.text(R.id.tv_summary, model.getSummary());
|
||||
// holder.text(R.id.tv_praise, model.getPraise() == 0 ? "点赞" : String.valueOf(model.getPraise()));
|
||||
// holder.text(R.id.tv_comment, model.getComment() == 0 ? "评论" : String.valueOf(model.getComment()));
|
||||
// holder.text(R.id.tv_read, "阅读量 " + model.getRead());
|
||||
// holder.image(R.id.iv_image, model.getImageUrl());
|
||||
//
|
||||
// holder.click(R.id.card_view, v -> Utils.goWeb(getContext(), model.getDetailUrl()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindBroccoli(RecyclerViewHolder holder, Broccoli broccoli) {
|
||||
// broccoli.addPlaceholders(
|
||||
// holder.findView(R.id.device_item_title),
|
||||
// holder.findView(R.id.device_item_title_icon),
|
||||
// holder.findView(R.id.device_item_time),
|
||||
// holder.findView(R.id.device_item_time_icon),
|
||||
// holder.findView(R.id.device_item_temperature),
|
||||
// holder.findView(R.id.device_item_humidity),
|
||||
// holder.findView(R.id.device_item_wifi),
|
||||
// holder.findView(R.id.device_item_wifi_icon),
|
||||
// holder.findView(R.id.device_item_state),
|
||||
// holder.findView(R.id.device_item_state_icon),
|
||||
// holder.findView(R.id.device_item_switch_button)
|
||||
// );
|
||||
}
|
||||
};
|
||||
|
||||
DelegateAdapter delegateAdapter = new DelegateAdapter(virtualLayoutManager);
|
||||
delegateAdapter.addAdapter(mNewsAdapter);
|
||||
recyclerView.setAdapter(delegateAdapter);
|
||||
|
||||
|
||||
//下拉刷新
|
||||
refreshLayout.setOnRefreshListener(refreshLayout -> {
|
||||
// TODO: 2020-02-25 这里只是模拟了网络请求
|
||||
refreshLayout.getLayout().postDelayed(() -> {
|
||||
mNewsAdapter.refresh(DemoDataProvider.getDemoNewInfos());
|
||||
refreshLayout.finishRefresh();
|
||||
}, 1000);
|
||||
});
|
||||
//上拉加载
|
||||
refreshLayout.setOnLoadMoreListener(refreshLayout -> {
|
||||
// TODO: 2020-02-25 这里只是模拟了网络请求
|
||||
refreshLayout.getLayout().postDelayed(() -> {
|
||||
mNewsAdapter.loadMore(DemoDataProvider.getDemoNewInfos());
|
||||
refreshLayout.finishLoadMore();
|
||||
}, 1000);
|
||||
});
|
||||
refreshLayout.autoRefresh();//第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (mUnbinder != null) {
|
||||
mUnbinder.unbind();
|
||||
}
|
||||
super.onDestroyView();
|
||||
Log.e(TAG, "onDestroyView:" + title);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package com.kerwin.wumei.fragment.news;
|
||||
|
||||
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.kerwin.wumei.adapter.base.broccoli.BroccoliSimpleDelegateAdapter;
|
||||
import com.kerwin.wumei.adapter.base.delegate.SimpleDelegateAdapter;
|
||||
import com.kerwin.wumei.adapter.base.delegate.SingleDelegateAdapter;
|
||||
import com.kerwin.wumei.adapter.entity.NewInfo;
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.utils.DemoDataProvider;
|
||||
import com.kerwin.wumei.utils.Utils;
|
||||
import com.kerwin.wumei.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.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 butterknife.BindView;
|
||||
import me.samlss.broccoli.Broccoli;
|
||||
|
||||
@Page(anim = CoreAnim.none)
|
||||
public class NewsFragment extends BaseFragment {
|
||||
|
||||
@BindView(R.id.recyclerView)
|
||||
RecyclerView recyclerView;
|
||||
@BindView(R.id.refreshLayout)
|
||||
SmartRefreshLayout refreshLayout;
|
||||
|
||||
private SimpleDelegateAdapter<NewInfo> mNewsAdapter;
|
||||
|
||||
/**
|
||||
* @return 返回为 null意为不需要导航栏
|
||||
*/
|
||||
@Override
|
||||
protected TitleBar initTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 布局的资源id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_news;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
@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);
|
||||
|
||||
//轮播条
|
||||
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);
|
||||
SimpleDelegateAdapter<AdapterItem> commonAdapter = new SimpleDelegateAdapter<AdapterItem>(R.layout.adapter_common_grid_item, gridLayoutHelper, DemoDataProvider.getGridItems(getContext())) {
|
||||
@Override
|
||||
protected void bindData(@NonNull RecyclerViewHolder holder, int position, AdapterItem item) {
|
||||
if (item != null) {
|
||||
RadiusImageView imageView = holder.findViewById(R.id.riv_item);
|
||||
imageView.setCircle(true);
|
||||
ImageLoader.get().loadImage(imageView, item.getIcon());
|
||||
holder.text(R.id.device_item_title, item.getTitle().toString().substring(0, 1));
|
||||
holder.text(R.id.tv_sub_title, item.getTitle());
|
||||
|
||||
holder.click(R.id.ll_container, v -> XToastUtils.toast("点击了:" + item.getTitle()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//动态的标题
|
||||
SingleDelegateAdapter titleAdapter = new SingleDelegateAdapter(R.layout.adapter_title_item) {
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
|
||||
holder.text(R.id.device_item_title, "动态");
|
||||
holder.text(R.id.tv_action, "更多");
|
||||
holder.click(R.id.tv_action, v -> XToastUtils.toast("更多"));
|
||||
}
|
||||
};
|
||||
|
||||
// 动态
|
||||
mNewsAdapter = new BroccoliSimpleDelegateAdapter<NewInfo>(R.layout.adapter_news_card_view_list_item, new LinearLayoutHelper(), DemoDataProvider.getEmptyNewInfo()) {
|
||||
@Override
|
||||
protected void onBindData(RecyclerViewHolder holder, NewInfo model, int position) {
|
||||
if (model != null) {
|
||||
holder.text(R.id.tv_user_name, model.getUserName());
|
||||
holder.text(R.id.tv_tag, model.getTag());
|
||||
holder.text(R.id.device_item_title, model.getTitle());
|
||||
holder.text(R.id.tv_summary, model.getSummary());
|
||||
holder.text(R.id.tv_praise, model.getPraise() == 0 ? "点赞" : String.valueOf(model.getPraise()));
|
||||
holder.text(R.id.tv_comment, model.getComment() == 0 ? "评论" : String.valueOf(model.getComment()));
|
||||
holder.text(R.id.tv_read, "阅读量 " + model.getRead());
|
||||
holder.image(R.id.iv_image, model.getImageUrl());
|
||||
|
||||
holder.click(R.id.card_view, v -> Utils.goWeb(getContext(), model.getDetailUrl()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindBroccoli(RecyclerViewHolder holder, Broccoli broccoli) {
|
||||
broccoli.addPlaceholders(
|
||||
holder.findView(R.id.tv_user_name),
|
||||
holder.findView(R.id.tv_tag),
|
||||
holder.findView(R.id.device_item_title),
|
||||
holder.findView(R.id.tv_summary),
|
||||
holder.findView(R.id.tv_praise),
|
||||
holder.findView(R.id.tv_comment),
|
||||
holder.findView(R.id.tv_read),
|
||||
holder.findView(R.id.iv_image)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
DelegateAdapter delegateAdapter = new DelegateAdapter(virtualLayoutManager);
|
||||
delegateAdapter.addAdapter(bannerAdapter);
|
||||
delegateAdapter.addAdapter(commonAdapter);
|
||||
delegateAdapter.addAdapter(titleAdapter);
|
||||
delegateAdapter.addAdapter(mNewsAdapter);
|
||||
|
||||
recyclerView.setAdapter(delegateAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initListeners() {
|
||||
//下拉刷新
|
||||
refreshLayout.setOnRefreshListener(refreshLayout -> {
|
||||
// TODO: 2020-02-25 这里只是模拟了网络请求
|
||||
refreshLayout.getLayout().postDelayed(() -> {
|
||||
mNewsAdapter.refresh(DemoDataProvider.getDemoNewInfos());
|
||||
refreshLayout.finishRefresh();
|
||||
}, 1000);
|
||||
});
|
||||
//上拉加载
|
||||
refreshLayout.setOnLoadMoreListener(refreshLayout -> {
|
||||
// TODO: 2020-02-25 这里只是模拟了网络请求
|
||||
refreshLayout.getLayout().postDelayed(() -> {
|
||||
mNewsAdapter.loadMore(DemoDataProvider.getDemoNewInfos());
|
||||
refreshLayout.finishLoadMore();
|
||||
}, 1000);
|
||||
});
|
||||
refreshLayout.autoRefresh();//第一次进入触发自动刷新,演示效果
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.kerwin.wumei.fragment.profile;
|
||||
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
|
||||
import com.kerwin.wumei.core.BaseFragment;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.kerwin.wumei.fragment.AboutFragment;
|
||||
import com.kerwin.wumei.fragment.FeedbackFragment;
|
||||
import com.kerwin.wumei.fragment.MessageFragment;
|
||||
import com.kerwin.wumei.fragment.SettingsFragment;
|
||||
import com.xuexiang.xaop.annotation.SingleClick;
|
||||
import com.xuexiang.xpage.annotation.Page;
|
||||
import com.xuexiang.xpage.enums.CoreAnim;
|
||||
import com.xuexiang.xpage.utils.Utils;
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar;
|
||||
import com.xuexiang.xui.widget.imageview.RadiusImageView;
|
||||
import com.xuexiang.xui.widget.textview.supertextview.SuperTextView;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
@Page(anim = CoreAnim.none)
|
||||
public class ProfileFragment extends BaseFragment implements SuperTextView.OnSuperTextViewClickListener {
|
||||
@BindView(R.id.riv_head_pic)
|
||||
RadiusImageView rivHeadPic;
|
||||
@BindView(R.id.menu_settings)
|
||||
SuperTextView menuSettings;
|
||||
@BindView(R.id.menu_about)
|
||||
SuperTextView menuAbout;
|
||||
@BindView(R.id.menu_feedback)
|
||||
SuperTextView menuFeedback;
|
||||
@BindView(R.id.menu_message)
|
||||
SuperTextView menuMessage;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
menuFeedback.setOnSuperTextViewClickListener(this);
|
||||
menuMessage.setOnSuperTextViewClickListener(this);
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
@Override
|
||||
public void onClick(SuperTextView view) {
|
||||
switch(view.getId()) {
|
||||
case R.id.menu_settings:
|
||||
openNewPage(SettingsFragment.class);
|
||||
break;
|
||||
case R.id.menu_about:
|
||||
openNewPage(AboutFragment.class);
|
||||
break;
|
||||
case R.id.menu_message:
|
||||
openNewPage(MessageFragment.class);
|
||||
break;
|
||||
case R.id.menu_feedback:
|
||||
openNewPage(FeedbackFragment.class);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.kerwin.wumei.adapter.entity.NewInfo;
|
||||
import com.kerwin.wumei.R;
|
||||
import com.xuexiang.xaop.annotation.MemoryCache;
|
||||
import com.xuexiang.xui.adapter.simple.AdapterItem;
|
||||
import com.xuexiang.xui.utils.ResUtils;
|
||||
import com.xuexiang.xui.widget.banner.widget.banner.BannerItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 演示数据
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2018/11/23 下午5:52
|
||||
*/
|
||||
public class DemoDataProvider {
|
||||
|
||||
public static String[] titles = new String[]{
|
||||
"伪装者:胡歌演绎'痞子特工'",
|
||||
"无心法师:生死离别!月牙遭虐杀",
|
||||
"花千骨:尊上沦为花千骨",
|
||||
"综艺饭:胖轩偷看夏天洗澡掀波澜",
|
||||
"碟中谍4:阿汤哥高塔命悬一线,超越不可能",
|
||||
};
|
||||
|
||||
public static String[] urls = new String[]{//640*360 360/640=0.5625
|
||||
"http://photocdn.sohu.com/tvmobilemvms/20150907/144160323071011277.jpg",//伪装者:胡歌演绎"痞子特工"
|
||||
"http://photocdn.sohu.com/tvmobilemvms/20150907/144158380433341332.jpg",//无心法师:生死离别!月牙遭虐杀
|
||||
"http://photocdn.sohu.com/tvmobilemvms/20150907/144160286644953923.jpg",//花千骨:尊上沦为花千骨
|
||||
"http://photocdn.sohu.com/tvmobilemvms/20150902/144115156939164801.jpg",//综艺饭:胖轩偷看夏天洗澡掀波澜
|
||||
"http://photocdn.sohu.com/tvmobilemvms/20150907/144159406950245847.jpg",//碟中谍4:阿汤哥高塔命悬一线,超越不可能
|
||||
};
|
||||
|
||||
@MemoryCache
|
||||
public static List<BannerItem> getBannerList() {
|
||||
List<BannerItem> list = new ArrayList<>();
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
BannerItem item = new BannerItem();
|
||||
item.imgUrl = urls[i];
|
||||
item.title = titles[i];
|
||||
|
||||
list.add(item);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于占位的空信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@MemoryCache
|
||||
public static List<NewInfo> getDemoNewInfos() {
|
||||
List<NewInfo> list = new ArrayList<>();
|
||||
list.add(new NewInfo("源码", "Android源码分析--Android系统启动")
|
||||
.setSummary("其实Android系统的启动最主要的内容无非是init、Zygote、SystemServer这三个进程的启动,他们一起构成的铁三角是Android系统的基础。")
|
||||
.setDetailUrl("https://juejin.im/post/5c6fc0cdf265da2dda694f05")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2019/2/22/16914891cd8a950a?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
|
||||
list.add(new NewInfo("Android UI", "XUI 一个简洁而优雅的Android原生UI框架,解放你的双手")
|
||||
.setSummary("涵盖绝大部分的UI组件:TextView、Button、EditText、ImageView、Spinner、Picker、Dialog、PopupWindow、ProgressBar、LoadingView、StateLayout、FlowLayout、Switch、Actionbar、TabBar、Banner、GuideView、BadgeView、MarqueeView、WebView、SearchView等一系列的组件和丰富多彩的样式主题。\n")
|
||||
.setDetailUrl("https://juejin.im/post/5c3ed1dae51d4543805ea48d")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2019/1/16/1685563ae5456408?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
|
||||
list.add(new NewInfo("面试", "写给即将面试的你")
|
||||
.setSummary("最近由于公司业务发展,需要招聘技术方面的人才,由于我在技术方面比较熟悉,技术面的任务就交给我了。今天我要分享的就和面试有关,主要包含技术面的流程、经验和建议,避免大家在今后的面试过程中走一些弯路,帮助即将需要跳槽面试的人。")
|
||||
.setDetailUrl("https://juejin.im/post/5ca4df966fb9a05e4e58320c")
|
||||
.setImageUrl("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554629219186&di=6cdab5cfceaae1f7e6d78dbe79104c9f&imgtype=0&src=http%3A%2F%2Fimg.qinxue365.com%2Fuploads%2Fallimg%2F1902%2F4158-1Z22FZ64E00.jpg"));
|
||||
|
||||
list.add(new NewInfo("Android", "XUpdate 一个轻量级、高可用性的Android版本更新框架")
|
||||
.setSummary("XUpdate 一个轻量级、高可用性的Android版本更新框架。本框架借鉴了AppUpdate中的部分思想和UI界面,将版本更新中的各部分环节抽离出来,形成了如下几个部分:")
|
||||
.setDetailUrl("https://juejin.im/post/5b480b79e51d45190905ef44")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2018/7/13/16492d9b7877dc21?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
|
||||
list.add(new NewInfo("Android/HTTP", "XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装")
|
||||
.setSummary("一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击使用说明文档,体验一下吧!")
|
||||
.setDetailUrl("https://juejin.im/post/5b6b9b49e51d4576b828978d")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2018/8/9/1651c568a7e30e02?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
list.add(new NewInfo("Android/HTTP", "XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装")
|
||||
.setSummary("一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击使用说明文档,体验一下吧!")
|
||||
.setDetailUrl("https://juejin.im/post/5b6b9b49e51d4576b828978d")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2018/8/9/1651c568a7e30e02?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
list.add(new NewInfo("Android/HTTP", "XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装")
|
||||
.setSummary("一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击使用说明文档,体验一下吧!")
|
||||
.setDetailUrl("https://juejin.im/post/5b6b9b49e51d4576b828978d")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2018/8/9/1651c568a7e30e02?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
list.add(new NewInfo("Android/HTTP", "XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装")
|
||||
.setSummary("一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击使用说明文档,体验一下吧!")
|
||||
.setDetailUrl("https://juejin.im/post/5b6b9b49e51d4576b828978d")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2018/8/9/1651c568a7e30e02?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
list.add(new NewInfo("Android/HTTP", "XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装")
|
||||
.setSummary("一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击使用说明文档,体验一下吧!")
|
||||
.setDetailUrl("https://juejin.im/post/5b6b9b49e51d4576b828978d")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2018/8/9/1651c568a7e30e02?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
list.add(new NewInfo("Android/HTTP", "XHttp2 一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp进行组装")
|
||||
.setSummary("一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击使用说明文档,体验一下吧!")
|
||||
.setDetailUrl("https://juejin.im/post/5b6b9b49e51d4576b828978d")
|
||||
.setImageUrl("https://user-gold-cdn.xitu.io/2018/8/9/1651c568a7e30e02?imageView2/0/w/1280/h/960/format/webp/ignore-error/1"));
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<AdapterItem> getGridItems(Context context) {
|
||||
return getGridItems(context, R.array.grid_titles_entry, R.array.grid_icons_entry);
|
||||
}
|
||||
|
||||
|
||||
private static List<AdapterItem> getGridItems(Context context, int titleArrayId, int iconArrayId) {
|
||||
List<AdapterItem> list = new ArrayList<>();
|
||||
String[] titles = ResUtils.getStringArray(titleArrayId);
|
||||
Drawable[] icons = ResUtils.getDrawableArray(context, iconArrayId);
|
||||
for (int i = 0; i < titles.length; i++) {
|
||||
list.add(new AdapterItem(titles[i], icons[i]));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于占位的空信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@MemoryCache
|
||||
public static List<NewInfo> getEmptyNewInfo() {
|
||||
List<NewInfo> list = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
list.add(new NewInfo());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
270
android/app/src/main/java/com/kerwin/wumei/utils/MMKVUtils.java
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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<String>) 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<String>) 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 <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T extends Parcelable> T getObject(String key, Class<T> tClass) {
|
||||
return getsMMKV().decodeParcelable(key, tClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param key
|
||||
* @param tClass 类型
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T extends Parcelable> T getObject(String key, Class<T> 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();
|
||||
}
|
||||
|
||||
}
|
||||
147
android/app/src/main/java/com/kerwin/wumei/utils/NetUtils.java
Normal file
@@ -0,0 +1,147 @@
|
||||
package com.kerwin.wumei.utils;
|
||||
|
||||
import android.net.DhcpInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class NetUtils {
|
||||
public static boolean isWifiConnected(WifiManager wifiManager) {
|
||||
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
||||
return wifiInfo != null
|
||||
&& wifiInfo.getNetworkId() != -1
|
||||
&& !"<unknown ssid>".equals(wifiInfo.getSSID());
|
||||
}
|
||||
|
||||
public static byte[] getRawSsidBytes(WifiInfo info) {
|
||||
try {
|
||||
Method method = info.getClass().getMethod("getWifiSsid");
|
||||
method.setAccessible(true);
|
||||
Object wifiSsid = method.invoke(info);
|
||||
if (wifiSsid == null) {
|
||||
return null;
|
||||
}
|
||||
method = wifiSsid.getClass().getMethod("getOctets");
|
||||
method.setAccessible(true);
|
||||
return (byte[]) method.invoke(wifiSsid);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] getRawSsidBytesOrElse(WifiInfo info, byte[] orElse) {
|
||||
byte[] raw = getRawSsidBytes(info);
|
||||
return raw != null ? raw : orElse;
|
||||
}
|
||||
|
||||
public static String getSsidString(WifiInfo info) {
|
||||
String ssid = info.getSSID();
|
||||
if (ssid.startsWith("\"") && ssid.endsWith("\"")) {
|
||||
ssid = ssid.substring(1, ssid.length() - 1);
|
||||
}
|
||||
return ssid;
|
||||
}
|
||||
|
||||
public static InetAddress getBroadcastAddress(WifiManager wifi) {
|
||||
DhcpInfo dhcp = wifi.getDhcpInfo();
|
||||
if (dhcp != null) {
|
||||
int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
|
||||
byte[] quads = new byte[4];
|
||||
for (int k = 0; k < 4; k++) {
|
||||
quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
|
||||
}
|
||||
try {
|
||||
return InetAddress.getByAddress(quads);
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return InetAddress.getByName("255.255.255.255");
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// Impossible arrive here
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean is5G(int frequency) {
|
||||
return frequency > 4900 && frequency < 5900;
|
||||
}
|
||||
|
||||
public static InetAddress getAddress(int ipAddress) {
|
||||
byte[] ip = new byte[]{
|
||||
(byte) (ipAddress & 0xff),
|
||||
(byte) ((ipAddress >> 8) & 0xff),
|
||||
(byte) ((ipAddress >> 16) & 0xff),
|
||||
(byte) ((ipAddress >> 24) & 0xff)
|
||||
};
|
||||
|
||||
try {
|
||||
return InetAddress.getByAddress(ip);
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
// Impossible arrive here
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static InetAddress getAddress(boolean isIPv4) {
|
||||
try {
|
||||
Enumeration<NetworkInterface> enums = NetworkInterface.getNetworkInterfaces();
|
||||
while (enums.hasMoreElements()) {
|
||||
NetworkInterface ni = enums.nextElement();
|
||||
Enumeration<InetAddress> addrs = ni.getInetAddresses();
|
||||
while (addrs.hasMoreElements()) {
|
||||
InetAddress address = addrs.nextElement();
|
||||
if (!address.isLoopbackAddress()) {
|
||||
if (isIPv4 && address instanceof Inet4Address) {
|
||||
return address;
|
||||
}
|
||||
if (!isIPv4 && address instanceof Inet6Address) {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static InetAddress getIPv4Address() {
|
||||
return getAddress(true);
|
||||
}
|
||||
|
||||
public static InetAddress getIPv6Address() {
|
||||
return getAddress(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bssid the bssid like aa:bb:cc:dd:ee:ff
|
||||
* @return byte array converted from bssid
|
||||
*/
|
||||
public static byte[] convertBssid2Bytes(String bssid) {
|
||||
String[] bssidSplits = bssid.split(":");
|
||||
if (bssidSplits.length != 6) {
|
||||
throw new IllegalArgumentException("Invalid bssid format");
|
||||
}
|
||||
byte[] result = new byte[bssidSplits.length];
|
||||
for (int i = 0; i < bssidSplits.length; i++) {
|
||||
result[i] = (byte) Integer.parseInt(bssidSplits[i], 16);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* desc : Random Utils
|
||||
* author : xuexiang
|
||||
* time : 2018/4/28 上午12:41
|
||||
* </pre>
|
||||
* <ul>
|
||||
* Shuffling algorithm
|
||||
* <li>{@link #shuffle(Object[])} Shuffling algorithm, Randomly permutes the specified array using a default source of
|
||||
* randomness</li>
|
||||
* <li>{@link #shuffle(Object[], int)} Shuffling algorithm, Randomly permutes the specified array</li>
|
||||
* <li>{@link #shuffle(int[])} Shuffling algorithm, Randomly permutes the specified int array using a default source of
|
||||
* randomness</li>
|
||||
* <li>{@link #shuffle(int[], int)} Shuffling algorithm, Randomly permutes the specified int array</li>
|
||||
* </ul>
|
||||
* <ul>
|
||||
* get random int
|
||||
* <li>{@link #getRandom(int)} get random int between 0 and max</li>
|
||||
* <li>{@link #getRandom(int, int)} get random int between min and max</li>
|
||||
* </ul>
|
||||
* <ul>
|
||||
* get random numbers or letters
|
||||
* <li>{@link #getRandomCapitalLetters(int)} get a fixed-length random string, its a mixture of uppercase letters</li>
|
||||
* <li>{@link #getRandomLetters(int)} get a fixed-length random string, its a mixture of uppercase and lowercase letters
|
||||
* </li>
|
||||
* <li>{@link #getRandomLowerCaseLetters(int)} get a fixed-length random string, its a mixture of lowercase letters</li>
|
||||
* <li>{@link #getRandomNumbers(int)} get a fixed-length random string, its a mixture of numbers</li>
|
||||
* <li>{@link #getRandomNumbersAndLetters(int)} get a fixed-length random string, its a mixture of uppercase, lowercase
|
||||
* letters and numbers</li>
|
||||
* <li>{@link #getRandom(String, int)} get a fixed-length random string, its a mixture of chars in source</li>
|
||||
* <li>{@link #getRandom(char[], int)} get a fixed-length random string, its a mixture of chars in sourceChar</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
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 <ul>
|
||||
* <li>if source is null or empty, return null</li>
|
||||
* <li>else see {@link RandomUtils#getRandom(char[] sourceChar, int length)}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static String getRandom(String source, int length) {
|
||||
return TextUtils.isEmpty(source) ? null : getRandom(source.toCharArray(), length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在一个字符数组源中获取一个定长的随机字符串
|
||||
*
|
||||
* @param sourceChar 字符数组源
|
||||
* @param length 长度
|
||||
* @return <ul>
|
||||
* <li>if sourceChar is null or empty, return null</li>
|
||||
* <li>if length less than 0, return null</li>
|
||||
* </ul>
|
||||
*/
|
||||
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 <ul>
|
||||
* <li>if max <= 0, return 0</li>
|
||||
* <li>else return random int between 0 and max</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static int getRandom(int max) {
|
||||
return getRandom(0, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* get random int between min and max
|
||||
*
|
||||
* @param min 最小随机数
|
||||
* @param max 最大随机数
|
||||
* @return <ul>
|
||||
* <li>if min > max, return 0</li>
|
||||
* <li>if min == max, return min</li>
|
||||
* <li>else return random int between min and max</li>
|
||||
* </ul>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
100
android/app/src/main/java/com/kerwin/wumei/utils/TokenUtils.java
Normal file
@@ -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.kerwin.wumei.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.kerwin.wumei.activity.LoginActivity;
|
||||
import com.umeng.analytics.MobclickAgent;
|
||||
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.xuexiang.templateproject.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("登录成功!");
|
||||
MobclickAgent.onProfileSignIn(KEY_PROFILE_CHANNEL, token);
|
||||
setToken(token);
|
||||
return true;
|
||||
} else {
|
||||
XToastUtils.error("登录失败!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登出的事件
|
||||
*/
|
||||
public static void handleLogoutSuccess() {
|
||||
MobclickAgent.onProfileSignOff();
|
||||
//登出时,清除账号信息
|
||||
clearToken();
|
||||
XToastUtils.success("登出成功!");
|
||||
//跳转到登录页
|
||||
ActivityUtils.startActivity(LoginActivity.class);
|
||||
}
|
||||
|
||||
}
|
||||
175
android/app/src/main/java/com/kerwin/wumei/utils/Utils.java
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.kerwin.wumei.core.webview.AgentWebActivity;
|
||||
import com.kerwin.wumei.core.webview.AgentWebFragment;
|
||||
import com.kerwin.wumei.R;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
*
|
||||
* @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(AgentWebFragment.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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils.sdkinit;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import com.kerwin.wumei.BuildConfig;
|
||||
import com.kerwin.wumei.MyApp;
|
||||
import com.meituan.android.walle.WalleChannelReader;
|
||||
import com.umeng.analytics.MobclickAgent;
|
||||
import com.umeng.commonsdk.UMConfigure;
|
||||
|
||||
/**
|
||||
* UMeng 统计 SDK初始化
|
||||
*
|
||||
* @author xuexiang
|
||||
* @since 2019-06-18 15:49
|
||||
*/
|
||||
public final class UMengInit {
|
||||
|
||||
private UMengInit() {
|
||||
throw new UnsupportedOperationException("u can't instantiate me...");
|
||||
}
|
||||
|
||||
private static String DEFAULT_CHANNEL_ID = "github";
|
||||
|
||||
/**
|
||||
* 初始化UmengSDK
|
||||
*/
|
||||
public static void init(Application application) {
|
||||
//设置LOG开关,默认为false
|
||||
UMConfigure.setLogEnabled(MyApp.isDebug());
|
||||
//初始化组件化基础库, 注意: 即使您已经在AndroidManifest.xml中配置过appkey和channel值,也需要在App代码中调用初始化接口(如需要使用AndroidManifest.xml中配置好的appkey和channel值,UMConfigure.init调用中appkey和channel参数请置为null)。
|
||||
//第二个参数是appkey,最后一个参数是pushSecret
|
||||
//这里BuildConfig.APP_ID_UMENG是根据local.properties中定义的APP_ID_UMENG生成的,只是运行看效果的话,可以不初始化该SDK
|
||||
UMConfigure.init(application, BuildConfig.APP_ID_UMENG, getChannel(application), UMConfigure.DEVICE_TYPE_PHONE, "");
|
||||
//统计SDK是否支持采集在子进程中打点的自定义事件,默认不支持
|
||||
//支持多进程打点
|
||||
UMConfigure.setProcessEvent(true);
|
||||
MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取渠道信息
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static String getChannel(final Context context) {
|
||||
return WalleChannelReader.getChannel(context, DEFAULT_CHANNEL_ID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils.sdkinit;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.kerwin.wumei.MyApp;
|
||||
import com.kerwin.wumei.core.BaseActivity;
|
||||
import com.kerwin.wumei.utils.TokenUtils;
|
||||
import com.kerwin.wumei.utils.XToastUtils;
|
||||
import com.xuexiang.xaop.XAOP;
|
||||
import com.xuexiang.xhttp2.XHttpSDK;
|
||||
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()
|
||||
.debug(MyApp.isDebug() ? "PageLog" : null)
|
||||
.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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils.sdkinit;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import com.kerwin.wumei.MyApp;
|
||||
import com.kerwin.wumei.utils.update.CustomUpdateDownloader;
|
||||
import com.kerwin.wumei.utils.update.CustomUpdateFailureListener;
|
||||
import com.kerwin.wumei.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 = "";
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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> T parseObject(String input, Type clazz) {
|
||||
return JsonUtil.fromJson(input, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 进程初始化的方法
|
||||
*
|
||||
* @param context 上下文
|
||||
*/
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils.update;
|
||||
|
||||
import com.kerwin.wumei.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被墙无法下载,是否考虑切换蒲公英下载?");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils.update;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.xuexiang.xui.widget.dialog.DialogLoader;
|
||||
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.xuexiang.templateproject.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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.utils.update;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.kerwin.wumei.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<String, Object> params, @NonNull final IUpdateHttpService.Callback callBack) {
|
||||
XHttp.get(url)
|
||||
.params(params)
|
||||
.keepJson(true)
|
||||
.execute(new SimpleCallBack<String>() {
|
||||
@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<String, Object> params, @NonNull final IUpdateHttpService.Callback callBack) {
|
||||
XHttp.post(url)
|
||||
.upJson(JsonUtil.toJson(params))
|
||||
.keepJson(true)
|
||||
.execute(new SimpleCallBack<String>() {
|
||||
@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<String>() {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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.kerwin.wumei.core.http.api.ApiService;
|
||||
import com.kerwin.wumei.core.http.callback.NoTipCallBack;
|
||||
import com.kerwin.wumei.core.http.entity.TipInfo;
|
||||
import com.kerwin.wumei.utils.MMKVUtils;
|
||||
import com.xuexiang.constant.TimeConstants;
|
||||
import com.kerwin.wumei.R;
|
||||
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.xuexiang.templateproject.widget.key_is_ignore_tips_";
|
||||
|
||||
private List<TipInfo> 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<List<TipInfo>>() {
|
||||
@Override
|
||||
public void onSuccess(List<TipInfo> response) throws Throwable {
|
||||
if (response != null && response.size() > 0) {
|
||||
new GuideTipsDialog(context, response).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public GuideTipsDialog(Context context, @NonNull List<TipInfo> tips) {
|
||||
super(context, R.layout.dialog_guide_tips);
|
||||
initViews();
|
||||
updateTips(tips);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化弹窗
|
||||
*/
|
||||
private void initViews() {
|
||||
mTvTitle = findViewById(R.id.device_item_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<TipInfo> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.kerwin.wumei.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:color="?attr/colorAccent" />
|
||||
<item android:state_selected="true" android:color="?attr/colorAccent" />
|
||||
<item android:state_enabled="false" android:color="@color/xui_btn_disable_color" />
|
||||
<item android:color="?attr/colorAccent" />
|
||||
</selector>
|
||||
22
android/app/src/main/res/color/selector_tab_text_color.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:color="?attr/colorAccent" />
|
||||
<item android:state_selected="true" android:color="?attr/colorAccent" />
|
||||
<item android:color="@color/xui_config_color_gray_6" />
|
||||
</selector>
|
||||
BIN
android/app/src/main/res/drawable-hdpi/about.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
android/app/src/main/res/drawable-hdpi/add.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
android/app/src/main/res/drawable-hdpi/add_device.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
android/app/src/main/res/drawable-hdpi/add_line.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
android/app/src/main/res/drawable-hdpi/add_white.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
android/app/src/main/res/drawable-hdpi/begin.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
android/app/src/main/res/drawable-hdpi/category.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
android/app/src/main/res/drawable-hdpi/device.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
android/app/src/main/res/drawable-hdpi/down.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |