From 54127ffd102ed962a04324441ea77977dbc12a37 Mon Sep 17 00:00:00 2001 From: 陶汉栋 <2821744554@qq.com> Date: Mon, 5 Mar 2018 17:33:43 +0800 Subject: [PATCH] no message --- .gitignore | 9 +++++++++ .idea/gradle.xml | 19 +++++++++++++++++++ .idea/misc.xml | 33 +++++++++++++++++++++++++++++++++ .idea/modules.xml | 10 ++++++++++ .idea/runConfigurations.xml | 12 ++++++++++++ .idea/vcs.xml | 6 ++++++ app/.gitignore | 1 + app/build.gradle | 30 ++++++++++++++++++++++++++++++ app/proguard-rules.pro | 21 +++++++++++++++++++++ app/src/androidTest/java/com/shunzhi/parent/ExampleInstrumentedTest.java | 26 ++++++++++++++++++++++++++ app/src/main/AndroidManifest.xml | 24 ++++++++++++++++++++++++ app/src/main/java/com/shunzhi/parent/MyApplication.java | 17 +++++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/MainActivity.java | 15 +++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/activity/LoginAndRegistActivity.java | 15 +++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/activity/StartActivity.java | 15 +++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/fragment/CePingFragment.java | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/fragment/ConsultFragment.java | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/fragment/MineFragment.java | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/fragment/ReportFragment.java | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/src/main/java/com/shunzhi/parent/ui/fragment/loginandregistfragment/LoginAndRegistFragment.java | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/src/main/res/drawable-v24/ic_launcher_foreground.xml | 34 ++++++++++++++++++++++++++++++++++ app/src/main/res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/src/main/res/layout/activity_main.xml | 18 ++++++++++++++++++ app/src/main/res/layout/activity_regist.xml | 9 +++++++++ app/src/main/res/layout/activity_start.xml | 9 +++++++++ app/src/main/res/layout/fragment_ce_ping.xml | 13 +++++++++++++ app/src/main/res/layout/fragment_login_and_regist.xml | 13 +++++++++++++ app/src/main/res/layout/fragment_mine.xml | 13 +++++++++++++ app/src/main/res/layout/fragment_report.xml | 13 +++++++++++++ app/src/main/res/layout/fragment_zi_xun.xml | 13 +++++++++++++ app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 5 +++++ app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml | 5 +++++ app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes app/src/main/res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes app/src/main/res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes app/src/main/res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes app/src/main/res/values/colors.xml | 6 ++++++ app/src/main/res/values/strings.xml | 6 ++++++ app/src/main/res/values/styles.xml | 11 +++++++++++ app/src/test/java/com/shunzhi/parent/ExampleUnitTest.java | 17 +++++++++++++++++ build.gradle | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gradle.properties | 17 +++++++++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes gradle/wrapper/gradle-wrapper.properties | 6 ++++++ gradlew | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gradlew.bat | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/.gitignore | 1 + mvpsdk/build.gradle | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/proguard-rules.pro | 21 +++++++++++++++++++++ mvpsdk/src/androidTest/java/com/share/mvpsdk/ExampleInstrumentedTest.java | 26 ++++++++++++++++++++++++++ mvpsdk/src/main/AndroidManifest.xml | 2 ++ mvpsdk/src/main/java/com/share/mvpsdk/AppManager.java | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/RxManager.java | 23 +++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/adapter/FragmentAdapter.java | 42 ++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimManager.java | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimUtils.java | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/anim/ToolbarAnimManager.java | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/BaseModel.java | 11 +++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/BasePresenter.java | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseActivity.java | 35 +++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseFragment.java | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseModel.java | 9 +++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseView.java | 47 +++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseCompatActivity.java | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseMVPCompatActivity.java | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewAdapter.java | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewHolder.java | 22 ++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemClickListener.java | 10 ++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemLongClickListener.java | 10 ++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseEntity.java | 47 +++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseListEntity.java | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseCompatFragment.java | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseMVPCompatFragment.java | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseRecycleFragment.java | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/config/DBConfig.java | 16 ++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/config/ItemState.java | 19 +++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/global/GlobalApplication.java | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/BottomNavigationViewHelper.java | 37 +++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/RetrofitCreateHelper.java | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/RxHelper.java | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/CacheInterceptor.java | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/HttpCache.java | 28 ++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NetInterceptor.java | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NoNetInterceptor.java | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/TrustManager.java | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/rxbus/BusData.java | 34 ++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/rxbus/RxBus.java | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/rxbus/Subscribe.java | 21 +++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/rxbus/SubscriberMethod.java | 44 ++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/rxbus/ThreadMode.java | 23 +++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/AppUtils.java | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/DBUtils.java | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/DateUtils.java | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/DialogUtils.java | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/DisplayUtils.java | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/FileUtils.java | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/HtmlUtils.java | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/HttpUtils.java | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/IOUtils.java | 25 +++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/ImageUtils.java | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/JsonUtils.java | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/LogUtils.java | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/MD5Utils.java | 38 ++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/NavigationUtils.java | 23 +++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/NetworkConnectionUtils.java | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/PermissionUtils.java | 323 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/ResourcesUtils.java | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/ScreenUtils.java | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/SnackbarUtils.java | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/SpUtils.java | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/StatusBarUtils.java | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/StringUtils.java | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/ThemeUtils.java | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/TimestampUtils.java | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/ToastUtils.java | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/UnicodeUtils.java | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/utils/WifiAutoConnectManager.java | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/view/MyEyeView.java | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/view/MyViewView.java | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/CompatNestedScrollView.java | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryChartView.java | 719 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryModeView.java | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryTipView.java | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingImageView.java | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingViewAnimator.java | 448 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/NestedScrollWebView.java | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/PickerView.java | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/ProgressButton.java | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/SlideSwitchView.java | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/WaitPorgressDialog.java | 22 ++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipView.java | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipViewLayout.java | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/anim/activity_finish_trans_in.xml | 11 +++++++++++ mvpsdk/src/main/res/anim/activity_finish_trans_out.xml | 21 +++++++++++++++++++++ mvpsdk/src/main/res/anim/activity_finish_zoom_in.xml | 12 ++++++++++++ mvpsdk/src/main/res/anim/activity_finish_zoom_out.xml | 18 ++++++++++++++++++ mvpsdk/src/main/res/anim/activity_start_trans_in.xml | 21 +++++++++++++++++++++ mvpsdk/src/main/res/anim/activity_start_trans_out.xml | 11 +++++++++++ mvpsdk/src/main/res/anim/activity_start_zoom_in.xml | 12 ++++++++++++ mvpsdk/src/main/res/anim/activity_start_zoom_out.xml | 17 +++++++++++++++++ mvpsdk/src/main/res/drawable/ic_vector_empty.xml | 30 ++++++++++++++++++++++++++++++ mvpsdk/src/main/res/drawable/ic_vector_loading.xml | 8 ++++++++ mvpsdk/src/main/res/drawable/ic_vector_net_error.xml | 15 +++++++++++++++ mvpsdk/src/main/res/drawable/indoor_temp.png | Bin 0 -> 617 bytes mvpsdk/src/main/res/drawable/item_touch_bg.xml | 5 +++++ mvpsdk/src/main/res/drawable/stackblur_default.png | Bin 0 -> 32876 bytes mvpsdk/src/main/res/drawable/week_normal.png | Bin 0 -> 904 bytes mvpsdk/src/main/res/drawable/week_selector.png | Bin 0 -> 3101 bytes mvpsdk/src/main/res/drawable/week_status.xml | 9 +++++++++ mvpsdk/src/main/res/drawable/week_tv_status.xml | 7 +++++++ mvpsdk/src/main/res/layout/sub_history_click_view.xml | 25 +++++++++++++++++++++++++ mvpsdk/src/main/res/layout/sub_history_tips.xml | 35 +++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/layout/view_empty.xml | 19 +++++++++++++++++++ mvpsdk/src/main/res/layout/view_loading.xml | 32 ++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/layout/view_network_error.xml | 19 +++++++++++++++++++ mvpsdk/src/main/res/mipmap-xhdpi/ic_arrow_back_white.png | Bin 0 -> 233 bytes mvpsdk/src/main/res/mipmap-xxhdpi/ic_arrow_back_white.png | Bin 0 -> 374 bytes mvpsdk/src/main/res/values-v21/styles.xml | 13 +++++++++++++ mvpsdk/src/main/res/values-zh-rCN/strings.xml | 38 ++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/values/arrays.xml | 24 ++++++++++++++++++++++++ mvpsdk/src/main/res/values/attrs.xml | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/values/colors.xml | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/values/dimens.xml | 45 +++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/values/strings.xml | 38 ++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/values/styles.xml | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/values/themes.xml | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/main/res/values/themes_colors.xml | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mvpsdk/src/test/java/com/share/mvpsdk/ExampleUnitTest.java | 17 +++++++++++++++++ settings.gradle | 1 + 175 files changed, 13161 insertions(+), 0 deletions(-) create mode 100644 .gitignore create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 .idea/vcs.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/shunzhi/parent/ExampleInstrumentedTest.java create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/shunzhi/parent/MyApplication.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/MainActivity.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/activity/LoginAndRegistActivity.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/activity/StartActivity.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/fragment/CePingFragment.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/fragment/ConsultFragment.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/fragment/MineFragment.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/fragment/ReportFragment.java create mode 100644 app/src/main/java/com/shunzhi/parent/ui/fragment/loginandregistfragment/LoginAndRegistFragment.java create mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/activity_regist.xml create mode 100644 app/src/main/res/layout/activity_start.xml create mode 100644 app/src/main/res/layout/fragment_ce_ping.xml create mode 100644 app/src/main/res/layout/fragment_login_and_regist.xml create mode 100644 app/src/main/res/layout/fragment_mine.xml create mode 100644 app/src/main/res/layout/fragment_report.xml create mode 100644 app/src/main/res/layout/fragment_zi_xun.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/test/java/com/shunzhi/parent/ExampleUnitTest.java create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 mvpsdk/.gitignore create mode 100644 mvpsdk/build.gradle create mode 100644 mvpsdk/proguard-rules.pro create mode 100644 mvpsdk/src/androidTest/java/com/share/mvpsdk/ExampleInstrumentedTest.java create mode 100644 mvpsdk/src/main/AndroidManifest.xml create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/AppManager.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/RxManager.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/adapter/FragmentAdapter.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimManager.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/anim/ToolbarAnimManager.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/BaseModel.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/BasePresenter.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseActivity.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseFragment.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseModel.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseCompatActivity.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseMVPCompatActivity.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewAdapter.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewHolder.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemClickListener.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemLongClickListener.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseEntity.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseListEntity.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseCompatFragment.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseMVPCompatFragment.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseRecycleFragment.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/config/DBConfig.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/config/ItemState.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/global/GlobalApplication.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/BottomNavigationViewHelper.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/RetrofitCreateHelper.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/RxHelper.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/CacheInterceptor.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/HttpCache.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NetInterceptor.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NoNetInterceptor.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/TrustManager.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/rxbus/BusData.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/rxbus/RxBus.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/rxbus/Subscribe.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/rxbus/SubscriberMethod.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/rxbus/ThreadMode.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/AppUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/DBUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/DateUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/DialogUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/DisplayUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/FileUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/HtmlUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/HttpUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/IOUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/ImageUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/JsonUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/LogUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/MD5Utils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/NavigationUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/NetworkConnectionUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/PermissionUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/ResourcesUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/ScreenUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/SnackbarUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/SpUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/StatusBarUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/StringUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/ThemeUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/TimestampUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/ToastUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/UnicodeUtils.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/utils/WifiAutoConnectManager.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/view/MyEyeView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/view/MyViewView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/CompatNestedScrollView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryChartView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryModeView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryTipView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingImageView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingViewAnimator.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/NestedScrollWebView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/PickerView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/ProgressButton.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/SlideSwitchView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/WaitPorgressDialog.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipView.java create mode 100644 mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipViewLayout.java create mode 100644 mvpsdk/src/main/res/anim/activity_finish_trans_in.xml create mode 100644 mvpsdk/src/main/res/anim/activity_finish_trans_out.xml create mode 100644 mvpsdk/src/main/res/anim/activity_finish_zoom_in.xml create mode 100644 mvpsdk/src/main/res/anim/activity_finish_zoom_out.xml create mode 100644 mvpsdk/src/main/res/anim/activity_start_trans_in.xml create mode 100644 mvpsdk/src/main/res/anim/activity_start_trans_out.xml create mode 100644 mvpsdk/src/main/res/anim/activity_start_zoom_in.xml create mode 100644 mvpsdk/src/main/res/anim/activity_start_zoom_out.xml create mode 100644 mvpsdk/src/main/res/drawable/ic_vector_empty.xml create mode 100644 mvpsdk/src/main/res/drawable/ic_vector_loading.xml create mode 100644 mvpsdk/src/main/res/drawable/ic_vector_net_error.xml create mode 100644 mvpsdk/src/main/res/drawable/indoor_temp.png create mode 100644 mvpsdk/src/main/res/drawable/item_touch_bg.xml create mode 100644 mvpsdk/src/main/res/drawable/stackblur_default.png create mode 100644 mvpsdk/src/main/res/drawable/week_normal.png create mode 100644 mvpsdk/src/main/res/drawable/week_selector.png create mode 100644 mvpsdk/src/main/res/drawable/week_status.xml create mode 100644 mvpsdk/src/main/res/drawable/week_tv_status.xml create mode 100644 mvpsdk/src/main/res/layout/sub_history_click_view.xml create mode 100644 mvpsdk/src/main/res/layout/sub_history_tips.xml create mode 100644 mvpsdk/src/main/res/layout/view_empty.xml create mode 100644 mvpsdk/src/main/res/layout/view_loading.xml create mode 100644 mvpsdk/src/main/res/layout/view_network_error.xml create mode 100644 mvpsdk/src/main/res/mipmap-xhdpi/ic_arrow_back_white.png create mode 100644 mvpsdk/src/main/res/mipmap-xxhdpi/ic_arrow_back_white.png create mode 100644 mvpsdk/src/main/res/values-v21/styles.xml create mode 100644 mvpsdk/src/main/res/values-zh-rCN/strings.xml create mode 100644 mvpsdk/src/main/res/values/arrays.xml create mode 100644 mvpsdk/src/main/res/values/attrs.xml create mode 100644 mvpsdk/src/main/res/values/colors.xml create mode 100644 mvpsdk/src/main/res/values/dimens.xml create mode 100644 mvpsdk/src/main/res/values/strings.xml create mode 100644 mvpsdk/src/main/res/values/styles.xml create mode 100644 mvpsdk/src/main/res/values/themes.xml create mode 100644 mvpsdk/src/main/res/values/themes_colors.xml create mode 100644 mvpsdk/src/test/java/com/share/mvpsdk/ExampleUnitTest.java create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39fb081 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..1887e74 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3963879 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..69c30e2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..2786678 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + defaultConfig { + applicationId "com.shunzhi.parent" + minSdkVersion 16 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'com.android.support:appcompat-v7:26.1.0' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support:support-v4:26.1.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + implementation project(':mvpsdk') +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/shunzhi/parent/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/shunzhi/parent/ExampleInstrumentedTest.java new file mode 100644 index 0000000..c6f8c6e --- /dev/null +++ b/app/src/androidTest/java/com/shunzhi/parent/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.shunzhi.parent; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.shunzhi.parent", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b2ebf1d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/shunzhi/parent/MyApplication.java b/app/src/main/java/com/shunzhi/parent/MyApplication.java new file mode 100644 index 0000000..ce05905 --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/MyApplication.java @@ -0,0 +1,17 @@ +package com.shunzhi.parent; + +import com.share.mvpsdk.global.GlobalApplication; + +/** + * Created by ToaHanDong on 2018/3/2. + */ + +public class MyApplication extends GlobalApplication { + + + @Override + public void onCreate() { + super.onCreate(); + + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/MainActivity.java b/app/src/main/java/com/shunzhi/parent/ui/MainActivity.java new file mode 100644 index 0000000..8a0fa33 --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/MainActivity.java @@ -0,0 +1,15 @@ +package com.shunzhi.parent.ui; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; + +import com.shunzhi.parent.R; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/activity/LoginAndRegistActivity.java b/app/src/main/java/com/shunzhi/parent/ui/activity/LoginAndRegistActivity.java new file mode 100644 index 0000000..3d7a8bb --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/activity/LoginAndRegistActivity.java @@ -0,0 +1,15 @@ +package com.shunzhi.parent.ui.activity; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; + +import com.shunzhi.parent.R; + +public class LoginAndRegistActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_regist); + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/activity/StartActivity.java b/app/src/main/java/com/shunzhi/parent/ui/activity/StartActivity.java new file mode 100644 index 0000000..5c1dc77 --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/activity/StartActivity.java @@ -0,0 +1,15 @@ +package com.shunzhi.parent.ui.activity; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; + +import com.shunzhi.parent.R; + +public class StartActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_start); + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/fragment/CePingFragment.java b/app/src/main/java/com/shunzhi/parent/ui/fragment/CePingFragment.java new file mode 100644 index 0000000..9a5617b --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/fragment/CePingFragment.java @@ -0,0 +1,109 @@ +package com.shunzhi.parent.ui.fragment; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.shunzhi.parent.R; + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link CePingFragment.OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link CePingFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class CePingFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + private OnFragmentInteractionListener mListener; + + public CePingFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment CePingFragment. + */ + // TODO: Rename and change types and number of parameters + public static CePingFragment newInstance(String param1, String param2) { + CePingFragment fragment = new CePingFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_ce_ping, container, false); + } + + // TODO: Rename method, update argument and hook method into UI event + public void onButtonPressed(Uri uri) { + if (mListener != null) { + mListener.onFragmentInteraction(uri); + } + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnFragmentInteractionListener { + // TODO: Update argument type and name + void onFragmentInteraction(Uri uri); + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/fragment/ConsultFragment.java b/app/src/main/java/com/shunzhi/parent/ui/fragment/ConsultFragment.java new file mode 100644 index 0000000..13b0279 --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/fragment/ConsultFragment.java @@ -0,0 +1,109 @@ +package com.shunzhi.parent.ui.fragment; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.shunzhi.parent.R; + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link ConsultFragment.OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link ConsultFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class ConsultFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + private OnFragmentInteractionListener mListener; + + public ConsultFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment ConsultFragment. + */ + // TODO: Rename and change types and number of parameters + public static ConsultFragment newInstance(String param1, String param2) { + ConsultFragment fragment = new ConsultFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_zi_xun, container, false); + } + + // TODO: Rename method, update argument and hook method into UI event + public void onButtonPressed(Uri uri) { + if (mListener != null) { + mListener.onFragmentInteraction(uri); + } + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnFragmentInteractionListener { + // TODO: Update argument type and name + void onFragmentInteraction(Uri uri); + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/fragment/MineFragment.java b/app/src/main/java/com/shunzhi/parent/ui/fragment/MineFragment.java new file mode 100644 index 0000000..a5b12cd --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/fragment/MineFragment.java @@ -0,0 +1,109 @@ +package com.shunzhi.parent.ui.fragment; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.shunzhi.parent.R; + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link MineFragment.OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link MineFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class MineFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + private OnFragmentInteractionListener mListener; + + public MineFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment MineFragment. + */ + // TODO: Rename and change types and number of parameters + public static MineFragment newInstance(String param1, String param2) { + MineFragment fragment = new MineFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_mine, container, false); + } + + // TODO: Rename method, update argument and hook method into UI event + public void onButtonPressed(Uri uri) { + if (mListener != null) { + mListener.onFragmentInteraction(uri); + } + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnFragmentInteractionListener { + // TODO: Update argument type and name + void onFragmentInteraction(Uri uri); + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/fragment/ReportFragment.java b/app/src/main/java/com/shunzhi/parent/ui/fragment/ReportFragment.java new file mode 100644 index 0000000..f49edca --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/fragment/ReportFragment.java @@ -0,0 +1,109 @@ +package com.shunzhi.parent.ui.fragment; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.shunzhi.parent.R; + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link ReportFragment.OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link ReportFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class ReportFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + private OnFragmentInteractionListener mListener; + + public ReportFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment ReportFragment. + */ + // TODO: Rename and change types and number of parameters + public static ReportFragment newInstance(String param1, String param2) { + ReportFragment fragment = new ReportFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_report, container, false); + } + + // TODO: Rename method, update argument and hook method into UI event + public void onButtonPressed(Uri uri) { + if (mListener != null) { + mListener.onFragmentInteraction(uri); + } + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnFragmentInteractionListener { + // TODO: Update argument type and name + void onFragmentInteraction(Uri uri); + } +} diff --git a/app/src/main/java/com/shunzhi/parent/ui/fragment/loginandregistfragment/LoginAndRegistFragment.java b/app/src/main/java/com/shunzhi/parent/ui/fragment/loginandregistfragment/LoginAndRegistFragment.java new file mode 100644 index 0000000..bfa05d5 --- /dev/null +++ b/app/src/main/java/com/shunzhi/parent/ui/fragment/loginandregistfragment/LoginAndRegistFragment.java @@ -0,0 +1,109 @@ +package com.shunzhi.parent.ui.fragment.loginandregistfragment; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.shunzhi.parent.R; + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link LoginAndRegistFragment.OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link LoginAndRegistFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class LoginAndRegistFragment extends Fragment { + // TODO: Rename parameter arguments, choose names that match + // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + private static final String ARG_PARAM1 = "param1"; + private static final String ARG_PARAM2 = "param2"; + + // TODO: Rename and change types of parameters + private String mParam1; + private String mParam2; + + private OnFragmentInteractionListener mListener; + + public LoginAndRegistFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment LoginAndRegistFragment. + */ + // TODO: Rename and change types and number of parameters + public static LoginAndRegistFragment newInstance(String param1, String param2) { + LoginAndRegistFragment fragment = new LoginAndRegistFragment(); + Bundle args = new Bundle(); + args.putString(ARG_PARAM1, param1); + args.putString(ARG_PARAM2, param2); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mParam1 = getArguments().getString(ARG_PARAM1); + mParam2 = getArguments().getString(ARG_PARAM2); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_login_and_regist, container, false); + } + + // TODO: Rename method, update argument and hook method into UI event + public void onButtonPressed(Uri uri) { + if (mListener != null) { + mListener.onFragmentInteraction(uri); + } + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnFragmentInteractionListener { + // TODO: Update argument type and name + void onFragmentInteraction(Uri uri); + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d252704 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_regist.xml b/app/src/main/res/layout/activity_regist.xml new file mode 100644 index 0000000..beac382 --- /dev/null +++ b/app/src/main/res/layout/activity_regist.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/layout/activity_start.xml b/app/src/main/res/layout/activity_start.xml new file mode 100644 index 0000000..0ec5e68 --- /dev/null +++ b/app/src/main/res/layout/activity_start.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/layout/fragment_ce_ping.xml b/app/src/main/res/layout/fragment_ce_ping.xml new file mode 100644 index 0000000..67700f1 --- /dev/null +++ b/app/src/main/res/layout/fragment_ce_ping.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_login_and_regist.xml b/app/src/main/res/layout/fragment_login_and_regist.xml new file mode 100644 index 0000000..9eb78e1 --- /dev/null +++ b/app/src/main/res/layout/fragment_login_and_regist.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_mine.xml b/app/src/main/res/layout/fragment_mine.xml new file mode 100644 index 0000000..253fc19 --- /dev/null +++ b/app/src/main/res/layout/fragment_mine.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_report.xml b/app/src/main/res/layout/fragment_report.xml new file mode 100644 index 0000000..f97eea6 --- /dev/null +++ b/app/src/main/res/layout/fragment_report.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_zi_xun.xml b/app/src/main/res/layout/fragment_zi_xun.xml new file mode 100644 index 0000000..507f4d9 --- /dev/null +++ b/app/src/main/res/layout/fragment_zi_xun.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a2f5908 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..1b52399 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..ff10afd Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..115a4c7 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..dcd3cd8 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..459ca60 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..8ca12fe Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8e19b41 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b824ebd Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..4c19a13 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..b577c32 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + parent + + + Hello blank fragment + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/test/java/com/shunzhi/parent/ExampleUnitTest.java b/app/src/test/java/com/shunzhi/parent/ExampleUnitTest.java new file mode 100644 index 0000000..1fd5dc3 --- /dev/null +++ b/app/src/test/java/com/shunzhi/parent/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.shunzhi.parent; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..43d23d9 --- /dev/null +++ b/build.gradle @@ -0,0 +1,66 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.0.1' + + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + +ext { + // Sdk and tools + minSdkVersion = 16 + targetSdkVersion = 26 + compileSdkVersion = 26 + buildToolsVersion = '26.0.2' + + // App dependencies + supportLibraryVersion = '26.1.0' + guavaVersion = '18.0' + junitVersion = '4.12' + mockitoVersion = '1.10.19' + powerMockito = '1.6.2' + hamcrestVersion = '1.3' + runnerVersion = '0.4.1' + rulesVersion = '0.4.1' + espressoVersion = '2.2.1' + retrofitVersion = '2.2.0' + okhttploggingVersion = '3.4.1' + okhttpVersion = '3.4.1' + rxjavaVersion = '2.0.1' + rxandroidVersion = '2.0.1' + rxbindingVersion = '2.0.0' + rxPerssionsVersion = '0.9.4@aar' + glideVersion = '3.6.1' + glideokhttpVersion = '1.3.1' + photoviewVersion = '1.2.4' + butterknifeVersion = '8.4.0' + fragmentationVersion = '1.1.6' + loggerVersion = '1.15' + circleImageviewVersion = '2.1.0' + BaseRecyclerViewAdapterHelperVersion = '2.9.34' + SwtichButtonVersion = '1.4.6' + PhotoViewVersion = '1.4.1' + BottomSheetVersion = '1.3.0@aar' + glideTransformationVersion = '2.0.1' +} + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..39d0fb2 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Feb 27 14:00:51 CST 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mvpsdk/.gitignore b/mvpsdk/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/mvpsdk/.gitignore @@ -0,0 +1 @@ +/build diff --git a/mvpsdk/build.gradle b/mvpsdk/build.gradle new file mode 100644 index 0000000..30a25ba --- /dev/null +++ b/mvpsdk/build.gradle @@ -0,0 +1,102 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + def BOOLEAN = "boolean" + def TRUE = "true" + def FALSE = "false" + def IS_SHOW_LOG = "IS_SHOW_LOG" + + release { + minifyEnabled false + buildConfigField BOOLEAN, IS_SHOW_LOG, FALSE + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + publishNonDefault true +} +//repositories { +// flatDir { +// dirs 'libs' //this way we can find the .aar file in libs folder +// } +// google() +//} +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'com.android.support:appcompat-v7:26.1.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + + // Android support + compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion" + compile "com.android.support:cardview-v7:$rootProject.supportLibraryVersion" + compile "com.android.support:design:$rootProject.supportLibraryVersion" + compile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion" + + // Retrofit + compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion" + compile "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion" + compile "com.squareup.retrofit2:adapter-rxjava2:$rootProject.retrofitVersion" + compile "com.squareup.okhttp3:logging-interceptor:$rootProject.okhttploggingVersion" + compile "com.squareup.okhttp3:okhttp:$rootProject.okhttpVersion" + + // RxJava + compile "io.reactivex.rxjava2:rxjava:$rootProject.rxjavaVersion" + compile "io.reactivex.rxjava2:rxandroid:$rootProject.rxandroidVersion" + compile "com.jakewharton.rxbinding2:rxbinding:$rootProject.rxbindingVersion" + + // Glide + compile "com.github.bumptech.glide:glide:$rootProject.glideVersion" + compile "com.github.bumptech.glide:okhttp-integration:$rootProject.glideokhttpVersion" + compile "jp.wasabeef:glide-transformations:$rootProject.glideTransformationVersion" + + //Butterknife + compile "com.jakewharton:butterknife:$rootProject.butterknifeVersion" + annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' + + //fragmentation + compile "me.yokeyword:fragmentation:$rootProject.fragmentationVersion" + + //Logger + compile "com.orhanobut:logger:$rootProject.loggerVersion" + + //circle imageview + compile "de.hdodenhof:circleimageview:$rootProject.circleImageviewVersion" + + //BaseRecyclerViewAdapterHelper + compile "com.github.CymChad:BaseRecyclerViewAdapterHelper:$rootProject.BaseRecyclerViewAdapterHelperVersion" + + //SwitchButton + compile "com.kyleduo.switchbutton:library:$rootProject.SwtichButtonVersion" + + //PhotoView + compile "com.bm.photoview:library:$rootProject.PhotoViewVersion" + + compile "com.cocosw:bottomsheet:$rootProject.BottomSheetVersion" + + //permissions + compile "com.tbruyelle.rxpermissions2:rxpermissions:$rootProject.rxPerssionsVersion" + + //timber + compile 'com.jakewharton.timber:timber:4.5.1' + + //jiaozivideoplayer + compile 'cn.jzvd:jiaozivideoplayer:6.2.7' +// compile(name: 'jiaozivideoplayer-6.2.3', ext: 'aar') +} diff --git a/mvpsdk/proguard-rules.pro b/mvpsdk/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/mvpsdk/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mvpsdk/src/androidTest/java/com/share/mvpsdk/ExampleInstrumentedTest.java b/mvpsdk/src/androidTest/java/com/share/mvpsdk/ExampleInstrumentedTest.java new file mode 100644 index 0000000..efd1849 --- /dev/null +++ b/mvpsdk/src/androidTest/java/com/share/mvpsdk/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.share.mvpsdk; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.share.mvpsdk.test", appContext.getPackageName()); + } +} diff --git a/mvpsdk/src/main/AndroidManifest.xml b/mvpsdk/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4cf5ec6 --- /dev/null +++ b/mvpsdk/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/AppManager.java b/mvpsdk/src/main/java/com/share/mvpsdk/AppManager.java new file mode 100644 index 0000000..34073cb --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/AppManager.java @@ -0,0 +1,111 @@ +package com.share.mvpsdk; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; + +import java.util.Stack; + +/** + * Created by Horrarndoo on 2017/4/5. + *

+ * AppManager 管理Activity栈 + */ + +public class AppManager { + private static Stack activityStack; + private static AppManager instance; + + private AppManager() { + } + + /** + * 单一实例 + */ + public static AppManager getAppManager() { + if (instance == null) { + instance = new AppManager(); + } + return instance; + } + + /** + * 添加Activity到堆栈 + */ + public void addActivity(Activity activity) { + if (activityStack == null) { + activityStack = new Stack(); + } + activityStack.add(activity); + } + + /** + * 获取当前Activity(堆栈中最后一个压入的) + */ + public Activity currentActivity() { + Activity activity = activityStack.lastElement(); + return activity; + } + + /** + * 结束当前Activity(堆栈中最后一个压入的) + */ + public void finishActivity() { + Activity activity = activityStack.lastElement(); + finishActivity(activity); + } + + /** + * 结束指定的Activity + */ + public void finishActivity(Activity activity) { + if (activity != null) { + activityStack.remove(activity); + activity.finish(); + activity = null; + } + } + + /** + * 结束指定类名的Activity + */ + public void finishActivity(Class cls) { + for (Activity activity : activityStack) { + if (activity.getClass().equals(cls)) { + finishActivity(activity); + } + } + } + + /** + * 结束所有Activity + */ + public void finishAllActivity() { + for (int i = 0, size = activityStack.size(); i < size; i++) { + if (null != activityStack.get(i)) { + activityStack.get(i).finish(); + } + } + activityStack.clear(); + } + + /** + * 退出应用程序 + */ + @SuppressLint("MissingPermission") + public void AppExit(Context context) { + try { + finishAllActivity(); + ActivityManager activityMgr = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + activityMgr.killBackgroundProcesses(context.getPackageName()); + System.exit(0); + } catch (Exception e) { + } + } + + public boolean isAppExit() { + return activityStack == null || activityStack.isEmpty(); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/RxManager.java b/mvpsdk/src/main/java/com/share/mvpsdk/RxManager.java new file mode 100644 index 0000000..5c910ca --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/RxManager.java @@ -0,0 +1,23 @@ +package com.share.mvpsdk; + + +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; + +/** + * Created by Horrarndoo on 2017/9/12. + *

+ * 用于管理Rxjava 注册订阅和取消订阅 + */ + +public class RxManager { + private CompositeDisposable mCompositeDisposable = new CompositeDisposable();// 管理订阅者者 + + public void register(Disposable d) { + mCompositeDisposable.add(d); + } + + public void unSubscribe() { + mCompositeDisposable.dispose();// 取消订阅 + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/adapter/FragmentAdapter.java b/mvpsdk/src/main/java/com/share/mvpsdk/adapter/FragmentAdapter.java new file mode 100644 index 0000000..807bbc5 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/adapter/FragmentAdapter.java @@ -0,0 +1,42 @@ +package com.share.mvpsdk.adapter; + + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.view.PagerAdapter; +import android.view.ViewGroup; + +import java.util.List; + +/** + * Created by Horrarndoo on 2017/9/7. + *

+ */ +public class FragmentAdapter extends FragmentStatePagerAdapter { + private List fragments; + + public FragmentAdapter(FragmentManager fm, List fragments) { + super(fm); + this.fragments = fragments; + } + + @Override + public Fragment getItem(int position) { + return fragments.get(position); + } + + @Override + public int getCount() { + return fragments == null ? 0 : fragments.size(); + } + + public int getItemPosition(Object object) { + return PagerAdapter.POSITION_NONE; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + // super.destroyItem(container, position, object); + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimManager.java b/mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimManager.java new file mode 100644 index 0000000..6fc18a1 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimManager.java @@ -0,0 +1,75 @@ +package com.share.mvpsdk.anim; + +import android.content.Context; +import android.os.Build; +import android.support.annotation.NonNull; +import android.view.View; +import android.view.animation.AnimationUtils; + +/** + * Created by Horrarndoo on 2017/9/11. + *

+ */ + +public class AnimManager { + /** + * Alpha and scaleX 动画 + * Alpha 0->1 + * ScaleX 0.8->1 + * + * @param context context + * @param view view + * @param startDelay 动画开始前延时(ms) + * @param duration 动画持续时间(ms) + */ + public static void animAlphaAndScaleX(Context context, @NonNull View view, int startDelay, int + duration) { + view.setAlpha(0f); + view.setScaleX(0.8f); + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + view.animate() + .alpha(1f) + .scaleX(1f) + .setStartDelay(startDelay) + .setDuration(duration) + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(context)) + .start(); + }else{ + view.animate() + .alpha(1f) + .scaleX(1f) + .setStartDelay(startDelay) + .setDuration(duration) + .setInterpolator(AnimationUtils.loadInterpolator(context, android.R.interpolator.linear)) + .start(); + } + } + + /** + * Alpha and scale X Y 动画 + * Alpha 0->1 + * ScaleX 0->1 + * ScaleY 0->1 + * + * @param context context + * @param view view + * @param startDelay 动画开始前延时(ms) + * @param duration 动画持续时间(ms) + */ + public static void animAlphaAndScale(Context context, @NonNull View view, int startDelay, int + duration) { + view.setAlpha(0f); + view.setScaleX(0f); + view.setScaleY(0f); + + view.animate() + .alpha(1f) + .scaleX(1f) + .scaleY(1f) + .setStartDelay(startDelay) + .setDuration(duration) + .setInterpolator(AnimationUtils.loadInterpolator(context, + android.R.interpolator.overshoot)).start(); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimUtils.java new file mode 100644 index 0000000..e018177 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/anim/AnimUtils.java @@ -0,0 +1,330 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.share.mvpsdk.anim; + +import android.animation.Animator; +import android.animation.TimeInterpolator; +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.transition.Transition; +import android.util.ArrayMap; +import android.util.Property; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import java.util.ArrayList; + +/** + * Utility methods for working with animations. + */ +public class AnimUtils { + + private AnimUtils() { } + + private static Interpolator fastOutSlowIn; + private static Interpolator fastOutLinearIn; + private static Interpolator linearOutSlowIn; + + public static Interpolator getFastOutSlowInInterpolator(Context context) { + if (fastOutSlowIn == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + fastOutSlowIn = AnimationUtils.loadInterpolator(context, + android.R.interpolator.fast_out_slow_in); + } + } + return fastOutSlowIn; + } + + public static Interpolator getFastOutLinearInInterpolator(Context context) { + if (fastOutLinearIn == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + fastOutLinearIn = AnimationUtils.loadInterpolator(context, + android.R.interpolator.fast_out_linear_in); + } + } + return fastOutLinearIn; + } + + public static Interpolator getLinearOutSlowInInterpolator(Context context) { + if (linearOutSlowIn == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + linearOutSlowIn = AnimationUtils.loadInterpolator(context, + android.R.interpolator.linear_out_slow_in); + } + } + return linearOutSlowIn; + } + + /** + * Linear interpolate between a and b with parameter t. + */ + public static float lerp(float a, float b, float t) { + return a + (b - a) * t; + } + + + /** + * An implementation of {@link Property} to be used specifically with fields of + * type + * float. This type-specific subclass enables performance benefit by allowing + * calls to a {@link #set(Object, Float) set()} function that takes the primitive + * float type and avoids autoboxing and other overhead associated with the + * Float class. + * + * @param The class on which the Property is declared. + **/ + public static abstract class FloatProperty extends Property { + public FloatProperty(String name) { + super(Float.class, name); + } + + /** + * A type-specific override of the {@link #set(Object, Float)} that is faster when dealing + * with fields of type float. + */ + public abstract void setValue(T object, float value); + + @Override + final public void set(T object, Float value) { + setValue(object, value); + } + } + + /** + * An implementation of {@link Property} to be used specifically with fields of + * type + * int. This type-specific subclass enables performance benefit by allowing + * calls to a {@link #set(Object, Integer) set()} function that takes the primitive + * int type and avoids autoboxing and other overhead associated with the + * Integer class. + * + * @param The class on which the Property is declared. + */ + public static abstract class IntProperty extends Property { + + public IntProperty(String name) { + super(Integer.class, name); + } + + /** + * A type-specific override of the {@link #set(Object, Integer)} that is faster when dealing + * with fields of type int. + */ + public abstract void setValue(T object, int value); + + @Override + final public void set(T object, Integer value) { + setValue(object, value.intValue()); + } + + } + + /** + * https://halfthought.wordpress.com/2014/11/07/reveal-transition/ + *

+ * Interrupting Activity transitions can yield an OperationNotSupportedException when the + * transition tries to pause the animator. Yikes! We can fix this by wrapping the Animator: + */ + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + public static class NoPauseAnimator extends Animator { + private final Animator mAnimator; + private final ArrayMap mListeners = + new ArrayMap(); + + public NoPauseAnimator(Animator animator) { + mAnimator = animator; + } + + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + @Override + public void addListener(AnimatorListener listener) { + AnimatorListener wrapper = new AnimatorListenerWrapper(this, listener); + if (!mListeners.containsKey(listener)) { + mListeners.put(listener, wrapper); + mAnimator.addListener(wrapper); + } + } + + @Override + public void cancel() { + mAnimator.cancel(); + } + + @Override + public void end() { + mAnimator.end(); + } + + @Override + public long getDuration() { + return mAnimator.getDuration(); + } + + @Override + public TimeInterpolator getInterpolator() { + return mAnimator.getInterpolator(); + } + + @Override + public void setInterpolator(TimeInterpolator timeInterpolator) { + mAnimator.setInterpolator(timeInterpolator); + } + + @Override + public ArrayList getListeners() { + return new ArrayList(mListeners.keySet()); + } + + @Override + public long getStartDelay() { + return mAnimator.getStartDelay(); + } + + @Override + public void setStartDelay(long delayMS) { + mAnimator.setStartDelay(delayMS); + } + + @Override + public boolean isPaused() { + return mAnimator.isPaused(); + } + + @Override + public boolean isRunning() { + return mAnimator.isRunning(); + } + + @Override + public boolean isStarted() { + return mAnimator.isStarted(); + } + + /* We don't want to override pause or resume methods because we don't want them + * to affect mAnimator. + public void pause(); + + public void resume(); + + public void addPauseListener(AnimatorPauseListener listener); + + public void removePauseListener(AnimatorPauseListener listener); + */ + + @Override + public void removeAllListeners() { + mListeners.clear(); + mAnimator.removeAllListeners(); + } + + @Override + public void removeListener(AnimatorListener listener) { + AnimatorListener wrapper = mListeners.get(listener); + if (wrapper != null) { + mListeners.remove(listener); + mAnimator.removeListener(wrapper); + } + } + + @Override + public Animator setDuration(long durationMS) { + mAnimator.setDuration(durationMS); + return this; + } + + @Override + public void setTarget(Object target) { + mAnimator.setTarget(target); + } + + @Override + public void setupEndValues() { + mAnimator.setupEndValues(); + } + + @Override + public void setupStartValues() { + mAnimator.setupStartValues(); + } + + @Override + public void start() { + mAnimator.start(); + } + } + + static class AnimatorListenerWrapper implements Animator.AnimatorListener { + private final Animator mAnimator; + private final Animator.AnimatorListener mListener; + + public AnimatorListenerWrapper(Animator animator, Animator.AnimatorListener listener) { + mAnimator = animator; + mListener = listener; + } + + @Override + public void onAnimationStart(Animator animator) { + mListener.onAnimationStart(mAnimator); + } + + @Override + public void onAnimationEnd(Animator animator) { + mListener.onAnimationEnd(mAnimator); + } + + @Override + public void onAnimationCancel(Animator animator) { + mListener.onAnimationCancel(mAnimator); + } + + @Override + public void onAnimationRepeat(Animator animator) { + mListener.onAnimationRepeat(mAnimator); + } + } + + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + public static class TransitionListenerAdapter implements Transition.TransitionListener { + + @Override + public void onTransitionStart(Transition transition) { + + } + + @Override + public void onTransitionEnd(Transition transition) { + + } + + @Override + public void onTransitionCancel(Transition transition) { + + } + + @Override + public void onTransitionPause(Transition transition) { + + } + + @Override + public void onTransitionResume(Transition transition) { + + } + } + +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/anim/ToolbarAnimManager.java b/mvpsdk/src/main/java/com/share/mvpsdk/anim/ToolbarAnimManager.java new file mode 100644 index 0000000..7db1c9c --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/anim/ToolbarAnimManager.java @@ -0,0 +1,93 @@ +package com.share.mvpsdk.anim; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v7.widget.ActionMenuView; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.ImageButton; +import android.widget.TextView; + +/** + * Created by Horrarndoo on 2017/9/11. + *

+ * Toolbar动画Manager + */ + +public class ToolbarAnimManager { + /** + * Toolbar 进场动画 + *

+ * Textview&ActionMenuView渐变动画 + * + * @param context context + * @param toolbar toolbar + */ + public static void animIn(Context context, @NonNull Toolbar toolbar) { + ImageButton ibIcon = null; + TextView tvTitle = null; + ActionMenuView amvTheme = null; + int childCount = toolbar.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = toolbar.getChildAt(i); + if(child instanceof ImageButton) { + ibIcon = (ImageButton) child; + continue; + } + + if (child instanceof ActionMenuView) { + amvTheme = (ActionMenuView) child; + continue; + } + + if (child instanceof TextView) + tvTitle = (TextView) child; + } + + if(ibIcon != null) + animNavigationIcon(context, ibIcon); + + if(tvTitle != null) + animTitle(context, tvTitle); + + if(amvTheme != null) + animMenu(context, amvTheme); + } + + /** + * Toolbar Title动画 + *

+ * NavigationIcon渐变动画 + * + * @param context context + * @param imageButton 执行动画的view + */ + public static void animNavigationIcon(Context context, @NonNull ImageButton imageButton) { + AnimManager.animAlphaAndScaleX(context, imageButton, 500, 900); + } + + /** + * Toolbar Title动画 + *

+ * ActionMenuView渐变动画 + * + * @param context context + * @param textView 执行动画的view + */ + public static void animTitle(Context context, @NonNull TextView textView) { + AnimManager.animAlphaAndScaleX(context, textView, 500, 900); + } + + /** + * Toolbar ActionMenuView动画 + *

+ * ActionMenuView渐变动画 + * + * @param context context + * @param avm 执行动画的view + */ + public static void animMenu(Context context, @NonNull ActionMenuView avm) { + AnimManager.animAlphaAndScale(context, avm, 500, 200); // filter + AnimManager.animAlphaAndScale(context, avm, 700, 200); // overflow + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/BaseModel.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/BaseModel.java new file mode 100644 index 0000000..4954d36 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/BaseModel.java @@ -0,0 +1,11 @@ +package com.share.mvpsdk.base; + +/** + * Created by Horrarndoo on 2017/4/25. + * base model类 + */ + +public abstract class BaseModel { + public BaseModel() { + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/BasePresenter.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/BasePresenter.java new file mode 100644 index 0000000..e0f5940 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/BasePresenter.java @@ -0,0 +1,53 @@ +package com.share.mvpsdk.base; + +import android.support.annotation.NonNull; + +import com.share.mvpsdk.RxManager; + + +/** + * Created by Horrarndoo on 2017/4/25. + *

+ * base presenter + */ + +public abstract class BasePresenter { + public M mIModel; + public V mIView; + protected RxManager mRxManager = new RxManager(); + + /** + * 返回presenter想持有的Model引用 + * + * @return presenter持有的Model引用 + */ + public abstract M getModel(); + + /** + * 绑定IModel和IView的引用 + * + * @param m model + * @param v view + */ + public void attachMV(@NonNull M m, @NonNull V v) { + this.mIModel = m; + this.mIView = v; + this.onStart(); + } + + /** + * 解绑IModel和IView + */ + public void detachMV() { + mRxManager.unSubscribe(); + mIView = null; + mIModel = null; + } + + /** + * IView和IModel绑定完成立即执行 + *

+ * 实现类实现绑定完成后的逻辑,例如数据初始化等,界面初始化, 更新等 + */ + public abstract void onStart(); +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseActivity.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseActivity.java new file mode 100644 index 0000000..7d9191f --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseActivity.java @@ -0,0 +1,35 @@ +package com.share.mvpsdk.base; + +import android.os.Bundle; +import android.support.annotation.NonNull; + +/** + * Created by Horrarndoo on 2017/9/6. + *

+ * BaseActivity接口 + */ + +public interface IBaseActivity extends IBaseView { + /** + * 跳往新的Activity + * + * @param clz 要跳往的Activity + */ + void startNewActivity(@NonNull Class clz); + + /** + * 跳往新的Activity + * + * @param clz 要跳往的Activity + * @param bundle 携带的bundle数据 + */ + void startNewActivity(@NonNull Class clz, Bundle bundle); + + /** + * 跳往新的Activity + * @param clz 要跳转的Activity + * @param bundle bundel数据 + * @param requestCode requestCode + */ + void startNewActivityForResult(@NonNull Class clz, Bundle bundle, int requestCode); +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseFragment.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseFragment.java new file mode 100644 index 0000000..676ca67 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseFragment.java @@ -0,0 +1,92 @@ +package com.share.mvpsdk.base; + +import android.app.Activity; +import android.os.Bundle; +import android.support.annotation.NonNull; + +import me.yokeyword.fragmentation.SupportFragment; + +/** + * Created by Horrarndoo on 2017/9/6. + *

+ * BaseFragment接口 + */ + +public interface IBaseFragment extends IBaseView { + /** + * 出栈到目标fragment + * + * @param targetFragmentClass 目标fragment + * @param includeTargetFragment 是否包含该fragment + * true 目标fragment也出栈 + *

+ * false 出栈到目标fragment,目标fragment不出栈 + */ + void popToFragment(Class targetFragmentClass, boolean includeTargetFragment); + + /** + * 跳往新的Fragment + * + * @param supportFragment 要跳往的Fragment(继承自supportFragment) + */ + void startNewFragment(@NonNull SupportFragment supportFragment); + + /** + * 跳往新的Fragment,并出栈当前fragment + * + * @param supportFragment 要跳往的Fragment(继承自supportFragment) + */ + void startNewFragmentWithPop(@NonNull SupportFragment supportFragment); + + /** + * 跳往新的Fragment + * + * @param supportFragment 要跳往的Fragment(继承自supportFragment) + * @param requestCode requestCode + */ + void startNewFragmentForResult(@NonNull SupportFragment supportFragment, int requestCode); + + /** + * 设置Fragment返回Result + * + * @param resultCode resultCode + * @param data result data + */ + void setOnFragmentResult(int resultCode, Bundle data); + + /** + * 跳往新的Activity + * + * @param clz 要跳往的Activity + */ + void startNewActivity(@NonNull Class clz); + + /** + * 跳往新的Activity + * + * @param clz 要跳往的Activity + * @param bundle 携带的bundle数据 + */ + void startNewActivity(@NonNull Class clz, Bundle bundle); + + /** + * 跳往新的Activity + * + * @param clz 要跳转的Activity + * @param bundle bundel数据 + * @param requestCode requestCode + */ + void startNewActivityForResult(@NonNull Class clz, Bundle bundle, int requestCode); + + /** + * 返回当前fragment是否可见 + * @return 当前fragment是否可见 + */ + boolean isVisiable(); + + /** + * 返回当前fragment绑定的activity + * @return activity + */ + Activity getBindActivity(); +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseModel.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseModel.java new file mode 100644 index 0000000..cd33c23 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseModel.java @@ -0,0 +1,9 @@ +package com.share.mvpsdk.base; + +/** + * Created by Horrarndoo on 2017/4/25. + * base model接口 + */ + +public interface IBaseModel { +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseView.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseView.java new file mode 100644 index 0000000..fb56316 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/IBaseView.java @@ -0,0 +1,47 @@ +package com.share.mvpsdk.base; + +import android.support.annotation.NonNull; + +/** + * Created by Horrarndoo on 2017/5/2. + * fragment base view接口 + */ + +public interface IBaseView { + /** + * 初始化presenter + *

+ * 此方法返回的presenter对象不可为空 + */ + @NonNull + BasePresenter initPresenter(); + + /** + * 显示toast消息 + * + * @param msg 要显示的toast消息字符串 + */ + void showToast(String msg); + + /** + * 显示等待dialog + * + * @param waitMsg 等待消息字符串 + */ + void showWaitDialog(String waitMsg); + + /** + * 隐藏等待dialog + */ + void hideWaitDialog(); + + /** + * 隐藏键盘 + */ + void hideKeybord(); + + /** + * 回退 + */ + void back(); +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseCompatActivity.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseCompatActivity.java new file mode 100644 index 0000000..8485743 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseCompatActivity.java @@ -0,0 +1,249 @@ +package com.share.mvpsdk.base.activity; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.support.v7.app.AppCompatDelegate; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.inputmethod.InputMethodManager; + + +import com.share.mvpsdk.AppManager; +import com.share.mvpsdk.R; +import com.share.mvpsdk.global.GlobalApplication; +import com.share.mvpsdk.utils.AppUtils; +import com.share.mvpsdk.utils.SpUtils; +import com.share.mvpsdk.utils.StatusBarUtils; +import com.share.mvpsdk.utils.ThemeUtils; +import com.share.mvpsdk.widgets.WaitPorgressDialog; + +import butterknife.ButterKnife; +import me.yokeyword.fragmentation.SupportActivity; +import me.yokeyword.fragmentation.anim.DefaultVerticalAnimator; +import me.yokeyword.fragmentation.anim.FragmentAnimator; + +/** + * Created by Horrarndoo on 2017/9/7. + *

+ * BaseActivity + */ + +public abstract class BaseCompatActivity extends SupportActivity { + protected GlobalApplication mApplication; + protected WaitPorgressDialog mWaitPorgressDialog; + protected Context mContext;//全局上下文对象 + protected boolean isTransAnim; + + static { + //5.0以下兼容vector + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + init(savedInstanceState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + AppManager.getAppManager().finishActivity(this); + } + + @Override + public FragmentAnimator onCreateFragmentAnimator() { + //fragment切换使用默认Vertical动画 + return new DefaultVerticalAnimator(); + } + + private void init(Bundle savedInstanceState) { + setTheme(ThemeUtils.themeArr[SpUtils.getThemeIndex(this)][ + SpUtils.getNightModel(this) ? 1 : 0]); + setContentView(getLayoutId()); + ButterKnife.bind(this); + StatusBarUtils.setTransparent(this); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + initData(); + initView(savedInstanceState); + AppManager.getAppManager().addActivity(this); + } + + public void reload() { + Intent intent = getIntent(); + overridePendingTransition(0, 0); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + finish(); + overridePendingTransition(0, 0); + startActivity(intent); + } + + /** + * 初始化数据 + *

+ * 子类可以复写此方法初始化子类数据 + */ + protected void initData() { + mContext = AppUtils.getContext(); + mApplication = GlobalApplication.getInstance(); + mWaitPorgressDialog = new WaitPorgressDialog(this); + isTransAnim = true; + } + + /** + * 初始化view + *

+ * 子类实现 控件绑定、视图初始化等内容 + * + * @param savedInstanceState savedInstanceState + */ + protected abstract void initView(Bundle savedInstanceState); + + /** + * 获取当前layouty的布局ID,用于设置当前布局 + *

+ * 交由子类实现 + * + * @return layout Id + */ + protected abstract int getLayoutId(); + + /** + * 显示提示框 + * + * @param msg 提示框内容字符串 + */ + protected void showProgressDialog(String msg) { + mWaitPorgressDialog.setMessage(msg); + mWaitPorgressDialog.show(); + } + + /** + * 隐藏提示框 + */ + protected void hideProgressDialog() { + if (mWaitPorgressDialog != null) { + mWaitPorgressDialog.dismiss(); + } + } + + /** + * [页面跳转] + * + * @param clz 要跳转的Activity + */ + public void startActivity(Class clz) { + startActivity(new Intent(this, clz)); + if (isTransAnim) + overridePendingTransition(R.anim.activity_start_zoom_in, R.anim + .activity_start_zoom_out); + } + + /** + * [页面跳转] + * + * @param clz 要跳转的Activity + * @param intent intent + */ + public void startActivity(Class clz, Intent intent) { + intent.setClass(this, clz); + startActivity(intent); + if (isTransAnim) + overridePendingTransition(R.anim.activity_start_zoom_in, R.anim + .activity_start_zoom_out); + } + + /** + * [携带数据的页面跳转] + * + * @param clz 要跳转的Activity + * @param bundle bundel数据 + */ + public void startActivity(Class clz, Bundle bundle) { + Intent intent = new Intent(); + intent.setClass(this, clz); + if (bundle != null) { + intent.putExtras(bundle); + } + startActivity(intent); + if (isTransAnim) + overridePendingTransition(R.anim.activity_start_zoom_in, R.anim + .activity_start_zoom_out); + } + + /** + * [含有Bundle通过Class打开编辑界面] + * + * @param clz 要跳转的Activity + * @param bundle bundel数据 + * @param requestCode requestCode + */ + public void startActivityForResult(Class clz, Bundle bundle, + int requestCode) { + Intent intent = new Intent(); + intent.setClass(this, clz); + if (bundle != null) { + intent.putExtras(bundle); + } + startActivityForResult(intent, requestCode); + if (isTransAnim) + overridePendingTransition(R.anim.activity_start_zoom_in, R.anim + .activity_start_zoom_out); + } + + @Override + public void finish() { + super.finish(); + if (isTransAnim) + overridePendingTransition(R.anim.activity_finish_trans_in, R.anim + .activity_finish_trans_out); + } + + /** + * 隐藏键盘 + * + * @return 隐藏键盘结果 + *

+ * true:隐藏成功 + *

+ * false:隐藏失败 + */ + protected boolean hiddenKeyboard() { + //点击空白位置 隐藏软键盘 + InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService + (INPUT_METHOD_SERVICE); + return mInputMethodManager.hideSoftInputFromWindow(this + .getCurrentFocus().getWindowToken(), 0); + } + + protected void initTitleBar(Toolbar toolbar, String title) { + toolbar.setTitle(title); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + toolbar.setNavigationIcon(R.mipmap.ic_arrow_back_white); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + onBackPressedSupport(); + } + }); + } + + /** + * 是否使用overridePendingTransition过度动画 + * @return 是否使用overridePendingTransition过度动画,默认使用 + */ + protected boolean isTransAnim() { + return isTransAnim; + } + + /** + * 设置是否使用overridePendingTransition过度动画 + */ + protected void setIsTransAnim(boolean b){ + isTransAnim = b; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseMVPCompatActivity.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseMVPCompatActivity.java new file mode 100644 index 0000000..63edbf1 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/activity/BaseMVPCompatActivity.java @@ -0,0 +1,94 @@ +package com.share.mvpsdk.base.activity; + +import android.os.Bundle; +import android.support.annotation.NonNull; + +import com.share.mvpsdk.base.BasePresenter; +import com.share.mvpsdk.base.IBaseActivity; +import com.share.mvpsdk.base.IBaseModel; +import com.share.mvpsdk.utils.ToastUtils; + + +/** + * Created by Horrarndoo on 2017/4/6. + *

+ * Mvp Activity基类 + */ +public abstract class BaseMVPCompatActivity

extends + BaseCompatActivity implements IBaseActivity { + /** + * presenter 具体的presenter由子类确定 + */ + protected P mPresenter; + + /** + * model 具体的model由子类确定 + */ + private M mIMode; + + /** + * 初始化数据 + *

+ * 子类可以复写此方法初始化子类数据 + */ + protected void initData() { + super.initData(); + mPresenter = (P) initPresenter(); + if (mPresenter != null) { + mIMode = (M) mPresenter.getModel(); + if (mIMode != null) { + mPresenter.attachMV(mIMode, this); + } + //Logger.d("attach M V success."); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mPresenter != null) { + mPresenter.detachMV(); + //Logger.d("detach M V success."); + } + } + + @Override + public void showWaitDialog(String msg) { + showProgressDialog(msg); + } + + @Override + public void hideWaitDialog() { + hideProgressDialog(); + } + + @Override + public void showToast(String msg) { + ToastUtils.showToast(msg); + } + + @Override + public void startNewActivity(@NonNull Class clz) { + startActivity(clz); + } + + @Override + public void startNewActivity(@NonNull Class clz, Bundle bundle) { + startActivity(clz, bundle); + } + + @Override + public void startNewActivityForResult(@NonNull Class clz, Bundle bundle, int requestCode) { + startActivityForResult(clz, bundle, requestCode); + } + + @Override + public void hideKeybord() { + hiddenKeyboard(); + } + + @Override + public void back() { + super.onBackPressedSupport(); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewAdapter.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewAdapter.java new file mode 100644 index 0000000..1ec06da --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewAdapter.java @@ -0,0 +1,79 @@ +package com.share.mvpsdk.base.adapter; + +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by ToaHanDong on 2017/3/23. + */ + +public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter { + + private List data = new ArrayList<>(); + public OnItemClickListener onItemClickListener; + public OnItemLongClickListener onItemLongClickListener; + + @Override + public BaseRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return null; + } + + @Override + public void onBindViewHolder(BaseRecyclerViewHolder holder, int position) { + holder.onBindViewHolder(data.get(getPosition(position)), getPosition(position)); + } + + + @Override + public int getItemCount() { + return data.size(); + } + + public void addAll(List data) { + removeAll(); + if (data!=null) + this.data.addAll(data); + notifyDataSetChanged(); + } + + public void removeAll() { + data.clear(); + } + + public void remove(int position) { + data.remove(position); + } + + public void notift() { + notifyDataSetChanged(); + } + + public void remove(T object) { + data.remove(object); + } + + public List getData() { + return data; + } + + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + this.onItemClickListener = onItemClickListener; + } + + public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) { + this.onItemLongClickListener = onItemLongClickListener; + } + + private int getPosition(int position) { + return position; +// if (data.size() <= 9) { +// return position; +// } +// return position % (data.size()); + } + +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewHolder.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewHolder.java new file mode 100644 index 0000000..39bde5a --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/BaseRecyclerViewHolder.java @@ -0,0 +1,22 @@ +package com.share.mvpsdk.base.adapter; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by ToaHanDong on 2017/3/23. + */ + +public abstract class BaseRecyclerViewHolder extends RecyclerView.ViewHolder { + + public BaseRecyclerViewHolder(View itemView) { + super(itemView); + } + + public abstract void onBindViewHolder(T object, final int position); + + void OnBaseBindViewHolder(T Object,int position){ + onBindViewHolder(Object,position); + } + +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemClickListener.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemClickListener.java new file mode 100644 index 0000000..874e278 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemClickListener.java @@ -0,0 +1,10 @@ +package com.share.mvpsdk.base.adapter; + +/** + * Created by ToaHanDong on 2017/3/23. + */ + +public interface OnItemClickListener { + + void onItemClickListener(T object, int position); +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemLongClickListener.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemLongClickListener.java new file mode 100644 index 0000000..c05bf3c --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/adapter/OnItemLongClickListener.java @@ -0,0 +1,10 @@ +package com.share.mvpsdk.base.adapter; + +/** + * Created by ToaHanDong on 2017/3/23. + */ + +public interface OnItemLongClickListener { + + void onItemLongClickListener(T object, int position); +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseEntity.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseEntity.java new file mode 100644 index 0000000..48f3c17 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseEntity.java @@ -0,0 +1,47 @@ +package com.share.mvpsdk.base.entity; + +import java.io.Serializable; + +/** + * Created by ToaHanDong on 2017/4/27. + */ + +public class BaseEntity implements Serializable { + + private int status; + private String message; + private T data; + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + @Override + public String toString() { + return "BaseEntity{" + + "status=" + status + + ", message='" + message + '\'' + + ", data=" + data + + '}'; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseListEntity.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseListEntity.java new file mode 100644 index 0000000..5bfdfef --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/entity/BaseListEntity.java @@ -0,0 +1,48 @@ +package com.share.mvpsdk.base.entity; + +import java.io.Serializable; +import java.util.List; + +/** + * Created by ToaHanDong on 2017/6/6. + */ + +public class BaseListEntity implements Serializable { + + private int status; + private String message; + private List data; + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + @Override + public String toString() { + return "BaseListEntity{" + + "status=" + status + + ", message='" + message + '\'' + + ", data=" + data + + '}'; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseCompatFragment.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseCompatFragment.java new file mode 100644 index 0000000..128a89e --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseCompatFragment.java @@ -0,0 +1,153 @@ +package com.share.mvpsdk.base.fragment; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + + +import com.share.mvpsdk.global.GlobalApplication; +import com.share.mvpsdk.utils.AppUtils; +import com.share.mvpsdk.widgets.WaitPorgressDialog; + +import butterknife.ButterKnife; +import butterknife.Unbinder; +import me.yokeyword.fragmentation.SupportFragment; +import timber.log.Timber; + +/** + * Created by Horrarndoo on 2017/9/26. + *

+ */ + +public abstract class BaseCompatFragment extends SupportFragment { + + protected String TAG; + protected Context mContext; + protected Activity mActivity; + protected GlobalApplication mApplication; + protected WaitPorgressDialog mWaitPorgressDialog; +// private Unbinder binder; + + @Override + public void onAttach(Context context) { + mActivity = (Activity) context; + mContext = context; + super.onAttach(context); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable + Bundle savedInstanceState) { + if (getLayoutView() != null) { + return getLayoutView(); + } else { + // return inflater.inflate(getLayoutId(), null); + return inflater.inflate(getLayoutId(), container, false); + } + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + ButterKnife.bind(this, view); + super.onViewCreated(view, savedInstanceState); + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + TAG = getClass().getSimpleName(); + getBundle(getArguments()); + initData(); + initUI(view, savedInstanceState); + } + + + @Override + public void onDestroyView() { + super.onDestroyView(); +// if (binder != null) +// binder.unbind(); + } + + @Override + public void onDetach() { + super.onDetach(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @LayoutRes + public abstract int getLayoutId(); + + public View getLayoutView() { + return null; + } + + /** + * 得到Activity传进来的值 + */ + public void getBundle(Bundle bundle) { + } + + /** + * 初始化UI + */ + public abstract void initUI(View view, @Nullable Bundle savedInstanceState); + + /** + * 在监听器之前把数据准备好 + */ + public void initData() { + mWaitPorgressDialog = new WaitPorgressDialog(mActivity); + mContext = AppUtils.getContext(); + mApplication = GlobalApplication.getInstance(); + } + + /** + * 处理回退事件 + * + * @return true 事件已消费 + *

+ * false 事件向上传递 + */ + @Override + public boolean onBackPressedSupport() { + if (getFragmentManager().getBackStackEntryCount() > 1) { + //如果当前存在fragment>1,当前fragment出栈 + pop(); + } else { + //已经退栈到root fragment,交由Activity处理回退事件 + return false; + } + return true; + } + + /** + * 显示提示框 + * + * @param msg 提示框内容字符串 + */ + protected void showProgressDialog(String msg) { + if (mWaitPorgressDialog.isShowing()) { + mWaitPorgressDialog.dismiss(); + } + + mWaitPorgressDialog.setMessage(msg); + mWaitPorgressDialog.show(); + } + + /** + * 隐藏提示框 + */ + protected void hideProgressDialog() { + if (mWaitPorgressDialog != null) { + mWaitPorgressDialog.dismiss(); + } + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseMVPCompatFragment.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseMVPCompatFragment.java new file mode 100644 index 0000000..d70cbcd --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseMVPCompatFragment.java @@ -0,0 +1,128 @@ +package com.share.mvpsdk.base.fragment; + +import android.app.Activity; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.widget.Toast; + + +import com.share.mvpsdk.base.BasePresenter; +import com.share.mvpsdk.base.IBaseFragment; +import com.share.mvpsdk.base.IBaseModel; +import com.share.mvpsdk.base.activity.BaseCompatActivity; +import com.share.mvpsdk.utils.ToastUtils; + +import me.yokeyword.fragmentation.SupportFragment; + +/** + * Created by Horrarndoo on 2017/9/6. + *

+ * Mvp Fragment基类 + *

+ * 实现IBaseView方法、绑定butterknife + */ + +public abstract class BaseMVPCompatFragment

extends + BaseCompatFragment implements IBaseFragment { + public P mPresenter; + public M mIMode; + + /** + * 在监听器之前把数据准备好 + */ + public void initData() { + super.initData(); + + mPresenter = (P) initPresenter(); + if (mPresenter != null) { + mIMode = (M) mPresenter.getModel(); + if (mIMode != null) { + mPresenter.attachMV(mIMode, this); + } + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mPresenter != null) { + mPresenter.detachMV(); + } + } + + @Override + public void showWaitDialog(String msg) { + showProgressDialog(msg); + } + + @Override + public void hideWaitDialog() { + hideProgressDialog(); + } + + @Override + public void showToast(String msg) { + ToastUtils.showToast(mContext, msg, Toast.LENGTH_SHORT); + } + + @Override + public void back() { + this.onBackPressedSupport(); + } + + @Override + public void startNewFragment(@NonNull SupportFragment supportFragment) { + start(supportFragment); + } + + @Override + public void startNewFragmentWithPop(@NonNull SupportFragment supportFragment) { + startWithPop(supportFragment); + } + + @Override + public void startNewFragmentForResult(@NonNull SupportFragment supportFragment, int + requestCode) { + startForResult(supportFragment, requestCode); + } + + @Override + public void popToFragment(Class targetFragmentClass, boolean includeTargetFragment) { + popTo(targetFragmentClass, includeTargetFragment); + } + + @Override + public void hideKeybord() { + hideSoftInput(); + } + + @Override + public void setOnFragmentResult(int ResultCode, Bundle data) { + setFragmentResult(ResultCode, data); + } + + @Override + public void startNewActivity(@NonNull Class clz) { + ((BaseCompatActivity) mActivity).startActivity(clz); + } + + @Override + public void startNewActivity(@NonNull Class clz, Bundle bundle) { + ((BaseCompatActivity) mActivity).startActivity(clz, bundle); + } + + @Override + public void startNewActivityForResult(@NonNull Class clz, Bundle bundle, int requestCode) { + ((BaseCompatActivity) mActivity).startActivityForResult(clz, bundle, requestCode); + } + + @Override + public boolean isVisiable() { + return isSupportVisible(); + } + + @Override + public Activity getBindActivity() { + return mActivity; + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseRecycleFragment.java b/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseRecycleFragment.java new file mode 100644 index 0000000..188c20f --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/base/fragment/BaseRecycleFragment.java @@ -0,0 +1,68 @@ +package com.share.mvpsdk.base.fragment; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.share.mvpsdk.R; +import com.share.mvpsdk.base.BasePresenter; +import com.share.mvpsdk.base.IBaseModel; + + +/** + * Created by Horrarndoo on 2017/10/17. + *

+ * 带RecycleView加载状态view的fragment,主要用于显示加载中、空界面、加载失败等状态界面显示 + */ + +public abstract class BaseRecycleFragment

extends + BaseMVPCompatFragment { + /** + * 网络异常View + */ + protected View errorView; + /** + * loadingView + */ + protected View loadingView; + /** + * 没有内容view + */ + protected View emptyView; + + @Override + public void onLazyInitView(@Nullable Bundle savedInstanceState) { + super.onLazyInitView(savedInstanceState); + showLoading(); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable + Bundle savedInstanceState) { + errorView = inflater.inflate(R.layout.view_network_error, container, false); + loadingView = inflater.inflate(R.layout.view_loading, container, false); + emptyView = inflater.inflate(R.layout.view_empty, container, false); + errorView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showLoading(); + onErrorViewClick(v); + } + }); + return super.onCreateView(inflater, container, savedInstanceState); + } + + /** + * 网络异常view被点击时触发,由子类实现 + * + * @param view view + */ + protected abstract void onErrorViewClick(View view); + + /** + * 显示加载中view,由子类实现 + */ + protected abstract void showLoading(); +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/config/DBConfig.java b/mvpsdk/src/main/java/com/share/mvpsdk/config/DBConfig.java new file mode 100644 index 0000000..f480cb7 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/config/DBConfig.java @@ -0,0 +1,16 @@ +package com.share.mvpsdk.config; + +/** + * Created by Horrarndoo on 2017/9/13. + *

+ * 数据库全局常量 + */ + +public class DBConfig { + public static final String DB_NAME = "db_name_yizhi"; + public static final String TABLE_ZHIHU = "table_zhihu"; + public static final String TABLE_WANGYI = "table_top_news"; + public static final String TABLE_WEIXIN = "table_weixin"; + public static final String TABLE_GANKIO_DAY = "table_gank_io_day"; + public static final String TABLE_GANKIO_CUSTOM = "table_gank_io_custom"; +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/config/ItemState.java b/mvpsdk/src/main/java/com/share/mvpsdk/config/ItemState.java new file mode 100644 index 0000000..d03b788 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/config/ItemState.java @@ -0,0 +1,19 @@ +package com.share.mvpsdk.config; + +/** + * Created by Horrarndoo on 2017/9/13. + *

+ * item状态全局常量 + */ + +public class ItemState { + /** + * 已读状态 + */ + public static final int STATE_IS_READ = 1; + + /** + * 非已读状态 + */ + public static final int STATE_IS_NO_READ = 0; +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/global/GlobalApplication.java b/mvpsdk/src/main/java/com/share/mvpsdk/global/GlobalApplication.java new file mode 100644 index 0000000..5e4b301 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/global/GlobalApplication.java @@ -0,0 +1,67 @@ +package com.share.mvpsdk.global; + +import android.app.Application; +import android.content.Context; +import android.os.Handler; + +import com.orhanobut.logger.LogLevel; +import com.orhanobut.logger.Logger; + +import timber.log.Timber; + + +/** + * Created by Horrarndoo on 2017/9/1. + *

+ * 全局Application + */ + +public class GlobalApplication extends Application { + private static final String LOG_TAG = "YZ_LOGGER"; + protected static Context context; + protected static Handler handler; + protected static int mainThreadId; + private static GlobalApplication mApp; + + public static synchronized GlobalApplication getInstance() { + return mApp; + } + + @Override + public void onCreate() { + super.onCreate(); + context = getApplicationContext(); + handler = new Handler(); + mainThreadId = android.os.Process.myTid(); + mApp=this; + //LogLevel.FULL : LogLevel.NONE + Logger.init(LOG_TAG).logLevel(LogLevel.FULL); + } + + /** + * 获取上下文对象 + * + * @return context + */ + public static Context getContext() { + return context; + } + + /** + * 获取全局handler + * + * @return 全局handler + */ + public static Handler getHandler() { + return handler; + } + + /** + * 获取主线程id + * + * @return 主线程id + */ + public static int getMainThreadId() { + return mainThreadId; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/BottomNavigationViewHelper.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/BottomNavigationViewHelper.java new file mode 100644 index 0000000..75c1839 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/BottomNavigationViewHelper.java @@ -0,0 +1,37 @@ +package com.share.mvpsdk.helper; + +import android.support.design.internal.BottomNavigationItemView; +import android.support.design.internal.BottomNavigationMenuView; +import android.support.design.widget.BottomNavigationView; +import android.util.Log; + +import java.lang.reflect.Field; + +/** + * Created by Horrarndoo on 2017/9/22. + *

+ * BottomNavigationView禁止3个item以上动画切换效果 + */ +public class BottomNavigationViewHelper { + public static void disableShiftMode(BottomNavigationView view) { + BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0); + try { + Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode"); + shiftingMode.setAccessible(true); + shiftingMode.setBoolean(menuView, false); + shiftingMode.setAccessible(false); + for (int i = 0; i < menuView.getChildCount(); i++) { + BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i); + //noinspection RestrictedApi + item.setShiftingMode(false); + // set once again checked value, so view will be updated + //noinspection RestrictedApi + item.setChecked(item.getItemData().isChecked()); + } + } catch (NoSuchFieldException e) { + Log.e("BNVHelper", "Unable to get shift mode field", e); + } catch (IllegalAccessException e) { + Log.e("BNVHelper", "Unable to change value of shift mode", e); + } + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/RetrofitCreateHelper.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/RetrofitCreateHelper.java new file mode 100644 index 0000000..178d58e --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/RetrofitCreateHelper.java @@ -0,0 +1,56 @@ +package com.share.mvpsdk.helper; + + + +import com.share.mvpsdk.helper.okhttp.CacheInterceptor; +import com.share.mvpsdk.helper.okhttp.HttpCache; +import com.share.mvpsdk.helper.okhttp.TrustManager; + +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + +/** + * Created by Horrarndoo on 2017/9/7. + *

+ */ + +public class RetrofitCreateHelper { + private static final int TIMEOUT_READ = 20; + private static final int TIMEOUT_CONNECTION = 10; + private static final HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor() + .setLevel(HttpLoggingInterceptor.Level.BODY); + private static CacheInterceptor cacheInterceptor = new CacheInterceptor(); + private static OkHttpClient okHttpClient = new OkHttpClient.Builder() + //SSL证书 + .sslSocketFactory(TrustManager.getUnsafeOkHttpClient()) + .hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) + //打印日志 + .addInterceptor(interceptor) + //设置Cache拦截器 + .addNetworkInterceptor(cacheInterceptor) + .addInterceptor(cacheInterceptor) + .cache(HttpCache.getCache()) + //time out + .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS) + .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS) + .writeTimeout(TIMEOUT_READ, TimeUnit.SECONDS) + //失败重连 + .retryOnConnectionFailure(true) + .build(); + + public static T createApi(Class clazz, String url) { + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(url) + .client(okHttpClient) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + return retrofit.create(clazz); + } +} + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/RxHelper.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/RxHelper.java new file mode 100644 index 0000000..1483ab4 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/RxHelper.java @@ -0,0 +1,76 @@ +package com.share.mvpsdk.helper; + + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; +import io.reactivex.FlowableEmitter; +import io.reactivex.FlowableOnSubscribe; +import io.reactivex.Observable; +import io.reactivex.ObservableEmitter; +import io.reactivex.ObservableOnSubscribe; +import io.reactivex.ObservableSource; +import io.reactivex.ObservableTransformer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + +/** + * Created by Horrarndoo on 2017/9/12. + *

+ */ +public class RxHelper { + /** + * 统一线程处理 + *

+ * 发布事件io线程,接收事件主线程 + */ + public static ObservableTransformer rxSchedulerHelper() {//compose处理线程 + return new ObservableTransformer() { + + @Override + public ObservableSource apply(Observable upstream) { + return upstream.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + }; + } + + /** + * 生成Flowable + * + * @param t + * @return Flowable + */ + public static Flowable createFlowable(final T t) { + return Flowable.create(new FlowableOnSubscribe() { + @Override + public void subscribe(FlowableEmitter emitter) throws Exception { + try { + emitter.onNext(t); + emitter.onComplete(); + } catch (Exception e) { + emitter.onError(e); + } + } + }, BackpressureStrategy.BUFFER); + } + + /** + * 生成Observable + * + * @param t + * @return Flowable + */ + public static Observable createObservable(final T t) { + return Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(ObservableEmitter emitter) throws Exception { + try { + emitter.onNext(t); + emitter.onComplete(); + } catch (Exception e) { + emitter.onError(e); + } + } + }); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/CacheInterceptor.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/CacheInterceptor.java new file mode 100644 index 0000000..9194d3d --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/CacheInterceptor.java @@ -0,0 +1,84 @@ +package com.share.mvpsdk.helper.okhttp; + + + +import com.share.mvpsdk.utils.AppUtils; +import com.share.mvpsdk.utils.NetworkConnectionUtils; + +import java.io.IOException; + +import okhttp3.CacheControl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +import static com.share.mvpsdk.utils.HttpUtils.getUserAgent; + + +/** + * Created by Horrarndoo on 2017/9/12. + *

+ * CacheInterceptor + */ +public class CacheInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + if (NetworkConnectionUtils.isNetworkConnected(AppUtils.getContext())) { + // 有网络时, 缓存1小时 + int maxAge = 60 * 60; + request = request.newBuilder() + .removeHeader("User-Agent") + .header("User-Agent", getUserAgent()) + .build(); + + Response response = chain.proceed(request); + return response.newBuilder() + .removeHeader("Pragma") + .removeHeader("Cache-Control") + .header("Cache-Control", "public, max-age=" + maxAge) + .build(); + } else { + // 无网络时,缓存为4周 + int maxStale = 60 * 60 * 24 * 28; + request = request.newBuilder() + .cacheControl(CacheControl.FORCE_CACHE) + .removeHeader("User-Agent") + .header("User-Agent", getUserAgent()) + .build(); + + Response response = chain.proceed(request); + return response.newBuilder() + .removeHeader("Pragma") + .removeHeader("Cache-Control") + .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) + .build(); + } + + // Request request = chain.request(); + // if (!NetworkConnectionUtils.isConnected(AppUtils.getContext())) { + // request = request.newBuilder() + // .cacheControl(CacheControl.FORCE_CACHE) + // .build(); + // } + // Response response = chain.proceed(request); + // if (NetworkConnectionUtils.isConnected(AppUtils.getContext())) { + // int maxAge = 0; + // // 有网络时, 不缓存, 最大保存时长为0 + // response.newBuilder() + // .header("Cache-Control", "public, max-age=" + maxAge) + // .removeHeader("Pragma") + // .build(); + // } else { + // // 无网络时,设置超时为4周 + // int maxStale = 60 * 60 * 24 * 28; + // response.newBuilder() + // .header("Cache-Control", "public, only-if-cached, max-stale=" + + // maxStale) + // .removeHeader("Pragma") + // .build(); + // } + // return response; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/HttpCache.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/HttpCache.java new file mode 100644 index 0000000..5c56799 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/HttpCache.java @@ -0,0 +1,28 @@ +package com.share.mvpsdk.helper.okhttp; + + +import android.os.Environment; + +import com.share.mvpsdk.utils.AppUtils; + +import java.io.File; + +import okhttp3.Cache; + +/** + * Created by Horrarndoo on 2017/9/12. + *

+ */ +public class HttpCache { + + private static final int HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 50 * 1024 * 1024; + + public static Cache getCache() { + File file=new File(Environment.getExternalStorageDirectory() + File + .separator + "data/NetCache"); + if (!file.getParentFile().exists())file.getParentFile().mkdirs(); + if (!file.exists())file.mkdirs(); + return new Cache(file, + HTTP_RESPONSE_DISK_CACHE_MAX_SIZE); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NetInterceptor.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NetInterceptor.java new file mode 100644 index 0000000..7add9d8 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NetInterceptor.java @@ -0,0 +1,49 @@ +package com.share.mvpsdk.helper.okhttp; + + +import com.share.mvpsdk.utils.AppUtils; +import com.share.mvpsdk.utils.NetworkConnectionUtils; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +import static com.share.mvpsdk.utils.HttpUtils.getUserAgent; + + +/** + * Created by Horrarndoo on 2017/9/18. + *

+ * 有网络时的缓存拦截器 + */ + +public class NetInterceptor implements Interceptor{ + @Override + public Response intercept(Chain chain) throws IOException { + // 有网络时, 缓存1分钟, 最大保存时长为60s + int maxAge = 60; + Request request = chain.request(); + + if (NetworkConnectionUtils.isNetworkConnected(AppUtils.getContext())) { + request = request.newBuilder() + .removeHeader("User-Agent") + .header("User-Agent", getUserAgent()) + // .header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; + // WOW64) AppleWebKit/537.36" + + // " (KHTML, like Gecko) Chrome/50.0.2661.102 + // Safari/537.36") + .build(); + + Response response = chain.proceed(request); + return response.newBuilder() + .removeHeader("Pragma") + .removeHeader("Cache-Control") + .header("Cache-Control", "public, max-age=" + maxAge) + .build(); + } + + return chain.proceed(request); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NoNetInterceptor.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NoNetInterceptor.java new file mode 100644 index 0000000..6f82e97 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/NoNetInterceptor.java @@ -0,0 +1,50 @@ +package com.share.mvpsdk.helper.okhttp; + + +import com.share.mvpsdk.utils.AppUtils; +import com.share.mvpsdk.utils.NetworkConnectionUtils; + +import java.io.IOException; + +import okhttp3.CacheControl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +import static com.share.mvpsdk.utils.HttpUtils.getUserAgent; + +/** + * Created by Horrarndoo on 2017/9/18. + *

+ * 无网络时的缓存拦截器 + */ + +public class NoNetInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + // 无网络时,设置超时为4周 + int maxStale = 60 * 60 * 24 * 28; + Request request = chain.request(); + + if (!NetworkConnectionUtils.isNetworkConnected(AppUtils.getContext())) { + request = request.newBuilder() + .cacheControl(CacheControl.FORCE_CACHE) + .removeHeader("User-Agent") + .header("User-Agent", getUserAgent()) + // .header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; + // WOW64) AppleWebKit/537.36" + + // " (KHTML, like Gecko) Chrome/50.0.2661.102 + // Safari/537.36") + .build(); + + Response response = chain.proceed(request); + return response.newBuilder() + .removeHeader("Pragma") + .removeHeader("Cache-Control") + .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) + .build(); + } + + return chain.proceed(request); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/TrustManager.java b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/TrustManager.java new file mode 100644 index 0000000..cc35751 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/helper/okhttp/TrustManager.java @@ -0,0 +1,54 @@ +package com.share.mvpsdk.helper.okhttp; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + +/** + * Created by Horrarndoo on 2017/9/12. + *

+ */ +public class TrustManager { + + public static SSLSocketFactory getUnsafeOkHttpClient() { + try { + // Create a trust manager that does not validate certificate chains + final X509TrustManager[] trustAllCerts = new X509TrustManager[]{new X509TrustManager() { + @Override + public void checkClientTrusted( + X509Certificate[] chain, + String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted( + X509Certificate[] chain, + String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }}; + + // Install the all-trusting trust manager + final SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, + new java.security.SecureRandom()); + // Create an ssl socket factory with our all-trusting manager + final SSLSocketFactory sslSocketFactory = sslContext + .getSocketFactory(); + + + return sslSocketFactory; + } catch (Exception e) { + throw new RuntimeException(e); + } + + } +} + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/BusData.java b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/BusData.java new file mode 100644 index 0000000..1e06a74 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/BusData.java @@ -0,0 +1,34 @@ +package com.share.mvpsdk.rxbus; + +/** + * RxBus data + * Created by gorden on 2016/7/8. + */ +public class BusData { + String id; + String status; + + public BusData() { + } + + public BusData(String id, String status) { + this.id = id; + this.status = status; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/RxBus.java b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/RxBus.java new file mode 100644 index 0000000..03a440c --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/RxBus.java @@ -0,0 +1,355 @@ +package com.share.mvpsdk.rxbus; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; +import io.reactivex.Scheduler; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; +import io.reactivex.functions.Function; +import io.reactivex.functions.Predicate; +import io.reactivex.schedulers.Schedulers; +import io.reactivex.subjects.PublishSubject; +import io.reactivex.subjects.Subject; + +/** + * RxBus + * Created by gorden on 2016/5/12. + * update 2017/3/1 + */ +@SuppressWarnings("unused") +public class RxBus { + public static final String LOG_BUS = "RXBUS_LOG"; + private static volatile RxBus defaultInstance; + + private Map> subscriptionsByEventType = new HashMap<>(); + + private Map> eventTypesBySubscriber = new HashMap<>(); + + private Map> subscriberMethodByEventType = new HashMap<>(); + + private final Subject bus; + + private RxBus() { + this.bus = PublishSubject.create().toSerialized(); + } + + public static RxBus get() { + RxBus rxBus = defaultInstance; + if (defaultInstance == null) { + synchronized (RxBus.class) { + rxBus = defaultInstance; + if (defaultInstance == null) { + rxBus = new RxBus(); + defaultInstance = rxBus; + } + } + } + return rxBus; + } + + /** + * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者 + * + * @param eventType 事件类型 + * @return return + */ + private Flowable toObservable(Class eventType) { + return bus.toFlowable(BackpressureStrategy.BUFFER).ofType(eventType); + } + + /** + * 根据传递的code和 eventType 类型返回特定类型(eventType)的 被观察者 + * + * @param code 事件code + * @param eventType 事件类型 + */ + private Flowable toObservable(final int code, final Class eventType) { + return bus.toFlowable(BackpressureStrategy.BUFFER).ofType(Message.class) + .filter(new Predicate() { + @Override + public boolean test(Message o) throws Exception { + return o.getCode() == code && eventType.isInstance(o.getObject()); + } + }).map(new Function() { + @Override + public Object apply(Message o) throws Exception { + return o.getObject(); + } + }).cast(eventType); + } + + /** + * 注册 + * + * @param subscriber 订阅者 + */ + public void register(Object subscriber) { + Class subClass = subscriber.getClass(); + Method[] methods = subClass.getDeclaredMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(Subscribe.class)) { + //获得参数类型 + Class[] parameterType = method.getParameterTypes(); + //参数不为空 且参数个数为1 + if (parameterType != null && parameterType.length == 1) { + + Class eventType = parameterType[0]; + + addEventTypeToMap(subscriber, eventType); + Subscribe sub = method.getAnnotation(Subscribe.class); + int code = sub.code(); + ThreadMode threadMode = sub.threadMode(); + + SubscriberMethod subscriberMethod = new SubscriberMethod(subscriber, method, eventType, code, threadMode); + addSubscriberToMap(eventType, subscriberMethod); + + addSubscriber(subscriberMethod); + } else if (parameterType == null || parameterType.length == 0) { + + Class eventType = BusData.class; + + addEventTypeToMap(subscriber, eventType); + Subscribe sub = method.getAnnotation(Subscribe.class); + int code = sub.code(); + ThreadMode threadMode = sub.threadMode(); + + SubscriberMethod subscriberMethod = new SubscriberMethod(subscriber, method, eventType, code, threadMode); + addSubscriberToMap(eventType, subscriberMethod); + + addSubscriber(subscriberMethod); + + } + } + } + } + + + /** + * 将event的类型以订阅中subscriber为key保存到map里 + * + * @param subscriber 订阅者 + * @param eventType event类型 + */ + private void addEventTypeToMap(Object subscriber, Class eventType) { + List eventTypes = eventTypesBySubscriber.get(subscriber); + if (eventTypes == null) { + eventTypes = new ArrayList<>(); + eventTypesBySubscriber.put(subscriber, eventTypes); + } + + if (!eventTypes.contains(eventType)) { + eventTypes.add(eventType); + } + } + + /** + * 将注解方法信息以event类型为key保存到map中 + * + * @param eventType event类型 + * @param subscriberMethod 注解方法信息 + */ + private void addSubscriberToMap(Class eventType, SubscriberMethod subscriberMethod) { + List subscriberMethods = subscriberMethodByEventType.get(eventType); + if (subscriberMethods == null) { + subscriberMethods = new ArrayList<>(); + subscriberMethodByEventType.put(eventType, subscriberMethods); + } + + if (!subscriberMethods.contains(subscriberMethod)) { + subscriberMethods.add(subscriberMethod); + } + } + + /** + * 将订阅事件以event类型为key保存到map,用于取消订阅时用 + * + * @param eventType event类型 + * @param disposable 订阅事件 + */ + private void addSubscriptionToMap(Class eventType, Disposable disposable) { + List disposables = subscriptionsByEventType.get(eventType); + if (disposables == null) { + disposables = new ArrayList<>(); + subscriptionsByEventType.put(eventType, disposables); + } + + if (!disposables.contains(disposable)) { + disposables.add(disposable); + } + } + + /** + * 用RxJava添加订阅者 + * + * @param subscriberMethod d + */ + @SuppressWarnings("unchecked") + private void addSubscriber(final SubscriberMethod subscriberMethod) { + Flowable flowable; + if (subscriberMethod.code == -1) { + flowable = toObservable(subscriberMethod.eventType); + } else { + flowable = toObservable(subscriberMethod.code, subscriberMethod.eventType); + } + Disposable subscription = postToObservable(flowable, subscriberMethod) + .subscribe(new Consumer() { + @Override + public void accept(Object o) throws Exception { + callEvent(subscriberMethod, o); + } + }); + + addSubscriptionToMap(subscriberMethod.subscriber.getClass(), subscription); + } + + /** + * 用于处理订阅事件在那个线程中执行 + * + * @param observable d + * @param subscriberMethod d + * @return Observable + */ + private Flowable postToObservable(Flowable observable, SubscriberMethod subscriberMethod) { + Scheduler scheduler; + switch (subscriberMethod.threadMode) { + case MAIN: + scheduler = AndroidSchedulers.mainThread(); + break; + + case NEW_THREAD: + scheduler = Schedulers.newThread(); + break; + + case CURRENT_THREAD: + scheduler = Schedulers.trampoline(); + break; + default: + throw new IllegalStateException("Unknown thread mode: " + subscriberMethod.threadMode); + } + return observable.observeOn(scheduler); + } + + /** + * 回调到订阅者的方法中 + * + * @param method code + * @param object obj + */ + private void callEvent(SubscriberMethod method, Object object) { + Class eventClass = object.getClass(); + List methods = subscriberMethodByEventType.get(eventClass); + if (methods != null && methods.size() > 0) { + for (SubscriberMethod subscriberMethod : methods) { + Subscribe sub = subscriberMethod.method.getAnnotation(Subscribe.class); + int c = sub.code(); + if (c == method.code && method.subscriber.equals(subscriberMethod.subscriber) && method.method.equals(subscriberMethod.method)) { + subscriberMethod.invoke(object); + } + + } + } + } + + /** + * 取消注册 + * + * @param subscriber object + */ + public void unRegister(Object subscriber) { + List subscribedTypes = eventTypesBySubscriber.get(subscriber); + if (subscribedTypes != null) { + for (Class eventType : subscribedTypes) { + unSubscribeByEventType(subscriber.getClass()); + unSubscribeMethodByEventType(subscriber, eventType); + } + eventTypesBySubscriber.remove(subscriber); + } + } + + /** + * subscriptions unsubscribe + * + * @param eventType eventType + */ + private void unSubscribeByEventType(Class eventType) { + List disposables = subscriptionsByEventType.get(eventType); + if (disposables != null) { + Iterator iterator = disposables.iterator(); + while (iterator.hasNext()) { + Disposable disposable = iterator.next(); + if (disposable != null && !disposable.isDisposed()) { + disposable.dispose(); + iterator.remove(); + } + } + } + } + + /** + * 移除subscriber对应的subscriberMethods + * + * @param subscriber subscriber + * @param eventType eventType + */ + private void unSubscribeMethodByEventType(Object subscriber, Class eventType) { + List subscriberMethods = subscriberMethodByEventType.get(eventType); + if (subscriberMethods != null) { + Iterator iterator = subscriberMethods.iterator(); + while (iterator.hasNext()) { + SubscriberMethod subscriberMethod = iterator.next(); + if (subscriberMethod.subscriber.equals(subscriber)) { + iterator.remove(); + } + } + } + } + + public void send(int code, Object o) { + bus.onNext(new Message(code, o)); + } + + public void send(Object o) { + bus.onNext(o); + } + + public void send(int code) { + bus.onNext(new Message(code, new BusData())); + } + + private class Message { + private int code; + private Object object; + + public Message() { + } + + private Message(int code, Object o) { + this.code = code; + this.object = o; + } + + private int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + private Object getObject() { + return object; + } + + public void setObject(Object object) { + this.object = object; + } + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/Subscribe.java b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/Subscribe.java new file mode 100644 index 0000000..fa30077 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/Subscribe.java @@ -0,0 +1,21 @@ +package com.share.mvpsdk.rxbus; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * Rxbus + * Created by gorden on 2016/7/23. + */ +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Subscribe { + int code() default -1; + + ThreadMode threadMode() default ThreadMode.CURRENT_THREAD; +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/SubscriberMethod.java b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/SubscriberMethod.java new file mode 100644 index 0000000..f7ee260 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/SubscriberMethod.java @@ -0,0 +1,44 @@ +package com.share.mvpsdk.rxbus; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * + * Created by gorden on 2016/7/23. + */ +public class SubscriberMethod { + public Method method; + public ThreadMode threadMode; + public Class eventType; + public Object subscriber; + public int code; + + public SubscriberMethod(Object subscriber, Method method, Class eventType, int code,ThreadMode threadMode) { + this.method = method; + this.threadMode = threadMode; + this.eventType = eventType; + this.subscriber = subscriber; + this.code = code; + } + + + /** + * 调用方法 + * @param o 参数 + */ + public void invoke(Object o){ + try { + Class[] parameterType = method.getParameterTypes(); + if(parameterType != null && parameterType.length == 1){ + method.invoke(subscriber, o); + }else if(parameterType == null || parameterType.length == 0){ + method.invoke(subscriber); + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/ThreadMode.java b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/ThreadMode.java new file mode 100644 index 0000000..e21d5bb --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/rxbus/ThreadMode.java @@ -0,0 +1,23 @@ +package com.share.mvpsdk.rxbus; + +/** + * + * Created by gorden on 2016/7/23. + */ +public enum ThreadMode { + /** + * current thread + */ + CURRENT_THREAD, + + /** + * android main thread + */ + MAIN, + + + /** + * new thread + */ + NEW_THREAD +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/AppUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/AppUtils.java new file mode 100644 index 0000000..1589c75 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/AppUtils.java @@ -0,0 +1,185 @@ +package com.share.mvpsdk.utils; + +import android.annotation.SuppressLint; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + + +import com.share.mvpsdk.global.GlobalApplication; + +import java.io.File; + +import timber.log.Timber; + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * App工具类 + */ +public class AppUtils { + + /** + * 获取上下文对象 + * + * @return 上下文对象 + */ + public static Context getContext() { + return GlobalApplication.getContext(); + } + + /** + * 获取全局handler + * + * @return 全局handler + */ + public static Handler getHandler() { + return GlobalApplication.getHandler(); + } + + /** + * 获取主线程id + * + * @return 主线程id + */ + public static int getMainThreadId() { + return GlobalApplication.getMainThreadId(); + } + + /** + * 获取版本名称 + */ + public static String getAppVersionName(Context context) { + String versionName = ""; + try { + // ---get the package info--- + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0); + versionName = pi.versionName; + if (versionName == null || versionName.length() <= 0) { + return ""; + } + } catch (Exception e) { + Log.e("VersionInfo", "Exception", e); + } + return versionName; + } + + /** + * 获取版本号 + */ + public static int getAppVersionCode(Context context) { + int versioncode = -1; + try { + // ---get the package info--- + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0); + versioncode = pi.versionCode; + } catch (Exception e) { + Log.e("VersionInfo", "Exception", e); + } + return versioncode; + } + + @SuppressLint("MissingPermission") + public static String getIMEI(Context context) { + TelephonyManager tm = (TelephonyManager) context.getSystemService(Context + .TELEPHONY_SERVICE); + return tm.getDeviceId(); + } + + /** + * 显示软键盘 + */ + public static void openSoftInput(EditText et) { + InputMethodManager inputMethodManager = (InputMethodManager) et.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(et, InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + * 隐藏软键盘 + */ + public static void hideSoftInput(EditText et) { + InputMethodManager inputMethodManager = (InputMethodManager) et.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(et.getWindowToken(), InputMethodManager + .HIDE_NOT_ALWAYS); + } + + /** + * 获取SD卡路径 + * + * @return 如果sd卡不存在则返回null + */ + public static File getSDPath() { + File sdDir = null; + boolean sdCardExist = Environment.getExternalStorageState().equals(Environment + .MEDIA_MOUNTED); //判断sd卡是否存在 + if (sdCardExist) { + sdDir = Environment.getExternalStorageDirectory();//获取跟目录 + } + return sdDir; + } + + /** + * 安装文件 + * + * @param data + */ + public static void promptInstall(Context context, Uri data) { + Intent promptInstall = new Intent(Intent.ACTION_VIEW) + .setDataAndType(data, "application/vnd.android.package-archive"); + // FLAG_ACTIVITY_NEW_TASK 可以保证安装成功时可以正常打开 app + promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(promptInstall); + } + + public static void copy2clipboard(Context context, String text) { + ClipboardManager cm = (ClipboardManager) context.getSystemService(Context + .CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("clip", text); + cm.setPrimaryClip(clip); + } + + /** + * 判断是否运行在主线程 + * + * @return true:当前线程运行在主线程 + * fasle:当前线程没有运行在主线程 + */ + public static boolean isRunOnUIThread() { + // 获取当前线程id, 如果当前线程id和主线程id相同, 那么当前就是主线程 + int myTid = android.os.Process.myTid(); + if (myTid == getMainThreadId()) { + return true; + } + return false; + } + + /** + * 运行在主线程 + * + * @param r 运行的Runnable对象 + */ + public static void runOnUIThread(Runnable r) { + if (isRunOnUIThread()) { + // 已经是主线程, 直接运行 + r.run(); + } else { + // 如果是子线程, 借助handler让其运行在主线程 + Log.d("66666","getHander="+getHandler()+"r="+r); + getHandler().post(r); + } + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/DBUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DBUtils.java new file mode 100644 index 0000000..663b4f5 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DBUtils.java @@ -0,0 +1,108 @@ +package com.share.mvpsdk.utils; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import com.share.mvpsdk.config.DBConfig; + + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * 数据库工具类 + */ +public class DBUtils { + public static final String CREATE_TABLE_IF_NOT_EXISTS = "create table if not exists %s " + + "(id integer primary key autoincrement,key text unique,is_read integer)"; + + private static DBUtils sDBUtis; + private SQLiteDatabase mSQLiteDatabase; + + private DBUtils(Context context) { + mSQLiteDatabase = new DBHelper(context, DBConfig.DB_NAME + ".db") + .getWritableDatabase(); + } + + public static synchronized DBUtils getDB(Context context) { + if (sDBUtis == null) { + synchronized (DBUtils.class) { + if (sDBUtis == null) + sDBUtis = new DBUtils(context); + } + } + return sDBUtis; + } + + /** + * 插入一个item已读状态到数据表 + * + * @param table 数据表名 + * @param key key值 + * @param value 数据值 + * @return 插入结果 + */ + public boolean insertRead(final String table, String key, int value) { + Cursor cursor = mSQLiteDatabase.query(table, null, null, null, null, null, "id asc"); + //最多缓存200条 + if (cursor.getCount() > 200 && cursor.moveToNext()) { + mSQLiteDatabase.delete(table, "id=?", new String[]{String.valueOf(cursor.getInt + (cursor.getColumnIndex("id")))}); + } + cursor.close(); + final ContentValues contentValues = new ContentValues(); + contentValues.put("key", key); + contentValues.put("is_read", value); + return (mSQLiteDatabase.insertWithOnConflict(table, null, contentValues, SQLiteDatabase + .CONFLICT_REPLACE) > 0); + } + + /** + * 判断item是否已经阅读过 + * + * @param table 数据表名 + * @param key key值 + * @param value + * @return + */ + public boolean isRead(String table, String key, int value) { + boolean isRead = false; + Cursor cursor = mSQLiteDatabase.query(table, null, "key=?", new String[]{key}, null, + null, null); + if (cursor.moveToNext() && (cursor.getInt(cursor.getColumnIndex("is_read")) == value)) { + isRead = true; + } + cursor.close(); + return isRead; + } + + public class DBHelper extends SQLiteOpenHelper { + + public DBHelper(Context context, String name) { + super(context, name, null, 1); + } + + @Override + public void onCreate(SQLiteDatabase db) { + //onCreate()方法只有数据库第一次被创建时才会调用,若数据库已存在,此方法不会被调用 + } + + @Override + public void onOpen(SQLiteDatabase db) { + super.onOpen(db); + //数据库打开时就会被调用,将插入新表的操作方到onOpen中 + db.execSQL(String.format(CREATE_TABLE_IF_NOT_EXISTS, DBConfig.TABLE_ZHIHU)); + db.execSQL(String.format(CREATE_TABLE_IF_NOT_EXISTS, DBConfig.TABLE_WANGYI)); + db.execSQL(String.format(CREATE_TABLE_IF_NOT_EXISTS, DBConfig.TABLE_WEIXIN)); + db.execSQL(String.format(CREATE_TABLE_IF_NOT_EXISTS, DBConfig.TABLE_GANKIO_DAY)); + db.execSQL(String.format(CREATE_TABLE_IF_NOT_EXISTS, DBConfig.TABLE_GANKIO_CUSTOM)); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + //只有数据库进行版本升级时被调用 + } + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/DateUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DateUtils.java new file mode 100644 index 0000000..d9af2e0 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DateUtils.java @@ -0,0 +1,250 @@ +package com.share.mvpsdk.utils; + +import android.text.TextUtils; +import android.text.format.DateFormat; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * 日期时间工具类 + */ +public class DateUtils { + + public static final long ONE_SECOND_MILLIONS = 1000; + public static final long ONE_MINUTE_MILLIONS = 60 * ONE_SECOND_MILLIONS; + public static final long ONE_HOUR_MILLIONS = 60 * ONE_MINUTE_MILLIONS; + public static final long ONE_DAY_MILLIONS = 24 * ONE_HOUR_MILLIONS; + public static final int DAY_OF_YEAR = 365; + + // 日期格式为 2016-02-03 17:04:58 + public static final String PATTERN_DATE = "yyyy年MM月dd日"; + public static final String PATTERN_TIME = "HH:mm:ss"; + public static final String PATTERN_SPLIT = " "; + public static final String PATTERN = PATTERN_DATE + PATTERN_SPLIT + PATTERN_TIME; + + public static String getShortTime(String dateStr) { + String str; + + Date date = str2date(dateStr); + Date curDate = new Date(); + + long durTime = curDate.getTime() - date.getTime(); + int dayDiff = calculateDayDiff(date, curDate); + + if (durTime <= 10 * ONE_MINUTE_MILLIONS) { + str = "刚刚"; + } else if (durTime < ONE_HOUR_MILLIONS) { + str = durTime / ONE_MINUTE_MILLIONS + "分钟前"; + } else if (dayDiff == 0) { + str = durTime / ONE_HOUR_MILLIONS + "小时前"; + } else if (dayDiff == -1) { + str = "昨天" + DateFormat.format("HH:mm", date); + } else if (isSameYear(date, curDate) && dayDiff < -1) { + str = DateFormat.format("MM-dd", date).toString(); + } else { + str = DateFormat.format("yyyy-MM", date).toString(); + } + + return str; + } + + + + /** + * 获取日期 PATTERN_DATE 部分 + */ + public static String getDate(String date) { + if (TextUtils.isEmpty(date) || !date.contains(PATTERN_SPLIT)) { + return ""; + } + return date.split(PATTERN_SPLIT)[0]; + } + + /** + * 原有日期上累加月 + * + * @return 累加后的日期 PATTERN_DATE 部分 + */ + public static String addMonth(String date, int moonCount) { + //如果date为空 就用当前时间 + if (TextUtils.isEmpty(date)) { + SimpleDateFormat df = new SimpleDateFormat(PATTERN_DATE + PATTERN_SPLIT + PATTERN_TIME); + date = df.format(new Date()); + } + Calendar calendar = str2calendar(date); + calendar.add(Calendar.MONTH, moonCount); + return getDate(calendar2str(calendar)); + } + + /** + * 计算天数差 + */ + public static int calculateDayDiff(Date targetTime, Date compareTime) { + boolean sameYear = isSameYear(targetTime, compareTime); + if (sameYear) { + return calculateDayDiffOfSameYear(targetTime, compareTime); + } else { + int dayDiff = 0; + + // 累计年数差的整年天数 + int yearDiff = calculateYearDiff(targetTime, compareTime); + dayDiff += yearDiff * DAY_OF_YEAR; + + // 累计同一年内的天数 + dayDiff += calculateDayDiffOfSameYear(targetTime, compareTime); + + return dayDiff; + } + } + + /** + * 计算同一年内的天数差 + */ + public static int calculateDayDiffOfSameYear(Date targetTime, Date compareTime) { + if (targetTime == null || compareTime == null) { + return 0; + } + + Calendar tarCalendar = Calendar.getInstance(); + tarCalendar.setTime(targetTime); + int tarDayOfYear = tarCalendar.get(Calendar.DAY_OF_YEAR); + + Calendar compareCalendar = Calendar.getInstance(); + compareCalendar.setTime(compareTime); + int comDayOfYear = compareCalendar.get(Calendar.DAY_OF_YEAR); + + return tarDayOfYear - comDayOfYear; + } + + /** + * 计算年数差 + */ + public static int calculateYearDiff(Date targetTime, Date compareTime) { + if (targetTime == null || compareTime == null) { + return 0; + } + + Calendar tarCalendar = Calendar.getInstance(); + tarCalendar.setTime(targetTime); + int tarYear = tarCalendar.get(Calendar.YEAR); + + Calendar compareCalendar = Calendar.getInstance(); + compareCalendar.setTime(compareTime); + int comYear = compareCalendar.get(Calendar.YEAR); + + return tarYear - comYear; + } + + /** + * 计算月数差 + * + * @param targetTime + * @param compareTime + * @return + */ + public static int calculateMonthDiff(String targetTime, String compareTime) { + return calculateMonthDiff(str2date(targetTime, PATTERN_DATE), + str2date(compareTime, PATTERN_DATE)); + } + + /** + * 计算月数差 + * + * @param targetTime + * @param compareTime + * @return + */ + public static int calculateMonthDiff(Date targetTime, Date compareTime) { + Calendar tarCalendar = Calendar.getInstance(); + tarCalendar.setTime(targetTime); + int tarYear = tarCalendar.get(Calendar.YEAR); + int tarMonth = tarCalendar.get(Calendar.MONTH); + + Calendar compareCalendar = Calendar.getInstance(); + compareCalendar.setTime(compareTime); + int comYear = compareCalendar.get(Calendar.YEAR); + int comMonth = compareCalendar.get(Calendar.MONTH); + return ((tarYear - comYear) * 12 + tarMonth - comMonth); + + } + + /** + * 是否为同一年 + */ + public static boolean isSameYear(Date targetTime, Date compareTime) { + if (targetTime == null || compareTime == null) { + return false; + } + + Calendar tarCalendar = Calendar.getInstance(); + tarCalendar.setTime(targetTime); + int tarYear = tarCalendar.get(Calendar.YEAR); + + Calendar compareCalendar = Calendar.getInstance(); + compareCalendar.setTime(compareTime); + int comYear = compareCalendar.get(Calendar.YEAR); + + return tarYear == comYear; + } + + public static Date str2date(String str, String format) { + Date date = null; + try { + if (str != null) { + SimpleDateFormat sdf = new SimpleDateFormat(format); + date = sdf.parse(str); + } + } catch (ParseException e) { + e.printStackTrace(); + } + return date; + } + + public static Date str2date(String str) { + return str2date(str, PATTERN); + } + + public static String date2str(Date date) { + return date2str(date, PATTERN); + } + + public static String date2str(Date date, String format) { + SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINA); + return sdf.format(date); + } + + public static Calendar str2calendar(String str) { + Calendar calendar = null; + Date date = str2date(str); + if (date != null) { + calendar = Calendar.getInstance(); + calendar.setTime(date); + } + return calendar; + } + + + public static Calendar str2calendar(String str, String format) { + Calendar calendar = null; + Date date = str2date(str, format); + if (date != null) { + calendar = Calendar.getInstance(); + calendar.setTime(date); + } + return calendar; + } + + public static String calendar2str(Calendar calendar) { + return date2str(calendar.getTime()); + } + + public static String calendar2str(Calendar calendar, String format) { + return date2str(calendar.getTime(), format); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/DialogUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DialogUtils.java new file mode 100644 index 0000000..83d188c --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DialogUtils.java @@ -0,0 +1,60 @@ +package com.share.mvpsdk.utils; + +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.support.v7.app.AlertDialog; + +import com.share.mvpsdk.R; + + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * 对话框工具类, 提供常用对话框显示, 使用support.v7包内的AlertDialog样式 + */ +public class DialogUtils { + + public static Dialog createProgressDialog(Context context) { + return createProgressDialog(context, true); + } + + public static Dialog createProgressDialog(Context context, boolean needCancle) { + ProgressDialog dialog = new ProgressDialog(context); + dialog.setMessage("Loading ..."); + dialog.setCancelable(needCancle); + dialog.setCanceledOnTouchOutside(false); + return dialog; + } + + public static Dialog showCommonDialog(Context context, String message, + DialogInterface.OnClickListener listener) { + return showCommonDialog(context, message, context.getString(R.string.dialog_positive), + context.getString(R.string.dialog_negative), listener); + } + + public static Dialog showCommonDialog(Context context, String message, String positiveText, + String negativeText, DialogInterface.OnClickListener + listener) { + return new AlertDialog.Builder(context) + .setMessage(message) + .setPositiveButton(positiveText, listener) + .setNegativeButton(negativeText, null) + .show(); + } + + public static Dialog showConfirmDialog(Context context, String message, + DialogInterface.OnClickListener listener) { + return showConfirmDialog(context, message, context.getString(R.string.dialog_positive), + listener); + } + + public static Dialog showConfirmDialog(Context context, String message, String positiveText, + DialogInterface.OnClickListener listener) { + return new AlertDialog.Builder(context) + .setMessage(message) + .setPositiveButton(positiveText, listener) + .show(); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/DisplayUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DisplayUtils.java new file mode 100644 index 0000000..5b27e0c --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/DisplayUtils.java @@ -0,0 +1,195 @@ +package com.share.mvpsdk.utils; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.os.Build; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; +import android.util.DisplayMetrics; +import android.view.View; +import android.widget.ImageView; + +import com.bumptech.glide.Glide; +import com.share.mvpsdk.R; + +import java.io.File; + +import jp.wasabeef.glide.transformations.BlurTransformation; + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * 显示相关工具类 + */ +public class DisplayUtils { + /** + * 将px值转换为dp值 + */ + public static int px2dp(float pxValue) { + final float scale = AppUtils.getContext().getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } + + /** + * 将dp值转换为px值 + */ + public static int dp2px(float dpValue) { + final float scale = AppUtils.getContext().getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + /** + * 将px值转换为sp值 + */ + public static int px2sp(float pxValue) { + final float scale = AppUtils.getContext().getResources().getDisplayMetrics().scaledDensity; + return (int) (pxValue / scale + 0.5f); + } + + /** + * 将sp值转换为px值 + */ + public static int sp2px(float dpValue) { + final float scale = AppUtils.getContext().getResources().getDisplayMetrics().scaledDensity; + return (int) (dpValue * scale + 0.5f); + } + + /** + * 获取屏幕宽度 + */ + public static int getScreenWidthPixels(Activity context) { + DisplayMetrics metric = new DisplayMetrics(); + context.getWindowManager().getDefaultDisplay().getMetrics(metric); + return metric.widthPixels; + } + + /** + * 获取屏幕高度 + */ + public static int getScreenHeightPixels(Activity context) { + DisplayMetrics metric = new DisplayMetrics(); + context.getWindowManager().getDefaultDisplay().getMetrics(metric); + return metric.heightPixels; + } + + /** + * 将一个view转换成bitmap位图 + * + * @param view 要转换的View + * @return view转换的bitmap + */ + public static Bitmap viewToBitmap(View view) { + Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), + Bitmap.Config.ARGB_8888); + view.draw(new Canvas(bitmap)); + return bitmap; + } + + /** + * 获取模糊虚化的bitmap + * + * @param context + * @param bitmap 要模糊的图片 + * @param radius 模糊等级 >=0 && <=25 + * @return + */ + public static Bitmap getBlurBitmap(Context context, Bitmap bitmap, int radius) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + return blurBitmap(context, bitmap, radius); + } + return bitmap; + } + + /** + * android系统的模糊方法 + * + * @param bitmap 要模糊的图片 + * @param radius 模糊等级 >=0 && <=25 + */ + public static Bitmap blurBitmap(Context context, Bitmap bitmap, int radius) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + //Let's create an empty bitmap with the same size of the bitmap we want to blur + Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap + .Config.ARGB_8888); + //Instantiate a new Renderscript + RenderScript rs = RenderScript.create(context); + //Create an Intrinsic Blur Script using the Renderscript + ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + //Create the Allocations (in/out) with the Renderscript and the in/out bitmaps + Allocation allIn = Allocation.createFromBitmap(rs, bitmap); + Allocation allOut = Allocation.createFromBitmap(rs, outBitmap); + //Set the radius of the blur + blurScript.setRadius(radius); + //Perform the Renderscript + blurScript.setInput(allIn); + blurScript.forEach(allOut); + //Copy the final bitmap created by the out Allocation to the outBitmap + allOut.copyTo(outBitmap); + //recycle the original bitmap + bitmap.recycle(); + //After finishing everything, we destroy the Renderscript. + rs.destroy(); + return outBitmap; + } else { + return bitmap; + } + } + + /** + * 显示网络虚化图片 + * + * @param context context + * @param imgUrl 图片url + * @param imageView 要显示的imageview + */ + public static void displayBlurImg(Context context, final String imgUrl, ImageView imageView) { + // "23":模糊度;"4":图片缩放4倍后再进行模糊 + Glide.with(context) + .load(imgUrl) + .error(R.drawable.stackblur_default) + .placeholder(R.drawable.stackblur_default) + .crossFade(300) + .bitmapTransform(new BlurTransformation(context, 23, 4)) + .into(imageView); + } + + /** + * 显示本地虚化图片 + * + * @param context context + * @param file 本地图片file + * @param imageView 要显示的imageview + */ + public static void displayBlurImg(Context context, File file, ImageView imageView) { + // "23":模糊度;"4":图片缩放4倍后再进行模糊 + Glide.with(context) + .load(file) + .error(R.drawable.stackblur_default) + .placeholder(R.drawable.stackblur_default) + .crossFade(300) + .bitmapTransform(new BlurTransformation(context, 23, 4)) + .into(imageView); + } + + /** + * 显示资源虚化图片 + * + * @param context context + * @param resourceId 图片资源id + * @param imageView 要显示的imageview + */ + public static void displayBlurImg(Context context, Integer resourceId, ImageView imageView) { + // "23":模糊度;"4":图片缩放4倍后再进行模糊 + Glide.with(context) + .load(resourceId) + .error(R.drawable.stackblur_default) + .placeholder(R.drawable.stackblur_default) + .crossFade(300) + .bitmapTransform(new BlurTransformation(context, 23, 4)) + .into(imageView); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/FileUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/FileUtils.java new file mode 100644 index 0000000..fc42307 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/FileUtils.java @@ -0,0 +1,291 @@ +package com.share.mvpsdk.utils; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.text.TextUtils; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * 读取文件工具类 + */ +public class FileUtils { + private static final String TAG = "FileUtils"; + + /** + * Convert byte[] to hex string.将byte转换成int, + * 然后利用Integer.toHexString(int)来转换成16进制字符串。 + * + * @param src byte[] data + * @return hex string + */ + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * 根据文件名称和路径,获取sd卡中的文件,以File形式返回byte + */ + public static File getFile(String fileName, String folder) + throws IOException { + String state = Environment.getExternalStorageState(); + if (state.equals(Environment.MEDIA_MOUNTED)) { + File pathFile = new File(Environment.getExternalStorageDirectory() + + folder); + // && !pathFile .isDirectory() + if (!pathFile.exists()) { + pathFile.mkdirs(); + } + File file = new File(pathFile, fileName); + return file; + } + return null; + } + + /** + * 根据文件名称和路径,获取sd卡中的文件,判断文件是否存在,存在返回true + */ + public static Boolean checkFile(String fileName, String folder) + throws IOException { + + File targetFile = getFile(fileName, folder); + + if (!targetFile.exists()) { + return false; + } else { + return true; + } + } + + /** + * 根据Uri返回文件绝对路径 + * 兼容了file:///开头的 和 content://开头的情况 + */ + public static String getRealFilePathFromUri(final Context context, final Uri uri) { + if (null == uri) + return null; + final String scheme = uri.getScheme(); + String data = null; + if (scheme == null) { + data = uri.getPath(); + } else if (ContentResolver.SCHEME_FILE.equalsIgnoreCase(scheme)) { + data = uri.getPath(); + } else if (ContentResolver.SCHEME_CONTENT.equalsIgnoreCase(scheme)) { + Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore + .Images.ImageColumns.DATA}, null, null, null); + if (null != cursor) { + if (cursor.moveToFirst()) { + int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); + if (index > -1) { + data = cursor.getString(index); + } + } + cursor.close(); + } + } + return data; + } + + /** + * 检查文件是否存在 + */ + public static String checkDirPath(String dirPath) { + if (TextUtils.isEmpty(dirPath)) { + return ""; + } + File dir = new File(dirPath); + if (!dir.exists()) { + dir.mkdirs(); + } + return dirPath; + } + + public static void copyFile(File sourcefile, File targetFile) { + FileInputStream input = null; + BufferedInputStream inbuff = null; + FileOutputStream out = null; + BufferedOutputStream outbuff = null; + + try { + + input = new FileInputStream(sourcefile); + inbuff = new BufferedInputStream(input); + + out = new FileOutputStream(targetFile); + outbuff = new BufferedOutputStream(out); + + byte[] b = new byte[1024 * 5]; + int len = 0; + while ((len = inbuff.read(b)) != -1) { + outbuff.write(b, 0, len); + } + outbuff.flush(); + } catch (Exception ex) { + } finally { + try { + if (inbuff != null) + inbuff.close(); + if (outbuff != null) + outbuff.close(); + if (out != null) + out.close(); + if (input != null) + input.close(); + } catch (Exception ex) { + + } + } + } + + /** + * 保存图片到本机 + * + * @param context context + * @param fileName 文件名 + * @param file file + * @param saveResultCallback 保存结果callback + */ + public static void saveImage(final Context context, final String fileName, final File file, + final SaveResultCallback saveResultCallback) { + new Thread(new Runnable() { + @Override + public void run() { + File appDir = new File(Environment.getExternalStorageDirectory(), "yizhi"); + if (!appDir.exists()) { + appDir.mkdir(); + } + String saveFileName = "yizhi_pic"; + if (fileName.contains(".png") || fileName.contains(".gif")) { + String fileFormat = fileName.substring(fileName.lastIndexOf(".")); + saveFileName = MD5Utils.getMD5("yizhi_pic" + fileName) + fileFormat; + } else { + saveFileName = MD5Utils.getMD5("yizhi_pic" + fileName) + ".png"; + } + saveFileName = saveFileName.substring(20);//取前20位作为SaveName + File savefile = new File(appDir, saveFileName); + try { + InputStream is = new FileInputStream(file); + FileOutputStream fos = new FileOutputStream(savefile); + byte[] buffer = new byte[1024 * 1024];//1M缓冲区 + int count = 0; + while ((count = is.read(buffer)) > 0) { + fos.write(buffer, 0, count); + } + fos.close(); + is.close(); + saveResultCallback.onSavedSuccess(); + } catch (FileNotFoundException e) { + saveResultCallback.onSavedFailed(); + e.printStackTrace(); + } catch (IOException e) { + saveResultCallback.onSavedFailed(); + e.printStackTrace(); + } + //保存图片后发送广播通知更新数据库 + Uri uri = Uri.fromFile(savefile); + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); + } + }).start(); + } + + /** + * 保存Bitmap到本机 + * + * @param context context + * @param fileName bitmap文件名 + * @param bmp bitmap + * @param saveResultCallback 保存结果callback + */ + public static void saveBitmap(final Context context, final String fileName, final Bitmap bmp, + final SaveResultCallback + saveResultCallback) { + new Thread(new Runnable() { + @Override + public void run() { + File appDir = new File(Environment.getExternalStorageDirectory(), "yizhi"); + if (!appDir.exists()) { + appDir.mkdir(); + } + // SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); + // 设置以当前时间格式为图片名称 + String saveFileName = MD5Utils.getMD5("yizhi_pic" + fileName) + ".png"; + saveFileName = saveFileName.substring(20);//取前20位作为SaveName + File file = new File(appDir, saveFileName); + try { + FileOutputStream fos = new FileOutputStream(file); + bmp.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + fos.close(); + saveResultCallback.onSavedSuccess(); + } catch (FileNotFoundException e) { + saveResultCallback.onSavedFailed(); + e.printStackTrace(); + } catch (IOException e) { + saveResultCallback.onSavedFailed(); + e.printStackTrace(); + } + //保存图片后发送广播通知更新数据库 + Uri uri = Uri.fromFile(file); + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); + } + }).start(); + } + + //保存webview缓存的 + public static String getCache() { + String cacheFilePath=Environment.getExternalStorageDirectory()+File.separator+"Cache"; + File cacheFile=new File(cacheFilePath); + if (!cacheFile.exists())cacheFile.mkdirs(); + return cacheFile.toString(); + } + + public static boolean webViewCacheIsExit(){ + File file=new File(getCache()); + FileInputStream fileInputStream=null; + try { + fileInputStream=new FileInputStream(file); + if (fileInputStream.available()>0) + return true; + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + public interface SaveResultCallback { + void onSavedSuccess(); + + void onSavedFailed(); + } +} + + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/HtmlUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/HtmlUtils.java new file mode 100644 index 0000000..74c1402 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/HtmlUtils.java @@ -0,0 +1,93 @@ +package com.share.mvpsdk.utils; + +import java.util.List; + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * Html工具类 + */ +public class HtmlUtils { + + //css样式,隐藏header + private static final String HIDE_HEADER_STYLE = ""; + + //css style tag,需要格式化 + private static final String NEEDED_FORMAT_CSS_TAG = ""; + + // js script tag,需要格式化 + private static final String NEEDED_FORMAT_JS_TAG = ""; + + public static final String MIME_TYPE = "text/html; charset=utf-8"; + + public static final String ENCODING = "utf-8"; + + private HtmlUtils() { + + } + + /** + * 根据css链接生成Link标签 + * + * @param url String + * @return String + */ + public static String createCssTag(String url) { + + return String.format(NEEDED_FORMAT_CSS_TAG, url); + } + + /** + * 根据多个css链接生成Link标签 + * + * @param urls List + * @return String + */ + public static String createCssTag(List urls) { + + final StringBuilder sb = new StringBuilder(); + for (String url : urls) { + sb.append(createCssTag(url)); + } + return sb.toString(); + } + + /** + * 根据js链接生成Script标签 + * + * @param url String + * @return String + */ + public static String createJsTag(String url) { + + return String.format(NEEDED_FORMAT_JS_TAG, url); + } + + /** + * 根据多个js链接生成Script标签 + * + * @param urls List + * @return String + */ + public static String createJsTag(List urls) { + + final StringBuilder sb = new StringBuilder(); + for (String url : urls) { + sb.append(createJsTag(url)); + } + return sb.toString(); + } + + /** + * 根据样式标签,html字符串,js标签 + * 生成完整的HTML文档 + */ + + public static String createHtmlData(String html, List cssList, List jsList) { + final String css = HtmlUtils.createCssTag(cssList); + final String js = HtmlUtils.createJsTag(jsList); + return css.concat(HIDE_HEADER_STYLE).concat(html).concat(js); + } +} + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/HttpUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/HttpUtils.java new file mode 100644 index 0000000..de47e25 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/HttpUtils.java @@ -0,0 +1,76 @@ +package com.share.mvpsdk.utils; + +import android.os.Build; +import android.support.annotation.NonNull; +import android.webkit.WebSettings; + +import com.orhanobut.logger.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by Horrarndoo on 2017/9/18. + *

+ * HttpUtils 主要用于获取UserAgent + */ + +public class HttpUtils { + /** + * 获取UserAgent + * + * @return UserAgent + */ + @NonNull + public static String getUserAgent() { + String userAgent = ""; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + try { + userAgent = WebSettings.getDefaultUserAgent(AppUtils.getContext()); + } catch (Exception e) { + userAgent = System.getProperty("http.agent"); + } + } else { + userAgent = System.getProperty("http.agent"); + } + StringBuffer sb = new StringBuffer(); + for (int i = 0, length = userAgent.length(); i < length; i++) { + char c = userAgent.charAt(i); + if (c <= '\u001f' || c >= '\u007f') { + sb.append(String.format("\\u%04x", (int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + public static String makeUA() { + final String ua = Build.BRAND + "/" + Build.MODEL + "/" + Build.VERSION.RELEASE; + return ua; + } + + public static String[] returnImageUrlsFromHtml(String html) { + List imageSrcList = new ArrayList(); + Pattern p = Pattern.compile("]*\\bsrc\\b\\s*=\\s*('|\")?([^'\"\n\r\f>]+(\\" + + ".jpg|\\.bmp|\\.eps|\\.gif|\\.mif|\\.miff|\\.png|\\.tif|\\.tiff|\\.svg|\\.wmf|\\" + + ".jpe|\\.jpeg|\\.dib|\\.ico|\\.tga|\\.cut|\\.pic|\\b)\\b)[^>]*>", Pattern + .CASE_INSENSITIVE); + Matcher m = p.matcher(html); + String quote = null; + String src = null; + while (m.find()) { + quote = m.group(1); + src = (quote == null || quote.trim().length() == 0) ? m.group(2).split("//s+")[0] : m + .group(2); + imageSrcList.add(src); + } + if (imageSrcList.size() == 0) { + Logger.e("资讯中未匹配到图片链接"); + return null; + } + return imageSrcList.toArray(new String[imageSrcList.size()]); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/IOUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/IOUtils.java new file mode 100644 index 0000000..32b2f04 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/IOUtils.java @@ -0,0 +1,25 @@ +package com.share.mvpsdk.utils; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * IO流工具类 + */ +public class IOUtils { + /** + * 关闭流 + */ + public static boolean close(Closeable io) { + if (io != null) { + try { + io.close(); + } catch (IOException e) { + LogUtils.e(e); + } + } + return true; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/ImageUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ImageUtils.java new file mode 100644 index 0000000..d423900 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ImageUtils.java @@ -0,0 +1,339 @@ +package com.share.mvpsdk.utils; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.support.v7.app.AlertDialog; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.FutureTarget; + +import io.reactivex.Observable; +import io.reactivex.ObservableSource; +import io.reactivex.functions.Function; + +/** + * 图片工具类 + */ +public class ImageUtils { + + /** + * 拍照 + */ + public static final int REQUEST_CODE_FROM_CAMERA = 1 << 10; + + /** + * 相册 + */ + public static final int REQUEST_CODE_FROM_ALBUM = 1 << 12; + + /** + * 裁剪 + */ + public static final int REQUEST_CODE_CROP_IMAGE = 1 << 14; + + /** + * 存放拍照图片的uri地址 + */ + public static Uri imageUriFromCamera; + + /** + * 存放裁剪图片的uri地址 + */ + public static Uri cropImageUri; + + /** + * 显示获取照片不同方式对话框 + */ + public static void showImagePickDialog(final Activity activity) { + showImagePickDialog(activity, 0); + } + + /** + * 显示获取照片不同方式对话框 + */ + public static void showImagePickDialog(final Activity activity, final int addRequest) { + String title = "选择获取图片方式"; + String[] items = new String[]{"拍照", "相册"}; + new AlertDialog.Builder(activity) + .setTitle(title) + .setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + switch (which) { + case 0: + pickImageFromCamera(activity, addRequest); + break; + case 1: + pickImageFromAlbum(activity, addRequest); + break; + default: + break; + } + } + }) + .setNegativeButton("取消", null) + .show(); + } + + /** + * 打开相机拍照获取图片 + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public static void pickImageFromCamera(final Activity activity, int addRequest) { + // 先生成一个uri地址用于存放拍照获取的图片 + imageUriFromCamera = createImageUri(activity); + + Intent intent = new Intent(); + intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); + intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUriFromCamera); + activity.startActivityForResult(intent, REQUEST_CODE_FROM_CAMERA + addRequest); + } + + /** + * 打开相机拍照获取图片 + */ + public static void pickImageFromCamera(final Activity activity) { + pickImageFromCamera(activity, 0); + } + + /** + * 打开本地相册选取图片 + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public static void pickImageFromAlbum(final Activity activity, int addRequest) { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + activity.startActivityForResult(intent, REQUEST_CODE_FROM_ALBUM + addRequest); + } + + /** + * 打开本地相册选取图片 + */ + public static void pickImageFromAlbum(final Activity activity) { + pickImageFromAlbum(activity, 0); + } + + /** + * 压缩图片 + * + * @param context context + * @param uri 图片uri + * @param reqW 上传图片需要压缩的宽度 + * @param reqH 上传图片需要压缩的高度 + * @return 上传成功回调 + */ + public static Observable compressImage(final Context context, + Uri uri, + final int reqW, + final int reqH) { + // 先从本地获取图片,利用Glide压缩图片后获取byte[] + return Observable.just(uri) + .flatMap(new Function>() { + @Override + public ObservableSource apply(Uri uri) throws Exception { + // 在work线程中,同步压缩图片,然后Observable返回 + // 即将Glide的回调封装成RxJava的Observable + FutureTarget future = Glide.with(context) + .load(uri) + .asBitmap() + .toBytes() + .into(reqW, reqH); + + byte[] bytes; + try { + bytes = future.get(); + } catch (Exception e) { + // 获取失败时,抛出runtime异常 + // 该异常会被Subscriber捕捉,进onError + throw new RuntimeException(e); + } + return Observable.just(bytes); + } + }); + } + + /** + * 图片裁剪 + */ + public static void cropImage(Activity activity, Uri srcUri) { + cropImageUri = createImageUri(activity); + + Intent intent = new Intent("com.android.camera.action.CROP"); + intent.setDataAndType(srcUri, "image/*"); + intent.putExtra("crop", "true"); + + //////////////////////////////////////////////////////////////// + // 1.宽高和比例都不设置时,裁剪框可以自行调整(比例和大小都可以随意调整) + //////////////////////////////////////////////////////////////// + // 2.只设置裁剪框宽高比(aspect)后,裁剪框比例固定不可调整,只能调整大小 + ///////////////////////////////// + // 3.裁剪后生成图片宽高(output)的设置和裁剪框无关,只决定最终生成图片大小 + //////////////////////////////////////////////////////////////// + // 4.裁剪框宽高比例(aspect)可以和裁剪后生成图片比例(output)不同,此时, + // 会以裁剪框的宽为准,按照裁剪宽高比例生成一个图片,该图和框选部分可能不同, + // 不同的情况可能是截取框选的一部分,也可能超出框选部分,向下延伸补足 + //////////////////////////////////////////////////////////////// + + // aspectX aspectY 是裁剪框宽高的比例 + intent.putExtra("aspectX", 1); + intent.putExtra("aspectY", 1); + // outputX outputY 是裁剪后生成图片的宽高 + // intent.putExtra("outputX", 300); + // intent.putExtra("outputY", 100); + + // return-data为true时,会直接返回bitmap数据,但是大图裁剪时会出现OOM,推荐下面为false时的方式 + // return-data为false时,不会返回bitmap,但需要指定一个MediaStore.EXTRA_OUTPUT保存图片uri + intent.putExtra("return-data", false); + intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri); + + activity.startActivityForResult(intent, REQUEST_CODE_CROP_IMAGE); + } + + /** + * 创建一条图片uri,用于保存拍照后的照片 + */ + private static Uri createImageUri(Context context) { + String name = "boreImg" + System.currentTimeMillis(); + ContentValues values = new ContentValues(); + values.put(MediaStore.Images.Media.TITLE, name); + values.put(MediaStore.Images.Media.DISPLAY_NAME, name + ".jpeg"); + values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + Uri uri = context.getContentResolver().insert(MediaStore.Images.Media + .EXTERNAL_CONTENT_URI, values); + return uri; + } + + /** + * 删除一条图片 + */ + public static void deleteImageUri(Context context, Uri uri) { + context.getContentResolver().delete(uri, null, null); + } + + /** + * 用第三方应用app打开图片 + */ + public static void openImageByOtherApp(Context context, Uri imageUri) { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setDataAndType(imageUri, "image/*"); + context.startActivity(intent); + } + + /** + * 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换 + */ + public static String getImageAbsolutePath19(Context context, Uri imageUri) { + if (context == null || imageUri == null) + return null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT + && DocumentsContract.isDocumentUri(context, imageUri)) { + if (isExternalStorageDocument(imageUri)) { + String docId = DocumentsContract.getDocumentId(imageUri); + String[] split = docId.split(":"); + String type = split[0]; + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + } else if (isDownloadsDocument(imageUri)) { + String id = DocumentsContract.getDocumentId(imageUri); + Uri contentUri = ContentUris.withAppendedId(Uri.parse + ("content://downloads/public_downloads"), Long.valueOf(id)); + return getDataColumn(context, contentUri, null, null); + } else if (isMediaDocument(imageUri)) { + String docId = DocumentsContract.getDocumentId(imageUri); + String[] split = docId.split(":"); + String type = split[0]; + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + String selection = MediaStore.Images.Media._ID + "=?"; + String[] selectionArgs = new String[]{split[1]}; + return getDataColumn(context, contentUri, selection, selectionArgs); + } + } + + // MediaStore (and general) + if ("content".equalsIgnoreCase(imageUri.getScheme())) { + // Return the remote address + if (isGooglePhotosUri(imageUri)) + return imageUri.getLastPathSegment(); + return getDataColumn(context, imageUri, null, null); + } + // File + else if ("file".equalsIgnoreCase(imageUri.getScheme())) { + return imageUri.getPath(); + } + return null; + } + + private static String getDataColumn(Context context, Uri uri, String selection, String[] + selectionArgs) { + Cursor cursor = null; + String column = MediaStore.Images.Media.DATA; + String[] projection = {column}; + try { + cursor = context.getContentResolver().query(uri, projection, selection, + selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + int index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + private static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + private static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is Google Photos. + */ + private static boolean isGooglePhotosUri(Uri uri) { + return "com.google.android.apps.photos.content".equals(uri.getAuthority()); + } + +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/JsonUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/JsonUtils.java new file mode 100644 index 0000000..c8aeefc --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/JsonUtils.java @@ -0,0 +1,65 @@ +package com.share.mvpsdk.utils; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; + +import java.lang.reflect.Type; + +/** + * Created by Horrarndoo on 2017/9/20. + *

+ * Json转换工具类 + */ +public class JsonUtils { + + private static Gson mGson = new Gson(); + + /** + * 将对象准换为json字符串 + * + * @param object + * @param + * @return + */ + public static String serialize(T object) { + return mGson.toJson(object); + } + + /** + * 将json字符串转换为对象 + * + * @param json + * @param clz + * @param + * @return + */ + public static T deserialize(String json, Class clz) throws JsonSyntaxException { + return mGson.fromJson(json, clz); + } + + /** + * 将json对象转换为实体对象 + * + * @param json + * @param clz + * @param + * @return + * @throws JsonSyntaxException + */ + public static T deserialize(JsonObject json, Class clz) throws JsonSyntaxException { + return mGson.fromJson(json, clz); + } + + /** + * 将json字符串转换为对象 + * + * @param json + * @param type + * @param + * @return + */ + public static T deserialize(String json, Type type) throws JsonSyntaxException { + return mGson.fromJson(json, type); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/LogUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/LogUtils.java new file mode 100644 index 0000000..140e0fc --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/LogUtils.java @@ -0,0 +1,168 @@ +package com.share.mvpsdk.utils; + +import android.util.Log; + + +/** + * Created by Horrarndoo on 2017/4/5. + *

+ * Log工具类 + */ +public class LogUtils { + /** + * 日志输出级别NONE + */ + public static final int LEVEL_NONE = 0; + /** + * 日志输出级别E + */ + public static final int LEVEL_ERROR = 1; + /** + * 日志输出级别W + */ + public static final int LEVEL_WARN = 2; + /** + * 日志输出级别I + */ + public static final int LEVEL_INFO = 3; + /** + * 日志输出级别D + */ + public static final int LEVEL_DEBUG = 4; + /** + * 日志输出级别V + */ + public static final int LEVEL_VERBOSE = 5; + + /** + * 日志输出时的TAG + */ + private static String mTag = "LogUtils"; + + /** + * 是否允许输出log + */ + private static int mDebuggable = LEVEL_VERBOSE; + + /** + * 设置调试Log开关 + * + * @param isEnable 是否允许log + */ + public static void setDebuggable(boolean isEnable) { + mDebuggable = isEnable ? LEVEL_VERBOSE : LEVEL_NONE; + } + + /** + * 以级别为 d 的形式输出LOG + */ + public static void v(String msg) { + if (mDebuggable >= LEVEL_VERBOSE) { + Log.v(mTag, msg); + } + } + + /** + * 以级别为 d 的形式输出LOG + */ + public static void d(String msg) { + if (mDebuggable >= LEVEL_DEBUG) { + Log.d(mTag, msg); + } + } + + /** + * 以级别为 i 的形式输出LOG + */ + public static void i(String msg) { + if (mDebuggable >= LEVEL_INFO) { + Log.i(mTag, msg); + } + } + + /** + * 以级别为 w 的形式输出LOG + */ + public static void w(String msg) { + if (mDebuggable >= LEVEL_WARN) { + Log.w(mTag, msg); + } + } + + /** + * 以级别为 e 的形式输出LOG + */ + public static void e(String msg) { + if (mDebuggable >= LEVEL_ERROR) { + Log.e(mTag, msg); + } + } + + /** + * 以级别为 w 的形式输出Throwable + */ + public static void w(Throwable tr) { + w("", tr); + } + + /** + * 以级别为 w 的形式输出LOG信息和Throwable + */ + public static void w(String msg, Throwable tr) { + Log.w(mTag, msg, tr); + } + + /** + * 以级别为 e 的形式输出Throwable + */ + public static void e(Throwable tr) { + e("", tr); + } + + /** + * 以级别为 e 的形式输出LOG信息和Throwable + */ + public static void e(String msg, Throwable tr) { + if (mDebuggable >= LEVEL_ERROR && null != msg) { + Log.e(mTag, msg, tr); + } + } + + private static int originStackIndex = 2; + + /** + * 获取当前方法所在的文件名 + * + * @return 当前方法所在的文件名 + */ + public static String getFileName() { + return Thread.currentThread().getStackTrace()[originStackIndex].getFileName(); + } + + /** + * 获取当前方法所在的Class名 + * + * @return 当前方法所在的Class名 + */ + public static String getClassName() { + return Thread.currentThread().getStackTrace()[originStackIndex].getClassName(); + } + + /** + * 获取当前方法名 + * + * @return 当前方法名 + */ + public static String getMethodName() { + return Thread.currentThread().getStackTrace()[originStackIndex].getMethodName(); + } + + /** + * 获取当前代码执行处行数 + * + * @return 当前代码执行处行数 + */ + public static int getLineNumber() { + return Thread.currentThread().getStackTrace()[originStackIndex].getLineNumber(); + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/MD5Utils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/MD5Utils.java new file mode 100644 index 0000000..8cd25d5 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/MD5Utils.java @@ -0,0 +1,38 @@ +package com.share.mvpsdk.utils; + +import java.security.MessageDigest; + +/** + * Created by Horrarndoo on 2017/4/5. + *

+ * MD5加密工具类 + */ +public class MD5Utils { + /* + * MD5加密,32位 + */ + public static String getMD5(String str) { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + char[] charArray = str.toCharArray(); + byte[] byteArray = new byte[charArray.length]; + for (int i = 0; i < charArray.length; i++) { + byteArray[i] = (byte) charArray[i]; + } + byte[] md5Bytes = md5.digest(byteArray); + StringBuffer hexValue = new StringBuffer(); + for (int i = 0; i < md5Bytes.length; i++) { + int val = ((int) md5Bytes[i]) & 0xff; + if (val < 16) { + hexValue.append("0"); + } + hexValue.append(Integer.toHexString(val)); + } + return hexValue.toString(); + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/NavigationUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/NavigationUtils.java new file mode 100644 index 0000000..11758d4 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/NavigationUtils.java @@ -0,0 +1,23 @@ +package com.share.mvpsdk.utils; + +import android.support.design.internal.NavigationMenuView; +import android.support.design.widget.NavigationView; + +/** + * Created by Horrarndoo on 2017/12/12. + *

+ * NavigationView utils + */ + +public class NavigationUtils { + + public static void disableNavigationViewScrollbars(NavigationView navigationView) { + if (navigationView != null) { + NavigationMenuView navigationMenuView = (NavigationMenuView) navigationView + .getChildAt(0); + if (navigationMenuView != null) { + navigationMenuView.setVerticalScrollBarEnabled(false); + } + } + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/NetworkConnectionUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/NetworkConnectionUtils.java new file mode 100644 index 0000000..371fbc3 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/NetworkConnectionUtils.java @@ -0,0 +1,236 @@ +package com.share.mvpsdk.utils; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.net.wifi.WifiManager; +import android.os.Build; + +import java.io.IOException; +import java.util.List; + +import static com.share.mvpsdk.utils.LogUtils.e; + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * Wifi连接工具类 + */ +public class NetworkConnectionUtils { + private final static String TAG = "NetworkConnectionUtils"; + + public NetworkConnectionUtils() { + } + + /** + * 连接指定 + * + * @param manager + * @param wifiSSID + * @return + */ + public static boolean connectToSocketWifi(WifiManager manager, String wifiSSID) { + LogUtils.i("要连接的socket wifi====>" + wifiSSID); + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.SSID = "\"" + wifiSSID + "\""; + wifiConfiguration.allowedKeyManagement.set(KeyMgmt.NONE); + wifiConfiguration.wepKeys[0] = "\"" + "\""; //小米手机MIUI7/华为EMUI4.1 需要webKey + + int networkId = manager.addNetwork(wifiConfiguration); + + if (networkId != -1) { + manager.enableNetwork(networkId, true); + e("连接设备成功"); + return true; + } else { + e("第一次连接失败,尝试第二次。"); + WifiConfiguration wifiConfiguration2 = new WifiConfiguration(); + wifiConfiguration2.SSID = "\"" + wifiSSID + "\""; + //wifiConfiguration.wepKeys[0] = "\"" + "\"";//去掉webKey //小米手机MIUI8不能有webKey + wifiConfiguration2.allowedKeyManagement.set(KeyMgmt.NONE); + networkId = manager.addNetwork(wifiConfiguration2); + if (networkId != -1) { + manager.enableNetwork(networkId, true); + e("连接设备成功"); + return true; + } + e("连接设备失败"); + } + return false; + } + + /** + * 获取要连接的wifi节点各个配置选项的加密类型 + * + * @param ssid + * @return wifiConfiguration + */ + public static WifiConfiguration getWifiConfiguration(WifiManager manager, String ssid, String + password) { + WifiConfiguration wifiConfiguration = new WifiConfiguration(); + wifiConfiguration.SSID = "\"" + ssid + "\""; + + List list = manager.getScanResults(); + for (ScanResult scResult : list) { + if (ssid.equals(scResult.SSID)) { + String capabilities = scResult.capabilities; + LogUtils.i("capabilities=" + capabilities); + if (capabilities.contains("WEP") || capabilities.contains("wep")) { + wifiConfiguration.allowedKeyManagement.set(KeyMgmt.WPA_EAP); + wifiConfiguration.preSharedKey = "\"" + password + "\""; + LogUtils.i("wep"); + } else if (capabilities.contains("WPA") || capabilities.contains("wpa")) { + wifiConfiguration.allowedKeyManagement.set(KeyMgmt.WPA_PSK); + wifiConfiguration.preSharedKey = "\"" + password + "\""; + LogUtils.i("wpa"); + } else { + wifiConfiguration.allowedKeyManagement.set(KeyMgmt.NONE); + LogUtils.i("none"); + } + } + } + return wifiConfiguration; + } + + /** + * 给温控器成功发送联网命令后,连接温控器连接的wifi节点 + * + * @param context 上下文对象 + * @param ssid ssid + * @param password 密码 + */ + public static void connectWifiSSID(Context context, WifiManager manager, String ssid, String + password) { + e("reSetNetwork----------连接设备连入的路由---" + ssid); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + e("当前手机系统>=Android6.0,采取兼容模式"); + new WifiAutoConnectManager(manager).connect(ssid, password, WifiAutoConnectManager + .getCipherType(context, ssid)); + } else { + int networkId = manager.addNetwork(getWifiConfiguration(manager, ssid, password)); + if (networkId != -1) { + manager.enableNetwork(networkId, true); + } + } + } + + /** + * 格式化RouterSSID + * + * @param strRouterSSID 要格式化的当前连接的路由ssid + * @return 去除"\"后的RouterSSID字符串 + */ + public static String formatRouterSSID(String strRouterSSID) { + //e("formate routerSSID before---" + strRouterSSID); + if (strRouterSSID.contains("\"")) { + strRouterSSID = strRouterSSID.replaceAll("\"", ""); + //e("formate routerSSID after---" + strRouterSSID); + } + return strRouterSSID; + } + + /** + * Ping + * 用于确定手机是否已经连接上指定设备ip地址 + */ + public static boolean pingTest(String IPOrDomainName) { + + boolean isSuccess = false; + int status; + String result = "failed"; + Process p; + try { + p = Runtime.getRuntime().exec("ping -c 1 " + IPOrDomainName);// + // m_strForNetAddress是输入的网址或者Ip地址 + status = p.waitFor();// status 只能获取是否成功,无法获取更多的信息 + + if (status == 0) { + result = "success"; + isSuccess = true; + } + + } catch (IOException | InterruptedException e) { + e(e); + } + LogUtils.d("Ping result = " + result); + return isSuccess; + } + + /** + * 判断网络是否连接 + */ + public static boolean isConnected(Context context) { + ConnectivityManager cm = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + + if (null == cm) { + return false; + } + + NetworkInfo info = cm.getActiveNetworkInfo(); + if (null != info && info.isConnected()) { + if (info.getState() == NetworkInfo.State.CONNECTED) { + return true; + } + } + return false; + } + + /** + * 判断是否有网络 + * + * @return 返回值 + */ + public static boolean isNetworkConnected(Context context) { + if (context != null) { + ConnectivityManager mConnectivityManager = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); + + if (mNetworkInfo != null) { + return mNetworkInfo.isAvailable(); + } + } + return false; + } + + /** + * 判断是否是wifi连接 + */ + public static boolean isWifi(Context context) { + ConnectivityManager cm = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + + if (null == cm) { + return false; + } + + NetworkInfo info = cm.getActiveNetworkInfo(); + if (null != info) { + if (info.getType() == ConnectivityManager.TYPE_WIFI) { + return true; + } + } + return false; + + } + + + /** + * 打开网络设置界面 + */ + public static void openSetting(Activity activity, int requestCode) { + Intent intent = new Intent("/"); + ComponentName cm = new ComponentName("com.android.settings", + "com.android.settings.WirelessSettings"); + intent.setComponent(cm); + intent.setAction(Intent.ACTION_VIEW); + activity.startActivityForResult(intent, requestCode); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/PermissionUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/PermissionUtils.java new file mode 100644 index 0000000..6b9de71 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/PermissionUtils.java @@ -0,0 +1,323 @@ +package com.share.mvpsdk.utils; + +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.util.Log; +import android.widget.Toast; + + +import com.share.mvpsdk.R; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by ToaHanDong on 2017/11/3. + */ + +public class PermissionUtils { + private static final String TAG = PermissionUtils.class.getSimpleName(); + public static final int CODE_RECORD_AUDIO = 0; + public static final int CODE_GET_ACCOUNTS = 1; + public static final int CODE_READ_PHONE_STATE = 2; + public static final int CODE_CALL_PHONE = 3; + public static final int CODE_CAMERA = 4; + public static final int CODE_ACCESS_FINE_LOCATION = 5; + public static final int CODE_ACCESS_COARSE_LOCATION = 6; + public static final int CODE_READ_EXTERNAL_STORAGE = 7; + public static final int CODE_WRITE_EXTERNAL_STORAGE = 8; + public static final int CODE_MULTI_PERMISSION = 100; + + //com.huawei.permission.sec.PERMISSION_MDM_SDCARD +// public static final String PERMISSION_MDM_SDCARD = "PERMISSION_MDM_SDCARD"; + public static final String PERMISSION_RECORD_AUDIO = Manifest.permission.RECORD_AUDIO; + public static final String PERMISSION_GET_ACCOUNTS = Manifest.permission.GET_ACCOUNTS; + public static final String PERMISSION_READ_PHONE_STATE = Manifest.permission.READ_PHONE_STATE; + public static final String PERMISSION_CALL_PHONE = Manifest.permission.CALL_PHONE; + public static final String PERMISSION_CAMERA = Manifest.permission.CAMERA; + public static final String PERMISSION_ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION; + public static final String PERMISSION_ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION; + public static final String PERMISSION_READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE; + public static final String PERMISSION_WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE; + + private static final String[] requestPermissions = { + PERMISSION_RECORD_AUDIO, + PERMISSION_GET_ACCOUNTS, + PERMISSION_READ_PHONE_STATE, + PERMISSION_CALL_PHONE, + PERMISSION_CAMERA, + PERMISSION_ACCESS_FINE_LOCATION, + PERMISSION_ACCESS_COARSE_LOCATION, + PERMISSION_READ_EXTERNAL_STORAGE, + PERMISSION_WRITE_EXTERNAL_STORAGE +// PERMISSION_MDM_SDCARD + }; + + public interface PermissionGrant { + void onPermissionGranted(int requestCode); + } + + /** + * Requests permission. + * + * @param activity + * @param requestCode request code, e.g. if you need request CAMERA permission,parameters is PermissionUtils.CODE_CAMERA + */ + public static void requestPermission(final Activity activity, final int requestCode, PermissionGrant permissionGrant) { + if (activity == null) { + return; + } + + Log.i(TAG, "requestPermission requestCode:" + requestCode); + if (requestCode < 0 || requestCode >= requestPermissions.length) { + Log.w(TAG, "requestPermission illegal requestCode:" + requestCode); + return; + } + + final String requestPermission = requestPermissions[requestCode]; + + //如果是6.0以下的手机,ActivityCompat.checkSelfPermission()会始终等于PERMISSION_GRANTED, + // 但是,如果用户关闭了你申请的权限,ActivityCompat.checkSelfPermission(),会导致程序崩溃(java.lang.RuntimeException: Unknown exception code: 1 msg null), + // 你可以使用try{}catch(){},处理异常,也可以在这个地方,低于23就什么都不做, + // 个人建议try{}catch(){}单独处理,提示用户开启权限。 +// if (Build.VERSION.SDK_INT < 23) { +// return; +// } + + int checkSelfPermission; + try { + checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission); + } catch (RuntimeException e) { +// Toast.makeText(activity, "please open this permission", Toast.LENGTH_SHORT) +// .show(); + Log.e(TAG, "RuntimeException:" + e.getMessage()); + return; + } + + if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) { + Log.i(TAG, "ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED"); + + + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) { + Log.i(TAG, "requestPermission shouldShowRequestPermissionRationale"); + shouldShowRationale(activity, requestCode, requestPermission); + + } else { + Log.d(TAG, "requestCameraPermission else"); + ActivityCompat.requestPermissions(activity, new String[]{requestPermission}, requestCode); + } + + } else { + Log.d(TAG, "ActivityCompat.checkSelfPermission ==== PackageManager.PERMISSION_GRANTED"); +// Toast.makeText(activity, "opened:" + requestPermissions[requestCode], Toast.LENGTH_SHORT).show(); + permissionGrant.onPermissionGranted(requestCode); + } + } + + private static void requestMultiResult(Activity activity, String[] permissions, int[] grantResults, PermissionGrant permissionGrant) { + + if (activity == null) { + return; + } + + //TODO + Log.d(TAG, "onRequestPermissionsResult permissions length:" + permissions.length); + Map perms = new HashMap<>(); + + ArrayList notGranted = new ArrayList<>(); + for (int i = 0; i < permissions.length; i++) { + Log.d(TAG, "permissions: [i]:" + i + ", permissions[i]" + permissions[i] + ",grantResults[i]:" + grantResults[i]); + perms.put(permissions[i], grantResults[i]); + if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { + notGranted.add(permissions[i]); + } + } + + if (notGranted.size() == 0) { +// Toast.makeText(activity, "all permission success" + notGranted, Toast.LENGTH_SHORT) +// .show(); + permissionGrant.onPermissionGranted(CODE_MULTI_PERMISSION); + } else { + openSettingActivity(activity, "those permission need granted!"); + } + + } + + + /** + * 一次申请多个权限 + */ + public static void requestMultiPermissions(final Activity activity, PermissionGrant grant) { + + final List permissionsList = getNoGrantedPermission(activity, false); + final List shouldRationalePermissionsList = getNoGrantedPermission(activity, true); + + //TODO checkSelfPermission + if (permissionsList == null || shouldRationalePermissionsList == null) { + return; + } + Log.d(TAG, "requestMultiPermissions permissionsList:" + permissionsList.size() + ",shouldRationalePermissionsList:" + shouldRationalePermissionsList.size()); + + if (permissionsList.size() > 0) { + ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]), + CODE_MULTI_PERMISSION); + Log.d(TAG, "showMessageOKCancel requestPermissions"); + + } else if (shouldRationalePermissionsList.size() > 0) { + showMessageOKCancel(activity, "should open those permission", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ActivityCompat.requestPermissions(activity, shouldRationalePermissionsList.toArray(new String[shouldRationalePermissionsList.size()]), + CODE_MULTI_PERMISSION); + Log.d(TAG, "showMessageOKCancel requestPermissions"); + } + }); + } else { + grant.onPermissionGranted(CODE_MULTI_PERMISSION); + } + + } + + + private static void shouldShowRationale(final Activity activity, final int requestCode, final String requestPermission) { + //TODO + String[] permissionsHint = activity.getResources().getStringArray(R.array.permissions); + showMessageOKCancel(activity, "Rationale: " + permissionsHint[requestCode], new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ActivityCompat.requestPermissions(activity, + new String[]{requestPermission}, + requestCode); + Log.d(TAG, "showMessageOKCancel requestPermissions:" + requestPermission); + } + }); + } + + private static void showMessageOKCancel(final Activity context, String message, DialogInterface.OnClickListener okListener) { + new AlertDialog.Builder(context) + .setMessage(message) + .setPositiveButton("OK", okListener) + .setNegativeButton("Cancel", null) + .create() + .show(); + + } + + /** + * @param activity + * @param requestCode Need consistent with requestPermission + * @param permissions + * @param grantResults + */ + public static void requestPermissionsResult(final Activity activity, final int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults, PermissionGrant permissionGrant) { + + if (activity == null) { + return; + } + Log.d(TAG, "requestPermissionsResult requestCode:" + requestCode); + + if (requestCode == CODE_MULTI_PERMISSION) { + requestMultiResult(activity, permissions, grantResults, permissionGrant); + return; + } + + if (requestCode < 0 || requestCode >= requestPermissions.length) { + Log.w(TAG, "requestPermissionsResult illegal requestCode:" + requestCode); +// Toast.makeText(activity, "illegal requestCode:" + requestCode, Toast.LENGTH_SHORT).show(); + return; + } + + Log.i(TAG, "onRequestPermissionsResult requestCode:" + requestCode + ",permissions:" + permissions.toString() + + ",grantResults:" + grantResults.toString() + ",length:" + grantResults.length); + + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Log.i(TAG, "onRequestPermissionsResult PERMISSION_GRANTED"); + //TODO success, do something, can use callback + permissionGrant.onPermissionGranted(requestCode); + + } else { + //TODO hint user this permission function + Log.i(TAG, "onRequestPermissionsResult PERMISSION NOT GRANTED"); + //TODO + String[] permissionsHint = activity.getResources().getStringArray(R.array.permissions); + openSettingActivity(activity, "Result" + permissionsHint[requestCode]); + } + + } + + private static void openSettingActivity(final Activity activity, String message) { + + showMessageOKCancel(activity, message, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Log.d(TAG, "getPackageName(): " + activity.getPackageName()); + Uri uri = Uri.fromParts("package", activity.getPackageName(), null); + intent.setData(uri); + activity.startActivity(intent); + } + }); + } + + + /** + * @param activity + * @param isShouldRationale true: return no granted and shouldShowRequestPermissionRationale permissions, false:return no granted and !shouldShowRequestPermissionRationale + * @return + */ + public static ArrayList getNoGrantedPermission(Activity activity, boolean isShouldRationale) { + + ArrayList permissions = new ArrayList<>(); + + for (int i = 0; i < requestPermissions.length; i++) { + String requestPermission = requestPermissions[i]; + + + //TODO checkSelfPermission + int checkSelfPermission = -1; + try { + checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission); + } catch (RuntimeException e) { + Toast.makeText(activity, "please open those permission", Toast.LENGTH_SHORT) + .show(); + Log.e(TAG, "RuntimeException:" + e.getMessage()); + return null; + } + + if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) { + Log.i(TAG, "getNoGrantedPermission ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED:" + requestPermission); + + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) { + Log.d(TAG, "shouldShowRequestPermissionRationale if"); + if (isShouldRationale) { + permissions.add(requestPermission); + } + + } else { + + if (!isShouldRationale) { + permissions.add(requestPermission); + } + Log.d(TAG, "shouldShowRequestPermissionRationale else"); + } + + } + } + + return permissions; + } + +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/ResourcesUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ResourcesUtils.java new file mode 100644 index 0000000..98e48d7 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ResourcesUtils.java @@ -0,0 +1,83 @@ +package com.share.mvpsdk.utils; + +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.view.View; + +/** + * Created by Horrarndoo on 2017/9/1. + *

+ * 资源工具类-加载资源文件 + */ + +public class ResourcesUtils { + /** + * 获取strings.xml资源文件字符串 + * + * @param id 资源文件id + * @return 资源文件对应字符串 + */ + public static String getString(int id) { + return AppUtils.getContext().getResources().getString(id); + } + + /** + * 获取strings.xml资源文件字符串数组 + * + * @param id 资源文件id + * @return 资源文件对应字符串数组 + */ + public static String[] getStringArray(int id) { + return AppUtils.getContext().getResources().getStringArray(id); + } + + /** + * 获取drawable资源文件图片 + * + * @param id 资源文件id + * @return 资源文件对应图片 + */ + public static Drawable getDrawable(int id) { + return AppUtils.getContext().getResources().getDrawable(id); + } + + /** + * 获取colors.xml资源文件颜色 + * + * @param id 资源文件id + * @return 资源文件对应颜色值 + */ + public static int getColor(int id) { + return AppUtils.getContext().getResources().getColor(id); + } + + /** + * 获取颜色的状态选择器 + * + * @param id 资源文件id + * @return 资源文件对应颜色状态 + */ + public static ColorStateList getColorStateList(int id) { + return AppUtils.getContext().getResources().getColorStateList(id); + } + + /** + * 获取dimens资源文件中具体像素值 + * + * @param id 资源文件id + * @return 资源文件对应像素值 + */ + public static int getDimen(int id) { + return AppUtils.getContext().getResources().getDimensionPixelSize(id);// 返回具体像素值 + } + + /** + * 加载布局文件 + * + * @param id 布局文件id + * @return 布局view + */ + public static View inflate(int id) { + return View.inflate(AppUtils.getContext(), id, null); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/ScreenUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ScreenUtils.java new file mode 100644 index 0000000..3e13c0f --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ScreenUtils.java @@ -0,0 +1,151 @@ +package com.share.mvpsdk.utils; + +import android.app.Activity; +import android.app.ActivityGroup; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.os.Build; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.util.TypedValue; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +/** + * Created by Horrarndoo on 2017/9/7. + *

+ * 屏幕相关工具类 + */ + +public class ScreenUtils { + private ScreenUtils() { + /* cannot be instantiated */ + throw new UnsupportedOperationException("cannot be instantiated"); + } + + + private static int mStatusHeight = -1; + + /** + * 获取屏幕的宽度 + * + * @param context + * @return + */ + public static int getScreenWidth(Context context) { + WindowManager manager = (WindowManager) context + .getSystemService(Context.WINDOW_SERVICE); + Display display = manager.getDefaultDisplay(); + return display.getWidth(); + } + + /** + * 获取屏幕的高度 + * + * @param context + * @return + */ + public static int getScreenHeight(Context context) { + WindowManager manager = (WindowManager) context + .getSystemService(Context.WINDOW_SERVICE); + Display display = manager.getDefaultDisplay(); + return display.getHeight(); + } + + /** + * 获取当前屏幕截图,不包含状态栏 + * + * @param activity + * @return bp + */ + public static Bitmap snapShotWithoutStatusBar(Activity activity) { + View view = activity.getWindow().getDecorView(); + view.setDrawingCacheEnabled(true); + view.buildDrawingCache(); + Bitmap bmp = view.getDrawingCache(); + if (bmp == null) { + return null; + } + Rect frame = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); + int statusBarHeight = frame.top; + Bitmap bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, bmp.getWidth(), bmp.getHeight() + - statusBarHeight); + view.destroyDrawingCache(); + view.setDrawingCacheEnabled(false); + + return bp; + } + + /** + * 获取actionbar的像素高度,默认使用android官方兼容包做actionbar兼容 + * + * @return + */ + public static int getActionBarHeight(Context context) { + int actionBarHeight = 0; + if (context instanceof AppCompatActivity && ((AppCompatActivity) context) + .getSupportActionBar() != null) { + Log.d("isAppCompatActivity", "==AppCompatActivity"); + actionBarHeight = ((AppCompatActivity) context).getSupportActionBar().getHeight(); + } else if (context instanceof Activity && ((Activity) context).getActionBar() != null) { + Log.d("isActivity", "==Activity"); + actionBarHeight = ((Activity) context).getActionBar().getHeight(); + } else if (context instanceof ActivityGroup) { + Log.d("ActivityGroup", "==ActivityGroup"); + if (((ActivityGroup) context).getCurrentActivity() instanceof AppCompatActivity && ( + (AppCompatActivity) ((ActivityGroup) context).getCurrentActivity()) + .getSupportActionBar() != null) { + actionBarHeight = ((AppCompatActivity) ((ActivityGroup) context) + .getCurrentActivity()).getSupportActionBar().getHeight(); + } else if (((ActivityGroup) context).getCurrentActivity() instanceof Activity && ( + (Activity) ((ActivityGroup) context).getCurrentActivity()).getActionBar() != + null) { + actionBarHeight = ((Activity) ((ActivityGroup) context).getCurrentActivity()) + .getActionBar().getHeight(); + } + } + if (actionBarHeight != 0) + return actionBarHeight; + final TypedValue tv = new TypedValue(); + if (context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr + .actionBarSize, tv, true)) { + if (context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr + .actionBarSize, tv, true)) + actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, context + .getResources().getDisplayMetrics()); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) + actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, context + .getResources().getDisplayMetrics()); + } else { + if (context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr + .actionBarSize, tv, true)) + actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, context + .getResources().getDisplayMetrics()); + } + Log.d("actionBarHeight", "====" + actionBarHeight); + return actionBarHeight; + } + + + /** + * 设置view margin + * + * @param v + * @param l + * @param t + * @param r + * @param b + */ + public static void setMargins(View v, int l, int t, int r, int b) { + if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); + p.setMargins(l, t, r, b); + v.requestLayout(); + } + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/SnackbarUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/SnackbarUtils.java new file mode 100644 index 0000000..6834796 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/SnackbarUtils.java @@ -0,0 +1,189 @@ +package com.share.mvpsdk.utils; + +import android.graphics.Color; +import android.support.design.widget.Snackbar; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.share.mvpsdk.R; + + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * Snackbar工具类 + */ +public class SnackbarUtils { + + public static final int Info = 1; + public static final int Confirm = 2; + public static final int Warning = 3; + public static final int Alert = 4; + + + public static int red = 0xfff44336; + public static int green = 0xff4caf50; + public static int blue = 0xff2195f3; + public static int orange = 0xffffc107; + + /** + * 短显示Snackbar,自定义颜色 + * + * @param view + * @param message + * @param messageColor + * @param backgroundColor + * @return + */ + public static Snackbar getShort(View view, String message, int messageColor, int + backgroundColor) { + Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT); + setSnackbarColor(snackbar, messageColor, backgroundColor); + return snackbar; + } + + /** + * 长显示Snackbar,自定义颜色 + * + * @param view + * @param message + * @param messageColor + * @param backgroundColor + * @return + */ + public static Snackbar getLong(View view, String message, int messageColor, int + backgroundColor) { + Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + setSnackbarColor(snackbar, messageColor, backgroundColor); + return snackbar; + } + + /** + * 自定义时常显示Snackbar,自定义颜色 + * + * @param view + * @param message + * @param messageColor + * @param backgroundColor + * @return + */ + public static Snackbar getIndefinite(View view, String message, int duration, int + messageColor, int backgroundColor) { + Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE).setDuration + (duration); + setSnackbarColor(snackbar, messageColor, backgroundColor); + return snackbar; + } + + /** + * 短显示Snackbar,可选预设类型 + * + * @param view + * @param message + * @param type + * @return + */ + public static Snackbar getShort(View view, String message, int type) { + Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT); + switchType(snackbar, type); + return snackbar; + } + + /** + * 长显示Snackbar,可选预设类型 + * + * @param view + * @param message + * @param type + * @return + */ + public static Snackbar getLong(View view, String message, int type) { + Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + switchType(snackbar, type); + return snackbar; + } + + /** + * 自定义时常显示Snackbar,可选预设类型 + * + * @param view + * @param message + * @param type + * @return + */ + public static Snackbar getIndefinite(View view, String message, int duration, int type) { + Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE).setDuration + (duration); + switchType(snackbar, type); + return snackbar; + } + + //选择预设类型 + private static void switchType(Snackbar snackbar, int type) { + switch (type) { + case Info: + setSnackbarColor(snackbar, blue); + break; + case Confirm: + setSnackbarColor(snackbar, green); + break; + case Warning: + setSnackbarColor(snackbar, orange); + break; + case Alert: + setSnackbarColor(snackbar, Color.YELLOW, red); + break; + } + } + + /** + * 设置Snackbar背景颜色 + * + * @param snackbar + * @param backgroundColor + */ + public static void setSnackbarColor(Snackbar snackbar, int backgroundColor) { + View view = snackbar.getView(); + if (view != null) { + view.setBackgroundColor(backgroundColor); + } + } + + /** + * 设置Snackbar文字和背景颜色 + * + * @param snackbar + * @param messageColor + * @param backgroundColor + */ + public static void setSnackbarColor(Snackbar snackbar, int messageColor, int backgroundColor) { + View view = snackbar.getView(); + if (view != null) { + view.setBackgroundColor(backgroundColor); + ((TextView) view.findViewById(R.id.snackbar_text)).setTextColor(messageColor); + } + } + + /** + * 向Snackbar中添加view + * + * @param snackbar + * @param layoutId + * @param index 新加布局在Snackbar中的位置 + */ + public static void addView(Snackbar snackbar, int layoutId, int index) { + View snackbarview = snackbar.getView(); + Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbarview; + + View add_view = LayoutInflater.from(snackbarview.getContext()).inflate(layoutId, null); + + LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(LinearLayout.LayoutParams + .WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + p.gravity = Gravity.CENTER_VERTICAL; + + snackbarLayout.addView(add_view, index, p); + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/SpUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/SpUtils.java new file mode 100644 index 0000000..fc81c46 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/SpUtils.java @@ -0,0 +1,212 @@ +package com.share.mvpsdk.utils; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +import java.util.ArrayList; +import java.util.List; + +/** + * SharedPreferences工具类封装 + */ +public class SpUtils { + private static SharedPreferences sp; + private static String mPreferencesName = "share_preference_default"; + + /** + * 设置preferencesName + * + * @param preferencesName preferencesName + */ + private void setPreferencesName(String preferencesName) { + mPreferencesName = preferencesName; + } + + /** + * 写入boolean变量至sp中 + * + * @param ctx 上下文环境 + * @param key 存储节点名称 + * @param value 存储节点的值 + */ + public static void putBoolean(Context ctx, String key, boolean value) { + //(存储节点文件名称,读写方式) + if (sp == null) { + sp = ctx.getSharedPreferences(mPreferencesName, Context + .MODE_PRIVATE); + } + sp.edit().putBoolean(key, value).apply(); + } + + /** + * 读取boolean标示从sp中 + * + * @param ctx 上下文环境 + * @param key 存储节点名称 + * @param defValue 没有此节点默认值 + * @return 默认值或者此节点读取到的结果 + */ + public static boolean getBoolean(Context ctx, String key, boolean defValue) { + //(存储节点文件名称,读写方式) + if (sp == null) { + sp = ctx.getSharedPreferences(mPreferencesName, Context + .MODE_PRIVATE); + } + return sp.getBoolean(key, defValue); + } + + /** + * 写入String变量至sp中 + * + * @param ctx 上下文环境 + * @param key 存储节点名称 + * @param value 存储节点的值 + */ + public static void putString(Context ctx, String key, String value) { + //(存储节点文件名称,读写方式) + if (sp == null) { + sp = ctx.getSharedPreferences(mPreferencesName, Context + .MODE_PRIVATE); + } + sp.edit().putString(key, value).apply(); + } + + /** + * 读取String标示从sp中 + * + * @param ctx 上下文环境 + * @param key 存储节点名称 + * @param defValue 没有此节点默认值 + * @return 默认值或者此节点读取到的结果 + */ + public static String getString(Context ctx, String key, String defValue) { + //(存储节点文件名称,读写方式) + if (sp == null) { + sp = ctx.getSharedPreferences(mPreferencesName, Context + .MODE_PRIVATE); + } + return sp.getString(key, defValue); + } + + + /** + * 写入int变量至sp中 + * + * @param ctx 上下文环境 + * @param key 存储节点名称 + * @param value 存储节点的值 + */ + public static void putInt(Context ctx, String key, int value) { + //(存储节点文件名称,读写方式) + if (sp == null) { + sp = ctx.getSharedPreferences(mPreferencesName, Context + .MODE_PRIVATE); + } + sp.edit().putInt(key, value).apply(); + } + + /** + * 读取int标示从sp中 + * + * @param ctx 上下文环境 + * @param key 存储节点名称 + * @param defValue 没有此节点默认值 + * @return 默认值或者此节点读取到的结果 + */ + public static int getInt(Context ctx, String key, int defValue) { + //(存储节点文件名称,读写方式) + if (sp == null) { + sp = ctx.getSharedPreferences(mPreferencesName, Context + .MODE_PRIVATE); + } + return sp.getInt(key, defValue); + } + + + /** + * 从sp中移除指定节点 + * + * @param ctx 上下文环境 + * @param key 需要移除节点的名称 + */ + public static void remove(Context ctx, String key) { + if (sp == null) { + sp = ctx.getSharedPreferences(mPreferencesName, Context + .MODE_PRIVATE); + } + sp.edit().remove(key).apply(); + } + + /** + * 保存List + * + * @param key sp key值 + * @param datalist list + * @param item 类型 + */ + public static void setDataList(String key, List datalist) { + if (null == datalist || datalist.size() <= 0) + return; + + Gson gson = new Gson(); + //转换成json数据,再保存 + String strJson = gson.toJson(datalist); + SpUtils.putString(AppUtils.getContext(), key, strJson); + } + + /** + * 获取List + * + * @param key sp key值 + * @param item 类型 + * @return list + */ + public static List getDataList(String key, Class cls) { + List datalist = new ArrayList(); + String strJson = SpUtils.getString(AppUtils.getContext(), key, null); + + if (null == strJson) { + return datalist; + } + + try { + Gson gson = new Gson(); + // datalist = gson.fromJson(strJson, new TypeToken>(){}.getType()); + JsonArray array = new JsonParser().parse(strJson).getAsJsonArray(); + for (final JsonElement elem : array) { + datalist.add(gson.fromJson(elem, cls)); + } + }catch (Exception e){ + e.printStackTrace(); + } + + return datalist; + } + + public static int getThemeIndex(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getInt("ThemeIndex", 5); + } + + public static void setThemeIndex(Context context, int index) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit().putInt("ThemeIndex", index).apply(); + } + + public static boolean getNightModel(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("pNightMode", false); + } + + public static void setNightModel(Context context, boolean nightModel) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit().putBoolean("pNightMode", nightModel).apply(); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/StatusBarUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/StatusBarUtils.java new file mode 100644 index 0000000..2abc82c --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/StatusBarUtils.java @@ -0,0 +1,139 @@ +package com.share.mvpsdk.utils; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; + +import java.lang.reflect.Field; + + +/** + * Created by Horrarndoo on 2017/8/31. + *

+ * StatusBar工具类 + */ +public class StatusBarUtils { + + private static final int DEFAULT_STATUS_BAR_ALPHA = 0; + + /** + * 设置状态栏颜色 + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + */ + public static void setColor(Activity activity, @ColorInt int color) { + setBarColor(activity, color); + } + + /** + * 设置状态栏背景色 + * 4.4以下不处理 + * 4.4使用默认沉浸式状态栏 + * + * @param color 要为状态栏设置的颜色值 + */ + public static void setBarColor(Activity activity, int color) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + Window win = activity.getWindow(); + View decorView = win.getDecorView(); + win.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//沉浸式状态栏(4.4-5.0透明,5.0以上半透明) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//android5.0以上设置透明效果 + win.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//清除flag,为了android5.0以上也全透明效果 + //让应用的主体内容占用系统状态栏的空间 + int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; + decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | option); + win.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + win.setStatusBarColor(color);//设置状态栏背景色 + } + } + } + + /** + * 设置状态栏全透明 + * + * @param activity 需要设置的activity + */ + public static void setTransparent(Activity activity) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + setColor(activity, Color.TRANSPARENT); + } + + /** + * 修正 Toolbar 的位置 + * 在 Android 4.4 版本下无法显示内容在 StatusBar 下,所以无需修正 Toolbar 的位置 + * + * @param toolbar + */ + public static void fixToolbar(Toolbar toolbar, Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + int statusHeight = getStatusBarHeight(activity); + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); + layoutParams.setMargins(0, statusHeight, 0, 0); + } + } + + /** + * 获取系统状态栏高度 + * + * @param context + * @return + */ + public static int getStatusBarHeight(Context context) { + Class c = null; + Object obj = null; + Field field = null; + int x = 0, statusBarHeight = 0; + try { + c = Class.forName("com.android.internal.R$dimen"); + obj = c.newInstance(); + field = c.getField("status_bar_height"); + x = Integer.parseInt(field.get(obj).toString()); + statusBarHeight = context.getResources().getDimensionPixelSize(x); + } catch (Exception e1) { + e1.printStackTrace(); + } + return statusBarHeight; + } + +// /** +// * 获取状态栏高度 +// * +// * @param context context +// * @return 状态栏高度 +// */ +// private static int getStatusBarHeight(Context context) { +// // 获得状态栏高度 +// int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", +// "android"); +// return context.getResources().getDimensionPixelSize(resourceId); +// } + + /** + * 计算状态栏颜色 + * + * @param color color值 + * @param alpha alpha值 + * @return 最终的状态栏颜色 + */ + private static int calculateStatusColor(@ColorInt int color, int alpha) { + float a = 1 - alpha / 255f; + int red = color >> 16 & 0xff; + int green = color >> 8 & 0xff; + int blue = color & 0xff; + red = (int) (red * a + 0.5); + green = (int) (green * a + 0.5); + blue = (int) (blue * a + 0.5); + return 0xff << 24 | red << 16 | green << 8 | blue; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/StringUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/StringUtils.java new file mode 100644 index 0000000..57486fa --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/StringUtils.java @@ -0,0 +1,125 @@ +package com.share.mvpsdk.utils; + +import android.text.TextUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by Horrarndoo on 2017/4/5. + * 字符串工具类 + */ +public class StringUtils { + /** + * 判断字符串是否有值,如果为null或者是空字符串或者只有空格或者为"null"字符串,则返回true,否则则返回false + */ + public static boolean isEmpty(String value) { + return !(value != null && !"".equalsIgnoreCase(value.trim()) + && !"null".equalsIgnoreCase(value.trim())); + } + + /** + * 判断字符串是否是邮箱 + * + * @param email email + * @return 字符串是否是邮箱 + */ + public static boolean isEmail(String email) { + String str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(" + + "([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$"; + Pattern p = Pattern.compile(str); + Matcher m = p.matcher(email); + return m.matches(); + } + + /** + * 判断手机号字符串是否合法 + * + * @param phoneNumber 手机号字符串 + * @return 手机号字符串是否合法 + */ + public static boolean isPhoneNumberValid(String phoneNumber) { + boolean isValid = false; + String expression = "^1[3|4|5|7|8]\\d{9}$"; + CharSequence inputStr = phoneNumber; + Pattern pattern = Pattern.compile(expression); + Matcher matcher = pattern.matcher(inputStr); + if (matcher.matches()) { + isValid = true; + } + return isValid; + } + + /** + * 判断手机号字符串是否合法 + * + * @param areaCode 区号 + * @param phoneNumber 手机号字符串 + * @return 手机号字符串是否合法 + */ + public static boolean isPhoneNumberValid(String areaCode, String phoneNumber) { + if (TextUtils.isEmpty(phoneNumber)) { + return false; + } + + if (phoneNumber.length() < 5) { + return false; + } + + if (TextUtils.equals(areaCode, "+86") || TextUtils.equals(areaCode, "86")) { + return isPhoneNumberValid(phoneNumber); + } + + boolean isValid = false; + String expression = "^[0-9]*$"; + CharSequence inputStr = phoneNumber; + Pattern pattern = Pattern.compile(expression); + Matcher matcher = pattern.matcher(inputStr); + if (matcher.matches()) { + isValid = true; + } + return isValid; + } + + /** + * 判断字符串是否是手机号格式 + * + * @param areaCode 区号 + * @param phoneNumber 手机号字符串 + * @return 字符串是否是手机号格式 + */ + public static boolean isPhoneFormat(String areaCode, String phoneNumber) { + if (TextUtils.isEmpty(phoneNumber)) { + return false; + } + + if (phoneNumber.length() < 7) { + return false; + } + + boolean isValid = false; + String expression = "^[0-9]*$"; + CharSequence inputStr = phoneNumber; + Pattern pattern = Pattern.compile(expression); + Matcher matcher = pattern.matcher(inputStr); + if (matcher.matches()) { + isValid = true; + } + return isValid; + } + + /** + * 判断字符串是否为纯数字 + * + * @param str 字符串 + * @return 是否纯数字 + */ + public static boolean isNumber(String str) { + for (int i = 0; i < str.length(); i++) { + if (!Character.isDigit(str.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/ThemeUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ThemeUtils.java new file mode 100644 index 0000000..cb743c5 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ThemeUtils.java @@ -0,0 +1,75 @@ +package com.share.mvpsdk.utils; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; + +import com.share.mvpsdk.R; + + +/** + * 作者:杭鹏伟 + * 日期:16-7-8 15:15 + * 邮箱:424346976@qq.com + */ +public class ThemeUtils { + public static int[][] themeArr = { + {R.style.AppThemeLight_Red, R.style.AppThemeDark_Red}, + {R.style.AppThemeLight_Pink, R.style.AppThemeDark_Pink}, + {R.style.AppThemeLight_Purple, R.style.AppThemeDark_Purple}, + {R.style.AppThemeLight_DeepPurple, R.style.AppThemeDark_DeepPurple}, + {R.style.AppThemeLight_Indigo, R.style.AppThemeDark_Indigo}, + {R.style.AppThemeLight_Blue, R.style.AppThemeDark_Blue}, + {R.style.AppThemeLight_LightBlue, R.style.AppThemeDark_LightBlue}, + {R.style.AppThemeLight_Cyan, R.style.AppThemeDark_Cyan}, + {R.style.AppThemeLight_Teal, R.style.AppThemeDark_Teal}, + {R.style.AppThemeLight_Green, R.style.AppThemeDark_Green}, + {R.style.AppThemeLight_LightGreen, R.style.AppThemeDark_LightGreen}, + {R.style.AppThemeLight_Lime, R.style.AppThemeDark_Lime}, + {R.style.AppThemeLight_Yellow, R.style.AppThemeDark_Yellow}, + {R.style.AppThemeLight_Amber, R.style.AppThemeDark_Amber}, + {R.style.AppThemeLight_Orange, R.style.AppThemeDark_Orange}, + {R.style.AppThemeLight_DeepOrange, R.style.AppThemeDark_DeepOrange}, + {R.style.AppThemeLight_Brown, R.style.AppThemeDark_Brown}, + {R.style.AppThemeLight_Grey, R.style.AppThemeDark_Grey}, + {R.style.AppThemeLight_BlueGrey, R.style.AppThemeDark_BlueGrey} + }; + + public static int[][] themeColorArr = { + {R.color.md_red_500, R.color.md_red_700}, {R.color.md_pink_500, R.color.md_pink_700}, + {R.color.md_purple_500, R.color.md_purple_700}, + {R.color.md_deep_purple_500, R.color.md_deep_purple_700}, + {R.color.md_indigo_500, R.color.md_indigo_700}, + {R.color.md_blue_500, R.color.md_blue_700}, + {R.color.md_light_blue_500, R.color.md_light_blue_700}, + {R.color.md_cyan_500, R.color.md_cyan_700}, {R.color.md_teal_500, R.color.md_teal_500}, + {R.color.md_green_500, R.color.md_green_500}, + {R.color.md_light_green_500, R.color.md_light_green_500}, + {R.color.md_lime_500, R.color.md_lime_700}, + {R.color.md_yellow_500, R.color.md_yellow_700}, + {R.color.md_amber_500, R.color.md_amber_700}, + {R.color.md_orange_500, R.color.md_orange_700}, + {R.color.md_deep_orange_500, R.color.md_deep_orange_700}, + {R.color.md_brown_500, R.color.md_brown_700}, {R.color.md_grey_500, R.color.md_grey_700}, + {R.color.md_blue_grey_500, R.color.md_blue_grey_700} + }; + + public static int getTheme(Context context) { + return context.getResources() + .getColor(themeColorArr[SpUtils.getThemeIndex(context)][0]); + } + + public static int getThemeColor(@NonNull Context context) { + return getThemeAttrColor(context, R.attr.colorPrimary); + } + + public static int getThemeAttrColor(@NonNull Context context, @AttrRes int attr) { + TypedArray a = context.obtainStyledAttributes(null, new int[]{attr}); + try { + return a.getColor(0, 0); + } finally { + a.recycle(); + } + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/TimestampUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/TimestampUtils.java new file mode 100644 index 0000000..355308a --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/TimestampUtils.java @@ -0,0 +1,57 @@ +package com.share.mvpsdk.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class TimestampUtils { + /** + * 获取当前的时间戳,时区为北京 + * + * @return + */ + public static String getCurrentTimestamp() { + //时间戳的格式必须为 yyyy-MM-dd HH:mm:ss + String timestamp = null; + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + timestamp = format.format(new Date()); + return timestamp; + } + + /** + * 获取当前的时间戳,时区为北京 + * + * @return + */ + public static String getCurrentTime(long times) { + //时间戳的格式必须为 yyyy-MM-dd HH:mm:ss + Date date = new Date(Long.valueOf(times)); + SimpleDateFormat format = new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + String time = format.format(date); + LogUtils.e("timetimetimetimetimetimetime为:" + time); + + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + .format(new Date()); + + return time; + } + + //法国时间:东一区 + public static String getDateTimeByGMT(int timeZone) { + SimpleDateFormat dff = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + switch (timeZone) { + case 1: + dff.setTimeZone(TimeZone.getTimeZone("GMT+1")); + break; + case 8: + dff.setTimeZone(TimeZone.getTimeZone("GMT+8")); + //LogUtils.i("采用东八区时区"); + break; + } + + String time = dff.format(new Date()); + //LogUtils.i("东八区时区时间为--》》" + time); + return time; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/ToastUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ToastUtils.java new file mode 100644 index 0000000..26c212e --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/ToastUtils.java @@ -0,0 +1,66 @@ +package com.share.mvpsdk.utils; + +import android.content.Context; +import android.widget.Toast; + +/** + * Created by Horrarndoo on 2017/4/5. + *

+ * toast工具类封装 + */ +public class ToastUtils { + private static Toast mToast = null; + + /** + * 显示一个toast提示 + * + * @param resouceId toast字符串资源id + */ + public static void showToast(int resouceId) { + showToast(ResourcesUtils.getString(resouceId)); + } + + /** + * 显示一个toast提示 + * + * @param text toast字符串 + */ + public static void showToast(String text) { + showToast(text, Toast.LENGTH_SHORT); + } + + /** + * 显示一个toast提示 + * + * @param text toast字符串 + * @param duration toast显示时间 + */ + public static void showToast(String text, int duration) { + showToast(AppUtils.getContext(), text, duration); + } + + /** + * 显示一个toast提示 + * + * @param context context 上下文对象 + * @param text toast字符串 + * @param duration toast显示时间 + */ + public static void showToast(final Context context, final String text, final int duration) { + /** + * 保证运行在主线程 + */ + AppUtils.runOnUIThread(new Runnable() { + @Override + public void run() { + if (mToast == null) { + mToast = Toast.makeText(context, text, duration); + } else { + mToast.setText(text); + mToast.setDuration(duration); + } + mToast.show(); + } + }); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/UnicodeUtils.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/UnicodeUtils.java new file mode 100644 index 0000000..59fc277 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/UnicodeUtils.java @@ -0,0 +1,108 @@ +package com.share.mvpsdk.utils; + +/** + * Created by Horrarndoo on 2017/10/11. + *

+ */ +public class UnicodeUtils { + /** + * utf-8 转换成 unicode + * + * @param inStr + * @return + */ + public static String utf8ToUnicode(String inStr) { + char[] myBuffer = inStr.toCharArray(); + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < inStr.length(); i++) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(myBuffer[i]); + if (ub == Character.UnicodeBlock.BASIC_LATIN) { + //英文及数字等 + sb.append(myBuffer[i]); + } else if (ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { + //全角半角字符 + int j = (int) myBuffer[i] - 65248; + sb.append((char) j); + } else { + //汉字 + short s = (short) myBuffer[i]; + String hexS = Integer.toHexString(s); + String unicode = "\\u" + hexS; + sb.append(unicode.toLowerCase()); + } + } + return sb.toString(); + } + + /** + * unicode 转换成 utf-8 + * + * @param theString + * @return + */ + public static String unicodeToUtf8(String theString) { + char aChar; + int len = theString.length(); + StringBuffer outBuffer = new StringBuffer(len); + for (int x = 0; x < len; ) { + aChar = theString.charAt(x++); + if (aChar == '\\') { + aChar = theString.charAt(x++); + if (aChar == 'u') { + // Read the xxxx + int value = 0; + for (int i = 0; i < 4; i++) { + aChar = theString.charAt(x++); + switch (aChar) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + value = (value << 4) + aChar - '0'; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + value = (value << 4) + 10 + aChar - 'a'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + value = (value << 4) + 10 + aChar - 'A'; + break; + default: + throw new IllegalArgumentException( + "Malformed \\uxxxx encoding."); + } + } + outBuffer.append((char) value); + } else { + if (aChar == 't') + aChar = '\t'; + else if (aChar == 'r') + aChar = '\r'; + else if (aChar == 'n') + aChar = '\n'; + else if (aChar == 'f') + aChar = '\f'; + outBuffer.append(aChar); + } + } else + outBuffer.append(aChar); + } + return outBuffer.toString(); + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/utils/WifiAutoConnectManager.java b/mvpsdk/src/main/java/com/share/mvpsdk/utils/WifiAutoConnectManager.java new file mode 100644 index 0000000..7a8320b --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/utils/WifiAutoConnectManager.java @@ -0,0 +1,224 @@ +package com.share.mvpsdk.utils; + +import android.content.Context; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; + +import java.util.List; + +/** + * Created by Horrarndoo on 2017/8/10. + *

+ * 兼容Android 6.0以上手机连接wifi + */ + +public class WifiAutoConnectManager { + + private static final String TAG = WifiAutoConnectManager.class + .getSimpleName(); + + WifiManager wifiManager; + + // 定义几种加密方式,一种是WEP,一种是WPA,还有没有密码的情况 + public enum WifiCipherType { + WIFICIPHER_WEP, WIFICIPHER_WPA, WIFICIPHER_NOPASS, WIFICIPHER_INVALID + } + + // 构造函数 + public WifiAutoConnectManager(WifiManager wifiManager) { + this.wifiManager = wifiManager; + } + + // 提供一个外部接口,传入要连接的无线网 + public void connect(String ssid, String password, WifiCipherType type) { + Thread thread = new Thread(new ConnectRunnable(ssid, password, type)); + thread.start(); + } + + // 查看以前是否也配置过这个网络 + private WifiConfiguration isExsits(String SSID) { + List existingConfigs = wifiManager + .getConfiguredNetworks(); + for (WifiConfiguration existingConfig : existingConfigs) { + if (existingConfig.SSID.equals("\"" + SSID + "\"")) { + return existingConfig; + } + } + return null; + } + + private WifiConfiguration createWifiInfo(String SSID, String Password, + WifiCipherType Type) { + WifiConfiguration config = new WifiConfiguration(); + config.allowedAuthAlgorithms.clear(); + config.allowedGroupCiphers.clear(); + config.allowedKeyManagement.clear(); + config.allowedPairwiseCiphers.clear(); + config.allowedProtocols.clear(); + config.SSID = "\"" + SSID + "\""; + // config.SSID = SSID; + // nopass + if (Type == WifiCipherType.WIFICIPHER_NOPASS) { + // config.wepKeys[0] = ""; + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + // config.wepTxKeyIndex = 0; + } + // wep + if (Type == WifiCipherType.WIFICIPHER_WEP) { + if (!StringUtils.isEmpty(Password)) { + if (isHexWepKey(Password)) { + config.wepKeys[0] = Password; + } else { + config.wepKeys[0] = "\"" + Password + "\""; + } + } + config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); + config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + config.wepTxKeyIndex = 0; + } + // wpa + if (Type == WifiCipherType.WIFICIPHER_WPA) { + config.preSharedKey = "\"" + Password + "\""; + config.hiddenSSID = true; + config.allowedAuthAlgorithms + .set(WifiConfiguration.AuthAlgorithm.OPEN); + config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + config.allowedPairwiseCiphers + .set(WifiConfiguration.PairwiseCipher.TKIP); + // 此处需要修改否则不能自动重联 + // config.allowedProtocols.set(WifiConfiguration.Protocol.WPA); + config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); + config.allowedPairwiseCiphers + .set(WifiConfiguration.PairwiseCipher.CCMP); + config.status = WifiConfiguration.Status.ENABLED; + + } + return config; + } + + // 打开wifi功能 + private boolean openWifi() { + boolean bRet = true; + if (!wifiManager.isWifiEnabled()) { + bRet = wifiManager.setWifiEnabled(true); + } + return bRet; + } + + // 关闭WIFI + private void closeWifi() { + if (wifiManager.isWifiEnabled()) { + wifiManager.setWifiEnabled(false); + } + } + + class ConnectRunnable implements Runnable { + private String ssid; + + private String password; + + private WifiCipherType type; + + public ConnectRunnable(String ssid, String password, WifiCipherType type) { + this.ssid = ssid; + this.password = password; + this.type = type; + } + + @Override + public void run() { + // 打开wifi + openWifi(); + // 开启wifi功能需要一段时间(我在手机上测试一般需要1-3秒左右),所以要等到wifi + // 状态变成WIFI_STATE_ENABLED的时候才能执行下面的语句 + while (wifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLING) { + try { + // 为了避免程序一直while循环,让它睡个100毫秒检测…… + Thread.sleep(100); + + } catch (InterruptedException ie) { + LogUtils.e(ie.toString()); + } + } + + WifiConfiguration tempConfig = isExsits(ssid); + + if (tempConfig != null) { + boolean b = wifiManager.enableNetwork(tempConfig.networkId, + true); + } else { + WifiConfiguration wifiConfig = createWifiInfo(ssid, password, + type); + if (wifiConfig == null) { + LogUtils.d("wifiConfig is null!"); + return; + } + + int netID = wifiManager.addNetwork(wifiConfig); + boolean enabled = wifiManager.enableNetwork(netID, true); + LogUtils.d("enableNetwork status enable=" + enabled); + boolean connected = wifiManager.reconnect(); + LogUtils.d("enableNetwork connected=" + connected); + } + + } + } + + private static boolean isHexWepKey(String wepKey) { + final int len = wepKey.length(); + + // WEP-40, WEP-104, and some vendors using 256-bit WEP (WEP-232?) + if (len != 10 && len != 26 && len != 58) { + return false; + } + + return isHex(wepKey); + } + + private static boolean isHex(String key) { + for (int i = key.length() - 1; i >= 0; i--) { + final char c = key.charAt(i); + if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' + && c <= 'f')) { + return false; + } + } + + return true; + } + + // 获取ssid的加密方式 + public static WifiCipherType getCipherType(Context context, String ssid) { + WifiManager wifiManager = (WifiManager) context + .getSystemService(Context.WIFI_SERVICE); + + List list = wifiManager.getScanResults(); + + for (ScanResult scResult : list) { + + if (!StringUtils.isEmpty(scResult.SSID) && scResult.SSID.equals(ssid)) { + String capabilities = scResult.capabilities; + + if (!StringUtils.isEmpty(capabilities)) { + + if (capabilities.contains("WPA") + || capabilities.contains("wpa")) { + LogUtils.i("wpa"); + return WifiCipherType.WIFICIPHER_WPA; + } else if (capabilities.contains("WEP") + || capabilities.contains("wep")) { + LogUtils.i("wep"); + return WifiCipherType.WIFICIPHER_WEP; + } else { + LogUtils.i("no"); + return WifiCipherType.WIFICIPHER_NOPASS; + } + } + } + } + return WifiCipherType.WIFICIPHER_INVALID; + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/view/MyEyeView.java b/mvpsdk/src/main/java/com/share/mvpsdk/view/MyEyeView.java new file mode 100644 index 0000000..4c7dcf7 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/view/MyEyeView.java @@ -0,0 +1,71 @@ +package com.share.mvpsdk.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Shader; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.View; + +/** + * Created by ToaHanDong on 2018/2/7. + */ + +public class MyEyeView extends View { + + Paint myPaint; + + int mypaintWidth=2; + + int centerLeft=0,centerTop=0,centerRight=0,centerBottom=0; + public MyEyeView(Context context) { + super(context); + init(context); + } + + public MyEyeView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public MyEyeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + while (centerLeft>0){ + centerLeft-=10; + centerTop-=10; + centerRight+=10; + centerBottom+=10; + canvas.drawRect(centerLeft,centerTop,centerRight,centerBottom,myPaint); + } + } + + private void init(Context context) { + myPaint=new Paint(); + myPaint.setColor(Color.WHITE); + myPaint.setAntiAlias(true); + myPaint.setStyle(Paint.Style.STROKE); + myPaint.setStrokeWidth(mypaintWidth); +// myPaint.setShader(new Shader()); + } + + public void setMyPaintWidth(int paintWidth){ + mypaintWidth=paintWidth; + } + + public void setIndexPoint(int centerLeft,int centerTop,int centerRight,int centerBottom){ + this.centerLeft=centerLeft; + this.centerTop=centerTop; + this.centerRight=centerRight; + this.centerBottom=centerBottom; + invalidate(); + } + +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/view/MyViewView.java b/mvpsdk/src/main/java/com/share/mvpsdk/view/MyViewView.java new file mode 100644 index 0000000..95486bd --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/view/MyViewView.java @@ -0,0 +1,368 @@ +package com.share.mvpsdk.view; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.http.SslError; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.webkit.JavascriptInterface; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.SslErrorHandler; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; + +import com.share.mvpsdk.utils.FileUtils; +import com.share.mvpsdk.utils.NetworkConnectionUtils; + +/** + * Created by ToaHanDong on 2018/1/25. + */ + +public class MyViewView extends WebView { + + public MyViewView(Context context) { + super(context); + init(context); + } + + public MyViewView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public MyViewView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private String TAG="MyWebView"; + //WebView的设置类 + private WebSettings webSettings = null; + + private void init(Context context) { + + initWebSetting(context); + + } + + private void initWebSetting(Context context) { + + webSettings = getSettings();//初始化websettings + + //如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript + webSettings.setJavaScriptEnabled(true); + //支持插件 + webSettings.setPluginState(WebSettings.PluginState.ON); + //设置自适应屏幕,两者合用 + webSettings.setUseWideViewPort(true);//将图片调整适合WebView的大小 + webSettings.setLoadWithOverviewMode(true);//缩放至屏幕的大小 + //缩放操作 + webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。 + webSettings.setBuiltInZoomControls(true);//设置内置的缩放控件。若为false,则该WebView不可缩放 + webSettings.setDisplayZoomControls(true);//隐藏原生的缩放控件 + //其他细节操作 + //LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据 + //LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。 + //LOAD_NO_CACHE: 不使用缓存,只从网络获取数据. + //LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。 +// webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存 + webSettings.setAllowFileAccess(true);//设置可以访问文件 + webSettings.setJavaScriptCanOpenWindowsAutomatically(true);//支持通过js打开新窗口 + webSettings.setLoadsImagesAutomatically(true);//设置自动加载图片 + webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式为ytf-8 + if (NetworkConnectionUtils.isConnected(context)) + webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); + else webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能 + webSettings.setDatabaseEnabled(true); //开启 database storage API 功能 + webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能 + if (!FileUtils.webViewCacheIsExit()) { + webSettings.setAppCachePath(FileUtils.getCache()); //设置 Application Caches 缓存目录,只能设置一次 + webSettings.setAppCacheMaxSize(8 * 1024 * 1024); + } + } + + FrameLayout frame; + public void setView(FrameLayout frame) { + this.frame=frame; + } + + /** + * js接口 + */ + public class SupportJavascriptInterface { + private Context context; + + public SupportJavascriptInterface(Context context) { + this.context = context; + } + + @JavascriptInterface + public void openImage(final String img) { + Log.d(TAG,"openImage="+img); +// goBackOrForward(-1); +// AppUtils.runOnUIThread(new Runnable() { +// @Override +// public void run() { +// gotoImageBrowse(img); +// } +// }); + } + } + + // WebViewClient类(主要作用是:处理各种通知 & 请求事件) + public void setMyWebViewClient(String url) { + //步骤1: 选择加载方式 +//方式a. 加载一个网页: + loadUrl(url); +//方式b:加载apk包中的html页面 +// loadUrl("file:///android_asset/test.html"); +//方式c:加载手机本地的html页面 +// loadUrl("content://com.android.htmlfileprovider/sdcard/test.html"); + addJavascriptInterface(new SupportJavascriptInterface(getContext()),"imagelistener"); + setWebViewClient(new WebViewClient() { + + //设定加载开始的操作 + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + Log.d(TAG,"onPageStarted="+url+"favicon="+favicon); + } + + //html加载完成 + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + Log.d(TAG,"onPageFinished="+url); + addWebImageClickListner(view); + } + + // 注入js函数监听 + protected void addWebImageClickListner(WebView webView) { + // 这段js函数的功能就是,遍历所有的img节点,并添加onclick函数, + // 函数的功能是在图片点击的时候调用本地java接口并传递url过去 + webView.loadUrl("javascript:(function(){" + + "var objs = document.getElementsByTagName(\"img\"); " + + "for(var i=0;i= Build.VERSION_CODES.M) { +// switch (error.getErrorCode()) +// } +// } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + + //webView默认是不处理https请求的,页面显示空白,需要进行如下设置: + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + super.onReceivedSslError(view, handler, error); + Log.d(TAG,"onReceivedSslError"); + handler.proceed();//表示等待证书响应 + } + }); + + } + + //辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。 + public void setMyWebChromeClient(){ + setWebChromeClient(new WebChromeClient(){ + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + super.onShowCustomView(view, callback); + Log.d(TAG,"onShowCustomView="); + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + Log.d(TAG,"newProgress="+newProgress); + } + + @Override + public void onReceivedTitle(WebView view, String title) { + super.onReceivedTitle(view, title); + Log.d(TAG,"onReceivedTitle="+title); + } + + /** + * js中的弹出框 + * @param view + * @param url + * @param message + * @param result 可以对弹出框做取消和确认操作 + * @return + */ + @Override + public boolean onJsAlert(WebView view, String url, String message, JsResult result) { + Log.d(TAG,"onJsAlert="+message+"url="+url); + return super.onJsAlert(view, url, message, result); + } + +// 支持javascript输入框,点击确认返回输入框中的值,点击取消返回 null。 + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + return super.onJsPrompt(view, url, message, defaultValue, result); + } + }); + } + + + //WebView的状态 + + /** + * 激活webView为活跃状态,能正常执行网页的响应 + */ + public void setonResume() { + onResume(); + } + + /** + * 当页面被失去焦点被切换到后台不可见状态,需要执行onPause + * 通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。 + */ + public void setOnPause() { + onPause(); + } + + /** + * 当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview + * 它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。 + */ + public void setPauseTimers() { + pauseTimers(); + } + + /** + * 恢复pauseTimers状态 + */ + public void setResumeTimers() { + resumeTimers(); + } + + public void destroyWebView() { + removeView(this); + destroy(); + } + + //WebView的简单操作 + + /** + * 判断网页是否可以回退 + * + * @return + */ + public boolean getCanGoBack() { + return canGoBack(); + } + + /** + * 回退网页 + */ + public void setGoBack() { + goBack(); + } + + /** + * 设置是否可以前进 + * + * @return + */ + public boolean setCanForward() { + return canGoForward(); + } + + /** + * 前进网页 + */ + public void setGoForward() { + goForward(); + } + + /** + * 以当前的index为起始点前进或者后退到历史记录中指定的steps + * 如果steps为负数则为后退,正数则为前进 + * + * @param steps + */ + public void setGoBackOrForward(int steps) { + goBackOrForward(steps); + } + + //清除缓存数据 + + /** + * 清除网页访问留下的缓存 + * 由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序. + * + * @param isClear + */ + public void clearMyCache(boolean isClear) { + clearCache(isClear); + } + + /** + * 清除当前webview访问的历史记录 + * 只会webview访问历史记录里的所有记录除了当前访问记录 + */ + public void clearMyHistory() { + + } + + /** + * 这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据 + */ + public void clearMyFormData() { + clearFormData(); + } + + +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/CompatNestedScrollView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/CompatNestedScrollView.java new file mode 100644 index 0000000..6ebaf09 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/CompatNestedScrollView.java @@ -0,0 +1,86 @@ +package com.share.mvpsdk.widgets; + +import android.content.Context; +import android.support.v4.widget.NestedScrollView; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.share.mvpsdk.utils.AppUtils; +import com.share.mvpsdk.utils.DisplayUtils; +import com.share.mvpsdk.utils.StatusBarUtils; + + +/** + * Created by Horrarndoo on 2017/10/19. + *

+ * 自定义NestedScrollView,主要实现根据滑动距离控制绑定View的alpha值 + */ + +public class CompatNestedScrollView extends NestedScrollView { + private ViewGroup childViewGroup; + private View headView; + private View bindView; + + public CompatNestedScrollView(Context context) { + super(context); + } + + public CompatNestedScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CompatNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + if (getChildCount() > 1) { + throw new IllegalArgumentException("only can 1 child in this view"); + } else { + if (getChildAt(0) instanceof ViewGroup) { + childViewGroup = (ViewGroup) getChildAt(0); + if (childViewGroup != null) { + headView = childViewGroup.getChildAt(0); + } + } else { + throw new IllegalArgumentException("child must be instanceof ViewGroup"); + } + } + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + float alpha = 1.f; + if (headView != null && bindView != null) { + //如果上滑超过toolbar高度,开启伴随动画 + //Logger.e("t = " + t); + //Logger.e("headView.getHeight = " + headView.getHeight()); + float slideValue = t - (DisplayUtils.dp2px(56) + StatusBarUtils.getStatusBarHeight + (AppUtils.getContext())); + + if (slideValue < 0) + slideValue = 0; + + float fraction = slideValue / (headView.getHeight() / 2.f); + if (fraction > 1) { + fraction = 1; + } + + alpha *= fraction; + bindView.setAlpha(alpha); + } + } + + /** + * 绑定要变化Alpha的view + * + * @param view 要变化Alpha的view + */ + public void bindAlphaView(View view) { + bindView = view; + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryChartView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryChartView.java new file mode 100644 index 0000000..b59bed1 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryChartView.java @@ -0,0 +1,719 @@ +package com.share.mvpsdk.widgets; + + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathEffect; +import android.graphics.PathMeasure; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.DecelerateInterpolator; + +import com.share.mvpsdk.R; +import com.share.mvpsdk.utils.DisplayUtils; + + +/** + * 历史记录查询图表 基于pathMeasure+DashPathEffect+属性动画实现 + * 绘制时采取双缓冲绘制 + * + * @author zyw + * @creation 2017-03-06 + */ +public class HistoryChartView extends View { + + private String TAG = "HistoryChartView"; + + TypedArray ta; + + private float MarginTop = 100; + + private float MarginBottom = 100; + + private float MarginLeft = 100; + + private float MarginRight = 100; + + private float mYLabelSize = 50; + + private float mXlabelSize = 35; + + private float mXUnitTextSize; + + private float mYUnitTextSize; + + // 圆半径 + private int circleRadius = 8; + + private int lineStrokeWidth = 3; + + private int dataStrokeWidth = 3; + + private static final long ANIM_DURATION = 1500; + private PathMeasure mRoomTempPathMeasure; + private PathMeasure mTargetTempPathMeasure; + private Path mRoomTempPath; + private Path mTargetTempPath; + private Paint mBmpPaint; + /** + * 柱形绘制进度 + */ + private float mRectFration; + + // X,Y轴的单位长度 + private float Xscale = 20; + private float Yscale = 20; + + // 绘制X轴总长度 + private float xLength; + // 绘制Y轴总长度 + private float yLength; + + // X轴第1个节点的偏移位置 + private float xFirstPointOffset; + + // y轴显示的节点间隔距离 + private int yScaleForData = 1; + + // x轴显示的节点间隔距离 + private int xScaleForData = 1; + + // 画线颜色 + private int lineColor; + + private int roomTempLineColor; + private int targetTempLineColor; + private int powerTimeLineColor; + private int mUnitColor; + + private String mXUnitText; + private String mY1UnitText; + private String mY2UnitText; + + private int mMode = 1;// 从Activity传过来的模式值 1:天 2:周 3:月 4:年 + + // 原点坐标 + private float Xpoint; + private float Ypoint; + + // X,Y轴上面的显示文字 + private String[] Xlabel = {"1", "2", "3", "4", "5", "6", "7"}; + private String[] Ylabel = {"0", "9", "18", "27", "36"}; + private String[] Ylabel2 = {"0", "25", "50", "75", "100"}; + + private final static int X_SCALE_FOR_DATA_DAY = 2; + private final static int X_SCALE_FOR_DATA_WEEK = 1; + private final static int X_SCALE_FOR_DATA_YEAR = 1; + private final static int X_SCALE_FOR_DATA_MOUNTH = 5; + + private final static int DAY_MODE = 0; + private final static int WEEK_MODE = 1; + private final static int MONTH_MODE = 2; + private final static int YEAR_MODE = 3; + + // 曲线数据 + private float[] roomTempDataArray = {15, 15, 15, 15, 15, 15, 15}; + private float[] targetTempDataArray = {16, 16, 16, 16, 16, 16, 16}; + private float[] powerOnTimeDataArray = {100, 100, 100, 100, 100, 100, 100}; + + /** + * 各条柱形图当前top值数组 + */ + private Float[] rectCurrentTops; + + private ValueAnimator mValueAnimator; + + private Paint linePaint; + private Paint targetTempPaint; + private Paint roomTempPaint; + private PathEffect mRoomTempEffect; + private PathEffect mtargetTempEffect; + //定义一个内存中的图片,该图片将作为缓冲区 + Bitmap mCacheBitmap = null; + //定义cacheBitmap上的Canvas对象 + Canvas mCacheCanvas = null; + + public HistoryChartView(Context context, String[] xlabel, String[] ylabel, + float[] roomDataArray) { + super(context); + this.Xlabel = xlabel; + this.Ylabel = ylabel; + this.roomTempDataArray = roomDataArray; + } + + public HistoryChartView(Context context, AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + //Logger.e("HistoryChartView(Context context, AttributeSet attrs, int defStyleAttr)"); + + ta = context.obtainStyledAttributes(attrs, R.styleable.HistoryChartView); + + setDefaultAttrrbutesValue(); + + initPaint(); + + initData(); + + initParams(); + + initPath(); + + ta.recycle(); + } + + public HistoryChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public HistoryChartView(Context context) { + this(context, null); + } + + /** + * 设置显示数据 + * + * @param strAlldata 历史数据全状态 + * @param mode 历史数据模式 + */ + public void setData(String strAlldata, int mode) { + //Logger.e("history chart view strAlldata = " + strAlldata); + String[] allHistroyArray = strAlldata.split("-"); + + String[] arrayRoomTempData = allHistroyArray[0].split(","); + String[] arraySetTempData = allHistroyArray[1].split(","); + String[] arrayPowerTimeData = allHistroyArray[2].split(","); + + mMode = mode; + + initXData(arrayRoomTempData); + + initRoomTempData(arrayRoomTempData); + + initTargetTempData(arraySetTempData); + + initPowerOnTimeData(arrayPowerTimeData); + + initData(); + + initParams(); + + initPath(); + + startAnimation(); + } + + private void initPaint() { + linePaint = new Paint(); + linePaint.setStyle(Paint.Style.STROKE); + linePaint.setAntiAlias(true); + linePaint.setColor(lineColor); + linePaint.setDither(true); + linePaint.setStrokeWidth(lineStrokeWidth); + + roomTempPaint = new Paint(); + roomTempPaint.setStyle(Paint.Style.STROKE); + roomTempPaint.setAntiAlias(true); + roomTempPaint.setColor(roomTempLineColor); + roomTempPaint.setDither(true); + roomTempPaint.setStrokeWidth(dataStrokeWidth); + + targetTempPaint = new Paint(); + targetTempPaint.setStyle(Paint.Style.STROKE); + targetTempPaint.setAntiAlias(true); + targetTempPaint.setColor(targetTempLineColor); + targetTempPaint.setDither(true); + targetTempPaint.setStrokeWidth(dataStrokeWidth); + + mBmpPaint = new Paint(); + } + + /** + * 初始化数据 + */ + private void initData() { + + mRoomTempPath = new Path(); + mTargetTempPath = new Path(); + + rectCurrentTops = new Float[roomTempDataArray.length]; + } + + /** + * 初始化宽高比例等数据 + */ + public void initParams() { + // LogUtils.error(TAG, "initParams"); + Xpoint = MarginLeft; + + xLength = this.getWidth() - MarginLeft - MarginRight + - (MarginRight + MarginLeft) / 16; + yLength = this.getHeight() - MarginTop - MarginBottom; + + Ypoint = this.getHeight() - MarginBottom + mYLabelSize / 3; + Xscale = (xLength - xFirstPointOffset * 2) / (this.Xlabel.length - 1); + Yscale = yLength / (this.Ylabel.length - 1); + } + + /** + * 初始化path + */ + private void initPath() { + initRoomTempPath(roomTempDataArray); + initTargetTempPath(targetTempDataArray); + } + + /** + * 初始化设定温度数据 + * + * @param arraySetTempData 设定温度数据 + */ + private void initTargetTempData(String[] arraySetTempData) { + targetTempDataArray = new float[arraySetTempData.length]; + for (int i = 0; i < arraySetTempData.length; i++) { + if (arraySetTempData[i].length() > 0) { + targetTempDataArray[i] = Float.parseFloat(arraySetTempData[i]); + } + } + } + + /** + * 初始化房间温度数据 + * + * @param arrayRoomTempData 房间温度数据 + */ + private void initRoomTempData(String[] arrayRoomTempData) { + roomTempDataArray = new float[arrayRoomTempData.length]; + for (int i = 0; i < arrayRoomTempData.length; i++) { + if (arrayRoomTempData[i].length() > 0) { + roomTempDataArray[i] = Float.parseFloat(arrayRoomTempData[i]); + // LogUtils.error(TAG, "" + roomTempDataArray[i]); + } + } + } + + /** + * 初始化开机时间数据 + * + * @param arrayPowerTimeData 开机时间数据 + */ + private void initPowerOnTimeData(String[] arrayPowerTimeData) { + powerOnTimeDataArray = new float[arrayPowerTimeData.length]; + for (int i = 0; i < arrayPowerTimeData.length; i++) { + if (arrayPowerTimeData[i].length() > 0) { + powerOnTimeDataArray[i] = Float + .parseFloat(arrayPowerTimeData[i]); + } + } + } + + /** + * 初始化X轴数据 + */ + private void initXData(String[] tempData) { + switch (mMode) { + case DAY_MODE: + xScaleForData = X_SCALE_FOR_DATA_DAY; + setXUnitText(getResources().getString(R.string.history_x_unit_hour)); + break; + case WEEK_MODE: + xScaleForData = X_SCALE_FOR_DATA_WEEK; + setXUnitText(getResources().getString(R.string.history_x_unit_day)); + break; + case MONTH_MODE: + xScaleForData = X_SCALE_FOR_DATA_MOUNTH; + setXUnitText(getResources().getString(R.string.history_x_unit_day)); + break; + case YEAR_MODE: + xScaleForData = X_SCALE_FOR_DATA_YEAR; + setXUnitText(getResources() + .getString(R.string.history_x_unit_month)); + break; + default: + break; + } + + Xlabel = new String[tempData.length]; + for (int i = 0; i < Xlabel.length; i++) { + Xlabel[i] = Integer.toString(i + 1); + } + } + + private void setDefaultAttrrbutesValue() { + float MarginTopPx = ta.getDimension( + R.styleable.HistoryChartView_margin_top, 50); + float MarginBottomPx = ta.getDimension( + R.styleable.HistoryChartView_margin_bottom, 50); + float MarginLeftPx = ta.getDimension( + R.styleable.HistoryChartView_margin_left, 50); + float MarginRightPx = ta.getDimension( + R.styleable.HistoryChartView_margin_right, 50); + + float yLabelSizePx = ta.getDimension( + R.styleable.HistoryChartView_ylabel_text_size, 30); + float xlabelSizePx = ta.getDimension( + R.styleable.HistoryChartView_xlabel_text_size, 20); + float xUnitSizePx = ta.getDimension( + R.styleable.HistoryChartView_x_unit_text_size, 30); + float yUnitSizePx = ta.getDimension( + R.styleable.HistoryChartView_y_unit_text_size, 30); + + float xFirstPointOffsetPx = ta.getDimension( + R.styleable.HistoryChartView_x_first_point_offset, 30); + float lineStrokeWidthPx = ta.getDimension( + R.styleable.HistoryChartView_line_stroke_width, 5); + float dataStrokeWidthPx = ta.getDimension( + R.styleable.HistoryChartView_data_stroke_width, 5); + float circleRadiusPx = ta.getDimension( + R.styleable.HistoryChartView_circle_radius, 6); + + xFirstPointOffset = DisplayUtils.px2sp(xFirstPointOffsetPx); + + MarginTop = DisplayUtils.px2dp(MarginTopPx); + MarginBottom = DisplayUtils.px2dp(MarginBottomPx); + MarginLeft = DisplayUtils.px2dp(MarginLeftPx); + MarginRight = DisplayUtils.px2dp(MarginRightPx); + + mYLabelSize = DisplayUtils.px2sp(yLabelSizePx); + mXlabelSize = DisplayUtils.px2sp(xlabelSizePx); + + mXUnitTextSize = DisplayUtils.px2sp(xUnitSizePx); + mYUnitTextSize = DisplayUtils.px2sp(yUnitSizePx); + + lineStrokeWidth = DisplayUtils.px2sp(lineStrokeWidthPx); + dataStrokeWidth = DisplayUtils.px2sp(dataStrokeWidthPx); + circleRadius = DisplayUtils.px2sp(circleRadiusPx); + + lineColor = ta.getColor(R.styleable.HistoryChartView_line_color, + getResources().getColor(R.color.light_yellow)); + roomTempLineColor = ta.getColor( + R.styleable.HistoryChartView_first_data_line_color, + getResources().getColor(R.color.indoor_temp)); + targetTempLineColor = ta.getColor( + R.styleable.HistoryChartView_second_data_line_color, + getResources().getColor(R.color.setpoint_temp)); + + powerTimeLineColor = ta.getColor( + R.styleable.HistoryChartView_rect_background_color, + getResources().getColor(R.color.power_time)); + + mUnitColor = ta.getColor(R.styleable.HistoryChartView_unit_color, + getResources().getColor(R.color.light_grey)); + + mXUnitText = ta.getString(R.styleable.HistoryChartView_x_unit_text); + mY1UnitText = ta.getString(R.styleable.HistoryChartView_y1_unit_text); + mY2UnitText = ta.getString(R.styleable.HistoryChartView_y2_unit_text); + } + + /** + * 设置X轴单位符号 + * + * @param xUnit x轴单位符号 + */ + public void setXUnitText(String xUnit) { + mXUnitText = xUnit; + } + + /** + * 绘制单位符号 + * + * @param canvas canvas + */ + private void drawUnit(Canvas canvas) { + Paint p = new Paint(); + p.setAntiAlias(true); + p.setStrokeWidth(dataStrokeWidth); + p.setColor(mUnitColor); + + drawXUnit(canvas, p); + drawY1Unit(canvas, p); + drawY2Unit(canvas, p); + } + + // 画横轴 + private void drawXLine(Canvas canvas, Paint p) { + p.setColor(getResources().getColor(R.color.light_yellow)); + canvas.drawLine(Xpoint, Ypoint, xLength + MarginLeft, Ypoint, p); + } + + // 画灰色横轴 + private void drawGreyXLine(Canvas canvas, Paint p) { + p.setColor(getResources().getColor(R.color.grey_line)); + float startX = Xpoint + MarginLeft / 4; + // 纵向 + for (int i = yScaleForData; (yLength - i * Yscale) >= 0; i += yScaleForData) { + float startY = Ypoint - i * Yscale; + canvas.drawLine(startX - MarginLeft / 4, startY, xLength + + MarginLeft, startY, p); + } + } + + // 画数据 + private void drawData(Canvas canvas, float[] data, int dataColor) { + Paint p = new Paint(); + p.setAntiAlias(true); + p.setStrokeWidth(dataStrokeWidth); + p.setTextSize(mXlabelSize); + // 横向 + for (int i = 0; i < Xlabel.length; i++) { + int xLableInt = Integer.parseInt(Xlabel[i]); + float startX = Xpoint + i * Xscale + xFirstPointOffset; + if (xLableInt % xScaleForData == 0) { + p.setColor(lineColor); + canvas.drawText(this.Xlabel[i], startX - mXlabelSize / 3, + Ypoint + mXlabelSize * 3 / 2, p); + } + p.setColor(dataColor); + canvas.drawCircle(startX, getDataY(data[i], Ylabel), circleRadius, + p); + } + + p.setTextSize(mYLabelSize); + // 纵向 + for (int i = 0; (yLength - i * Yscale) >= 0; i += yScaleForData) { + p.setColor(lineColor); + canvas.drawText(this.Ylabel[i], MarginLeft / 4, + getDataY(Float.valueOf(Ylabel[i]), Ylabel) + mYLabelSize + / 3, p); + canvas.drawText(this.Ylabel2[i], this.getWidth() - MarginLeft, + getDataY(Float.valueOf(Ylabel2[i]), Ylabel2) + mYLabelSize + / 3, p); + } + } + + // 获取room temp绘线Path数据 + private void initRoomTempPath(float[] data) { + mRoomTempPath.reset(); + // Path path = new Path(); + float pointX; + float pointY; + // 横向 + mRoomTempPath.moveTo(Xpoint + xFirstPointOffset, + getDataY(data[0], Ylabel)); + mRoomTempPath.moveTo(Xpoint + xFirstPointOffset, + getDataY(data[0], Ylabel)); + for (int i = 0; i < Xlabel.length; i++) { + float startX = Xpoint + i * Xscale + xFirstPointOffset; + // 绘制数据连线 + if (i != 0) { + pointX = Xpoint + (i - 1) * Xscale + xFirstPointOffset; + pointY = getDataY(data[i - 1], Ylabel); + mRoomTempPath.lineTo(pointX, pointY); + } + if (i == Xlabel.length - 1) { + pointX = startX; + pointY = getDataY(data[i], Ylabel); + mRoomTempPath.lineTo(pointX, pointY); + } + } + mRoomTempPathMeasure = new PathMeasure(mRoomTempPath, false); + } + + /** + * 获取target temp绘线Path数据 + * + * @param data target temp绘线Path数据 + */ + private void initTargetTempPath(float[] data) { + mTargetTempPath.reset(); + float pointX; + float pointY; + // 横向 + mTargetTempPath.moveTo(Xpoint + xFirstPointOffset, + getDataY(data[0], Ylabel)); + for (int i = 0; i < Xlabel.length; i++) { + float startX = Xpoint + i * Xscale + xFirstPointOffset; + // 绘制数据连线 + if (i != 0) { + pointX = Xpoint + (i - 1) * Xscale + xFirstPointOffset; + pointY = getDataY(data[i - 1], Ylabel); + mTargetTempPath.lineTo(pointX, pointY); + } + if (i == Xlabel.length - 1) { + pointX = startX; + pointY = getDataY(data[i], Ylabel); + mTargetTempPath.lineTo(pointX, pointY); + } + } + mTargetTempPathMeasure = new PathMeasure(mTargetTempPath, false); + } + + // 绘制矩形图 + private void drawRect(Canvas canvas, float[] data, int dataColor) { + Paint p = new Paint(); + float left; + float top; + float right; + float bottom; + float stopY = getDataY(Float.parseFloat(Ylabel[Ylabel.length - 1]), + Ylabel);// 灰色线Y轴位置 + float rectYScale = (Ypoint - stopY) / 100; + + p.setAntiAlias(true); + p.setStrokeWidth(dataStrokeWidth); + p.setColor(dataColor); + + // 横向 + for (int i = 0; i < Xlabel.length; i++) { + // 绘制柱形图 + if (i != 0) { + left = Xpoint + (i - 1) * Xscale + xFirstPointOffset + Xscale + / 6; + top = Ypoint - data[i - 1] * rectYScale + lineStrokeWidth;// 要绘制的rect最终top值 + // 起点top + (起点top - 终点top) * mRectFration + rectCurrentTops[i] = Ypoint - (Ypoint - top) * mRectFration;// 根据fraction动态更新top值 + right = left + Xscale * 4 / 6; + bottom = Ypoint; + canvas.drawRect(left, rectCurrentTops[i], right, bottom, p);// + // 每次valueAnimator更新时重绘最新top值 + } + } + } + + private void drawY1Unit(Canvas canvas, Paint p) { + int maxYLabelValue = Integer.valueOf(Ylabel[Ylabel.length - 1]); + p.setTextSize(mYUnitTextSize); + float textWidth = p.measureText(mY1UnitText); + canvas.drawText(mY1UnitText, MarginLeft / 2 - textWidth / 2, + getDataY(maxYLabelValue, Ylabel) - mYLabelSize - mYLabelSize + / 5, p); + } + + private void drawY2Unit(Canvas canvas, Paint p) { + int maxYLabel2Value = Integer.valueOf(Ylabel2[Ylabel2.length - 1]); + p.setTextSize(mYUnitTextSize); + float textWidth = p.measureText(mY2UnitText); + canvas.drawText(mY2UnitText, this.getWidth() - MarginRight / 2 + - textWidth * 3 / 4, getDataY(maxYLabel2Value, Ylabel2) + - mYLabelSize - mYLabelSize / 5, p); + } + + private void drawXUnit(Canvas canvas, Paint p) { + p.setTextSize(mXUnitTextSize); + float textWidth = p.measureText(mXUnitText); + canvas.drawText(mXUnitText, this.getWidth() / 2 - textWidth / 2, Ypoint + + mXlabelSize * 3 + mXlabelSize / 5, p); + } + + /** + * 获取data对应绘制Y点值 + */ + private float getDataY(float dataY, String[] Ylabel) { + float y0 = 0; + float y1 = 0; + try { + y0 = Float.parseFloat(Ylabel[0]); + y1 = Float.parseFloat(Ylabel[1]); + } catch (Exception e) { + return 0; + } + try { + return Ypoint - ((dataY - y0) * Yscale / (y1 - y0)); + } catch (Exception e) { + return 0; + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + super.onLayout(changed, left, top, right, bottom); + // LogUtils.error(TAG, "onLayout"); + initParams(); + + if (onViewLayoutListener != null) { + onViewLayoutListener.onLayoutSuccess(); + } + + //创建一个与该View相同大小的缓冲区 + mCacheBitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config + .ARGB_8888); + mCacheCanvas = new Canvas(); + //设置cacheCanvas将会绘制到内存中cacheBitmap上 + mCacheCanvas.setBitmap(mCacheBitmap); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + mCacheCanvas.drawColor(Color.BLACK); + + drawGreyXLine(mCacheCanvas, linePaint); + + drawUnit(mCacheCanvas); + + if (powerOnTimeDataArray.length > 1) { + drawRect(mCacheCanvas, powerOnTimeDataArray, powerTimeLineColor); + } + + mCacheCanvas.drawPath(mRoomTempPath, roomTempPaint); + + if (roomTempDataArray.length > 1) { + drawData(mCacheCanvas, roomTempDataArray, roomTempLineColor); + } + + mCacheCanvas.drawPath(mTargetTempPath, targetTempPaint); + + if (targetTempDataArray.length > 1) { + drawData(mCacheCanvas, targetTempDataArray, targetTempLineColor); + } + + drawXLine(mCacheCanvas, linePaint); + + //将cacheBitmap绘制到该View组件 + canvas.drawBitmap(mCacheBitmap, 0, 0, mBmpPaint); + } + + /** + * 开启动画 + */ + private void startAnimation() { + if (mValueAnimator != null) { + mValueAnimator.cancel(); + } + final float targetTempLength = mTargetTempPathMeasure.getLength(); + final float roomTempLength = mRoomTempPathMeasure.getLength(); + mValueAnimator = ValueAnimator.ofFloat(1, 0); + mValueAnimator.setDuration(ANIM_DURATION); + // 减速插值器 + mValueAnimator.setInterpolator(new DecelerateInterpolator()); + mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float fraction = (Float) animation.getAnimatedValue(); + // 更新mtargetTempEffect + mtargetTempEffect = new DashPathEffect(new float[]{ + targetTempLength, targetTempLength}, fraction + * targetTempLength); + targetTempPaint.setPathEffect(mtargetTempEffect); + // 更新mRoomTempEffect + mRoomTempEffect = new DashPathEffect(new float[]{ + roomTempLength, roomTempLength}, fraction + * roomTempLength); + roomTempPaint.setPathEffect(mRoomTempEffect); + // 更新rect绘制fraction进度 + mRectFration = 1 - fraction;// fraction是1->0 我们需要的柱形图绘制比例是0->1 + postInvalidate(); + } + }); + mValueAnimator.start(); + } + + public interface OnViewLayoutListener { + void onLayoutSuccess(); + } + + public void setOnViewLayoutListener( + OnViewLayoutListener onViewLayoutListener) { + this.onViewLayoutListener = onViewLayoutListener; + } + + private OnViewLayoutListener onViewLayoutListener; +} + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryModeView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryModeView.java new file mode 100644 index 0000000..ef019af --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryModeView.java @@ -0,0 +1,107 @@ +package com.share.mvpsdk.widgets; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.share.mvpsdk.R; +import com.share.mvpsdk.utils.ResourcesUtils; + + +/** + * 历史数据查询界面模式自定义View + * @author zyw + * @creation 2016-12-26 + */ +public class HistoryModeView extends LinearLayout { + private LinearLayout ll_click_view; + private TextView tv_mode; + private float mSelectTextSize; + private float mUnSelectTextSize; + + public HistoryModeView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + ll_click_view = (LinearLayout) View.inflate(context, R.layout.sub_history_click_view, this); + tv_mode = (TextView) findViewById(R.id.tv_click_view); + + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HistoryModeView); + + setText(ta.getString(R.styleable.HistoryModeView_android_text)); + setSelectTextSize(ta.getDimension(R.styleable.HistoryModeView_select_text_size, 16)); + setUnSelectTextSize(ta.getDimension(R.styleable.HistoryModeView_un_select_text_size, 12)); + setViewTextSize(); + + ta.recycle(); + } + + public HistoryModeView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + //init(context); + } + + public HistoryModeView(Context context) { + this(context, null); + //init(context); + } + + /** + * 设置控件选中状态 + * @param isSelected 控件是否选中 + */ + public void setHistoryViewSelected(boolean isSelected){ + ll_click_view.setSelected(isSelected); + setViewTextSize(); + if(isSelected){ + setTextColor(ResourcesUtils.getColor(R.color.light_yellow)); + }else { + setTextColor(ResourcesUtils.getColor(R.color.light_grey)); + } + } + + /** + * 设置控件文字 + * @param str 控件文字 + */ + public void setText(String str){ + tv_mode.setText(str); + } + + /** + * 设置控件文字颜色 + * @param color 控件文字颜色 + */ + public void setTextColor(int color){ + tv_mode.setTextColor(color); + } + + /** + * 设置根据view的选中状态更新text字号 + */ + public void setViewTextSize() { + if(ll_click_view.isSelected()){ + tv_mode.getPaint().setTextSize(mSelectTextSize); + }else{ + tv_mode.getPaint().setTextSize(mUnSelectTextSize); + } + } + + /** + * 设置view选中的字体大小 + * @param selectSize 选中字体大小 + */ + public void setSelectTextSize(float selectSize){ + mSelectTextSize = selectSize; + } + + /** + * 设置view没有选中的字体大小 + * @param unSelectSize 非选中字体大小 + */ + public void setUnSelectTextSize(float unSelectSize){ + mUnSelectTextSize = unSelectSize; + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryTipView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryTipView.java new file mode 100644 index 0000000..f9a28ae --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/HistoryTipView.java @@ -0,0 +1,95 @@ +package com.share.mvpsdk.widgets; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.share.mvpsdk.R; + + +/** + * 历史数据查询界面Tips自定义View + * @author zyw + * @creation 2016-12-27 + */ +public class HistoryTipView extends LinearLayout { + private TextView tv_tips1; + private TextView tv_tips2; + private ImageView iv_tips; + private TypedArray ta; + + public HistoryTipView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + ta = context.obtainStyledAttributes(attrs, R.styleable.HistoryTipsView); + init(context); + ta.recycle(); + } + + public HistoryTipView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + //init(context); + } + + public HistoryTipView(Context context) { + this(context, null); + //init(context); + } + + public void init(Context context){ + View.inflate(context, R.layout.sub_history_tips, this); + + tv_tips1 = (TextView) findViewById(R.id.tv_tip1); + tv_tips2 = (TextView) findViewById(R.id.tv_tip2); + iv_tips = (ImageView) findViewById(R.id.iv_tip); + + setTipOneText(ta.getString(R.styleable.HistoryTipsView_tip_one_text)); + setTipTwoText(ta.getString(R.styleable.HistoryTipsView_tip_two_text)); + setTextColor(ta.getColor(R.styleable.HistoryTipsView_android_textColor, Color.WHITE)); + setTextSize(ta.getDimension(R.styleable.HistoryTipsView_android_textSize, 12)); + setBackground(ta.getResourceId(R.styleable.HistoryTipsView_android_src, 0)); + } + + /** + * 设置控件Tip1文字 + * @param str + */ + public void setTipOneText(String str){ + tv_tips1.setText(str); + } + + /** + * 设置控件Tip2s文字 + * @param str + */ + public void setTipTwoText(String str){ + tv_tips2.setText(str); + } + + /** + * 设置控件Tip1 2文字颜色 + * @param color + */ + public void setTextColor(int color){ + tv_tips1.setTextColor(color); + tv_tips2.setTextColor(color); + } + + /** + * 设置根据view的选中状态更新text字号 + */ + public void setTextSize(float textSize) { + tv_tips1.getPaint().setTextSize(textSize); + tv_tips2.getPaint().setTextSize(textSize); + } + + public void setBackground(int resId){ + iv_tips.setImageResource(resId); + } +} + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingImageView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingImageView.java new file mode 100644 index 0000000..d4dd66b --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingImageView.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2014 Albert Grobas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.share.mvpsdk.widgets; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.util.AttributeSet; +import android.widget.ImageView; + +import com.share.mvpsdk.R; + + +/** + * Created by Horrarndoo on 2017/9/7. + *

+ * 自定义可以背景滚动的ImageView + */ +public class MovingImageView extends ImageView { + + private float canvasWidth, canvasHeight; + private float imageWidth, imageHeight; + private float offsetWidth, offsetHeight; + /** + * 移动类型 + */ + private int movementType; + + /** + * 限定最大比值 + * canvasHeight/drawableHeight 或者 canvasWidth/drawableWidth + */ + private float maxRelativeSize; + /** + * 最小相对偏移值,图片最起码可以位移图*0.2的距离 + */ + private float minRelativeOffset; + private int mSpeed; + private long startDelay; + private int mRepetitions; + private boolean loadOnCreate;//load完毕后是否移动 + + private MovingViewAnimator mAnimator; + + public MovingImageView(Context context) { + this(context, null); + } + + public MovingImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public MovingImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, + R.styleable.MovingImageView, defStyle, 0); + + try { + maxRelativeSize = ta.getFloat(R.styleable.MovingImageView_miv_max_relative_size, 3.0f); + minRelativeOffset = ta.getFloat(R.styleable.MovingImageView_miv_min_relative_offset, + 0.2f); + mSpeed = ta.getInt(R.styleable.MovingImageView_miv_speed, 50); + mRepetitions = ta.getInt(R.styleable.MovingImageView_miv_repetitions, -1); + startDelay = ta.getInt(R.styleable.MovingImageView_miv_start_delay, 0); + loadOnCreate = ta.getBoolean(R.styleable.MovingImageView_miv_load_on_create, true); + } finally { + ta.recycle(); + } + + init(); + } + + private void init() { + super.setScaleType(ScaleType.MATRIX); + mAnimator = new MovingViewAnimator(this); + } + + /** + * 更新canvas size + * + * @param w new width. + * @param h new height. + * @param oldW old width. + * @param oldH old height. + */ + @Override + protected void onSizeChanged(int w, int h, int oldW, int oldH) { + super.onSizeChanged(w, h, oldW, oldH); + canvasWidth = (float) w - (float) (getPaddingLeft() + getPaddingRight()); + canvasHeight = (float) h - (float) (getPaddingTop() + getPaddingBottom()); + updateAll(); + } + + private void updateAll() { + if (getDrawable() != null) { + updateImageSize(); + updateOffsets(); + updateAnimatorValues(); + } + } + + /** + * 更新图片Size + */ + private void updateImageSize() { + imageWidth = getDrawable().getIntrinsicWidth();//获取图片高度 + imageHeight = getDrawable().getIntrinsicHeight();//获取图片宽度 + } + + /** + * 更新偏移量,确定动画范围 + */ + private void updateOffsets() { + float minSizeX = imageWidth * minRelativeOffset; + float minSizeY = imageHeight * minRelativeOffset; + offsetWidth = (imageWidth - canvasWidth - minSizeX) > 0 ? imageWidth - canvasWidth : 0; + offsetHeight = (imageHeight - canvasHeight - minSizeY) > 0 ? imageHeight - canvasHeight : 0; + } + + /** + * 更新动画基本数据 + */ + private void updateAnimatorValues() { + if (canvasHeight == 0 && canvasWidth == 0) + return; + + float scale = calculateTypeAndScale(); + if (scale == 0) + return; + + float w = (imageWidth * scale) - canvasWidth; + float h = (imageHeight * scale) - canvasHeight; + + mAnimator.updateValues(movementType, w, h); + mAnimator.setStartDelay(startDelay); + mAnimator.setSpeed(mSpeed); + mAnimator.setRepetition(mRepetitions); + + if (loadOnCreate) { + startMoving(); + } + } + + /** + * 设置最佳的运动类型 + * 计算缩放比例 + * + * @return image scale. + */ + private float calculateTypeAndScale() { + movementType = MovingViewAnimator.AUTO_MOVE; + float scale = 1f; + float scaleByImage = Math.max(imageWidth / canvasWidth, imageHeight / canvasHeight); + Matrix matrix = new Matrix(); + + if (offsetWidth == 0 && offsetHeight == 0) {//图片太小,无法动画,需要放大 + //画布宽度/图片宽度 + float sW = canvasWidth / imageWidth; + //画布高度/图片高度 + float sH = canvasHeight / imageHeight; + + if (sW > sH) { + scale = Math.min(sW, maxRelativeSize);//限定最大缩放值 + matrix.setTranslate((canvasWidth - imageWidth * scale) / 2f, 0); + movementType = MovingViewAnimator.VERTICAL_MOVE;//垂直移动 + + } else if (sW < sH) { + scale = Math.min(sH, maxRelativeSize);//限定最大缩放值 + matrix.setTranslate(0, (canvasHeight - imageHeight * scale) / 2f); + movementType = MovingViewAnimator.HORIZONTAL_MOVE;//水平移动 + + } else { + scale = Math.max(sW, maxRelativeSize);//限定最大缩放值 + movementType = (scale == sW) ? MovingViewAnimator.NONE_MOVE : + MovingViewAnimator.DIAGONAL_MOVE;//对角线移动 + } + } else if (offsetWidth == 0) {//宽度太小,无法执行水平动画,放大宽度 + scale = canvasWidth / imageWidth; + movementType = MovingViewAnimator.VERTICAL_MOVE; + + } else if (offsetHeight == 0) {//高度太小,无法执行垂直动画,放大高度 + scale = canvasHeight / imageHeight;//求出画布高度和图片高度的比值用于确定画布起始坐标 + movementType = MovingViewAnimator.HORIZONTAL_MOVE; + + } else if (scaleByImage > maxRelativeSize) {//图片太大,根据最大比值设定图片缩放值 + scale = maxRelativeSize / scaleByImage; + if (imageWidth * scale < canvasWidth || imageHeight * scale < canvasHeight) { + scale = Math.max(canvasWidth / imageWidth, canvasHeight / imageHeight); + } + } + + matrix.preScale(scale, scale); + setImageMatrix(matrix); + return scale; + } + + /** + * 禁止设置ScaleType + * + * @param scaleType + */ + @Override + @Deprecated + public void setScaleType(ScaleType scaleType) { + //super.setScaleType(scaleType); + } + + @Override + public void setImageResource(int resId) { + super.setImageResource(resId); + updateAll(); + } + + @Override + public void setImageURI(Uri uri) { + super.setImageURI(uri); + updateAll(); + } + + @Override + public void setImageDrawable(Drawable drawable) { + super.setImageDrawable(drawable); + updateAll(); + } + + @Override + public void setImageBitmap(Bitmap bm) { + super.setImageBitmap(bm); + updateAll(); + } + + /** + * 获取animator + * + * @return + */ + public MovingViewAnimator getMovingAnimator() { + return mAnimator; + } + + public float getMaxRelativeSize() { + return maxRelativeSize; + } + + public void setMaxRelativeSize(float max) { + maxRelativeSize = max; + updateAnimatorValues(); + } + + public float getMinRelativeOffset() { + return minRelativeOffset; + } + + public void setMinRelativeOffset(float min) { + minRelativeOffset = min; + updateAnimatorValues(); + } + + public boolean isLoadOnCreate() { + return loadOnCreate; + } + + public void setLoadOnCreate(boolean loadOnCreate) { + this.loadOnCreate = loadOnCreate; + } + + /** + * 开始移动 + * 默认不停的移动 + */ + public void startMoving() { + startMoving(-1); + } + + /** + * 开始移动 + * + * @param repetition 循环模式 + */ + public void startMoving(int repetition) { + mAnimator.setRepetition(repetition); + mAnimator.start(); + } + + /** + * 恢复移动 + */ + public void resumeMoving() { + mAnimator.resume(); + } + + /** + * 暂停移动 + */ + public void pauseMoving() { + mAnimator.pause(); + } + + /** + * 停止移动 + */ + public void stopMoving() { + mAnimator.stop(); + } + + /** + * 获取当前状态 + * + * @return + */ + public MovingViewAnimator.MovingState getMovingState() { + return mAnimator.getMovingState(); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingViewAnimator.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingViewAnimator.java new file mode 100644 index 0000000..2fad1d0 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/MovingViewAnimator.java @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2014 Albert Grobas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.share.mvpsdk.widgets; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.annotation.TargetApi; +import android.os.Build; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Horrarndoo on 2017/9/7. + *

+ * 滚动view动画 + */ +public class MovingViewAnimator { + /** + * 水平移动 + */ + public static final int HORIZONTAL_MOVE = 1; + /** + * 垂直移动 + */ + public static final int VERTICAL_MOVE = 2; + /** + * 对角线移动 + */ + public static final int DIAGONAL_MOVE = 3; + /** + * 自动移动 + */ + public static final int AUTO_MOVE = 0; + /** + * 不移动 + */ + public static final int NONE_MOVE = -1; + + private AnimatorSet mAnimatorSet; + private View mView; + + private boolean isRunning; + private int currentLoop; + private boolean infiniteRepetition = true; + private ArrayList pathDistances; + + private int loopCount = -1; + private int movementType; + private float offsetWidth, offsetHeight; + private int mSpeed = 50; + private long mDelay = 0; + private Interpolator mInterpolator; + + private MovingState currentState = MovingState.stop; + + public enum MovingState { + stop, + moving, + pause + } + + private Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(final Animator animation) { + //super.onAnimationEnd(animation); + //运行在主线程 + mView.post((new Runnable() { + public void run() { + if (isRunning) { + if (infiniteRepetition) { + mAnimatorSet.start(); + } else { + currentLoop--; + if (currentLoop > 0) { + mAnimatorSet.start(); + } + } + } + } + })); + } + }; + + public MovingViewAnimator(View imgView) { + mView = imgView; + isRunning = false; + mAnimatorSet = new AnimatorSet(); + pathDistances = new ArrayList<>(); + mInterpolator = new AccelerateDecelerateInterpolator(); + } + + public MovingViewAnimator(View imgView, int type, float width, float height) { + this(imgView); + updateValues(type, width, height); + } + + private void init() { + setUpAnimator(); + setUpValues(); + } + + /** + * 根据移动类型设置不同的动画 + */ + private void setUpAnimator() { + AnimatorSet animatorSet = new AnimatorSet(); + pathDistances.clear(); + + switch (movementType) { + case HORIZONTAL_MOVE: + animatorSet.playSequentially(createHorizontalAnimator(0, offsetWidth), + createHorizontalAnimator(offsetWidth, 0)); + break; + case VERTICAL_MOVE: + animatorSet.playSequentially(createVerticalAnimator(0, offsetHeight), + createVerticalAnimator(offsetHeight, 0)); + break; + case DIAGONAL_MOVE: + animatorSet.playSequentially(createDiagonalAnimator(0, offsetWidth, 0, + offsetHeight), + createDiagonalAnimator(offsetWidth, 0, offsetHeight, 0)); + break; + case AUTO_MOVE: + animatorSet.playSequentially( + createVerticalAnimator(0, offsetHeight), + createDiagonalAnimator(0, offsetWidth, offsetHeight, 0), + createHorizontalAnimator(offsetWidth, 0), + createDiagonalAnimator(0, offsetWidth, 0, offsetHeight), + createHorizontalAnimator(offsetWidth, 0), + createVerticalAnimator(offsetHeight, 0)); + } + + if (mAnimatorSet != null) { + mAnimatorSet.removeAllListeners(); + stop(); + } + mAnimatorSet = animatorSet; + } + + /** + * 设置参数数据 + */ + private void setUpValues() { + setSpeed(mSpeed); + setStartDelay(mDelay); + setRepetition(loopCount); + setInterpolator(mInterpolator); + } + + private void setListener() { + mAnimatorSet.addListener(animatorListener); + } + + /** + * 更新动画值. + * + * @param type + * @param w + * @param h + */ + public void updateValues(int type, float w, float h) { + this.movementType = type; + this.offsetWidth = w; + this.offsetHeight = h; + init(); + } + + public void setMovementType(int type) { + updateValues(type, offsetWidth, offsetHeight); + } + + public void setOffsets(float w, float h) { + updateValues(movementType, w, h); + } + + public void start() { + //Log.e("tag", "start."); + if (movementType != NONE_MOVE) { + isRunning = true; + if (!infiniteRepetition) + currentLoop = loopCount; + setListener(); + mAnimatorSet.start(); + currentState = MovingState.moving; + } + } + + public void cancel() { + if (isRunning) { + mAnimatorSet.removeListener(animatorListener); + mAnimatorSet.cancel(); + currentState = MovingState.stop; + } + } + + @TargetApi(19) + public void pause() { + //Log.e("tag", "pause."); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) + return; + + if (mAnimatorSet.isStarted()) { + mAnimatorSet.pause(); + currentState = MovingState.pause; + } + } + + @TargetApi(19) + public void resume() { + //Log.e("tag", "resume."); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) + return; + + if (mAnimatorSet.isPaused()) { + mAnimatorSet.resume(); + currentState = MovingState.moving; + } + } + + public void stop() { + //Log.e("tag", "stop."); + isRunning = false; + mAnimatorSet.removeListener(animatorListener); + mAnimatorSet.end(); + mView.clearAnimation(); + currentState = MovingState.stop; + } + + /** + * 设置重复模式 + * + * @param repetition repetition < 0 循环播放 + * repetition > 0 循环repetition次 + */ + public void setRepetition(int repetition) { + if (repetition < 0) + infiniteRepetition = true; + else { + loopCount = repetition; + currentLoop = loopCount; + infiniteRepetition = false; + } + } + + public Builder addCustomMovement() { + return new Builder(); + } + + public void clearCustomMovement() { + init(); + start(); + } + + public int getMovementType() { + return movementType; + } + + public int getRemainingRepetitions() { + return (infiniteRepetition) ? -1 : currentLoop; + } + + public void setInterpolator(Interpolator interpolator) { + mInterpolator = interpolator; + mAnimatorSet.setInterpolator(interpolator); + } + + /** + * 设置动画播放之前的延时时间 + * + * @param time + */ + public void setStartDelay(long time) { + mDelay = time; + mAnimatorSet.setStartDelay(time); + } + + /** + * 设置每个动画对应的持续时间 + * + * @param speed + */ + public void setSpeed(int speed) { + mSpeed = speed; + List listAnimator = mAnimatorSet.getChildAnimations(); + for (int i = 0; i < listAnimator.size(); i++) { + Animator a = listAnimator.get(i); + a.setDuration(parseSpeed(pathDistances.get(i))); + } + } + + /** + * 将速度设置值转换成秒 + * + * @param distance + * @return + */ + private long parseSpeed(float distance) { + return (long) ((distance / (float) mSpeed) * 1000f); + } + + /** + * 创建水平移动动画 + * + * @param startValue + * @param endValue + * @return + */ + private ObjectAnimator createHorizontalAnimator(float startValue, float endValue) { + pathDistances.add(Math.abs(startValue - endValue)); + return createObjectAnimation("scrollX", startValue, endValue); + } + + /** + * 创建垂直移动动画 + * + * @param startValue + * @param endValue + * @return + */ + private ObjectAnimator createVerticalAnimator(float startValue, float endValue) { + pathDistances.add(Math.abs(startValue - endValue)); + return createObjectAnimation("scrollY", startValue, endValue); + } + + /** + * 创建对角线移动动画 + * + * @param startW + * @param endW + * @param startH + * @param endH + * @return + */ + private ObjectAnimator createDiagonalAnimator(float startW, float endW, float startH, float + endH) { + float diagonal = Pythagoras(Math.abs(startW - endW), Math.abs(startH - endH)); + pathDistances.add(diagonal); + PropertyValuesHolder pvhX = createPropertyValuesHolder("scrollX", startW, endW); + PropertyValuesHolder pvhY = createPropertyValuesHolder("scrollY", startH, endH); + return ObjectAnimator.ofPropertyValuesHolder(mView, pvhX, pvhY); + } + + private ObjectAnimator createObjectAnimation(String prop, float startValue, float endValue) { + return ObjectAnimator.ofInt(mView, prop, (int) startValue, (int) endValue); + } + + private PropertyValuesHolder createPropertyValuesHolder(String prop, float startValue, float + endValue) { + return PropertyValuesHolder.ofInt(prop, (int) startValue, (int) endValue); + } + + private static float Pythagoras(float a, float b) { + return (float) Math.sqrt((a * a) + (b * b)); + } + + /** + * 自定义自动移动方式 + */ + public class Builder { + + private ArrayList mList; + + private Builder() { + mList = new ArrayList<>(); + pathDistances.clear(); + } + + public Builder addHorizontalMoveToRight() { + mList.add(createHorizontalAnimator(0, offsetWidth)); + return this; + } + + public Builder addHorizontalMoveToLeft() { + mList.add(createHorizontalAnimator(offsetWidth, 0)); + return this; + } + + public Builder addVerticalMoveToDown() { + mList.add(createVerticalAnimator(0, offsetHeight)); + return this; + } + + public Builder addVerticalMoveToUp() { + mList.add(createVerticalAnimator(offsetHeight, 0)); + return this; + } + + public Builder addDiagonalMoveToDownRight() { + mList.add(createDiagonalAnimator(0, offsetWidth, 0, offsetHeight)); + return this; + } + + public Builder addDiagonalMoveToDownLeft() { + mList.add(createDiagonalAnimator(offsetWidth, 0, 0, offsetHeight)); + return this; + } + + public Builder addDiagonalMoveToUpRight() { + mList.add(createDiagonalAnimator(0, offsetWidth, offsetHeight, 0)); + return this; + } + + public Builder addDiagonalMoveToUpLeft() { + mList.add(createDiagonalAnimator(offsetWidth, 0, offsetHeight, 0)); + return this; + } + + public void start() { + mAnimatorSet.removeAllListeners(); + stop(); + mAnimatorSet = new AnimatorSet(); + mAnimatorSet.playSequentially(mList); + setListener(); + setUpValues(); + MovingViewAnimator.this.start(); + } + } + + /** + * 获取当前状态 + * + * @return + */ + public MovingState getMovingState() { + return currentState; + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/NestedScrollWebView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/NestedScrollWebView.java new file mode 100644 index 0000000..9cc9691 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/NestedScrollWebView.java @@ -0,0 +1,168 @@ +package com.share.mvpsdk.widgets; + +import android.content.Context; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.NestedScrollingChild; +import android.support.v4.view.NestedScrollingChildHelper; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.webkit.WebView; + +/** + * Created by Horrarndoo on 2017/9/27. + *

+ * 继承原生WebView,目的是为了和AppBarLayout、CollapsingToolbarLayout等Android Design Support Library控件配合使用。 + *

+ * 避免AppBarLayout+NestSrollView+WebView嵌套导致的WebView高度判断异常导致WebView跳转后高度异常的问题; + */ +public class NestedScrollWebView extends WebView implements NestedScrollingChild { + + public static final String TAG = NestedScrollWebView.class.getSimpleName(); + + private int mLastMotionY; + + private final int[] mScrollOffset = new int[2]; + private final int[] mScrollConsumed = new int[2]; + + private int mNestedYOffset; + private boolean mChange; + + private NestedScrollingChildHelper mChildHelper; + + public NestedScrollWebView(Context context) { + super(context); + init(); + } + + public NestedScrollWebView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public NestedScrollWebView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + mChildHelper = new NestedScrollingChildHelper(this); + setNestedScrollingEnabled(true); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean result = false; + + MotionEvent trackedEvent = MotionEvent.obtain(event); + + final int action = MotionEventCompat.getActionMasked(event); + + if (action == MotionEvent.ACTION_DOWN) { + mNestedYOffset = 0; + } + + int y = (int) event.getY(); + + event.offsetLocation(0, mNestedYOffset); + + switch (action) { + case MotionEvent.ACTION_DOWN: + mLastMotionY = y; + startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); + result = super.onTouchEvent(event); + mChange = false; + break; + case MotionEvent.ACTION_MOVE: + int deltaY = mLastMotionY - y; + + if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { + deltaY -= mScrollConsumed[1]; + trackedEvent.offsetLocation(0, mScrollOffset[1]); + mNestedYOffset += mScrollOffset[1]; + } + + int oldY = getScrollY(); + mLastMotionY = y - mScrollOffset[1]; + int newScrollY = Math.max(0, oldY + deltaY); + deltaY -= newScrollY - oldY; + if (dispatchNestedScroll(0, newScrollY - deltaY, 0, deltaY, mScrollOffset)) { + mLastMotionY -= mScrollOffset[1]; + trackedEvent.offsetLocation(0, mScrollOffset[1]); + mNestedYOffset += mScrollOffset[1]; + } + if (mScrollConsumed[1] == 0 && mScrollOffset[1] == 0) { + if (mChange) { + mChange = false; + trackedEvent.setAction(MotionEvent.ACTION_DOWN); + super.onTouchEvent(trackedEvent); + } else { + result = super.onTouchEvent(trackedEvent); + } + trackedEvent.recycle(); + } else { + if (!mChange) { + mChange = true; + super.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, + 0, 0)); + } + } + break; + case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + stopNestedScroll(); + result = super.onTouchEvent(event); + break; + } + return result; + } + + // NestedScrollingChild + @Override + public void setNestedScrollingEnabled(boolean enabled) { + mChildHelper.setNestedScrollingEnabled(enabled); + } + + @Override + public boolean isNestedScrollingEnabled() { + return mChildHelper.isNestedScrollingEnabled(); + } + + @Override + public boolean startNestedScroll(int axes) { + return mChildHelper.startNestedScroll(axes); + } + + @Override + public void stopNestedScroll() { + mChildHelper.stopNestedScroll(); + } + + @Override + public boolean hasNestedScrollingParent() { + return mChildHelper.hasNestedScrollingParent(); + } + + @Override + public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int + dyUnconsumed, int[] offsetInWindow) { + return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, + dyUnconsumed, offsetInWindow); + } + + @Override + public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { + return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); + } + + @Override + public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { + return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); + } + + @Override + public boolean dispatchNestedPreFling(float velocityX, float velocityY) { + return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/PickerView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/PickerView.java new file mode 100644 index 0000000..092e775 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/PickerView.java @@ -0,0 +1,314 @@ +package com.share.mvpsdk.widgets; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.FontMetricsInt; +import android.graphics.Paint.Style; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Created by Horrarndoo on 2017/4/5. + *

+ * 滚动选择器 + */ +public class PickerView extends View { + + public static final String TAG = "PickerView"; + /** + * text之间间距和minTextSize之比 + */ + public static final float MARGIN_ALPHA = 2.8f; + /** + * 自动回滚到中间的速度 + */ + public static final float SPEED = 2; + + private List mDataList; + /** + * 选中的位置,这个位置是mDataList的中心位置,一直不变 + */ + private int mCurrentSelected; + private Paint mPaint; + + private float mMaxTextSize = 80; + private float mMinTextSize = 50; + // private float mMinTextSize = 40; + + private float mMaxTextAlpha = 255; + private float mMinTextAlpha = 120; + + private int mColorText = 0xFF9500;// 森威尔的黄色 + + private int mViewHeight; + private int mViewWidth; + + private float mLastDownY; + /** + * 滑动的距离 + */ + private float mMoveLen = 0; + private boolean isInit = false; + private onSelectListener mSelectListener; + private Timer timer; + private MyTimerTask mTask; + + Handler updateHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (Math.abs(mMoveLen) < SPEED) { + mMoveLen = 0; + if (mTask != null) { + mTask.cancel(); + mTask = null; + performSelect(); + } + } else + // 这里mMoveLen / Math.abs(mMoveLen)是为了保有mMoveLen的正负号,以实现上滚或下滚 + mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * SPEED; + invalidate(); + } + + }; + + public PickerView(Context context) { + super(context); + init(); + } + + public PickerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public void setOnSelectListener(onSelectListener listener) { + mSelectListener = listener; + } + + private void performSelect() { + if (mSelectListener != null) + mSelectListener.onSelect(mDataList.get(mCurrentSelected)); + } + + public void setData(List datas) { + mDataList = datas; + mCurrentSelected = datas.size() / 2; + invalidate(); + } + + /** + * 选择选中的item的index + * + * @param selected + */ + public void setSelected(int selected) { + mCurrentSelected = selected; + int distance = mDataList.size() / 2 - mCurrentSelected; + if (distance < 0) + for (int i = 0; i < -distance; i++) { + moveHeadToTail(); + mCurrentSelected--; + } + else if (distance > 0) + for (int i = 0; i < distance; i++) { + moveTailToHead(); + mCurrentSelected++; + } + invalidate(); + } + + /** + * 选择选中的内容 + * + * @param mSelectItem + */ + public void setSelected(String mSelectItem) { + for (int i = 0; i < mDataList.size(); i++) + if (mDataList.get(i).equals(mSelectItem)) { + setSelected(i); + break; + } + } + + private void moveHeadToTail() { + String head = mDataList.get(0); + mDataList.remove(0); + mDataList.add(head); + } + + private void moveTailToHead() { + String tail = mDataList.get(mDataList.size() - 1); + mDataList.remove(mDataList.size() - 1); + mDataList.add(0, tail); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mViewHeight = getMeasuredHeight(); + mViewWidth = getMeasuredWidth(); + // 按照View的高度计算字体大小 + mMaxTextSize = mViewHeight / 4.0f; + mMinTextSize = mMaxTextSize / 2f; + isInit = true; + invalidate(); + } + + private void init() { + timer = new Timer(); + mDataList = new ArrayList(); + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Style.FILL); + mPaint.setTextAlign(Align.CENTER); + mPaint.setColor(mColorText); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + // 根据index绘制view + if (isInit) + drawData(canvas); + } + + private void drawData(Canvas canvas) { + // 先绘制选中的text再往上往下绘制其余的text + float scale = parabola(mViewHeight / 4.0f, mMoveLen); + float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize; + mPaint.setTextSize(size); + mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha)); + // text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标 + float x = (float) (mViewWidth / 2.0); + float y = (float) (mViewHeight / 2.0 + mMoveLen); + FontMetricsInt fmi = mPaint.getFontMetricsInt(); + float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); + if (mDataList.size() > 0) { + canvas.drawText(mDataList.get(mCurrentSelected), x, baseline, + mPaint); + } + // 绘制上方data + for (int i = 1; (mCurrentSelected - i) >= 0; i++) { + drawOtherText(canvas, i, -1); + } + // 绘制下方data + for (int i = 1; (mCurrentSelected + i) < mDataList.size(); i++) { + drawOtherText(canvas, i, 1); + } + } + + /** + * @param canvas + * @param position 距离mCurrentSelected的差值 + * @param type 1表示向下绘制,-1表示向上绘制 + */ + private void drawOtherText(Canvas canvas, int position, int type) { + float d = (float) (MARGIN_ALPHA * mMinTextSize * position + type + * mMoveLen); + float scale = parabola(mViewHeight / 4.0f, d); + float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize; + mPaint.setTextSize(size); + mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha)); + float y = (float) (mViewHeight / 2.0 + type * d); + FontMetricsInt fmi = mPaint.getFontMetricsInt(); + float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); + canvas.drawText(mDataList.get(mCurrentSelected + type * position), + (float) (mViewWidth / 2.0), baseline, mPaint); + } + + /** + * 抛物线 + * + * @param zero 零点坐标 + * @param x 偏移量 + * @return scale + */ + private float parabola(float zero, float x) { + float f = (float) (1 - Math.pow(x / zero, 2)); + return f < 0 ? 0 : f; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + doDown(event); + break; + case MotionEvent.ACTION_MOVE: + doMove(event); + break; + case MotionEvent.ACTION_UP: + doUp(event); + break; + } + return true; + } + + private void doDown(MotionEvent event) { + if (mTask != null) { + mTask.cancel(); + mTask = null; + } + mLastDownY = event.getY(); + } + + private void doMove(MotionEvent event) { + + mMoveLen += (event.getY() - mLastDownY); + + if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2) { + // 往下滑超过离开距离 + moveTailToHead(); + mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize; + } else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2) { + // 往上滑超过离开距离 + moveHeadToTail(); + mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize; + } + + mLastDownY = event.getY(); + invalidate(); + } + + private void doUp(MotionEvent event) { + // 抬起手后mCurrentSelected的位置由当前位置move到中间选中位置 + if (Math.abs(mMoveLen) < 0.0001) { + mMoveLen = 0; + return; + } + if (mTask != null) { + mTask.cancel(); + mTask = null; + } + mTask = new MyTimerTask(updateHandler); + timer.schedule(mTask, 0, 10); + } + + class MyTimerTask extends TimerTask { + Handler handler; + + public MyTimerTask(Handler handler) { + this.handler = handler; + } + + @Override + public void run() { + handler.sendMessage(handler.obtainMessage()); + } + + } + + public interface onSelectListener { + void onSelect(String text); + } +} + diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/ProgressButton.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/ProgressButton.java new file mode 100644 index 0000000..7dbd39f --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/ProgressButton.java @@ -0,0 +1,79 @@ +package com.share.mvpsdk.widgets; + + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.widget.Button; + +import com.share.mvpsdk.R; + + +/** + * Created by Horrarndoo on 2017/4/5. + *

+ * 带有进度变化的button + */ +public class ProgressButton extends Button { + public static final int MAX_PROGRESS = 1000; + private boolean isAdding = false; + private int progress = 0; + private int progressStrokeWidth = 6; + + private Paint mPaint; + private RectF dst; + + private Context context; + + public ProgressButton(Context context, AttributeSet attrs) { + super(context, attrs); + dst = new RectF(); + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setStyle(Style.STROKE); + mPaint.setStrokeWidth(progressStrokeWidth); + this.context = context; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = this.getWidth(); + int height = this.getHeight(); + + if (width != height) { + int min = Math.min(width, height); + width = min; + height = min; + } + + mPaint.setColor(context.getResources().getColor(R.color.dark_grey)); + canvas.drawColor(Color.TRANSPARENT); + + dst.left = progressStrokeWidth / 2; // 左上角x + dst.top = progressStrokeWidth / 2; // 左上角y + dst.right = width - progressStrokeWidth / 2; // 左下角x + dst.bottom = height - progressStrokeWidth / 2; // 右下角y + + canvas.drawArc(dst, -90, 360, false, mPaint); + + if (isAdding) { + mPaint.setColor(context.getResources().getColor(R.color.light_yellow)); + canvas.drawArc(dst, -90, ((float) progress / MAX_PROGRESS) * 360, false, mPaint); + } + } + + /** + * 非UI线程调用 + */ + public void setProgressNotInUiThread(int progress, boolean isAdding) { + this.isAdding = isAdding; + this.progress = progress; + this.postInvalidate(); + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/SlideSwitchView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/SlideSwitchView.java new file mode 100644 index 0000000..cd956d4 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/SlideSwitchView.java @@ -0,0 +1,259 @@ +package com.share.mvpsdk.widgets; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import com.share.mvpsdk.R; + + +/** + * Created by Horrarndoo on 2017/6/1. + *

+ * 自定义滑动开关 + *

+ * Android 的界面绘制流程 + * 测量 摆放 绘制 + * measure -> layout -> draw + * | | | + * onMeasure -> onLayout -> onDraw 重写这些方法, 实现自定义控件 + *

+ * onResume()之后执行 + *

+ * View + * onMeasure() (在这个方法里指定自己的宽高) -> onDraw() (绘制自己的内容) + *

+ * ViewGroup + * onMeasure() (指定自己的宽高, 所有子View的宽高)-> onLayout() (摆放所有子View) -> onDraw() (绘制内容) + */ +public class SlideSwitchView extends View { + private Bitmap slideButtonBitmap; // 滑块图片 + private Paint mPaint; // 画笔 + private float currentX; //当前滑动的x坐标 + private int mBaseLineY; // text基准线 + private String mTextContent; //text内容 + + public SlideSwitchView(Context context) { + this(context, null); + } + + public SlideSwitchView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlideSwitchView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initPaint(); + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlideSwitchView); + setSlideButtonResource(ta.getResourceId(R.styleable.SlideSwitchView_slide_button, -1)); + setText(ta.getString(R.styleable.SlideSwitchView_android_text)); + setTextSize(ta.getDimension(R.styleable.SlideSwitchView_android_textSize, 30)); + setTextColor(ta.getColor(R.styleable.SlideSwitchView_android_textColor, Color.BLACK)); + ta.recycle(); + } + + /** + * 初始化画笔 + */ + private void initPaint() { + mPaint = new Paint(); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setTextAlign(Paint.Align.LEFT); + mPaint.setAntiAlias(true); + } + + /** + * 初始化text居中基准线 + */ + private void initTextBaseLine() { + Paint.FontMetrics fontMetrics = mPaint.getFontMetrics(); + float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top + float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom + mBaseLineY = (int) (getMeasuredHeight() / 2 - top / 2 - bottom / 2);//基线中间点的y轴计算公式 + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + + if (widthMode == MeasureSpec.AT_MOST) { + int newWidth = (int) (slideButtonBitmap.getWidth() * 2 + getTextWidth()); + if (width >= newWidth) + width = newWidth; + } + + if (heightMode == MeasureSpec.EXACTLY) { + if (height < slideButtonBitmap.getHeight()) { + // 获得图片的宽高 + int widthSlide = slideButtonBitmap.getWidth(); + int heightSlide = slideButtonBitmap.getHeight(); + float scaleHeight = height * 1.0f / slideButtonBitmap.getHeight(); + Matrix matrix = new Matrix(); + matrix.postScale(scaleHeight, scaleHeight); + slideButtonBitmap = Bitmap.createBitmap(slideButtonBitmap, 0, 0, widthSlide, + heightSlide, matrix, true); + invalidate(); + } + } + + if (slideButtonBitmap.getWidth() > (width - getTextWidth()) / 2) { + // 获得图片的宽高 + int widthSlide = slideButtonBitmap.getWidth(); + int heightSlide = slideButtonBitmap.getHeight(); + float scaleWidth = (width - getTextWidth()) / 2 / slideButtonBitmap.getWidth(); + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleWidth); + slideButtonBitmap = Bitmap.createBitmap(slideButtonBitmap, 0, 0, widthSlide, + heightSlide, matrix, true); + invalidate(); + } + + setMeasuredDimension(width, slideButtonBitmap.getHeight()); + initTextBaseLine(); + } + + // Canvas 画布, 画板. 在上边绘制的内容都会显示到界面上. + @Override + protected void onDraw(Canvas canvas) { + // 1. 绘制text + canvas.drawText(mTextContent, slideButtonBitmap.getWidth(), mBaseLineY, mPaint); + + // 2. 绘制滑块 + if (isTouchMode) { + // 根据当前用户触摸到的位置画滑块 + // 让滑块向左移动自身一半大小的位置 + float newLeft = currentX - slideButtonBitmap.getWidth() / 2.0f; + + int maxLeft = getMeasuredWidth() - slideButtonBitmap.getWidth(); + + // 限定滑块范围 + if (newLeft < 0) { + newLeft = 0; // 左边范围 + } else if (newLeft > maxLeft) { + newLeft = maxLeft; // 右边范围 + } + + canvas.drawBitmap(slideButtonBitmap, newLeft, 0, mPaint); + } else { + //还原button位置 + canvas.drawBitmap(slideButtonBitmap, 0, 0, mPaint); + } + + } + + boolean isTouchMode = false; + private OnSwitchStateUpdateListener onSwitchStateUpdateListener; + + // 重写触摸事件, 响应用户的触摸. + @Override + public boolean onTouchEvent(MotionEvent event) { + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + isTouchMode = true; + currentX = event.getX(); + break; + case MotionEvent.ACTION_MOVE: + currentX = event.getX(); + break; + case MotionEvent.ACTION_UP: + isTouchMode = false; + currentX = event.getX(); + + float center = getMeasuredWidth() / 2.0f; + + // 根据当前按下的位置, 和控件中心的位置进行比较. + boolean isStateChanged = currentX > center; + + // 如果开关状态变化了, 通知界面 + if (isStateChanged && onSwitchStateUpdateListener != null) { + onSwitchStateUpdateListener.onStateUpdate(); + } + break; + + default: + break; + } + + // 重绘界面 + invalidate(); // 会引发onDraw()被调用, 里边的变量会重新生效.界面会更新 + + return true; // 消费了用户的触摸事件, 才可以收到其他的事件. + } + + /** + * 设置滑块图片资源 + * + * @param slideButton 滑块图片资源 + */ + public void setSlideButtonResource(int slideButton) { + slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton); + } + + /** + * 设置text字号大小 + * + * @param textSize text字号大小 + */ + public void setTextSize(float textSize) { + mPaint.setTextSize(textSize); + mPaint.setStrokeWidth(textSize / 15.f); + } + + /** + * 设置text内容 + * + * @param text text内容 + */ + public void setText(String text) { + mTextContent = text; + } + + /** + * 设置text颜色 + * + * @param color text颜色资源 + */ + public void setTextColor(int color) { + mPaint.setColor(color); + } + + /** + * 获取text文字宽度 + * + * @return text文字宽度 + */ + private float getTextWidth() { + return mPaint.measureText(mTextContent); + } + + /** + * 获取text文字高度 + * + * @return text文字高度 + */ + private float getTextHeight() { + return mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top; + } + + public interface OnSwitchStateUpdateListener { + // 状态回调 + void onStateUpdate(); + } + + public void setOnSwitchStateUpdateListener( + OnSwitchStateUpdateListener onSwitchStateUpdateListener) { + this.onSwitchStateUpdateListener = onSwitchStateUpdateListener; + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/WaitPorgressDialog.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/WaitPorgressDialog.java new file mode 100644 index 0000000..86e5d19 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/WaitPorgressDialog.java @@ -0,0 +1,22 @@ +package com.share.mvpsdk.widgets; + +import android.app.ProgressDialog; +import android.content.Context; + +/** + * Created by Horrarndoo on 2017/4/17. + *

+ * 等待提示dialog + */ + +public class WaitPorgressDialog extends ProgressDialog { + + public WaitPorgressDialog(Context context) { + this(context, 0); + } + + public WaitPorgressDialog(Context context, int theme) { + super(context, theme); + setCanceledOnTouchOutside(false); + } +} diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipView.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipView.java new file mode 100644 index 0000000..d7762c7 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipView.java @@ -0,0 +1,157 @@ +package com.share.mvpsdk.widgets.headclip; + +/** + * Created by Horrarndoo on 2017/9/25. + *

+ */ + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.Xfermode; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.WindowManager; + +/** + * 头像上传裁剪框 + */ +public class ClipView extends View { + private Paint paint = new Paint(); + //画裁剪区域边框的画笔 + private Paint borderPaint = new Paint(); + //裁剪框水平方向间距 + private float mHorizontalPadding; + //裁剪框边框宽度 + private int clipBorderWidth; + //裁剪圆框的半径 + private int clipRadiusWidth; + //裁剪框矩形宽度 + private int clipWidth; + //裁剪框类别,(圆形、矩形),默认为圆形 + private ClipType clipType = ClipType.CIRCLE; + private Xfermode xfermode; + + public ClipView(Context context) { + this(context, null); + } + + public ClipView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClipView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + //去锯齿 + paint.setAntiAlias(true); + borderPaint.setStyle(Paint.Style.STROKE); + borderPaint.setColor(Color.WHITE); + borderPaint.setStrokeWidth(clipBorderWidth); + borderPaint.setAntiAlias(true); + xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG + | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG + | Canvas.CLIP_TO_LAYER_SAVE_FLAG; + //通过Xfermode的DST_OUT来产生中间的透明裁剪区域,一定要另起一个Layer(层) + canvas.saveLayer(0, 0, this.getWidth(), this.getHeight(), null, LAYER_FLAGS); + //设置背景 + canvas.drawColor(Color.parseColor("#a8000000")); + paint.setXfermode(xfermode); + //绘制圆形裁剪框 + if (clipType == ClipType.CIRCLE) { + //中间的透明的圆 + canvas.drawCircle(this.getWidth() / 2, this.getHeight() / 2, clipRadiusWidth, paint); + //白色的圆边框 + canvas.drawCircle(this.getWidth() / 2, this.getHeight() / 2, clipRadiusWidth, borderPaint); + } else if (clipType == ClipType.RECTANGLE) { //绘制矩形裁剪框 + //绘制中间的矩形 + canvas.drawRect(mHorizontalPadding, this.getHeight() / 2 - clipWidth / 2, + this.getWidth() - mHorizontalPadding, this.getHeight() / 2 + clipWidth / 2, paint); + //绘制白色的矩形边框 + canvas.drawRect(mHorizontalPadding, this.getHeight() / 2 - clipWidth / 2, + this.getWidth() - mHorizontalPadding, this.getHeight() / 2 + clipWidth / 2, borderPaint); + } + //出栈,恢复到之前的图层,意味着新建的图层会被删除,新建图层上的内容会被绘制到canvas (or the previous layer) + canvas.restore(); + } + + /** + * 获取裁剪区域的Rect + * + * @return + */ + public Rect getClipRect() { + Rect rect = new Rect(); + //宽度的一半 - 圆的半径 + rect.left = (this.getWidth() / 2 - clipRadiusWidth); + //宽度的一半 + 圆的半径 + rect.right = (this.getWidth() / 2 + clipRadiusWidth); + //高度的一半 - 圆的半径 + rect.top = (this.getHeight() / 2 - clipRadiusWidth); + //高度的一半 + 圆的半径 + rect.bottom = (this.getHeight() / 2 + clipRadiusWidth); + return rect; + } + + /** + * 设置裁剪框边框宽度 + * + * @param clipBorderWidth + */ + public void setClipBorderWidth(int clipBorderWidth) { + this.clipBorderWidth = clipBorderWidth; + borderPaint.setStrokeWidth(clipBorderWidth); + invalidate(); + } + + /** + * 设置裁剪框水平间距 + * + * @param mHorizontalPadding + */ + public void setmHorizontalPadding(float mHorizontalPadding) { + this.mHorizontalPadding = mHorizontalPadding; + this.clipRadiusWidth = (int) (getScreenWidth(getContext()) - 2 * mHorizontalPadding) / 2; + this.clipWidth = clipRadiusWidth * 2; + } + + /** + * 获得屏幕高度 + * + * @param context + * @return + */ + public static int getScreenWidth(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics outMetrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(outMetrics); + return outMetrics.widthPixels; + } + + + /** + * 设置裁剪框类别 + * + * @param clipType + */ + public void setClipType(ClipType clipType) { + this.clipType = clipType; + } + + /** + * 裁剪框类别,圆形、矩形 + */ + public enum ClipType { + CIRCLE, RECTANGLE + } +} \ No newline at end of file diff --git a/mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipViewLayout.java b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipViewLayout.java new file mode 100644 index 0000000..5126345 --- /dev/null +++ b/mvpsdk/src/main/java/com/share/mvpsdk/widgets/headclip/ClipViewLayout.java @@ -0,0 +1,485 @@ +package com.share.mvpsdk.widgets.headclip; + +/** + * Created by Horrarndoo on 2017/9/25. + *

+ */ + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.media.ExifInterface; +import android.net.Uri; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import android.widget.RelativeLayout; + + +import com.share.mvpsdk.R; +import com.share.mvpsdk.utils.LogUtils; + +import java.io.IOException; + +import static com.share.mvpsdk.utils.FileUtils.getRealFilePathFromUri; + + +/** + * 头像上传原图裁剪容器 + */ +public class ClipViewLayout extends RelativeLayout { + //裁剪原图 + private ImageView imageView; + //裁剪框 + private ClipView clipView; + //裁剪框水平方向间距,xml布局文件中指定 + private float mHorizontalPadding; + //裁剪框垂直方向间距,计算得出 + private float mVerticalPadding; + //图片缩放、移动操作矩阵 + private Matrix matrix = new Matrix(); + //图片原来已经缩放、移动过的操作矩阵 + private Matrix savedMatrix = new Matrix(); + //动作标志:无 + private static final int NONE = 0; + //动作标志:拖动 + private static final int DRAG = 1; + //动作标志:缩放 + private static final int ZOOM = 2; + //初始化动作标志 + private int mode = NONE; + //记录起始坐标 + private PointF start = new PointF(); + //记录缩放时两指中间点坐标 + private PointF mid = new PointF(); + private float oldDist = 1f; + //用于存放矩阵的9个值 + private final float[] matrixValues = new float[9]; + //最小缩放比例 + private float minScale; + //最大缩放比例 + private float maxScale = 4; + + + public ClipViewLayout(Context context) { + this(context, null); + } + + public ClipViewLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClipViewLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + //初始化控件自定义的属性 + public void init(Context context, AttributeSet attrs) { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ClipViewLayout); + + //获取剪切框距离左右的边距, 默认为50dp + mHorizontalPadding = array.getDimensionPixelSize(R.styleable.ClipViewLayout_mHorizontalPadding, + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics())); + //获取裁剪框边框宽度,默认1dp + int clipBorderWidth = array.getDimensionPixelSize(R.styleable.ClipViewLayout_clipBorderWidth, + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics())); + //裁剪框类型(圆或者矩形) + int clipType = array.getInt(R.styleable.ClipViewLayout_clipType, 1); + + //回收 + array.recycle(); + clipView = new ClipView(context); + //设置裁剪框类型 + clipView.setClipType(clipType == 1 ? ClipView.ClipType.CIRCLE : ClipView.ClipType.RECTANGLE); + //设置剪切框边框 + clipView.setClipBorderWidth(clipBorderWidth); + //设置剪切框水平间距 + clipView.setmHorizontalPadding(mHorizontalPadding); + imageView = new ImageView(context); + //相对布局布局参数 + android.view.ViewGroup.LayoutParams lp = new LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, + android.view.ViewGroup.LayoutParams.MATCH_PARENT); + this.addView(imageView, lp); + this.addView(clipView, lp); + } + + + /** + * 初始化图片 + */ + public void setImageSrc(final Uri uri) { + //需要等到imageView绘制完毕再初始化原图 + ViewTreeObserver observer = imageView.getViewTreeObserver(); + observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + public void onGlobalLayout() { + initSrcPic(uri); + imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + }); + } + + /** + * 初始化图片 + * step 1: decode 出 720*1280 左右的照片 因为原图可能比较大 直接加载出来会OOM + * step 2: 将图片缩放 移动到imageView 中间 + */ + public void initSrcPic(Uri uri) { + if (uri == null) { + return; + } + LogUtils.d("**********clip_view uri******* " + uri); + String path = getRealFilePathFromUri(getContext(), uri); + LogUtils.d("**********clip_view path******* " + path); + if (TextUtils.isEmpty(path)) { + return; + } + + //这里decode出720*1280 左右的照片,防止OOM + Bitmap bitmap = decodeSampledBitmap(path, 720, 1280); + if (bitmap == null) { + return; + } + + //竖屏拍照的照片,直接使用的话,会旋转90度,下面代码把角度旋转过来 + int rotation = getExifOrientation(path); //查询旋转角度 + Matrix m = new Matrix(); + m.setRotate(rotation); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); + + //图片的缩放比 + float scale; + if (bitmap.getWidth() >= bitmap.getHeight()) {//宽图 + scale = (float) imageView.getWidth() / bitmap.getWidth(); + //如果高缩放后小于裁剪区域 则将裁剪区域与高的缩放比作为最终的缩放比 + Rect rect = clipView.getClipRect(); + //高的最小缩放比 + minScale = rect.height() / (float) bitmap.getHeight(); + if (scale < minScale) { + scale = minScale; + } + } else {//高图 + //高的缩放比 + scale = (float) imageView.getHeight() / bitmap.getHeight(); + //如果宽缩放后小于裁剪区域 则将裁剪区域与宽的缩放比作为最终的缩放比 + Rect rect = clipView.getClipRect(); + //宽的最小缩放比 + minScale = rect.width() / (float) bitmap.getWidth(); + if (scale < minScale) { + scale = minScale; + } + } + // 缩放 + matrix.postScale(scale, scale); + // 平移,将缩放后的图片平移到imageview的中心 + //imageView的中心x + int midX = imageView.getWidth() / 2; + //imageView的中心y + int midY = imageView.getHeight() / 2; + //bitmap的中心x + int imageMidX = (int) (bitmap.getWidth() * scale / 2); + //bitmap的中心y + int imageMidY = (int) (bitmap.getHeight() * scale / 2); + matrix.postTranslate(midX - imageMidX, midY - imageMidY); + imageView.setScaleType(ImageView.ScaleType.MATRIX); + imageView.setImageMatrix(matrix); + imageView.setImageBitmap(bitmap); + } + + /** + * 查询图片旋转角度 + */ + public static int getExifOrientation(String filepath) {// YOUR MEDIA PATH AS STRING + int degree = 0; + ExifInterface exif = null; + try { + exif = new ExifInterface(filepath); + } catch (IOException ex) { + ex.printStackTrace(); + } + if (exif != null) { + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1); + if (orientation != -1) { + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + degree = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + degree = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + degree = 270; + break; + } + + } + } + return degree; + } + + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + savedMatrix.set(matrix); + //设置开始点位置 + start.set(event.getX(), event.getY()); + mode = DRAG; + break; + case MotionEvent.ACTION_POINTER_DOWN: + //开始放下时候两手指间的距离 + oldDist = spacing(event); + if (oldDist > 10f) { + savedMatrix.set(matrix); + midPoint(mid, event); + mode = ZOOM; + } + break; + case MotionEvent.ACTION_UP: + break; + case MotionEvent.ACTION_POINTER_UP: + mode = NONE; + break; + case MotionEvent.ACTION_MOVE: + if (mode == DRAG) { //拖动 + matrix.set(savedMatrix); + float dx = event.getX() - start.x; + float dy = event.getY() - start.y; + mVerticalPadding = clipView.getClipRect().top; + matrix.postTranslate(dx, dy); + //检查边界 + checkBorder(); + } else if (mode == ZOOM) { //缩放 + //缩放后两手指间的距离 + float newDist = spacing(event); + if (newDist > 10f) { + //手势缩放比例 + float scale = newDist / oldDist; + if (scale < 1) { //缩小 + if (getScale() > minScale) { + matrix.set(savedMatrix); + mVerticalPadding = clipView.getClipRect().top; + matrix.postScale(scale, scale, mid.x, mid.y); + //缩放到最小范围下面去了,则返回到最小范围大小 + while (getScale() < minScale) { + //返回到最小范围的放大比例 + scale = 1 + 0.01F; + matrix.postScale(scale, scale, mid.x, mid.y); + } + } + //边界检查 + checkBorder(); + } else { //放大 + if (getScale() <= maxScale) { + matrix.set(savedMatrix); + mVerticalPadding = clipView.getClipRect().top; + matrix.postScale(scale, scale, mid.x, mid.y); + } + } + } + } + imageView.setImageMatrix(matrix); + break; + } + return true; + } + + /** + * 根据当前图片的Matrix获得图片的范围 + */ + private RectF getMatrixRectF(Matrix matrix) { + RectF rect = new RectF(); + Drawable d = imageView.getDrawable(); + if (null != d) { + rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); + matrix.mapRect(rect); + } + return rect; + } + + /** + * 边界检测 + */ + private void checkBorder() { + RectF rect = getMatrixRectF(matrix); + float deltaX = 0; + float deltaY = 0; + int width = imageView.getWidth(); + int height = imageView.getHeight(); + // 如果宽或高大于屏幕,则控制范围 ; 这里的0.001是因为精度丢失会产生问题,但是误差一般很小,所以我们直接加了一个0.01 + if (rect.width() >= width - 2 * mHorizontalPadding) { + if (rect.left > mHorizontalPadding) { + deltaX = -rect.left + mHorizontalPadding; + } + if (rect.right < width - mHorizontalPadding) { + deltaX = width - mHorizontalPadding - rect.right; + } + } + if (rect.height() >= height - 2 * mVerticalPadding) { + if (rect.top > mVerticalPadding) { + deltaY = -rect.top + mVerticalPadding; + } + if (rect.bottom < height - mVerticalPadding) { + deltaY = height - mVerticalPadding - rect.bottom; + } + } + matrix.postTranslate(deltaX, deltaY); + } + + /** + * 获得当前的缩放比例 + */ + public final float getScale() { + matrix.getValues(matrixValues); + return matrixValues[Matrix.MSCALE_X]; + } + + + /** + * 多点触控时,计算最先放下的两指距离 + */ + private float spacing(MotionEvent event) { + float x = event.getX(0) - event.getX(1); + float y = event.getY(0) - event.getY(1); + return (float) Math.sqrt(x * x + y * y); + } + + /** + * 多点触控时,计算最先放下的两指中心坐标 + */ + private void midPoint(PointF point, MotionEvent event) { + float x = event.getX(0) + event.getX(1); + float y = event.getY(0) + event.getY(1); + point.set(x / 2, y / 2); + } + + + /** + * 获取剪切图 + */ + public Bitmap clip() { + imageView.setDrawingCacheEnabled(true); + imageView.buildDrawingCache(); + Rect rect = clipView.getClipRect(); + Bitmap cropBitmap = null; + Bitmap zoomedCropBitmap = null; + try { + cropBitmap = Bitmap.createBitmap(imageView.getDrawingCache(), rect.left, rect.top, rect.width(), rect.height()); + zoomedCropBitmap = zoomBitmap(cropBitmap, 200, 200); + } catch (Exception e) { + e.printStackTrace(); + } + if (cropBitmap != null) { + cropBitmap.recycle(); + } + // 释放资源 + imageView.destroyDrawingCache(); + return zoomedCropBitmap; + } + + + /** + * 图片等比例压缩 + * + * @param filePath + * @param reqWidth 期望的宽 + * @param reqHeight 期望的高 + * @return + */ + public static Bitmap decodeSampledBitmap(String filePath, int reqWidth, + int reqHeight) { + + // First decode with inJustDecodeBounds=true to check dimensions + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + options.inPreferredConfig = Bitmap.Config.RGB_565; + //bitmap is null + Bitmap bitmap = BitmapFactory.decodeFile(filePath, options); + + // Calculate inSampleSize + options.inSampleSize = calculateInSampleSize(options, reqWidth, + reqHeight); + + // Decode bitmap with inSampleSize set + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFile(filePath, options); + } + + /** + * 计算InSampleSize + * 宽的压缩比和高的压缩比的较小值 取接近的2的次幂的值 + * 比如宽的压缩比是3 高的压缩比是5 取较小值3 而InSampleSize必须是2的次幂,取接近的2的次幂4 + * + * @param options + * @param reqWidth + * @param reqHeight + * @return + */ + public static int calculateInSampleSize(BitmapFactory.Options options, + int reqWidth, int reqHeight) { + // Raw height and width of image + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + + // Calculate ratios of height and width to requested height and + // width + final int heightRatio = Math.round((float) height + / (float) reqHeight); + final int widthRatio = Math.round((float) width / (float) reqWidth); + + // Choose the smallest ratio as inSampleSize value, this will + // guarantee + // a final image with both dimensions larger than or equal to the + // requested height and width. + int ratio = heightRatio < widthRatio ? heightRatio : widthRatio; + // inSampleSize只能是2的次幂 将ratio就近取2的次幂的值 + if (ratio < 3) + inSampleSize = ratio; + else if (ratio < 6.5) + inSampleSize = 4; + else if (ratio < 8) + inSampleSize = 8; + else + inSampleSize = ratio; + } + + return inSampleSize; + } + + /** + * 图片缩放到指定宽高 + *

+ * 非等比例压缩,图片会被拉伸 + * + * @param bitmap 源位图对象 + * @param w 要缩放的宽度 + * @param h 要缩放的高度 + * @return 新Bitmap对象 + */ + public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + Matrix matrix = new Matrix(); + float scaleWidth = ((float) w / width); + float scaleHeight = ((float) h / height); + matrix.postScale(scaleWidth, scaleHeight); + Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false); + return newBmp; + } + + +} \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_finish_trans_in.xml b/mvpsdk/src/main/res/anim/activity_finish_trans_in.xml new file mode 100644 index 0000000..2bca8d2 --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_finish_trans_in.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_finish_trans_out.xml b/mvpsdk/src/main/res/anim/activity_finish_trans_out.xml new file mode 100644 index 0000000..d583d15 --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_finish_trans_out.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_finish_zoom_in.xml b/mvpsdk/src/main/res/anim/activity_finish_zoom_in.xml new file mode 100644 index 0000000..d89e6f2 --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_finish_zoom_in.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_finish_zoom_out.xml b/mvpsdk/src/main/res/anim/activity_finish_zoom_out.xml new file mode 100644 index 0000000..7aee74c --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_finish_zoom_out.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_start_trans_in.xml b/mvpsdk/src/main/res/anim/activity_start_trans_in.xml new file mode 100644 index 0000000..3f414b1 --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_start_trans_in.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_start_trans_out.xml b/mvpsdk/src/main/res/anim/activity_start_trans_out.xml new file mode 100644 index 0000000..22d8ccb --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_start_trans_out.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_start_zoom_in.xml b/mvpsdk/src/main/res/anim/activity_start_zoom_in.xml new file mode 100644 index 0000000..53be488 --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_start_zoom_in.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/anim/activity_start_zoom_out.xml b/mvpsdk/src/main/res/anim/activity_start_zoom_out.xml new file mode 100644 index 0000000..b33cd1d --- /dev/null +++ b/mvpsdk/src/main/res/anim/activity_start_zoom_out.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/drawable/ic_vector_empty.xml b/mvpsdk/src/main/res/drawable/ic_vector_empty.xml new file mode 100644 index 0000000..5537e7b --- /dev/null +++ b/mvpsdk/src/main/res/drawable/ic_vector_empty.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/mvpsdk/src/main/res/drawable/ic_vector_loading.xml b/mvpsdk/src/main/res/drawable/ic_vector_loading.xml new file mode 100644 index 0000000..185a813 --- /dev/null +++ b/mvpsdk/src/main/res/drawable/ic_vector_loading.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/mvpsdk/src/main/res/drawable/ic_vector_net_error.xml b/mvpsdk/src/main/res/drawable/ic_vector_net_error.xml new file mode 100644 index 0000000..1e05a45 --- /dev/null +++ b/mvpsdk/src/main/res/drawable/ic_vector_net_error.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mvpsdk/src/main/res/drawable/indoor_temp.png b/mvpsdk/src/main/res/drawable/indoor_temp.png new file mode 100644 index 0000000..7057fb6 Binary files /dev/null and b/mvpsdk/src/main/res/drawable/indoor_temp.png differ diff --git a/mvpsdk/src/main/res/drawable/item_touch_bg.xml b/mvpsdk/src/main/res/drawable/item_touch_bg.xml new file mode 100644 index 0000000..00ea33d --- /dev/null +++ b/mvpsdk/src/main/res/drawable/item_touch_bg.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/mvpsdk/src/main/res/drawable/stackblur_default.png b/mvpsdk/src/main/res/drawable/stackblur_default.png new file mode 100644 index 0000000..f182b41 Binary files /dev/null and b/mvpsdk/src/main/res/drawable/stackblur_default.png differ diff --git a/mvpsdk/src/main/res/drawable/week_normal.png b/mvpsdk/src/main/res/drawable/week_normal.png new file mode 100644 index 0000000..0a5dd1e Binary files /dev/null and b/mvpsdk/src/main/res/drawable/week_normal.png differ diff --git a/mvpsdk/src/main/res/drawable/week_selector.png b/mvpsdk/src/main/res/drawable/week_selector.png new file mode 100644 index 0000000..5903030 Binary files /dev/null and b/mvpsdk/src/main/res/drawable/week_selector.png differ diff --git a/mvpsdk/src/main/res/drawable/week_status.xml b/mvpsdk/src/main/res/drawable/week_status.xml new file mode 100644 index 0000000..3d3a16c --- /dev/null +++ b/mvpsdk/src/main/res/drawable/week_status.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/drawable/week_tv_status.xml b/mvpsdk/src/main/res/drawable/week_tv_status.xml new file mode 100644 index 0000000..2f631f3 --- /dev/null +++ b/mvpsdk/src/main/res/drawable/week_tv_status.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/layout/sub_history_click_view.xml b/mvpsdk/src/main/res/layout/sub_history_click_view.xml new file mode 100644 index 0000000..18dc813 --- /dev/null +++ b/mvpsdk/src/main/res/layout/sub_history_click_view.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/layout/sub_history_tips.xml b/mvpsdk/src/main/res/layout/sub_history_tips.xml new file mode 100644 index 0000000..e46deda --- /dev/null +++ b/mvpsdk/src/main/res/layout/sub_history_tips.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/layout/view_empty.xml b/mvpsdk/src/main/res/layout/view_empty.xml new file mode 100644 index 0000000..030fdb3 --- /dev/null +++ b/mvpsdk/src/main/res/layout/view_empty.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/layout/view_loading.xml b/mvpsdk/src/main/res/layout/view_loading.xml new file mode 100644 index 0000000..54760be --- /dev/null +++ b/mvpsdk/src/main/res/layout/view_loading.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/layout/view_network_error.xml b/mvpsdk/src/main/res/layout/view_network_error.xml new file mode 100644 index 0000000..77e6790 --- /dev/null +++ b/mvpsdk/src/main/res/layout/view_network_error.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/mipmap-xhdpi/ic_arrow_back_white.png b/mvpsdk/src/main/res/mipmap-xhdpi/ic_arrow_back_white.png new file mode 100644 index 0000000..8214d9d Binary files /dev/null and b/mvpsdk/src/main/res/mipmap-xhdpi/ic_arrow_back_white.png differ diff --git a/mvpsdk/src/main/res/mipmap-xxhdpi/ic_arrow_back_white.png b/mvpsdk/src/main/res/mipmap-xxhdpi/ic_arrow_back_white.png new file mode 100644 index 0000000..0e43ff9 Binary files /dev/null and b/mvpsdk/src/main/res/mipmap-xxhdpi/ic_arrow_back_white.png differ diff --git a/mvpsdk/src/main/res/values-v21/styles.xml b/mvpsdk/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..52975c6 --- /dev/null +++ b/mvpsdk/src/main/res/values-v21/styles.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/mvpsdk/src/main/res/values-zh-rCN/strings.xml b/mvpsdk/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..bd06b22 --- /dev/null +++ b/mvpsdk/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,38 @@ + + 确定 + 取消 + 正在加载中…… + 加载失败,请点我重试 + 再按一次退出 + + + + + + + 室内 + 温度 + 目标 + 加热 + 时间 + (天) + (小时) + (月) + (℃) + (%) + + + (分) + (小时) + (小时) + (小时) + (天) + (天) + (千瓦/时) + + + + + 网络异常,点击重试 + 没有数据,点击重试 + diff --git a/mvpsdk/src/main/res/values/arrays.xml b/mvpsdk/src/main/res/values/arrays.xml new file mode 100644 index 0000000..c40d7cd --- /dev/null +++ b/mvpsdk/src/main/res/values/arrays.xml @@ -0,0 +1,24 @@ + + + + @string/permission_recode_audio_hint + @string/permission_get_accounts_hint + @string/permission_read_phone_hint + @string/permission_call_phone_hint + @string/permission_camera_hint + @string/permission_access_fine_location_hint + @string/permission_access_coarse_location_hint + @string/permission_read_external_hint + @string/permission_white_external_hint + + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_GET_ACCOUNTS + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_READ_PHONE_STATE + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_CALL_PHONE + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_CAMERA + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_ACCESS_FINE_LOCATION + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_ACCESS_COARSE_LOCATION + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_READ_EXTERNAL_STORAGE + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_WRITE_EXTERNAL_STORAGE + 没有此权限,无法开启这个功能,请开启权限。PERMISSION_RECORD_AUDIO + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/values/attrs.xml b/mvpsdk/src/main/res/values/attrs.xml new file mode 100644 index 0000000..cd76c60 --- /dev/null +++ b/mvpsdk/src/main/res/values/attrs.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/values/colors.xml b/mvpsdk/src/main/res/values/colors.xml new file mode 100644 index 0000000..488c181 --- /dev/null +++ b/mvpsdk/src/main/res/values/colors.xml @@ -0,0 +1,76 @@ + + + + + #ffeeeeee + + #ffeeeeee + #ff303030 + #b3ffffff + #de000000 + #ffe0e0e0 + #ff666666 + @android:color/white + #ffeeeeee + #ffcccccc + #ff3c3c3c + #ff444444 + #ff666666 + + #212121 + + #808080 + + #CCCCCC + + #f1f1f1 + + #e8e8e8 + + #d54c1c + + #cbcbcb + + #1ea5ff + + + #C7C7C7 + + + #ffffff + #cccccc + + #8BC34A + #808080 + + #88888888 + #f1f1f1 + #8BC34A + + + #0D8BC34A + #1A8BC34A + #8f000000 + + #FF9500 + #808080 + #333333 + + #80FF9500 + + #808080 + #FF9500 + #ffffff + #4a2317 + #AAAAAA + + #88000000 + #88000000 + #707070 + + #F0F0F0 + #48495F + + + #FB5B81 + diff --git a/mvpsdk/src/main/res/values/dimens.xml b/mvpsdk/src/main/res/values/dimens.xml new file mode 100644 index 0000000..7b556b6 --- /dev/null +++ b/mvpsdk/src/main/res/values/dimens.xml @@ -0,0 +1,45 @@ + + + + 16dp + 16dp + + + 4dp + 8dp + 16dp + 32dp + 64dp + 48dp + + + 34sp + 24sp + 20sp + 16sp + 14sp + 12sp + + + 2dp + 136dp + + 50dp + 50dp + 6dp + + 0.66dp + 4dp + 10dp + 14dp + 22dp + 36dp + 40dp + 60dp + 72dp + + 12sp + 14sp + 16sp + 18sp + \ No newline at end of file diff --git a/mvpsdk/src/main/res/values/strings.xml b/mvpsdk/src/main/res/values/strings.xml new file mode 100644 index 0000000..b98c96e --- /dev/null +++ b/mvpsdk/src/main/res/values/strings.xml @@ -0,0 +1,38 @@ + + Confirm + Cancel + Loading…… + Load failed. Please click me again + Press again to exit + + + Day + Week + Month + Year + Indoor + Temp + Setpoint + Cumulative + Time + (DAY) + (HOUR) + (MONTH) + (℃) + (%) + + + (min) + (hour) + (hour) + (hour) + (day) + (day) + (KW/H) + DAY + WEEK + MONTH + + Network error, Click Retry + There is no data, Click Retry + diff --git a/mvpsdk/src/main/res/values/styles.xml b/mvpsdk/src/main/res/values/styles.xml new file mode 100644 index 0000000..31fbc40 --- /dev/null +++ b/mvpsdk/src/main/res/values/styles.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/values/themes.xml b/mvpsdk/src/main/res/values/themes.xml new file mode 100644 index 0000000..4445fc8 --- /dev/null +++ b/mvpsdk/src/main/res/values/themes.xml @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mvpsdk/src/main/res/values/themes_colors.xml b/mvpsdk/src/main/res/values/themes_colors.xml new file mode 100644 index 0000000..d5c000c --- /dev/null +++ b/mvpsdk/src/main/res/values/themes_colors.xml @@ -0,0 +1,85 @@ + + + + #e51c23 + #d01716 + + + #e91e63 + #c2185b + + + #9c27b0 + #7b1fa2 + + + #673ab7 + #512da8 + + + #3f51b5 + #303f9f + + + #5677fc + #455ede + + + #03a9f4 + #0288d1 + + + #00bcd4 + #0097a7 + + + #009688 + #00796b + + + #259b24 + #0a7e07 + + + + #8bc34a + #689f38 + + + #cddc39 + #afb42b + + + #ffeb3b + #fbc02d + + + #ffc107 + #ffa000 + + + #ff9800 + #f57c00 + + + #ff5722 + #e64a19 + + + #795548 + #5d4037 + + + #9e9e9e + #616161 + + + #ffffff + + + #607d8b + #455a64 + + #03a9f4 + #ff3c3c3c + \ No newline at end of file diff --git a/mvpsdk/src/test/java/com/share/mvpsdk/ExampleUnitTest.java b/mvpsdk/src/test/java/com/share/mvpsdk/ExampleUnitTest.java new file mode 100644 index 0000000..b538286 --- /dev/null +++ b/mvpsdk/src/test/java/com/share/mvpsdk/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.share.mvpsdk; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..670125b --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app', ':mvpsdk' -- libgit2 0.21.0