├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── SAF_Framework.png ├── app ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ ├── androidTest │ └── java │ │ └── app │ │ └── magicwindow │ │ └── cn │ │ └── testsaf │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── test │ │ └── saf │ │ ├── activity │ │ ├── AnnotationActivity.java │ │ ├── MainActivity.java │ │ ├── SplashActivity.java │ │ └── User.java │ │ ├── adapter │ │ ├── AnnotationAdapter.java │ │ ├── DividerGridItemDecoration.java │ │ └── NormalTextViewHolder.java │ │ ├── app │ │ ├── BaseActivity.java │ │ ├── BaseFragment.java │ │ └── DemoApp.java │ │ ├── config │ │ └── Config.java │ │ ├── domain │ │ ├── HttpResponse.java │ │ └── MMPicsResponse.java │ │ ├── fragment │ │ ├── CacheFragment.java │ │ ├── EventBusFragment.java │ │ ├── GeneralAnnotationFragment.java │ │ ├── HomeFragment.java │ │ ├── LogFragment.java │ │ ├── PermissionFragment.java │ │ ├── RouterFragment.java │ │ └── SqliteORMFragment.java │ │ ├── menu │ │ └── MenuManager.java │ │ ├── ui │ │ └── GridRecyclerView.java │ │ └── utils │ │ └── DoubleClickExitUtils.java │ └── res │ ├── anim │ ├── grid_layout_animation.xml │ └── slide_in_bottom.xml │ ├── drawable-hdpi │ └── ic_menu_black_24dp.png │ ├── drawable-nodpi │ └── drawer_background.png │ ├── drawable-xxhdpi │ ├── default_girl.png │ ├── enter.png │ ├── ic_add_black.png │ └── ic_edit.png │ ├── drawable │ ├── ic_annotation.xml │ ├── ic_cache.xml │ ├── ic_eventbus.xml │ ├── ic_log.xml │ ├── ic_permission.xml │ ├── ic_router.xml │ ├── ic_safinfo.xml │ ├── ic_sqlite.xml │ ├── selectable_item_background.xml │ └── textview_border.xml │ ├── layout │ ├── activity_annotation.xml │ ├── activity_main.xml │ ├── activity_main2.xml │ ├── activity_splash.xml │ ├── cell_general_annotation.xml │ ├── cell_second.xml │ ├── drawer_header.xml │ ├── fragment_general_annotation.xml │ ├── fragment_home.xml │ └── fragment_permission.xml │ ├── menu │ └── drawer.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── dependencies.gradle ├── docs ├── cache.md ├── event_bus.md ├── rest_client.md ├── rxasynctask.md ├── sqlite_orm.md └── utils.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── logo.png ├── saf-cache ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── safframework │ │ ├── cache │ │ ├── Cache.java │ │ ├── CacheException.java │ │ └── ParcelableUtils.java │ │ └── prefs │ │ ├── AppPrefs.java │ │ └── BasePrefs.java │ └── res │ └── values │ └── strings.xml ├── saf-core ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── safframework │ │ └── saf │ │ ├── app │ │ ├── SAFActivity.java │ │ ├── SAFApp.java │ │ ├── SAFAppCompatActivity.java │ │ ├── SAFFragment.java │ │ ├── SAFFragmentActivity.java │ │ ├── SafeHandler.java │ │ └── package-info.java │ │ ├── async │ │ ├── RetryWithDelay.java │ │ └── RxAsyncTask.java │ │ ├── concurrent │ │ ├── BackgroundExecutor.java │ │ └── BackgroundPriorityThreadFactory.java │ │ ├── config │ │ ├── SAFConstant.java │ │ └── package-info.java │ │ ├── eventbus │ │ ├── AnnotatedFinder.java │ │ ├── BackgroundPoster.java │ │ ├── DeadEvent.java │ │ ├── EventBus.java │ │ ├── EventBusException.java │ │ ├── EventBusUtils.java │ │ ├── EventHandler.java │ │ ├── EventProducer.java │ │ ├── Produce.java │ │ ├── ScheduledEvent.java │ │ ├── ScheduledPoster.java │ │ ├── Subscribe.java │ │ ├── SubscriberMethod.java │ │ └── ThreadMode.java │ │ ├── orm │ │ ├── DBDomain.java │ │ ├── DBManager.java │ │ ├── DBVersionPrefs.java │ │ ├── DatabaseHelper.java │ │ ├── DomainInfo.java │ │ ├── NaturalOrderComparator.java │ │ ├── SQLiteUtils.java │ │ ├── TableInfo.java │ │ └── annotation │ │ │ ├── Column.java │ │ │ └── Table.java │ │ ├── parcel │ │ └── AbstractParcelable.java │ │ ├── recyclerview │ │ └── OnItemClickListener.java │ │ ├── rest │ │ ├── CloseOperation.java │ │ ├── ConnectionFactory.java │ │ ├── HttpResponseHandler.java │ │ ├── HttpResponseRetryHandler.java │ │ ├── HttpRetryStrategy.java │ │ ├── Operation.java │ │ ├── RestClient.java │ │ ├── RestConstant.java │ │ ├── RestException.java │ │ ├── RestOutputStream.java │ │ ├── RestUtil.java │ │ └── UrlBuilder.java │ │ ├── utils │ │ ├── AsyncTaskExecutor.java │ │ ├── DeviceUtils.java │ │ ├── NoEmptyHashMap.java │ │ ├── ReflectionUtils.java │ │ ├── ResourceUtil.java │ │ ├── SAFUtils.java │ │ ├── StringUtils.java │ │ ├── ToastUtils.java │ │ └── ViewUtils.java │ │ └── view │ │ ├── LightDialog.java │ │ └── package-info.java │ └── res │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── saf-permission ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── safframework │ │ └── permission │ │ ├── Permission.java │ │ ├── RxPermissions.java │ │ └── RxPermissionsFragment.java │ └── res │ └── values │ └── strings.xml ├── saf-queue ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── safframework │ │ └── queue │ │ └── ExampleInstrumentedTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── safframework │ │ └── queue │ │ ├── Operation.java │ │ ├── OperationRunnable.java │ │ └── Queue.java │ └── res │ └── values │ └── strings.xml ├── saf-rxlifecycle ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── safframework │ │ └── lifecycle │ │ ├── BindingFragment.java │ │ ├── BindingV4Fragment.java │ │ ├── LifecyclePublisher.java │ │ ├── LifecycleTransformer.java │ │ └── RxLifecycle.java │ └── res │ └── values │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | 4 | .DS_Store 5 | /build 6 | /captures 7 | 8 | # Built application files 9 | *.apk 10 | *.ap_ 11 | 12 | # Files for the Dalvik VM 13 | *.dex 14 | 15 | # Java class files 16 | *.class 17 | 18 | # Generated files 19 | bin/ 20 | gen/ 21 | target/ 22 | 23 | # Gradle files 24 | .gradle/ 25 | build/ 26 | 27 | # Local configuration file (sdk path, etc) 28 | local.properties 29 | 30 | .classpath 31 | .project 32 | .settings/ 33 | 34 | .idea/ 35 | /.idea/workspace.xml 36 | /.idea/libraries 37 | *.iml 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | sudo: false 4 | 5 | android: 6 | components: 7 | - tools 8 | - build-tools-27.0.2 9 | - android-27 10 | - extra-google-m2repository 11 | - extra-android-m2repository 12 | - extra-android-support 13 | licenses: 14 | - 'android-sdk-preview-license-.+' 15 | - 'android-sdk-license-.+' 16 | - 'google-gdk-license-.+' 17 | 18 | before_install: 19 | - chmod +x gradlew 20 | - mkdir -p "$ANDROID_HOME/licenses" || true 21 | - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" 22 | - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" 23 | - yes | sdkmanager "platforms;android-27" 24 | 25 | script: 26 | - ./gradlew assembleRelease -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | SAF 2 | === 3 | 1.1.4之前的开发都是在google code上托管的,从1.1.4开始记录版本日志。 4 | 5 | Version 1.1.19 6 | --- 7 | 2016-05-17 8 | * 针对android6.0的权限需求,imageloader做了修改 9 | * 新增NoEmptyHashMap 10 | * 优化RxImageLoader 11 | * 新增aspectj的支持, 增加了@LogMethod @Trace @Cacheable @Safe @Async @Prefs 等注解,这些注解无需使用反射 12 | * 新增RxAsyncTask 13 | 14 | Version 1.1.18 15 | --- 16 | 2016-02-15 17 | * 修复底层的bug 优化代码 18 | * 项目结构由原先的eclipse转变成android studio 19 | * 增加工具类Preconditions 20 | * 增加RxEventBus RxImageloader,这2个模块还处于试验阶段,不推荐使用 21 | * 增加android5.0以后判断app是否处于前台的方法 22 | * 增加cache类 23 | * 增加SAFRecycleAdapter类 24 | * 去掉原先插件化的类,打算重新设计这个模块 25 | 26 | Version 1.1.17 27 | --- 28 | 2015-08-28 29 | * 增加HttpRetryStrategy接口 30 | * imageloader的线程数,由当前手机的cpu数量决定 31 | * 增加新型的反射工具类 32 | 33 | Version 1.1.16 34 | --- 35 | 2015-08-04 36 | * 删除RestClient的BinaryResponseHandler 37 | * RestClient增加response的header支持 38 | * 修复android L以后版本无法使用imageloader的bug 39 | 40 | Version 1.1.15 41 | --- 42 | 2015-07-21 43 | * 增加下载模块 44 | * 修复删除图片缓存时,报的空指针bug 45 | 46 | Version 1.1.14 47 | --- 48 | 2015-06-18 49 | * 增加AbstractParcelable 50 | * 精简SAFActivity、SAFFragmentActivity 51 | * 删除cn.salesuite.saf.net包,减少saf包的大小 52 | 53 | Version 1.1.13 54 | --- 55 | 2015-05-06 56 | * RestClient增加https的支持 57 | * 修复L的bug 58 | * InjectExtra增加默认值 59 | * AsyncTaskExecutor增加android.os.AsyncTask的支持 60 | 61 | Version 1.1.12 62 | --- 63 | 2015-03-11 64 | * 增加OnClick的hocker接口,before和after方法,用于埋点 65 | * 增加OnItemClick的hocker接口,before和after方法,用于埋点 66 | * 增加工具方法 67 | * 增加RestClient的BinaryResponseHandler(曹亚民) 68 | * 增加全新的AsyncTask(曹亚民) 69 | 70 | Version 1.1.11 71 | --- 72 | 2015-02-24 73 | * 增加工具类 74 | * 优化Router,Router增加便于埋点的接口 75 | * 修复ToastUtils的bug 76 | 77 | Version 1.1.10 78 | --- 79 | 2014-12-24 80 | * eventbus增加线程模式ScheduleBackgroundThread 81 | * 增加List的帮助类Lists 82 | * BasePrefs中各个put方法,都包含save()方法,避免BasePrefs再调用save() 83 | * 修复bug 84 | 85 | Version 1.1.9 86 | --- 87 | 2014-11-12 88 | * 完善工具类命名规范 89 | 90 | Version 1.1.8 91 | --- 92 | 2014-11-10 93 | * 完善SAFAdapter 94 | * Injector增加Adapter的支持 95 | * 完善多个工具类 96 | 97 | Version 1.1.7 98 | --- 99 | 2014-10-10 100 | * 优化ImageLoader 101 | * 增加@OnTouch 102 | * 增加SAFAdapter 103 | * 完善L、ToastUtil 104 | 105 | Version 1.1.6 106 | --- 107 | 2014-09-17 108 | * 优化RestClient,增加重试机制 109 | * 增加@OnLongClick 110 | 111 | Version 1.1.5 112 | --- 113 | 2014-09-11 114 | * 优化ImageLoader,disklrucache升级到2.0.3 115 | 116 | Version 1.1.4 117 | --- 118 | 2014-09-06 119 | * 增加@InjectViews、@OnClick、@OnItemClick 120 | * 增加日志框架L 121 | * 优化@InjectView 122 | -------------------------------------------------------------------------------- /SAF_Framework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/SAF_Framework.png -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.hujiang.android-aspectjx' 3 | 4 | def cfg = rootProject.ext.configuration // 配置 5 | def libs = rootProject.ext.libraries // 库 6 | 7 | android { 8 | compileSdkVersion cfg.compileVersion 9 | buildToolsVersion cfg.buildToolsVersion 10 | //兼容API 23干掉了httpClient,手动加入httpClient库,或者将org.apache.http.legacy.jar放到lib文件中 11 | useLibrary 'org.apache.http.legacy' 12 | 13 | defaultConfig { 14 | applicationId "app.magicwindow.cn.testsaf" 15 | minSdkVersion 15 16 | targetSdkVersion cfg.targetSdk 17 | versionCode 1 18 | versionName "1.0" 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | // 多个jar包里包含了同样的文件(NOTICE.txt),导致打包时因为担心相互覆盖问题而提示出错, 去掉这2个文件 29 | packagingOptions { 30 | exclude 'META-INF/LICENSE.txt' 31 | exclude 'META-INF/NOTICE.txt' 32 | } 33 | 34 | compileOptions { 35 | sourceCompatibility = "1.7" 36 | targetCompatibility = "1.7" 37 | } 38 | 39 | lintOptions { 40 | abortOnError false 41 | } 42 | } 43 | 44 | allprojects { 45 | repositories { 46 | jcenter() 47 | maven { 48 | url { 49 | "https://jitpack.io" 50 | } 51 | } 52 | } 53 | } 54 | 55 | dependencies { 56 | compile fileTree(include: ['*.jar'], dir: 'libs') 57 | testCompile "junit:junit:${libs.junit}" 58 | compile "com.android.support:appcompat-v7:${libs.support_appcompat_v7}" 59 | compile 'com.github.clans:fab:1.6.4' 60 | compile 'thereisnospon.codeview:codeview:0.3.1' 61 | 62 | compile 'com.safframework:saf-queue:1.0.0' 63 | 64 | implementation 'com.safframework.router:saf-router:1.1.4' 65 | implementation 'com.safframework.router:saf-router-annotation:1.1.1' 66 | annotationProcessor 'com.safframework.router:saf-router-compiler:1.1.1' 67 | 68 | implementation 'com.safframework.injectview:saf-injectview:1.1.1' 69 | implementation 'com.safframework.injectview:saf-injectview-annotation:1.1.1' 70 | annotationProcessor 'com.safframework.injectview:saf-injectview-compiler:1.1.1' 71 | 72 | compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' 73 | 74 | compile project(':saf-core') 75 | compile project(':saf-permission') 76 | } 77 | -------------------------------------------------------------------------------- /app/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Nov 05 19:06:23 CST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 7 | -------------------------------------------------------------------------------- /app/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/androidTest/java/app/magicwindow/cn/testsaf/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package app.magicwindow.cn.testsaf; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 36 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/activity/AnnotationActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.activity; 2 | 3 | import android.os.Bundle; 4 | import android.widget.TextView; 5 | 6 | import com.safframework.injectview.annotations.InjectExtra; 7 | import com.safframework.injectview.annotations.InjectView; 8 | import com.safframework.router.RouterRule; 9 | import com.safframework.tony.common.utils.Preconditions; 10 | import com.test.saf.R; 11 | import com.test.saf.app.BaseActivity; 12 | 13 | import thereisnospon.codeview.CodeView; 14 | import thereisnospon.codeview.CodeViewTheme; 15 | 16 | /** 17 | * Created by Tony Shen on 2016/11/22. 18 | */ 19 | @RouterRule(url={"annotationName/:anno_name"}) 20 | public class AnnotationActivity extends BaseActivity { 21 | 22 | @InjectView(R.id.text) 23 | CodeView text; 24 | 25 | @InjectView(R.id.title) 26 | TextView title; 27 | 28 | @InjectExtra(key = ANNO_NAME) 29 | String annotationName; 30 | 31 | public final static String ANNO_NAME = "anno_name"; 32 | 33 | @Override 34 | public void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_annotation); 37 | 38 | initViews(); 39 | } 40 | 41 | private void initViews() { 42 | 43 | if (Preconditions.isNotBlank(annotationName)) { 44 | title.setText(annotationName+"使用方法"); 45 | } 46 | 47 | String content = getUsage(annotationName); 48 | 49 | if (Preconditions.isNotBlank(content)) { 50 | text.setTheme(CodeViewTheme.ANDROIDSTUDIO).fillColor(); 51 | text.showCode(content); 52 | } 53 | } 54 | 55 | private String getUsage(String annotationName) { 56 | 57 | String result = null; 58 | if ("@Async".equals(annotationName)) { 59 | result = " @Async\n" + 60 | " private void useAsync() {\n" + 61 | " Log.e(TAG, \" thread=\" + Thread.currentThread().getId());\n" + 62 | " Log.e(TAG, \"ui thread=\" + Looper.getMainLooper().getThread().getId" + 63 | "());\n" + 64 | " }"; 65 | } else if ("@Cacheable".equals(annotationName)) { 66 | result = " @Cacheable(key = \"user\")\n" + 67 | " private User initData() {\n" + 68 | " User user = new User();\n" + 69 | " user.userName = \"tony\";\n" + 70 | " user.password = \"123456\";\n" + 71 | " return user;\n" + 72 | " }"; 73 | } 74 | 75 | return result; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.NavigationView; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.view.GravityCompat; 7 | import android.support.v4.widget.DrawerLayout; 8 | import android.support.v7.app.ActionBar; 9 | import android.support.v7.widget.Toolbar; 10 | import android.view.KeyEvent; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | 14 | import com.safframework.injectview.annotations.InjectView; 15 | import com.safframework.tony.common.utils.Preconditions; 16 | import com.test.saf.R; 17 | import com.test.saf.app.BaseActivity; 18 | import com.test.saf.fragment.HomeFragment; 19 | import com.test.saf.menu.MenuManager; 20 | import com.test.saf.utils.DoubleClickExitUtils; 21 | 22 | public class MainActivity extends BaseActivity { 23 | 24 | @InjectView(R.id.drawer_layout) 25 | DrawerLayout drawerLayout; 26 | 27 | @InjectView(R.id.navigation_view) 28 | NavigationView navigationView; 29 | 30 | @InjectView(R.id.toolbar) 31 | Toolbar toolbar; 32 | 33 | private MenuManager menuManager; 34 | private Fragment mContent; 35 | private DoubleClickExitUtils doubleClickExitHelper; 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_main); 41 | 42 | initViews(); 43 | initData(); 44 | } 45 | 46 | private void initViews() { 47 | initToolbar(); 48 | navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { 49 | @Override 50 | public boolean onNavigationItemSelected(MenuItem menuItem) { 51 | 52 | if (Preconditions.isNotBlank(menuItem.getTitle())) { 53 | toolbar.setTitle(menuItem.getTitle()); 54 | } 55 | showMenu(menuItem); 56 | menuItem.setChecked(true); 57 | 58 | if (drawerLayout.isDrawerOpen(GravityCompat.START)) { 59 | drawerLayout.closeDrawer(GravityCompat.START); 60 | } 61 | return true; 62 | } 63 | }); 64 | } 65 | 66 | private void initData() { 67 | 68 | doubleClickExitHelper = new DoubleClickExitUtils(this); 69 | 70 | if (mContent == null) { 71 | menuManager = MenuManager.getInstance(getSupportFragmentManager()); 72 | mContent = new HomeFragment(); 73 | } 74 | 75 | getSupportFragmentManager().beginTransaction().add(R.id.content_frame,mContent, MenuManager.MenuType.HOME.getTitle()).commit(); 76 | } 77 | 78 | private void initToolbar() { 79 | setSupportActionBar(toolbar); 80 | final ActionBar actionBar = getSupportActionBar(); 81 | 82 | if (actionBar != null) { 83 | actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp); 84 | actionBar.setDisplayHomeAsUpEnabled(true); 85 | } 86 | 87 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 88 | 89 | @Override 90 | public void onClick(View v) { 91 | drawerLayout.openDrawer(GravityCompat.START); 92 | } 93 | }); 94 | } 95 | 96 | private void showMenu(MenuItem menuItem) { 97 | 98 | switch (menuItem.getItemId()) { 99 | case R.id.drawer_saf: 100 | menuManager.show(MenuManager.MenuType.HOME); 101 | break; 102 | case R.id.drawer_anno: 103 | menuManager.show(MenuManager.MenuType.ANNOTATION); 104 | break; 105 | case R.id.drawer_eventbus: 106 | menuManager.show(MenuManager.MenuType.EVENTBUS); 107 | break; 108 | case R.id.drawer_sqlite: 109 | menuManager.show(MenuManager.MenuType.SQLITE); 110 | break; 111 | case R.id.drawer_router: 112 | menuManager.show(MenuManager.MenuType.ROUTER); 113 | break; 114 | case R.id.drawer_cache: 115 | menuManager.show(MenuManager.MenuType.CACHE); 116 | break; 117 | case R.id.drawer_log: 118 | menuManager.show(MenuManager.MenuType.LOG); 119 | break; 120 | case R.id.drawer_permission: 121 | menuManager.show(MenuManager.MenuType.PERMISSION); 122 | break; 123 | default: 124 | break; 125 | } 126 | } 127 | 128 | //重写物理按键的返回逻辑(实现返回键跳转到上一页) 129 | @Override 130 | public boolean onKeyDown(int keyCode, KeyEvent event) { 131 | //用户触摸返回键 132 | if(keyCode == KeyEvent.KEYCODE_BACK){ 133 | doubleClickExitHelper.onKeyDown(keyCode, event); 134 | return true; 135 | } 136 | return super.onKeyDown(keyCode, event); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/activity/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.activity; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.util.DisplayMetrics; 6 | 7 | import com.safframework.log.L; 8 | import com.test.saf.R; 9 | import com.test.saf.app.BaseActivity; 10 | import com.test.saf.config.Config; 11 | 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import io.reactivex.Observable; 15 | import io.reactivex.android.schedulers.AndroidSchedulers; 16 | import io.reactivex.annotations.NonNull; 17 | import io.reactivex.functions.Consumer; 18 | 19 | 20 | /** 21 | * Created by Tony Shen on 2016/12/5. 22 | */ 23 | 24 | public class SplashActivity extends BaseActivity { 25 | 26 | @Override 27 | public void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_splash); 30 | initData(); 31 | } 32 | 33 | private void initData() { 34 | DisplayMetrics metric = new DisplayMetrics(); 35 | getWindowManager().getDefaultDisplay().getMetrics(metric); 36 | int width = metric.widthPixels; // 屏幕宽度(像素) 37 | int height = metric.heightPixels; // 屏幕高度(像素) 38 | float density = metric.density; // 屏幕密度(0.75 / 1.0 / 1.5 /2.0) 39 | L.i("device screen: " + width + "*" + height + " desity: " + density); 40 | Config.height = height; 41 | Config.width = width; 42 | Config.density = density; 43 | 44 | loadingNext(); 45 | } 46 | 47 | /** 48 | * 跳转到主页面 49 | */ 50 | private void loadingNext() { 51 | 52 | // 延迟2秒跳到首页 53 | Observable.timer(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) 54 | .subscribe(new Consumer() { 55 | @Override 56 | public void accept(@NonNull Long aLong) throws Exception { 57 | Intent i = new Intent(mContext, MainActivity.class); 58 | startActivity(i); 59 | finish(); 60 | } 61 | }); 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/activity/User.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.activity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by Tony Shen on 16/2/5. 7 | */ 8 | public class User implements Serializable{ 9 | 10 | public String userName; 11 | public String password; 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/adapter/AnnotationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.test.saf.R; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Created by Tony Shen on 2016/11/22. 14 | */ 15 | 16 | public class AnnotationAdapter extends RecyclerView.Adapter { 17 | 18 | private List mList; 19 | 20 | public AnnotationAdapter(List data) { 21 | mList = data; 22 | } 23 | 24 | @Override 25 | public NormalTextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 26 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_general_annotation, parent, false); 27 | return new NormalTextViewHolder(view); 28 | } 29 | 30 | @Override 31 | public void onBindViewHolder(NormalTextViewHolder holder, int position) { 32 | holder.name.setText((String)mList.get(position)); 33 | } 34 | 35 | @Override 36 | public int getItemCount() { 37 | return mList!=null?mList.size():0; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/adapter/NormalTextViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import com.safframework.injectview.Injector; 8 | import com.safframework.injectview.annotations.InjectView; 9 | import com.test.saf.R; 10 | 11 | 12 | /** 13 | * Created by Tony Shen on 2016/12/24. 14 | */ 15 | 16 | public class NormalTextViewHolder extends RecyclerView.ViewHolder { 17 | 18 | @InjectView(R.id.name) 19 | TextView name; 20 | 21 | public NormalTextViewHolder(View itemView) { 22 | super(itemView); 23 | Injector.injectInto(this,itemView); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/app/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.app; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.safframework.injectview.Injector; 6 | import com.safframework.saf.app.SAFAppCompatActivity; 7 | 8 | 9 | /** 10 | * Created by Tony Shen on 15/11/19. 11 | */ 12 | public class BaseActivity extends SAFAppCompatActivity { 13 | 14 | public void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | } 17 | 18 | public void setContentView(int layoutResID) { 19 | super.setContentView(layoutResID); 20 | Injector.injectInto(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/app/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.app; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentManager; 5 | 6 | import com.safframework.cache.Cache; 7 | import com.safframework.saf.app.SAFFragment; 8 | 9 | 10 | /** 11 | * Created by Tony Shen on 15/11/19. 12 | */ 13 | public class BaseFragment extends SAFFragment { 14 | 15 | protected DemoApp app; 16 | protected FragmentManager fmgr; 17 | protected Cache mCache; 18 | 19 | @Override 20 | public void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | 23 | app = DemoApp.getInstance(); 24 | fmgr = getFragmentManager(); 25 | 26 | mCache = Cache.get(mContext); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/app/DemoApp.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.app; 2 | 3 | import com.safframework.queue.Queue; 4 | import com.safframework.router.RouterManager; 5 | import com.safframework.saf.app.SAFApp; 6 | 7 | /** 8 | * Created by Tony Shen on 15/11/19. 9 | */ 10 | public class DemoApp extends SAFApp { 11 | 12 | private static DemoApp mInstance = null; 13 | 14 | public Queue queue; 15 | 16 | public static DemoApp getInstance() { 17 | return mInstance; 18 | } 19 | 20 | public void onCreate() { 21 | 22 | super.onCreate(); 23 | mInstance = this; 24 | 25 | initData(); 26 | } 27 | 28 | private void initData() { 29 | 30 | RouterManager.init(this);// 这一步是必须的,用于初始化Router 31 | 32 | // 初始化队列 33 | queue = new Queue("saf_queue"); 34 | queue.start(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/config/Config.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.config; 2 | 3 | /** 4 | * Created by Tony Shen on 2016/12/5. 5 | */ 6 | 7 | public class Config { 8 | 9 | public static float density; // 屏幕密度 10 | public static int height; // 屏幕高度 11 | public static int width; // 屏幕宽度 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/domain/HttpResponse.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.domain; 2 | 3 | import com.safframework.saf.parcel.AbstractParcelable; 4 | 5 | /** 6 | * Created by Tony Shen on 2016/12/1. 7 | */ 8 | 9 | public class HttpResponse extends AbstractParcelable { 10 | 11 | public boolean status; 12 | public int total; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/domain/MMPicsResponse.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.domain; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by Tony Shen on 2016/12/1. 8 | */ 9 | 10 | public class MMPicsResponse extends HttpResponse { 11 | 12 | public List tngou; 13 | 14 | public static class Pic implements Serializable { 15 | 16 | private static final long serialVersionUID = 3376457660406783270L; 17 | 18 | public String img; 19 | public String title; 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/CacheFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import com.test.saf.app.BaseFragment; 4 | 5 | /** 6 | * Created by Tony Shen on 2016/11/22. 7 | */ 8 | 9 | public class CacheFragment extends BaseFragment { 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/EventBusFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import com.test.saf.app.BaseFragment; 4 | 5 | /** 6 | * Created by Tony Shen on 2016/11/22. 7 | */ 8 | 9 | public class EventBusFragment extends BaseFragment { 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/GeneralAnnotationFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.DividerItemDecoration; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.safframework.injectview.Injector; 12 | import com.safframework.injectview.annotations.InjectView; 13 | import com.safframework.router.Router; 14 | import com.safframework.saf.recyclerview.OnItemClickListener; 15 | import com.test.saf.R; 16 | import com.test.saf.adapter.AnnotationAdapter; 17 | import com.test.saf.app.BaseFragment; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | 23 | /** 24 | * Created by tony on 2016/11/20. 25 | */ 26 | 27 | public class GeneralAnnotationFragment extends BaseFragment { 28 | 29 | @InjectView(R.id.recyclerview) 30 | RecyclerView recyclerview; 31 | 32 | private List data = new ArrayList<>(); 33 | 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 | View v = inflater.inflate(R.layout.fragment_general_annotation, container, false); 37 | Injector.injectInto(this, v); 38 | initData(); 39 | 40 | return v; 41 | } 42 | 43 | private void initData() { 44 | 45 | data.clear(); 46 | data.add("@Async"); 47 | data.add("@Cacheable"); 48 | data.add("@LogMethod"); 49 | data.add("@Prefs"); 50 | data.add("@Safe"); 51 | data.add("@Trace"); 52 | 53 | recyclerview.setLayoutManager(new LinearLayoutManager(mContext));//这里用线性显示 类似于listview 54 | recyclerview.setAdapter(new AnnotationAdapter(data)); 55 | recyclerview.getLayoutManager().setAutoMeasureEnabled(true); 56 | recyclerview.setNestedScrollingEnabled(false); 57 | recyclerview.setHasFixedSize(false); 58 | recyclerview.addOnItemTouchListener(new OnItemClickListener(recyclerview){ 59 | 60 | @Override 61 | public void onItemClick(RecyclerView.ViewHolder holder, int position) { 62 | String annotationName = (String) data.get(position); 63 | Router.getInstance().open("annotationName/"+annotationName); 64 | } 65 | 66 | @Override 67 | public void onLongPress(RecyclerView.ViewHolder holder, int position) { 68 | } 69 | }); 70 | recyclerview.addItemDecoration(new DividerItemDecoration( 71 | mContext, DividerItemDecoration.VERTICAL)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import android.app.ProgressDialog; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.webkit.WebChromeClient; 9 | import android.webkit.WebSettings; 10 | import android.webkit.WebView; 11 | import android.webkit.WebViewClient; 12 | 13 | import com.safframework.injectview.Injector; 14 | import com.safframework.injectview.annotations.InjectView; 15 | import com.test.saf.R; 16 | import com.test.saf.app.BaseFragment; 17 | 18 | 19 | 20 | /** 21 | * Created by tony on 2016/11/20. 22 | */ 23 | 24 | public class HomeFragment extends BaseFragment { 25 | 26 | @InjectView(R.id.webview) 27 | WebView webview; 28 | 29 | private ProgressDialog progDailog; 30 | 31 | @Override 32 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 33 | View v = inflater.inflate(R.layout.fragment_home, container, false); 34 | Injector.injectInto(this, v); 35 | 36 | // progDailog = ProgressDialog.show(mContext, "Loading","Please wait...", true); 37 | // progDailog.setCancelable(false); 38 | // 39 | // initViews(); 40 | 41 | return v; 42 | } 43 | 44 | private void initViews() { 45 | webview.loadUrl("https://github.com/fengzhizi715/SAF"); 46 | //覆盖webView默认通过系统浏览器打开网页的方式 47 | webview.setWebViewClient(new WebViewClient(){ 48 | 49 | @Override 50 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 51 | progDailog.show(); 52 | view.loadUrl(url); 53 | 54 | return true; 55 | } 56 | @Override 57 | public void onPageFinished(WebView view, final String url) { 58 | progDailog.dismiss(); 59 | } 60 | }); 61 | //获取WebView类设置对象 62 | WebSettings settings = webview.getSettings(); 63 | //使webView支持js 64 | settings.setJavaScriptEnabled(true); 65 | //设置webView缓存模式 66 | webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//(优先使用缓存) 67 | //webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);//(不使用缓存) 68 | webview.setWebChromeClient(new WebChromeClient() {}); 69 | 70 | webview.getSettings().setAllowFileAccess(true); 71 | //如果访问的页面中有Javascript,则webview必须设置支持Javascript 72 | webview.getSettings().setJavaScriptEnabled(true); 73 | // webView.getSettings().setUserAgentString(MyApplication.getUserAgent()); 74 | webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); 75 | webview.getSettings().setAllowFileAccess(true); 76 | webview.getSettings().setAppCacheEnabled(true); 77 | webview.getSettings().setDomStorageEnabled(true); 78 | webview.getSettings().setDatabaseEnabled(true); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/LogFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import com.test.saf.app.BaseFragment; 4 | 5 | /** 6 | * Created by Tony Shen on 2016/11/22. 7 | */ 8 | 9 | public class LogFragment extends BaseFragment { 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/PermissionFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import android.Manifest; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | import android.widget.Toast; 10 | 11 | import com.jakewharton.rxbinding2.view.RxView; 12 | import com.safframework.injectview.Injector; 13 | import com.safframework.injectview.annotations.InjectView; 14 | import com.safframework.permission.RxPermissions; 15 | import com.test.saf.R; 16 | import com.test.saf.app.BaseFragment; 17 | 18 | import java.util.concurrent.TimeUnit; 19 | 20 | import io.reactivex.annotations.NonNull; 21 | import io.reactivex.functions.Consumer; 22 | 23 | /** 24 | * Created by Tony Shen on 2017/6/23. 25 | */ 26 | 27 | public class PermissionFragment extends BaseFragment { 28 | 29 | @InjectView(R.id.text1) 30 | TextView text1; 31 | 32 | @InjectView(R.id.text2) 33 | TextView text2; 34 | 35 | @Override 36 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 37 | View v = inflater.inflate(R.layout.fragment_permission, container, false); 38 | Injector.injectInto(this, v); 39 | 40 | initViews(); 41 | 42 | return v; 43 | } 44 | 45 | private void initViews() { 46 | 47 | RxView.clicks(text1).throttleFirst(500, TimeUnit.MILLISECONDS) 48 | .compose(new RxPermissions(mContext).ensure(Manifest.permission.CAMERA)) 49 | .subscribe(new Consumer() { 50 | @Override 51 | public void accept(@NonNull Boolean aBoolean) throws Exception { 52 | 53 | if (aBoolean) { 54 | Toast.makeText(mContext,"获取摄像头权限成功",Toast.LENGTH_SHORT).show(); 55 | } else { 56 | Toast.makeText(mContext,"获取摄像头权限失败",Toast.LENGTH_SHORT).show(); 57 | } 58 | } 59 | }); 60 | 61 | RxView.clicks(text2).throttleFirst(500, TimeUnit.MILLISECONDS) 62 | .compose(new RxPermissions(mContext).ensure(Manifest.permission.CALL_PHONE)) 63 | .subscribe(new Consumer() { 64 | @Override 65 | public void accept(@NonNull Boolean aBoolean) throws Exception { 66 | 67 | if (aBoolean) { 68 | Toast.makeText(mContext,"获取打电话权限成功",Toast.LENGTH_SHORT).show(); 69 | } else { 70 | Toast.makeText(mContext,"获取打电话权限失败",Toast.LENGTH_SHORT).show(); 71 | } 72 | } 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/RouterFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import com.test.saf.app.BaseFragment; 4 | 5 | /** 6 | * Created by Tony Shen on 2016/11/22. 7 | */ 8 | 9 | public class RouterFragment extends BaseFragment { 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/fragment/SqliteORMFragment.java: -------------------------------------------------------------------------------- 1 | package com.test.saf.fragment; 2 | 3 | import com.test.saf.app.BaseFragment; 4 | 5 | /** 6 | * Created by Tony Shen on 2016/11/22. 7 | */ 8 | 9 | public class SqliteORMFragment extends BaseFragment { 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/ui/GridRecyclerView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Freddie (Musenkishi) Lust-Hed 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.test.saf.ui; 18 | 19 | import android.content.Context; 20 | import android.support.annotation.NonNull; 21 | import android.support.v7.widget.GridLayoutManager; 22 | import android.support.v7.widget.RecyclerView; 23 | import android.util.AttributeSet; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.view.animation.GridLayoutAnimationController; 27 | 28 | /** 29 | * An extension of RecyclerView, focused more on resembling a GridView. 30 | * Unlike {@link android.support.v7.widget.RecyclerView}, this view can handle 31 | * {@code } as long as you provide it a 32 | * {@link android.support.v7.widget.GridLayoutManager} in 33 | * {@code setLayoutManager(LayoutManager layout)}. 34 | *

35 | * Created by Freddie (Musenkishi) Lust-Hed. 36 | */ 37 | public class GridRecyclerView extends RecyclerView { 38 | 39 | public GridRecyclerView(Context context) { 40 | super(context); 41 | } 42 | 43 | public GridRecyclerView(Context context, AttributeSet attrs) { 44 | super(context, attrs); 45 | } 46 | 47 | public GridRecyclerView(Context context, AttributeSet attrs, int defStyle) { 48 | super(context, attrs, defStyle); 49 | } 50 | 51 | @Override 52 | public void setLayoutManager(LayoutManager layout) { 53 | if (layout instanceof GridLayoutManager) { 54 | super.setLayoutManager(layout); 55 | } else { 56 | throw new ClassCastException("You should only use a GridLayoutManager with GridRecyclerView."); 57 | } 58 | } 59 | 60 | @Override 61 | protected void attachLayoutAnimationParameters(View child, @NonNull ViewGroup.LayoutParams params, int index, int count) { 62 | 63 | if (getAdapter() != null && getLayoutManager() instanceof GridLayoutManager) { 64 | 65 | GridLayoutAnimationController.AnimationParameters animationParams = 66 | (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters; 67 | 68 | if (animationParams == null) { 69 | animationParams = new GridLayoutAnimationController.AnimationParameters(); 70 | params.layoutAnimationParameters = animationParams; 71 | } 72 | 73 | int columns = ((GridLayoutManager) getLayoutManager()).getSpanCount(); 74 | 75 | animationParams.count = count; 76 | animationParams.index = index; 77 | animationParams.columnsCount = columns; 78 | animationParams.rowsCount = count / columns; 79 | 80 | final int invertedIndex = count - 1 - index; 81 | animationParams.column = columns - 1 - (invertedIndex % columns); 82 | animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns; 83 | 84 | } else { 85 | super.attachLayoutAnimationParameters(child, params, index, count); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /app/src/main/java/com/test/saf/utils/DoubleClickExitUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.test.saf.utils; 5 | 6 | import android.app.Activity; 7 | import android.os.Handler; 8 | import android.os.Looper; 9 | import android.view.KeyEvent; 10 | import android.widget.Toast; 11 | 12 | import com.test.saf.R; 13 | 14 | 15 | /** 16 | * @author Tony Shen 17 | * 18 | */ 19 | public class DoubleClickExitUtils { 20 | 21 | private final Activity mActivity; 22 | 23 | private boolean isOnKeyBacking; 24 | private Handler mHandler; 25 | private Toast mBackToast; 26 | 27 | public DoubleClickExitUtils(Activity activity) { 28 | mActivity = activity; 29 | mHandler = new Handler(Looper.getMainLooper()); 30 | } 31 | 32 | /** 33 | * Activity onKeyDown事件 34 | */ 35 | public boolean onKeyDown(int keyCode, KeyEvent event) { 36 | if(keyCode != KeyEvent.KEYCODE_BACK) { 37 | return false; 38 | } 39 | 40 | if(isOnKeyBacking) { 41 | mHandler.removeCallbacks(onBackTimeRunnable); 42 | if(mBackToast != null){ 43 | mBackToast.cancel(); 44 | } 45 | 46 | mActivity.finish(); 47 | android.os.Process.killProcess(android.os.Process.myPid()); 48 | return true; 49 | } else { 50 | isOnKeyBacking = true; 51 | if(mBackToast == null) { 52 | mBackToast = Toast.makeText(mActivity, R.string.finish_by_back_again, 2000); 53 | } 54 | mBackToast.show(); 55 | mHandler.postDelayed(onBackTimeRunnable, 2000); 56 | return true; 57 | } 58 | } 59 | 60 | private Runnable onBackTimeRunnable = new Runnable() { 61 | 62 | @Override 63 | public void run() { 64 | isOnKeyBacking = false; 65 | if(mBackToast != null){ 66 | mBackToast.cancel(); 67 | } 68 | } 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/res/anim/grid_layout_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/drawable-hdpi/ic_menu_black_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/drawer_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/drawable-nodpi/drawer_background.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/default_girl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/drawable-xxhdpi/default_girl.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/enter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/drawable-xxhdpi/enter.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_add_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/drawable-xxhdpi/ic_add_black.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/drawable-xxhdpi/ic_edit.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_annotation.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cache.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_eventbus.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_log.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_permission.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_router.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_safinfo.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sqlite.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selectable_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/textview_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_annotation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 21 | 22 | 23 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 15 | 16 | 21 | 22 | 29 | 30 | 31 | 32 | 37 | 38 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main2.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/cell_general_annotation.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/cell_second.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/drawer_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 26 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_general_annotation.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_permission.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/menu/drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 |

19 | 20 | 22 | 23 | 28 | 29 | 33 | 34 | 38 | 39 | 43 | 44 | 45 | 49 | 50 | 54 | 55 | 59 | 60 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #FFFFFF 8 | #0b566e 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 5 | 16dp 6 | 7 | 25dp 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SAFDemo 3 | Settings 4 | 5 | SAF介绍 6 | 通用注解 7 | Event Bus 8 | 图片加载 9 | Sqlite ORM 10 | Router 11 | Cache 12 | 日志框架L 13 | 权限框架 14 | 15 | 再按一次退出程序 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 26 | 27 | #00BCD4 28 | #0097A7 29 | #FFEB3B 30 | #4390ac 31 | #FFF493 32 | 33 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | configuration = [ 3 | buildToolsVersion: "27.0.2", 4 | compileVersion : 27, 5 | minSdk : 15, 6 | targetSdk : 27, 7 | saf_version_name : "1.1.19" 8 | ] 9 | 10 | libraries = [ 11 | junit : "4.12", 12 | support_design : "27.0.2", 13 | support_appcompat_v7: "27.0.2", 14 | 15 | commons_codec : "1.10", 16 | saf_log : "1.5.4", 17 | saf_cache : "1.1.1", 18 | tony_common_utils : "1.2.6" 19 | ] 20 | } 21 | 22 | buildscript { 23 | 24 | repositories { 25 | jcenter() 26 | google() 27 | } 28 | dependencies { 29 | classpath 'com.android.tools.build:gradle:3.0.1' 30 | classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4' 31 | classpath 'org.aspectj:aspectjtools:1.8.13' 32 | classpath 'com.novoda:bintray-release:0.5.0' 33 | // NOTE: Do not place your application dependencies here; they belong 34 | // in the individual module build.gradle files 35 | } 36 | } 37 | 38 | allprojects { 39 | repositories { 40 | google() 41 | jcenter() 42 | } 43 | 44 | //加上这些 45 | tasks.withType(Javadoc) { 46 | options{ encoding "UTF-8" 47 | charSet 'UTF-8' 48 | links "http://docs.oracle.com/javase/7/docs/api" 49 | } 50 | } 51 | } 52 | 53 | task clean(type: Delete) { 54 | delete rootProject.buildDir 55 | } 56 | 57 | apply from: rootProject.file('dependencies.gradle') 58 | 59 | 60 | tasks.getByPath(":saf-cache:bintrayUpload").enabled = false 61 | tasks.getByPath(":saf-queue:bintrayUpload").enabled = false 62 | tasks.getByPath(":saf-permission:bintrayUpload").enabled = true 63 | tasks.getByPath(":saf-rxlifecycle:bintrayUpload").enabled = false -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | androidPlugin = 'com.android.tools.build:gradle:2.2.2' 3 | minSdkVersion = 14 4 | compileSdkVersion = 25 5 | buildToolsVersion = '25.0.0' 6 | 7 | supportVersion = '25.0.1' 8 | supportAnnotations = "com.android.support:support-annotations:$supportVersion" 9 | supportV4CoreUi = "com.android.support:support-core-ui:$supportVersion" 10 | supportRecyclerView = "com.android.support:recyclerview-v7:$supportVersion" 11 | supportAppCompat = "com.android.support:appcompat-v7:$supportVersion" 12 | supportDesign = "com.android.support:design:$supportVersion" 13 | 14 | rxJava = 'io.reactivex.rxjava2:rxjava:2.1.8' 15 | rxAndroid = 'io.reactivex.rxjava2:rxandroid:2.0.1' 16 | junit = 'junit:junit:4.12' 17 | 18 | } 19 | -------------------------------------------------------------------------------- /docs/cache.md: -------------------------------------------------------------------------------- 1 | # 下载安装 2 | 3 | Gradle: 4 | ```groovy 5 | compile 'com.safframework:saf-cache:1.1.0' 6 | ``` 7 | 8 | Maven: 9 | ```groovy 10 | 11 | com.safframework 12 | saf-cache 13 | 1.1.0 14 | pom 15 | 16 | ``` 17 | 18 | 这是一个通用的Cache,可以保存String、对象、JSON等等,操作起来十分简单,支持设置缓存的过期时间。保存Cache的过程也可以使用@Cacheable 19 | 20 | 保持缓存数据: 21 | ```Java 22 | Cache cache = Cache.get(this); 23 | cache.put("key1", "test value"); 24 | cache.put("key2", "test value", 10);//保存10秒钟,10秒后会过期 25 | ``` 26 | 27 | 获取缓存数据: 28 | ```Java 29 | Cache cache = Cache.get(this); 30 | String value = cache.getString("key1"); 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/event_bus.md: -------------------------------------------------------------------------------- 1 | 事件总线框架,类似于google guava、square otto的event bus。它是一种消息发布-订阅模式,它的工作机制类似于观察者模式,通过通知者去注册观察者,最后由通知者向观察者发布消息。 2 | 3 | Event Bus解耦了asyncTask、handler、thread、broadcast等组件。使用Event bus可以轻松地跨多个Fragment进行通讯。 4 | 5 | 它用法很简单,在Activity或者Fragment中使用,其中event是一个简单的POJO
6 | ```Java 7 | // 退出系统的事件 8 | eventBus.post(new LogoutEvent()); 9 | ``` 10 | 11 | 回调事件,同样在Activity或者Fragment中定义好。回调方法名可以随便定义,参数须要和event一一对应。并且在方法名前加上注解Subscribe 12 | 13 | ```Java 14 | /** 15 | * 退出整个app 16 | * @param event 17 | */ 18 | @Subscribe 19 | public void onLogoutEvent(LogoutEvent event) { 20 | } 21 | ``` 22 | 23 | @Subscribe可以使用枚举 24 | 25 | ```Java 26 | /** 27 | * 使用ThreadMode.BackgroundThread枚举,表示在后台线程运行,不在主线程中运行。 28 | * @param event 29 | */ 30 | @Subscribe(ThreadMode.BackgroundThread) 31 | public void onBackendFresh(BackendFreshEvent event) { 32 | } 33 | ``` 34 | 35 | 使用枚举BackgroundThread时,如果在回调方法中需要更新ui,则必须要配合handler使用。 在不使用枚举的情况下,@Subscribe会默认使用PostThread,表示回调方法会在主线程中运行。 如果在一个Activity中存在多个Fragment,并且在Activity或者在Fragment中存在订阅同一event的回调方法。如果发出event的请求时,这些回调方法都会起作用。 36 | -------------------------------------------------------------------------------- /docs/rest_client.md: -------------------------------------------------------------------------------- 1 | Rest Client模块提供了http的get、post、put、delete方法。这个模块还不是很完善,只是适应自身项目需要,未来会不断增加新的功能。 2 | 这个模块完全使用jdk中的HttpURLConnection。 3 | 4 | 同步调用get方法: 5 | 6 | ```Java 7 | RestClient client = RestClient.get(url); 8 | String body = client.body(); 9 | ``` 10 | 11 | 异步调用get方法: 12 | 13 | ```Java 14 | RestClient.get(url,new HttpResponseHandler(){ 15 | 16 | public void onSuccess(String content) { 17 | // content为http请求成功后返回的response 18 | } 19 | 20 | @Override 21 | public void onFail(RestException exception){ 22 | 23 | } 24 | }); 25 | ``` 26 | 27 | 28 | 同步调用post方法:post body内容为json 29 | 30 | ```Java 31 | RestClient client = RestClient.post(url); 32 | client.acceptJson().contentType("application/json", null); 33 | client.send(jsonString); // jsonString是已经由json对象转换成string类型 34 | String body = client.body(); 35 | ``` 36 | 37 | 异步调用post方法:post body内容为json 38 | 39 | ```Java 40 | RestClient.post(url,json,new HttpResponseHandler(){ // json对应的是fastjson的JSONObject对象 41 | 42 | public void onSuccess(String content) { 43 | } 44 | 45 | @Override 46 | public void onFail(RestException exception){ 47 | 48 | } 49 | 50 | }); 51 | ``` 52 | 53 | 异步调用post方法:以form形式传递数据 54 | 55 | ```Java 56 | RestClient.post(urlString, map, new HttpResponseHandler(){ 57 | 58 | @Override 59 | public void onSuccess(String content) { 60 | 61 | } 62 | 63 | @Override 64 | public void onFail(RestException exception){ 65 | } 66 | 67 | }); 68 | ``` 69 | -------------------------------------------------------------------------------- /docs/rxasynctask.md: -------------------------------------------------------------------------------- 1 | 2 | 它完全可以替代android sdk中自带的AsyncTask使用,底层使用rxjava从而无需关心线程池的问题,开发者只需实现onExecute()即可。 3 | 它支持链式调用,支持重试机制retry()。
4 | 默认情况下它是这样的,只需实现onExecute()和success() 5 | ```Java 6 | new RxAsyncTask(){ 7 | @Override 8 | public String onExecute() { 9 | return RestClient.get("https://api.github.com/users/fengzhizi715").body(); 10 | } 11 | }.success(new RxAsyncTask.SuccessHandler() { 12 | @Override 13 | public void onSuccess(String content) { 14 | L.json(content); 15 | } 16 | }).start(); 17 | ``` 18 | 19 | 20 | 使用重试机制retry()时,还需要实现failed(),以便重试失败后调用failed()。 21 | ```Java 22 | new RxAsyncTask() { 23 | @Override 24 | public String onExecute() { 25 | return RestClient.get("https://api.github.com/users/fengzhizi715").body(); 26 | } 27 | }.retry(3) 28 | .success(new RxAsyncTask.SuccessHandler() { 29 | @Override 30 | public void onSuccess(String content) { 31 | L.json(content); 32 | } 33 | }).failed(new RxAsyncTask.FailedHandler() { 34 | @Override 35 | public void onFail(Throwable e) { 36 | L.e("error=" + e.getMessage()); 37 | } 38 | }).start(); 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/sqlite_orm.md: -------------------------------------------------------------------------------- 1 | 顾名思义就是sqlite的orm框架,采用oop的方式简化对sqlite的操作。
2 | 注意:在android studio2.0以后如果使用InstantRun功能,并且在android 5.0以上手机上调试会报错。强烈建议在调试时关闭InstantRun功能,release版本的app不会受到任何影响。(参考:http://stackoverflow.com/questions/36572515/dexfile-in-2-0-versions-of-android-studio-and-gradle)
3 | 首先需要在AndroidManifest.xml中配上一些参数 4 | 5 | ```Java 6 | 7 | 10 | 11 | 12 | 15 | 16 | 17 | 20 | ``` 21 | 22 | 23 | 使用orm框架需要初始化DBManager,需要在Applicaion中完成。SAF中的SAFApp,没有初始化DBManager,如果需要使用SAFApp可以重写一个Application继承SAFApp,并初始化DBManager。 24 | 25 | ```Java 26 | /** 27 | * @author Tony Shen 28 | * 29 | */ 30 | public class TestApp extends Application{ 31 | 32 | @Override 33 | public void onCreate() { 34 | super.onCreate(); 35 | DBManager.initialize(this); 36 | } 37 | 38 | } 39 | ``` 40 | 41 | db的domain使用是也是基于注解 42 | 43 | ```Java 44 | /** 45 | * 46 | * 表示sqlite中autocomplete表的属性 47 | * @author Tony Shen 48 | * 49 | */ 50 | @Table(name="autocomplete") 51 | public class Autocomplete extends DBDomain{ 52 | 53 | @Column(name="key_words",length=20,notNull=true) 54 | public String KEY_WORDS; 55 | 56 | @Column(name="key_type",length=20,notNull=true) 57 | public String KEY_TYPE; 58 | 59 | @Column(name="key_reference",length=80) 60 | public String KEY_REFERENCE; 61 | } 62 | ``` 63 | 64 | 65 | db的操作很简单 66 | 67 | ```Java 68 | Autocomplete auto = new Autocomplete(); 69 | auto.KEY_TYPE = "1"; 70 | auto.KEY_WORDS = "testtest"; 71 | auto.save(); // 插入第一条记录 72 | 73 | Autocomplete auto2 = new Autocomplete(); 74 | auto2.KEY_TYPE = "0"; 75 | auto2.KEY_WORDS = "haha"; 76 | auto2.save(); // 插入第二条记录 77 | 78 | Autocomplete auto3 = new Autocomplete().get(1); // 获取Autocomplete的第一条记录 79 | if (auto3!=null) { 80 | Log.i("+++++++++++++++","auto3.KEY_WORDS="+auto3.KEY_WORDS); 81 | } else { 82 | Log.i("+++++++++++++++","auto3 is null!"); 83 | } 84 | ``` 85 | 86 | 查询结果集 87 | 88 | ```Java 89 | List list = new Autocomplete().executeQuery("select * from autocomplete where KEY_WORDS = 'testtest'"); 90 | Log.i("+++++++++++++++","list.size()="+list.size()); // 根据sql条件查询 91 | 92 | List list2 = new Autocomplete().executeQuery("select * from autocomplete where KEY_WORDS = ? and Id = ?","testtest","1"); 93 | Log.i("+++++++++++++++","list2.size()="+list2.size()); // 表示查询select * from autocomplete where KEY_WORDS = 'testtest' and Id = '1' 94 | ``` 95 | -------------------------------------------------------------------------------- /docs/utils.md: -------------------------------------------------------------------------------- 1 | 包含了很多常用的工具类,比如日期操作、字符串操作、SAFUtil里包含各种乱七八糟的常用类等等。 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | org.gradle.daemon=true 19 | org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Nov 20 11:31:18 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/logo.png -------------------------------------------------------------------------------- /saf-cache/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /saf-cache/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | def cfg = rootProject.ext.configuration // 配置 5 | def libs = rootProject.ext.libraries // 库 6 | 7 | android { 8 | compileSdkVersion cfg.compileVersion 9 | buildToolsVersion cfg.buildToolsVersion 10 | 11 | defaultConfig { 12 | minSdkVersion 9 13 | targetSdkVersion 27 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | lintOptions { 27 | abortOnError false 28 | checkReleaseBuilds false 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(dir: 'libs', include: ['*.jar']) 34 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 35 | exclude group: 'com.android.support', module: 'support-annotations' 36 | }) 37 | compileOnly "com.android.support:appcompat-v7:${libs.support_appcompat_v7}" 38 | testCompile "junit:junit:${libs.junit}" 39 | implementation "commons-codec:commons-codec:${libs.commons_codec}" 40 | } 41 | 42 | repositories { 43 | jcenter() 44 | } 45 | 46 | allprojects { 47 | repositories { 48 | jcenter() 49 | } 50 | //加上这些 51 | tasks.withType(Javadoc) { 52 | options{ encoding "UTF-8" 53 | charSet 'UTF-8' 54 | links "http://docs.oracle.com/javase/7/docs/api" 55 | } 56 | } 57 | } 58 | 59 | publish{ 60 | userOrg = 'fengzhizi715' 61 | groupId = 'com.safframework' 62 | artifactId = 'saf-cache' 63 | publishVersion = '1.1.1' 64 | desc = 'this is a cache library for android' 65 | website = 'https://github.com/fengzhizi715/SAF' 66 | } 67 | -------------------------------------------------------------------------------- /saf-cache/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.4/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /saf-cache/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /saf-cache/src/main/java/com/safframework/cache/CacheException.java: -------------------------------------------------------------------------------- 1 | package com.safframework.cache; 2 | 3 | /** 4 | * Created by Tony Shen on 2017/7/16. 5 | */ 6 | 7 | public class CacheException extends RuntimeException { 8 | 9 | private static final long serialVersionUID = 6330641953592523314L; 10 | 11 | public CacheException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /saf-cache/src/main/java/com/safframework/cache/ParcelableUtils.java: -------------------------------------------------------------------------------- 1 | package com.safframework.cache; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by Tony Shen on 2017/6/20. 8 | */ 9 | 10 | public class ParcelableUtils { 11 | 12 | public static byte[] marshall(Parcelable parceable) { 13 | Parcel parcel = Parcel.obtain(); 14 | parceable.writeToParcel(parcel, 0); 15 | byte[] bytes = parcel.marshall(); 16 | parcel.recycle(); 17 | return bytes; 18 | } 19 | 20 | public static Parcel unmarshall(byte[] bytes) { 21 | Parcel parcel = Parcel.obtain(); 22 | parcel.unmarshall(bytes, 0, bytes.length); 23 | parcel.setDataPosition(0); // This is extremely important! 24 | return parcel; 25 | } 26 | 27 | public static T unmarshall(byte[] bytes, Parcelable.Creator creator) { 28 | Parcel parcel = unmarshall(bytes); 29 | T result = creator.createFromParcel(parcel); 30 | parcel.recycle(); 31 | return result; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /saf-cache/src/main/java/com/safframework/prefs/AppPrefs.java: -------------------------------------------------------------------------------- 1 | package com.safframework.prefs; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * 配合使用@Prefs 7 | * Created by Tony Shen on 16/3/28. 8 | */ 9 | public class AppPrefs extends BasePrefs { 10 | 11 | private static final String PREFS_NAME = "AppPrefs"; 12 | 13 | private AppPrefs(Context context) { 14 | super(context, PREFS_NAME); 15 | } 16 | 17 | public static AppPrefs get(Context context) { 18 | return new AppPrefs(context); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /saf-cache/src/main/java/com/safframework/prefs/BasePrefs.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.prefs; 5 | 6 | import android.content.Context; 7 | import android.content.SharedPreferences; 8 | import android.text.TextUtils; 9 | 10 | import org.apache.commons.codec.binary.Base64; 11 | 12 | import java.io.ByteArrayInputStream; 13 | import java.io.ByteArrayOutputStream; 14 | import java.io.IOException; 15 | import java.io.ObjectInputStream; 16 | import java.io.ObjectOutputStream; 17 | 18 | /** 19 | * @author Tony Shen 20 | * 21 | * 22 | */ 23 | public class BasePrefs { 24 | 25 | private SharedPreferences prefs; 26 | private SharedPreferences.Editor editor; 27 | 28 | protected BasePrefs(Context context, String prefsName) { 29 | prefs = context.getSharedPreferences(prefsName, Context.MODE_PRIVATE); 30 | } 31 | 32 | public boolean getBoolean(String key, boolean defValue) { 33 | return prefs.getBoolean(key, defValue); 34 | } 35 | 36 | public float getFloat(String key, float defValue) { 37 | return prefs.getFloat(key, defValue); 38 | } 39 | 40 | public int getInt(String key, int defValue) { 41 | return prefs.getInt(key, defValue); 42 | } 43 | 44 | public long getLong(String key, long defValue) { 45 | return prefs.getLong(key, defValue); 46 | } 47 | 48 | public String getString(String key, String defValue) { 49 | return prefs.getString(key, defValue); 50 | } 51 | 52 | public Object getObject(String key) { 53 | try { 54 | String stringBase64 = prefs.getString(key, ""); 55 | if (TextUtils.isEmpty(stringBase64)) 56 | return null; 57 | 58 | byte[] base64Bytes = Base64.decodeBase64(stringBase64.getBytes()); 59 | ByteArrayInputStream bais = new ByteArrayInputStream(base64Bytes); 60 | ObjectInputStream ois = new ObjectInputStream(bais); 61 | return ois.readObject(); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | return null; 66 | } 67 | 68 | public void putBoolean(String key, boolean v) { 69 | ensureEditorAvailability(); 70 | editor.putBoolean(key, v); 71 | save(); 72 | } 73 | 74 | public void putFloat(String key, float v) { 75 | ensureEditorAvailability(); 76 | editor.putFloat(key, v); 77 | save(); 78 | } 79 | 80 | public void putInt(String key, int v) { 81 | ensureEditorAvailability(); 82 | editor.putInt(key, v); 83 | save(); 84 | } 85 | 86 | public void putLong(String key, long v) { 87 | ensureEditorAvailability(); 88 | editor.putLong(key, v); 89 | save(); 90 | } 91 | 92 | public void putString(String key, String v) { 93 | ensureEditorAvailability(); 94 | editor.putString(key, v); 95 | save(); 96 | } 97 | 98 | public void putObject(String key, Object obj) { 99 | ensureEditorAvailability(); 100 | try { 101 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 102 | ObjectOutputStream oos = new ObjectOutputStream(baos); 103 | oos.writeObject(obj); 104 | 105 | String stringBase64 = new String(Base64.encodeBase64(baos.toByteArray())); 106 | editor.putString(key, stringBase64); 107 | save(); 108 | } catch (IOException e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | 113 | public void save() { 114 | if (editor != null) { 115 | editor.apply(); 116 | } 117 | } 118 | 119 | private void ensureEditorAvailability() { 120 | if (editor == null) { 121 | editor = prefs.edit(); 122 | } 123 | } 124 | 125 | public void remove(String key) { 126 | ensureEditorAvailability(); 127 | editor.remove(key); 128 | save(); 129 | } 130 | 131 | public void clear() { 132 | ensureEditorAvailability(); 133 | editor.clear(); 134 | save(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /saf-cache/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | saf-cache 3 | 4 | -------------------------------------------------------------------------------- /saf-core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: "findbugs" 3 | 4 | def cfg = rootProject.ext.configuration // 配置 5 | def libs = rootProject.ext.libraries // 库 6 | 7 | android { 8 | compileSdkVersion cfg.compileVersion 9 | buildToolsVersion cfg.buildToolsVersion 10 | 11 | defaultConfig { 12 | minSdkVersion cfg.minSdk 13 | targetSdkVersion cfg.targetSdk 14 | versionCode 1 15 | versionName cfg.saf_version_name 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | sourceSets { 27 | main { 28 | jniLibs.srcDirs = ['libs'] 29 | } 30 | } 31 | } 32 | 33 | task clearJar(type: Delete) { 34 | delete 'build/outputs/*.jar' 35 | } 36 | 37 | dependencies { 38 | testCompile rootProject.ext.junit 39 | compile "com.android.support:design:${libs.support_design}" 40 | compile 'com.alibaba:fastjson:1.1.56.android' 41 | compile rootProject.ext.rxJava 42 | compile rootProject.ext.rxAndroid 43 | compile "com.safframework.log:saf-log:${libs.saf_log}" 44 | compile "com.safframework:saf-cache:${libs.saf_cache}" 45 | compile "tony-common:tony-common-utils:${libs.tony_common_utils}" 46 | } 47 | 48 | task buildJar(dependsOn: ['assembleRelease'], type: Jar) { 49 | 50 | archiveName = 'SAF-all-1.1.19.jar' 51 | 52 | //需打包的资源所在的路径集 53 | def srcClassDir = [project.buildDir.absolutePath + "/intermediates/classes/release"]; 54 | //初始化资源路径集 55 | from srcClassDir 56 | 57 | //去除路径集下部分的资源 58 | exclude "com/safframework/saf/BuildConfig.class" 59 | exclude "com/safframework/saf/BuildConfig\$*.class" 60 | exclude "**/R.class" 61 | exclude "**/R\$*.class" 62 | 63 | //只导入资源路径集下的部分资源 64 | include "com/safframework/saf/**/*.class" 65 | destinationDir = file('build/outputs') 66 | } 67 | 68 | task javadoc(type: Javadoc) { 69 | 70 | javadoc { 71 | options.charSet = 'UTF-8' 72 | } 73 | 74 | source = android.sourceSets.main.java.srcDirs 75 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 76 | destinationDir = file("../library/build/outputs/javadoc/") 77 | failOnError false 78 | } 79 | 80 | task findbugs(type: FindBugs,dependsOn:'assembleDebug') { 81 | 82 | ignoreFailures= true 83 | effort= "default" 84 | reportLevel= "high" 85 | println( "$project.buildDir") 86 | classes = files("$project.buildDir/intermediates/classes") 87 | source= fileTree("src/main/java/") 88 | classpath= files() 89 | reports{ 90 | xml.enabled=false 91 | html.enabled=true 92 | xml { 93 | destination "$project.buildDir/findbugs.xml" 94 | } 95 | html{ 96 | destination "$project.buildDir/findbugs.html" 97 | } 98 | } 99 | } 100 | 101 | defaultTasks 'buildAll','javadoc' 102 | -------------------------------------------------------------------------------- /saf-core/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.4/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /saf-core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/app/SAFActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.app; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.os.Bundle; 9 | import android.os.Handler; 10 | import android.util.Log; 11 | 12 | import com.safframework.log.L; 13 | import com.safframework.saf.utils.SAFUtils; 14 | import com.safframework.saf.utils.ToastUtils; 15 | 16 | 17 | /** 18 | * SAF框架基类的Activity 19 | * @author Tony Shen 20 | * 21 | */ 22 | public class SAFActivity extends Activity{ 23 | 24 | public SAFApp app; 25 | public String TAG; 26 | protected Context mContext; 27 | protected Handler mHandler = new SafeHandler(this); 28 | 29 | public void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | 32 | app = (SAFApp) this.getApplication(); 33 | 34 | mContext = this; 35 | 36 | TAG = SAFUtils.makeLogTag(this.getClass()); 37 | L.init(TAG); 38 | addActivityToManager(this); 39 | } 40 | 41 | protected void addActivityToManager(Activity act) { 42 | Log.i(TAG, "addActivityToManager"); 43 | if (!app.activityManager.contains(act)) { 44 | Log.i(TAG , "addActivityToManager, packagename = " + act.getClass().getName()) ; 45 | app.activityManager.add(act); 46 | } 47 | } 48 | 49 | protected void closeAllActivities() { 50 | Log.i(TAG, "closeAllActivities"); 51 | for (final Activity act : app.activityManager) { 52 | if (act != null) { 53 | act.finish(); 54 | } 55 | } 56 | } 57 | 58 | protected void delActivityFromManager(Activity act) { 59 | Log.i(TAG,"delActivityFromManager") ; 60 | if (app.activityManager.contains(act)) { 61 | app.activityManager.remove(act); 62 | } 63 | } 64 | 65 | /** 66 | * 返回当前运行activity的名称 67 | * @return 68 | */ 69 | protected String getCurrentActivityName() { 70 | int size = app.activityManager.size(); 71 | if (size > 0) { 72 | return app.activityManager.get(size-1).getClass().getName(); 73 | } 74 | return null; 75 | } 76 | 77 | @Override 78 | protected void onDestroy() { 79 | super.onDestroy(); 80 | delActivityFromManager(this); 81 | } 82 | 83 | /** 84 | * @param message toast的内容 85 | */ 86 | protected void toast(String message) { 87 | ToastUtils.showShort(this, message); 88 | } 89 | 90 | /** 91 | * @param resId toast的内容来自String.xml 92 | */ 93 | protected void toast(int resId) { 94 | ToastUtils.showShort(this, resId); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/app/SAFApp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.app; 5 | 6 | import android.Manifest; 7 | import android.annotation.TargetApi; 8 | import android.app.Activity; 9 | import android.app.Application; 10 | import android.content.Context; 11 | import android.content.pm.PackageInfo; 12 | import android.content.pm.PackageManager; 13 | import android.content.pm.PackageManager.NameNotFoundException; 14 | import android.os.Build; 15 | import android.telephony.TelephonyManager; 16 | import android.util.Log; 17 | 18 | import com.safframework.saf.config.SAFConstant; 19 | import com.safframework.saf.utils.SAFUtils; 20 | import com.safframework.tony.common.utils.Preconditions; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * SAFApp是自定义的Application,session可作为缓存存放app的全局变量
27 | * SAFApp并不是每个app都需要使用,可自由选择,也可以继承它
28 | * 如需使用SAFApp,则在AndroidManifest.xml中配置,
29 | * 在application中增加android:name="cn.salesuite.saf.app.SAFApp" 30 | * 31 | * @author Tony Shen 32 | * 33 | */ 34 | public class SAFApp extends Application { 35 | 36 | public List activityManager; 37 | 38 | public String deviceid; // 设备ID 39 | public String osVersion; // 操作系统版本 40 | public String mobileType;// 手机型号 41 | public String version; // app的versionName 42 | public int versionCode; // app的versionCode 43 | 44 | private static SAFApp instance; 45 | 46 | /** 47 | * @see Application#onCreate() 48 | */ 49 | @Override 50 | public void onCreate() { 51 | super.onCreate(); 52 | init(); 53 | } 54 | 55 | public void init() { 56 | instance = this; 57 | 58 | activityManager = new ArrayList(); 59 | 60 | PackageManager manager = this.getPackageManager(); 61 | try { 62 | PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0); 63 | deviceid = getDeviceId(); 64 | osVersion = Build.VERSION.RELEASE; 65 | mobileType = Build.MODEL; 66 | if (null != info) { 67 | version = info.versionName; 68 | versionCode = info.versionCode; 69 | } 70 | } catch (NameNotFoundException e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | 75 | /** 76 | * 获取手机的设备号(imei) 77 | * 78 | * @return 79 | */ 80 | @TargetApi(23) 81 | private String getDeviceId() { 82 | String imei = null; 83 | 84 | if (SAFUtils.isMOrHigher()) { 85 | if (getPackageManager().checkPermission(Manifest.permission.READ_PHONE_STATE, 86 | getPackageName()) == PackageManager.PERMISSION_GRANTED) { 87 | TelephonyManager mphonemanger = ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)); 88 | if (mphonemanger !=null) { 89 | imei = mphonemanger.getDeviceId(); 90 | } 91 | } else { 92 | Log.e("SAFApp","no android.permission.READ_PHONE_STATE permission"); 93 | } 94 | } else { 95 | TelephonyManager mphonemanger = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 96 | if (mphonemanger != null) { 97 | imei = mphonemanger.getDeviceId(); 98 | } 99 | } 100 | 101 | if (Preconditions.isBlank(imei)) { 102 | imei = SAFConstant.SPECIAL_IMEI; 103 | } 104 | 105 | return imei; 106 | } 107 | 108 | public static SAFApp getInstance() { 109 | return instance; 110 | } 111 | } -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/app/SAFAppCompatActivity.java: -------------------------------------------------------------------------------- 1 | package com.safframework.saf.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.util.Log; 9 | 10 | import com.safframework.log.L; 11 | import com.safframework.saf.utils.SAFUtils; 12 | import com.safframework.saf.utils.ToastUtils; 13 | 14 | 15 | /** 16 | * Created by Tony Shen on 2016/11/17. 17 | */ 18 | 19 | public class SAFAppCompatActivity extends AppCompatActivity { 20 | 21 | public SAFApp app; 22 | public String TAG; 23 | protected Context mContext; 24 | protected Handler mHandler = new SafeHandler(this); 25 | 26 | public void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | 29 | app = (SAFApp) this.getApplication(); 30 | 31 | mContext = this; 32 | 33 | TAG = SAFUtils.makeLogTag(this.getClass()); 34 | L.init(TAG); 35 | addActivityToManager(this); 36 | } 37 | 38 | protected void addActivityToManager(Activity act) { 39 | Log.i(TAG, "addActivityToManager"); 40 | if (!app.activityManager.contains(act)) { 41 | Log.i(TAG , "addActivityToManager, packagename = " + act.getClass().getName()) ; 42 | app.activityManager.add(act); 43 | } 44 | } 45 | 46 | protected void closeAllActivities() { 47 | Log.i(TAG, "closeAllActivities"); 48 | for (final Activity act : app.activityManager) { 49 | if (act != null) { 50 | act.finish(); 51 | } 52 | } 53 | } 54 | 55 | protected void delActivityFromManager(Activity act) { 56 | Log.i(TAG,"delActivityFromManager") ; 57 | if (app.activityManager.contains(act)) { 58 | app.activityManager.remove(act); 59 | } 60 | } 61 | 62 | /** 63 | * 返回当前运行activity的名称 64 | * @return 65 | */ 66 | protected String getCurrentActivityName() { 67 | int size = app.activityManager.size(); 68 | if (size > 0) { 69 | return app.activityManager.get(size-1).getClass().getName(); 70 | } 71 | return null; 72 | } 73 | 74 | @Override 75 | protected void onDestroy() { 76 | super.onDestroy(); 77 | delActivityFromManager(this); 78 | } 79 | 80 | /** 81 | * @param message toast的内容 82 | */ 83 | protected void toast(String message) { 84 | ToastUtils.showShort(this, message); 85 | } 86 | 87 | /** 88 | * @param resId toast的内容来自String.xml 89 | */ 90 | protected void toast(int resId) { 91 | ToastUtils.showShort(this, resId); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/app/SAFFragment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.app; 5 | 6 | import android.annotation.TargetApi; 7 | import android.app.Activity; 8 | import android.content.Context; 9 | import android.os.Bundle; 10 | import android.support.v4.app.Fragment; 11 | 12 | import com.safframework.log.L; 13 | import com.safframework.saf.utils.SAFUtils; 14 | import com.safframework.saf.utils.ToastUtils; 15 | 16 | /** 17 | * @author Tony Shen 18 | * 19 | */ 20 | public class SAFFragment extends Fragment { 21 | 22 | /** 23 | * Fragment 所在的 FragmentActivity 24 | */ 25 | public Activity mContext; 26 | 27 | public String TAG; 28 | 29 | /** 30 | * Deprecated on API 23 31 | * @param activity 32 | */ 33 | @Override 34 | public void onAttach(Activity activity) { 35 | super.onAttach(activity); 36 | if (!SAFUtils.isMOrHigher()) { 37 | this.mContext = activity; 38 | } 39 | } 40 | 41 | @TargetApi(23) 42 | public void onAttach(Context context) { 43 | super.onAttach(context); 44 | 45 | if (context instanceof Activity){ 46 | this.mContext = (Activity) context; 47 | } 48 | } 49 | 50 | @Override 51 | public void onCreate(Bundle savedInstanceState) { 52 | super.onCreate(savedInstanceState); 53 | 54 | TAG = SAFUtils.makeLogTag(this.getClass()); 55 | L.init(TAG); 56 | } 57 | 58 | /** 59 | * @param message toast的内容 60 | */ 61 | protected void toast(String message) { 62 | ToastUtils.showShort(mContext, message); 63 | } 64 | 65 | /** 66 | * @param resId toast的内容来自String.xml 67 | */ 68 | protected void toast(int resId) { 69 | ToastUtils.showShort(mContext, resId); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/app/SAFFragmentActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.app; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.os.Bundle; 9 | import android.os.Handler; 10 | import android.support.v4.app.FragmentActivity; 11 | import android.util.Log; 12 | 13 | import com.safframework.log.L; 14 | import com.safframework.saf.utils.SAFUtils; 15 | import com.safframework.saf.utils.ToastUtils; 16 | 17 | /** 18 | * @author Tony Shen 19 | * 20 | */ 21 | public class SAFFragmentActivity extends FragmentActivity{ 22 | 23 | public SAFApp app; 24 | public String TAG; 25 | protected Context mContext; 26 | protected Handler mHandler = new SafeHandler(this); 27 | 28 | public void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | 31 | app = (SAFApp) this.getApplication(); 32 | 33 | mContext = this; 34 | 35 | TAG = SAFUtils.makeLogTag(this.getClass()); 36 | L.init(TAG); 37 | addActivityToManager(this); 38 | } 39 | 40 | protected void addActivityToManager(Activity act) { 41 | Log.i(TAG, "addActivityToManager"); 42 | if (!app.activityManager.contains(act)) { 43 | Log.i(TAG , "addActivityToManager, packagename = " + act.getClass().getName()) ; 44 | app.activityManager.add(act); 45 | } 46 | } 47 | 48 | protected void closeAllActivities() { 49 | Log.i(TAG, "closeAllActivities"); 50 | for (final Activity act : app.activityManager) { 51 | if (act != null) { 52 | act.finish(); 53 | } 54 | } 55 | } 56 | 57 | protected void delActivityFromManager(Activity act) { 58 | Log.i(TAG,"delActivityFromManager") ; 59 | if (app.activityManager.contains(act)) { 60 | app.activityManager.remove(act); 61 | } 62 | } 63 | 64 | /** 65 | * 返回当前运行activity的名称 66 | * @return 67 | */ 68 | protected String getCurrentActivityName() { 69 | int size = app.activityManager.size(); 70 | if (size > 0) { 71 | return app.activityManager.get(size-1).getClass().getName(); 72 | } 73 | return null; 74 | } 75 | 76 | @Override 77 | protected void onDestroy() { 78 | super.onDestroy(); 79 | delActivityFromManager(this); 80 | } 81 | 82 | /** 83 | * @param message toast的内容 84 | */ 85 | protected void toast(String message) { 86 | ToastUtils.showShort(this, message); 87 | } 88 | 89 | /** 90 | * @param resId toast的内容来自String.xml 91 | */ 92 | protected void toast(int resId) { 93 | ToastUtils.showShort(this, resId); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/app/SafeHandler.java: -------------------------------------------------------------------------------- 1 | package com.safframework.saf.app; 2 | 3 | import android.app.Activity; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | 7 | import java.lang.ref.WeakReference; 8 | 9 | /** 10 | * Created by Tony Shen on 16/7/5. 11 | */ 12 | public class SafeHandler extends Handler { 13 | 14 | private final WeakReference mActivity; 15 | 16 | public SafeHandler(Activity activity) { 17 | mActivity = new WeakReference(activity); 18 | } 19 | 20 | @Override 21 | public void handleMessage(Message msg) { 22 | if(mActivity.get() == null) { 23 | return; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/app/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * SAF框架基类的Activity和application 3 | */ 4 | package com.safframework.saf.app; -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/async/RetryWithDelay.java: -------------------------------------------------------------------------------- 1 | package com.safframework.saf.async; 2 | 3 | import com.safframework.log.L; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import io.reactivex.Flowable; 8 | import io.reactivex.annotations.NonNull; 9 | import io.reactivex.functions.Function; 10 | 11 | 12 | /** 13 | * Created by Tony Shen on 2016/12/12. 14 | */ 15 | 16 | public class RetryWithDelay implements 17 | Function, Flowable> { 18 | 19 | private final int maxRetries; 20 | private final int retryDelayMillis; 21 | private int retryCount; 22 | 23 | public RetryWithDelay(int maxRetries, int retryDelayMillis) { 24 | this.maxRetries = maxRetries; 25 | this.retryDelayMillis = retryDelayMillis; 26 | } 27 | 28 | @Override 29 | public Flowable apply(@NonNull Flowable flowable) throws Exception { 30 | return flowable 31 | .flatMap(new Function>() { 32 | @Override 33 | public Flowable apply(@NonNull Throwable throwable) throws 34 | Exception { 35 | if (++retryCount <= maxRetries) { 36 | // When this Observable calls onNext, the original Observable will be 37 | // retried (i.e. re-subscribed). 38 | L.i("get error, it will try after " + retryDelayMillis 39 | + " millisecond, retry count " + retryCount); 40 | return Flowable.timer(retryDelayMillis, 41 | TimeUnit.MILLISECONDS); 42 | } 43 | // Max retries hit. Just pass the error along. 44 | return Flowable.error(throwable); 45 | } 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/concurrent/BackgroundExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.concurrent; 5 | 6 | import java.util.concurrent.LinkedBlockingQueue; 7 | import java.util.concurrent.SynchronousQueue; 8 | import java.util.concurrent.ThreadPoolExecutor; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @author Tony Shen 13 | * 14 | */ 15 | public class BackgroundExecutor extends ThreadPoolExecutor { 16 | 17 | public BackgroundExecutor() { 18 | super(0, Integer.MAX_VALUE, 60L, 19 | TimeUnit.SECONDS, new SynchronousQueue(), 20 | new BackgroundPriorityThreadFactory()); 21 | } 22 | 23 | public BackgroundExecutor(int nThreads) { 24 | super(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, 25 | new LinkedBlockingQueue(), 26 | new BackgroundPriorityThreadFactory()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/concurrent/BackgroundPriorityThreadFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.concurrent; 5 | 6 | import android.os.Process; 7 | 8 | import com.safframework.tony.common.utils.Preconditions; 9 | 10 | import java.util.concurrent.ThreadFactory; 11 | 12 | /** 13 | * 使用该类时,app需要增加权限:<uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY"/> 14 | * @author Tony Shen 15 | * 16 | */ 17 | public class BackgroundPriorityThreadFactory implements ThreadFactory { 18 | 19 | public String threadName; 20 | 21 | public BackgroundPriorityThreadFactory() { 22 | } 23 | 24 | public BackgroundPriorityThreadFactory(String threadName) { 25 | this.threadName = threadName; 26 | } 27 | 28 | @Override 29 | public Thread newThread(Runnable r) { 30 | 31 | if (Preconditions.isNotBlank(threadName)) { 32 | return new Thread(r,threadName) { 33 | @Override 34 | public void run() { 35 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 36 | super.run(); 37 | } 38 | }; 39 | } else { 40 | return new Thread(r) { 41 | @Override 42 | public void run() { 43 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 44 | super.run(); 45 | } 46 | }; 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/config/SAFConstant.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.config; 5 | 6 | /** 7 | * @author Tony Shen 8 | * 9 | */ 10 | public class SAFConstant { 11 | 12 | //os类型 13 | public static final String SPECIAL_IMEI="000000000000000"; 14 | 15 | /** 16 | * 需要使用ImageLoader组件时,可以设置default_img_id的值,表示全局的默认图片 17 | */ 18 | public static int default_img_id; 19 | 20 | /** app存储目录/文件 可根据app的名称覆盖 默认使用saf作为文件名**/ 21 | public static final String DIR = "/saf"; 22 | public static final String CACHE_DIR = DIR + "/images"; 23 | } 24 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/config/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * SAF框架中常量信息 3 | */ 4 | package com.safframework.saf.config; -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/BackgroundPoster.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | import java.util.concurrent.Callable; 7 | 8 | 9 | /** 10 | * 后台线程运行,使用BackgroundExecutor 11 | * @author Tony Shen 12 | * 13 | */ 14 | public class BackgroundPoster { 15 | 16 | private final EventBus eventBus; 17 | 18 | BackgroundPoster(EventBus eventBus) { 19 | this.eventBus = eventBus; 20 | } 21 | 22 | public void enqueue(final Object event,final EventHandler subscription) { 23 | EventBus.executorService.submit(new Callable(){ 24 | public Void call() throws Exception { 25 | eventBus.invokeSubscriber(event,subscription); 26 | return null; 27 | } 28 | }); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/DeadEvent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | /** 7 | * EventBus不处理DeadEvent 8 | * @author Tony Shen 9 | * 10 | */ 11 | public class DeadEvent { 12 | 13 | public final Object source; 14 | public final Object event; 15 | 16 | public DeadEvent(Object source, Object event) { 17 | this.source = source; 18 | this.event = event; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/EventBusException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | /** 9 | * @author Tony Shen 10 | * 11 | */ 12 | public class EventBusException extends RuntimeException{ 13 | 14 | private static final long serialVersionUID = 7663747274756461744L; 15 | 16 | protected EventBusException(InvocationTargetException cause) { 17 | super(cause); 18 | } 19 | 20 | protected EventBusException(String msg,InvocationTargetException cause) { 21 | if (cause.getCause() != null) { 22 | throw new RuntimeException(msg, cause); 23 | } else { 24 | throw new RuntimeException(msg); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/EventBusUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /** 10 | * @author Tony Shen 11 | * 12 | */ 13 | public class EventBusUtils { 14 | 15 | /** 16 | * 查找Producer 17 | * @param listener 18 | * @return 19 | */ 20 | public static Map, EventProducer> findAllProducers(Object listener) { 21 | return AnnotatedFinder.findAllProducers(listener); 22 | } 23 | 24 | /** 25 | * 查找Subscriber 26 | * @param listener 27 | * @return 28 | */ 29 | public static Map, Set> findAllSubscribers(Object listener) { 30 | return AnnotatedFinder.findAllSubscribers(listener); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/EventHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | package com.safframework.saf.eventbus; 4 | 5 | import java.lang.reflect.InvocationTargetException; 6 | 7 | /** 8 | * 9 | * @author Tony Shen 10 | * 11 | */ 12 | class EventHandler { 13 | 14 | final Object target; 15 | 16 | final SubscriberMethod subscriberMethod; 17 | 18 | private final int hashCode; 19 | 20 | private boolean valid = true; 21 | 22 | EventHandler(Object target, SubscriberMethod method) { 23 | if (target == null) { 24 | throw new NullPointerException("EventHandler target cannot be null."); 25 | } 26 | if (method == null) { 27 | throw new NullPointerException("EventHandler method cannot be null."); 28 | } 29 | 30 | this.target = target; 31 | this.subscriberMethod = method; 32 | method.method.setAccessible(true); 33 | 34 | final int prime = 31; 35 | hashCode = (prime + method.hashCode()) * prime + target.hashCode(); 36 | } 37 | 38 | public boolean isValid() { 39 | return valid; 40 | } 41 | 42 | public void invalidate() { 43 | valid = false; 44 | } 45 | 46 | /** 47 | * 处理event 48 | * @param event 49 | * @throws InvocationTargetException 50 | */ 51 | public void handleEvent(Object event) throws InvocationTargetException { 52 | if (!valid) { 53 | throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events."); 54 | } 55 | try { 56 | subscriberMethod.method.invoke(target, event); 57 | } catch (IllegalAccessException e) { 58 | throw new AssertionError(e); 59 | } catch (InvocationTargetException e) { 60 | if (e.getCause() instanceof Error) { 61 | throw (Error) e.getCause(); 62 | } 63 | throw e; 64 | } 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "[EventHandler " + subscriberMethod + "]"; 70 | } 71 | 72 | @Override 73 | public int hashCode() { 74 | return hashCode; 75 | } 76 | 77 | @Override 78 | public boolean equals(Object obj) { 79 | if (this == obj) { 80 | return true; 81 | } 82 | 83 | if (obj == null) { 84 | return false; 85 | } 86 | 87 | if (getClass() != obj.getClass()) { 88 | return false; 89 | } 90 | 91 | final EventHandler other = (EventHandler) obj; 92 | 93 | return subscriberMethod.equals(other.subscriberMethod) && target == other.target; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/EventProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | 4 | package com.safframework.saf.eventbus; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * 11 | * @author Tony Shen 12 | * 13 | */ 14 | class EventProducer { 15 | 16 | final Object target; 17 | 18 | private final Method method; 19 | 20 | private final int hashCode; 21 | 22 | private boolean valid = true; 23 | 24 | EventProducer(Object target, Method method) { 25 | if (target == null) { 26 | throw new NullPointerException("EventProducer target cannot be null."); 27 | } 28 | if (method == null) { 29 | throw new NullPointerException("EventProducer method cannot be null."); 30 | } 31 | 32 | this.target = target; 33 | this.method = method; 34 | method.setAccessible(true); 35 | 36 | final int prime = 31; 37 | hashCode = (prime + method.hashCode()) * prime + target.hashCode(); 38 | } 39 | 40 | public boolean isValid() { 41 | return valid; 42 | } 43 | 44 | public void invalidate() { 45 | valid = false; 46 | } 47 | 48 | /** 49 | * 返回event 50 | * @return 51 | * @throws InvocationTargetException 52 | */ 53 | public Object produceEvent() throws InvocationTargetException { 54 | if (!valid) { 55 | throw new IllegalStateException(toString() + " has been invalidated and can no longer produce events."); 56 | } 57 | try { 58 | return method.invoke(target); 59 | } catch (IllegalAccessException e) { 60 | throw new AssertionError(e); 61 | } catch (InvocationTargetException e) { 62 | if (e.getCause() instanceof Error) { 63 | throw (Error) e.getCause(); 64 | } 65 | throw e; 66 | } 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "[EventProducer " + method + "]"; 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | return hashCode; 77 | } 78 | 79 | @Override 80 | public boolean equals(Object obj) { 81 | if (this == obj) { 82 | return true; 83 | } 84 | 85 | if (obj == null) { 86 | return false; 87 | } 88 | 89 | if (getClass() != obj.getClass()) { 90 | return false; 91 | } 92 | 93 | final EventProducer other = (EventProducer) obj; 94 | 95 | return method.equals(other.method) && target == other.target; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/Produce.java: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | package com.safframework.saf.eventbus; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * @author Tony Shen 12 | * 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.METHOD) 16 | public @interface Produce { 17 | } 18 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/ScheduledEvent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * @author Tony Shen 10 | * 11 | */ 12 | public class ScheduledEvent { 13 | 14 | public long initialDelay; 15 | public long period; 16 | public TimeUnit unit = TimeUnit.MILLISECONDS; 17 | } 18 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/ScheduledPoster.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | 7 | /** 8 | * @author Tony Shen 9 | * 10 | */ 11 | public class ScheduledPoster { 12 | 13 | private final EventBus eventBus; 14 | 15 | ScheduledPoster(EventBus eventBus) { 16 | this.eventBus = eventBus; 17 | } 18 | 19 | public void enqueue(final Object event,final EventHandler subscription) { 20 | 21 | ScheduledEvent scheduledEvent = (ScheduledEvent)event; 22 | EventBus.scheduledExecutorService.scheduleAtFixedRate(new Runnable(){ 23 | 24 | @Override 25 | public void run() { 26 | eventBus.invokeSubscriber(event,subscription); 27 | } 28 | 29 | },scheduledEvent.initialDelay,scheduledEvent.period,scheduledEvent.unit); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/Subscribe.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * 消息的订阅者默认使用ThreadMode.PostThread(在当前的thread运行) 13 | * @author Tony Shen 14 | * 15 | */ 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.METHOD) 18 | public @interface Subscribe { 19 | ThreadMode value() default ThreadMode.PostThread; 20 | } 21 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/SubscriberMethod.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.eventbus; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * 订阅者的方法,封装了Method和ThreadMode 10 | * @author Tony Shen 11 | * 12 | */ 13 | final class SubscriberMethod { 14 | 15 | final Method method; 16 | final ThreadMode threadMode; 17 | 18 | /** Used for efficient comparison */ 19 | String methodString; 20 | 21 | SubscriberMethod(Method method, ThreadMode threadMode) { 22 | this.method = method; 23 | this.threadMode = threadMode; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object other) { 28 | if (other instanceof SubscriberMethod) { 29 | checkMethodString(); 30 | return methodString.equals(((SubscriberMethod) other).methodString); 31 | } else { 32 | return false; 33 | } 34 | } 35 | 36 | private synchronized void checkMethodString() { 37 | if (methodString == null) { 38 | StringBuilder builder = new StringBuilder(64); 39 | builder.append(method.getDeclaringClass().getName()); 40 | builder.append('#').append(method.getName()); 41 | methodString = builder.toString(); 42 | } 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return method.hashCode(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/eventbus/ThreadMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | */ 3 | package com.safframework.saf.eventbus; 4 | 5 | /** 6 | * 线程模式的枚举 7 | * @author Tony 8 | */ 9 | public enum ThreadMode { 10 | 11 | PostThread, 12 | 13 | BackgroundThread, 14 | 15 | Async, 16 | 17 | ScheduleBackgroundThread 18 | } -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/orm/DBManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.orm; 5 | 6 | import android.app.Application; 7 | import android.content.Context; 8 | import android.database.sqlite.SQLiteDatabase; 9 | import android.support.v4.util.LruCache; 10 | import android.util.Log; 11 | 12 | import java.util.Collection; 13 | 14 | /** 15 | * 如果要使用sqlite orm,必须在application中先使用DBManager.initialize(this);
16 | * 如果结合SAFApp的功能,可以先写一个application继承SAFApp,然后将DBManager.initialize(this); 17 | * @author Tony Shen 18 | * 19 | */ 20 | public final class DBManager { 21 | 22 | public static final int DEFAULT_CACHE_SIZE = 1024; 23 | public static final String TAG = "DBManager"; 24 | 25 | private static boolean isInitialized = false; 26 | private static Context mContext; 27 | private static DomainInfo mDomainInfo; 28 | private static DatabaseHelper mDatabaseHelper; 29 | private static LruCache mEntities; 30 | 31 | private DBManager() { 32 | } 33 | 34 | public static void initialize(Application app) { 35 | initialize(app,DEFAULT_CACHE_SIZE); 36 | } 37 | 38 | public static void initialize(Application app, int cacheSize) { 39 | if (isInitialized) { 40 | Log.i(TAG,"DBManager already initialized."); 41 | return; 42 | } 43 | 44 | mContext = app; 45 | mDomainInfo = new DomainInfo(app); 46 | mDatabaseHelper = new DatabaseHelper(app); 47 | mEntities = new LruCache(cacheSize); 48 | openDatabase(); 49 | isInitialized = true; 50 | 51 | Log.i(TAG,"DBManager initialized successfully."); 52 | } 53 | 54 | public static synchronized void close() { 55 | if (mDatabaseHelper!=null) { 56 | mDatabaseHelper.close(); 57 | } 58 | mDatabaseHelper = null; 59 | 60 | if (mEntities!=null) { 61 | mEntities.evictAll(); 62 | } 63 | mEntities = null; 64 | 65 | mDomainInfo = null; 66 | isInitialized = false; 67 | 68 | Log.i(TAG,"DBManager closed."); 69 | } 70 | 71 | public static Context getContext() { 72 | return mContext; 73 | } 74 | 75 | public static synchronized SQLiteDatabase openDatabase() { 76 | return mDatabaseHelper.getWritableDatabase(); 77 | } 78 | 79 | public static synchronized Collection getTableInfos() { 80 | return mDomainInfo.getTableInfos(); 81 | } 82 | 83 | public static synchronized TableInfo getTableInfo(Class type) { 84 | return mDomainInfo.getTableInfo(type); 85 | } 86 | 87 | public static synchronized String getTableName(Class type) { 88 | return mDomainInfo.getTableInfo(type).getTableName(); 89 | } 90 | 91 | public static String getId(Class type, Long id) { 92 | return getTableName(type) + "@" + id; 93 | } 94 | 95 | public static String getId(DBDomain entity) { 96 | return getId(entity.getClass(), entity.getId()); 97 | } 98 | 99 | public static synchronized void addEntity(DBDomain entity) { 100 | mEntities.put(getId(entity), entity); 101 | } 102 | 103 | public static synchronized DBDomain getEntity(Class type, long id) { 104 | return mEntities.get(getId(type, id)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/orm/DBVersionPrefs.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.orm; 5 | 6 | import android.content.Context; 7 | 8 | import com.safframework.prefs.BasePrefs; 9 | 10 | /** 11 | * @author Tony Shen 12 | * 13 | */ 14 | public class DBVersionPrefs extends BasePrefs { 15 | 16 | private static final String PREFS_NAME = "DBVersionPrefs"; 17 | public static final String DB_VERSION = "db_version"; 18 | 19 | private DBVersionPrefs(Context context) { 20 | super(context, PREFS_NAME); 21 | } 22 | 23 | public static DBVersionPrefs get(Context context) { 24 | return new DBVersionPrefs(context); 25 | } 26 | 27 | public int getDBVersion() { 28 | return getInt(DB_VERSION, -1); 29 | } 30 | 31 | public void setDBVersion(int v) { 32 | putInt(DB_VERSION, v); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/orm/DomainInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.orm; 5 | 6 | import android.app.Application; 7 | import android.text.TextUtils; 8 | import android.util.Log; 9 | 10 | import com.safframework.saf.utils.ReflectionUtils; 11 | import com.safframework.saf.utils.SAFUtils; 12 | 13 | import java.io.IOException; 14 | import java.util.Collection; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | 20 | /** 21 | * @author Tony Shen 22 | * 23 | */ 24 | public final class DomainInfo { 25 | 26 | public static final String TAG = "DomainInfo"; 27 | private final static String DOMAIN_PACKAGE = "DOMAIN_PACKAGE"; 28 | 29 | private Map, TableInfo> mTableInfos = new HashMap, TableInfo>(); 30 | 31 | public DomainInfo(Application app) { 32 | if (loadDomainFromMetaData(app)) { 33 | Log.i(TAG,"DomainInfo loaded successfully."); 34 | } else { 35 | Log.i(TAG,"DomainInfo loaded failure."); 36 | } 37 | } 38 | 39 | private boolean loadDomainFromMetaData(Application app) { 40 | String domainPackage = SAFUtils.getMetaData(app, DOMAIN_PACKAGE); 41 | 42 | if (!TextUtils.isEmpty(domainPackage)) { 43 | loadDomainList(app, domainPackage); 44 | } 45 | 46 | return mTableInfos.size() > 0; 47 | } 48 | 49 | private void loadDomainList(Application app, String domainPackage) { 50 | final ClassLoader classLoader = app.getClass().getClassLoader(); 51 | try { 52 | List domains = ReflectionUtils.getPackageAllClassName(app, domainPackage); 53 | if (domains!=null && domains.size()>0) { 54 | for(String domain:domains) { 55 | Class domainClass = Class.forName(domain, false, classLoader); 56 | mTableInfos.put(domainClass, new TableInfo(domainClass)); 57 | } 58 | } 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } catch (ClassNotFoundException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | public Collection getTableInfos() { 67 | return mTableInfos.values(); 68 | } 69 | 70 | public TableInfo getTableInfo(Class type) { 71 | return mTableInfos.get(type); 72 | } 73 | 74 | @SuppressWarnings("unchecked") 75 | public List> getModelClasses() { 76 | return (List>) mTableInfos.keySet(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/orm/NaturalOrderComparator.java: -------------------------------------------------------------------------------- 1 | package com.safframework.saf.orm; 2 | 3 | /* 4 | NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java. 5 | Copyright (C) 2003 by Pierre-Luc Paour 6 | 7 | Based on the C version by Martin Pool, of which this is more or less a straight conversion. 8 | Copyright (C) 2000 by Martin Pool 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | */ 26 | 27 | import java.util.Comparator; 28 | 29 | public class NaturalOrderComparator implements Comparator { 30 | int compareRight(String a, String b) { 31 | int bias = 0; 32 | int ia = 0; 33 | int ib = 0; 34 | 35 | // The longest run of digits wins. That aside, the greatest 36 | // value wins, but we can't know that it will until we've scanned 37 | // both numbers to know that they have the same magnitude, so we 38 | // remember it in BIAS. 39 | for (;; ia++, ib++) { 40 | char ca = charAt(a, ia); 41 | char cb = charAt(b, ib); 42 | 43 | if (!Character.isDigit(ca) && !Character.isDigit(cb)) { 44 | return bias; 45 | } 46 | else if (!Character.isDigit(ca)) { 47 | return -1; 48 | } 49 | else if (!Character.isDigit(cb)) { 50 | return +1; 51 | } 52 | else if (ca < cb) { 53 | if (bias == 0) { 54 | bias = -1; 55 | } 56 | } 57 | else if (ca > cb) { 58 | if (bias == 0) 59 | bias = +1; 60 | } 61 | else if (ca == 0 && cb == 0) { 62 | return bias; 63 | } 64 | } 65 | } 66 | 67 | public int compare(Object o1, Object o2) { 68 | String a = o1.toString(); 69 | String b = o2.toString(); 70 | 71 | int ia = 0, ib = 0; 72 | int nza = 0, nzb = 0; 73 | char ca, cb; 74 | int result; 75 | 76 | while (true) { 77 | // only count the number of zeroes leading the last number compared 78 | nza = nzb = 0; 79 | 80 | ca = charAt(a, ia); 81 | cb = charAt(b, ib); 82 | 83 | // skip over leading spaces or zeros 84 | while (Character.isSpaceChar(ca) || ca == '0') { 85 | if (ca == '0') { 86 | nza++; 87 | } 88 | else { 89 | // only count consecutive zeroes 90 | nza = 0; 91 | } 92 | 93 | ca = charAt(a, ++ia); 94 | } 95 | 96 | while (Character.isSpaceChar(cb) || cb == '0') { 97 | if (cb == '0') { 98 | nzb++; 99 | } 100 | else { 101 | // only count consecutive zeroes 102 | nzb = 0; 103 | } 104 | 105 | cb = charAt(b, ++ib); 106 | } 107 | 108 | // process run of digits 109 | if (Character.isDigit(ca) && Character.isDigit(cb)) { 110 | if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) { 111 | return result; 112 | } 113 | } 114 | 115 | if (ca == 0 && cb == 0) { 116 | // The strings compare the same. Perhaps the caller 117 | // will want to call strcmp to break the tie. 118 | return nza - nzb; 119 | } 120 | 121 | if (ca < cb) { 122 | return -1; 123 | } 124 | else if (ca > cb) { 125 | return +1; 126 | } 127 | 128 | ++ia; 129 | ++ib; 130 | } 131 | } 132 | 133 | static char charAt(String s, int i) { 134 | if (i >= s.length()) { 135 | return 0; 136 | } 137 | else { 138 | return s.charAt(i); 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/orm/TableInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.orm; 5 | 6 | import com.safframework.saf.orm.annotation.Column; 7 | import com.safframework.saf.orm.annotation.Table; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.Collection; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * @author Tony Shen 19 | * 20 | */ 21 | public final class TableInfo { 22 | 23 | private Class mType; 24 | private String mTableName; 25 | 26 | private Map mColumnNames = new HashMap(); 27 | 28 | public TableInfo(Class type) { 29 | mType = type; 30 | 31 | final Table tableAnnotation = type.getAnnotation(Table.class); 32 | if (tableAnnotation != null) { 33 | mTableName = tableAnnotation.name(); 34 | } 35 | else { 36 | mTableName = type.getSimpleName(); 37 | } 38 | 39 | List fields = new ArrayList(Arrays.asList(type.getDeclaredFields())); 40 | fields.add(getIdField(type)); 41 | 42 | for (Field field : fields) { 43 | if (field.isAnnotationPresent(Column.class)) { 44 | final Column columnAnnotation = field.getAnnotation(Column.class); 45 | mColumnNames.put(field, columnAnnotation.name()); 46 | } 47 | } 48 | } 49 | 50 | public Class getType() { 51 | return mType; 52 | } 53 | 54 | public String getTableName() { 55 | return mTableName; 56 | } 57 | 58 | public Collection getFields() { 59 | return mColumnNames.keySet(); 60 | } 61 | 62 | public String getColumnName(Field field) { 63 | return mColumnNames.get(field); 64 | } 65 | 66 | private Field getIdField(Class type) { 67 | if (type.equals(DBDomain.class)) { 68 | try { 69 | return type.getDeclaredField("mId"); 70 | } 71 | catch (NoSuchFieldException e) { 72 | } 73 | } 74 | else if (type.getSuperclass() != null) { 75 | return getIdField(type.getSuperclass()); 76 | } 77 | 78 | return null; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/orm/annotation/Column.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.orm.annotation; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * @author Tony Shen 13 | * 14 | */ 15 | @Target(ElementType.FIELD) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface Column { 18 | 19 | public String name(); 20 | 21 | public int length() default -1; 22 | 23 | public boolean notNull() default false; 24 | } 25 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/orm/annotation/Table.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.orm.annotation; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * @author Tony Shen 13 | * 14 | */ 15 | @Target(ElementType.TYPE) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface Table { 18 | public String name(); 19 | } 20 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/recyclerview/OnItemClickListener.java: -------------------------------------------------------------------------------- 1 | package com.safframework.saf.recyclerview; 2 | 3 | import android.support.v4.view.GestureDetectorCompat; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.GestureDetector; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | 9 | /** 10 | * Created by Tony Shen on 2016/11/27. 11 | */ 12 | 13 | public abstract class OnItemClickListener implements RecyclerView.OnItemTouchListener { 14 | 15 | private GestureDetectorCompat mGestureDetector; 16 | private RecyclerView recyclerView; 17 | 18 | public OnItemClickListener(RecyclerView view) { 19 | this.recyclerView = view; 20 | mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener()); 21 | } 22 | 23 | @Override 24 | public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { 25 | mGestureDetector.onTouchEvent(e); 26 | return false; 27 | } 28 | 29 | @Override 30 | public void onTouchEvent(RecyclerView rv, MotionEvent e) { 31 | mGestureDetector.onTouchEvent(e); 32 | } 33 | 34 | @Override 35 | public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 36 | 37 | } 38 | 39 | public abstract void onItemClick(RecyclerView.ViewHolder holder, int position); 40 | 41 | public abstract void onLongPress(RecyclerView.ViewHolder holder, int position); 42 | 43 | class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener { 44 | @Override 45 | public boolean onSingleTapUp(MotionEvent e) { 46 | View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); 47 | if (child != null) { 48 | int position = recyclerView.indexOfChild(child); 49 | onItemClick(recyclerView.getChildViewHolder(child), position); 50 | } 51 | return true; 52 | } 53 | 54 | @Override 55 | public void onLongPress(MotionEvent e) { 56 | View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); 57 | if (child != null) { 58 | int position = recyclerView.indexOfChild(child); 59 | OnItemClickListener.this.onLongPress(recyclerView.getChildViewHolder(child), position); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/CloseOperation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | import java.io.Closeable; 7 | import java.io.Flushable; 8 | import java.io.IOException; 9 | 10 | /** 11 | * @author Tony Shen 12 | * 13 | */ 14 | public abstract class CloseOperation extends Operation { 15 | 16 | private final Closeable closeable; 17 | 18 | private final boolean ignoreCloseExceptions; 19 | 20 | protected CloseOperation(final Closeable closeable, 21 | final boolean ignoreCloseExceptions) { 22 | this.closeable = closeable; 23 | this.ignoreCloseExceptions = ignoreCloseExceptions; 24 | } 25 | 26 | @Override 27 | protected void done() throws IOException { 28 | if (closeable instanceof Flushable) 29 | ((Flushable) closeable).flush(); 30 | if (ignoreCloseExceptions) 31 | try { 32 | closeable.close(); 33 | } catch (IOException e) { 34 | // Ignored 35 | } 36 | else 37 | closeable.close(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/ConnectionFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | import java.io.IOException; 7 | import java.net.HttpURLConnection; 8 | import java.net.Proxy; 9 | import java.net.URL; 10 | 11 | /** 12 | * @author Tony Shen 13 | * 14 | */ 15 | public interface ConnectionFactory { 16 | 17 | HttpURLConnection create(URL url) throws IOException; 18 | 19 | HttpURLConnection create(URL url, Proxy proxy) throws IOException; 20 | } 21 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/HttpResponseHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author Tony Shen 11 | * 12 | */ 13 | public interface HttpResponseHandler { 14 | 15 | /** 16 | * http请求成功后,response转换成content 17 | * @param content 18 | * @param heads 19 | */ 20 | public void onSuccess(String content, Map> heads); 21 | 22 | /** 23 | * http请求失败后,response转换成jsonString 24 | * 25 | * @param e 26 | */ 27 | public void onFail(RestException e); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/HttpResponseRetryHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | 7 | /** 8 | * 带http重试机制的handler 9 | * @author Tony Shen 10 | * 11 | */ 12 | public abstract class HttpResponseRetryHandler implements HttpResponseHandler,HttpRetryStrategy { 13 | 14 | private int retryNum = RestConstant.DEFAULT_RETRY_NUM; 15 | 16 | public HttpResponseRetryHandler(){ 17 | } 18 | 19 | public HttpResponseRetryHandler(int retryNum){ 20 | this.retryNum = retryNum; 21 | } 22 | 23 | public int getRetryNum() { 24 | return retryNum; 25 | } 26 | 27 | public void setRetryNum(int retryNum) { 28 | this.retryNum = retryNum; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/HttpRetryStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | /** 7 | * http重试机制的策略 8 | * @author Tony Shen 9 | * 10 | */ 11 | public interface HttpRetryStrategy { 12 | 13 | /** 14 | * 执行重试的逻辑,这些逻辑往往是比较特别的 15 | */ 16 | public void retry(); 17 | } 18 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/Operation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | import java.io.IOException; 7 | import java.util.concurrent.Callable; 8 | 9 | /** 10 | * @author Tony Shen 11 | * 12 | */ 13 | public abstract class Operation implements Callable { 14 | 15 | /** 16 | * 回调运行的操作 17 | * 18 | * @return result 19 | * @throws RestException 20 | * @throws IOException 21 | */ 22 | protected abstract V run() throws RestException, IOException; 23 | 24 | /** 25 | * 回调完成后的操作,主要是关闭一些流 26 | * 27 | * @throws IOException 28 | */ 29 | protected abstract void done() throws IOException; 30 | 31 | public V call() throws RestException { 32 | boolean thrown = false; 33 | try { 34 | return run(); 35 | } catch (RestException e) { 36 | thrown = true; 37 | throw e; 38 | } catch (IOException e) { 39 | thrown = true; 40 | throw new RestException(e); 41 | } finally { 42 | try { 43 | done(); 44 | } catch (IOException e) { 45 | if (!thrown) 46 | throw new RestException(e); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/RestConstant.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | /** 7 | * @author Tony Shen 8 | * 9 | */ 10 | public class RestConstant { 11 | 12 | /** 13 | * 'UTF-8' charset name 14 | */ 15 | public static final String CHARSET_UTF8 = "UTF-8"; 16 | 17 | /** 18 | * 'application/x-www-form-urlencoded' content type header value 19 | */ 20 | public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"; 21 | 22 | /** 23 | * 'application/json' content type header value 24 | */ 25 | public static final String CONTENT_TYPE_JSON = "application/json"; 26 | 27 | /** 28 | * 'gzip' encoding header value 29 | */ 30 | public static final String ENCODING_GZIP = "gzip"; 31 | 32 | /** 33 | * 'Accept' header name 34 | */ 35 | public static final String HEADER_ACCEPT = "Accept"; 36 | 37 | /** 38 | * 'Accept-Charset' header name 39 | */ 40 | public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset"; 41 | 42 | /** 43 | * 'Accept-Encoding' header name 44 | */ 45 | public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; 46 | 47 | /** 48 | * 'Authorization' header name 49 | */ 50 | public static final String HEADER_AUTHORIZATION = "Authorization"; 51 | 52 | /** 53 | * 'Cache-Control' header name 54 | */ 55 | public static final String HEADER_CACHE_CONTROL = "Cache-Control"; 56 | 57 | /** 58 | * 'Content-Encoding' header name 59 | */ 60 | public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; 61 | 62 | /** 63 | * 'Content-Length' header name 64 | */ 65 | public static final String HEADER_CONTENT_LENGTH = "Content-Length"; 66 | 67 | /** 68 | * 'Content-Type' header name 69 | */ 70 | public static final String HEADER_CONTENT_TYPE = "Content-Type"; 71 | 72 | /** 73 | * 'Date' header name 74 | */ 75 | public static final String HEADER_DATE = "Date"; 76 | 77 | /** 78 | * 'ETag' header name 79 | */ 80 | public static final String HEADER_ETAG = "ETag"; 81 | 82 | /** 83 | * 'Expires' header name 84 | */ 85 | public static final String HEADER_EXPIRES = "Expires"; 86 | 87 | /** 88 | * 'If-None-Match' header name 89 | */ 90 | public static final String HEADER_IF_NONE_MATCH = "If-None-Match"; 91 | 92 | /** 93 | * 'Last-Modified' header name 94 | */ 95 | public static final String HEADER_LAST_MODIFIED = "Last-Modified"; 96 | 97 | /** 98 | * 'Location' header name 99 | */ 100 | public static final String HEADER_LOCATION = "Location"; 101 | 102 | /** 103 | * 'Server' header name 104 | */ 105 | public static final String HEADER_SERVER = "Server"; 106 | 107 | /** 108 | * 'User-Agent' header name 109 | */ 110 | public static final String HEADER_USER_AGENT = "User-Agent"; 111 | 112 | /** 113 | * 'DELETE' request method 114 | */ 115 | public static final String METHOD_DELETE = "DELETE"; 116 | 117 | /** 118 | * 'GET' request method 119 | */ 120 | public static final String METHOD_GET = "GET"; 121 | 122 | /** 123 | * 'HEAD' request method 124 | */ 125 | public static final String METHOD_HEAD = "HEAD"; 126 | 127 | /** 128 | * 'OPTIONS' options method 129 | */ 130 | public static final String METHOD_OPTIONS = "OPTIONS"; 131 | 132 | /** 133 | * 'POST' request method 134 | */ 135 | public static final String METHOD_POST = "POST"; 136 | 137 | /** 138 | * 'PUT' request method 139 | */ 140 | public static final String METHOD_PUT = "PUT"; 141 | 142 | /** 143 | * 'TRACE' request method 144 | */ 145 | public static final String METHOD_TRACE = "TRACE"; 146 | 147 | /** 148 | * 'charset' header value parameter 149 | */ 150 | public static final String PARAM_CHARSET = "charset"; 151 | 152 | public static final String BOUNDARY = "00content0boundary00"; 153 | 154 | public static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; boundary=" 155 | + BOUNDARY; 156 | 157 | public static final String CRLF = "\r\n"; 158 | 159 | public static int DEFAULT_READ_TIMEOUT = 30000; 160 | 161 | public static int DEFAULT_CONNECTION_TIMEOUT = 30000; 162 | 163 | public static int DEFAULT_RETRY_NUM = 3; 164 | 165 | public static final int SUCCESS = 200; 166 | 167 | public static final String COOKIE = "Cookie"; 168 | 169 | public static final String SET_COOKIE = "Set-Cookie"; 170 | } 171 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/RestException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * @author Tony Shen 10 | * 11 | */ 12 | public class RestException extends RuntimeException{ 13 | 14 | private static final long serialVersionUID = 8520390726356829268L; 15 | public static final String RETRY_CONNECTION = "retry"; 16 | private String errorCode; 17 | 18 | protected RestException(IOException cause) { 19 | super(cause); 20 | } 21 | 22 | protected RestException(IOException cause,String code) { 23 | super(cause); 24 | this.errorCode = code; 25 | } 26 | 27 | protected RestException(String code) { 28 | super(); 29 | this.errorCode = code; 30 | } 31 | 32 | public String getErrorCode() { 33 | return errorCode; 34 | } 35 | 36 | @Override 37 | public IOException getCause() { 38 | return (IOException) super.getCause(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/RestOutputStream.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | import java.io.BufferedOutputStream; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.nio.ByteBuffer; 10 | import java.nio.CharBuffer; 11 | import java.nio.charset.Charset; 12 | import java.nio.charset.CharsetEncoder; 13 | 14 | /** 15 | * 封装BufferedOutputStream 16 | * @author Tony Shen 17 | * 18 | */ 19 | public class RestOutputStream extends BufferedOutputStream{ 20 | 21 | private final CharsetEncoder encoder; 22 | 23 | public RestOutputStream(OutputStream stream, String charset, 24 | int bufferSize) { 25 | super(stream, bufferSize); 26 | 27 | encoder = Charset.forName(RestUtil.getValidCharset(charset)).newEncoder(); 28 | } 29 | 30 | /** 31 | * 将string的值写入stream 32 | * 33 | * @param value 34 | * @return this stream 35 | * @throws IOException 36 | */ 37 | public RestOutputStream write(String value) throws IOException { 38 | final ByteBuffer bytes = encoder.encode(CharBuffer.wrap(value)); 39 | 40 | super.write(bytes.array(), 0, bytes.limit()); 41 | 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/rest/RestUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.rest; 5 | 6 | import com.alibaba.fastjson.JSON; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | 11 | /** 12 | * @author Tony Shen 13 | * 14 | */ 15 | public class RestUtil { 16 | 17 | /** 18 | * 验证charset 19 | * @param charset 20 | * @return 21 | */ 22 | public static String getValidCharset(String charset) { 23 | if (charset != null && charset.length() > 0) 24 | return charset; 25 | else 26 | return RestConstant.CHARSET_UTF8; 27 | } 28 | 29 | /** 30 | * 根据返回的http body内容转换成json对象 31 | * @param cls 32 | * @param body 33 | * @return 34 | * @throws IOException 35 | */ 36 | public static T parseAs(Class cls,String body) throws IOException { 37 | return JSON.parseObject(body, cls); 38 | } 39 | 40 | /** 41 | * 根据返回的http body内容转换成json对象数组 42 | * @param cls 43 | * @param body 44 | * @return 45 | * @throws IOException 46 | */ 47 | public static ArrayList parseArray(Class cls,String body) throws IOException { 48 | return (ArrayList) JSON.parseArray(body, cls); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/utils/AsyncTaskExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.utils; 5 | 6 | import android.annotation.SuppressLint; 7 | import android.os.AsyncTask; 8 | 9 | import java.util.concurrent.BlockingQueue; 10 | import java.util.concurrent.LinkedBlockingQueue; 11 | import java.util.concurrent.ThreadFactory; 12 | import java.util.concurrent.ThreadPoolExecutor; 13 | import java.util.concurrent.TimeUnit; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | /** 17 | * 推荐使用RxAsyncTask替换AsyncTask 18 | * @author Tony Shen 19 | * 20 | */ 21 | @Deprecated 22 | public class AsyncTaskExecutor { 23 | 24 | private static final int CORE_POOL_SIZE; 25 | private static final int MAXIMUM_POOL_SIZE; 26 | private static final int KEEP_ALIVE; 27 | private static final TimeUnit TIME_UNIT; 28 | 29 | private static final BlockingQueue concurrentPoolWorkQueue; 30 | private static final ThreadFactory concurrentThreadFactory; 31 | private static final ThreadPoolExecutor concurrentExecutor; 32 | 33 | private AsyncTaskExecutor() {} 34 | 35 | static { 36 | CORE_POOL_SIZE = 5; 37 | MAXIMUM_POOL_SIZE = 128; 38 | KEEP_ALIVE = 1; 39 | TIME_UNIT = TimeUnit.SECONDS; 40 | 41 | concurrentPoolWorkQueue = new LinkedBlockingQueue(10); 42 | concurrentThreadFactory = new AsyncTaskThreadFactory(); 43 | concurrentExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, 44 | MAXIMUM_POOL_SIZE, KEEP_ALIVE, TIME_UNIT, concurrentPoolWorkQueue, concurrentThreadFactory); 45 | } 46 | 47 | /** 48 | * Concurrently executes AsyncTask on any Android version 49 | * @param task to execute 50 | * @param params for task 51 | * @return executing AsyncTask 52 | */ 53 | @SuppressLint("NewApi") 54 | public static AsyncTask 55 | executeAsyncTask(AsyncTask task, 56 | Params... params) { 57 | if (SAFUtils.isHoneycombOrHigher()) { 58 | task.executeOnExecutor(concurrentExecutor, params); 59 | } else { 60 | task.execute(params); 61 | } 62 | return task; 63 | } 64 | 65 | private static class AsyncTaskThreadFactory implements ThreadFactory { 66 | private final AtomicInteger count; 67 | 68 | { 69 | count = new AtomicInteger(1); 70 | } 71 | 72 | @Override 73 | public Thread newThread(Runnable r) { 74 | return new Thread(r, "AsyncTask #" + count.getAndIncrement()); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/utils/NoEmptyHashMap.java: -------------------------------------------------------------------------------- 1 | package com.safframework.saf.utils; 2 | 3 | import com.safframework.tony.common.utils.Preconditions; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * 此类存在的目的:hashmap可以存key为null或者value为null的 10 | * 但是这个map不能,保证所有的value都不为空 11 | * Created by Tony Shen on 16/4/22. 12 | */ 13 | public class NoEmptyHashMap extends HashMap { 14 | 15 | public NoEmptyHashMap() { 16 | super(); 17 | } 18 | 19 | public NoEmptyHashMap(final Map map) { 20 | super(); 21 | putAll(map); 22 | } 23 | 24 | @Override 25 | public V put(final K key, final V value) { 26 | if (Preconditions.isBlank(key) || Preconditions.isBlank(value)) { 27 | return null; 28 | } 29 | return super.put(key, value); 30 | } 31 | 32 | @Override 33 | public void putAll(final Map map) { 34 | for (Entry entry : map.entrySet()) { 35 | put(entry.getKey(), entry.getValue()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/utils/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.utils; 5 | 6 | import android.content.Context; 7 | 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.Enumeration; 11 | import java.util.List; 12 | 13 | import dalvik.system.DexFile; 14 | 15 | /** 16 | * 反射的帮助类 17 | * @author Tony Shen 18 | * 19 | */ 20 | public class ReflectionUtils { 21 | 22 | /** 23 | * 获取某一个包名下的所有类名 24 | * @param context 25 | * @param packageName 26 | * @return 27 | * @throws IOException 28 | */ 29 | public static List getPackageAllClassName(Context context,String packageName) throws IOException{ 30 | String sourcePath = context.getApplicationInfo().sourceDir; 31 | List paths = new ArrayList(); 32 | 33 | if (sourcePath != null) { 34 | DexFile dexfile = new DexFile(sourcePath); 35 | Enumeration entries = dexfile.entries(); 36 | 37 | while (entries.hasMoreElements()) { 38 | String element = entries.nextElement(); 39 | if (element.contains(packageName)) { 40 | paths.add(element); 41 | } 42 | } 43 | } 44 | 45 | return paths; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/utils/ResourceUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.utils; 5 | 6 | import android.content.Context; 7 | import android.content.pm.PackageInfo; 8 | import android.content.pm.PackageManager; 9 | import android.content.pm.PackageManager.NameNotFoundException; 10 | 11 | import java.lang.reflect.Field; 12 | 13 | /** 14 | * 通过反射获取资源文件的帮助类
15 | * 使用方法,可现在app的引导页面中使用如下的语句:
16 | * 17 | * ResourceUtil.GEN_PACKAGE_NAME = ResourceUtil.getPackageName(contex)+".R"; 18 | * 19 | *
20 | * 然后就可以使用了 21 | * @author Tony Shen 22 | * 23 | */ 24 | public class ResourceUtil { 25 | 26 | public static String GEN_PACKAGE_NAME = ""; 27 | 28 | /** 29 | * 获取app的包名 30 | * @param contex 31 | * @return 32 | */ 33 | public static String getPackageName(Context contex) { 34 | PackageManager manager = contex.getPackageManager(); 35 | try { 36 | PackageInfo info = manager.getPackageInfo(contex.getPackageName(), 0); 37 | return info.packageName; 38 | } catch (NameNotFoundException e) { 39 | e.printStackTrace(); 40 | } 41 | return null; 42 | } 43 | 44 | public static void setPackageName(String packageName) { 45 | StringBuilder sb = new StringBuilder(); 46 | sb.append(packageName); 47 | sb.append(".R"); 48 | GEN_PACKAGE_NAME = sb.toString(); 49 | } 50 | 51 | /** 52 | * 获取android资源id 53 | * @param packageName 调用方包的名称 54 | * @param resFileName 资源文件名 55 | * @param parameterName 56 | * @return 57 | */ 58 | public static int getResourceId(String packageName, String resFileName, 59 | String parameterName) { 60 | if ((packageName != null) && (resFileName != null) 61 | && (parameterName != null)) 62 | try { 63 | Class localClass = Class.forName(packageName + "$" 64 | + resFileName); 65 | Field localField = localClass.getField(parameterName); 66 | Object localObject = localField.get(localClass.newInstance()); 67 | return Integer.parseInt(localObject.toString()); 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | return -1; 72 | } 73 | 74 | /** 75 | * 获取android资源id 76 | * @param resFileName 资源文件名 77 | * @param parameterName 78 | * @return 79 | */ 80 | public static int getResourceId(String resFileName, 81 | String parameterName) { 82 | return getResourceId(GEN_PACKAGE_NAME, resFileName,parameterName); 83 | } 84 | 85 | /** 86 | * 获取工程中资源id 87 | * @param parameterName 88 | * @return 89 | */ 90 | public static int getResourceId(String parameterName) { 91 | return getResourceId("id",parameterName); 92 | } 93 | 94 | /** 95 | * 获取工程中array资源id 96 | * @param parameterName 97 | * @return 98 | */ 99 | public static int getResourceArrayId(String parameterName) { 100 | return getResourceId("array",parameterName); 101 | } 102 | 103 | /** 104 | * 获取工程中color资源id 105 | * @param parameterName 106 | * @return 107 | */ 108 | public static int getResourceColorId(String parameterName) { 109 | return getResourceId("color",parameterName); 110 | } 111 | 112 | /** 113 | * 获取工程中drawable资源id 114 | * @param parameterName 115 | * @return 116 | */ 117 | public static int getResourceDrawableId(String parameterName) { 118 | return getResourceId("drawable",parameterName); 119 | } 120 | 121 | /** 122 | * 获取工程中layout资源id 123 | * @param parameterName 124 | * @return 125 | */ 126 | public static int getResourceLayoutId(String parameterName) { 127 | return getResourceId("layout",parameterName); 128 | } 129 | 130 | /** 131 | * 获取工程中string资源id 132 | * @param parameterName 133 | * @return 134 | */ 135 | public static int getResourceStringId(String parameterName) { 136 | return getResourceId("string",parameterName); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/utils/ViewUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.utils; 5 | 6 | import android.graphics.Rect; 7 | import android.text.Editable; 8 | import android.text.TextWatcher; 9 | import android.util.DisplayMetrics; 10 | import android.view.TouchDelegate; 11 | import android.view.View; 12 | import android.widget.Button; 13 | import android.widget.EditText; 14 | 15 | import com.safframework.tony.common.utils.Preconditions; 16 | 17 | /** 18 | * @author Tony Shen 19 | * 20 | */ 21 | public class ViewUtils { 22 | 23 | /** 24 | * 增加view的点击区域,当view是一张小图或者不方便点击时可采用此方法 25 | * 26 | * @param amount 27 | * @param delegate 28 | */ 29 | public static void increaseHitRectBy(final int amount, final View delegate) { 30 | increaseHitRectBy(amount, amount, amount, amount, delegate); 31 | } 32 | 33 | /** 34 | * 35 | * @param top 36 | * @param left 37 | * @param bottom 38 | * @param right 39 | * @param delegate 40 | */ 41 | public static void increaseHitRectBy(final int top, final int left, 42 | final int bottom, final int right, final View delegate) { 43 | final View parent = (View) delegate.getParent(); 44 | if (parent != null && delegate.getContext() != null) { 45 | parent.post(new Runnable() { 46 | // Post in the parent's message queue to make sure the parent 47 | // lays out its children before we call getHitRect() 48 | public void run() { 49 | final float densityDpi = delegate.getContext() 50 | .getResources().getDisplayMetrics().densityDpi; 51 | final Rect r = new Rect(); 52 | delegate.getHitRect(r); 53 | r.top -= transformToDensityPixel(top, densityDpi); 54 | r.left -= transformToDensityPixel(left, densityDpi); 55 | r.bottom += transformToDensityPixel(bottom, densityDpi); 56 | r.right += transformToDensityPixel(right, densityDpi); 57 | parent.setTouchDelegate(new TouchDelegate(r, delegate)); 58 | } 59 | }); 60 | } 61 | } 62 | 63 | public static int transformToDensityPixel(int regularPixel, 64 | DisplayMetrics displayMetrics) { 65 | return transformToDensityPixel(regularPixel, displayMetrics.densityDpi); 66 | } 67 | 68 | public static int transformToDensityPixel(int regularPixel, float densityDpi) { 69 | return (int) (regularPixel * densityDpi); 70 | } 71 | 72 | public static boolean checkBtnEnable(EditText... editTexts) { 73 | boolean enable = true; 74 | for (EditText each : editTexts) { 75 | if (Preconditions.isBlank(each.getText())) { 76 | enable = false; 77 | break; 78 | } 79 | } 80 | return enable; 81 | } 82 | 83 | public static void checkNotBlank(final Button button, 84 | final EditText... editTexts) { 85 | TextWatcher notBlank = new TextWatcher() { 86 | @Override 87 | public void beforeTextChanged(CharSequence charSequence, int i, 88 | int i2, int i3) { 89 | 90 | } 91 | 92 | @Override 93 | public void onTextChanged(CharSequence charSequence, int i, int i2, 94 | int i3) { 95 | if (checkBtnEnable(editTexts)) { 96 | button.setEnabled(true); 97 | } else { 98 | button.setEnabled(false); 99 | } 100 | } 101 | 102 | @Override 103 | public void afterTextChanged(Editable editable) { 104 | 105 | } 106 | }; 107 | 108 | for (EditText each : editTexts) { 109 | each.addTextChangedListener(notBlank); 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/view/LightDialog.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.safframework.saf.view; 5 | 6 | import android.app.AlertDialog; 7 | import android.content.Context; 8 | 9 | import com.safframework.saf.utils.SAFUtils; 10 | 11 | /** 12 | * Holo Light theme的dialog 13 | * 用法如下: 14 | *
 15 |  * 
 16 |  * LightDialog dialog = LightDialog.create(this,"title","test");
 17 |  * dialog.show();
 18 |  * 
 19 |  * 
20 | * @author Tony Shen 21 | * 22 | */ 23 | public class LightDialog extends AlertDialog{ 24 | 25 | /** 26 | * 创建LightDialog,在android4.0及以上的版本dialog采用Holo Light theme 27 | * 28 | * @param context 29 | * @return light alert dialog 30 | */ 31 | public static LightDialog create(Context context) { 32 | LightDialog dialog; 33 | if (SAFUtils.isICSOrHigher()) 34 | dialog = new LightDialog(context, 3); 35 | else { 36 | dialog = new LightDialog(context); 37 | dialog.setInverseBackgroundForced(true); 38 | } 39 | return dialog; 40 | } 41 | 42 | public static LightDialog create(Context context, String title, String message) { 43 | LightDialog dialog = create(context); 44 | dialog.setTitle(title); 45 | dialog.setMessage(message); 46 | return dialog; 47 | } 48 | 49 | public static LightDialog create(Context context, int title, int message) { 50 | LightDialog dialog = create(context); 51 | dialog.setTitle(title); 52 | dialog.setMessage(context.getString(message)); 53 | return dialog; 54 | } 55 | 56 | public static LightDialog create(Context context, int title, String message) { 57 | LightDialog dialog = create(context); 58 | dialog.setTitle(title); 59 | dialog.setMessage(message); 60 | return dialog; 61 | } 62 | 63 | public static LightDialog create(Context context, String title, int message) { 64 | return create(context, title, context.getString(message)); 65 | } 66 | 67 | protected LightDialog(Context context) { 68 | super(context); 69 | } 70 | 71 | /** 72 | * @param context 73 | * @param theme 74 | */ 75 | protected LightDialog(Context context, int theme) { 76 | super(context, theme); 77 | } 78 | 79 | public LightDialog setPositiveButton(int text, OnClickListener listener) { 80 | return setPositiveButton(getContext().getString(text), listener); 81 | } 82 | 83 | public LightDialog setPositiveButton(CharSequence text, 84 | OnClickListener listener) { 85 | setButton(BUTTON_POSITIVE, text, listener); 86 | return this; 87 | } 88 | 89 | public LightDialog setPositiveButton(OnClickListener listener) { 90 | return setPositiveButton(android.R.string.ok, listener); 91 | } 92 | 93 | public LightDialog setNegativeButton(int text, OnClickListener listener) { 94 | return setNegativeButton(getContext().getString(text), listener); 95 | } 96 | 97 | public LightDialog setNegativeButton(CharSequence text, 98 | OnClickListener listener) { 99 | setButton(BUTTON_NEGATIVE, text, listener); 100 | return this; 101 | } 102 | 103 | public LightDialog setNegativeButton(OnClickListener listener) { 104 | return setNegativeButton(android.R.string.cancel, listener); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /saf-core/src/main/java/com/safframework/saf/view/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * SAF常用的组件 3 | */ 4 | package com.safframework.saf.view; -------------------------------------------------------------------------------- /saf-core/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/saf-core/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /saf-core/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/saf-core/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /saf-core/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/saf-core/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /saf-core/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengzhizi715/SAF/e32a9ad8bec474e85c2cf168fe61a2cf2f3e4374/saf-core/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /saf-core/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /saf-core/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /saf-core/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SAF 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /saf-core/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /saf-permission/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /saf-permission/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | def cfg = rootProject.ext.configuration // 配置 5 | def libs = rootProject.ext.libraries // 库 6 | 7 | android { 8 | compileSdkVersion cfg.compileVersion 9 | buildToolsVersion cfg.buildToolsVersion 10 | 11 | defaultConfig { 12 | minSdkVersion 11 13 | targetSdkVersion 25 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 31 | exclude group: 'com.android.support', module: 'support-annotations' 32 | }) 33 | compileOnly 'com.android.support:appcompat-v7:25.1.1' 34 | testCompile 'junit:junit:4.12' 35 | 36 | compileOnly rootProject.ext.rxJava 37 | compileOnly rootProject.ext.rxAndroid 38 | } 39 | 40 | repositories { 41 | jcenter() 42 | } 43 | 44 | allprojects { 45 | repositories { 46 | jcenter() 47 | } 48 | //加上这些 49 | tasks.withType(Javadoc) { 50 | options{ encoding "UTF-8" 51 | charSet 'UTF-8' 52 | links "http://docs.oracle.com/javase/7/docs/api" 53 | } 54 | } 55 | } 56 | 57 | publish{ 58 | userOrg = 'fengzhizi715' 59 | groupId = 'com.safframework' 60 | artifactId = 'saf-permission' 61 | publishVersion = '1.1.0' 62 | desc = 'this is a permission library for android' 63 | website = 'https://github.com/fengzhizi715/SAF' 64 | } -------------------------------------------------------------------------------- /saf-permission/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.4/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /saf-permission/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /saf-permission/src/main/java/com/safframework/permission/Permission.java: -------------------------------------------------------------------------------- 1 | package com.safframework.permission; 2 | 3 | /** 4 | * Created by Tony Shen on 2017/6/23. 5 | */ 6 | 7 | public class Permission { 8 | 9 | public final String name; 10 | public final boolean granted; 11 | public final boolean shouldShowRequestPermissionRationale; 12 | 13 | public Permission(String name, boolean granted) { 14 | this(name, granted, false); 15 | } 16 | 17 | public Permission(String name, boolean granted, boolean shouldShowRequestPermissionRationale) { 18 | this.name = name; 19 | this.granted = granted; 20 | this.shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale; 21 | } 22 | 23 | @Override 24 | @SuppressWarnings("SimplifiableIfStatement") 25 | public boolean equals(final Object o) { 26 | if (this == o) return true; 27 | if (o == null || getClass() != o.getClass()) return false; 28 | 29 | final Permission that = (Permission) o; 30 | 31 | if (granted != that.granted) return false; 32 | if (shouldShowRequestPermissionRationale != that.shouldShowRequestPermissionRationale) 33 | return false; 34 | return name.equals(that.name); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int result = name.hashCode(); 40 | result = 31 * result + (granted ? 1 : 0); 41 | result = 31 * result + (shouldShowRequestPermissionRationale ? 1 : 0); 42 | return result; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "Permission{" + 48 | "name='" + name + '\'' + 49 | ", granted=" + granted + 50 | ", shouldShowRequestPermissionRationale=" + shouldShowRequestPermissionRationale + 51 | '}'; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /saf-permission/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | saf-permission 3 | 4 | -------------------------------------------------------------------------------- /saf-queue/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /saf-queue/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | def cfg = rootProject.ext.configuration // 配置 5 | def libs = rootProject.ext.libraries // 库 6 | 7 | android { 8 | compileSdkVersion cfg.compileVersion 9 | buildToolsVersion cfg.buildToolsVersion 10 | 11 | defaultConfig { 12 | minSdkVersion 9 13 | targetSdkVersion 25 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 31 | exclude group: 'com.android.support', module: 'support-annotations' 32 | }) 33 | compile 'com.android.support:appcompat-v7:25.1.1' 34 | testCompile 'junit:junit:4.12' 35 | } 36 | 37 | repositories { 38 | jcenter() 39 | } 40 | 41 | allprojects { 42 | repositories { 43 | jcenter() 44 | } 45 | //加上这些 46 | tasks.withType(Javadoc) { 47 | options{ encoding "UTF-8" 48 | charSet 'UTF-8' 49 | links "http://docs.oracle.com/javase/7/docs/api" 50 | } 51 | } 52 | } 53 | 54 | publish{ 55 | userOrg = 'fengzhizi715' 56 | groupId = 'com.safframework' 57 | artifactId = 'saf-queue' 58 | publishVersion = '1.0.1' 59 | desc = 'this is a queue library for android' 60 | website = 'https://github.com/fengzhizi715/SAF' 61 | } -------------------------------------------------------------------------------- /saf-queue/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/23.0.2/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /saf-queue/src/androidTest/java/com/safframework/queue/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.safframework.queue; 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 | * Instrumentation 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.safframework.queue.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /saf-queue/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /saf-queue/src/main/java/com/safframework/queue/Operation.java: -------------------------------------------------------------------------------- 1 | package com.safframework.queue; 2 | 3 | import android.os.Bundle; 4 | 5 | /** 6 | * Created by tony on 2017/4/15. 7 | */ 8 | 9 | public interface Operation { 10 | 11 | void run(Queue queue, Bundle bundle); 12 | } 13 | -------------------------------------------------------------------------------- /saf-queue/src/main/java/com/safframework/queue/OperationRunnable.java: -------------------------------------------------------------------------------- 1 | package com.safframework.queue; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import java.lang.ref.WeakReference; 7 | 8 | /** 9 | * Created by tony on 2017/4/15. 10 | */ 11 | 12 | public class OperationRunnable implements Runnable { 13 | 14 | private Operation operation = null; 15 | private Type type = Type.NORMAL; 16 | private Object token = null; 17 | private long time = 0; 18 | private WeakReference owner; 19 | private static final Handler handler = new Handler(Looper.getMainLooper()); 20 | 21 | protected OperationRunnable(Queue queue, Operation operation) { 22 | this(queue, operation, Type.NORMAL, null, 0); 23 | } 24 | 25 | public static void runOnUiThread(Runnable runnable) { 26 | handler.post(runnable); 27 | } 28 | 29 | public static void runOnUiThreadAfterDelay(Runnable runnable, long delayTimeMillis) { 30 | handler.postDelayed(runnable, delayTimeMillis); 31 | } 32 | 33 | public static void removeOperationOnUiThread(Runnable runnable) { 34 | handler.removeCallbacks(runnable); 35 | } 36 | 37 | public static void sleep(long sleepTimeMillis) { 38 | try { 39 | Thread.sleep(sleepTimeMillis); 40 | } catch (Exception e) { 41 | } 42 | } 43 | 44 | protected enum Type { 45 | 46 | NORMAL, 47 | ATFIRST, 48 | ATTIME, 49 | ATTIME_WITH_TOKEN, 50 | DELAY, 51 | } 52 | 53 | protected OperationRunnable(Queue queue, Operation operation, Type type, Object token, long time) { 54 | this.owner = new WeakReference(queue); 55 | this.operation = operation; 56 | this.type = type; 57 | this.token = token; 58 | this.time = time; 59 | } 60 | 61 | @Override 62 | public boolean equals(Object o) { 63 | if (this == o) return true; 64 | if (o == null || getClass() != o.getClass()) return false; 65 | 66 | OperationRunnable that = (OperationRunnable) o; 67 | 68 | if (!operation.equals(that.operation)) return false; 69 | return token != null ? token.equals(that.token) : that.token == null; 70 | 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | int result = operation.hashCode(); 76 | result = 31 * result + (token != null ? token.hashCode() : 0); 77 | return result; 78 | } 79 | 80 | @Override 81 | public void run() { 82 | 83 | Queue queue = owner.get(); 84 | if(queue != null && queue.isActivated()) { 85 | operation.run(queue, queue.getBundle()); 86 | } 87 | 88 | } 89 | 90 | protected void queueing(Handler handler) { 91 | switch (type) { 92 | case NORMAL: 93 | handler.post(this); 94 | break; 95 | case ATFIRST: 96 | handler.postAtFrontOfQueue(this); 97 | break; 98 | case ATTIME: 99 | handler.postAtTime(this, time); 100 | break; 101 | case ATTIME_WITH_TOKEN: 102 | handler.postAtTime(this, token, time); 103 | break; 104 | case DELAY: 105 | handler.postDelayed(this, time); 106 | break; 107 | default: 108 | handler.post(this); 109 | break; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /saf-queue/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | saf-queue 3 | 4 | -------------------------------------------------------------------------------- /saf-rxlifecycle/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /saf-rxlifecycle/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | def cfg = rootProject.ext.configuration // 配置 5 | def libs = rootProject.ext.libraries // 库 6 | 7 | android { 8 | compileSdkVersion cfg.compileVersion 9 | buildToolsVersion cfg.buildToolsVersion 10 | 11 | defaultConfig { 12 | minSdkVersion cfg.minSdk 13 | targetSdkVersion cfg.targetSdk 14 | versionCode 1 15 | versionName cfg.saf_version_name 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 31 | exclude group: 'com.android.support', module: 'support-annotations' 32 | }) 33 | compile 'com.android.support:appcompat-v7:25.1.1' 34 | testCompile 'junit:junit:4.12' 35 | 36 | compile rootProject.ext.rxJava 37 | compile rootProject.ext.rxAndroid 38 | } 39 | 40 | repositories { 41 | jcenter() 42 | } 43 | 44 | allprojects { 45 | repositories { 46 | jcenter() 47 | } 48 | //加上这些 49 | tasks.withType(Javadoc) { 50 | options{ encoding "UTF-8" 51 | charSet 'UTF-8' 52 | links "http://docs.oracle.com/javase/7/docs/api" 53 | } 54 | } 55 | } 56 | 57 | publish{ 58 | userOrg = 'fengzhizi715' 59 | groupId = 'com.safframework' 60 | artifactId = 'saf-rxlifecycle' 61 | publishVersion = '1.1.2' 62 | desc = 'this is a lifecycle library for rxjava2 in android' 63 | website = 'https://github.com/fengzhizi715/SAF' 64 | } -------------------------------------------------------------------------------- /saf-rxlifecycle/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.4/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /saf-rxlifecycle/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /saf-rxlifecycle/src/main/java/com/safframework/lifecycle/BindingFragment.java: -------------------------------------------------------------------------------- 1 | package com.safframework.lifecycle; 2 | 3 | import android.app.Activity; 4 | import android.app.Fragment; 5 | import android.content.Context; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.support.annotation.Nullable; 9 | import android.support.annotation.RequiresApi; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | /** 15 | * Created by tony on 2017/12/26. 16 | */ 17 | @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) 18 | public class BindingFragment extends Fragment { 19 | 20 | private final LifecyclePublisher lifecyclePublisher = new LifecyclePublisher(); 21 | 22 | public BindingFragment() { 23 | } 24 | 25 | public LifecyclePublisher getLifecyclePublisher() { 26 | return lifecyclePublisher; 27 | } 28 | 29 | @Override 30 | public void onAttach(Context context) { 31 | super.onAttach(context); 32 | lifecyclePublisher.onAttach(); 33 | } 34 | 35 | @Override 36 | public void onAttach(Activity activity) { 37 | super.onAttach(activity); 38 | lifecyclePublisher.onAttach(); 39 | } 40 | 41 | @Override 42 | public void onCreate(@Nullable Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | lifecyclePublisher.onCreate(); 45 | } 46 | 47 | @Nullable 48 | @Override 49 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 50 | lifecyclePublisher.onCreateView(); 51 | return null; 52 | } 53 | 54 | @Override 55 | public void onStart() { 56 | super.onStart(); 57 | lifecyclePublisher.onStart(); 58 | } 59 | 60 | @Override 61 | public void onResume() { 62 | super.onResume(); 63 | lifecyclePublisher.onResume(); 64 | } 65 | 66 | @Override 67 | public void onPause() { 68 | super.onPause(); 69 | lifecyclePublisher.onPause(); 70 | } 71 | 72 | @Override 73 | public void onStop() { 74 | super.onStop(); 75 | lifecyclePublisher.onStop(); 76 | } 77 | 78 | @Override 79 | public void onDestroyView() { 80 | super.onDestroyView(); 81 | lifecyclePublisher.onDestroyView(); 82 | } 83 | 84 | @Override 85 | public void onDestroy() { 86 | super.onDestroy(); 87 | lifecyclePublisher.onDestroy(); 88 | } 89 | 90 | @Override 91 | public void onDetach() { 92 | super.onDetach(); 93 | lifecyclePublisher.onDetach(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /saf-rxlifecycle/src/main/java/com/safframework/lifecycle/BindingV4Fragment.java: -------------------------------------------------------------------------------- 1 | package com.safframework.lifecycle; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | /** 13 | * Created by Tony Shen on 2017/5/25. 14 | */ 15 | 16 | public class BindingV4Fragment extends Fragment { 17 | 18 | private final LifecyclePublisher lifecyclePublisher = new LifecyclePublisher(); 19 | 20 | public BindingV4Fragment() { 21 | } 22 | 23 | public LifecyclePublisher getLifecyclePublisher() { 24 | return lifecyclePublisher; 25 | } 26 | 27 | @Override 28 | public void onAttach(Context context) { 29 | super.onAttach(context); 30 | lifecyclePublisher.onAttach(); 31 | } 32 | 33 | @Override 34 | public void onAttach(Activity activity) { 35 | super.onAttach(activity); 36 | lifecyclePublisher.onAttach(); 37 | } 38 | 39 | @Override 40 | public void onCreate(@Nullable Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | lifecyclePublisher.onCreate(); 43 | } 44 | 45 | @Nullable 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 48 | lifecyclePublisher.onCreateView(); 49 | return null; 50 | } 51 | 52 | @Override 53 | public void onStart() { 54 | super.onStart(); 55 | lifecyclePublisher.onStart(); 56 | } 57 | 58 | @Override 59 | public void onResume() { 60 | super.onResume(); 61 | lifecyclePublisher.onResume(); 62 | } 63 | 64 | @Override 65 | public void onPause() { 66 | super.onPause(); 67 | lifecyclePublisher.onPause(); 68 | } 69 | 70 | @Override 71 | public void onStop() { 72 | super.onStop(); 73 | lifecyclePublisher.onStop(); 74 | } 75 | 76 | @Override 77 | public void onDestroyView() { 78 | super.onDestroyView(); 79 | lifecyclePublisher.onDestroyView(); 80 | } 81 | 82 | @Override 83 | public void onDestroy() { 84 | super.onDestroy(); 85 | lifecyclePublisher.onDestroy(); 86 | } 87 | 88 | @Override 89 | public void onDetach() { 90 | super.onDetach(); 91 | lifecyclePublisher.onDetach(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /saf-rxlifecycle/src/main/java/com/safframework/lifecycle/LifecyclePublisher.java: -------------------------------------------------------------------------------- 1 | package com.safframework.lifecycle; 2 | 3 | import android.support.annotation.IntDef; 4 | 5 | import io.reactivex.processors.BehaviorProcessor; 6 | 7 | /** 8 | * Created by Tony Shen on 2017/5/25. 9 | */ 10 | 11 | public class LifecyclePublisher { 12 | 13 | public static final int ON_ATTACH = 0; 14 | public static final int ON_CREATE = 1; 15 | public static final int ON_CREATE_VIEW = 2; 16 | public static final int ON_START = 3; 17 | public static final int ON_RESUME = 4; 18 | public static final int ON_PAUSE = 5; 19 | public static final int ON_STOP = 6; 20 | public static final int ON_DESTROY_VIEW = 7; 21 | public static final int ON_DESTROY = 8; 22 | public static final int ON_DETACH = 9; 23 | 24 | private final BehaviorProcessor behavior = BehaviorProcessor.create(); 25 | 26 | @IntDef({ON_ATTACH, ON_CREATE, ON_CREATE_VIEW, 27 | ON_START, ON_RESUME, 28 | ON_PAUSE, ON_STOP, 29 | ON_DESTROY_VIEW, ON_DESTROY, ON_DETACH}) 30 | public @interface Event { 31 | } 32 | 33 | public BehaviorProcessor getBehavior() { 34 | return behavior; 35 | } 36 | 37 | public void onAttach() { 38 | behavior.onNext(ON_ATTACH); 39 | } 40 | 41 | public void onCreate() { 42 | behavior.onNext(ON_CREATE); 43 | } 44 | 45 | public void onCreateView() { 46 | behavior.onNext(ON_CREATE_VIEW); 47 | } 48 | 49 | public void onStart() { 50 | behavior.onNext(ON_START); 51 | } 52 | 53 | public void onResume() { 54 | behavior.onNext(ON_RESUME); 55 | } 56 | 57 | public void onPause() { 58 | behavior.onNext(ON_PAUSE); 59 | } 60 | 61 | public void onStop() { 62 | behavior.onNext(ON_STOP); 63 | } 64 | 65 | public void onDestroyView() { 66 | behavior.onNext(ON_DESTROY_VIEW); 67 | } 68 | 69 | public void onDestroy() { 70 | behavior.onNext(ON_DESTROY); 71 | } 72 | 73 | public void onDetach() { 74 | behavior.onNext(ON_DETACH); 75 | } 76 | } -------------------------------------------------------------------------------- /saf-rxlifecycle/src/main/java/com/safframework/lifecycle/RxLifecycle.java: -------------------------------------------------------------------------------- 1 | package com.safframework.lifecycle; 2 | 3 | import android.app.Fragment; 4 | import android.app.FragmentManager; 5 | import android.app.FragmentTransaction; 6 | import android.os.Build; 7 | import android.support.annotation.NonNull; 8 | import android.support.annotation.RequiresApi; 9 | import android.support.v7.app.AppCompatActivity; 10 | 11 | import io.reactivex.Flowable; 12 | import io.reactivex.Observable; 13 | 14 | /** 15 | * Created by Tony Shen on 2017/5/25. 16 | */ 17 | 18 | public class RxLifecycle { 19 | 20 | private static final String FRAGMENT_TAG = "_BINDING_FRAGMENT_"; 21 | private final LifecyclePublisher lifecyclePublisher; 22 | 23 | @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) 24 | public static RxLifecycle bind(@NonNull AppCompatActivity targetActivity) { 25 | return bind(targetActivity.getSupportFragmentManager()); 26 | } 27 | 28 | 29 | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) 30 | public static RxLifecycle bind(@NonNull Fragment targetFragment) { 31 | return bind(targetFragment.getChildFragmentManager()); 32 | } 33 | 34 | @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) 35 | public static RxLifecycle bind(@NonNull FragmentManager fragmentManager) { 36 | BindingFragment fragment = (BindingFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG); 37 | if (fragment == null) { 38 | fragment = new BindingFragment(); 39 | 40 | final FragmentTransaction transaction = fragmentManager.beginTransaction(); 41 | transaction.add(fragment, FRAGMENT_TAG); 42 | transaction.commit(); 43 | 44 | } else if (Build.VERSION.SDK_INT >= 13 && fragment.isDetached()) { 45 | final FragmentTransaction transaction = fragmentManager.beginTransaction(); 46 | transaction.attach(fragment); 47 | transaction.commit(); 48 | } 49 | 50 | return bind(fragment.getLifecyclePublisher()); 51 | } 52 | 53 | public static RxLifecycle bind(@NonNull android.support.v4.app.Fragment targetFragment) { 54 | return bind(targetFragment.getChildFragmentManager()); 55 | } 56 | 57 | public static RxLifecycle bind(@NonNull android.support.v4.app.FragmentManager fragmentManager) { 58 | BindingV4Fragment fragment = (BindingV4Fragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG); 59 | if (fragment == null) { 60 | fragment = new BindingV4Fragment(); 61 | 62 | final android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction(); 63 | transaction.add(fragment, FRAGMENT_TAG); 64 | transaction.commit(); 65 | } else if (fragment.isDetached()) { 66 | 67 | final android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction(); 68 | transaction.attach(fragment); 69 | transaction.commit(); 70 | } 71 | 72 | return bind(fragment.getLifecyclePublisher()); 73 | } 74 | 75 | public static RxLifecycle bind(@NonNull LifecyclePublisher lifecyclePublisher) { 76 | return new RxLifecycle(lifecyclePublisher); 77 | } 78 | 79 | private RxLifecycle() throws IllegalAccessException { 80 | throw new IllegalAccessException(); 81 | } 82 | 83 | private RxLifecycle(@NonNull LifecyclePublisher lifecyclePublisher) { 84 | this.lifecyclePublisher = lifecyclePublisher; 85 | } 86 | 87 | public Flowable asFlowable() { 88 | return lifecyclePublisher.getBehavior(); 89 | } 90 | 91 | public Observable asObservable() { 92 | return lifecyclePublisher.getBehavior().toObservable(); 93 | } 94 | 95 | public LifecycleTransformer toLifecycleTransformer() { 96 | return new LifecycleTransformer(lifecyclePublisher.getBehavior()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /saf-rxlifecycle/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | saf-rxlifecycle 3 | 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':saf-core' 2 | include ':saf-queue' 3 | include ':saf-cache' 4 | include ':saf-permission' 5 | include ':saf-rxlifecycle' 6 | include 'app' 7 | --------------------------------------------------------------------------------