├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── keystore.jks ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── happynote │ │ └── ExampleInstrumentedTest.kt │ ├── debug │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── happynote │ │ └── GLApplication.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sjtu │ │ │ └── yifei │ │ │ └── happynote │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_dashboard_black_24dp.xml │ │ ├── ic_home_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ └── ic_notifications_black_24dp.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── menu │ │ └── navigation.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 │ │ ├── colors.xml │ │ ├── dimens.xml │ │ └── strings.xml │ ├── release │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── happynote │ │ └── GLApplication.kt │ └── test │ └── java │ └── com │ └── sjtu │ └── yifei │ └── happynote │ └── ExampleUnitTest.kt ├── base ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── base │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sjtu │ │ │ └── yifei │ │ │ └── base │ │ │ ├── BaseActivity.kt │ │ │ ├── BaseApplication.kt │ │ │ ├── BaseFragment.kt │ │ │ ├── OkHttpClientManager.kt │ │ │ └── util │ │ │ ├── ActivityExtensions.kt │ │ │ ├── AppExecutors.kt │ │ │ ├── AppUtils.kt │ │ │ ├── AsyncWriteThread.kt │ │ │ ├── FileUtil.kt │ │ │ ├── LogUtil.kt │ │ │ └── NetworkUtil.kt │ └── res │ │ ├── drawable │ │ └── ic_add_black_24dp.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── sjtu │ └── yifei │ └── base │ └── ExampleUnitTest.java ├── build.gradle ├── business ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── business │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sjtu │ │ │ └── yifei │ │ │ ├── business │ │ │ ├── Bus1Activity.kt │ │ │ ├── api │ │ │ │ ├── ApiService.kt │ │ │ │ └── RetrofitManager.kt │ │ │ └── bean │ │ │ │ ├── AuthorInfoBean.kt │ │ │ │ ├── CategoryBean.kt │ │ │ │ ├── HomeBean.kt │ │ │ │ └── TabInfoBean.kt │ │ │ └── list │ │ │ └── DetailListActivity.kt │ └── res │ │ ├── layout │ │ ├── activity_bus1.xml │ │ └── activity_detail_list.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── sjtu │ └── yifei │ └── business │ └── ExampleUnitTest.java ├── depconfig.gradle ├── editor ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── editor │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sjtu │ │ │ └── yifei │ │ │ └── editor │ │ │ ├── api │ │ │ └── ApiService.kt │ │ │ └── ui │ │ │ └── EditorActivity.kt │ └── res │ │ ├── layout │ │ ├── activity_editor.xml │ │ └── content_editor.xml │ │ ├── menu │ │ └── menu_editor.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── sjtu │ └── yifei │ └── editor │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── hybrid ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── hybrid │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── WebViewJavascriptBridge.js │ ├── java │ │ └── com │ │ │ └── sjtu │ │ │ └── yifei │ │ │ └── hybrid │ │ │ ├── HybridActivity.kt │ │ │ └── web │ │ │ ├── BridgeHandler.java │ │ │ ├── BridgeUtil.java │ │ │ ├── BridgeWebView.java │ │ │ ├── BridgeWebViewClient.java │ │ │ ├── CallBackFunction.java │ │ │ ├── DefaultHandler.java │ │ │ ├── Message.java │ │ │ └── WebViewJavascriptBridge.java │ └── res │ │ ├── drawable │ │ ├── hybrid_progress_drawable.xml │ │ ├── ic_refresh_black_24dp.xml │ │ └── ic_refresh_white_24dp.xml │ │ ├── layout │ │ └── hybrid_activity.xml │ │ ├── menu │ │ └── hybrid_menu.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── sjtu │ └── yifei │ └── hybrid │ └── ExampleUnitTest.java ├── login ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── login │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sjtu │ │ │ └── yifei │ │ │ └── login │ │ │ ├── LoginActivity.kt │ │ │ └── LoginInterceptor.kt │ └── res │ │ ├── layout │ │ └── activity_login.xml │ │ └── values │ │ ├── dimens.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── sjtu │ └── yifei │ └── login │ └── ExampleUnitTest.java ├── performance ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── debug │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── performance │ │ ├── AppLeakCanaryService.kt │ │ └── PerformanceDetection.kt │ └── main │ ├── AndroidManifest.xml │ └── res │ └── values │ └── strings.xml ├── router ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sjtu │ │ └── yifei │ │ └── router │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sjtu │ │ │ └── yifei │ │ │ └── router │ │ │ ├── RouterPath.kt │ │ │ └── RouterService.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── sjtu │ └── yifei │ └── router │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | apply plugin: 'kotlin-android-extensions' 5 | 6 | apply plugin: 'com.sjtu.yifei.autoinject' 7 | 8 | autoinject { 9 | isDebug = true //是否在gradle console 中输出transform log 10 | } 11 | 12 | android { 13 | compileSdkVersion versions.compileSdkVersion 14 | 15 | defaultConfig { 16 | applicationId "com.sjtu.yifei.happynote" 17 | 18 | minSdkVersion versions.min_sdk 19 | targetSdkVersion versions.target_sdk 20 | versionCode 1 21 | versionName "1.0" 22 | 23 | vectorDrawables.useSupportLibrary = true 24 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 25 | } 26 | 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_8 29 | targetCompatibility JavaVersion.VERSION_1_8 30 | } 31 | 32 | signingConfigs { 33 | releaseconfig { 34 | keyAlias 'yifei' 35 | keyPassword 'yifei666' 36 | storeFile file('keystore.jks') 37 | storePassword 'yifei666' 38 | } 39 | } 40 | 41 | buildTypes { 42 | release { 43 | minifyEnabled true 44 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 45 | signingConfig signingConfigs.releaseconfig 46 | debuggable false 47 | jniDebuggable false 48 | renderscriptDebuggable false 49 | zipAlignEnabled true 50 | } 51 | } 52 | 53 | dexOptions { 54 | javaMaxHeapSize "4g" 55 | } 56 | 57 | lintOptions { 58 | abortOnError false 59 | } 60 | 61 | } 62 | 63 | 64 | dependencies { 65 | implementation fileTree(include: ['*.jar'], dir: 'libs') 66 | kapt yifei.auto_complier 67 | implementation project(path: ':router') 68 | implementation project(path: ':base') 69 | implementation project(path: ':login') 70 | implementation project(path: ':business') 71 | implementation project(path: ':editor') 72 | implementation project(path: ':hybrid') 73 | implementation project(path: ':performance') 74 | testImplementation testSupport.junit 75 | androidTestImplementation testSupport.testRunner 76 | androidTestImplementation testSupport.testEspresso 77 | } 78 | -------------------------------------------------------------------------------- /app/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/keystore.jks -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | 24 | -keep class * implements com.sjtu.yifei.ioc.**{*;} 25 | -keep class * implements com.sjtu.yifei.annotation.InjectContract{*;} 26 | -keep class * implements com.sjtu.yifei.router.IPerformanceProvider{*;} 27 | 28 | 29 | -dontwarn javax.annotation.** 30 | 31 | # okio 32 | -dontwarn okio.** 33 | 34 | # OkHttp3.0 35 | -keepattributes Signature 36 | -keepattributes *Annotation* 37 | -keep class okhttp3.** { *; } 38 | -keep interface okhttp3.** { *; } 39 | -dontwarn okhttp3.** 40 | 41 | # d8 42 | -dontwarn okhttp3.** 43 | -dontwarn okio.** 44 | -dontwarn javax.annotation.** 45 | -dontwarn org.conscrypt.** 46 | # A resource is loaded with a relative path so the package of this class must be preserved. 47 | -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase 48 | 49 | 50 | # retrofit2.0 51 | -dontnote retrofit2.Platform 52 | -dontwarn retrofit2.Platform$Java8 53 | -keepattributes Signature 54 | -keepattributes Exceptions 55 | -keepclasseswithmembers class * { 56 | @retrofit2.http.* ; 57 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/sjtu/yifei/happynote/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.happynote 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.sjtu.yifei.happynote", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/debug/java/com/sjtu/yifei/happynote/GLApplication.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.happynote 2 | 3 | import com.sjtu.yifei.base.BaseApplication 4 | import com.sjtu.yifei.base.util.AppUtils 5 | import com.sjtu.yifei.performance.PerformanceDetection 6 | import com.sjtu.yifei.route.Routerfit 7 | 8 | /** 9 | * 类描述: 10 | * 创建人:yifei 11 | * 创建时间:2018/6/27 12 | * 修改人: 13 | * 修改时间: 14 | * 修改备注: 15 | */ 16 | class GLApplication : BaseApplication() { 17 | 18 | override fun onCreate() { 19 | super.onCreate() 20 | /** 21 | * This process is dedicated to LeakCanary for heap analysis. 22 | * You should not init your app in this process. 23 | */ 24 | if (PerformanceDetection.instance.isInAnalyzerProcess(this)) { 25 | return 26 | } 27 | 28 | if (AppUtils.isAppProcess(this)) { 29 | Routerfit.init(this) 30 | PerformanceDetection.instance.initLeakCanary(this, true) 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/sjtu/yifei/happynote/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.happynote 2 | 3 | import android.os.Bundle 4 | import android.support.design.widget.BottomNavigationView 5 | import android.widget.Toast 6 | import com.sjtu.yifei.base.BaseActivity 7 | import com.sjtu.yifei.base.util.setupActionBar 8 | import com.sjtu.yifei.route.ActivityCallback 9 | import com.sjtu.yifei.route.Routerfit 10 | import com.sjtu.yifei.router.RouterService 11 | import kotlinx.android.synthetic.main.activity_main.* 12 | 13 | class MainActivity : BaseActivity() { 14 | 15 | private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item -> 16 | when (item.itemId) { 17 | R.id.navigation_home -> { 18 | message.setText(R.string.title_home) 19 | type_navigation = R.id.navigation_home 20 | return@OnNavigationItemSelectedListener true 21 | } 22 | R.id.navigation_dashboard -> { 23 | message.setText(R.string.title_dashboard) 24 | type_navigation = R.id.navigation_dashboard 25 | return@OnNavigationItemSelectedListener true 26 | } 27 | R.id.navigation_notifications -> { 28 | message.setText(R.string.title_notifications) 29 | type_navigation = R.id.navigation_notifications 30 | return@OnNavigationItemSelectedListener true 31 | } 32 | } 33 | false 34 | } 35 | 36 | private var type_navigation: Int = R.id.navigation_home 37 | 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContentView(R.layout.activity_main) 41 | setupActionBar(R.id.toolbar) { 42 | setDisplayHomeAsUpEnabled(false) 43 | setDisplayShowHomeEnabled(true) 44 | setTitle(R.string.app_name) 45 | } 46 | message.setOnClickListener { 47 | when (type_navigation) { 48 | R.id.navigation_home -> Routerfit.register(RouterService::class.java).openDetailList() 49 | R.id.navigation_dashboard -> Routerfit.register(RouterService::class.java).openEditorUi(ActivityCallback { result, data -> 50 | Toast.makeText(MainActivity@ this, "${result == Routerfit.RESULT_OK} $data", Toast.LENGTH_SHORT).show() 51 | }) 52 | R.id.navigation_notifications -> Routerfit.register(RouterService::class.java).openHybridUi("www.baidu.com") 53 | } 54 | } 55 | navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /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_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 23 | 27 | 28 | 38 | 39 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/menu/navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yifei8/HappyNote/9ad49e5c28d6aae0c45e3f64eeee5f5b6d7124f1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | HappyNote 3 | Bus1Activity 4 | EditActivity 5 | HybridActivity 6 | 7 | -------------------------------------------------------------------------------- /app/src/release/java/com/sjtu/yifei/happynote/GLApplication.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.happynote 2 | 3 | import com.sjtu.yifei.base.BaseApplication 4 | import com.sjtu.yifei.base.util.AppUtils 5 | import com.sjtu.yifei.route.Routerfit 6 | 7 | /** 8 | * 类描述: 9 | * 创建人:yifei 10 | * 创建时间:2018/6/27 11 | * 修改人: 12 | * 修改时间: 13 | * 修改备注: 14 | */ 15 | class GLApplication : BaseApplication() { 16 | 17 | override fun onCreate() { 18 | super.onCreate() 19 | if (AppUtils.isAppProcess(this)) { 20 | Routerfit.init(this) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/test/java/com/sjtu/yifei/happynote/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.happynote 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /base/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /base/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion versions.compileSdkVersion 7 | 8 | defaultConfig { 9 | minSdkVersion versions.min_sdk 10 | targetSdkVersion versions.target_sdk 11 | 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | } 23 | 24 | dependencies { 25 | api fileTree(dir: 'libs', include: ['*.jar']) 26 | 27 | api cp.kotlin_stdlib 28 | 29 | // Support libraries 30 | api support.annotations 31 | api support.app_compat 32 | api support.recyclerview 33 | api support.cardview 34 | api support.design 35 | api support.v4 36 | api support.cl 37 | 38 | // retrofit & rxandroid 39 | api net.retrofit 40 | api net.adapter_rxjava2 41 | api net.converter_gson 42 | api net.rxandroid 43 | api net.logging_interceptor 44 | 45 | testImplementation testSupport.junit 46 | androidTestImplementation testSupport.testRunner 47 | androidTestImplementation testSupport.testEspresso 48 | } 49 | -------------------------------------------------------------------------------- /base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /base/src/androidTest/java/com/sjtu/yifei/base/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sjtu.yifei.base.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.view.MenuItem 5 | import io.reactivex.disposables.CompositeDisposable 6 | import io.reactivex.disposables.Disposable 7 | 8 | /** 9 | * 类描述: 10 | * 创建人:yifei 11 | * 创建时间:2018/6/27 12 | * 修改人: 13 | * 修改时间: 14 | * 修改备注: 15 | */ 16 | open class BaseActivity : AppCompatActivity() { 17 | 18 | var TAG = this.javaClass.simpleName 19 | 20 | private var mCompositeDisposable: CompositeDisposable? = null 21 | 22 | override fun onOptionsItemSelected(item: MenuItem?): Boolean { 23 | val id = item?.itemId 24 | if (android.R.id.home == id) { 25 | onBackPressed() 26 | return true 27 | } 28 | return super.onOptionsItemSelected(item) 29 | } 30 | 31 | override fun onDestroy() { 32 | unSubscribe() 33 | super.onDestroy() 34 | } 35 | 36 | /** 37 | * 优化rx 内存泄漏问题 38 | * 39 | * @param disposable 40 | */ 41 | fun subscribe(disposable: Disposable?) { 42 | if (disposable != null) { 43 | if (mCompositeDisposable == null) { 44 | mCompositeDisposable = CompositeDisposable() 45 | } 46 | mCompositeDisposable?.add(disposable) 47 | } 48 | } 49 | 50 | /** 51 | * 优化rx 内存泄漏问题 52 | */ 53 | private fun unSubscribe() { 54 | if (mCompositeDisposable != null) { 55 | mCompositeDisposable?.clear() 56 | } 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/BaseApplication.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import kotlin.properties.Delegates 6 | 7 | /** 8 | * 类描述: 9 | * 创建人:yifei 10 | * 创建时间:2018/6/27 11 | * 修改人: 12 | * 修改时间: 13 | * 修改备注: 14 | */ 15 | open class BaseApplication : Application() { 16 | 17 | companion object { 18 | var context: Context by Delegates.notNull() 19 | var token: String by Delegates.notNull() 20 | } 21 | 22 | override fun onCreate() { 23 | super.onCreate() 24 | context = applicationContext 25 | token = "" 26 | } 27 | 28 | override fun onTerminate() { 29 | super.onTerminate() 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base 2 | 3 | import android.support.v4.app.Fragment 4 | import io.reactivex.disposables.CompositeDisposable 5 | import io.reactivex.disposables.Disposable 6 | 7 | /** 8 | * 类描述: 9 | * 创建人:yifei 10 | * 创建时间:2018/6/27 11 | * 修改人: 12 | * 修改时间: 13 | * 修改备注: 14 | */ 15 | open class BaseFragment : Fragment() { 16 | 17 | var TAG = this.javaClass.simpleName 18 | 19 | private var mCompositeDisposable: CompositeDisposable? = null 20 | 21 | override fun onDetach() { 22 | super.onDetach() 23 | unSubscribe() 24 | } 25 | 26 | /** 27 | * 优化rx 内存泄漏问题 28 | * 29 | * @param disposable 30 | */ 31 | fun subscribe(disposable: Disposable?) { 32 | if (disposable != null) { 33 | if (mCompositeDisposable == null) { 34 | mCompositeDisposable = CompositeDisposable() 35 | } 36 | mCompositeDisposable?.add(disposable) 37 | } 38 | } 39 | 40 | /** 41 | * 优化rx 内存泄漏问题 42 | */ 43 | private fun unSubscribe() { 44 | if (mCompositeDisposable != null) { 45 | mCompositeDisposable?.clear() 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/OkHttpClientManager.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base 2 | 3 | import com.sjtu.yifei.base.util.AppUtils 4 | import com.sjtu.yifei.base.util.NetworkUtil 5 | import okhttp3.CacheControl 6 | import okhttp3.Interceptor 7 | import okhttp3.OkHttpClient 8 | import okhttp3.Request 9 | import okhttp3.logging.HttpLoggingInterceptor 10 | import java.util.concurrent.TimeUnit 11 | 12 | /** 13 | * 类描述: 14 | * 创建人:yifei 15 | * 创建时间:2018/10/23 16 | * 修改人: 17 | * 修改时间: 18 | * 修改备注: 19 | */ 20 | class OkHttpClientManager private constructor() { 21 | 22 | companion object { 23 | val instance: OkHttpClientManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { 24 | OkHttpClientManager() 25 | } 26 | } 27 | 28 | private var clients: MutableMap = HashMap() 29 | 30 | fun getBaseClient(key: String): OkHttpClient { 31 | var client: OkHttpClient? = clients[key] 32 | if (client == null) { 33 | client = getOkHttpClient() 34 | clients[key] = client 35 | } 36 | return client 37 | } 38 | 39 | private fun getOkHttpClient(): OkHttpClient { 40 | //添加一个log拦截器,打印所有的log 41 | val httpLoggingInterceptor = HttpLoggingInterceptor() 42 | //可以设置请求过滤的水平,body,basic,headers 43 | httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY 44 | return OkHttpClient.Builder() 45 | .addInterceptor(addCacheInterceptor()) 46 | // .addInterceptor(addHeaderInterceptor()) 47 | // .addInterceptor(addQueryParameterInterceptor()) 48 | .addInterceptor(httpLoggingInterceptor) //日志,所有的请求响应度看到 49 | .connectTimeout(60L, TimeUnit.SECONDS) 50 | .readTimeout(60L, TimeUnit.SECONDS) 51 | .writeTimeout(60L, TimeUnit.SECONDS) 52 | .build() 53 | } 54 | 55 | /** 56 | * 设置公共参数 57 | */ 58 | private fun addQueryParameterInterceptor(): Interceptor { 59 | return Interceptor { chain -> 60 | val originalRequest = chain.request() 61 | val request: Request 62 | val modifiedUrl = originalRequest.url().newBuilder() 63 | .addQueryParameter("deviceModel", AppUtils.getMobileModel()) 64 | .build() 65 | request = originalRequest.newBuilder().url(modifiedUrl).build() 66 | chain.proceed(request) 67 | } 68 | } 69 | 70 | /** 71 | * 设置头 72 | */ 73 | private fun addHeaderInterceptor(): Interceptor { 74 | return Interceptor { chain -> 75 | val originalRequest = chain.request() 76 | val requestBuilder = originalRequest.newBuilder() 77 | // Provide your custom header here 78 | .header("token", BaseApplication.token) 79 | .method(originalRequest.method(), originalRequest.body()) 80 | val request = requestBuilder.build() 81 | chain.proceed(request) 82 | } 83 | } 84 | 85 | /** 86 | * 设置缓存 87 | */ 88 | private fun addCacheInterceptor(): Interceptor { 89 | return Interceptor { chain -> 90 | var request = chain.request() 91 | if (!NetworkUtil.isNetworkAvailable(BaseApplication.context)) { 92 | request = request.newBuilder() 93 | .cacheControl(CacheControl.FORCE_CACHE) 94 | .build() 95 | } 96 | val response = chain.proceed(request) 97 | if (NetworkUtil.isNetworkAvailable(BaseApplication.context)) { 98 | val maxAge = 0 99 | // 有网络时 设置缓存超时时间0个小时 ,意思就是不读取缓存数据,只对get有用,post没有缓冲 100 | response.newBuilder() 101 | .header("Cache-Control", "public, max-age=$maxAge") 102 | .removeHeader("Retrofit")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 103 | .build() 104 | } else { 105 | // 无网络时,设置超时为4周 只对get有用,post没有缓冲 106 | val maxStale = 60 * 60 * 24 * 28 107 | response.newBuilder() 108 | .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale") 109 | .removeHeader("nyn") 110 | .build() 111 | } 112 | response 113 | } 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/util/ActivityExtensions.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base.util 2 | 3 | import android.support.annotation.IdRes 4 | import android.support.v4.app.Fragment 5 | import android.support.v4.app.FragmentManager 6 | import android.support.v4.app.FragmentTransaction 7 | import android.support.v7.app.ActionBar 8 | import android.support.v7.app.AppCompatActivity 9 | 10 | fun AppCompatActivity.replaceFragment(@IdRes id: Int, fragment: Fragment) { 11 | supportFragmentManager.transact { replace(id, fragment) } 12 | } 13 | 14 | fun AppCompatActivity.addFragment(@IdRes id: Int, fragment: Fragment) { 15 | supportFragmentManager.transact { add(id, fragment) } 16 | } 17 | 18 | fun AppCompatActivity.findFragmentByTag(tag: String): Fragment? = 19 | supportFragmentManager.findFragmentByTag(tag) 20 | 21 | fun AppCompatActivity.findFragmentById(tag: Int): Fragment? = 22 | supportFragmentManager.findFragmentById(tag) 23 | 24 | fun AppCompatActivity.setupActionBar(@IdRes toolbarId: Int, action: ActionBar.() -> Unit) { 25 | setSupportActionBar(findViewById(toolbarId)) 26 | supportActionBar?.run { 27 | action() 28 | } 29 | } 30 | 31 | private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) { 32 | beginTransaction().apply { 33 | action() 34 | }.commit() 35 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/util/AppExecutors.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 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.sjtu.yifei.base.util 18 | 19 | import android.os.Handler 20 | import android.os.Looper 21 | import java.util.concurrent.Executor 22 | import java.util.concurrent.Executors 23 | 24 | const val THREAD_COUNT = 3 25 | 26 | open class AppExecutors constructor( 27 | val diskIO: Executor = DiskIOThreadExecutor(), 28 | val networkIO: Executor = Executors.newFixedThreadPool(THREAD_COUNT), 29 | val mainThread: Executor = MainThreadExecutor() 30 | ) { 31 | 32 | private class DiskIOThreadExecutor : Executor { 33 | 34 | private val diskIO = Executors.newSingleThreadExecutor() 35 | 36 | override fun execute(command: Runnable) { 37 | diskIO.execute(command) 38 | } 39 | } 40 | 41 | private class MainThreadExecutor : Executor { 42 | private val mainThreadHandler = Handler(Looper.getMainLooper()) 43 | 44 | override fun execute(command: Runnable) { 45 | mainThreadHandler.post(command) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/util/AppUtils.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base.util 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.ActivityManager 5 | import android.app.Application 6 | import android.content.Context 7 | import android.content.pm.PackageManager 8 | import android.os.Build 9 | import android.text.TextUtils 10 | import java.security.MessageDigest 11 | 12 | /** 13 | * 类描述: 14 | * 创建人:yifei 15 | * 创建时间:2018/10/25 16 | * 修改人: 17 | * 修改时间: 18 | * 修改备注: 19 | */ 20 | class AppUtils { 21 | init { 22 | throw Error("Do not need instantiate!") 23 | } 24 | 25 | companion object { 26 | 27 | /** 28 | * 判断是否当前APP主进程 29 | * 30 | * @param application 31 | * @return 32 | */ 33 | fun isAppProcess(application: Application): Boolean { 34 | val processName = getProcessName(application, android.os.Process.myPid()) 35 | return if (!TextUtils.isEmpty(processName)) { 36 | processName == getAppPackageName() 37 | } else false 38 | } 39 | 40 | /** 41 | * @return null may be returned if the specified process not found 42 | */ 43 | private fun getProcessName(cxt: Context, pid: Int): String? { 44 | val am = cxt.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 45 | val runningApps = am.runningAppProcesses ?: return null 46 | for (procInfo in runningApps) { 47 | if (procInfo.pid == pid) { 48 | return procInfo.processName 49 | } 50 | } 51 | return null 52 | } 53 | 54 | private fun getAppPackageName(): String { 55 | return "com.sjtu.yifei.happynote" 56 | } 57 | 58 | /** 59 | * 得到软件版本号 60 | * 61 | * @param context 上下文 62 | * @return 当前版本Code 63 | */ 64 | fun getVerCode(context: Context): Int { 65 | var verCode = -1 66 | try { 67 | val packageName = context.packageName 68 | verCode = context.packageManager 69 | .getPackageInfo(packageName, 0).versionCode 70 | } catch (e: PackageManager.NameNotFoundException) { 71 | e.printStackTrace() 72 | } 73 | 74 | return verCode 75 | } 76 | 77 | 78 | /** 79 | * 获取应用运行的最大内存 80 | * 81 | * @return 最大内存 82 | */ 83 | val maxMemory: Long 84 | get() = Runtime.getRuntime().maxMemory() / 1024 85 | 86 | 87 | /** 88 | * 得到软件显示版本信息 89 | * 90 | * @param context 上下文 91 | * @return 当前版本信息 92 | */ 93 | fun getVerName(context: Context): String { 94 | var verName = "" 95 | try { 96 | val packageName = context.packageName 97 | verName = context.packageManager 98 | .getPackageInfo(packageName, 0).versionName 99 | } catch (e: PackageManager.NameNotFoundException) { 100 | e.printStackTrace() 101 | } 102 | 103 | return verName 104 | } 105 | 106 | 107 | @SuppressLint("PackageManagerGetSignatures") 108 | /** 109 | * 获取应用签名 110 | * 111 | * @param context 上下文 112 | * @param pkgName 包名 113 | * @return 返回应用的签名 114 | */ 115 | fun getSign(context: Context, pkgName: String): String? { 116 | return try { 117 | @SuppressLint("PackageManagerGetSignatures") val pis = context.packageManager 118 | .getPackageInfo(pkgName, 119 | PackageManager.GET_SIGNATURES) 120 | hexDigest(pis.signatures[0].toByteArray()) 121 | } catch (e: PackageManager.NameNotFoundException) { 122 | e.printStackTrace() 123 | null 124 | } 125 | 126 | } 127 | 128 | /** 129 | * 将签名字符串转换成需要的32位签名 130 | * 131 | * @param paramArrayOfByte 签名byte数组 132 | * @return 32位签名字符串 133 | */ 134 | private fun hexDigest(paramArrayOfByte: ByteArray): String { 135 | val hexDigits = charArrayOf(48.toChar(), 49.toChar(), 50.toChar(), 51.toChar(), 52.toChar(), 53.toChar(), 54.toChar(), 55.toChar(), 56.toChar(), 57.toChar(), 97.toChar(), 98.toChar(), 99.toChar(), 100.toChar(), 101.toChar(), 102.toChar()) 136 | try { 137 | val localMessageDigest = MessageDigest.getInstance("MD5") 138 | localMessageDigest.update(paramArrayOfByte) 139 | val arrayOfByte = localMessageDigest.digest() 140 | val arrayOfChar = CharArray(32) 141 | var i = 0 142 | var j = 0 143 | while (true) { 144 | if (i >= 16) { 145 | return String(arrayOfChar) 146 | } 147 | val k = arrayOfByte[i].toInt() 148 | arrayOfChar[j] = hexDigits[0xF and k.ushr(4)] 149 | arrayOfChar[++j] = hexDigits[k and 0xF] 150 | i++ 151 | j++ 152 | } 153 | } catch (e: Exception) { 154 | e.printStackTrace() 155 | } 156 | 157 | return "" 158 | } 159 | 160 | 161 | /** 162 | * 获取设备的可用内存大小 163 | * 164 | * @param context 应用上下文对象context 165 | * @return 当前内存大小 166 | */ 167 | fun getDeviceUsableMemory(context: Context): Int { 168 | val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 169 | val mi = ActivityManager.MemoryInfo() 170 | am.getMemoryInfo(mi) 171 | // 返回当前系统的可用内存 172 | return (mi.availMem / (1024 * 1024)).toInt() 173 | } 174 | 175 | 176 | fun getMobileModel(): String { 177 | var model: String? = Build.MODEL 178 | model = model?.trim { it <= ' ' } ?: "" 179 | return model 180 | } 181 | 182 | /** 183 | * 获取手机系统SDK版本 184 | * 185 | * @return 如API 17 则返回 17 186 | */ 187 | val sdkVersion: Int 188 | get() = android.os.Build.VERSION.SDK_INT 189 | } 190 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/util/AsyncWriteThread.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base.util 2 | 3 | import android.os.Environment 4 | 5 | import java.io.File 6 | import java.io.IOException 7 | import java.util.concurrent.ConcurrentLinkedQueue 8 | 9 | import okio.Okio 10 | 11 | /** 12 | * 类描述:异步写入字符串到文件中 13 | * 创建人:yifei 14 | * 创建时间:2018/10/29 15 | * 修改人: 16 | * 修改时间: 17 | * 修改备注: 18 | */ 19 | class AsyncWriteThread(private val dirName: String, private val fileName: String) : Thread() { 20 | 21 | companion object { 22 | fun init(dirName: String, fileName: String) { 23 | val asyncWriteThread = AsyncWriteThread(dirName, fileName) 24 | 25 | 26 | } 27 | 28 | 29 | } 30 | 31 | private var isRunning = false 32 | private val lock = java.lang.Object() 33 | var file: File? = null 34 | private set 35 | private val mQueue = ConcurrentLinkedQueue() 36 | 37 | val isFinish: Boolean 38 | get() = mQueue.isEmpty() && !isRunning 39 | 40 | init { 41 | if (file == null) { 42 | file = initFile() 43 | } 44 | if (file != null) { 45 | isRunning = true 46 | } 47 | } 48 | 49 | fun enqueue(str: String) { 50 | mQueue.add(str) 51 | if (!isRunning) { 52 | awake() 53 | } 54 | } 55 | 56 | /** 57 | * 判断文件是否存在 58 | * 59 | * @return 60 | */ 61 | val isFileExist: Boolean 62 | get() = if (file == null) { 63 | false 64 | } else file!!.exists() && file!!.length() > 3 65 | 66 | 67 | /** 68 | * 删除文件 69 | */ 70 | fun deleteFile(): Boolean { 71 | return if (file == null) { 72 | false 73 | } else file!!.exists() && file!!.delete() 74 | } 75 | 76 | private fun awake() { 77 | synchronized(lock) { 78 | lock.notify() 79 | } 80 | } 81 | 82 | override fun run() { 83 | while (true) { 84 | synchronized(lock) { 85 | isRunning = true 86 | while (!mQueue.isEmpty()) { 87 | try { 88 | //pop出队列的头字符串写入到文件中 89 | write(mQueue.poll()) 90 | } catch (e: Exception) { 91 | e.printStackTrace() 92 | } 93 | 94 | } 95 | isRunning = false 96 | try { 97 | lock.wait() 98 | } catch (e: InterruptedException) { 99 | e.printStackTrace() 100 | } 101 | 102 | } 103 | 104 | } 105 | } 106 | 107 | private fun write(json: String) { 108 | try { 109 | if (file == null || !file!!.exists()) { 110 | file = initFile() 111 | } 112 | Okio.buffer(Okio.appendingSink(file!!)) 113 | .writeUtf8(json) 114 | .close() 115 | } catch (e: Exception) { 116 | e.printStackTrace() 117 | } 118 | 119 | } 120 | 121 | private fun initFile(): File? { 122 | if (exist()) { 123 | val dir = File(dirName) 124 | if (!dir.exists()) { 125 | dir.mkdirs() 126 | } 127 | val file = File(this.dirName, fileName) 128 | try { 129 | if (!file.exists()) { 130 | if (file.createNewFile()) { 131 | return file 132 | } 133 | } else { 134 | return file 135 | } 136 | } catch (e: IOException) { 137 | e.printStackTrace() 138 | } 139 | 140 | } 141 | return null 142 | } 143 | 144 | private fun exist(): Boolean { 145 | return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/util/FileUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base.util 2 | 3 | import android.content.Context 4 | import android.os.Environment 5 | 6 | /** 7 | * 类描述: 8 | * 创建人:yifei 9 | * 创建时间:2018/11/1 10 | * 修改人: 11 | * 修改时间: 12 | * 修改备注: 13 | */ 14 | class FileUtil { 15 | 16 | companion object { 17 | 18 | /** 19 | * /data/data/com.xxx.xxx/cache 20 | */ 21 | fun getCacheDir(context: Context): String { 22 | return if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED 23 | || !Environment.isExternalStorageRemovable()) 24 | context.externalCacheDir!!.path 25 | else 26 | context.cacheDir.path 27 | } 28 | 29 | /** 30 | * /data/data/com.xxx.xxx/files 31 | */ 32 | fun getFilesDir(context: Context): String { 33 | return if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED 34 | || !Environment.isExternalStorageRemovable()) 35 | context.externalCacheDir!!.path 36 | else 37 | context.filesDir.path 38 | } 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/util/LogUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base.util 2 | 3 | import android.util.Log 4 | 5 | /** 6 | * 类描述:这里可以使用任意自己熟悉或者流行的log库,而不用修改业务层的log代码 7 | * 创建人:yifei 8 | * 创建时间:2018/10/29 9 | * 修改人: 10 | * 修改时间: 11 | * 修改备注: 12 | */ 13 | class LogUtil { 14 | 15 | companion object { 16 | // 设置为false则可以使得Log不输出 17 | var enable: Boolean = true 18 | 19 | fun isEnable(enable: Boolean) { 20 | LogUtil.enable = enable 21 | } 22 | 23 | fun v(tag: String, msg: String) { 24 | if (enable) { 25 | Log.v(tag, msg) 26 | } 27 | } 28 | 29 | fun d(tag: String, msg: String) { 30 | if (enable) { 31 | Log.d(tag, msg) 32 | } 33 | } 34 | 35 | fun i(tag: String, msg: String) { 36 | if (enable) { 37 | Log.i(tag, msg) 38 | } 39 | } 40 | 41 | fun e(tag: String, msg: String) { 42 | if (enable) { 43 | Log.e(tag, msg) 44 | } 45 | } 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /base/src/main/java/com/sjtu/yifei/base/util/NetworkUtil.kt: -------------------------------------------------------------------------------- 1 | package com.sjtu.yifei.base.util 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.NetworkInfo 6 | import android.telephony.TelephonyManager 7 | import java.io.IOException 8 | import java.net.HttpURLConnection 9 | import java.net.NetworkInterface 10 | import java.net.SocketException 11 | import java.net.URL 12 | 13 | /** 14 | * 类描述: 15 | * 创建人:yifei 16 | * 创建时间:2018/10/25 17 | * 修改人: 18 | * 修改时间: 19 | * 修改备注: 20 | */ 21 | class NetworkUtil { 22 | init { 23 | throw Error("Do not need instantiate!") 24 | } 25 | 26 | companion object { 27 | var NET_CNNT_BAIDU_OK = 1 // NetworkAvailable 28 | var NET_CNNT_BAIDU_TIMEOUT = 2 // no NetworkAvailable 29 | var NET_NOT_PREPARE = 3 // Net no ready 30 | var NET_ERROR = 4 //net error 31 | private const val TIMEOUT = 3000 // TIMEOUT 32 | /** 33 | * check NetworkAvailable 34 | * 35 | * @param context 36 | * @return 37 | */ 38 | @JvmStatic 39 | fun isNetworkAvailable(context: Context): Boolean { 40 | val manager = context.applicationContext.getSystemService( 41 | Context.CONNECTIVITY_SERVICE) as ConnectivityManager 42 | val info = manager.activeNetworkInfo 43 | return !(null == info || !info.isAvailable) 44 | } 45 | 46 | /** 47 | * 得到ip地址 48 | * 49 | * @return 50 | */ 51 | @JvmStatic 52 | fun getLocalIpAddress(): String { 53 | var ret = "" 54 | try { 55 | val en = NetworkInterface.getNetworkInterfaces() 56 | while (en.hasMoreElements()) { 57 | val enumIpAddress = en.nextElement().inetAddresses 58 | while (enumIpAddress.hasMoreElements()) { 59 | val netAddress = enumIpAddress.nextElement() 60 | if (!netAddress.isLoopbackAddress) { 61 | ret = netAddress.hostAddress.toString() 62 | } 63 | } 64 | } 65 | } catch (ex: SocketException) { 66 | ex.printStackTrace() 67 | } 68 | 69 | return ret 70 | } 71 | 72 | 73 | /** 74 | * ping "http://www.baidu.com" 75 | * 76 | * @return 77 | */ 78 | @JvmStatic 79 | private fun pingNetWork(url: String): Boolean { 80 | var result = false 81 | var httpUrl: HttpURLConnection? = null 82 | try { 83 | httpUrl = URL(url) 84 | .openConnection() as HttpURLConnection 85 | httpUrl.connectTimeout = TIMEOUT 86 | httpUrl.connect() 87 | result = true 88 | } catch (e: IOException) { 89 | } finally { 90 | if (null != httpUrl) { 91 | httpUrl.disconnect() 92 | } 93 | } 94 | return result 95 | } 96 | 97 | /** 98 | * check is3G 99 | * 100 | * @param context 101 | * @return boolean 102 | */ 103 | @JvmStatic 104 | fun is3G(context: Context): Boolean { 105 | val connectivityManager = context 106 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 107 | val activeNetInfo = connectivityManager.activeNetworkInfo 108 | return activeNetInfo != null && activeNetInfo.type == ConnectivityManager.TYPE_MOBILE 109 | } 110 | 111 | /** 112 | * isWifi 113 | * 114 | * @param context 115 | * @return boolean 116 | */ 117 | @JvmStatic 118 | fun isWifi(context: Context): Boolean { 119 | val connectivityManager = context 120 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 121 | val activeNetInfo = connectivityManager.activeNetworkInfo 122 | return activeNetInfo != null && activeNetInfo.type == ConnectivityManager.TYPE_WIFI 123 | } 124 | 125 | /** 126 | * is2G 127 | * 128 | * @param context 129 | * @return boolean 130 | */ 131 | @JvmStatic 132 | fun is2G(context: Context): Boolean { 133 | val connectivityManager = context 134 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 135 | val activeNetInfo = connectivityManager.activeNetworkInfo 136 | return activeNetInfo != null && (activeNetInfo.subtype == TelephonyManager.NETWORK_TYPE_EDGE 137 | || activeNetInfo.subtype == TelephonyManager.NETWORK_TYPE_GPRS || activeNetInfo 138 | .subtype == TelephonyManager.NETWORK_TYPE_CDMA) 139 | } 140 | 141 | /** 142 | * is wifi on 143 | */ 144 | @JvmStatic 145 | fun isWifiEnabled(context: Context): Boolean { 146 | val mgrConn = context 147 | .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 148 | val mgrTel = context 149 | .getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager 150 | return mgrConn.activeNetworkInfo != null && mgrConn 151 | .activeNetworkInfo.state == NetworkInfo.State.CONNECTED || mgrTel 152 | .networkType == TelephonyManager.NETWORK_TYPE_UMTS 153 | } 154 | 155 | } 156 | 157 | } -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_add_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #FFFFFF 7 | 8 | -------------------------------------------------------------------------------- /base/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 76dp 7 | 16dp 8 | 16dp 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | base 3 | 4 | -------------------------------------------------------------------------------- /base/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 16 | 17 |