├── .gitignore ├── .idea └── copyright │ ├── profiles_settings.xml │ └── xuexiang.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── channel ├── debug.jks ├── multiple-channel.gradle ├── proguard-rules.pro ├── src │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── xuexiang │ │ │ └── templateandserver │ │ │ └── ExampleInstrumentedTest.java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ └── web │ │ │ │ ├── css │ │ │ │ └── login.css │ │ │ │ ├── image │ │ │ │ └── logo.png │ │ │ │ ├── index.html │ │ │ │ ├── login.html │ │ │ │ └── register.html │ │ ├── java │ │ │ └── com │ │ │ │ └── xuexiang │ │ │ │ ├── server │ │ │ │ ├── CoreService.java │ │ │ │ ├── OnServerStatusListener.java │ │ │ │ ├── ServerConfig.java │ │ │ │ ├── ServerManager.java │ │ │ │ ├── api │ │ │ │ │ ├── base │ │ │ │ │ │ ├── ApiException.java │ │ │ │ │ │ └── ApiResult.java │ │ │ │ │ ├── request │ │ │ │ │ │ └── PageQuery.java │ │ │ │ │ └── response │ │ │ │ │ │ └── LoginInfo.java │ │ │ │ ├── component │ │ │ │ │ ├── AppConfig.java │ │ │ │ │ ├── AppExceptionResolver.java │ │ │ │ │ ├── AppMessageConverter.java │ │ │ │ │ ├── LoggerInterceptor.java │ │ │ │ │ └── LoginInterceptor.java │ │ │ │ ├── controller │ │ │ │ │ ├── FileController.java │ │ │ │ │ ├── PageController.java │ │ │ │ │ └── UserController.java │ │ │ │ ├── model │ │ │ │ │ └── User.java │ │ │ │ ├── service │ │ │ │ │ ├── UserService.java │ │ │ │ │ └── impl │ │ │ │ │ │ └── UserServiceImpl.java │ │ │ │ └── utils │ │ │ │ │ ├── ApiUtils.java │ │ │ │ │ ├── StorageUtils.java │ │ │ │ │ └── TokenUtils.java │ │ │ │ └── templateandserver │ │ │ │ ├── MyApp.java │ │ │ │ ├── activity │ │ │ │ └── MainActivity.java │ │ │ │ ├── adapter │ │ │ │ └── UserManageAdapter.java │ │ │ │ ├── core │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── BaseContainerFragment.java │ │ │ │ ├── BaseFragment.java │ │ │ │ ├── BaseSimpleListFragment.java │ │ │ │ ├── SimpleListAdapter.java │ │ │ │ └── http │ │ │ │ │ ├── TestApi.java │ │ │ │ │ ├── callback │ │ │ │ │ ├── NoTipCallBack.java │ │ │ │ │ ├── TipCallBack.java │ │ │ │ │ └── TipProgressLoadingCallBack.java │ │ │ │ │ ├── loader │ │ │ │ │ ├── IProgressLoaderFactory.java │ │ │ │ │ ├── MiniLoadingDialogLoader.java │ │ │ │ │ ├── MiniProgressLoaderFactory.java │ │ │ │ │ └── ProgressLoader.java │ │ │ │ │ └── subscriber │ │ │ │ │ ├── NoTipRequestSubscriber.java │ │ │ │ │ ├── TipProgressLoadingSubscriber.java │ │ │ │ │ └── TipRequestSubscriber.java │ │ │ │ ├── db │ │ │ │ ├── ExternalDataBase.java │ │ │ │ └── InternalDataBase.java │ │ │ │ ├── fragment │ │ │ │ ├── MainFragment.java │ │ │ │ ├── manage │ │ │ │ │ ├── EditUserFragment.java │ │ │ │ │ ├── RegisterFragment.java │ │ │ │ │ └── ServerManageFragment.java │ │ │ │ └── net │ │ │ │ │ ├── EditUserTestFragment.java │ │ │ │ │ ├── FileUploadFragment.java │ │ │ │ │ ├── RegisterTestFragment.java │ │ │ │ │ └── ServerTestFragment.java │ │ │ │ └── utils │ │ │ │ ├── NetUtils.java │ │ │ │ ├── Utils.java │ │ │ │ ├── XToastUtils.java │ │ │ │ ├── sdkinit │ │ │ │ ├── UMengInit.java │ │ │ │ └── XBasicLibInit.java │ │ │ │ └── service │ │ │ │ └── JsonSerializationService.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ ├── ic_launcher_background.xml │ │ │ └── ic_upload.xml │ │ │ ├── layout │ │ │ ├── adapter_item_simple_list_2.xml │ │ │ ├── adapter_user_list_item.xml │ │ │ ├── fragment_file_upload.xml │ │ │ ├── fragment_main.xml │ │ │ ├── fragment_server_manage.xml │ │ │ ├── fragment_server_test.xml │ │ │ └── fragment_user_info_manage.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ ├── arrays.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── styles_widget.xml │ │ │ └── xml │ │ │ └── network_security_config.xml │ └── test │ │ └── java │ │ └── com │ │ └── xuexiang │ │ └── templateandserver │ │ └── ExampleUnitTest.java └── x-library-server.gradle ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── renovate.json ├── settings.gradle └── versions.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /LocalRepository 4 | /keystores 5 | /local.properties 6 | /.idea/caches 7 | /.idea/codeStyles 8 | /.idea/inspectionProfiles 9 | /.idea/libraries 10 | /.idea/dictionaries 11 | /.idea/markdown-navigator 12 | /.idea/*.xml 13 | .DS_Store 14 | /build 15 | /captures 16 | .externalNativeBuild -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/xuexiang.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TemplateAndServer 2 | 3 | 简化版的Android服务端模版,用于接口模拟测试。 4 | 5 | ## 关于我 6 | 7 | [![github](https://img.shields.io/badge/GitHub-xuexiangjys-blue.svg)](https://github.com/xuexiangjys) [![csdn](https://img.shields.io/badge/CSDN-xuexiangjys-green.svg)](http://blog.csdn.net/xuexiangjys) [![简书](https://img.shields.io/badge/简书-xuexiangjys-red.svg)](https://www.jianshu.com/u/6bf605575337) [![掘金](https://img.shields.io/badge/掘金-xuexiangjys-brightgreen.svg)](https://juejin.im/user/598feef55188257d592e56ed) [![知乎](https://img.shields.io/badge/知乎-xuexiangjys-violet.svg)](https://www.zhihu.com/people/xuexiangjys) 8 | 9 | ## 演示(请star支持) 10 | 11 | ### 服务端演示 12 | 13 | ![](https://z3.ax1x.com/2021/06/15/2HmX0x.gif) 14 | 15 | ### 浏览器演示 16 | 17 | ![](https://z3.ax1x.com/2021/06/15/2Hmj76.gif) 18 | 19 | ### 写法对比 20 | 21 | * AndServer 22 | 23 | ![](https://z3.ax1x.com/2021/06/15/2HnC1H.png) 24 | 25 | ![](https://z3.ax1x.com/2021/06/15/2HnPcd.png) 26 | 27 | * SpringBoot 28 | 29 | ![](https://z3.ax1x.com/2021/06/15/2Hn99e.png) 30 | 31 | 从上面的图片我们很容易看出,AndServer的写法和SpringBoot是非常相似的,就连项目工程的结构也是相似的。 32 | 33 | --- 34 | 35 | ## 功能介绍 36 | 37 | > 本项目使用[AndServer](https://github.com/yanzhenjie/AndServer)提供的服务搭建。 38 | 39 | * 统一的请求日志记录。 40 | 41 | * 全局异常捕获处理,返回统一API结果。 42 | 43 | * 增加全局权限验证拦截器。 44 | 45 | * 文件上传。 46 | 47 | * 后台管理界面。 48 | 49 | * 接口测试界面。 50 | 51 | ## 返回Json格式 52 | 53 | ``` 54 | { 55 | "code":0, //响应码,0为成功,否则失败 56 | "msg":"", //请求失败的原因说明 57 | "data":{} //返回的数据对象 58 | } 59 | ``` 60 | 61 | ## 使用方式 62 | 63 | ### 服务器配置 64 | 65 | 在`com.xuexiang.server.ServerConfig`进行配置的修改。 66 | 67 | ### 服务接口编写 68 | 69 | 1.在`com.xuexiang.server.controller`包下创建Controller类。 70 | 71 | 2.在Controller类上加上`@RestController`和`@RequestMapping`注解。 72 | 73 | ### 日志查看 74 | 75 | 在logcat上搜索关键词"AndServer"即可查看请求日志。 76 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'img-optimizer' 3 | apply plugin: 'com.yanzhenjie.andserver' 4 | //打包时,记得设置true启用 5 | if (isNeedPackage.toBoolean() && isUseBooster.toBoolean()) { 6 | apply plugin: 'com.didiglobal.booster' 7 | } 8 | 9 | android { 10 | compileSdkVersion build_versions.target_sdk 11 | buildToolsVersion build_versions.build_tools 12 | 13 | defaultConfig { 14 | applicationId "com.xuexiang.templateandserver" 15 | minSdkVersion 17 16 | targetSdkVersion build_versions.target_sdk 17 | versionCode 1 18 | versionName "1.0" 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | 21 | multiDexEnabled true 22 | vectorDrawables.useSupportLibrary = true 23 | 24 | javaCompileOptions { 25 | annotationProcessorOptions { 26 | arguments = [moduleName: project.getName()] 27 | } 28 | } 29 | } 30 | 31 | signingConfigs { 32 | if (isNeedPackage.toBoolean()) { 33 | release { 34 | storeFile file(app_release.storeFile) 35 | storePassword app_release.storePassword 36 | keyAlias app_release.keyAlias 37 | keyPassword app_release.keyPassword 38 | } 39 | } 40 | 41 | debug { 42 | storeFile file("./debug.jks") 43 | storePassword "123456" 44 | keyAlias "debug" 45 | keyPassword "123456" 46 | } 47 | } 48 | 49 | buildTypes { 50 | release { 51 | minifyEnabled true 52 | shrinkResources true 53 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 54 | if (isNeedPackage.toBoolean()) { 55 | signingConfig signingConfigs.release 56 | 57 | Properties properties = new Properties() 58 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 59 | def appID = properties.getProperty("APP_ID_UMENG") 60 | if (appID != null) { 61 | buildConfigField "String", "APP_ID_UMENG", appID 62 | } else { 63 | buildConfigField "String", "APP_ID_UMENG", '""' 64 | } 65 | } else { 66 | signingConfig signingConfigs.debug 67 | buildConfigField "String", "APP_ID_UMENG", '""' 68 | } 69 | } 70 | 71 | debug { 72 | debuggable true 73 | minifyEnabled false 74 | 75 | signingConfig signingConfigs.debug 76 | buildConfigField "String", "APP_ID_UMENG", '""' 77 | } 78 | } 79 | 80 | lintOptions { 81 | abortOnError false 82 | } 83 | 84 | compileOptions { 85 | sourceCompatibility JavaVersion.VERSION_1_8 86 | targetCompatibility JavaVersion.VERSION_1_8 87 | } 88 | } 89 | 90 | dependencies { 91 | implementation fileTree(dir: 'libs', include: ['*.jar']) 92 | testImplementation deps.junit 93 | androidTestImplementation deps.runner 94 | androidTestImplementation deps.espresso.core 95 | 96 | //AndServer 97 | implementation 'com.yanzhenjie.andserver:api:2.1.5' 98 | annotationProcessor 'com.yanzhenjie.andserver:processor:2.1.5' 99 | implementation 'org.apache.commons:commons-lang3:3.11' 100 | implementation 'org.apache.commons:commons-collections4:4.4' 101 | implementation 'io.jsonwebtoken:jjwt:0.9.0' 102 | implementation 'com.squareup.retrofit2:retrofit:2.9.0' 103 | //数据库 104 | implementation 'com.github.xuexiangjys.XOrmlite:xormlite-runtime:1.0.2' 105 | annotationProcessor 'com.github.xuexiangjys.XOrmlite:xormlite-compiler:1.0.2' 106 | implementation 'com.github.xuexiangjys:rxutil2:1.2.1' 107 | implementation deps.androidx.recyclerview 108 | implementation 'com.github.xuexiangjys.SmartRefreshLayout:refresh-layout:1.1.5' 109 | implementation deps.androidx.multidex 110 | //屏幕适配AutoSize 111 | implementation 'me.jessyan:autosize:1.1.2' 112 | //umeng统计 113 | implementation 'com.umeng.umsdk:common:9.3.8' 114 | implementation 'com.umeng.umsdk:asms:1.2.1' 115 | 116 | //美团多渠道打包 117 | implementation 'com.meituan.android.walle:library:1.1.6' 118 | } 119 | 120 | //x-library依赖脚本 121 | apply from: 'x-library-server.gradle' 122 | //walle多渠道打包 123 | apply from: 'multiple-channel.gradle' 124 | 125 | -------------------------------------------------------------------------------- /app/channel: -------------------------------------------------------------------------------- 1 | # 美团 2 | meituan 3 | # 三星 4 | samsungapps 5 | # 小米 6 | xiaomi 7 | # 91助手 8 | 91com 9 | # 魅族 10 | meizu 11 | # 豌豆荚 12 | wandou 13 | # Google Play 14 | googleplay 15 | # 百度 16 | baidu 17 | # 360 18 | 360cn 19 | # 应用宝 20 | myapp 21 | # 华为 22 | huawei 23 | # 蒲公英 24 | pgyer 25 | github -------------------------------------------------------------------------------- /app/debug.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateAndServer/e55a40e3997e6da7b8c6a141b31952aaea979c3a/app/debug.jks -------------------------------------------------------------------------------- /app/multiple-channel.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'walle' 2 | 3 | walle { 4 | // 指定渠道包的输出路径 5 | apkOutputFolder = new File("${project.buildDir}/outputs/channels") 6 | // 定制渠道包的APK的文件名称 7 | apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk' 8 | // 渠道配置文件 9 | channelFile = new File("${project.getProjectDir()}/channel") 10 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xuexiang/templateandserver/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.templateandserver; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.xuexiang.templateproject", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 44 | 45 | 46 | 49 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/assets/web/css/login.css: -------------------------------------------------------------------------------- 1 | .sec_body { color:#404040;background:#EBEBEB;text-shadow:#ddd 0 1px 0px;font-family:Helvetica;line-height:1.5;font-size:small; } 2 | .center_father {color:#404040;text-align: center;} 3 | .center_son { margin-right: auto; margin-left: auto; } -------------------------------------------------------------------------------- /app/src/main/assets/web/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/TemplateAndServer/e55a40e3997e6da7b8c6a141b31952aaea979c3a/app/src/main/assets/web/image/logo.png -------------------------------------------------------------------------------- /app/src/main/assets/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AndServer Sample 8 | 11 | 12 | 13 |
14 |
15 |

服务正在运行中...

16 | 点击注册   点击登录 17 |
18 | 19 | -------------------------------------------------------------------------------- /app/src/main/assets/web/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Sign In 10 | 11 | 12 | 13 |
14 |

Sign In

15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 |
LoginName:  
    
Password:  
    
36 | 37 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/assets/web/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Register 10 | 11 | 12 | 13 |
14 |

Register

15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 |
LoginName:  
    
Password:  
    
36 | 37 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/CoreService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server; 17 | 18 | import android.app.Service; 19 | import android.content.Intent; 20 | import android.os.IBinder; 21 | 22 | import androidx.annotation.Nullable; 23 | 24 | import com.xuexiang.templateandserver.utils.NetUtils; 25 | import com.yanzhenjie.andserver.AndServer; 26 | import com.yanzhenjie.andserver.Server; 27 | 28 | import java.net.InetAddress; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | /** 32 | * 核心服务 33 | * 34 | * @author xuexiang 35 | * @since 2020/8/30 2:56 AM 36 | */ 37 | public class CoreService extends Service { 38 | 39 | private Server mServer; 40 | 41 | @Override 42 | public void onCreate() { 43 | mServer = AndServer.webServer(this) 44 | .port(ServerConfig.SERVER_PORT) 45 | .timeout(ServerConfig.SERVER_TIMEOUT, TimeUnit.SECONDS) 46 | .listener(new Server.ServerListener() { 47 | @Override 48 | public void onStarted() { 49 | InetAddress address = NetUtils.getLocalIPAddress(); 50 | if (address != null) { 51 | ServerManager.onServerStart(CoreService.this, address.getHostAddress()); 52 | } 53 | } 54 | 55 | @Override 56 | public void onStopped() { 57 | ServerManager.onServerStop(CoreService.this); 58 | } 59 | 60 | @Override 61 | public void onException(Exception e) { 62 | ServerManager.onServerError(CoreService.this, e.getMessage()); 63 | } 64 | }) 65 | .build(); 66 | } 67 | 68 | @Override 69 | public int onStartCommand(Intent intent, int flags, int startId) { 70 | startServer(); 71 | return START_STICKY; 72 | } 73 | 74 | @Override 75 | public void onDestroy() { 76 | stopServer(); 77 | super.onDestroy(); 78 | } 79 | 80 | /** 81 | * Start server. 82 | */ 83 | private void startServer() { 84 | mServer.startup(); 85 | } 86 | 87 | /** 88 | * Stop server. 89 | */ 90 | private void stopServer() { 91 | mServer.shutdown(); 92 | } 93 | 94 | @Nullable 95 | @Override 96 | public IBinder onBind(Intent intent) { 97 | return null; 98 | } 99 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/OnServerStatusListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server; 19 | 20 | /** 21 | * 服务状态监听 22 | * 23 | * @author xuexiang 24 | * @since 2020/8/30 3:01 AM 25 | */ 26 | public interface OnServerStatusListener { 27 | 28 | /** 29 | * 服务开启 30 | * 31 | * @param ip 服务器的地址 32 | */ 33 | void onServerStart(String ip); 34 | 35 | /** 36 | * 服务出错 37 | * 38 | * @param message 出错信息 39 | */ 40 | void onServerError(String message); 41 | 42 | /** 43 | * 服务停止 44 | */ 45 | void onServerStop(); 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/ServerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server; 19 | 20 | import com.xuexiang.constant.MemoryConstants; 21 | 22 | /** 23 | * 服务器配置 24 | * 25 | * @author xuexiang 26 | * @since 2020/8/30 2:51 AM 27 | */ 28 | public final class ServerConfig { 29 | 30 | private ServerConfig() { 31 | throw new UnsupportedOperationException("u can't instantiate me..."); 32 | } 33 | 34 | /** 35 | * web页面服务资源的加载路径 36 | */ 37 | public static final String WEB_ASSENTS = "/web"; 38 | 39 | /** 40 | * 服务器端口 41 | */ 42 | public static final int SERVER_PORT = 8080; 43 | 44 | /** 45 | * 服务器响应超时时间(秒) 46 | */ 47 | public static final int SERVER_TIMEOUT = 10; 48 | 49 | 50 | /** 51 | * 文件上传配置 52 | */ 53 | public static final class Upload { 54 | /** 55 | * 文件上传的缓存目录 56 | */ 57 | public static final String CACHE_DIR = "_server_upload_cache_"; 58 | 59 | /** 60 | * 一次上传的所有文件的最大大小 61 | */ 62 | public static final int ALL_FILE_MAX_SIZE = MemoryConstants.MB * 20; 63 | 64 | /** 65 | * 一次上传的单个文件的最大大小 66 | */ 67 | public static final int SINGLE_FILE_MAX_SIZE = MemoryConstants.MB * 5; 68 | 69 | /** 70 | * 上传文件写入磁盘前读取的缓存池大小 71 | */ 72 | public static final int MAX_IN_MEMORY_SIZE = MemoryConstants.KB * 10; 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/ServerManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server; 17 | 18 | import android.content.BroadcastReceiver; 19 | import android.content.Context; 20 | import android.content.Intent; 21 | 22 | import com.xuexiang.server.utils.StorageUtils; 23 | import com.xuexiang.templateandserver.MyApp; 24 | import com.xuexiang.templateandserver.db.ExternalDataBase; 25 | import com.xuexiang.xaop.annotation.Permission; 26 | import com.xuexiang.xormlite.AndServerDataBaseRepository; 27 | import com.xuexiang.xormlite.logs.DBLog; 28 | import com.xuexiang.xutil.app.BroadcastUtils; 29 | 30 | import static com.xuexiang.xaop.consts.PermissionConsts.STORAGE; 31 | 32 | /** 33 | * 服务管理者 34 | * 35 | * @author xuexiang 36 | * @since 2020/8/30 2:53 AM 37 | */ 38 | public class ServerManager extends BroadcastReceiver { 39 | 40 | private static final String ACTION = "com.xuexiang.server.receiver"; 41 | 42 | private static final String CMD_KEY = "cmd_key"; 43 | private static final String MESSAGE_KEY = "message_key"; 44 | 45 | private static final int CMD_VALUE_START = 1; 46 | private static final int CMD_VALUE_ERROR = 2; 47 | private static final int CMD_VALUE_STOP = 4; 48 | 49 | /** 50 | * Notify serverStart. 51 | * 52 | * @param context context. 53 | */ 54 | public static void onServerStart(Context context, String hostAddress) { 55 | sendBroadcast(context, CMD_VALUE_START, hostAddress); 56 | } 57 | 58 | /** 59 | * Notify serverStop. 60 | * 61 | * @param context context. 62 | */ 63 | public static void onServerError(Context context, String error) { 64 | sendBroadcast(context, CMD_VALUE_ERROR, error); 65 | } 66 | 67 | /** 68 | * Notify serverStop. 69 | * 70 | * @param context context. 71 | */ 72 | public static void onServerStop(Context context) { 73 | sendBroadcast(context, CMD_VALUE_STOP); 74 | } 75 | 76 | private static void sendBroadcast(Context context, int cmd) { 77 | sendBroadcast(context, cmd, null); 78 | } 79 | 80 | private static void sendBroadcast(Context context, int cmd, String message) { 81 | Intent broadcast = new Intent(ACTION); 82 | broadcast.putExtra(CMD_KEY, cmd); 83 | broadcast.putExtra(MESSAGE_KEY, message); 84 | context.sendBroadcast(broadcast); 85 | } 86 | 87 | private OnServerStatusListener mOnServerStatusListener; 88 | private Context mContext; 89 | private Intent mService; 90 | 91 | public ServerManager(Context context, OnServerStatusListener listener) { 92 | mContext = context.getApplicationContext(); 93 | mOnServerStatusListener = listener; 94 | mService = new Intent(mContext, CoreService.class); 95 | } 96 | 97 | /** 98 | * 注册广播 99 | */ 100 | public void register() { 101 | BroadcastUtils.registerReceiver(mContext, this, ACTION); 102 | } 103 | 104 | /** 105 | * 注销广播 106 | */ 107 | public void unRegister() { 108 | BroadcastUtils.unregisterReceiver(mContext, this); 109 | } 110 | 111 | @Permission(STORAGE) 112 | public void startServer() { 113 | initServer(); 114 | mContext.startService(mService); 115 | } 116 | 117 | public void stopServer() { 118 | mContext.stopService(mService); 119 | } 120 | 121 | @Override 122 | public void onReceive(Context context, Intent intent) { 123 | String action = intent.getAction(); 124 | if (ACTION.equals(action)) { 125 | int cmd = intent.getIntExtra(CMD_KEY, 0); 126 | switch (cmd) { 127 | case CMD_VALUE_START: 128 | String ip = intent.getStringExtra(MESSAGE_KEY); 129 | mOnServerStatusListener.onServerStart(ip); 130 | break; 131 | case CMD_VALUE_ERROR: 132 | String error = intent.getStringExtra(MESSAGE_KEY); 133 | mOnServerStatusListener.onServerError(error); 134 | break; 135 | case CMD_VALUE_STOP: 136 | mOnServerStatusListener.onServerStop(); 137 | break; 138 | default: 139 | break; 140 | } 141 | } 142 | } 143 | 144 | 145 | private void initServer() { 146 | StorageUtils.initRootPath(mContext); 147 | initDB(mContext); 148 | } 149 | 150 | 151 | /** 152 | * 初始化数据库框架 153 | */ 154 | public static void initDB(Context context) { 155 | AndServerDataBaseRepository.getInstance() 156 | .setIDatabase(new ExternalDataBase(StorageUtils.getDateBasePath(), AndServerDataBaseRepository.DATABASE_NAME, AndServerDataBaseRepository.DATABASE_VERSION)) 157 | .init(context); 158 | DBLog.debug(MyApp.isDebug()); 159 | } 160 | 161 | 162 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/api/base/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.server.api.base; 2 | 3 | /** 4 | * 错误信息实体 5 | * 6 | * @author xuexiang 7 | * @since 2020/8/30 10:51 PM 8 | */ 9 | public class ApiException extends Exception { 10 | 11 | /** 12 | * 错误的code码 13 | */ 14 | private int mCode; 15 | 16 | public ApiException(String message, int code) { 17 | super(message); 18 | mCode = code; 19 | } 20 | 21 | public ApiException(Throwable e, int code) { 22 | super(e); 23 | mCode = code; 24 | } 25 | 26 | public int getCode() { 27 | return mCode; 28 | } 29 | 30 | /** 31 | * 约定异常 32 | */ 33 | public static class ERROR { 34 | 35 | /** 36 | * Token失效,需要重新获取token的code码 37 | */ 38 | public static final int TOKEN_INVALID = 100; 39 | /** 40 | * 缺少Token 41 | */ 42 | public static final int TOKEN_MISSING = TOKEN_INVALID + 1; 43 | /** 44 | * 认证失败 45 | */ 46 | public static final int AUTH_ERROR = TOKEN_MISSING + 1; 47 | 48 | 49 | /** 50 | * 未知错误 51 | */ 52 | public static final int UNKNOWN = 5000; 53 | 54 | /** 55 | * 一般性业务错误 56 | */ 57 | public static final int COMMON_BUSINESS_ERROR = UNKNOWN + 1; 58 | 59 | /** 60 | * 文件存储失败 61 | */ 62 | public static final int FILE_STORE_ERROR = COMMON_BUSINESS_ERROR + 1; 63 | 64 | /** 65 | * 请求超出限制 66 | */ 67 | public static final int REQUEST_BEYOND_LIMIT = FILE_STORE_ERROR + 1; 68 | 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/api/base/ApiResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.server.api.base; 18 | 19 | import android.os.Parcel; 20 | import android.os.Parcelable; 21 | 22 | import com.google.gson.annotations.SerializedName; 23 | 24 | /** 25 | * 提供的默认的标注返回api 26 | * 27 | * @author xuexiang 28 | * @since 2018/5/22 下午4:22 29 | */ 30 | public class ApiResult implements Parcelable { 31 | 32 | public final static String CODE = "code"; 33 | public final static String MSG = "msg"; 34 | public final static String DATA = "data"; 35 | 36 | @SerializedName(value = CODE) 37 | private int code; 38 | @SerializedName(value = MSG) 39 | private String msg = ""; 40 | @SerializedName(value = DATA) 41 | private T data; 42 | 43 | public ApiResult() { 44 | 45 | } 46 | 47 | protected ApiResult(Parcel in) { 48 | code = in.readInt(); 49 | msg = in.readString(); 50 | } 51 | 52 | public static final Creator CREATOR = new Creator() { 53 | @Override 54 | public ApiResult createFromParcel(Parcel in) { 55 | return new ApiResult(in); 56 | } 57 | 58 | @Override 59 | public ApiResult[] newArray(int size) { 60 | return new ApiResult[size]; 61 | } 62 | }; 63 | 64 | @Override 65 | public int describeContents() { 66 | return 0; 67 | } 68 | 69 | @Override 70 | public void writeToParcel(Parcel dest, int flags) { 71 | dest.writeInt(code); 72 | dest.writeString(msg); 73 | } 74 | 75 | public int getCode() { 76 | return code; 77 | } 78 | 79 | public ApiResult setCode(int code) { 80 | this.code = code; 81 | return this; 82 | } 83 | 84 | public String getMsg() { 85 | return msg; 86 | } 87 | 88 | public ApiResult setMsg(String msg) { 89 | this.msg = msg; 90 | return this; 91 | } 92 | 93 | public ApiResult setData(T data) { 94 | this.data = data; 95 | return this; 96 | } 97 | 98 | public ApiResult setError(int code, String msg) { 99 | this.code = code; 100 | this.msg = msg; 101 | return this; 102 | } 103 | 104 | /** 105 | * 获取请求响应的数据,自定义api的时候需要重写【很关键】 106 | * 107 | * @return 响应的数据 108 | */ 109 | public T getData() { 110 | return data; 111 | } 112 | 113 | /** 114 | * 是否请求成功,自定义api的时候需要重写【很关键】 115 | * 116 | * @return 是否请求成功 117 | */ 118 | public boolean isSuccess() { 119 | return getCode() == 0; 120 | } 121 | 122 | @Override 123 | public String toString() { 124 | return "ApiResult{" + 125 | "code='" + code + '\'' + 126 | ", msg='" + msg + '\'' + 127 | ", data=" + data + 128 | '}'; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/api/request/PageQuery.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.server.api.request; 2 | 3 | import com.xuexiang.xutil.net.JsonUtil; 4 | 5 | /** 6 | * @author xuexiang 7 | * @since 2018/8/15 上午12:26 8 | */ 9 | public class PageQuery { 10 | 11 | /** 12 | * 第几页数 13 | */ 14 | public int pageNum; 15 | /** 16 | * 每页的数量 17 | */ 18 | public int pageSize; 19 | 20 | public PageQuery() { 21 | } 22 | 23 | public PageQuery(int pageNum, int pageSize) { 24 | this.pageNum = pageNum; 25 | this.pageSize = pageSize; 26 | } 27 | 28 | public int getPageNum() { 29 | return pageNum; 30 | } 31 | 32 | public PageQuery setPageNum(int pageNum) { 33 | this.pageNum = pageNum; 34 | return this; 35 | } 36 | 37 | public int getPageSize() { 38 | return pageSize; 39 | } 40 | 41 | public PageQuery setPageSize(int pageSize) { 42 | this.pageSize = pageSize; 43 | return this; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return JsonUtil.toJson(this); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/api/response/LoginInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.api.response; 19 | 20 | import com.xuexiang.server.model.User; 21 | 22 | /** 23 | * 登录信息 24 | * 25 | * @author xuexiang 26 | * @since 2018/8/6 下午6:14 27 | */ 28 | public class LoginInfo { 29 | 30 | private User user; 31 | 32 | private String token; 33 | 34 | public User getUser() { 35 | return user; 36 | } 37 | 38 | public LoginInfo setUser(User user) { 39 | this.user = user; 40 | return this; 41 | } 42 | 43 | public String getToken() { 44 | return token; 45 | } 46 | 47 | public LoginInfo setToken(String token) { 48 | this.token = token; 49 | return this; 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/component/AppConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server.component; 17 | 18 | import android.content.Context; 19 | 20 | import com.xuexiang.server.ServerConfig; 21 | import com.xuexiang.xutil.file.FileUtils; 22 | import com.yanzhenjie.andserver.annotation.Config; 23 | import com.yanzhenjie.andserver.framework.config.Multipart; 24 | import com.yanzhenjie.andserver.framework.config.WebConfig; 25 | import com.yanzhenjie.andserver.framework.website.AssetsWebsite; 26 | 27 | /** 28 | * web页面配置 29 | * 30 | * @author xuexiang 31 | * @since 2020/8/30 3:51 AM 32 | */ 33 | @Config 34 | public class AppConfig implements WebConfig { 35 | 36 | @Override 37 | public void onConfig(Context context, Delegate delegate) { 38 | delegate.addWebsite(new AssetsWebsite(context, ServerConfig.WEB_ASSENTS)); 39 | 40 | delegate.setMultipart(Multipart.newBuilder() 41 | .allFileMaxSize(ServerConfig.Upload.ALL_FILE_MAX_SIZE) 42 | .fileMaxSize(ServerConfig.Upload.SINGLE_FILE_MAX_SIZE) 43 | .maxInMemorySize(ServerConfig.Upload.MAX_IN_MEMORY_SIZE) 44 | .uploadTempDir(FileUtils.getFileByPath(FileUtils.getDiskCacheDir(ServerConfig.Upload.CACHE_DIR))) 45 | .build()); 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/component/AppExceptionResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server.component; 17 | 18 | import androidx.annotation.NonNull; 19 | 20 | import com.xuexiang.server.api.base.ApiException; 21 | import com.xuexiang.server.utils.ApiUtils; 22 | import com.yanzhenjie.andserver.annotation.Resolver; 23 | import com.yanzhenjie.andserver.error.BasicException; 24 | import com.yanzhenjie.andserver.framework.ExceptionResolver; 25 | import com.yanzhenjie.andserver.framework.body.JsonBody; 26 | import com.yanzhenjie.andserver.http.HttpRequest; 27 | import com.yanzhenjie.andserver.http.HttpResponse; 28 | import com.yanzhenjie.andserver.util.StatusCode; 29 | 30 | /** 31 | * 应用服务错误信息处理 32 | * 33 | * @author xuexiang 34 | * @since 2020/8/30 11:04 PM 35 | */ 36 | @Resolver 37 | public class AppExceptionResolver implements ExceptionResolver { 38 | 39 | @Override 40 | public void onResolve(@NonNull HttpRequest request, @NonNull HttpResponse response, @NonNull Throwable e) { 41 | e.printStackTrace(); 42 | int code = StatusCode.SC_INTERNAL_SERVER_ERROR; 43 | if (e instanceof BasicException) { 44 | // 基础框架的错误 45 | code = ((BasicException) e).getStatusCode(); 46 | } else if (e instanceof ApiException) { 47 | // 自定义的错误 48 | code = ((ApiException) e).getCode(); 49 | } 50 | String body = ApiUtils.failedJson(code, e.getMessage()); 51 | response.setBody(new JsonBody(body)); 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/component/AppMessageConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server.component; 17 | 18 | import androidx.annotation.NonNull; 19 | import androidx.annotation.Nullable; 20 | 21 | import com.xuexiang.server.utils.ApiUtils; 22 | import com.xuexiang.xutil.net.JsonUtil; 23 | import com.yanzhenjie.andserver.annotation.Converter; 24 | import com.yanzhenjie.andserver.framework.MessageConverter; 25 | import com.yanzhenjie.andserver.framework.body.JsonBody; 26 | import com.yanzhenjie.andserver.http.ResponseBody; 27 | import com.yanzhenjie.andserver.util.IOUtils; 28 | import com.yanzhenjie.andserver.util.MediaType; 29 | 30 | import java.io.IOException; 31 | import java.io.InputStream; 32 | import java.lang.reflect.Type; 33 | import java.nio.charset.Charset; 34 | 35 | /** 36 | * 应用消息解析器 37 | * 38 | * @author xuexiang 39 | * @since 2020/8/30 10:40 PM 40 | */ 41 | @Converter 42 | public class AppMessageConverter implements MessageConverter { 43 | 44 | @Override 45 | public ResponseBody convert(@NonNull Object output, @Nullable MediaType mediaType) { 46 | return new JsonBody(ApiUtils.successfulJson(output)); 47 | } 48 | 49 | @Nullable 50 | @Override 51 | public T convert(@NonNull InputStream stream, @Nullable MediaType mediaType, Type type) throws IOException { 52 | Charset charset = mediaType == null ? null : mediaType.getCharset(); 53 | if (charset == null) { 54 | return JsonUtil.fromJson(IOUtils.toString(stream), type); 55 | } 56 | return JsonUtil.fromJson(IOUtils.toString(stream, charset), type); 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/component/LoggerInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server.component; 17 | 18 | import androidx.annotation.NonNull; 19 | 20 | import com.xuexiang.xutil.common.logger.Logger; 21 | import com.xuexiang.xutil.net.JsonUtil; 22 | import com.yanzhenjie.andserver.annotation.Interceptor; 23 | import com.yanzhenjie.andserver.framework.HandlerInterceptor; 24 | import com.yanzhenjie.andserver.framework.handler.RequestHandler; 25 | import com.yanzhenjie.andserver.http.HttpMethod; 26 | import com.yanzhenjie.andserver.http.HttpRequest; 27 | import com.yanzhenjie.andserver.http.HttpResponse; 28 | import com.yanzhenjie.andserver.util.MultiValueMap; 29 | 30 | /** 31 | * 日志拦截器 32 | * 33 | * @author xuexiang 34 | * @since 2020/8/30 10:37 PM 35 | */ 36 | @Interceptor 37 | public class LoggerInterceptor implements HandlerInterceptor { 38 | private static final String TAG = "AndServer"; 39 | 40 | @Override 41 | public boolean onIntercept(@NonNull HttpRequest request, @NonNull HttpResponse response, 42 | @NonNull RequestHandler handler) { 43 | String path = request.getPath(); 44 | HttpMethod method = request.getMethod(); 45 | MultiValueMap valueMap = request.getParameter(); 46 | Logger.iTag(TAG, "---------->|开始请求接口: " + path); 47 | Logger.iTag(TAG, " |请求方式: " + method.value()); 48 | Logger.iTag(TAG, " |请求参数: " + JsonUtil.toJson(valueMap)); 49 | return false; 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/component/LoginInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server.component; 17 | 18 | import androidx.annotation.NonNull; 19 | 20 | import com.yanzhenjie.andserver.annotation.Interceptor; 21 | import com.yanzhenjie.andserver.error.BasicException; 22 | import com.yanzhenjie.andserver.framework.HandlerInterceptor; 23 | import com.yanzhenjie.andserver.framework.handler.MethodHandler; 24 | import com.yanzhenjie.andserver.framework.handler.RequestHandler; 25 | import com.yanzhenjie.andserver.framework.mapping.Addition; 26 | import com.yanzhenjie.andserver.http.HttpRequest; 27 | import com.yanzhenjie.andserver.http.HttpResponse; 28 | import com.yanzhenjie.andserver.http.session.Session; 29 | 30 | import org.apache.commons.lang3.ArrayUtils; 31 | 32 | /** 33 | * 登录验证拦截器 34 | * 35 | * @author xuexiang 36 | * @since 2020/8/30 10:27 PM 37 | */ 38 | @Interceptor 39 | public class LoginInterceptor implements HandlerInterceptor { 40 | 41 | public static final String LOGIN_ATTRIBUTE = "USER.LOGIN.SIGN"; 42 | 43 | @Override 44 | public boolean onIntercept(@NonNull HttpRequest request, @NonNull HttpResponse response, 45 | @NonNull RequestHandler handler) { 46 | if (handler instanceof MethodHandler) { 47 | MethodHandler methodHandler = (MethodHandler) handler; 48 | Addition addition = methodHandler.getAddition(); 49 | if (!isLogin(request, addition)) { 50 | throw new BasicException(401, "You are not logged in yet."); 51 | } 52 | } 53 | return false; 54 | } 55 | 56 | private boolean isNeedLogin(Addition addition) { 57 | if (addition == null) { 58 | return false; 59 | } 60 | 61 | String[] stringType = addition.getStringType(); 62 | if (ArrayUtils.isEmpty(stringType)) { 63 | return false; 64 | } 65 | 66 | boolean[] booleanType = addition.getBooleanType(); 67 | if (ArrayUtils.isEmpty(booleanType)) { 68 | return false; 69 | } 70 | return stringType[0].equalsIgnoreCase("login") && booleanType[0]; 71 | } 72 | 73 | private boolean isLogin(HttpRequest request, Addition addition) { 74 | if (isNeedLogin(addition)) { 75 | Session session = request.getSession(); 76 | if (session != null) { 77 | Object o = session.getAttribute(LOGIN_ATTRIBUTE); 78 | return o instanceof Boolean && (boolean) o; 79 | } 80 | return false; 81 | } 82 | return true; 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/controller/FileController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.controller; 19 | 20 | import com.xuexiang.server.utils.StorageUtils; 21 | import com.yanzhenjie.andserver.annotation.PostMapping; 22 | import com.yanzhenjie.andserver.annotation.RequestMapping; 23 | import com.yanzhenjie.andserver.annotation.RequestParam; 24 | import com.yanzhenjie.andserver.annotation.RestController; 25 | import com.yanzhenjie.andserver.http.multipart.MultipartFile; 26 | 27 | import java.io.File; 28 | import java.io.IOException; 29 | 30 | /** 31 | * @author xuexiang 32 | * @since 2020/9/6 12:37 AM 33 | */ 34 | @RestController 35 | @RequestMapping(path = "/file") 36 | public class FileController { 37 | 38 | @PostMapping(path = "/upload") 39 | String upload(@RequestParam(name = "file") MultipartFile file, @RequestParam(name = "type") String type) throws IOException { 40 | File localFile = StorageUtils.createRandomFile(file, type); 41 | file.transferTo(localFile); 42 | return StorageUtils.getServerSaveFile(localFile); 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/controller/PageController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.server.controller; 17 | 18 | import com.yanzhenjie.andserver.annotation.Controller; 19 | import com.yanzhenjie.andserver.annotation.GetMapping; 20 | 21 | /** 22 | * Web页面服务 23 | * 24 | * @author xuexiang 25 | * @since 2020/8/30 3:52 AM 26 | */ 27 | @Controller 28 | public class PageController { 29 | 30 | @GetMapping(path = "/") 31 | public String index() { 32 | // Equivalent to [return "/index"]. 33 | return "forward:/index.html"; 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/controller/UserController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.controller; 19 | 20 | import com.xuexiang.server.api.base.ApiException; 21 | import com.xuexiang.server.api.request.PageQuery; 22 | import com.xuexiang.server.api.response.LoginInfo; 23 | import com.xuexiang.server.model.User; 24 | import com.xuexiang.server.service.UserService; 25 | import com.xuexiang.server.service.impl.UserServiceImpl; 26 | import com.xuexiang.server.utils.TokenUtils; 27 | import com.yanzhenjie.andserver.annotation.PostMapping; 28 | import com.yanzhenjie.andserver.annotation.RequestBody; 29 | import com.yanzhenjie.andserver.annotation.RequestMapping; 30 | import com.yanzhenjie.andserver.annotation.RequestParam; 31 | import com.yanzhenjie.andserver.annotation.RestController; 32 | 33 | import java.util.List; 34 | 35 | import static com.xuexiang.server.api.base.ApiException.ERROR.COMMON_BUSINESS_ERROR; 36 | 37 | /** 38 | * 用户服务 39 | * 40 | * @author xuexiang 41 | * @since 2020/8/30 10:25 PM 42 | */ 43 | @RestController 44 | @RequestMapping(path = "/user") 45 | public class UserController { 46 | 47 | private UserService mUserService; 48 | 49 | private UserService getUserService() { 50 | if (mUserService == null) { 51 | mUserService = new UserServiceImpl(); 52 | } 53 | return mUserService; 54 | } 55 | 56 | /** 57 | * 登陆,获取token 58 | * 59 | * @param loginName 用户名 60 | * @param password 密码 61 | * @return 62 | * @throws Exception 63 | */ 64 | @PostMapping(path = "/action/login") 65 | LoginInfo login(@RequestParam(name = "loginName") String loginName, 66 | @RequestParam(name = "password") String password) throws Exception { 67 | return handleLoginRequest(loginName, password); 68 | } 69 | 70 | /** 71 | * 注册用户 72 | * 73 | * @param loginName 用户名 74 | * @param password 密码 75 | * @return 76 | * @throws Exception 77 | */ 78 | @PostMapping(path = "/action/register") 79 | boolean registerUser(@RequestParam(name = "loginName") String loginName, 80 | @RequestParam(name = "password") String password) throws Exception { 81 | User user = new User(); 82 | user.setLoginName(loginName); 83 | user.setPassword(password); 84 | return handleRegisterRequest(user); 85 | } 86 | 87 | /** 88 | * 注册用户 89 | */ 90 | @PostMapping(path = "/registerUser") 91 | boolean registerUser(@RequestBody User user) throws Exception { 92 | return handleRegisterRequest(user); 93 | } 94 | 95 | /** 96 | * 编辑用户信息 97 | * 98 | * @param user 用户 99 | * @return 100 | * @throws Exception 101 | */ 102 | @PostMapping(path = "/editUser") 103 | boolean editUser(@RequestBody User user) throws Exception { 104 | return getUserService().updateUser(user); 105 | } 106 | 107 | /** 108 | * 删除用户 109 | * 110 | * @param user 用户 111 | * @return 112 | * @throws Exception 113 | */ 114 | @PostMapping(path = "/deleteUser") 115 | boolean deleteUser(@RequestBody User user) throws Exception { 116 | return getUserService().deleteUser((int) user.getId()); 117 | } 118 | 119 | /** 120 | * 登陆,获取token 121 | * 122 | * @param loginUser 登录信息 123 | * @return 登录 124 | */ 125 | @PostMapping(path = "/login") 126 | LoginInfo login(@RequestBody User loginUser) throws Exception { 127 | return handleLoginRequest(loginUser.getLoginName(), loginUser.getPassword()); 128 | } 129 | 130 | 131 | /** 132 | * 分页查询用户信息 133 | * 134 | * @param pageQuery 分页查询 135 | * @return 登录 136 | */ 137 | @PostMapping(path = "/queryUser") 138 | List queryUser(@RequestBody PageQuery pageQuery) throws Exception { 139 | return getUserService().findAllUser(pageQuery.pageNum, pageQuery.pageSize); 140 | } 141 | 142 | /** 143 | * 处理登录请求 144 | * 145 | * @param loginName 用户名 146 | * @param password 密码 147 | * @return 登录信息 148 | * @throws Exception 149 | */ 150 | private LoginInfo handleLoginRequest(String loginName, String password) throws Exception { 151 | if (getUserService().findUserByAccount(loginName) == null) { 152 | throw new ApiException("账号不存在!", COMMON_BUSINESS_ERROR); 153 | } 154 | 155 | User user = getUserService().login(loginName, password); 156 | 157 | if (user != null) { 158 | return new LoginInfo() 159 | .setUser(user) 160 | .setToken(TokenUtils.createJwtToken(user.getLoginName())); 161 | } else { 162 | throw new ApiException("用户名或密码错误!", COMMON_BUSINESS_ERROR); 163 | } 164 | } 165 | 166 | /** 167 | * 处理注册请求 168 | * 169 | * @param user 用户 170 | */ 171 | private boolean handleRegisterRequest(User user) throws Exception { 172 | if (getUserService().findUserByAccount(user.getLoginName()) != null) { 173 | throw new ApiException("账号已存在!", COMMON_BUSINESS_ERROR); 174 | } 175 | return getUserService().addUser(user); 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/model/User.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.server.model; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | /** 7 | * 用户数据库表 8 | * 9 | * @author xuexiang 10 | * @since 2020/8/31 12:22 AM 11 | */ 12 | @DatabaseTable(tableName = "User") 13 | public class User { 14 | public static final String KEY_ID = "Id"; 15 | public static final String KEY_LOGIN_NAME = "loginName"; 16 | public static final String KEY_PASSWORD = "password"; 17 | 18 | @DatabaseField(generatedId = true) 19 | private long Id; 20 | 21 | @DatabaseField(unique = true) 22 | private String loginName; 23 | 24 | @DatabaseField 25 | private String password; 26 | 27 | @DatabaseField 28 | private String name; 29 | 30 | @DatabaseField 31 | private int gender; 32 | 33 | @DatabaseField 34 | private int age; 35 | 36 | @DatabaseField 37 | private String phone; 38 | 39 | public long getId() { 40 | return Id; 41 | } 42 | 43 | public User setId(long id) { 44 | Id = id; 45 | return this; 46 | } 47 | 48 | public String getLoginName() { 49 | return loginName; 50 | } 51 | 52 | public User setLoginName(String loginName) { 53 | this.loginName = loginName; 54 | return this; 55 | } 56 | 57 | public String getPassword() { 58 | return password; 59 | } 60 | 61 | public User setPassword(String password) { 62 | this.password = password; 63 | return this; 64 | } 65 | 66 | public String getName() { 67 | return name; 68 | } 69 | 70 | public User setName(String name) { 71 | this.name = name; 72 | return this; 73 | } 74 | 75 | public int getGender() { 76 | return gender; 77 | } 78 | 79 | public User setGender(int gender) { 80 | this.gender = gender; 81 | return this; 82 | } 83 | 84 | public int getAge() { 85 | return age; 86 | } 87 | 88 | public User setAge(int age) { 89 | this.age = age; 90 | return this; 91 | } 92 | 93 | public String getPhone() { 94 | return phone; 95 | } 96 | 97 | public User setPhone(String phone) { 98 | this.phone = phone; 99 | return this; 100 | } 101 | 102 | public String getGenderName() { 103 | if (gender == 0) { 104 | return "男"; 105 | } else if (gender == 1) { 106 | return "女"; 107 | } else { 108 | return "未知"; 109 | } 110 | } 111 | 112 | @Override 113 | public String toString() { 114 | return "User{" + 115 | "Id=" + Id + 116 | ", loginName='" + loginName + '\'' + 117 | ", password='" + password + '\'' + 118 | ", name='" + name + '\'' + 119 | ", gender=" + gender + 120 | ", age=" + age + 121 | ", phone='" + phone + '\'' + 122 | '}'; 123 | } 124 | } -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/service/UserService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.service; 19 | 20 | 21 | import com.xuexiang.server.model.User; 22 | 23 | import java.sql.SQLException; 24 | import java.util.List; 25 | 26 | /** 27 | * 用户服务 28 | * 29 | * @author xuexiang 30 | * @since 2020/8/31 12:36 AM 31 | */ 32 | public interface UserService { 33 | 34 | /** 35 | * 添加用户 36 | * 37 | * @param user 用户 38 | * @return 是否成功 39 | * @throws SQLException 数据库执行报错 40 | */ 41 | boolean addUser(User user) throws SQLException; 42 | 43 | /** 44 | * 删除用户 45 | * 46 | * @param userId 用户id 47 | * @return 是否成功 48 | * @throws SQLException 数据库执行报错 49 | */ 50 | boolean deleteUser(int userId) throws SQLException; 51 | 52 | /** 53 | * 更新用户 54 | * 55 | * @param record 用户最新信息 56 | * @return 是否更新成功 57 | * @throws SQLException 数据库执行报错 58 | */ 59 | boolean updateUser(User record) throws SQLException; 60 | 61 | /** 62 | * 分页查询所有用户信息 63 | * 64 | * @param pageNum 页号 65 | * @param pageSize 一页的数量 66 | * @return 指定页面的用户信息 67 | * @throws SQLException 数据库执行报错 68 | */ 69 | List findAllUser(int pageNum, int pageSize) throws SQLException; 70 | 71 | /** 72 | * 查询所有用户信息 73 | * 74 | * @return 所有用户信息 75 | * @throws SQLException 数据库执行报错 76 | */ 77 | List findAllUser() throws SQLException; 78 | 79 | /** 80 | * 根据账户名找到用户信息 81 | * 82 | * @param loginName 登录名 83 | * @return 用户信息 84 | * @throws SQLException 数据库执行报错 85 | */ 86 | User findUserByAccount(String loginName) throws SQLException; 87 | 88 | /** 89 | * 用户登陆 90 | * 91 | * @param loginName 登录名 92 | * @param password 密码 93 | * @return 用户信息 94 | * @throws SQLException 数据库执行报错 95 | */ 96 | User login(String loginName, String password) throws SQLException; 97 | 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.service.impl; 19 | 20 | import com.xuexiang.server.model.User; 21 | import com.xuexiang.server.service.UserService; 22 | import com.xuexiang.xormlite.AndServerDataBaseRepository; 23 | import com.xuexiang.xormlite.db.DBService; 24 | 25 | import java.sql.SQLException; 26 | import java.util.List; 27 | 28 | /** 29 | * 用户服务实现类 30 | * 31 | * @author xuexiang 32 | * @since 2020/8/31 12:42 AM 33 | */ 34 | public class UserServiceImpl implements UserService { 35 | 36 | private DBService mService; 37 | 38 | public UserServiceImpl() { 39 | mService = AndServerDataBaseRepository.getInstance().getDataBase(User.class); 40 | } 41 | 42 | @Override 43 | public boolean addUser(User user) throws SQLException { 44 | return mService.insert(user) > 0; 45 | } 46 | 47 | @Override 48 | public boolean deleteUser(int userId) throws SQLException { 49 | return mService.deleteById(userId) > 0; 50 | } 51 | 52 | @Override 53 | public boolean updateUser(User record) throws SQLException { 54 | return mService.updateData(record) > 0; 55 | } 56 | 57 | @Override 58 | public List findAllUser(int pageNum, int pageSize) throws SQLException { 59 | return mService.queryPage(pageNum, pageSize, User.KEY_ID, true); 60 | } 61 | 62 | @Override 63 | public List findAllUser() throws SQLException { 64 | return mService.queryAll(); 65 | } 66 | 67 | @Override 68 | public User findUserByAccount(String loginName) throws SQLException { 69 | return mService.queryForColumnFirst(User.KEY_LOGIN_NAME, loginName); 70 | } 71 | 72 | @Override 73 | public User login(String loginName, String password) throws SQLException { 74 | return mService.queryForColumnFirst(User.KEY_LOGIN_NAME, loginName, User.KEY_PASSWORD, password); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/utils/ApiUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.utils; 19 | 20 | import androidx.annotation.NonNull; 21 | 22 | import com.xuexiang.server.api.base.ApiException; 23 | import com.xuexiang.server.api.base.ApiResult; 24 | import com.xuexiang.xutil.net.JsonUtil; 25 | 26 | /** 27 | * 接口工具类 28 | * 29 | * @author xuexiang 30 | * @since 2020/8/30 10:56 PM 31 | */ 32 | public final class ApiUtils { 33 | 34 | private ApiUtils() { 35 | throw new UnsupportedOperationException("u can't instantiate me..."); 36 | } 37 | 38 | /** 39 | * 获取出错返回 40 | * 41 | * @param ex 错误信息实体 42 | * @return 出错返回 43 | */ 44 | public static ApiResult error(@NonNull ApiException ex) { 45 | ApiResult apiResult = new ApiResult(); 46 | apiResult.setError(ex.getCode(), ex.getMessage()); 47 | return apiResult; 48 | } 49 | 50 | /** 51 | * 获取出错返回 52 | * 53 | * @param code 错误码 54 | * @param msg 错误信息 55 | * @return 出错返回 56 | */ 57 | public static ApiResult error(int code, String msg) { 58 | ApiResult apiResult = new ApiResult(); 59 | apiResult.setError(code, msg); 60 | return apiResult; 61 | } 62 | 63 | /** 64 | * Business is successful. 65 | * 66 | * @param data return data. 67 | * @return json. 68 | */ 69 | public static String successfulJson(T data) { 70 | ApiResult apiResult = new ApiResult<>(); 71 | apiResult.setData(data); 72 | return JsonUtil.toJson(apiResult); 73 | } 74 | 75 | /** 76 | * Business is failed. 77 | * 78 | * @param code error code. 79 | * @param message message. 80 | * @return json. 81 | */ 82 | public static String failedJson(int code, String message) { 83 | return JsonUtil.toJson(error(code, message)); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/utils/StorageUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.utils; 19 | 20 | import android.content.Context; 21 | import android.os.Environment; 22 | import android.webkit.MimeTypeMap; 23 | 24 | import com.xuexiang.xutil.file.FileUtils; 25 | import com.yanzhenjie.andserver.http.multipart.MultipartFile; 26 | import com.yanzhenjie.andserver.util.IOUtils; 27 | import com.yanzhenjie.andserver.util.StringUtils; 28 | 29 | import java.io.File; 30 | import java.util.UUID; 31 | 32 | /** 33 | * 存储工具类 34 | * 35 | * @author xuexiang 36 | * @since 2020/9/6 12:40 AM 37 | */ 38 | public final class StorageUtils { 39 | 40 | private StorageUtils() { 41 | throw new UnsupportedOperationException("u can't instantiate me..."); 42 | } 43 | 44 | private static File mRootDir; 45 | 46 | public static void initRootPath(Context context) { 47 | if (mRootDir != null) { 48 | return; 49 | } 50 | 51 | if (FileUtils.isSDCardExist()) { 52 | mRootDir = Environment.getExternalStorageDirectory(); 53 | } else { 54 | mRootDir = context.getFilesDir(); 55 | } 56 | mRootDir = new File(mRootDir, "AndServer"); 57 | IOUtils.createFolder(mRootDir); 58 | } 59 | 60 | public static String getDateBasePath() { 61 | return new File(mRootDir, "databases").getAbsolutePath(); 62 | } 63 | 64 | public static File getRootDir() { 65 | return mRootDir; 66 | } 67 | 68 | public static File getUploadDir(String dirPath) { 69 | return new File(mRootDir, dirPath); 70 | } 71 | 72 | /** 73 | * Create a random file based on mimeType. 74 | * 75 | * @param file file. 76 | * @return file object. 77 | */ 78 | public static File createRandomFile(MultipartFile file, String dirPath) { 79 | String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(file.getContentType().toString()); 80 | if (StringUtils.isEmpty(extension)) { 81 | extension = MimeTypeMap.getFileExtensionFromUrl(file.getFilename()); 82 | } 83 | String uuid = UUID.randomUUID().toString(); 84 | String fileName = uuid + "." + extension; 85 | File uploadDir = getUploadDir(dirPath); 86 | IOUtils.createFolder(uploadDir); 87 | return new File(uploadDir, fileName); 88 | } 89 | 90 | public static String getServerSaveFile(File file) { 91 | String rootPath = getRootDir().getAbsolutePath(); 92 | String filePath = file.getAbsolutePath(); 93 | return filePath.replaceFirst(rootPath, ""); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/server/utils/TokenUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.server.utils; 19 | 20 | import com.xuexiang.constant.TimeConstants; 21 | import com.xuexiang.xutil.security.EncodeUtils; 22 | import com.yanzhenjie.andserver.http.HttpRequest; 23 | 24 | import org.apache.commons.lang3.StringUtils; 25 | 26 | import java.security.Key; 27 | import java.util.Date; 28 | 29 | import javax.crypto.spec.SecretKeySpec; 30 | 31 | import io.jsonwebtoken.Claims; 32 | import io.jsonwebtoken.JwtBuilder; 33 | import io.jsonwebtoken.Jwts; 34 | import io.jsonwebtoken.SignatureAlgorithm; 35 | 36 | /** 37 | * @author xuexiang 38 | * @since 2020/9/1 12:01 AM 39 | */ 40 | public final class TokenUtils { 41 | 42 | private TokenUtils() { 43 | throw new UnsupportedOperationException("u can't instantiate me..."); 44 | } 45 | 46 | /** 47 | * 签名秘钥 48 | */ 49 | public static final String SECRET = "xuexiangjys"; 50 | 51 | /** 52 | * 生成token 53 | * 54 | * @param id 一般传入userName 55 | * @return 56 | */ 57 | public static String createJwtToken(String id) { 58 | String issuer = "www.github.com"; 59 | String subject = "xuexiangjys@163.com"; 60 | // 30秒有效 61 | long ttlMillis = 30 * TimeConstants.SEC; 62 | return createJwtToken(id, issuer, subject, ttlMillis); 63 | } 64 | 65 | /** 66 | * 生成Token 67 | * 68 | * @param id 编号 69 | * @param issuer 该JWT的签发者,是否使用是可选的 70 | * @param subject 该JWT所面向的用户,是否使用是可选的; 71 | * @param ttlMillis 签发时间 (有效时间,过期会报错) 72 | * @return token String 73 | */ 74 | public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) { 75 | // 签名算法 ,将对token进行签名 76 | SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; 77 | 78 | // 生成签发时间 79 | long nowMillis = System.currentTimeMillis(); 80 | Date now = new Date(nowMillis); 81 | 82 | // 通过秘钥签名JWT 83 | byte[] apiKeySecretBytes = EncodeUtils.base64Decode(SECRET); 84 | Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); 85 | 86 | // Let's set the JWT Claims 87 | JwtBuilder builder = Jwts.builder().setId(id) 88 | .setIssuedAt(now) 89 | .setSubject(subject) 90 | .setIssuer(issuer) 91 | .signWith(signatureAlgorithm, signingKey); 92 | 93 | // if it has been specified, let's add the expiration 94 | if (ttlMillis >= 0) { 95 | long expMillis = nowMillis + ttlMillis; 96 | Date exp = new Date(expMillis); 97 | builder.setExpiration(exp); 98 | } 99 | 100 | // Builds the JWT and serializes it to a compact, URL-safe string 101 | return builder.compact(); 102 | 103 | } 104 | 105 | /** 106 | * Sample method to validate and read the JWT 107 | * 108 | * @param jwt 109 | * @return 110 | */ 111 | public static Claims parseJWT(String jwt) { 112 | return Jwts.parser() 113 | .setSigningKey(EncodeUtils.base64Decode(SECRET)) 114 | .parseClaimsJws(jwt).getBody(); 115 | } 116 | 117 | 118 | /** 119 | * 从HttpRequest中解析出token 120 | * 121 | * @param request 请求 122 | * @return 请求token 123 | */ 124 | public static String parseToken(HttpRequest request) { 125 | String accessToken = request.getHeader("token"); 126 | if (StringUtils.isEmpty(accessToken)) { 127 | accessToken = request.getParameter("token"); 128 | } 129 | return accessToken; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/MyApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver; 19 | 20 | import android.app.Application; 21 | import android.content.Context; 22 | 23 | import androidx.multidex.MultiDex; 24 | 25 | import com.xuexiang.templateandserver.utils.sdkinit.UMengInit; 26 | import com.xuexiang.templateandserver.utils.sdkinit.XBasicLibInit; 27 | import com.xuexiang.xormlite.annotation.DataBase; 28 | import com.xuexiang.xormlite.enums.DataBaseType; 29 | 30 | /** 31 | * @author xuexiang 32 | * @since 2018/11/7 下午1:12 33 | */ 34 | @DataBase(name = "AndServer", type = DataBaseType.EXTERNAL, path = "/storage/emulated/0/AndServer/databases") 35 | public class MyApp extends Application { 36 | 37 | @Override 38 | protected void attachBaseContext(Context base) { 39 | super.attachBaseContext(base); 40 | //解决4.x运行崩溃的问题 41 | MultiDex.install(this); 42 | } 43 | 44 | @Override 45 | public void onCreate() { 46 | super.onCreate(); 47 | initLibs(); 48 | } 49 | 50 | /** 51 | * 初始化基础库 52 | */ 53 | private void initLibs() { 54 | XBasicLibInit.init(this); 55 | UMengInit.init(this); 56 | } 57 | 58 | 59 | /** 60 | * @return 当前app是否是调试开发模式 61 | */ 62 | public static boolean isDebug() { 63 | return BuildConfig.DEBUG; 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.activity; 19 | 20 | import android.os.Bundle; 21 | 22 | import com.xuexiang.templateandserver.core.BaseActivity; 23 | import com.xuexiang.templateandserver.fragment.MainFragment; 24 | 25 | /** 26 | * 程序入口,空壳容器 27 | * 28 | * @author xuexiang 29 | * @since 2019-07-07 23:53 30 | */ 31 | public class MainActivity extends BaseActivity { 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | openPage(MainFragment.class); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/adapter/UserManageAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.adapter; 19 | 20 | import androidx.annotation.NonNull; 21 | 22 | import com.xuexiang.server.model.User; 23 | import com.xuexiang.templateandserver.R; 24 | import com.xuexiang.xui.adapter.recyclerview.BaseRecyclerAdapter; 25 | import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder; 26 | 27 | /** 28 | * @author xuexiang 29 | * @since 2020/9/5 4:51 PM 30 | */ 31 | public class UserManageAdapter extends BaseRecyclerAdapter { 32 | 33 | private boolean mIsShowPassword; 34 | 35 | public UserManageAdapter() { 36 | this(true); 37 | } 38 | 39 | public UserManageAdapter(boolean isShowPassword) { 40 | mIsShowPassword = isShowPassword; 41 | } 42 | 43 | @Override 44 | protected int getItemLayoutId(int viewType) { 45 | return R.layout.adapter_user_list_item; 46 | } 47 | 48 | @Override 49 | protected void bindData(@NonNull RecyclerViewHolder holder, int position, User item) { 50 | if (item == null) { 51 | return; 52 | } 53 | holder.text(R.id.tv_user_id, String.valueOf(item.getId())); 54 | holder.text(R.id.tv_login_name, item.getLoginName()); 55 | holder.text(R.id.tv_password, mIsShowPassword ? item.getPassword() : "****"); 56 | holder.text(R.id.tv_user_name, item.getName()); 57 | holder.text(R.id.tv_age, String.valueOf(item.getAge())); 58 | holder.text(R.id.tv_gender, item.getGenderName()); 59 | holder.text(R.id.tv_phone, item.getPhone()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/BaseActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core; 19 | 20 | import android.content.Context; 21 | import android.os.Bundle; 22 | 23 | import com.xuexiang.rxutil2.lifecycle.RxLifecycle; 24 | import com.xuexiang.xpage.base.XPageActivity; 25 | import com.xuexiang.xpage.base.XPageFragment; 26 | import com.xuexiang.xpage.core.CoreSwitchBean; 27 | import com.xuexiang.xrouter.facade.service.SerializationService; 28 | import com.xuexiang.xrouter.launcher.XRouter; 29 | import com.xuexiang.xui.XUI; 30 | 31 | import butterknife.ButterKnife; 32 | import butterknife.Unbinder; 33 | import io.github.inflationx.viewpump.ViewPumpContextWrapper; 34 | 35 | /** 36 | * 基础容器Activity 37 | * 38 | * @author XUE 39 | * @since 2019/3/22 11:21 40 | */ 41 | public class BaseActivity extends XPageActivity { 42 | 43 | Unbinder mUnbinder; 44 | 45 | @Override 46 | protected void attachBaseContext(Context newBase) { 47 | //注入字体 48 | super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase)); 49 | } 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | XUI.initTheme(this); 54 | super.onCreate(savedInstanceState); 55 | mUnbinder = ButterKnife.bind(this); 56 | RxLifecycle.injectRxLifecycle(this); 57 | } 58 | 59 | /** 60 | * 打开fragment 61 | * 62 | * @param clazz 页面类 63 | * @param addToBackStack 是否添加到栈中 64 | * @return 打开的fragment对象 65 | */ 66 | public T openPage(Class clazz, boolean addToBackStack) { 67 | CoreSwitchBean page = new CoreSwitchBean(clazz) 68 | .setAddToBackStack(addToBackStack); 69 | return (T) openPage(page); 70 | } 71 | 72 | /** 73 | * 打开fragment 74 | * 75 | * @return 打开的fragment对象 76 | */ 77 | public T openNewPage(Class clazz) { 78 | CoreSwitchBean page = new CoreSwitchBean(clazz) 79 | .setNewActivity(true); 80 | return (T) openPage(page); 81 | } 82 | 83 | /** 84 | * 切换fragment 85 | * 86 | * @param clazz 页面类 87 | * @return 打开的fragment对象 88 | */ 89 | public T switchPage(Class clazz) { 90 | return openPage(clazz, false); 91 | } 92 | 93 | /** 94 | * 序列化对象 95 | * 96 | * @param object 97 | * @return 98 | */ 99 | public String serializeObject(Object object) { 100 | return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); 101 | } 102 | 103 | @Override 104 | protected void onRelease() { 105 | mUnbinder.unbind(); 106 | super.onRelease(); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/BaseContainerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core; 19 | 20 | import android.content.res.Configuration; 21 | import android.view.View; 22 | import android.view.ViewGroup; 23 | import android.widget.AdapterView; 24 | 25 | import com.umeng.analytics.MobclickAgent; 26 | import com.xuexiang.xaop.annotation.SingleClick; 27 | import com.xuexiang.xpage.base.XPageContainerListFragment; 28 | import com.xuexiang.xui.widget.actionbar.TitleBar; 29 | import com.xuexiang.xui.widget.actionbar.TitleUtils; 30 | 31 | import java.util.ArrayList; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | import static com.xuexiang.templateandserver.core.SimpleListAdapter.KEY_SUB_TITLE; 37 | import static com.xuexiang.templateandserver.core.SimpleListAdapter.KEY_TITLE; 38 | 39 | /** 40 | * 修改列表样式为主副标题显示 41 | * 42 | * @author xuexiang 43 | * @since 2018/11/22 上午11:26 44 | */ 45 | public abstract class BaseContainerFragment extends XPageContainerListFragment { 46 | 47 | @Override 48 | protected void initPage() { 49 | initTitle(); 50 | initViews(); 51 | initListeners(); 52 | } 53 | 54 | protected TitleBar initTitle() { 55 | return TitleUtils.addTitleBarDynamic((ViewGroup) getRootView(), getPageTitle(), new View.OnClickListener() { 56 | @Override 57 | public void onClick(View v) { 58 | popToBack(); 59 | } 60 | }); 61 | } 62 | 63 | @Override 64 | protected void initData() { 65 | mSimpleData = initSimpleData(mSimpleData); 66 | 67 | List> data = new ArrayList<>(); 68 | for (String content : mSimpleData) { 69 | Map item = new HashMap<>(); 70 | int index = content.indexOf("\n"); 71 | if (index > 0) { 72 | item.put(KEY_TITLE, String.valueOf(content.subSequence(0, index))); 73 | item.put(KEY_SUB_TITLE, String.valueOf(content.subSequence(index + 1, content.length()))); 74 | } else { 75 | item.put(KEY_TITLE, content); 76 | item.put(KEY_SUB_TITLE, ""); 77 | } 78 | data.add(item); 79 | } 80 | 81 | getListView().setAdapter(new SimpleListAdapter(getContext(), data)); 82 | initSimply(); 83 | } 84 | 85 | @Override 86 | public void onItemClick(AdapterView adapterView, View view, int position, long id) { 87 | onItemClick(view, position); 88 | } 89 | 90 | @SingleClick 91 | private void onItemClick(View view, int position) { 92 | onItemClick(position); 93 | } 94 | 95 | @Override 96 | public void onDestroyView() { 97 | getListView().setOnItemClickListener(null); 98 | super.onDestroyView(); 99 | } 100 | 101 | @Override 102 | public void onConfigurationChanged(Configuration newConfig) { 103 | //屏幕旋转时刷新一下title 104 | super.onConfigurationChanged(newConfig); 105 | ViewGroup root = (ViewGroup) getRootView(); 106 | if (root.getChildAt(0) instanceof TitleBar) { 107 | root.removeViewAt(0); 108 | initTitle(); 109 | } 110 | } 111 | 112 | @Override 113 | public void onResume() { 114 | super.onResume(); 115 | MobclickAgent.onPageStart(getPageName()); 116 | } 117 | 118 | @Override 119 | public void onPause() { 120 | super.onPause(); 121 | MobclickAgent.onPageEnd(getPageName()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/SimpleListAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core; 19 | 20 | import android.content.Context; 21 | import android.view.View; 22 | import android.widget.TextView; 23 | 24 | import com.xuexiang.templateandserver.R; 25 | import com.xuexiang.xui.adapter.listview.BaseListAdapter; 26 | import com.xuexiang.xutil.common.StringUtils; 27 | 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * 主副标题显示适配器 33 | * 34 | * @author xuexiang 35 | * @since 2018/12/19 上午12:19 36 | */ 37 | public class SimpleListAdapter extends BaseListAdapter, SimpleListAdapter.ViewHolder> { 38 | 39 | public static final String KEY_TITLE = "title"; 40 | public static final String KEY_SUB_TITLE = "sub_title"; 41 | 42 | public SimpleListAdapter(Context context, List> data) { 43 | super(context, data); 44 | } 45 | 46 | @Override 47 | protected ViewHolder newViewHolder(View convertView) { 48 | ViewHolder holder = new ViewHolder(); 49 | holder.mTvTitle = convertView.findViewById(R.id.tv_title); 50 | holder.mTvSubTitle = convertView.findViewById(R.id.tv_sub_title); 51 | return holder; 52 | } 53 | 54 | @Override 55 | protected int getLayoutId() { 56 | return R.layout.adapter_item_simple_list_2; 57 | } 58 | 59 | @Override 60 | protected void convert(ViewHolder holder, Map item, int position) { 61 | holder.mTvTitle.setText(item.get(KEY_TITLE)); 62 | if (!StringUtils.isEmpty(item.get(KEY_SUB_TITLE))) { 63 | holder.mTvSubTitle.setText(item.get(KEY_SUB_TITLE)); 64 | holder.mTvSubTitle.setVisibility(View.VISIBLE); 65 | } else { 66 | holder.mTvSubTitle.setVisibility(View.GONE); 67 | } 68 | } 69 | 70 | public static class ViewHolder { 71 | /** 72 | * 标题 73 | */ 74 | public TextView mTvTitle; 75 | /** 76 | * 副标题 77 | */ 78 | public TextView mTvSubTitle; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/TestApi.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http; 19 | 20 | import com.xuexiang.server.model.User; 21 | import com.xuexiang.xhttp2.annotation.NetMethod; 22 | import com.xuexiang.xhttp2.model.ApiResult; 23 | 24 | import io.reactivex.Observable; 25 | import retrofit2.http.Body; 26 | import retrofit2.http.Headers; 27 | import retrofit2.http.POST; 28 | 29 | /** 30 | * @author xuexiang 31 | * @since 2020/9/5 11:19 PM 32 | */ 33 | public final class TestApi { 34 | 35 | 36 | public interface UserService { 37 | 38 | /** 39 | * 这里使用的是retrofit的接口定义 40 | */ 41 | @POST("/user/registerUser/") 42 | @Headers({"Content-Type: application/json", "Accept: application/json"}) 43 | Observable> register(@Body User user); 44 | 45 | /** 46 | * 这里使用的是retrofit的接口定义 47 | */ 48 | @POST("/user/editUser/") 49 | @Headers({"Content-Type: application/json", "Accept: application/json"}) 50 | Observable> editUser(@Body User user); 51 | 52 | /** 53 | * 这里使用的是自定义的接口定义 54 | * 55 | * @param userId 用户id 56 | */ 57 | @NetMethod(parameterNames = {"Id"}, url = "/user/deleteUser/", accessToken = false) 58 | Observable deleteUser(long userId); 59 | 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/callback/NoTipCallBack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.callback; 19 | 20 | import com.xuexiang.xhttp2.callback.SimpleCallBack; 21 | import com.xuexiang.xhttp2.exception.ApiException; 22 | import com.xuexiang.xhttp2.model.XHttpRequest; 23 | import com.xuexiang.xutil.common.StringUtils; 24 | import com.xuexiang.xutil.common.logger.Logger; 25 | 26 | /** 27 | * 不带错误提示的网络请求回调 28 | * 29 | * @author xuexiang 30 | * @since 2019-11-18 23:02 31 | */ 32 | public abstract class NoTipCallBack extends SimpleCallBack { 33 | 34 | /** 35 | * 记录一下请求的url,确定出错的请求是哪个请求 36 | */ 37 | private String mUrl; 38 | 39 | public NoTipCallBack() { 40 | 41 | } 42 | 43 | public NoTipCallBack(XHttpRequest req) { 44 | this(req.getUrl()); 45 | } 46 | 47 | public NoTipCallBack(String url) { 48 | mUrl = url; 49 | } 50 | 51 | @Override 52 | public void onError(ApiException e) { 53 | if (!StringUtils.isEmpty(mUrl)) { 54 | Logger.e("网络请求的url:" + mUrl, e); 55 | } else { 56 | Logger.e(e); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/callback/TipCallBack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.callback; 19 | 20 | import com.xuexiang.templateandserver.utils.XToastUtils; 21 | import com.xuexiang.xhttp2.callback.SimpleCallBack; 22 | import com.xuexiang.xhttp2.exception.ApiException; 23 | import com.xuexiang.xhttp2.model.XHttpRequest; 24 | import com.xuexiang.xutil.common.StringUtils; 25 | import com.xuexiang.xutil.common.logger.Logger; 26 | 27 | /** 28 | * 带错误toast提示的网络请求回调 29 | * 30 | * @author xuexiang 31 | * @since 2019-11-18 23:02 32 | */ 33 | public abstract class TipCallBack extends SimpleCallBack { 34 | 35 | /** 36 | * 记录一下请求的url,确定出错的请求是哪个请求 37 | */ 38 | private String mUrl; 39 | 40 | public TipCallBack() { 41 | 42 | } 43 | 44 | public TipCallBack(XHttpRequest req) { 45 | this(req.getUrl()); 46 | } 47 | 48 | public TipCallBack(String url) { 49 | mUrl = url; 50 | } 51 | 52 | @Override 53 | public void onError(ApiException e) { 54 | XToastUtils.error(e); 55 | if (!StringUtils.isEmpty(mUrl)) { 56 | Logger.e("网络请求的url:" + mUrl, e); 57 | } else { 58 | Logger.e(e); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/callback/TipProgressLoadingCallBack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.callback; 19 | 20 | import androidx.annotation.NonNull; 21 | 22 | import com.xuexiang.templateandserver.core.BaseFragment; 23 | import com.xuexiang.templateandserver.utils.XToastUtils; 24 | import com.xuexiang.xhttp2.callback.ProgressLoadingCallBack; 25 | import com.xuexiang.xhttp2.exception.ApiException; 26 | import com.xuexiang.xhttp2.model.XHttpRequest; 27 | import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; 28 | import com.xuexiang.xutil.common.StringUtils; 29 | import com.xuexiang.xutil.common.logger.Logger; 30 | 31 | /** 32 | * 带错误toast提示和加载进度条的网络请求回调 33 | * 34 | * @author xuexiang 35 | * @since 2019-11-18 23:16 36 | */ 37 | public abstract class TipProgressLoadingCallBack extends ProgressLoadingCallBack { 38 | /** 39 | * 记录一下请求的url,确定出错的请求是哪个请求 40 | */ 41 | private String mUrl; 42 | 43 | public TipProgressLoadingCallBack(BaseFragment fragment) { 44 | super(fragment.getProgressLoader()); 45 | } 46 | 47 | public TipProgressLoadingCallBack(IProgressLoader iProgressLoader) { 48 | super(iProgressLoader); 49 | } 50 | 51 | public TipProgressLoadingCallBack(@NonNull XHttpRequest req, IProgressLoader iProgressLoader) { 52 | this(req.getUrl(), iProgressLoader); 53 | } 54 | 55 | public TipProgressLoadingCallBack(String url, IProgressLoader iProgressLoader) { 56 | super(iProgressLoader); 57 | mUrl = url; 58 | } 59 | 60 | @Override 61 | public void onError(ApiException e) { 62 | super.onError(e); 63 | XToastUtils.error(e); 64 | if (!StringUtils.isEmpty(mUrl)) { 65 | Logger.e("网络请求的url:" + mUrl, e); 66 | } else { 67 | Logger.e(e); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/loader/IProgressLoaderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.loader; 19 | 20 | import android.content.Context; 21 | 22 | import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; 23 | 24 | /** 25 | * IProgressLoader的创建工厂实现接口 26 | * 27 | * @author xuexiang 28 | * @since 2019-11-18 23:17 29 | */ 30 | public interface IProgressLoaderFactory { 31 | 32 | 33 | /** 34 | * 创建进度加载者 35 | * 36 | * @param context 37 | * @return 38 | */ 39 | IProgressLoader create(Context context); 40 | 41 | 42 | /** 43 | * 创建进度加载者 44 | * 45 | * @param context 46 | * @param message 默认提示 47 | * @return 48 | */ 49 | IProgressLoader create(Context context, String message); 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/loader/MiniLoadingDialogLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.loader; 19 | 20 | import android.content.Context; 21 | import android.content.DialogInterface; 22 | 23 | import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; 24 | import com.xuexiang.xhttp2.subsciber.impl.OnProgressCancelListener; 25 | import com.xuexiang.xui.widget.dialog.MiniLoadingDialog; 26 | 27 | /** 28 | * 默认进度加载 29 | * 30 | * @author xuexiang 31 | * @since 2019-11-18 23:07 32 | */ 33 | public class MiniLoadingDialogLoader implements IProgressLoader { 34 | /** 35 | * 进度loading弹窗 36 | */ 37 | private MiniLoadingDialog mDialog; 38 | /** 39 | * 进度框取消监听 40 | */ 41 | private OnProgressCancelListener mOnProgressCancelListener; 42 | 43 | public MiniLoadingDialogLoader(Context context) { 44 | this(context, "请求中..."); 45 | } 46 | 47 | public MiniLoadingDialogLoader(Context context, String msg) { 48 | mDialog = new MiniLoadingDialog(context, msg); 49 | } 50 | 51 | @Override 52 | public boolean isLoading() { 53 | return mDialog != null && mDialog.isShowing(); 54 | } 55 | 56 | @Override 57 | public void updateMessage(String msg) { 58 | if (mDialog != null) { 59 | mDialog.updateMessage(msg); 60 | } 61 | } 62 | 63 | @Override 64 | public void showLoading() { 65 | if (mDialog != null && !mDialog.isShowing()) { 66 | mDialog.show(); 67 | } 68 | } 69 | 70 | @Override 71 | public void dismissLoading() { 72 | if (mDialog != null && mDialog.isShowing()) { 73 | mDialog.dismiss(); 74 | } 75 | } 76 | 77 | @Override 78 | public void setCancelable(boolean flag) { 79 | mDialog.setCancelable(flag); 80 | if (flag) { 81 | mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { 82 | @Override 83 | public void onCancel(DialogInterface dialogInterface) { 84 | if (mOnProgressCancelListener != null) { 85 | mOnProgressCancelListener.onCancelProgress(); 86 | } 87 | } 88 | }); 89 | } 90 | } 91 | 92 | @Override 93 | public void setOnProgressCancelListener(OnProgressCancelListener listener) { 94 | mOnProgressCancelListener = listener; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/loader/MiniProgressLoaderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.loader; 19 | 20 | import android.content.Context; 21 | 22 | import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; 23 | 24 | /** 25 | * 迷你加载框创建工厂 26 | * 27 | * @author xuexiang 28 | * @since 2019-11-18 23:23 29 | */ 30 | public class MiniProgressLoaderFactory implements IProgressLoaderFactory { 31 | 32 | @Override 33 | public IProgressLoader create(Context context) { 34 | return new MiniLoadingDialogLoader(context); 35 | } 36 | 37 | @Override 38 | public IProgressLoader create(Context context, String message) { 39 | return new MiniLoadingDialogLoader(context, message); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/loader/ProgressLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.loader; 19 | 20 | import android.content.Context; 21 | 22 | import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; 23 | 24 | /** 25 | * 创建进度加载者 26 | * 27 | * @author xuexiang 28 | * @since 2019-07-02 12:51 29 | */ 30 | public final class ProgressLoader { 31 | 32 | private ProgressLoader() { 33 | throw new UnsupportedOperationException("u can't instantiate me..."); 34 | } 35 | 36 | private static IProgressLoaderFactory sIProgressLoaderFactory = new MiniProgressLoaderFactory(); 37 | 38 | public static void setIProgressLoaderFactory(IProgressLoaderFactory sIProgressLoaderFactory) { 39 | ProgressLoader.sIProgressLoaderFactory = sIProgressLoaderFactory; 40 | } 41 | 42 | /** 43 | * 创建进度加载者 44 | * 45 | * @param context 46 | * @return 47 | */ 48 | public static IProgressLoader create(Context context) { 49 | return sIProgressLoaderFactory.create(context); 50 | } 51 | 52 | /** 53 | * 创建进度加载者 54 | * 55 | * @param context 56 | * @param message 默认提示信息 57 | * @return 58 | */ 59 | public static IProgressLoader create(Context context, String message) { 60 | return sIProgressLoaderFactory.create(context, message); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/subscriber/NoTipRequestSubscriber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.subscriber; 19 | 20 | import com.xuexiang.xhttp2.exception.ApiException; 21 | import com.xuexiang.xhttp2.model.XHttpRequest; 22 | import com.xuexiang.xhttp2.subsciber.BaseSubscriber; 23 | import com.xuexiang.xutil.common.StringUtils; 24 | import com.xuexiang.xutil.common.logger.Logger; 25 | 26 | /** 27 | * 不带错误toast提示的网络请求订阅,只存储错误的日志 28 | * 29 | * @author xuexiang 30 | * @since 2019-11-18 23:11 31 | */ 32 | public abstract class NoTipRequestSubscriber extends BaseSubscriber { 33 | 34 | /** 35 | * 记录一下请求的url,确定出错的请求是哪个请求 36 | */ 37 | private String mUrl; 38 | 39 | public NoTipRequestSubscriber() { 40 | 41 | } 42 | 43 | public NoTipRequestSubscriber(XHttpRequest req) { 44 | this(req.getUrl()); 45 | } 46 | 47 | public NoTipRequestSubscriber(String url) { 48 | mUrl = url; 49 | } 50 | 51 | @Override 52 | public void onError(ApiException e) { 53 | if (!StringUtils.isEmpty(mUrl)) { 54 | Logger.e("网络请求的url:" + mUrl, e); 55 | } else { 56 | Logger.e(e); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/subscriber/TipProgressLoadingSubscriber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.subscriber; 19 | 20 | import androidx.annotation.NonNull; 21 | 22 | import com.xuexiang.templateandserver.core.BaseFragment; 23 | import com.xuexiang.templateandserver.utils.XToastUtils; 24 | import com.xuexiang.xhttp2.exception.ApiException; 25 | import com.xuexiang.xhttp2.model.XHttpRequest; 26 | import com.xuexiang.xhttp2.subsciber.ProgressLoadingSubscriber; 27 | import com.xuexiang.xhttp2.subsciber.impl.IProgressLoader; 28 | import com.xuexiang.xutil.common.StringUtils; 29 | import com.xuexiang.xutil.common.logger.Logger; 30 | 31 | /** 32 | * 带错误toast提示和加载进度条的网络请求订阅 33 | * 34 | * @author xuexiang 35 | * @since 2019-11-18 23:11 36 | */ 37 | public abstract class TipProgressLoadingSubscriber extends ProgressLoadingSubscriber { 38 | 39 | /** 40 | * 记录一下请求的url,确定出错的请求是哪个请求 41 | */ 42 | private String mUrl; 43 | 44 | public TipProgressLoadingSubscriber() { 45 | super(); 46 | } 47 | 48 | public TipProgressLoadingSubscriber(BaseFragment fragment) { 49 | super(fragment.getProgressLoader()); 50 | } 51 | 52 | public TipProgressLoadingSubscriber(IProgressLoader iProgressLoader) { 53 | super(iProgressLoader); 54 | } 55 | 56 | public TipProgressLoadingSubscriber(@NonNull XHttpRequest req) { 57 | this(req.getUrl()); 58 | } 59 | 60 | public TipProgressLoadingSubscriber(String url) { 61 | super(); 62 | mUrl = url; 63 | } 64 | 65 | @Override 66 | public void onError(ApiException e) { 67 | super.onError(e); 68 | XToastUtils.error(e); 69 | if (!StringUtils.isEmpty(mUrl)) { 70 | Logger.e("网络请求的url:" + mUrl, e); 71 | } else { 72 | Logger.e(e); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/core/http/subscriber/TipRequestSubscriber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.core.http.subscriber; 19 | 20 | 21 | import androidx.annotation.NonNull; 22 | 23 | import com.xuexiang.templateandserver.utils.XToastUtils; 24 | import com.xuexiang.xhttp2.exception.ApiException; 25 | import com.xuexiang.xhttp2.model.XHttpRequest; 26 | import com.xuexiang.xhttp2.subsciber.BaseSubscriber; 27 | import com.xuexiang.xutil.common.StringUtils; 28 | import com.xuexiang.xutil.common.logger.Logger; 29 | 30 | /** 31 | * 带错误toast提示的网络请求订阅 32 | * 33 | * @author xuexiang 34 | * @since 2019-11-18 23:10 35 | */ 36 | public abstract class TipRequestSubscriber extends BaseSubscriber { 37 | /** 38 | * 记录一下请求的url,确定出错的请求是哪个请求 39 | */ 40 | private String mUrl; 41 | 42 | public TipRequestSubscriber() { 43 | 44 | } 45 | 46 | public TipRequestSubscriber(@NonNull XHttpRequest req) { 47 | this(req.getUrl()); 48 | } 49 | 50 | public TipRequestSubscriber(String url) { 51 | mUrl = url; 52 | } 53 | 54 | 55 | @Override 56 | public void onError(ApiException e) { 57 | XToastUtils.error(e); 58 | if (!StringUtils.isEmpty(mUrl)) { 59 | Logger.e("网络请求的url:" + mUrl, e); 60 | } else { 61 | Logger.e(e); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/db/ExternalDataBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.templateandserver.db; 18 | 19 | import android.database.sqlite.SQLiteDatabase; 20 | 21 | import com.j256.ormlite.support.ConnectionSource; 22 | import com.xuexiang.xormlite.db.IExternalDataBase; 23 | import com.xuexiang.xormlite.logs.DBLog; 24 | import com.xuexiang.xutil.file.FileUtils; 25 | 26 | /** 27 | *外部存储的数据库 实现接口 28 | * 29 | * @author xuexiang 30 | * @since 2020/9/6 2:18 AM 31 | */ 32 | public class ExternalDataBase extends InternalDataBase implements IExternalDataBase { 33 | 34 | private int mDatabaseVersion; 35 | private String mDBPath; 36 | private String mDBName; 37 | 38 | public ExternalDataBase(String dbPath, String dbName, int databaseVersion) { 39 | mDBPath = dbPath; 40 | mDBName = dbName; 41 | mDatabaseVersion = databaseVersion; 42 | } 43 | 44 | /** 45 | * 创建或者打开数据库 46 | * 47 | * @param connectionSource 48 | */ 49 | @Override 50 | public void createOrOpenDB(ConnectionSource connectionSource) { 51 | String dbFilePath = FileUtils.getFilePath(mDBPath, mDBName); 52 | if (FileUtils.createOrExistsFile(dbFilePath)) { 53 | SQLiteDatabase db = null; 54 | try { 55 | db = SQLiteDatabase.openOrCreateDatabase(dbFilePath, null); 56 | } catch (Exception e) { 57 | DBLog.e(e); 58 | } 59 | if (db != null) { 60 | int oldVersionCode = db.getVersion(); 61 | //版本不一致需要操作 62 | if (oldVersionCode != mDatabaseVersion) { 63 | if (oldVersionCode == 0) { 64 | onCreate(db, connectionSource); 65 | } else { 66 | if (oldVersionCode < mDatabaseVersion) { 67 | onUpgrade(db, connectionSource, oldVersionCode, mDatabaseVersion); 68 | } 69 | } 70 | } 71 | db.setVersion(mDatabaseVersion); 72 | } 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/db/InternalDataBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.templateandserver.db; 18 | 19 | import android.database.sqlite.SQLiteDatabase; 20 | 21 | import com.j256.ormlite.support.ConnectionSource; 22 | import com.xuexiang.xormlite.AppDataBaseTable; 23 | import com.xuexiang.xormlite.db.DataBaseUtils; 24 | import com.xuexiang.xormlite.db.IDatabase; 25 | import com.xuexiang.xormlite.logs.DBLog; 26 | 27 | import java.sql.SQLException; 28 | 29 | /** 30 | * 应用内部数据库 实现接口 31 | * 32 | * @author xuexiang 33 | * @since 2020/8/31 12:30 AM 34 | */ 35 | public class InternalDataBase implements IDatabase { 36 | /** 37 | * 数据库创建 38 | * 39 | * @param database SQLite数据库 40 | * @param connectionSource 数据库连接 41 | */ 42 | @Override 43 | public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { 44 | try { 45 | DataBaseUtils.createTablesByClassNames(connectionSource, AppDataBaseTable.getTables()); 46 | } catch (SQLException e) { 47 | DBLog.e(e); 48 | } 49 | } 50 | 51 | /** 52 | * 数据库升级和降级操作 53 | * 54 | * @param database SQLite数据库 55 | * @param connectionSource 数据库连接 56 | * @param oldVersion 旧版本 57 | * @param newVersion 新版本 58 | */ 59 | @Override 60 | public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { 61 | DBLog.i("数据库旧版本:" + oldVersion); 62 | DBLog.i("数据库新版本:" + newVersion); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/fragment/manage/EditUserFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.fragment.manage; 19 | 20 | import android.view.View; 21 | 22 | import com.xuexiang.server.model.User; 23 | import com.xuexiang.templateandserver.R; 24 | import com.xuexiang.templateandserver.core.BaseFragment; 25 | import com.xuexiang.templateandserver.utils.XToastUtils; 26 | import com.xuexiang.xaop.annotation.SingleClick; 27 | import com.xuexiang.xormlite.AndServerDataBaseRepository; 28 | import com.xuexiang.xormlite.db.DBService; 29 | import com.xuexiang.xpage.annotation.Page; 30 | import com.xuexiang.xrouter.annotation.AutoWired; 31 | import com.xuexiang.xrouter.launcher.XRouter; 32 | import com.xuexiang.xui.widget.actionbar.TitleBar; 33 | import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText; 34 | import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner; 35 | import com.xuexiang.xutil.common.StringUtils; 36 | 37 | import java.sql.SQLException; 38 | 39 | import butterknife.BindView; 40 | 41 | import static android.app.Activity.RESULT_OK; 42 | 43 | /** 44 | * @author xuexiang 45 | * @since 2020/9/5 6:31 PM 46 | */ 47 | @Page 48 | public class EditUserFragment extends BaseFragment { 49 | 50 | @AutoWired 51 | User user; 52 | 53 | @BindView(R.id.et_login_name) 54 | MaterialEditText etLoginName; 55 | @BindView(R.id.et_password) 56 | MaterialEditText etPassword; 57 | @BindView(R.id.et_name) 58 | MaterialEditText etName; 59 | @BindView(R.id.et_age) 60 | MaterialEditText etAge; 61 | @BindView(R.id.ms_gender) 62 | MaterialSpinner msGender; 63 | @BindView(R.id.et_phone) 64 | MaterialEditText etPhone; 65 | 66 | @Override 67 | protected int getLayoutId() { 68 | return R.layout.fragment_user_info_manage; 69 | } 70 | 71 | @Override 72 | protected void initArgs() { 73 | XRouter.getInstance().inject(this); 74 | } 75 | 76 | @Override 77 | protected TitleBar initTitle() { 78 | TitleBar titleBar = super.initTitle() 79 | .setTitle("账户编辑"); 80 | titleBar.addAction(new TitleBar.TextAction("保存") { 81 | @SingleClick 82 | @Override 83 | public void performAction(View view) { 84 | handleSubmit(); 85 | } 86 | }); 87 | return titleBar; 88 | } 89 | 90 | private void handleSubmit() { 91 | if (!validateInput()) { 92 | return; 93 | } 94 | 95 | user.setLoginName(etLoginName.getEditValue()) 96 | .setPassword(etPassword.getEditValue()) 97 | .setName(etName.getEditValue()) 98 | .setAge(StringUtils.toInt(etAge.getEditValue())) 99 | .setGender(msGender.getSelectedIndex()) 100 | .setPhone(etPhone.getEditValue()); 101 | 102 | if (saveUser(user)) { 103 | XToastUtils.success("编辑成功"); 104 | setFragmentResult(RESULT_OK, null); 105 | popToBack(); 106 | } else { 107 | XToastUtils.success("编辑失败"); 108 | } 109 | } 110 | 111 | @Override 112 | protected void initViews() { 113 | etLoginName.setText(StringUtils.getString(user.getLoginName())); 114 | etPassword.setText(StringUtils.getString(user.getPassword())); 115 | etName.setText(StringUtils.getString(user.getName())); 116 | etAge.setText(String.valueOf(user.getAge())); 117 | msGender.setSelectedIndex(user.getGender()); 118 | etPhone.setText(StringUtils.getString(user.getPhone())); 119 | } 120 | 121 | 122 | private boolean validateInput() { 123 | return etLoginName.validate() && etPassword.validate() && etAge.validate() && etPhone.validate(); 124 | } 125 | 126 | public boolean saveUser(User user) { 127 | DBService dbService = AndServerDataBaseRepository.getInstance().getDataBase(User.class); 128 | try { 129 | return dbService.updateData(user) > 0; 130 | } catch (SQLException e) { 131 | e.printStackTrace(); 132 | } 133 | return false; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/fragment/manage/RegisterFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.fragment.manage; 19 | 20 | import android.view.View; 21 | 22 | import com.xuexiang.server.model.User; 23 | import com.xuexiang.templateandserver.R; 24 | import com.xuexiang.templateandserver.core.BaseFragment; 25 | import com.xuexiang.templateandserver.utils.XToastUtils; 26 | import com.xuexiang.xaop.annotation.SingleClick; 27 | import com.xuexiang.xormlite.AndServerDataBaseRepository; 28 | import com.xuexiang.xormlite.db.DBService; 29 | import com.xuexiang.xpage.annotation.Page; 30 | import com.xuexiang.xui.widget.actionbar.TitleBar; 31 | import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText; 32 | import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner; 33 | import com.xuexiang.xutil.common.StringUtils; 34 | 35 | import java.sql.SQLException; 36 | 37 | import butterknife.BindView; 38 | 39 | import static android.app.Activity.RESULT_OK; 40 | 41 | /** 42 | * @author xuexiang 43 | * @since 2020/9/5 5:11 PM 44 | */ 45 | @Page 46 | public class RegisterFragment extends BaseFragment { 47 | 48 | @BindView(R.id.et_login_name) 49 | MaterialEditText etLoginName; 50 | @BindView(R.id.et_password) 51 | MaterialEditText etPassword; 52 | @BindView(R.id.et_name) 53 | MaterialEditText etName; 54 | @BindView(R.id.et_age) 55 | MaterialEditText etAge; 56 | @BindView(R.id.ms_gender) 57 | MaterialSpinner msGender; 58 | @BindView(R.id.et_phone) 59 | MaterialEditText etPhone; 60 | 61 | @Override 62 | protected int getLayoutId() { 63 | return R.layout.fragment_user_info_manage; 64 | } 65 | 66 | @Override 67 | protected TitleBar initTitle() { 68 | TitleBar titleBar = super.initTitle() 69 | .setTitle("账户注册"); 70 | titleBar.addAction(new TitleBar.TextAction("提交") { 71 | @SingleClick 72 | @Override 73 | public void performAction(View view) { 74 | handleSubmit(); 75 | } 76 | }); 77 | return titleBar; 78 | } 79 | 80 | private void handleSubmit() { 81 | if (!validateInput()) { 82 | return; 83 | } 84 | 85 | User user = new User().setLoginName(etLoginName.getEditValue()) 86 | .setPassword(etPassword.getEditValue()) 87 | .setName(etName.getEditValue()) 88 | .setAge(StringUtils.toInt(etAge.getEditValue())) 89 | .setGender(msGender.getSelectedIndex()) 90 | .setPhone(etPhone.getEditValue()); 91 | 92 | 93 | if (saveUser(user)) { 94 | XToastUtils.success("注册成功"); 95 | setFragmentResult(RESULT_OK, null); 96 | popToBack(); 97 | } else { 98 | XToastUtils.success("注册失败"); 99 | } 100 | } 101 | 102 | @Override 103 | protected void initViews() { 104 | 105 | } 106 | 107 | private boolean validateInput() { 108 | return etLoginName.validate() && etPassword.validate() && etAge.validate() && etPhone.validate(); 109 | } 110 | 111 | public boolean saveUser(User user) { 112 | DBService dbService = AndServerDataBaseRepository.getInstance().getDataBase(User.class); 113 | try { 114 | return dbService.insert(user) > 0; 115 | } catch (SQLException e) { 116 | e.printStackTrace(); 117 | } 118 | return false; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/fragment/manage/ServerManageFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.fragment.manage; 19 | 20 | import android.content.Intent; 21 | import android.view.View; 22 | 23 | import androidx.recyclerview.widget.RecyclerView; 24 | 25 | import com.scwang.smartrefresh.layout.SmartRefreshLayout; 26 | import com.xuexiang.server.model.User; 27 | import com.xuexiang.templateandserver.R; 28 | import com.xuexiang.templateandserver.adapter.UserManageAdapter; 29 | import com.xuexiang.templateandserver.core.BaseFragment; 30 | import com.xuexiang.xaop.annotation.SingleClick; 31 | import com.xuexiang.xormlite.AndServerDataBaseRepository; 32 | import com.xuexiang.xormlite.db.DBService; 33 | import com.xuexiang.xpage.annotation.Page; 34 | import com.xuexiang.xui.utils.WidgetUtils; 35 | import com.xuexiang.xui.widget.actionbar.TitleBar; 36 | import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog; 37 | import com.xuexiang.xutil.common.CollectionUtils; 38 | 39 | import java.sql.SQLException; 40 | import java.util.List; 41 | 42 | import butterknife.BindView; 43 | 44 | import static android.app.Activity.RESULT_OK; 45 | 46 | /** 47 | * @author xuexiang 48 | * @since 2020/9/5 4:02 PM 49 | */ 50 | @Page(name = "数据管理后台") 51 | public class ServerManageFragment extends BaseFragment { 52 | 53 | public static final int REQUEST_REGISTER = 100; 54 | public static final int REQUEST_EDIT = 101; 55 | 56 | @BindView(R.id.recyclerView) 57 | RecyclerView recyclerView; 58 | @BindView(R.id.refreshLayout) 59 | SmartRefreshLayout refreshLayout; 60 | 61 | private DBService mDBService; 62 | private UserManageAdapter mAdapter; 63 | 64 | private int mPageIndex = 0; 65 | 66 | @Override 67 | protected int getLayoutId() { 68 | return R.layout.fragment_server_manage; 69 | } 70 | 71 | @Override 72 | protected TitleBar initTitle() { 73 | TitleBar titleBar = super.initTitle(); 74 | titleBar.addAction(new TitleBar.TextAction("新增") { 75 | @SingleClick 76 | @Override 77 | public void performAction(View view) { 78 | openPageForResult(RegisterFragment.class, REQUEST_REGISTER); 79 | } 80 | }); 81 | return titleBar; 82 | } 83 | 84 | @Override 85 | protected void initArgs() { 86 | mDBService = AndServerDataBaseRepository.getInstance().getDataBase(User.class); 87 | } 88 | 89 | @Override 90 | protected void initViews() { 91 | WidgetUtils.initRecyclerView(recyclerView, 0); 92 | recyclerView.setAdapter(mAdapter = new UserManageAdapter()); 93 | } 94 | 95 | @Override 96 | protected void initListeners() { 97 | //下拉刷新 98 | refreshLayout.setOnRefreshListener(refreshLayout -> refreshLayout.getLayout().postDelayed(() -> { 99 | mAdapter.refresh(pageQuery(mPageIndex = 0)); 100 | refreshLayout.finishRefresh(); 101 | }, 500)); 102 | //上拉加载 103 | refreshLayout.setOnLoadMoreListener(refreshLayout -> refreshLayout.getLayout().postDelayed(() -> { 104 | mPageIndex++; 105 | List userList = pageQuery(mPageIndex); 106 | if (CollectionUtils.isNotEmpty(userList)) { 107 | mAdapter.loadMore(userList); 108 | } else { 109 | mPageIndex--; 110 | } 111 | refreshLayout.finishLoadMore(); 112 | }, 500)); 113 | //第一次进刷新 114 | autoRefresh(); 115 | 116 | mAdapter.setOnItemLongClickListener((itemView, item, position) -> showMenuDialog(item)); 117 | } 118 | 119 | private void showMenuDialog(User item) { 120 | new MaterialDialog.Builder(getContext()) 121 | .title(R.string.tip_options) 122 | .items(R.array.menu_values) 123 | .itemsCallback((dialog, itemView, position, text) -> { 124 | if (position == 0) { 125 | editUser(item); 126 | } else { 127 | deleteUser(item); 128 | } 129 | }) 130 | .show(); 131 | } 132 | 133 | 134 | private void editUser(User item) { 135 | openPageForResult(EditUserFragment.class, "user", item, REQUEST_EDIT); 136 | 137 | } 138 | 139 | private void deleteUser(User item) { 140 | try { 141 | boolean result = mDBService.deleteData(item) > 0; 142 | if (result) { 143 | autoRefresh(); 144 | } 145 | } catch (SQLException e) { 146 | e.printStackTrace(); 147 | } 148 | } 149 | 150 | private void autoRefresh() { 151 | if (refreshLayout != null) { 152 | refreshLayout.autoRefresh(); 153 | } 154 | } 155 | 156 | @Override 157 | public void onFragmentResult(int requestCode, int resultCode, Intent data) { 158 | super.onFragmentResult(requestCode, resultCode, data); 159 | if (resultCode == RESULT_OK) { 160 | autoRefresh(); 161 | } 162 | } 163 | 164 | /** 165 | * 分页查询 166 | * 167 | * @param pageIndex 页面索引 168 | * @return 169 | */ 170 | private List pageQuery(int pageIndex) { 171 | try { 172 | return mDBService.queryPage(pageIndex, 10, "Id", true); 173 | } catch (SQLException e) { 174 | e.printStackTrace(); 175 | } 176 | return null; 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/fragment/net/EditUserTestFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.fragment.net; 19 | 20 | import android.view.View; 21 | 22 | import com.xuexiang.server.model.User; 23 | import com.xuexiang.templateandserver.R; 24 | import com.xuexiang.templateandserver.core.BaseFragment; 25 | import com.xuexiang.templateandserver.core.http.TestApi; 26 | import com.xuexiang.templateandserver.core.http.callback.TipProgressLoadingCallBack; 27 | import com.xuexiang.templateandserver.utils.XToastUtils; 28 | import com.xuexiang.xaop.annotation.SingleClick; 29 | import com.xuexiang.xhttp2.XHttp; 30 | import com.xuexiang.xhttp2.request.CustomRequest; 31 | import com.xuexiang.xpage.annotation.Page; 32 | import com.xuexiang.xrouter.annotation.AutoWired; 33 | import com.xuexiang.xrouter.launcher.XRouter; 34 | import com.xuexiang.xui.widget.actionbar.TitleBar; 35 | import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText; 36 | import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner; 37 | import com.xuexiang.xutil.common.StringUtils; 38 | 39 | import butterknife.BindView; 40 | 41 | import static android.app.Activity.RESULT_OK; 42 | 43 | /** 44 | * @author xuexiang 45 | * @since 2020/9/5 6:31 PM 46 | */ 47 | @Page 48 | public class EditUserTestFragment extends BaseFragment { 49 | 50 | @AutoWired 51 | User user; 52 | 53 | @BindView(R.id.et_login_name) 54 | MaterialEditText etLoginName; 55 | @BindView(R.id.et_password) 56 | MaterialEditText etPassword; 57 | @BindView(R.id.et_name) 58 | MaterialEditText etName; 59 | @BindView(R.id.et_age) 60 | MaterialEditText etAge; 61 | @BindView(R.id.ms_gender) 62 | MaterialSpinner msGender; 63 | @BindView(R.id.et_phone) 64 | MaterialEditText etPhone; 65 | 66 | @Override 67 | protected int getLayoutId() { 68 | return R.layout.fragment_user_info_manage; 69 | } 70 | 71 | @Override 72 | protected void initArgs() { 73 | XRouter.getInstance().inject(this); 74 | } 75 | 76 | @Override 77 | protected TitleBar initTitle() { 78 | TitleBar titleBar = super.initTitle() 79 | .setTitle("账户编辑"); 80 | titleBar.addAction(new TitleBar.TextAction("保存") { 81 | @SingleClick 82 | @Override 83 | public void performAction(View view) { 84 | handleSubmit(); 85 | } 86 | }); 87 | return titleBar; 88 | } 89 | 90 | private void handleSubmit() { 91 | if (!validateInput()) { 92 | return; 93 | } 94 | 95 | user.setName(etName.getEditValue()) 96 | .setAge(StringUtils.toInt(etAge.getEditValue())) 97 | .setGender(msGender.getSelectedIndex()) 98 | .setPhone(etPhone.getEditValue()); 99 | 100 | CustomRequest request = XHttp.custom(); 101 | request.apiCall(request.create(TestApi.UserService.class).editUser(user), new TipProgressLoadingCallBack(this) { 102 | @Override 103 | public void onSuccess(Boolean response) throws Throwable { 104 | XToastUtils.success("编辑成功"); 105 | setFragmentResult(RESULT_OK, null); 106 | popToBack(); 107 | } 108 | }); 109 | } 110 | 111 | @Override 112 | protected void initViews() { 113 | etLoginName.setText(StringUtils.getString(user.getLoginName())); 114 | etPassword.setText(StringUtils.getString(user.getPassword())); 115 | etName.setText(StringUtils.getString(user.getName())); 116 | etAge.setText(String.valueOf(user.getAge())); 117 | msGender.setSelectedIndex(user.getGender()); 118 | etPhone.setText(StringUtils.getString(user.getPhone())); 119 | 120 | etLoginName.setEnabled(false); 121 | etPassword.setEnabled(false); 122 | } 123 | 124 | 125 | private boolean validateInput() { 126 | return etAge.validate() && etPhone.validate(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/fragment/net/FileUploadFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.fragment.net; 19 | 20 | import android.annotation.SuppressLint; 21 | import android.content.Intent; 22 | import android.net.Uri; 23 | import android.view.View; 24 | import android.widget.TextView; 25 | 26 | import com.xuexiang.rxutil2.lifecycle.RxLifecycle; 27 | import com.xuexiang.templateandserver.R; 28 | import com.xuexiang.templateandserver.core.BaseFragment; 29 | import com.xuexiang.templateandserver.core.http.subscriber.TipProgressLoadingSubscriber; 30 | import com.xuexiang.templateandserver.utils.XToastUtils; 31 | import com.xuexiang.xaop.annotation.Permission; 32 | import com.xuexiang.xaop.annotation.SingleClick; 33 | import com.xuexiang.xhttp2.XHttp; 34 | import com.xuexiang.xpage.annotation.Page; 35 | import com.xuexiang.xutil.app.IntentUtils; 36 | import com.xuexiang.xutil.app.PathUtils; 37 | import com.xuexiang.xutil.common.StringUtils; 38 | import com.xuexiang.xutil.file.FileUtils; 39 | import com.xuexiang.xutil.tip.ToastUtils; 40 | 41 | import butterknife.BindView; 42 | import butterknife.OnClick; 43 | 44 | import static android.app.Activity.RESULT_OK; 45 | import static com.xuexiang.xaop.consts.PermissionConsts.STORAGE; 46 | 47 | /** 48 | * @author xuexiang 49 | * @since 2020/9/6 1:15 AM 50 | */ 51 | @Page(name = "文件上传") 52 | public class FileUploadFragment extends BaseFragment { 53 | 54 | public static final int REQUEST_PICK = 100; 55 | 56 | @BindView(R.id.tv_upload_path) 57 | TextView tvUploadPath; 58 | @BindView(R.id.tv_save_path) 59 | TextView tvSavePath; 60 | 61 | String mUploadPath; 62 | 63 | @Override 64 | protected int getLayoutId() { 65 | return R.layout.fragment_file_upload; 66 | } 67 | 68 | @Override 69 | protected void initViews() { 70 | 71 | } 72 | 73 | @SingleClick 74 | @OnClick({R.id.bth_select, R.id.bth_upload}) 75 | public void onViewClicked(View view) { 76 | switch (view.getId()) { 77 | case R.id.bth_select: 78 | selectFile(); 79 | break; 80 | case R.id.bth_upload: 81 | uploadFile(); 82 | break; 83 | default: 84 | break; 85 | } 86 | } 87 | 88 | @Override 89 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 90 | super.onActivityResult(requestCode, resultCode, data); 91 | //选择系统图片并解析 92 | if (requestCode == REQUEST_PICK && resultCode == RESULT_OK) { 93 | if (data != null) { 94 | Uri uri = data.getData(); 95 | if (uri != null) { 96 | mUploadPath = PathUtils.getFilePathByUri(uri); 97 | tvUploadPath.setText(mUploadPath); 98 | tvSavePath.setText(""); 99 | } 100 | } 101 | } 102 | } 103 | 104 | @Permission(STORAGE) 105 | private void selectFile() { 106 | startActivityForResult(IntentUtils.getDocumentPickerIntent(IntentUtils.DocumentType.ANY), REQUEST_PICK); 107 | } 108 | 109 | @SuppressLint("CheckResult") 110 | private void uploadFile() { 111 | if (StringUtils.isEmpty(mUploadPath)) { 112 | ToastUtils.toast("请先选择需要上传的文件!"); 113 | selectFile(); 114 | return; 115 | } 116 | getProgressLoader().updateMessage("上传中..."); 117 | XHttp.post("/file/upload") 118 | .params("type", "common") 119 | .uploadFile("file", FileUtils.getFileByPath(mUploadPath), (bytesWritten, contentLength, done) -> { 120 | 121 | }).execute(String.class) 122 | .compose(RxLifecycle.with(this).bindToLifecycle()) 123 | .subscribeWith(new TipProgressLoadingSubscriber(this) { 124 | @Override 125 | public void onSuccess(String savePath) { 126 | XToastUtils.success("文件上传成功"); 127 | tvSavePath.setText(savePath); 128 | } 129 | }); 130 | } 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/fragment/net/RegisterTestFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.fragment.net; 19 | 20 | import android.view.View; 21 | 22 | import com.xuexiang.server.model.User; 23 | import com.xuexiang.templateandserver.R; 24 | import com.xuexiang.templateandserver.core.BaseFragment; 25 | import com.xuexiang.templateandserver.core.http.TestApi; 26 | import com.xuexiang.templateandserver.core.http.callback.TipProgressLoadingCallBack; 27 | import com.xuexiang.templateandserver.utils.XToastUtils; 28 | import com.xuexiang.xaop.annotation.SingleClick; 29 | import com.xuexiang.xhttp2.XHttp; 30 | import com.xuexiang.xhttp2.request.CustomRequest; 31 | import com.xuexiang.xpage.annotation.Page; 32 | import com.xuexiang.xui.widget.actionbar.TitleBar; 33 | import com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText; 34 | import com.xuexiang.xui.widget.spinner.materialspinner.MaterialSpinner; 35 | import com.xuexiang.xutil.common.StringUtils; 36 | 37 | import butterknife.BindView; 38 | 39 | import static android.app.Activity.RESULT_OK; 40 | 41 | /** 42 | * @author xuexiang 43 | * @since 2020/9/5 5:11 PM 44 | */ 45 | @Page 46 | public class RegisterTestFragment extends BaseFragment { 47 | 48 | @BindView(R.id.et_login_name) 49 | MaterialEditText etLoginName; 50 | @BindView(R.id.et_password) 51 | MaterialEditText etPassword; 52 | @BindView(R.id.et_name) 53 | MaterialEditText etName; 54 | @BindView(R.id.et_age) 55 | MaterialEditText etAge; 56 | @BindView(R.id.ms_gender) 57 | MaterialSpinner msGender; 58 | @BindView(R.id.et_phone) 59 | MaterialEditText etPhone; 60 | 61 | @Override 62 | protected int getLayoutId() { 63 | return R.layout.fragment_user_info_manage; 64 | } 65 | 66 | @Override 67 | protected TitleBar initTitle() { 68 | TitleBar titleBar = super.initTitle() 69 | .setTitle("账户注册"); 70 | titleBar.addAction(new TitleBar.TextAction("提交") { 71 | @SingleClick 72 | @Override 73 | public void performAction(View view) { 74 | handleSubmit(); 75 | } 76 | }); 77 | return titleBar; 78 | } 79 | 80 | private void handleSubmit() { 81 | if (!validateInput()) { 82 | return; 83 | } 84 | 85 | User user = new User().setLoginName(etLoginName.getEditValue()) 86 | .setPassword(etPassword.getEditValue()) 87 | .setName(etName.getEditValue()) 88 | .setAge(StringUtils.toInt(etAge.getEditValue())) 89 | .setGender(msGender.getSelectedIndex()) 90 | .setPhone(etPhone.getEditValue()); 91 | 92 | 93 | CustomRequest request = XHttp.custom(); 94 | request.apiCall(request.create(TestApi.UserService.class).register(user), new TipProgressLoadingCallBack(this) { 95 | @Override 96 | public void onSuccess(Boolean response) throws Throwable { 97 | XToastUtils.success("注册成功"); 98 | setFragmentResult(RESULT_OK, null); 99 | popToBack(); 100 | } 101 | }); 102 | } 103 | 104 | @Override 105 | protected void initViews() { 106 | 107 | } 108 | 109 | private boolean validateInput() { 110 | return etLoginName.validate() && etPassword.validate() && etAge.validate() && etPhone.validate(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/utils/NetUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2018 Zhenjie Yan. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.xuexiang.templateandserver.utils; 17 | 18 | import java.net.InetAddress; 19 | import java.net.NetworkInterface; 20 | import java.net.SocketException; 21 | import java.util.Enumeration; 22 | import java.util.regex.Pattern; 23 | 24 | /** 25 | * 网络工具类 26 | * 27 | * @author xuexiang 28 | * @since 2020/8/30 2:52 AM 29 | */ 30 | public final class NetUtils { 31 | 32 | private NetUtils() { 33 | throw new UnsupportedOperationException("u can't instantiate me..."); 34 | } 35 | 36 | /** 37 | * Ipv4 address check. 38 | */ 39 | private static final Pattern IPV4_PATTERN = Pattern.compile( 40 | "^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + 41 | "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"); 42 | 43 | /** 44 | * Check if valid IPV4 address. 45 | * 46 | * @param input the address string to check for validity. 47 | * @return True if the input parameter is a valid IPv4 address. 48 | */ 49 | public static boolean isIPv4Address(String input) { 50 | return IPV4_PATTERN.matcher(input).matches(); 51 | } 52 | 53 | /** 54 | * Get local Ip address. 55 | */ 56 | public static InetAddress getLocalIPAddress() { 57 | Enumeration enumeration = null; 58 | try { 59 | enumeration = NetworkInterface.getNetworkInterfaces(); 60 | } catch (SocketException e) { 61 | e.printStackTrace(); 62 | } 63 | if (enumeration != null) { 64 | while (enumeration.hasMoreElements()) { 65 | NetworkInterface nif = enumeration.nextElement(); 66 | Enumeration inetAddresses = nif.getInetAddresses(); 67 | if (inetAddresses != null) { 68 | while (inetAddresses.hasMoreElements()) { 69 | InetAddress inetAddress = inetAddresses.nextElement(); 70 | if (!inetAddress.isLoopbackAddress() && isIPv4Address(inetAddress.getHostAddress())) { 71 | return inetAddress; 72 | } 73 | } 74 | } 75 | } 76 | } 77 | return null; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/utils/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.utils; 19 | 20 | import android.content.Intent; 21 | import android.net.Uri; 22 | 23 | import androidx.fragment.app.Fragment; 24 | 25 | /** 26 | * 工具类 27 | * 28 | * @author xuexiang 29 | * @since 2020/8/30 3:27 AM 30 | */ 31 | public final class Utils { 32 | 33 | private Utils() { 34 | throw new UnsupportedOperationException("u can't instantiate me..."); 35 | } 36 | 37 | /** 38 | * 以系统API的方式请求浏览器 39 | * 40 | * @param url 41 | */ 42 | public static void goWeb(Fragment fragment, final String url) { 43 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 44 | fragment.startActivity(intent); 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/utils/XToastUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.utils; 19 | 20 | import androidx.annotation.MainThread; 21 | import androidx.annotation.NonNull; 22 | import androidx.annotation.StringRes; 23 | 24 | import com.xuexiang.xui.XUI; 25 | import com.xuexiang.xui.widget.toast.XToast; 26 | 27 | /** 28 | * xtoast 工具类 29 | * 30 | * @author xuexiang 31 | * @since 2019-06-30 19:04 32 | */ 33 | public final class XToastUtils { 34 | 35 | 36 | private XToastUtils() { 37 | throw new UnsupportedOperationException("u can't instantiate me..."); 38 | } 39 | 40 | static { 41 | XToast.Config.get() 42 | .setAlpha(200) 43 | .allowQueue(false); 44 | } 45 | 46 | //======普通土司=======// 47 | 48 | @MainThread 49 | public static void toast(@NonNull CharSequence message) { 50 | XToast.normal(XUI.getContext(), message).show(); 51 | } 52 | 53 | @MainThread 54 | public static void toast(@StringRes int message) { 55 | XToast.normal(XUI.getContext(), message).show(); 56 | } 57 | 58 | @MainThread 59 | public static void toast(@NonNull CharSequence message, int duration) { 60 | XToast.normal(XUI.getContext(), message, duration).show(); 61 | } 62 | 63 | @MainThread 64 | public static void toast(@StringRes int message, int duration) { 65 | XToast.normal(XUI.getContext(), message, duration).show(); 66 | } 67 | 68 | //======错误【红色】=======// 69 | 70 | @MainThread 71 | public static void error(@NonNull Throwable throwable) { 72 | XToast.error(XUI.getContext(), throwable.getMessage()).show(); 73 | } 74 | 75 | @MainThread 76 | public static void error(@NonNull CharSequence message) { 77 | XToast.error(XUI.getContext(), message).show(); 78 | } 79 | 80 | @MainThread 81 | public static void error(@StringRes int message) { 82 | XToast.error(XUI.getContext(), message).show(); 83 | } 84 | 85 | @MainThread 86 | public static void error(@NonNull CharSequence message, int duration) { 87 | XToast.error(XUI.getContext(), message, duration).show(); 88 | } 89 | 90 | @MainThread 91 | public static void error(@StringRes int message, int duration) { 92 | XToast.error(XUI.getContext(), message, duration).show(); 93 | } 94 | 95 | //======成功【绿色】=======// 96 | 97 | @MainThread 98 | public static void success(@NonNull CharSequence message) { 99 | XToast.success(XUI.getContext(), message).show(); 100 | } 101 | 102 | @MainThread 103 | public static void success(@StringRes int message) { 104 | XToast.success(XUI.getContext(), message).show(); 105 | } 106 | 107 | @MainThread 108 | public static void success(@NonNull CharSequence message, int duration) { 109 | XToast.success(XUI.getContext(), message, duration).show(); 110 | } 111 | 112 | @MainThread 113 | public static void success(@StringRes int message, int duration) { 114 | XToast.success(XUI.getContext(), message, duration).show(); 115 | } 116 | 117 | //======信息【蓝色】=======// 118 | 119 | @MainThread 120 | public static void info(@NonNull CharSequence message) { 121 | XToast.info(XUI.getContext(), message).show(); 122 | } 123 | 124 | @MainThread 125 | public static void info(@StringRes int message) { 126 | XToast.info(XUI.getContext(), message).show(); 127 | } 128 | 129 | @MainThread 130 | public static void info(@NonNull CharSequence message, int duration) { 131 | XToast.info(XUI.getContext(), message, duration).show(); 132 | } 133 | 134 | @MainThread 135 | public static void info(@StringRes int message, int duration) { 136 | XToast.info(XUI.getContext(), message, duration).show(); 137 | } 138 | 139 | //=======警告【黄色】======// 140 | 141 | @MainThread 142 | public static void warning(@NonNull CharSequence message) { 143 | XToast.warning(XUI.getContext(), message).show(); 144 | } 145 | 146 | @MainThread 147 | public static void warning(@StringRes int message) { 148 | XToast.warning(XUI.getContext(), message).show(); 149 | } 150 | 151 | @MainThread 152 | public static void warning(@NonNull CharSequence message, int duration) { 153 | XToast.warning(XUI.getContext(), message, duration).show(); 154 | } 155 | 156 | @MainThread 157 | public static void warning(@StringRes int message, int duration) { 158 | XToast.warning(XUI.getContext(), message, duration).show(); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/utils/sdkinit/UMengInit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.utils.sdkinit; 19 | 20 | import android.app.Application; 21 | import android.content.Context; 22 | 23 | import androidx.annotation.NonNull; 24 | 25 | import com.meituan.android.walle.WalleChannelReader; 26 | import com.umeng.analytics.MobclickAgent; 27 | import com.umeng.commonsdk.UMConfigure; 28 | import com.xuexiang.templateandserver.BuildConfig; 29 | import com.xuexiang.templateandserver.MyApp; 30 | 31 | /** 32 | * UMeng 统计 SDK初始化 33 | * 34 | * @author xuexiang 35 | * @since 2019-06-18 15:49 36 | */ 37 | public final class UMengInit { 38 | 39 | private UMengInit() { 40 | throw new UnsupportedOperationException("u can't instantiate me..."); 41 | } 42 | 43 | private static String DEFAULT_CHANNEL_ID = "github"; 44 | 45 | /** 46 | * 初始化SDK,合规指南【先进行预初始化,如果用户隐私同意后可以初始化UmengSDK进行信息上报】 47 | */ 48 | public static void init(@NonNull Context context) { 49 | Context appContext = context.getApplicationContext(); 50 | if (appContext instanceof Application) { 51 | init((Application) appContext); 52 | } 53 | } 54 | 55 | /** 56 | * 初始化SDK,合规指南【先进行预初始化,如果用户隐私同意后可以初始化UmengSDK进行信息上报】 57 | */ 58 | public static void init(Application application) { 59 | // 运营统计数据调试运行时不初始化 60 | if (MyApp.isDebug()) { 61 | return; 62 | } 63 | UMConfigure.setLogEnabled(false); 64 | UMConfigure.preInit(application, BuildConfig.APP_ID_UMENG, getChannel(application)); 65 | // 用户同意了隐私协议 66 | if (isAgreePrivacy()) { 67 | realInit(application); 68 | } 69 | } 70 | 71 | /** 72 | * @return 用户是否同意了隐私协议 73 | */ 74 | private static boolean isAgreePrivacy() { 75 | // TODO: 2021/5/11 隐私协议设置 76 | return true; 77 | } 78 | 79 | /** 80 | * 真实的初始化UmengSDK【进行设备信息的统计上报,必须在获得用户隐私同意后方可调用】 81 | */ 82 | private static void realInit(Application application) { 83 | // 运营统计数据调试运行时不初始化 84 | if (MyApp.isDebug()) { 85 | return; 86 | } 87 | //初始化组件化基础库, 注意: 即使您已经在AndroidManifest.xml中配置过appkey和channel值,也需要在App代码中调用初始化接口(如需要使用AndroidManifest.xml中配置好的appkey和channel值,UMConfigure.init调用中appkey和channel参数请置为null)。 88 | //第二个参数是appkey,最后一个参数是pushSecret 89 | //这里BuildConfig.APP_ID_UMENG是根据local.properties中定义的APP_ID_UMENG生成的,只是运行看效果的话,可以不初始化该SDK 90 | UMConfigure.init(application, BuildConfig.APP_ID_UMENG, getChannel(application), UMConfigure.DEVICE_TYPE_PHONE, ""); 91 | //统计SDK是否支持采集在子进程中打点的自定义事件,默认不支持 92 | //支持多进程打点 93 | UMConfigure.setProcessEvent(true); 94 | MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO); 95 | } 96 | 97 | /** 98 | * 获取渠道信息 99 | */ 100 | private static String getChannel(final Context context) { 101 | return WalleChannelReader.getChannel(context, DEFAULT_CHANNEL_ID); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/utils/sdkinit/XBasicLibInit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.utils.sdkinit; 19 | 20 | import android.app.Application; 21 | 22 | import com.xuexiang.templateandserver.MyApp; 23 | import com.xuexiang.templateandserver.core.BaseActivity; 24 | import com.xuexiang.templateandserver.utils.XToastUtils; 25 | import com.xuexiang.xaop.XAOP; 26 | import com.xuexiang.xhttp2.XHttpSDK; 27 | import com.xuexiang.xpage.PageConfig; 28 | import com.xuexiang.xrouter.launcher.XRouter; 29 | import com.xuexiang.xui.XUI; 30 | import com.xuexiang.xutil.XUtil; 31 | import com.xuexiang.xutil.common.StringUtils; 32 | 33 | /** 34 | * X系列基础库初始化 35 | * 36 | * @author xuexiang 37 | * @since 2019-06-30 23:54 38 | */ 39 | public final class XBasicLibInit { 40 | 41 | private XBasicLibInit() { 42 | throw new UnsupportedOperationException("u can't instantiate me..."); 43 | } 44 | 45 | /** 46 | * 初始化基础库SDK 47 | */ 48 | public static void init(Application application) { 49 | //工具类 50 | initXUtil(application); 51 | 52 | //网络请求框架 53 | initXHttp2(application); 54 | 55 | //页面框架 56 | initXPage(application); 57 | 58 | //切片框架 59 | initXAOP(application); 60 | 61 | //UI框架 62 | initXUI(application); 63 | 64 | //路由框架 65 | initRouter(application); 66 | 67 | } 68 | 69 | /** 70 | * 初始化XUtil工具类 71 | */ 72 | private static void initXUtil(Application application) { 73 | XUtil.init(application); 74 | XUtil.debug(MyApp.isDebug()); 75 | } 76 | 77 | /** 78 | * 初始化XPage页面框架 79 | */ 80 | private static void initXPage(Application application) { 81 | PageConfig.getInstance() 82 | .debug(MyApp.isDebug() ? "PageLog" : null) 83 | .setContainActivityClazz(BaseActivity.class) 84 | .init(application); 85 | } 86 | 87 | /** 88 | * 初始化XAOP 89 | */ 90 | private static void initXAOP(Application application) { 91 | XAOP.init(application); 92 | XAOP.debug(MyApp.isDebug()); 93 | //设置动态申请权限切片 申请权限被拒绝的事件响应监听 94 | XAOP.setOnPermissionDeniedListener(permissionsDenied -> XToastUtils.error("权限申请被拒绝:" + StringUtils.listToString(permissionsDenied, ","))); 95 | } 96 | 97 | /** 98 | * 初始化XUI框架 99 | */ 100 | private static void initXUI(Application application) { 101 | XUI.init(application); 102 | XUI.debug(MyApp.isDebug()); 103 | } 104 | 105 | /** 106 | * 初始化路由框架 107 | */ 108 | private static void initRouter(Application application) { 109 | // 这两行必须写在init之前,否则这些配置在init过程中将无效 110 | if (MyApp.isDebug()) { 111 | XRouter.openLog(); // 打印日志 112 | XRouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) 113 | } 114 | XRouter.init(application); 115 | } 116 | 117 | /** 118 | * 初始化XHttp2 119 | */ 120 | private static void initXHttp2(Application application) { 121 | //初始化网络请求框架,必须首先执行 122 | XHttpSDK.init(application); 123 | //需要调试的时候执行 124 | if (MyApp.isDebug()) { 125 | XHttpSDK.debug(); 126 | } 127 | //设置网络请求的全局基础地址 128 | XHttpSDK.setBaseUrl("http://127.0.0.1:8080"); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/templateandserver/utils/service/JsonSerializationService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.xuexiang.templateandserver.utils.service; 19 | 20 | import android.content.Context; 21 | 22 | import com.xuexiang.xrouter.annotation.Router; 23 | import com.xuexiang.xrouter.facade.service.SerializationService; 24 | import com.xuexiang.xutil.net.JsonUtil; 25 | 26 | import java.lang.reflect.Type; 27 | 28 | /** 29 | * @author XUE 30 | * @since 2019/3/27 16:39 31 | */ 32 | @Router(path = "/service/json") 33 | public class JsonSerializationService implements SerializationService { 34 | /** 35 | * 对象序列化为json 36 | * 37 | * @param instance obj 38 | * @return json string 39 | */ 40 | @Override 41 | public String object2Json(Object instance) { 42 | return JsonUtil.toJson(instance); 43 | } 44 | 45 | /** 46 | * json反序列化为对象 47 | * 48 | * @param input json string 49 | * @param clazz object type 50 | * @return instance of object 51 | */ 52 | @Override 53 | public T parseObject(String input, Type clazz) { 54 | return JsonUtil.fromJson(input, clazz); 55 | } 56 | 57 | /** 58 | * 进程初始化的方法 59 | * 60 | * @param context 上下文 61 | */ 62 | @Override 63 | public void init(Context context) { 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_upload.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_item_simple_list_2.xml: -------------------------------------------------------------------------------- 1 | 17 | 26 | 27 | 31 | 32 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_file_upload.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 22 | 23 | 28 | 29 | 33 | 34 |