Pārlūkot izejas kodu

读书页面

master
leiyun pirms 4 gadiem
vecāks
revīzija
f14a8f9c63
100 mainītis faili ar 11864 papildinājumiem un 1240 dzēšanām
  1. +5
    -0
      .idea/jarRepositories.xml
  2. +43
    -4
      app/build.gradle
  3. +9
    -5
      app/src/main/AndroidManifest.xml
  4. +132
    -0
      app/src/main/assets/weex/components/HelloWorld.js
  5. +322
    -0
      app/src/main/assets/weex/index.js
  6. +287
    -0
      app/src/main/assets/weex/reader.js
  7. +47
    -2
      app/src/main/java/com/yzx/webebook/App.kt
  8. +44
    -1
      app/src/main/java/com/yzx/webebook/MainActivity.kt
  9. +19
    -2
      app/src/main/java/com/yzx/webebook/activity/HomeActivity.kt
  10. +0
    -725
      app/src/main/java/com/yzx/webebook/activity/Main1Activity.java
  11. +765
    -0
      app/src/main/java/com/yzx/webebook/activity/ReadActivity.java
  12. +19
    -0
      app/src/main/java/com/yzx/webebook/activity/WeexTestActivity.kt
  13. +12
    -2
      app/src/main/java/com/yzx/webebook/activity/base/BaseActivity.kt
  14. +201
    -0
      app/src/main/java/com/yzx/webebook/activity/base/BaseWeexActivity.kt
  15. +38
    -0
      app/src/main/java/com/yzx/webebook/adapter/CategoryAdapter.java
  16. +63
    -0
      app/src/main/java/com/yzx/webebook/adapter/CategoryHolder.java
  17. +21
    -0
      app/src/main/java/com/yzx/webebook/adapter/ImageAdapter.kt
  18. +46
    -0
      app/src/main/java/com/yzx/webebook/adapter/PageStyleAdapter.java
  19. +40
    -0
      app/src/main/java/com/yzx/webebook/adapter/PageStyleHolder.java
  20. +154
    -0
      app/src/main/java/com/yzx/webebook/adapter/base/BaseListAdapter.java
  21. +19
    -0
      app/src/main/java/com/yzx/webebook/adapter/base/BaseViewHolder.java
  22. +92
    -0
      app/src/main/java/com/yzx/webebook/adapter/base/EasyAdapter.java
  23. +204
    -0
      app/src/main/java/com/yzx/webebook/adapter/base/GroupAdapter.java
  24. +15
    -0
      app/src/main/java/com/yzx/webebook/adapter/base/IViewHolder.java
  25. +42
    -0
      app/src/main/java/com/yzx/webebook/adapter/base/ViewHolderImpl.java
  26. +116
    -0
      app/src/main/java/com/yzx/webebook/model/bean/AuthorBean.java
  27. +146
    -0
      app/src/main/java/com/yzx/webebook/model/bean/BookChapterBean.java
  28. +54
    -0
      app/src/main/java/com/yzx/webebook/model/bean/BookRecordBean.java
  29. +89
    -0
      app/src/main/java/com/yzx/webebook/model/bean/ChapterInfoBean.java
  30. +372
    -0
      app/src/main/java/com/yzx/webebook/model/bean/CollBookBean.java
  31. +81
    -0
      app/src/main/java/com/yzx/webebook/model/bean/CommentBean.java
  32. +31
    -0
      app/src/main/java/com/yzx/webebook/model/bean/DetailBean.java
  33. +212
    -0
      app/src/main/java/com/yzx/webebook/model/bean/DownloadTaskBean.java
  34. +67
    -0
      app/src/main/java/com/yzx/webebook/model/bean/ReplyToBean.java
  35. +276
    -0
      app/src/main/java/com/yzx/webebook/model/local/BookRepository.java
  36. +56
    -0
      app/src/main/java/com/yzx/webebook/model/local/DaoDbHelper.java
  37. +16
    -0
      app/src/main/java/com/yzx/webebook/model/local/DeleteDbHelper.java
  38. +23
    -0
      app/src/main/java/com/yzx/webebook/model/local/GetDbHelper.java
  39. +77
    -0
      app/src/main/java/com/yzx/webebook/model/local/LocalRepository.java
  40. +34
    -0
      app/src/main/java/com/yzx/webebook/model/local/MyOpenHelper.java
  41. +134
    -0
      app/src/main/java/com/yzx/webebook/model/local/ReadSettingManager.java
  42. +18
    -0
      app/src/main/java/com/yzx/webebook/model/local/SaveDbHelper.java
  43. +8
    -0
      app/src/main/java/com/yzx/webebook/model/local/Void.java
  44. +207
    -0
      app/src/main/java/com/yzx/webebook/model/local/update/MigrationHelper.java
  45. +284
    -0
      app/src/main/java/com/yzx/webebook/model/local/update/Update2Helper.java
  46. +25
    -0
      app/src/main/java/com/yzx/webebook/modules/ActivityWXModule.kt
  47. +24
    -0
      app/src/main/java/com/yzx/webebook/presenter/ReadPresenter.java
  48. +14
    -0
      app/src/main/java/com/yzx/webebook/presenter/ReadView.java
  49. +221
    -0
      app/src/main/java/com/yzx/webebook/utils/BookManager.java
  50. +108
    -0
      app/src/main/java/com/yzx/webebook/utils/BrightnessUtils.java
  51. +22
    -0
      app/src/main/java/com/yzx/webebook/utils/Charset.java
  52. +101
    -0
      app/src/main/java/com/yzx/webebook/utils/Constant.java
  53. +49
    -0
      app/src/main/java/com/yzx/webebook/utils/FileStack.java
  54. +254
    -176
      app/src/main/java/com/yzx/webebook/utils/FileUtils.java
  55. +21
    -0
      app/src/main/java/com/yzx/webebook/utils/IOUtils.java
  56. +222
    -0
      app/src/main/java/com/yzx/webebook/utils/LogUtils.java
  57. +43
    -0
      app/src/main/java/com/yzx/webebook/utils/MD5Utils.java
  58. +63
    -0
      app/src/main/java/com/yzx/webebook/utils/NetworkUtils.java
  59. +34
    -0
      app/src/main/java/com/yzx/webebook/utils/PermissionsChecker.java
  60. +60
    -0
      app/src/main/java/com/yzx/webebook/utils/RxUtils.java
  61. +127
    -0
      app/src/main/java/com/yzx/webebook/utils/ScreenUtils.java
  62. +61
    -0
      app/src/main/java/com/yzx/webebook/utils/SharedPreUtils.java
  63. +181
    -323
      app/src/main/java/com/yzx/webebook/utils/StringUtils.java
  64. +162
    -0
      app/src/main/java/com/yzx/webebook/utils/SystemBarUtils.java
  65. +17
    -0
      app/src/main/java/com/yzx/webebook/utils/ToastUtils.java
  66. +31
    -0
      app/src/main/java/com/yzx/webebook/utils/media/LoaderCreator.java
  67. +137
    -0
      app/src/main/java/com/yzx/webebook/utils/media/LocalFileLoader.java
  68. +67
    -0
      app/src/main/java/com/yzx/webebook/utils/media/MediaStoreHelper.java
  69. +333
    -0
      app/src/main/java/com/yzx/webebook/widget/ReadSettingDialog.java
  70. +98
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/AnimationProvider.java
  71. +100
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/CoverPageAnim.java
  72. +230
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/HorizonPageAnim.java
  73. +37
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/NonePageAnim.java
  74. +157
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/PageAnimation.java
  75. +408
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/ScrollPageAnim.java
  76. +668
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/SimulationPageAnim.java
  77. +101
    -0
      app/src/main/java/com/yzx/webebook/widget/animation/SlidePageAnim.java
  78. +440
    -0
      app/src/main/java/com/yzx/webebook/widget/page/LocalPageLoader.java
  79. +226
    -0
      app/src/main/java/com/yzx/webebook/widget/page/NetPageLoader.java
  80. +1450
    -0
      app/src/main/java/com/yzx/webebook/widget/page/PageLoader.java
  81. +10
    -0
      app/src/main/java/com/yzx/webebook/widget/page/PageMode.java
  82. +38
    -0
      app/src/main/java/com/yzx/webebook/widget/page/PageStyle.java
  83. +378
    -0
      app/src/main/java/com/yzx/webebook/widget/page/PageView.java
  84. +70
    -0
      app/src/main/java/com/yzx/webebook/widget/page/TxtChapter.java
  85. +14
    -0
      app/src/main/java/com/yzx/webebook/widget/page/TxtPage.java
  86. +8
    -0
      app/src/main/res/anim/rotate_0_to_180.xml
  87. +8
    -0
      app/src/main/res/anim/rotate_180_to_360.xml
  88. +7
    -0
      app/src/main/res/anim/slide_bottom_in.xml
  89. +7
    -0
      app/src/main/res/anim/slide_bottom_out.xml
  90. +6
    -0
      app/src/main/res/anim/slide_left_in.xml
  91. +6
    -0
      app/src/main/res/anim/slide_left_out.xml
  92. +6
    -0
      app/src/main/res/anim/slide_right_in.xml
  93. +6
    -0
      app/src/main/res/anim/slide_right_out.xml
  94. +8
    -0
      app/src/main/res/anim/slide_top_in.xml
  95. +6
    -0
      app/src/main/res/anim/slide_top_out.xml
  96. +8
    -0
      app/src/main/res/color/selector_btn_read_setting.xml
  97. +7
    -0
      app/src/main/res/color/selector_chapter.xml
  98. +25
    -0
      app/src/main/res/drawable/seekbar_bg.xml
  99. +8
    -0
      app/src/main/res/drawable/seekbar_thumb.xml
  100. +6
    -0
      app/src/main/res/drawable/selector_btn_read_setting.xml

+ 5
- 0
.idea/jarRepositories.xml Parādīt failu

@@ -31,5 +31,10 @@
<option name="name" value="maven2" />
<option name="url" value="https://dl.bintray.com/umsdk/release" />
</remote-repository>
<remote-repository>
<option name="id" value="maven3" />
<option name="name" value="maven3" />
<option name="url" value="https://dl.bintray.com/qichuan/maven/" />
</remote-repository>
</component>
</project>

+ 43
- 4
app/build.gradle Parādīt failu

@@ -4,16 +4,25 @@ apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

//apply from: 'https://raw.githubusercontent.com/apache/incubator-weex/release/0.28/android/sdk/buildSrc/download_jsc.gradle'

apply plugin: 'org.greenrobot.greendao'

android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.yzx.webebook"
minSdkVersion 26
targetSdkVersion 30
// minSdkVersion 26
minSdkVersion 21
targetSdkVersion 25
versionCode 3
versionName "2.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86"
}
}
signingConfigs {
yzx {
@@ -59,10 +68,10 @@ android {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// debuggable true
debuggable true
signingConfig signingConfigs.release
// 移除无用的resource文件
// shrinkResources false
shrinkResources false
buildConfigField "String", "BASE_URL", '"http://192.168.69.99:9009"'
buildConfigField "String", "M_URL", '"http://192.168.69.112:8098"'
buildConfigField "String", "APP_NAME", '"家校互通(开发)"'
@@ -96,6 +105,24 @@ android {
// but continue the build even when errors are found:
abortOnError false
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}


greendao{
schemaVersion 3
daoPackage 'com.yzx.webebook.model.gen'
targetGenDir 'src/main/java'
}

repositories {
maven {
url 'https://dl.bintray.com/qichuan/maven/'
}
}

dependencies {
@@ -127,4 +154,16 @@ dependencies {
implementation 'com.umeng.umsdk:asms:1.2.1' // 必选
implementation 'com.github.thomhurst:RoundImageView:1.0.2'
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'

implementation 'org.apache.weex:sdk:0.28.0'
implementation 'com.alibaba:fastjson:1.1.46.android'


//ORM Database
implementation deps.greendao.runtime
testImplementation deps.testing.junit

//RxJava
implementation deps.reactivex.rxandroid
implementation deps.reactivex.rxjava2
}

+ 9
- 5
app/src/main/AndroidManifest.xml Parādīt failu

@@ -12,15 +12,23 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.BATTERY_STATS"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
<activity android:name=".activity.ReadActivity"></activity>
<activity android:name=".activity.WeexTestActivity" />
<activity
android:name=".activity.HomeActivity"
android:configChanges="orientation|keyboard"
@@ -51,10 +59,6 @@
android:name=".activity.BookActivity"
android:configChanges="orientation|keyboard"
android:screenOrientation="portrait" />
<activity
android:name=".activity.Main1Activity"
android:configChanges="orientation|keyboard"
android:screenOrientation="portrait" />

<provider
android:name=".utils.YzxFileProvider"


+ 132
- 0
app/src/main/assets/weex/components/HelloWorld.js Parādīt failu

@@ -0,0 +1,132 @@
// { "framework": "Vue"}

/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 2);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

var __vue_exports__, __vue_options__
var __vue_styles__ = []

/* template */
var __vue_template__ = __webpack_require__(1)
__vue_options__ = __vue_exports__ = __vue_exports__ || {}
if (
typeof __vue_exports__.default === "object" ||
typeof __vue_exports__.default === "function"
) {
if (Object.keys(__vue_exports__).some(function (key) { return key !== "default" && key !== "__esModule" })) {console.error("named exports are not supported in *.vue files.")}
__vue_options__ = __vue_exports__ = __vue_exports__.default
}
if (typeof __vue_options__ === "function") {
__vue_options__ = __vue_options__.options
}
__vue_options__.__file = "D:\\demo\\androidweex\\src\\components\\HelloWorld.vue"
__vue_options__.render = __vue_template__.render
__vue_options__.staticRenderFns = __vue_template__.staticRenderFns
__vue_options__.style = __vue_options__.style || {}
__vue_styles__.forEach(function (module) {
for (var name in module) {
__vue_options__.style[name] = module[name]
}
})
if (typeof __register_static_styles__ === "function") {
__register_static_styles__(__vue_options__._scopeId, __vue_styles__)
}

module.exports = __vue_exports__


/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('text', {
staticClass: ["message"]
}, [_vm._v("Now, let's use Vue.js to build your Weex app.")])
},staticRenderFns: []}
module.exports.render._withStripped = true

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _HelloWorld = __webpack_require__(0);

var _HelloWorld2 = _interopRequireDefault(_HelloWorld);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

_HelloWorld2.default.el = '#root';
new Vue(_HelloWorld2.default);

/***/ })
/******/ ]);

+ 322
- 0
app/src/main/assets/weex/index.js
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 287
- 0
app/src/main/assets/weex/reader.js Parādīt failu

@@ -0,0 +1,287 @@
// { "framework": "Vue"}

/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 9);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

var __vue_exports__, __vue_options__
var __vue_styles__ = []

/* template */
var __vue_template__ = __webpack_require__(1)
__vue_options__ = __vue_exports__ = __vue_exports__ || {}
if (
typeof __vue_exports__.default === "object" ||
typeof __vue_exports__.default === "function"
) {
if (Object.keys(__vue_exports__).some(function (key) { return key !== "default" && key !== "__esModule" })) {console.error("named exports are not supported in *.vue files.")}
__vue_options__ = __vue_exports__ = __vue_exports__.default
}
if (typeof __vue_options__ === "function") {
__vue_options__ = __vue_options__.options
}
__vue_options__.__file = "D:\\demo\\androidweex\\src\\components\\HelloWorld.vue"
__vue_options__.render = __vue_template__.render
__vue_options__.staticRenderFns = __vue_template__.staticRenderFns
__vue_options__.style = __vue_options__.style || {}
__vue_styles__.forEach(function (module) {
for (var name in module) {
__vue_options__.style[name] = module[name]
}
})
if (typeof __register_static_styles__ === "function") {
__register_static_styles__(__vue_options__._scopeId, __vue_styles__)
}

module.exports = __vue_exports__


/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('text', {
staticClass: ["message"]
}, [_vm._v("Now, let's use Vue.js to build your Weex app.")])
},staticRenderFns: []}
module.exports.render._withStripped = true

/***/ }),
/* 2 */,
/* 3 */,
/* 4 */,
/* 5 */,
/* 6 */,
/* 7 */,
/* 8 */,
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _reader = __webpack_require__(10);

var _reader2 = _interopRequireDefault(_reader);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

_reader2.default.el = '#root';
new Vue(_reader2.default);

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

var __vue_exports__, __vue_options__
var __vue_styles__ = []

/* styles */
__vue_styles__.push(__webpack_require__(11)
)

/* script */
__vue_exports__ = __webpack_require__(12)

/* template */
var __vue_template__ = __webpack_require__(13)
__vue_options__ = __vue_exports__ = __vue_exports__ || {}
if (
typeof __vue_exports__.default === "object" ||
typeof __vue_exports__.default === "function"
) {
if (Object.keys(__vue_exports__).some(function (key) { return key !== "default" && key !== "__esModule" })) {console.error("named exports are not supported in *.vue files.")}
__vue_options__ = __vue_exports__ = __vue_exports__.default
}
if (typeof __vue_options__ === "function") {
__vue_options__ = __vue_options__.options
}
__vue_options__.__file = "D:\\demo\\androidweex\\src\\reader.vue"
__vue_options__.render = __vue_template__.render
__vue_options__.staticRenderFns = __vue_template__.staticRenderFns
__vue_options__._scopeId = "data-v-06390049"
__vue_options__.style = __vue_options__.style || {}
__vue_styles__.forEach(function (module) {
for (var name in module) {
__vue_options__.style[name] = module[name]
}
})
if (typeof __register_static_styles__ === "function") {
__register_static_styles__(__vue_options__._scopeId, __vue_styles__)
}

module.exports = __vue_exports__


/***/ }),
/* 11 */
/***/ (function(module, exports) {

module.exports = {
"wrapper": {
"justifyContent": "center",
"alignItems": "center"
},
"logo": {
"width": "424",
"height": "200"
},
"greeting": {
"textAlign": "center",
"marginTop": "70",
"fontSize": "50",
"color": "#41b883"
},
"message": {
"marginTop": "30",
"marginRight": "30",
"marginBottom": "30",
"marginLeft": "30",
"fontSize": "32",
"color": "#727272"
}
}

/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
value: true
});

var _HelloWorld = __webpack_require__(0);

var _HelloWorld2 = _interopRequireDefault(_HelloWorld);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

exports.default = {
name: 'App',
components: {
HelloWorld: _HelloWorld2.default
},
data: function data() {
return {
logo: 'https://gw.alicdn.com/tfs/TB1yopEdgoQMeJjy1XaXXcSsFXa-640-302.png',
params: "1"
};
},
mounted: function mounted() {
this.params = weex.config.params;
},

methods: {
onJumpClick: function onJumpClick() {
var json = {
url: "weex/index.js",
params: "你好首页,我是reader"
};
weex.requireModule("activity").navigateTo(json);
}
}
}; //
//
//
//
//
//
//
//
//
//

/***/ }),
/* 13 */
/***/ (function(module, exports) {

module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
staticClass: ["wrapper"]
}, [_c('image', {
staticClass: ["logo"],
attrs: {
"src": _vm.logo
},
on: {
"click": _vm.onJumpClick
}
}), _c('text', {
staticClass: ["greeting"]
}, [_vm._v("这是读书页面")]), _c('text', {
staticClass: ["greeting"]
}, [_vm._v(_vm._s(_vm.params))])])
},staticRenderFns: []}
module.exports.render._withStripped = true

/***/ })
/******/ ]);

+ 47
- 2
app/src/main/java/com/yzx/webebook/App.kt Parādīt failu

@@ -1,19 +1,64 @@
package com.yzx.webebook

import android.app.Application
import android.content.Context
import android.util.Log
import com.blankj.utilcode.util.Utils
import com.lzy.okgo.OkGo
import com.umeng.analytics.MobclickAgent
import com.umeng.commonsdk.UMConfigure
import com.yzx.webebook.adapter.ImageAdapter
import com.yzx.webebook.modules.ActivityWXModule
import org.apache.weex.InitConfig
import org.apache.weex.WXEnvironment
import org.apache.weex.WXSDKEngine
import org.apache.weex.adapter.DefaultWXHttpAdapter
import org.apache.weex.bridge.WXBridgeManager

class App: Application() {
class App : Application() {


companion object {
var app: App? = null
fun getContext(): App {
return app ?: App()
}
}

override fun onCreate() {
super.onCreate()
app = this
val config = InitConfig.Builder() //图片库接口
.setImgAdapter(ImageAdapter()) //网络库接口
.setHttpAdapter(DefaultWXHttpAdapter())
.build()
WXSDKEngine.initialize(this, config)

WXBridgeManager.updateGlobalConfig("wson_on")
WXEnvironment.setOpenDebugLog(true)
WXEnvironment.setApkDebugable(true)
WXSDKEngine.addCustomOptions("appName", "WXSample")
WXSDKEngine.addCustomOptions("appGroup", "WXApp")
val registerSuccess = WXSDKEngine.registerModule("navevent", ActivityWXModule::class.java)

Log.i("welog", "WXSDKEngine.isInitializedApplication: " + WXSDKEngine.isInitialized())
Log.i("welog", "WXSDKEngine.registerModule: $registerSuccess")

OkGo.getInstance().init(this)


Utils.init(this)

UMConfigure.init(this, "60334d01425ec25f10fbd5b4", "ebook", UMConfigure.DEVICE_TYPE_PHONE, "")
UMConfigure.init(
this,
"60334d01425ec25f10fbd5b4",
"ebook",
UMConfigure.DEVICE_TYPE_PHONE,
""
)
MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO)

}


}

+ 44
- 1
app/src/main/java/com/yzx/webebook/MainActivity.kt Parādīt failu

@@ -4,13 +4,17 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.text.TextUtils
import android.util.Log
import com.blankj.utilcode.util.StringUtils
import com.bumptech.glide.Glide
import com.yzx.webebook.activity.*
import com.yzx.webebook.activity.base.BaseActivity
import com.yzx.webebook.config.Config
import com.yzx.webebook.model.User
import com.yzx.webebook.model.bean.CollBookBean
import com.yzx.webebook.model.local.BookRepository
import com.yzx.webebook.presenter.base.BasePresenter
import com.yzx.webebook.utils.MD5Utils
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
@@ -84,7 +88,7 @@ class MainActivity : BaseActivity<BasePresenter<*>>() {
}

btn7.setOnClickListener {
startActivity<Main1Activity>()
startActivity<ReadActivity>()
}

/*OkGo.post<String>("https://fileupload.oa.qbjjyyun.net/edufile/fileUpload")
@@ -112,9 +116,48 @@ class MainActivity : BaseActivity<BasePresenter<*>>() {
}
})*/

// val apps = AppUtils.getAppInfo("com.tencent.weread.eink")
// appInfos.text = apps.toString();
appInfos.setOnClickListener {
val intent = packageManager.getLaunchIntentForPackage("com.tencent.weread.eink")
intent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
startActivity(intent)
Log.d("AppUtils", "initView: ${intent.toString()}")
}


btn8.setOnClickListener {
startActivity<WeexTestActivity>("url" to "weex/index.js","params" to "我是android传递的消息")
}

btn9.setOnClickListener {
val intent = packageManager.getLaunchIntentForPackage("com.example.weexdemo")
intent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
startActivity(intent)
Log.d("AppUtils", "initView: ${intent.toString()}")
}
}

override fun initData() {

saveDb()
}

fun saveDb(){
val path = "/storage/emulated/0/sgyy.txt";
val collBook = CollBookBean()
collBook._id = MD5Utils.strToMd5By16(path)
collBook.title = "三国演义sgyy"
collBook.author = "yzx"
collBook.shortIntro = ""
collBook.cover = path
collBook.lastChapter = "开始阅读"
collBook.updated = "2021年5月7日18:20:25"
collBook.lastRead = "2021年5月7日18:20:34"
collBook.setIsLocal(true)

BookRepository.getInstance()
.saveCollBooks(mutableListOf(collBook))
}

override fun initPresenter(): BasePresenter<*>? = null


+ 19
- 2
app/src/main/java/com/yzx/webebook/activity/HomeActivity.kt Parādīt failu

@@ -1,5 +1,6 @@
package com.yzx.webebook.activity

import android.Manifest
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.Dialog
@@ -8,6 +9,7 @@ import android.content.DialogInterface
import android.util.Log
import android.view.View
import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.GridLayoutManager
import com.allenliu.versionchecklib.v2.AllenVersionChecker
@@ -34,7 +36,6 @@ import com.yzx.webebook.model.Version
import com.yzx.webebook.presenter.base.BasePresenter
import com.yzx.webebook.widget.BaseDialog
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.activity_home.titleTv
import org.jetbrains.anko.find
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
@@ -47,8 +48,9 @@ class HomeActivity : BaseActivity<BasePresenter<*>>() {
list.add(HomeItem("家庭作业", R.mipmap.ic_home_work, "/homework/ebook_list"))
list.add(HomeItem("笔记本", R.mipmap.ic_home_notebook, "/notebook/list"))
list.add(HomeItem("背诵默写", R.mipmap.ic_home_write, "/write/index"))
list.add(HomeItem("书架", R.mipmap.ic_home_rack))
list.add(HomeItem("阅读", R.mipmap.ic_home_rack,"/read/index"))
list.add(HomeItem("错题本", R.mipmap.ic_home_errorbook, "/errorbook/ebookindex"))
list.add(HomeItem("随堂检测", R.mipmap.ic_home_classwork))
list.add(HomeItem("我的", R.mipmap.ic_home_my, "/mine/ebook"))
HomeAdapter(list)
}
@@ -102,8 +104,23 @@ class HomeActivity : BaseActivity<BasePresenter<*>>() {
}
}
}


//请求权限
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
PERMISSIONS_REQUEST_STORAGE
)
}

private val PERMISSIONS_REQUEST_STORAGE = 1

val PERMISSIONS = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)

override fun initPresenter(): BasePresenter<*>? {
return null
}


+ 0
- 725
app/src/main/java/com/yzx/webebook/activity/Main1Activity.java Parādīt failu

@@ -1,725 +0,0 @@
package com.yzx.webebook.activity;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.yzx.webebook.utils.FileUtils;
import com.yzx.webebook.utils.GsonHelper;
import com.yzx.webebook.utils.ListUtils;
import com.wetao.note.NotePageInfo;
import com.wetao.note.OnInitedListener;
import com.wetao.note.WeNoteView;
import com.wetao.note.WePoint;
import com.yzx.webebook.R;

import android.Manifest;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.RelativeLayout;


public class Main1Activity extends Activity {
private static final String TAG = "MainActivity";

private static final String NOTE1_FOLDER_DIR = "/mnt/sdcard/MyTestNote/Note/";

static final int[] mBGDrawableList = {-1, R.drawable.background1, R.drawable.background2,
R.drawable.background3, R.drawable.background4, R.drawable.background5, R.drawable.background6,
R.drawable.background7, R.drawable.background8, R.drawable.background9, R.drawable.background10};

private static final int MSG_SAVE_END = 2001;

private WeNoteView mView;

private Button mDeleteBtn;
private Button mPicBtn;
private Button mClearBtn;
private Button mZoomBtn;
private Button mUndoBtn;
private Button mRedoBtn;
private Button mPenWidthBtn;
private Button mEraserWidthBtn;
private Button mCancelBtn;
private Button mTypeBtn;

private CheckBox mVisibleCB;

private static int mBGId = 0;

private boolean mReDrawEnable = false;

private static ArrayList<WePoint> mWePointList = null;

private static NotePageInfo mNotePageInfo = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main1);
checkPermission();
Log.d(TAG, "Flash test : +++++++ onCreate()");

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

mView = findViewById(R.id.note_view);
mView.setBackground(null);
mBGId = 0;

mDeleteBtn = findViewById(R.id.delete);
mDeleteBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (deleteNote(NOTE1_FOLDER_DIR)) {
cancel(false);
Main1Activity.this.finish();
}
}
});

mPicBtn = findViewById(R.id.init);
mPicBtn.setText("背景" + mBGId);
mPicBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mBGId++;
if (mBGId >= mBGDrawableList.length) mBGId = 0;
if (mBGId == 0) {
mView.setBackground(new ColorDrawable());
mPicBtn.setText("背景0");
} else {
mView.setBackgroundResource(mBGDrawableList[mBGId]);
mPicBtn.setText("背景" + mBGId);
}
saveBGId(NOTE1_FOLDER_DIR, mBGId);
}
});

mClearBtn = findViewById(R.id.clear);
mClearBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mView.clear();
}
});

mPenWidthBtn = findViewById(R.id.pen_width);
mPenWidthBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int width = mView.getPenWidth();
if (width == 10) {
width = 1;
} else {
width++;
}

if (width > 0 && width <= 10) {
mView.setPenWidth(width);
mPenWidthBtn.setText("宽度" + width);
}
}
});

mZoomBtn = findViewById(R.id.zoom);
mZoomBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mView.getLayoutParams();
// 设置宽为100dp
params.width = 1000;
// 设置高为100dp
params.height = 200;
// 根据布局参数的设置,重新设置view(这里用了text view,当然其他的view也是通用的)的大小
mView.setLayoutParams(params);
}
});

mUndoBtn = findViewById(R.id.undo);
mUndoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mView.unDo();
}
});

mRedoBtn = findViewById(R.id.redo);
mRedoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mView.reDo();
}
});

mEraserWidthBtn = findViewById(R.id.eraser_width);
mEraserWidthBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int width = mView.getEraserWidth();
if (width == 20) {
width = 1;
} else {
width++;
}

if (width > 0 && width <= 20) {
mView.setEraserWidth(width);
mEraserWidthBtn.setText("橡皮宽" + width);
}
}
});

mCancelBtn = findViewById(R.id.cancel);
mCancelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
cancel(true);
Main1Activity.this.finish();
}
});

mTypeBtn = findViewById(R.id.draw_circle);
mTypeBtn.setTypeface(null, Typeface.BOLD);
mTypeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int type = mView.getPenType();
Log.d(TAG, "Flash test : +++++++ onClick() type btn current type = " + type);
if (type == WeNoteView.TYPE_DRAW_CURVE) {
setPenType(WeNoteView.TYPE_DRAW_STROKES);
mTypeBtn.setText("笔锋");
} else if (type == WeNoteView.TYPE_DRAW_STROKES) {
setPenType(WeNoteView.TYPE_DRAW_LINE);
mTypeBtn.setText("直线");
} else if (type == WeNoteView.TYPE_DRAW_LINE) {
setPenType(WeNoteView.TYPE_DRAW_TRIANGLE);
mTypeBtn.setText("三角形");
} else if (type == WeNoteView.TYPE_DRAW_TRIANGLE) {
setPenType(WeNoteView.TYPE_DRAW_CIRCLE);
mTypeBtn.setText("圆形");
} else if (type == WeNoteView.TYPE_DRAW_CIRCLE) {
setPenType(WeNoteView.TYPE_DRAW_RECT);
mTypeBtn.setText("矩形");
} else if (type == WeNoteView.TYPE_DRAW_RECT) {
setPenType(WeNoteView.TYPE_DRAW_ERASER);
mTypeBtn.setText("橡皮擦");
} else if (type == WeNoteView.TYPE_DRAW_ERASER) {
setPenType(WeNoteView.TYPE_DRAW_CIRCLE_ERASER);
mTypeBtn.setText("圈涂");
} else if (type == WeNoteView.TYPE_DRAW_CIRCLE_ERASER) {
setPenType(WeNoteView.TYPE_DRAW_CURVE);
mTypeBtn.setText("曲线");
}

}
});

mVisibleCB = findViewById(R.id.visible);
mVisibleCB.setSelected(true);
mVisibleCB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mVisibleCB.isChecked()) {
mVisibleCB.setSelected(false);
mView.setVisibility(View.INVISIBLE);
} else {
mVisibleCB.setSelected(true);
mView.setVisibility(View.VISIBLE);
}
}
});

mWePointList = new ArrayList<WePoint>();
mWePointList.clear();

mView.setOnFinishListener(new OnInitedListener() {
@Override
public void onInited() {
//mView.setTouchEventHander(mPointHandler);

Log.d(TAG, "Flash test : ++++++ mRunnable() start loader notepageinfo");
if (mNotePageInfo == null) {
mNotePageInfo = getNotePageInfo(NOTE1_FOLDER_DIR);
}
if (mNotePageInfo == null) {
Log.d(TAG, "Flash test : +++++ no note, so init a page");
initPage(NOTE1_FOLDER_DIR, 0, mBGId);
}
loadOldPage();
}

@Override
public void onSizeChanged() {

}
});

}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.d(TAG, "Flash test : ++++++++++ onWindowFocusChanged() hasFocus = " + hasFocus);
if (hasFocus) {
mView.onResume();
mReDrawEnable = true;
} else {
mView.onPause();
mReDrawEnable = false;
}
}

private void setPenType(int type) {
mView.setPenType(type);
mView.updateEnableStatus();
}

private void loadOldPage() {
if (mNotePageInfo == null) {
return;
}
Log.d(TAG, "Flash test : +++++ loadOldPage() mNotePageInfo = " + mNotePageInfo);
mBGId = mNotePageInfo.drwId;
if (mBGDrawableList[mBGId] == -1) {
mView.setBackground(new ColorDrawable());
} else {
mView.setBackgroundResource(mBGDrawableList[mBGId]);
}
mView.showExistPage(mNotePageInfo.notePath);
mPicBtn.setText("背景" + mBGId);
}

public void onResume() {
Log.d(TAG, "Flash test : +++++++ onResume()");
//mView.setEnable(true);
mView.onResume();
mReDrawEnable = true;
super.onResume();
}

public void onPause() {
Log.d(TAG, "Flash test : +++++++ onPause()");
mView.onPause();
mReDrawEnable = false;
super.onPause();
}

public void onDestroy() {
Log.d(TAG, "Flash test : +++++++ onDestroy()");
mView.exitView();
super.onDestroy();
// System.exit(0);
}

private Handler mPointHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
int what = msg.what;
//同步获取的笔记点坐标信息msg
if (what == WeNoteView.TOUCH_EVENT) {
WePoint point = (WePoint) msg.obj;
Log.d(TAG, "Flash test : ++++++++ mPointHandler WePoint = " + point);
mWePointList.add(point);
transData(point);
}

//保存笔记结束msg
if (what == MSG_SAVE_END) {
int status = msg.arg1;
if (status > 0) { //保存成功
Log.d(TAG, "Flash test : ++++++++ mPointHandler page save ok");
//Toast.makeText(mContext, "Note saved OK", Toast.LENGTH_SHORT).show();
} else { //保存失败
Log.d(TAG, "Flash test : ++++++++ mPointHandler page save failed");
//Toast.makeText(mContext, "Note saved Fail", Toast.LENGTH_SHORT).show();
}
}
}
};

private void cancel(boolean save) {
if (save && mView.isHandwritingExist()) {
saveNote(NOTE1_FOLDER_DIR, mView, mBGId);
}
Main1Activity.this.finish();
}

private boolean initPage(String dir, int viewId, int bgId) {
Log.d(TAG, "Flash test : +++++++++ intPage()");
File file = new File(dir);
if(!file.exists()) {
Log.e(TAG, "Flash test : +++++++++ addPageFolder() no folder path = " + dir);
if (!makeDir(file)) {
return false;
}
}

String notePath = null;
file = new File(dir + "/note.png");
if(file.exists()) {
notePath = dir + "/note.png";
}
file = new File(dir + "/.drawable.txt");
if(!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
if (bgId == 1) bgId = 2;
saveBGId(dir, bgId);
mNotePageInfo = new NotePageInfo(notePath, bgId);
return true;
}

private boolean deleteNote(String dir) {
Log.d(TAG, "Flash test : +++++++++ deletdNote()");
File file = new File(dir);
if(!file.exists()) {
Log.e(TAG, "Flash test : +++++++++ deletdPageFolder() no folder path = " + dir);
return true;
}
deleteDirWithFile(file);
return true;
}

private boolean saveNote(String dir, WeNoteView view, int bgId) {
Log.d(TAG, "Flash test : ++++++++ saveNote()");
File f = new File(dir);
if(!f.exists()) {
Log.d(TAG, "Flash test : +++++++ saveNote() create folder " + dir);
if (!makeDir(f)) {
return false;
}
}

String noteName = "note.png";
final Bitmap bmp = Bitmap.createBitmap(view.getCurrentNoteBitmap());
if (bmp == null) {
Log.e(TAG, "Flash test : ++++ saveNote() getCurrentNoteBitmap is null");
return false;
}

String path = dir + "/" + noteName;
if (!saveNoteWithoutBG(path, bmp)) {
Log.e(TAG, "Flash test : ++++ saveNote() saveNoteWithoutBG fail");
freeBitmap(bmp);
return false;
}

saveBGId(dir, bgId);
freeBitmap(bmp);
Log.e(TAG, "Flash test : ++++ saveNote() OK !!!!!");
return true;
}

private static boolean saveBGId(String dir, int bgId) {
Log.d(TAG, "Flash test : ++++++++ saveBGId drwId = " + bgId);
File f = new File(dir);
if(!f.exists()) {
Log.d(TAG, "Flash test : +++++++ saveAllNote() create folder " + dir);
if (!makeDir(f)) {
return false;
}
}

String path = dir + "/.drawable.txt";
writeFileData(path, String.valueOf(bgId));
return true;
}

private static void freeBitmap(Bitmap bit) {
if (bit != null && !bit.isRecycled()) {
bit.recycle();
}
}



//保存不带背景的笔记为png
private boolean saveNoteWithoutBG(String picPath, Bitmap bmp) {
Log.d(TAG, "Flash test : +++++++ saveNoteWithoutBG() picPath = " + picPath);
File f = new File(picPath);
if(f.exists()) {
f.delete();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
bmp.compress(Bitmap.CompressFormat.PNG, 90, fos);
try {
fos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}

//保存带背景的笔记为png
private boolean saveAll(String picPath, Bitmap bmp, Drawable drw) {
Log.d(TAG, "Flash test : ++++ saveAll() picPath = " + picPath);
int width = bmp.getWidth();
int height = bmp.getHeight();

Bitmap bitmap = Bitmap.createBitmap(width, height,
drw.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drw.setBounds(0, 0, width, height);
drw.draw(canvas);

canvas.drawBitmap(bmp, new Rect(0, 0, width, height),
new Rect(0, 0, width, height) , null);
canvas.save();//Canvas.ALL_SAVE_FLAG);
canvas.restore();

boolean result = saveNoteWithoutBG(picPath, bitmap);
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
System.gc();
return result;
}

//保存笔记背景为png
private boolean saveNoteBG(String picPath, Drawable drw, int width, int height) {
Log.d(TAG, "Flash test : ++++ saveNoteBG() picPath = " + picPath);
Bitmap bitmap = Bitmap.createBitmap(width, height,
drw.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drw.setBounds(0, 0, width, height);
drw.draw(canvas);

boolean result = saveNoteWithoutBG(picPath, bitmap);
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
System.gc();
return result;
}

//重绘之前绘制的画笔
private void drawWePointList() {
if (mView.isReDrawDoing()) {
Log.i(TAG, "Flash test : +++++ drawWePointList() return because isReDrawDoing is true");
return;
}
if (mWePointList != null && !mWePointList.isEmpty()) {
new Thread(new Runnable() {
@Override
public void run() {
mView.setReDrawEnable(true);
mReDrawEnable = true;
int size = mWePointList.size();
int i = 0;
while (i<size) {
while(!mReDrawEnable) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
WePoint wp = mWePointList.get(i);
mView.drawPoint(wp);
try {
if (i+1 < size) Thread.sleep(mWePointList.get(i+1).eventTime - wp.eventTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
i++;
}
mView.setReDrawEnable(false);
}
}).start();
}
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Main1Activity.this.finish();
return true;
}
return super.onKeyDown(keyCode, event);
}

//从笔记文件夹中读取当前笔记的页面信息
private NotePageInfo getNotePageInfo(String path) {
Log.d(TAG, "Flash test : +++++++++ getNotePageInfo() path = " + path);
File file = new File(path);
if(!file.exists()) {
Log.e(TAG, "Flash test : +++++++++ getNotePageInfo() no folder path = " + path);
return null;
}
File[] files = file.listFiles();
if (files == null) {
Log.e(TAG, "Flash test : +++++++++ getNotePageInfo() no files in this Note folder = " + path);
return null;
}
NotePageInfo info = new NotePageInfo(null, 0);
String notePath = path + "/note.png";
file = new File(notePath);
if(file.exists()) {
info.notePath = notePath;
}
String drwId = readFileData(path + "/.drawable.txt");
info.drwId = Integer.parseInt(drwId);
return info;
}

private void checkPermission() {
boolean isGranted = true;
if (android.os.Build.VERSION.SDK_INT >= 23) {
if (this.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//如果没有写sd卡权限
isGranted = false;
}
if (this.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
isGranted = false;
}
Log.i("cbs","isGranted == "+isGranted);
if (!isGranted) {
this.requestPermissions(
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission
.ACCESS_FINE_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
102);
}
}
}

private static boolean makeDir(File folder) {
try {
//按照指定的路径创建文件夹
folder.mkdirs();
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "Flash test : +++++ makeDir() create dir " + folder.getPath() + " error = " + e);
return false;
}
Log.d(TAG, "Flash test : +++++ makeDir() create dir " + folder.getPath());
return true;
}

private void deleteDirWithFile(File dir) {
if (dir == null || !dir.exists() || !dir.isDirectory())
return;
for (File file : dir.listFiles()) {
if (file.isFile())
file.delete(); // 删除所有文件
else if (file.isDirectory())
deleteDirWithFile(file); // 递规的方式删除文件夹
}
dir.delete();// 删除目录本身
}

public static void writeFileData(String fileName, String content) {
File file = new File(fileName);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

try {
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
bw.write(content);
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//打开指定文件,读取其数据,返回字符串对象
public static String readFileData(String fileName) {
File file = new File(fileName);
if (!file.exists()) {
return "1";
}
BufferedReader br;
try {
br = new BufferedReader(new FileReader(file));
//System.out.println("br.readLine=" + br.readLine());
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "1";
}

private List<String> stringList;
private void transData(WePoint point) {
if (ListUtils.isEmpty(stringList)) {
stringList = new ArrayList<>();
}
String s = GsonHelper.toJson(point);
stringList.add(s);
stringList.add("\r\n");
if (point.action == MotionEvent.ACTION_UP) {
String join = String.join("", stringList);
saveData(join);
stringList.clear();
}
}

private void saveData(String msg) {
File file = new File("/mnt/sdcard/point.txt");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileUtils.writeFile(file.getPath(), msg, true);
}
}

+ 765
- 0
app/src/main/java/com/yzx/webebook/activity/ReadActivity.java Parādīt failu

@@ -0,0 +1,765 @@
package com.yzx.webebook.activity;


import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;

import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;

import com.google.android.material.appbar.AppBarLayout;
import com.yzx.webebook.R;
import com.yzx.webebook.activity.base.BaseActivity;
import com.yzx.webebook.adapter.CategoryAdapter;
import com.yzx.webebook.model.bean.BookChapterBean;
import com.yzx.webebook.model.bean.CollBookBean;
import com.yzx.webebook.model.local.BookRepository;
import com.yzx.webebook.model.local.ReadSettingManager;
import com.yzx.webebook.presenter.ReadPresenter;
import com.yzx.webebook.presenter.ReadView;
import com.yzx.webebook.utils.BrightnessUtils;
import com.yzx.webebook.utils.Constant;
import com.yzx.webebook.utils.LogUtils;
import com.yzx.webebook.utils.RxUtils;
import com.yzx.webebook.utils.ScreenUtils;
import com.yzx.webebook.utils.StringUtils;
import com.yzx.webebook.utils.SystemBarUtils;
import com.yzx.webebook.widget.ReadSettingDialog;
import com.yzx.webebook.widget.page.PageLoader;
import com.yzx.webebook.widget.page.PageView;
import com.yzx.webebook.widget.page.TxtChapter;

import org.jetbrains.annotations.Nullable;

import java.util.List;

import io.reactivex.disposables.Disposable;

import static android.view.View.GONE;
import static android.view.View.VISIBLE;

public class ReadActivity extends BaseActivity<ReadPresenter> implements ReadView {
private static final String TAG = "ReadActivity";
public static final int REQUEST_MORE_SETTING = 1;
public static final String EXTRA_COLL_BOOK = "extra_coll_book";
public static final String EXTRA_IS_COLLECTED = "extra_is_collected";

// 注册 Brightness 的 uri
private final Uri BRIGHTNESS_MODE_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
private final Uri BRIGHTNESS_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
private final Uri BRIGHTNESS_ADJ_URI =
Settings.System.getUriFor("screen_auto_brightness_adj");

private static final int WHAT_CATEGORY = 1;
private static final int WHAT_CHAPTER = 2;

DrawerLayout mDlSlide;
/*************top_menu_view*******************/
AppBarLayout mAblTopMenu;
TextView mTvCommunity;
TextView mTvBrief;
/***************content_view******************/
PageView mPvPage;
/***************bottom_menu_view***************************/
TextView mTvPageTip;

LinearLayout mLlBottomMenu;
TextView mTvPreChapter;
SeekBar mSbChapterProgress;
TextView mTvNextChapter;
TextView mTvCategory;
TextView mTvNightMode;
TextView mTvSetting;
/***************left slide*******************************/
ListView mLvCategory;
/*****************view******************/
private ReadSettingDialog mSettingDialog;
private PageLoader mPageLoader;
private Animation mTopInAnim;
private Animation mTopOutAnim;
private Animation mBottomInAnim;
private Animation mBottomOutAnim;
private CategoryAdapter mCategoryAdapter;
private CollBookBean mCollBook;
//控制屏幕常亮
private PowerManager.WakeLock mWakeLock;


/***************params*****************/
private boolean isCollected = false; // isFromSDCard
private boolean isNightMode = false;
private boolean isFullScreen = false;
private boolean isRegistered = false;

private String mBookId;


private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);

switch (msg.what) {
case WHAT_CATEGORY:
mLvCategory.setSelection(mPageLoader.getChapterPos());
break;
case WHAT_CHAPTER:
mPageLoader.openChapter();
break;
}
}
};
// 接收电池信息和时间更新的广播
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
int level = intent.getIntExtra("level", 0);
mPageLoader.updateBattery(level);
}
// 监听分钟的变化
else if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
mPageLoader.updateTime();
}
}
};

// 亮度调节监听
// 由于亮度调节没有 Broadcast 而是直接修改 ContentProvider 的。所以需要创建一个 Observer 来监听 ContentProvider 的变化情况。
private ContentObserver mBrightObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
onChange(selfChange, null);
}

@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange);

// 判断当前是否跟随屏幕亮度,如果不是则返回
if (selfChange || !mSettingDialog.isBrightFollowSystem()) return;

// 如果系统亮度改变,则修改当前 Activity 亮度
if (BRIGHTNESS_MODE_URI.equals(uri)) {
Log.d(TAG, "亮度模式改变");
} else if (BRIGHTNESS_URI.equals(uri) && !BrightnessUtils.isAutoBrightness(ReadActivity.this)) {
Log.d(TAG, "亮度模式为手动模式 值改变");
BrightnessUtils.setBrightness(ReadActivity.this, BrightnessUtils.getScreenBrightness(ReadActivity.this));
} else if (BRIGHTNESS_ADJ_URI.equals(uri) && BrightnessUtils.isAutoBrightness(ReadActivity.this)) {
Log.d(TAG, "亮度模式为自动模式 值改变");
BrightnessUtils.setDefaultBrightness(ReadActivity.this);
} else {
Log.d(TAG, "亮度调整 其他");
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public int getInflateId() {
return R.layout.activity_read;
}

@Override
public void initView() {
mDlSlide = findViewById(R.id.read_dl_slide);
mAblTopMenu = findViewById(R.id.read_abl_top_menu);
mTvCommunity = findViewById(R.id.read_tv_community);
mTvBrief = findViewById(R.id.read_tv_brief);
mPvPage = findViewById(R.id.read_pv_page);
mTvPageTip = findViewById(R.id.read_tv_page_tip);
mLlBottomMenu = findViewById(R.id.read_ll_bottom_menu);
mTvPreChapter = findViewById(R.id.read_tv_pre_chapter);
mSbChapterProgress = findViewById(R.id.read_sb_chapter_progress);
mTvNextChapter = findViewById(R.id.read_tv_next_chapter);
mTvCategory = findViewById(R.id.read_tv_category);
mTvNightMode = findViewById(R.id.read_tv_night_mode);
mTvSetting = findViewById(R.id.read_tv_setting);
mLvCategory = findViewById(R.id.read_iv_category);



}

@Override
public void initData() {
// mCollBook = getIntent().getParcelableExtra(EXTRA_COLL_BOOK);

mCollBook = BookRepository.getInstance().getCollBook("75979111277b0955");


isCollected = getIntent().getBooleanExtra(EXTRA_IS_COLLECTED, false);
isNightMode = ReadSettingManager.getInstance().isNightMode();
isFullScreen = ReadSettingManager.getInstance().isFullScreen();

mBookId = mCollBook.get_id();

Log.d(TAG, "initData: " + mCollBook.toString());

//获取页面加载器
mPageLoader = mPvPage.getPageLoader(mCollBook);
//禁止滑动展示DrawerLayout
mDlSlide.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
//侧边打开后,返回键能够起作用
mDlSlide.setFocusableInTouchMode(false);
mSettingDialog = new ReadSettingDialog(this, mPageLoader);

setUpAdapter();

//夜间模式按钮的状态
toggleNightMode();

//注册广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
intentFilter.addAction(Intent.ACTION_TIME_TICK);
registerReceiver(mReceiver, intentFilter);

//设置当前Activity的Brightness
if (ReadSettingManager.getInstance().isBrightnessAuto()) {
BrightnessUtils.setDefaultBrightness(this);
} else {
BrightnessUtils.setBrightness(this, ReadSettingManager.getInstance().getBrightness());
}

//初始化屏幕常亮类
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "ireader:keep bright");

//隐藏StatusBar
mPvPage.post(
() -> hideSystemBar()
);

//初始化TopMenu
initTopMenu();

//初始化BottomMenu
initBottomMenu();


Disposable disposable = BookRepository.getInstance()
.getBookChaptersInRx(mBookId)
.compose(RxUtils::toSimpleSingle)
.subscribe(
(bookChapterBeen, throwable) -> {
// 设置 CollBook
mPageLoader.getCollBook().setBookChapters(bookChapterBeen);
// 刷新章节列表
mPageLoader.refreshChapterList();
// 如果是网络小说并被标记更新的,则从网络下载目录
// if (mCollBook.isUpdate() && !mCollBook.isLocal()) {
// getMPresenter().loadCategory(mBookId);
// }
LogUtils.e(throwable);
}
);
addDisposable(disposable);

initClick();
}


private void initClick(){
mPageLoader.setOnPageChangeListener(
new PageLoader.OnPageChangeListener() {

@Override
public void onChapterChange(int pos) {
mCategoryAdapter.setChapter(pos);
}

@Override
public void requestChapters(List<TxtChapter> requestChapters) {
getMPresenter().loadChapter(mBookId, requestChapters);
mHandler.sendEmptyMessage(WHAT_CATEGORY);
//隐藏提示
mTvPageTip.setVisibility(GONE);
}

@Override
public void onCategoryFinish(List<TxtChapter> chapters) {
for (TxtChapter chapter : chapters) {
chapter.setTitle(StringUtils.convertCC(chapter.getTitle(), mPvPage.getContext()));
}
mCategoryAdapter.refreshItems(chapters);
}

@Override
public void onPageCountChange(int count) {
mSbChapterProgress.setMax(Math.max(0, count - 1));
mSbChapterProgress.setProgress(0);
// 如果处于错误状态,那么就冻结使用
if (mPageLoader.getPageStatus() == PageLoader.STATUS_LOADING
|| mPageLoader.getPageStatus() == PageLoader.STATUS_ERROR) {
mSbChapterProgress.setEnabled(false);
} else {
mSbChapterProgress.setEnabled(true);
}
}

@Override
public void onPageChange(int pos) {
mSbChapterProgress.post(
() -> mSbChapterProgress.setProgress(pos)
);
}
}
);

mSbChapterProgress.setOnSeekBarChangeListener(
new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mLlBottomMenu.getVisibility() == VISIBLE) {
//显示标题
mTvPageTip.setText((progress + 1) + "/" + (mSbChapterProgress.getMax() + 1));
mTvPageTip.setVisibility(VISIBLE);
}
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//进行切换
int pagePos = mSbChapterProgress.getProgress();
if (pagePos != mPageLoader.getPagePos()) {
mPageLoader.skipToPage(pagePos);
}
//隐藏提示
mTvPageTip.setVisibility(GONE);
}
}
);

mPvPage.setTouchListener(new PageView.TouchListener() {
@Override
public boolean onTouch() {
return !hideReadMenu();
}

@Override
public void center() {
toggleMenu(true);
}

@Override
public void prePage() {
}

@Override
public void nextPage() {
}

@Override
public void cancel() {
}
});
mLvCategory.setOnItemClickListener(
(parent, view, position, id) -> {
mDlSlide.closeDrawer(GravityCompat.START);
mPageLoader.skipToChapter(position);
}
);

mTvCategory.setOnClickListener(
(v) -> {
//移动到指定位置
if (mCategoryAdapter.getCount() > 0) {
mLvCategory.setSelection(mPageLoader.getChapterPos());
}
//切换菜单
toggleMenu(true);
//打开侧滑动栏
mDlSlide.openDrawer(GravityCompat.START);
}
);
mTvSetting.setOnClickListener(
(v) -> {
toggleMenu(false);
mSettingDialog.show();
}
);

mTvPreChapter.setOnClickListener(
(v) -> {
if (mPageLoader.skipPreChapter()) {
mCategoryAdapter.setChapter(mPageLoader.getChapterPos());
}
}
);

mTvNextChapter.setOnClickListener(
(v) -> {
if (mPageLoader.skipNextChapter()) {
mCategoryAdapter.setChapter(mPageLoader.getChapterPos());
}
}
);

mTvNightMode.setOnClickListener(
(v) -> {
if (isNightMode) {
isNightMode = false;
} else {
isNightMode = true;
}
mPageLoader.setNightMode(isNightMode);
toggleNightMode();
}
);

// mTvBrief.setOnClickListener(
// (v) -> BookDetailActivity.startActivity(this, mBookId)
// );

// mTvCommunity.setOnClickListener(
// (v) -> {
// Intent intent = new Intent(this, CommunityActivity.class);
// startActivity(intent);
// }
// );

mSettingDialog.setOnDismissListener(
dialog -> hideSystemBar()
);
}

@Nullable
@Override
public ReadPresenter initPresenter() {
return new ReadPresenter(this);
}

@Override
public void showCategory(List<BookChapterBean> bookChapterList) {

}

@Override
public void finishChapter() {

}

@Override
public void errorChapter() {

}

private void initTopMenu() {
// if (Build.VERSION.SDK_INT >= 19) {
// mAblTopMenu.setPadding(0, ScreenUtils.getStatusBarHeight(), 0, 0);
// }
}

private void initBottomMenu() {
//判断是否全屏
if (ReadSettingManager.getInstance().isFullScreen()) {
//还需要设置mBottomMenu的底部高度
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mLlBottomMenu.getLayoutParams();
params.bottomMargin = ScreenUtils.getNavigationBarHeight();
mLlBottomMenu.setLayoutParams(params);
} else {
//设置mBottomMenu的底部距离
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mLlBottomMenu.getLayoutParams();
params.bottomMargin = 0;
mLlBottomMenu.setLayoutParams(params);
}
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.d(TAG, "onWindowFocusChanged: " + mAblTopMenu.getMeasuredHeight());
}

private void toggleNightMode() {
if (isNightMode) {
mTvNightMode.setText("日间");
Drawable drawable = ContextCompat.getDrawable(this, R.mipmap.ic_read_menu_morning);
mTvNightMode.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
} else {
mTvNightMode.setText("夜间");
Drawable drawable = ContextCompat.getDrawable(this, R.mipmap.ic_read_menu_night);
mTvNightMode.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
}
}

private void setUpAdapter() {
mCategoryAdapter = new CategoryAdapter();
mLvCategory.setAdapter(mCategoryAdapter);
mLvCategory.setFastScrollEnabled(true);
}

// 注册亮度观察者
private void registerBrightObserver() {
try {
if (mBrightObserver != null) {
if (!isRegistered) {
final ContentResolver cr = getContentResolver();
cr.unregisterContentObserver(mBrightObserver);
cr.registerContentObserver(BRIGHTNESS_MODE_URI, false, mBrightObserver);
cr.registerContentObserver(BRIGHTNESS_URI, false, mBrightObserver);
cr.registerContentObserver(BRIGHTNESS_ADJ_URI, false, mBrightObserver);
isRegistered = true;
}
}
} catch (Throwable throwable) {
LogUtils.e(TAG, "register mBrightObserver error! " + throwable);
}
}

//解注册
private void unregisterBrightObserver() {
try {
if (mBrightObserver != null) {
if (isRegistered) {
getContentResolver().unregisterContentObserver(mBrightObserver);
isRegistered = false;
}
}
} catch (Throwable throwable) {
LogUtils.e(TAG, "unregister BrightnessObserver error! " + throwable);
}
}

/**
* 隐藏阅读界面的菜单显示
*
* @return 是否隐藏成功
*/
private boolean hideReadMenu() {
hideSystemBar();
if (mAblTopMenu.getVisibility() == VISIBLE) {
toggleMenu(true);
return true;
} else if (mSettingDialog.isShowing()) {
mSettingDialog.dismiss();
return true;
}
return false;
}

private void showSystemBar() {
//显示
// SystemBarUtils.showUnStableStatusBar(this);
// if (isFullScreen) {
// SystemBarUtils.showUnStableNavBar(this);
// }
}

private void hideSystemBar() {
//隐藏
// SystemBarUtils.hideStableStatusBar(this);
// if (isFullScreen) {
// SystemBarUtils.hideStableNavBar(this);
// }
}

/**
* 切换菜单栏的可视状态
* 默认是隐藏的
*/
private void toggleMenu(boolean hideStatusBar) {
initMenuAnim();

if (mAblTopMenu.getVisibility() == View.VISIBLE) {
//关闭
mAblTopMenu.startAnimation(mTopOutAnim);
mLlBottomMenu.startAnimation(mBottomOutAnim);
mAblTopMenu.setVisibility(GONE);
mLlBottomMenu.setVisibility(GONE);
mTvPageTip.setVisibility(GONE);

if (hideStatusBar) {
hideSystemBar();
}
} else {
mAblTopMenu.setVisibility(View.VISIBLE);
mLlBottomMenu.setVisibility(View.VISIBLE);
mAblTopMenu.startAnimation(mTopInAnim);
mLlBottomMenu.startAnimation(mBottomInAnim);

showSystemBar();
}
}

//初始化菜单动画
private void initMenuAnim() {
if (mTopInAnim != null) return;

mTopInAnim = AnimationUtils.loadAnimation(this, R.anim.slide_top_in);
mTopOutAnim = AnimationUtils.loadAnimation(this, R.anim.slide_top_out);
mBottomInAnim = AnimationUtils.loadAnimation(this, R.anim.slide_bottom_in);
mBottomOutAnim = AnimationUtils.loadAnimation(this, R.anim.slide_bottom_out);
//退出的速度要快
mTopOutAnim.setDuration(200);
mBottomOutAnim.setDuration(200);
}

@Override
public void onBackPressed() {
if (mAblTopMenu.getVisibility() == View.VISIBLE) {
// 非全屏下才收缩,全屏下直接退出
if (!ReadSettingManager.getInstance().isFullScreen()) {
toggleMenu(true);
return;
}
} else if (mSettingDialog.isShowing()) {
mSettingDialog.dismiss();
return;
} else if (mDlSlide.isDrawerOpen(GravityCompat.START)) {
mDlSlide.closeDrawer(GravityCompat.START);
return;
}

if (!mCollBook.isLocal() && !isCollected
&& !mCollBook.getBookChapters().isEmpty()) {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("加入书架")
.setMessage("喜欢本书就加入书架吧")
.setPositiveButton("确定", (dialog, which) -> {
//设置为已收藏
isCollected = true;
//设置阅读时间
mCollBook.setLastRead(StringUtils.
dateConvert(System.currentTimeMillis(), Constant.FORMAT_BOOK_DATE));

BookRepository.getInstance()
.saveCollBookWithAsync(mCollBook);

exit();
})
.setNegativeButton("取消", (dialog, which) -> {
exit();
}).create();
alertDialog.show();
} else {
exit();
}
}

// 退出
private void exit() {
// 返回给BookDetail。
// Intent result = new Intent();
// result.putExtra(BookDetailActivity.RESULT_IS_COLLECTED, isCollected);
// setResult(Activity.RESULT_OK, result);
// 退出
super.onBackPressed();
}

@Override
protected void onStart() {
super.onStart();
registerBrightObserver();
}

@Override
protected void onResume() {
super.onResume();
mWakeLock.acquire();
}

@Override
protected void onPause() {
super.onPause();
mWakeLock.release();
if (isCollected) {
mPageLoader.saveRecord();
}
}

@Override
protected void onStop() {
super.onStop();
unregisterBrightObserver();
}

@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);

mHandler.removeMessages(WHAT_CATEGORY);
mHandler.removeMessages(WHAT_CHAPTER);

mPageLoader.closeBook();
mPageLoader = null;
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean isVolumeTurnPage = ReadSettingManager
.getInstance().isVolumeTurnPage();
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
if (isVolumeTurnPage) {
return mPageLoader.skipToPrePage();
}
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
if (isVolumeTurnPage) {
return mPageLoader.skipToNextPage();
}
break;
}
return super.onKeyDown(keyCode, event);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// SystemBarUtils.hideStableStatusBar(this);
if (requestCode == REQUEST_MORE_SETTING) {
boolean fullScreen = ReadSettingManager.getInstance().isFullScreen();
if (isFullScreen != fullScreen) {
isFullScreen = fullScreen;
// 刷新BottomMenu
initBottomMenu();
}

// 设置显示状态
// if (isFullScreen) {
// SystemBarUtils.hideStableNavBar(this);
// } else {
// SystemBarUtils.showStableNavBar(this);
// }
}
}
}

+ 19
- 0
app/src/main/java/com/yzx/webebook/activity/WeexTestActivity.kt Parādīt failu

@@ -0,0 +1,19 @@
package com.yzx.webebook.activity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.yzx.webebook.activity.base.BaseWeexActivity

class WeexTestActivity : BaseWeexActivity() {


override fun initView() {

}

override fun initData() {
val url = intent.getStringExtra("url") ?: ""
val params = intent.getStringExtra("params") ?: ""
setUrlInfo(url, "RaderPage", params)
}
}

+ 12
- 2
app/src/main/java/com/yzx/webebook/activity/base/BaseActivity.kt Parādīt failu

@@ -5,6 +5,8 @@ import androidx.appcompat.app.AppCompatActivity
import com.gyf.immersionbar.ktx.immersionBar
import com.yzx.webebook.R
import com.yzx.webebook.presenter.base.BasePresenter
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable

/**
* 类名:BaseActivity
@@ -38,7 +40,7 @@ abstract class BaseActivity<out P : BasePresenter<*>> : AppCompatActivity() {

abstract fun initPresenter(): P?

protected var mDisposable: CompositeDisposable? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -59,7 +61,12 @@ abstract class BaseActivity<out P : BasePresenter<*>> : AppCompatActivity() {
}



protected open fun addDisposable(d: Disposable?) {
if (mDisposable == null) {
mDisposable = CompositeDisposable()
}
mDisposable!!.add(d!!)
}


override fun onBackPressed() {
@@ -68,6 +75,9 @@ abstract class BaseActivity<out P : BasePresenter<*>> : AppCompatActivity() {

override fun onDestroy() {
super.onDestroy()
if (mDisposable != null) {
mDisposable!!.dispose()
}
}

}

+ 201
- 0
app/src/main/java/com/yzx/webebook/activity/base/BaseWeexActivity.kt Parādīt failu

@@ -0,0 +1,201 @@
package com.yzx.webebook.activity.base

import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Message
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.gyf.immersionbar.ktx.immersionBar
import com.yzx.webebook.R
import com.yzx.webebook.modules.ActivityWXModule
import com.yzx.webebook.utils.StringUtils
import kotlinx.android.synthetic.main.activity_base_weex.*
import org.apache.weex.IWXRenderListener
import org.apache.weex.WXSDKEngine
import org.apache.weex.WXSDKInstance
import org.apache.weex.common.WXRenderStrategy
import org.apache.weex.utils.WXFileUtils

/**
* 类名:BaseActivity
* 作者:Yun.Lei
* 功能:
* 创建日期:2020年5月6日14:27:04
* 修改人:
* 修改时间:
* 修改备注:
*/
abstract class BaseWeexActivity : AppCompatActivity(), IWXRenderListener {


/**
* 初始化视图操作在这里执行,执行时机为onCreate之后
*/
abstract fun initView(): Unit

/**
* 数据初始化在这里执行,执行时机为initView之后
*/
abstract fun initData(): Unit

var mWXSDKInstance: WXSDKInstance? = null
var pageName: String = "WeexPage"
var bundleUrl: String = ""
var params: String = ""

private val handler: Handler = @SuppressLint("HandlerLeak")
object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
/**
* 轮询访问 WXSDKEngine 初始化状态 防止异步造成的初始化失败问题
*/
if (msg.what == 1) {
Log.i(
"welog",
"WXSDKEngine.isInitializedActivity: " + WXSDKEngine.isInitialized()
)
//&& !StringUtils.isEmpty(pageName) && !StringUtils.isEmpty( bundleUrl)
if (WXSDKEngine.isInitialized()) {
startRender()
} else {
sendEmptyMessageDelayed(1, 300)
}
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_base_weex)
initView()
initData()
setStatusBar()

mWXSDKInstance = WXSDKInstance(this)
mWXSDKInstance!!.registerRenderListener(this)

startRender()
if (WXSDKEngine.isInitialized()) {
} else {
handler.sendEmptyMessageDelayed(1, 300)
}
}

/**
* WXSDKEngine 初始化成功后 开始渲染
*/
private fun startRender() {

/**
* 防止空指针
*/
if (mWXSDKInstance == null) {
mWXSDKInstance = WXSDKInstance(this)
mWXSDKInstance!!.registerRenderListener(this)
}
/**
* 渲染远程js
*/
// bundleUrl = "http://dotwe.org/raw/dist/38e202c16bdfefbdb88a8754f975454c.bundle.wx";
try {
Log.d("welog", "开始加载")
bundleUrl = "https://oa.live.educlouddata.com/index.js";
mWXSDKInstance?.renderByUrl("https://oa.live.educlouddata.com", bundleUrl, null, null, WXRenderStrategy.APPEND_ASYNC)
Log.d("welog", "加载中")
}catch (e:Exception){
Log.d("welog", "加载失败,$e")
}

/**
* 渲染本地js
*/
// val options = mapOf("params" to this.params)
// Log.d("welog", "startRender: $options")
// mWXSDKInstance!!.render(
// pageName,
// WXFileUtils.loadAsset(bundleUrl, this),
// options,
// null,
// WXRenderStrategy.APPEND_ASYNC
// )
}

fun setUrlInfo(url: String, pageName: String, params: String = "") {
this.pageName = pageName
this.bundleUrl = url
this.params = params
}

open fun setStatusBar() {
immersionBar {
statusBarColor(R.color.white)
fitsSystemWindows(true)
statusBarDarkFont(true, 0.2f)
keyboardEnable(true)
init()
}

btnBack.setOnClickListener { onBackPressed() }
btnClose.setOnClickListener {
finish()
}
}


override fun onViewCreated(instance: WXSDKInstance?, view: View) {
Log.d("welog", "onViewCreated:")
if (view.parent == null) {
webLayout.addView(view);
}
webLayout.requestLayout();
}

override fun onRenderSuccess(instance: WXSDKInstance?, width: Int, height: Int) {
Log.d("welog", "onRenderSuccess:")
}

override fun onRefreshSuccess(instance: WXSDKInstance?, width: Int, height: Int) {
Log.d("welog", "onRefreshSuccess:")
}

override fun onException(instance: WXSDKInstance?, errCode: String?, msg: String?) {
Log.d("welog", "onException: $errCode//$msg")
handler.sendEmptyMessageDelayed(1, 300)
}

override fun onResume() {
super.onResume()
if (mWXSDKInstance != null) {
mWXSDKInstance!!.onActivityResume()
}
}

override fun onPause() {
super.onPause()
if (mWXSDKInstance != null) {
mWXSDKInstance!!.onActivityPause()
}
}

override fun onStop() {
super.onStop()
if (mWXSDKInstance != null) {
mWXSDKInstance!!.onActivityStop()
}
}

override fun onBackPressed() {
super.onBackPressed()
}

override fun onDestroy() {
super.onDestroy()
if (mWXSDKInstance != null) {
mWXSDKInstance!!.onActivityDestroy()
}
}

}

+ 38
- 0
app/src/main/java/com/yzx/webebook/adapter/CategoryAdapter.java Parādīt failu

@@ -0,0 +1,38 @@
package com.yzx.webebook.adapter;

import android.view.View;
import android.view.ViewGroup;

import com.yzx.webebook.adapter.base.EasyAdapter;
import com.yzx.webebook.adapter.base.IViewHolder;
import com.yzx.webebook.widget.page.TxtChapter;


/**
* Created by newbiechen on 17-6-5.
*/

public class CategoryAdapter extends EasyAdapter<TxtChapter> {
private int currentSelected = 0;
@Override
protected IViewHolder<TxtChapter> onCreateViewHolder(int viewType) {
return new CategoryHolder();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
CategoryHolder holder = (CategoryHolder) view.getTag();

if (position == currentSelected){
holder.setSelectedChapter();
}

return view;
}

public void setChapter(int pos){
currentSelected = pos;
notifyDataSetChanged();
}
}

+ 63
- 0
app/src/main/java/com/yzx/webebook/adapter/CategoryHolder.java Parādīt failu

@@ -0,0 +1,63 @@
package com.yzx.webebook.adapter;

import android.graphics.drawable.Drawable;
import android.widget.TextView;

import androidx.core.content.ContextCompat;

import com.yzx.webebook.R;
import com.yzx.webebook.adapter.base.ViewHolderImpl;
import com.yzx.webebook.utils.BookManager;
import com.yzx.webebook.widget.page.TxtChapter;


/**
* Created by newbiechen on 17-5-16.
*/

public class CategoryHolder extends ViewHolderImpl<TxtChapter> {

private TextView mTvChapter;

@Override
public void initView() {
mTvChapter = findById(R.id.category_tv_chapter);
}

@Override
public void onBind(TxtChapter value, int pos){
//首先判断是否该章已下载
Drawable drawable = null;

//TODO:目录显示设计的有点不好,需要靠成员变量是否为null来判断。
//如果没有链接地址表示是本地文件
if (value.getLink() == null){
drawable = ContextCompat.getDrawable(getContext(),R.drawable.selector_category_load);
}
else {
if (value.getBookId() != null
&& BookManager
.isChapterCached(value.getBookId(),value.getTitle())){
drawable = ContextCompat.getDrawable(getContext(),R.drawable.selector_category_load);
}
else {
drawable = ContextCompat.getDrawable(getContext(), R.drawable.selector_category_unload);
}
}

mTvChapter.setSelected(false);
mTvChapter.setTextColor(ContextCompat.getColor(getContext(),R.color.nb_text_default));
mTvChapter.setCompoundDrawablesWithIntrinsicBounds(drawable,null,null,null);
mTvChapter.setText(value.getTitle());
}

@Override
protected int getItemLayoutId() {
return R.layout.item_category;
}

public void setSelectedChapter(){
mTvChapter.setTextColor(ContextCompat.getColor(getContext(),R.color.light_red));
mTvChapter.setSelected(true);
}
}

+ 21
- 0
app/src/main/java/com/yzx/webebook/adapter/ImageAdapter.kt Parādīt failu

@@ -0,0 +1,21 @@
package com.yzx.webebook.adapter

import android.widget.ImageView
import com.bumptech.glide.Glide
import org.apache.weex.WXEnvironment
import org.apache.weex.adapter.IWXImgLoaderAdapter
import org.apache.weex.common.WXImageStrategy
import org.apache.weex.dom.WXImageQuality

class ImageAdapter:IWXImgLoaderAdapter {
override fun setImage(
url: String?,
view: ImageView?,
quality: WXImageQuality?,
strategy: WXImageStrategy?
) {
Glide.with(WXEnvironment.sApplication)
.load(url)
.into(view!!)
}
}

+ 46
- 0
app/src/main/java/com/yzx/webebook/adapter/PageStyleAdapter.java Parādīt failu

@@ -0,0 +1,46 @@
package com.yzx.webebook.adapter;

import android.graphics.drawable.Drawable;
import android.view.View;

import androidx.recyclerview.widget.RecyclerView;

import com.yzx.webebook.adapter.base.BaseListAdapter;
import com.yzx.webebook.adapter.base.BaseViewHolder;
import com.yzx.webebook.adapter.base.IViewHolder;
import com.yzx.webebook.widget.page.PageStyle;


/**
* Created by newbiechen on 17-5-19.
*/

public class PageStyleAdapter extends BaseListAdapter<Drawable> {
private int currentChecked;

@Override
protected IViewHolder<Drawable> createViewHolder(int viewType) {
return new PageStyleHolder();
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
IViewHolder iHolder = ((BaseViewHolder) holder).holder;
PageStyleHolder pageStyleHolder = (PageStyleHolder) iHolder;
if (currentChecked == position){
pageStyleHolder.setChecked();
}
}

public void setPageStyleChecked(PageStyle pageStyle){
currentChecked = pageStyle.ordinal();
}

@Override
protected void onItemClick(View v, int pos) {
super.onItemClick(v, pos);
currentChecked = pos;
notifyDataSetChanged();
}
}

+ 40
- 0
app/src/main/java/com/yzx/webebook/adapter/PageStyleHolder.java Parādīt failu

@@ -0,0 +1,40 @@
package com.yzx.webebook.adapter;

import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;

import com.yzx.webebook.R;
import com.yzx.webebook.adapter.base.ViewHolderImpl;


/**
* Created by newbiechen on 17-5-19.
*/

public class PageStyleHolder extends ViewHolderImpl<Drawable> {

private View mReadBg;
private ImageView mIvChecked;

@Override
public void initView() {
mReadBg = findById(R.id.read_bg_view);
mIvChecked = findById(R.id.read_bg_iv_checked);
}

@Override
public void onBind(Drawable data, int pos) {
mReadBg.setBackground(data);
mIvChecked.setVisibility(View.GONE);
}

@Override
protected int getItemLayoutId() {
return R.layout.item_read_bg;
}

public void setChecked(){
mIvChecked.setVisibility(View.VISIBLE);
}
}

+ 154
- 0
app/src/main/java/com/yzx/webebook/adapter/base/BaseListAdapter.java Parādīt failu

@@ -0,0 +1,154 @@
package com.yzx.webebook.adapter.base;

import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;

import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Created by newbiechen on 17-3-21.
*/

public abstract class BaseListAdapter <T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

private static final String TAG = "BaseListAdapter";
/*common statement*/
protected final List<T> mList = new ArrayList<>();
protected OnItemClickListener mClickListener;
protected OnItemLongClickListener mLongClickListener;

/************************abstract area************************/
protected abstract IViewHolder<T> createViewHolder(int viewType);

/*************************rewrite logic area***************************************/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
IViewHolder<T> viewHolder = createViewHolder(viewType);

View view = viewHolder.createItemView(parent);
//初始化
RecyclerView.ViewHolder holder = new BaseViewHolder(view, viewHolder);
return holder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//防止别人直接使用RecyclerView.ViewHolder调用该方法
if (!(holder instanceof BaseViewHolder))
throw new IllegalArgumentException("The ViewHolder item must extend BaseViewHolder");

IViewHolder<T> iHolder = ((BaseViewHolder) holder).holder;
iHolder.onBind(getItem(position),position);

//设置点击事件
holder.itemView.setOnClickListener((v)->{
if (mClickListener != null){
mClickListener.onItemClick(v,position);
}
//adapter监听点击事件
iHolder.onClick();
onItemClick(v,position);
});
//设置长点击事件
holder.itemView.setOnLongClickListener(
(v) -> {
boolean isClicked = false;
if (mLongClickListener != null){
isClicked = mLongClickListener.onItemLongClick(v,position);
}
//Adapter监听长点击事件
onItemLongClick(v,position);
return isClicked;
}
);
}

@Override
public int getItemCount() {
return mList.size();
}

protected void onItemClick(View v,int pos){
}

protected void onItemLongClick(View v,int pos){
}

/******************************public area***********************************/

public void setOnItemClickListener(OnItemClickListener mListener) {
this.mClickListener = mListener;
}

public void setOnItemLongClickListener(OnItemLongClickListener mListener){
this.mLongClickListener = mListener;
}

public void addItem(T value){
mList.add(value);
notifyDataSetChanged();
}

public void addItem(int index,T value){
mList.add(index, value);
notifyDataSetChanged();
}

public void addItems(List<T> values){
mList.addAll(values);

Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
}

public void removeItem(T value){
mList.remove(value);
notifyDataSetChanged();
}

public void removeItems(List<T> value){
mList.removeAll(value);
notifyDataSetChanged();
}

public T getItem(int position){
return mList.get(position);
}

public List<T> getItems(){
return Collections.unmodifiableList(mList);
}

public int getItemSize(){
return mList.size();
}

public void refreshItems(List<T> list){
mList.clear();
mList.addAll(list);
notifyDataSetChanged();
}

public void clear(){
mList.clear();
}

/***************************inner class area***********************************/
public interface OnItemClickListener{
void onItemClick(View view, int pos);
}

public interface OnItemLongClickListener{
boolean onItemLongClick(View view, int pos);
}
}

+ 19
- 0
app/src/main/java/com/yzx/webebook/adapter/base/BaseViewHolder.java Parādīt failu

@@ -0,0 +1,19 @@
package com.yzx.webebook.adapter.base;

import android.view.View;

import androidx.recyclerview.widget.RecyclerView;

/**
* Created by newbiechen on 17-5-17.
*/

public class BaseViewHolder<T> extends RecyclerView.ViewHolder{
public IViewHolder<T> holder;

public BaseViewHolder(View itemView, IViewHolder<T> holder) {
super(itemView);
this.holder = holder;
holder.initView();
}
}

+ 92
- 0
app/src/main/java/com/yzx/webebook/adapter/base/EasyAdapter.java Parādīt failu

@@ -0,0 +1,92 @@
package com.yzx.webebook.adapter.base;

import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Created by newbiechen on 17-6-5.
* ListView 使用的Adapter
*/

public abstract class EasyAdapter<T> extends BaseAdapter {

private List<T> mList = new ArrayList<T>();

@Override
public int getCount() {
return mList.size();
}

@Override
public T getItem(int position) {
return mList.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

public void addItem(T value){
mList.add(value);
notifyDataSetChanged();
}

public void addItem(int index,T value){
mList.add(index, value);
notifyDataSetChanged();
}

public void addItems(List<T> values){
mList.addAll(values);
notifyDataSetChanged();
}

public void removeItem(T value){
mList.remove(value);
notifyDataSetChanged();
}

public List<T> getItems(){
return Collections.unmodifiableList(mList);
}

public int getItemSize(){
return mList.size();
}

public void refreshItems(List<T> list){
mList.clear();
mList.addAll(list);
notifyDataSetChanged();
}

public void clear(){
mList.clear();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
IViewHolder holder = null;
if (convertView == null){
holder = onCreateViewHolder(getItemViewType(position));
convertView = holder.createItemView(parent);
convertView.setTag(holder);
//初始化
holder.initView();
}
else {
holder = (IViewHolder)convertView.getTag();
}
//执行绑定
holder.onBind(getItem(position),position);
return convertView;
}

protected abstract IViewHolder<T> onCreateViewHolder(int viewType);
}

+ 204
- 0
app/src/main/java/com/yzx/webebook/adapter/base/GroupAdapter.java Parādīt failu

@@ -0,0 +1,204 @@
package com.yzx.webebook.adapter.base;

import android.view.View;
import android.view.ViewGroup;

import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

/**
* Created by newbiechen on 17-5-5.
* 用于头标签 + 表格的布局View
*/

public abstract class GroupAdapter<T,R> extends RecyclerView.Adapter{
private static final String TAG = "GroupAdapter";

private static final int TYPE_GROUP = 1;
private static final int TYPE_CHILD = 2;

private OnGroupClickListener mGroupListener;
private OnChildClickListener mChildClickListener;

public abstract int getGroupCount();
public abstract int getChildCount(int groupPos);

public abstract T getGroupItem(int groupPos);
public abstract R getChildItem(int groupPos,int childPos);

protected abstract IViewHolder<T> createGroupViewHolder();
protected abstract IViewHolder<R> createChildViewHolder();

public GroupAdapter(RecyclerView recyclerView,int spanSize){
GridLayoutManager manager = new GridLayoutManager(recyclerView.getContext(),spanSize);
manager.setSpanSizeLookup(new GroupSpanSizeLookup(spanSize));
recyclerView.setLayoutManager(manager);
}

public void setOnGroupItemListener(OnGroupClickListener listener){
mGroupListener = listener;
}

public void setOnChildItemListener(OnChildClickListener listener) {
mChildClickListener = listener;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
IViewHolder iViewHolder = null;
View view = null;
if (viewType == TYPE_GROUP){
iViewHolder = createGroupViewHolder();
view = iViewHolder.createItemView(parent);
}
else if (viewType == TYPE_CHILD){
iViewHolder = createChildViewHolder();
view = iViewHolder.createItemView(parent);
}
RecyclerView.ViewHolder viewHolder = new BaseViewHolder(view, iViewHolder);
return viewHolder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (! (holder instanceof BaseViewHolder))
throw new IllegalArgumentException("The ViewHolder item must extend BaseViewHolder");

IViewHolder iHolder = ((BaseViewHolder) holder).holder;
int type = getItemViewType(position);
if (type == TYPE_GROUP){
//计算当前的group
int groupPos = calculateGroup(position);
holder.itemView.setOnClickListener(
(v) -> {
iHolder.onClick();
if (mGroupListener != null){
mGroupListener.onGroupClick(v,groupPos);
}
}
);
iHolder.onBind(getGroupItem(groupPos),groupPos);
}
else if (type == TYPE_CHILD){
int groupPos = calculateGroup(position);
int childPos = calculateChild(position);

holder.itemView.setOnClickListener(
v -> {
iHolder.onClick();
if (mChildClickListener != null) {
mChildClickListener.onChildClick(v,groupPos,childPos);
}
}
);
//这里有点小问题,返回的是childPos
iHolder.onBind(getChildItem(groupPos,childPos),childPos);
}
}

//计算position是哪个group中的头
private int calculateGroup(int position){
int total = 0;
for (int i=0; i<getGroupCount(); ++i){
total += getChildCount(i)+1; //当前group的大小范围
if (total > position){ //判断是否pos在total内
return i;
}
}
return -1;
}

//计算position是那个group中的child
protected int calculateChild(int position){
for (int i=0; i<getGroupCount(); ++i){
int total = getChildCount(i)+1; //每个队列的总和
int loc = position - total; //Loc表示在第二队列的位置
if (loc < 0){ //如果 < 0 表示在上一队列中,则返回
return position-1;
}
else { //否则设置当前队列为pos
position = loc;
}
}
//返回child在指定group的位置
return -1;
}

@Override
public int getItemCount() {
int groupCount = getGroupCount();
//因为Group需要有头部
int totalCount = groupCount;
for (int i=0; i<groupCount; ++i){
totalCount += getChildCount(i);
}
return totalCount;
}

//判断获取的item是group还是child
@Override
public int getItemViewType(int position) {
if (position == 0){
return TYPE_GROUP;
}

for (int i=0; i<getGroupCount(); ++i){
int total = getChildCount(i)+1; //每个队列的总和
if (position == 0){
return TYPE_GROUP;
}
else if (position < 0){
return TYPE_CHILD;
}
position -= total;
}
//剩下的肯定是最后一行
return TYPE_CHILD;
}

/**
* 设置Group与child在GridLayoutManager情况下占用的格子
*/
class GroupSpanSizeLookup extends GridLayoutManager.SpanSizeLookup{
private int maxSize;
public GroupSpanSizeLookup(int maxSize) {
this.maxSize = maxSize;
}

@Override
public int getSpanSize(int position) {
if (getItemViewType(position) == TYPE_GROUP){
return maxSize;
}
else {
return 1;
}
}
}

public int getGroupToPosition(int groupPos){
int position = 0;
for (int i=0; i<groupPos; ++i){
position += getChildCount(groupPos)+1;
}
return position;
}

//child转换成position
public int getChildToPosition(int groupPos, int childPos){
int position = 0;
for (int i=0; i<groupPos; ++i){
position += getChildCount(i)+1;
}
position += childPos + 1;
return position;
}

public interface OnGroupClickListener {
void onGroupClick(View view,int pos);
}

public interface OnChildClickListener {
void onChildClick(View view,int groupPos,int childPos);
}
}

+ 15
- 0
app/src/main/java/com/yzx/webebook/adapter/base/IViewHolder.java Parādīt failu

@@ -0,0 +1,15 @@
package com.yzx.webebook.adapter.base;

import android.view.View;
import android.view.ViewGroup;

/**
* Created by newbiechen on 17-5-17.
*/

public interface IViewHolder<T> {
View createItemView(ViewGroup parent);
void initView();
void onBind(T data,int pos);
void onClick();
}

+ 42
- 0
app/src/main/java/com/yzx/webebook/adapter/base/ViewHolderImpl.java Parādīt failu

@@ -0,0 +1,42 @@
package com.yzx.webebook.adapter.base;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
* Created by newbiechen on 17-5-17.
*/

public abstract class ViewHolderImpl<T> implements IViewHolder<T> {
private View view;
private Context context;
/****************************************************/
protected abstract int getItemLayoutId();


@Override
public View createItemView(ViewGroup parent) {
view = LayoutInflater.from(parent.getContext())
.inflate(getItemLayoutId(), parent, false);
context = parent.getContext();
return view;
}

protected <V extends View> V findById(int id){
return (V) view.findViewById(id);
}

protected Context getContext(){
return context;
}

protected View getItemView(){
return view;
}

@Override
public void onClick() {
}
}

+ 116
- 0
app/src/main/java/com/yzx/webebook/model/bean/AuthorBean.java Parādīt failu

@@ -0,0 +1,116 @@
package com.yzx.webebook.model.bean;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;

/**
* Created by newbiechen on 17-4-20.
* 作者
*/
@Entity
public class AuthorBean {
/**
* _id : 553136ba70feaa764a096f6f
* avatar : /avatar/26/eb/26ebf8ede76d7f52cd377960bd66383b
* nickname : 九歌
* activityAvatar :
* type : normal
* lv : 8
* gender : female
*/
@Id
private String _id;

private String avatar;
private String nickname;
private String activityAvatar;
private String type;
private int lv;
private String gender;

@Generated(hash = 1152582024)
public AuthorBean(String _id, String avatar, String nickname,
String activityAvatar, String type, int lv, String gender) {
this._id = _id;
this.avatar = avatar;
this.nickname = nickname;
this.activityAvatar = activityAvatar;
this.type = type;
this.lv = lv;
this.gender = gender;
}

@Generated(hash = 1694633584)
public AuthorBean() {
}

public String get_id() {
return _id;
}

public void set_id(String _id) {
this._id = _id;
}

public String getAvatar() {
return avatar;
}

public void setAvatar(String avatar) {
this.avatar = avatar;
}

public String getNickname() {
return nickname;
}

public void setNickname(String nickname) {
this.nickname = nickname;
}

public String getActivityAvatar() {
return activityAvatar;
}

public void setActivityAvatar(String activityAvatar) {
this.activityAvatar = activityAvatar;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public int getLv() {
return lv;
}

public void setLv(int lv) {
this.lv = lv;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

@Override
public String toString() {
return "AuthorBean{" +
"_id='" + _id + '\'' +
", avatar='" + avatar + '\'' +
", nickname='" + nickname + '\'' +
", activityAvatar='" + activityAvatar + '\'' +
", type='" + type + '\'' +
", lv=" + lv +
", gender='" + gender + '\'' +
'}';
}
}

+ 146
- 0
app/src/main/java/com/yzx/webebook/model/bean/BookChapterBean.java Parādīt failu

@@ -0,0 +1,146 @@
package com.yzx.webebook.model.bean;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index;

import java.io.Serializable;

/**
* Created by newbiechen on 17-5-10.
* 书的章节链接(作为下载的进度数据)
* 同时作为网络章节和本地章节 (没有找到更好分离两者的办法)
*/
@Entity
public class BookChapterBean implements Serializable{
private static final long serialVersionUID = 56423411313L;
/**
* title : 第一章 他叫白小纯
* link : http://read.qidian.com/chapter/rJgN8tJ_cVdRGoWu-UQg7Q2/6jr-buLIUJSaGfXRMrUjdw2
* unreadble : false
*/
@Id
private String id;

private String link;

private String title;

//所属的下载任务
private String taskName;

private boolean unreadble;

//所属的书籍
@Index
private String bookId;

//本地书籍参数


//在书籍文件中的起始位置
private long start;

//在书籍文件中的终止位置
private long end;

@Generated(hash = 1508543635)
public BookChapterBean(String id, String link, String title, String taskName,
boolean unreadble, String bookId, long start, long end) {
this.id = id;
this.link = link;
this.title = title;
this.taskName = taskName;
this.unreadble = unreadble;
this.bookId = bookId;
this.start = start;
this.end = end;
}

@Generated(hash = 853839616)
public BookChapterBean() {
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getLink() {
return link;
}

public void setLink(String link) {
this.link = link;
}

public boolean isUnreadble() {
return unreadble;
}

public void setUnreadble(boolean unreadble) {
this.unreadble = unreadble;
}

public String getTaskName() {
return taskName;
}

public void setTaskName(String taskName) {
this.taskName = taskName;
}

public boolean getUnreadble() {
return this.unreadble;
}

public String getBookId() {
return bookId;
}

public void setBookId(String bookId) {
this.bookId = bookId;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public long getStart() {
return start;
}

public void setStart(long start) {
this.start = start;
}

public long getEnd() {
return end;
}

public void setEnd(long end) {
this.end = end;
}

@Override
public String toString() {
return "BookChapterBean{" +
"id='" + id + '\'' +
", link='" + link + '\'' +
", title='" + title + '\'' +
", taskName='" + taskName + '\'' +
", unreadble=" + unreadble +
", bookId='" + bookId + '\'' +
", start=" + start +
", end=" + end +
'}';
}
}

+ 54
- 0
app/src/main/java/com/yzx/webebook/model/bean/BookRecordBean.java Parādīt failu

@@ -0,0 +1,54 @@
package com.yzx.webebook.model.bean;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;

/**
* Created by newbiechen on 17-5-20.
*/
@Entity
public class BookRecordBean {
//所属的书的id
@Id
private String bookId;
//阅读到了第几章
private int chapter;
//当前的页码
private int pagePos;

@Generated(hash = 340380968)
public BookRecordBean(String bookId, int chapter, int pagePos) {
this.bookId = bookId;
this.chapter = chapter;
this.pagePos = pagePos;
}

@Generated(hash = 398068002)
public BookRecordBean() {
}

public String getBookId() {
return bookId;
}

public void setBookId(String bookId) {
this.bookId = bookId;
}

public int getChapter() {
return chapter;
}

public void setChapter(int chapter) {
this.chapter = chapter;
}

public int getPagePos() {
return pagePos;
}

public void setPagePos(int pagePos) {
this.pagePos = pagePos;
}
}

+ 89
- 0
app/src/main/java/com/yzx/webebook/model/bean/ChapterInfoBean.java Parādīt failu

@@ -0,0 +1,89 @@
package com.yzx.webebook.model.bean;

/**
* Created by newbiechen on 17-5-10.
*/

public class ChapterInfoBean {
/**
* title : 第一章 他叫白小纯
* body : 帽儿山,位于东林山脉中,山下有一个村子,民风淳朴,以耕田为生,与世隔绝。
清晨,村庄的大门前,整个村子里的乡亲,正为一个十五六岁少年送别,这少年瘦弱,但却白白净净,看起来很是乖巧,衣着尽管是寻常的青衫,可却洗的泛白,穿在这少年的身上,与他目中的纯净搭配在一起,透出一股子灵动。
他叫白小纯。
“父老乡亲们,我要去修仙了,可我舍不得你们啊。”少年满脸不舍,原本就乖巧的样子,此刻看起来更为纯朴。
四周的乡亲,面面相觑,顿时摆出难舍之色。
“小纯,你爹娘走的早,你是个……好孩子!!难道你不想长生了么,成为仙人就可以长生,能活的很久很久,走吧,雏鹰长大,总有飞出去的那一天。”人群内走出一个头发花白的老者,说道好孩子三个字时,他顿了一下。
“在外面遇到任何事情,都要坚持下去,走出村子,就不要回来,因为你的路在前方!”老人神色慈祥,拍了拍少年的肩膀。
“长生……”白小纯身体一震,目中慢慢坚定起来,在老者以及四周乡亲鼓励的目光下,他重重的点了点头,深深的看了一眼四周的乡亲,转身迈着大步,渐渐走出了村子。
眼看少年的身影远去,村中的众人,一个个都激动起来,目中的难舍刹那就被喜悦代替,那之前满脸慈祥的老者,此刻也在颤抖,眼中流下泪水。
“苍天有眼,这白鼠狼,他终于……终于走了,是谁告诉他在附近看到仙人的,你为村子立下了大功!”
“这白鼠狼终于肯离开了,可怜我家的几只鸡,就因为这白鼠狼怕鸡打鸣,不知用了什么方法,唆使一群孩子吃鸡肉,把全村的鸡都给吃的干干净净……”
“今天过年了!”欢呼之声,立刻在这不大的村子里,沸腾而起,甚至有人拿出了锣鼓,高兴的敲打起来。
村子外,白小纯还没等走远,他就听到了身后村子内,传出了敲锣打鼓的声音,还夹着欢呼。
白小纯脚步一顿,神色有些古怪,干咳一声,伴随着耳边传来的锣鼓,白小纯顺着山路,走上了帽儿山。
这帽儿山虽不高,却灌木杂多,虽是清晨,可看起来也是黑压压一片,很是安静。
“听二狗说,他前几天在这里被一头野猪追赶时,看到天上有仙人飞过……”白小纯走在山路上,心脏怦怦跳动时,忽然一旁的灌林中传来阵阵哗哗声,似野猪一样,这声音来的突然,让本就紧张的白小纯,顿时背后发凉。
“谁,谁在那里!”白小纯右手快速从行囊中拿出四把斧头,六把柴刀,还觉得不放心,又从怀里取出了一小根黑色的香,死死的抓住。
“别出来,千万别出来,我有斧头,有柴刀,手里的香还可以召唤天雷,能引仙人降临,你敢出来,就劈死你!”白小纯哆嗦的大喊,连滚带爬的夹着那些武器,赶紧顺着山路跑去,沿途叮当乱响,斧头柴刀掉了一地。
或许是真的被他给吓住了,很快的哗哗声就消失,没有什么野兽跑出来,白小纯面色苍白,擦了擦冷汗,有心放弃继续上山,可一想到手中这根香是他爹娘去世前留给他的,据说是祖上曾偶然的救下一个落魄的仙人,那仙人离去时留下这根香作为报答,曾言会收下白家血脉一人为弟子,只要点燃,仙人就会到来。
可至今为止,这根香他点过十多次,始终不见仙人到来,让白小纯开始怀疑仙人是不是真的会来,这一次之所以下定决心,一方面是香所剩不多,另一方面是他听村子里人说,头几天在这看到有仙人从天上飞过。
所以他这才到来,想着距离仙人近一些,或许仙人就察觉到了也说不定。
踌躇一番,白小纯咬牙继续,好在此山不高,不久他气喘吁吁的到了山顶,站在那里,他遥望山下的村庄,神色颇为感慨,又低头看着手中的只有指甲盖大小的黑香,此香似乎被燃烧了好多次,所剩不多。
“三年了,爹娘保佑我,这次一定要成功!”白小纯深吸口气,小心的将香点燃,立刻四周狂风顿起,天空更是眨眼间乌云密布,一道道闪电划过,还有震耳欲聋的雷鸣在白小纯耳边直接炸开。
声音之大,气势之强,让白小纯身体哆嗦,有种随时会被雷劈死的感觉,下意识的就想要吐口唾沫将那根香灭掉,但却挣扎忍住。
“三年了,我点这根香点了十二次,这是第十三次,这次一定要忍住,小纯不怕,应该不会被劈死……”白小纯想起了这三年的经历,不算这次,点了十二次,每次都是这样的雷鸣闪电,仙人也没有到来,吓的本就怕死的他每次都吐口唾沫将其熄灭,说来也怪,这根香看似不凡,可实际上一样是浇水就灭。
在白小纯这里心惊肉跳,艰难的于那雷声中等待时,距离这里不远处的天空上,有一道长虹正急速的呼啸而来。
长虹内是一个中年男子,这男子衣着华丽,仙风道骨,可偏偏风尘仆仆,甚至仔细去看,可以看到他神色内深深的疲惫。
“我倒要看看,到底是个什么样的人,竟然点根香点了三年!”
一想到自己这三年的经历,中年男子就气恼,三年前他察觉有人点燃自己还是凝气时送出的香药,想起了当年在凡俗中的一段人情。
这才飞出寻来,原本按照他的打算,很快就会回来,可没成想,刚寻着香气过去,还没等多远,那气息就瞬间消失,断了联系。若是一次也就罢了,这三年,气息出现了十多次。
使得他这里,多次在寻找时中断,就这样来来回回,折腾了三年……
此刻他遥遥的看到了帽儿山,看到了山顶上白小纯,气不打一处来,一瞬飞出,直接就站在了山顶,大手一挥,那根所剩不多的香,直接熄灭。
雷声刹那消失,白小纯愣了一下,抬头一看,看到了自己的身边多了一个中年男子。
“仙人?”白小纯小心翼翼的开口,有些拿不准,背后偷偷捡起一把斧头。
“本座李青候,你是白家后人?”中年修士目光如电,无视白小纯身后的斧子,打量了白小纯一番,觉得眼前此子眉清目秀,依稀与当年的故人相似,资质也不错,心底的恼意,也不由缓了一些。
“晚辈正是白家后人,白小纯。”白小纯眨了眨眼,小声说道,虽然心中有些畏惧,但还是挺了挺腰板。
“我问你,点一根香,为什么点了三年!”中年修士淡淡开口,问出了他这三年里,最想要知道的问题。
白小纯听到这个问题,脑筋飞速转动,然后脸上摆出惆怅,遥望山下的村庄。
“晚辈是一个重情重义的人,舍不得那些乡亲们,每一次我点燃香,他们也都不舍得我离去,如今山下的他们,还在因为我的离去而悲伤呢。”
中年修士一愣,这个缘由,是他之前没想到的,目中的恼色又少了一些,单单从话语上看,此子的本性还是不错的。
可当他的目光落在山下的村子时,他的神识随之扫过,听到了村子里的敲锣打鼓以及那一句句欢呼白鼠狼离去的话语,面色立刻难看起来,有些头疼,看着眼前这个外表乖巧纯朴,人畜无害的白小纯,已心底明朗对方实际上一肚子坏水。
“说实话!”中年修士一瞪眼,声音如同雷声一样,白小纯吓得一个哆嗦。
“这不怨我啊,你那什么破香啊,每次点燃都会打雷,好几次都差点劈死我,我躲过了十三次,已经很不容易了。”白小纯可怜兮兮的说道。
中年修士看着白小纯,半晌无语。
“既然你这么害怕,为什么还要强行去点香十多次?”中年修士缓缓开口。
“我怕死啊,修仙不是能长生么,我想长生啊。”白小纯委屈的说道。
中年修士再次无语,不过觉得此子总算执念可嘉,扔到门派里磨炼一番,或可在性子上改变一二。
于是略一思索,大袖一甩卷着白小纯化作一道长虹,直奔天边而去。
“跟我走吧。”
“去哪?这也太高了吧……”白小纯看到自己在天上飞,下面是万丈深渊,立刻脸色苍白,斧头一扔,死死的抱住仙人的大腿。
中年修士看了眼自己的腿,无奈开口。
“灵溪宗。”
兄弟姐妹们,阔别2个月,你们想不想我啊,我非常想你们!
这本书,我做了详细的大纲,每次回顾大纲里的情节,都很兴奋,有种燃烧的感觉,我非常满意,明天,正式更新,依旧是中午一章,晚上一章!
很兴奋,我们已沉寂了数月,如今归来,要……再战起点!
新书期,兄弟姐妹,别忘了收藏与推荐啊,收藏与推荐至关重要!
求收藏!!求推荐!!
让众人知晓,我们……归来了!
我们的目标,依旧是……点击榜,推荐榜,第一!
*/

private String title;
private String body;

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getBody() {
return body;
}

public void setBody(String body) {
this.body = body;
}
}

+ 372
- 0
app/src/main/java/com/yzx/webebook/model/bean/CollBookBean.java Parādīt failu

@@ -0,0 +1,372 @@
package com.yzx.webebook.model.bean;

import android.os.Parcel;
import android.os.Parcelable;

import com.yzx.webebook.App;
import com.yzx.webebook.utils.StringUtils;

import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.ToMany;

import java.util.List;
import com.yzx.webebook.model.gen.DaoSession;
import com.yzx.webebook.model.gen.BookChapterBeanDao;
import com.yzx.webebook.model.gen.CollBookBeanDao;

/**
* Created by newbiechen on 17-5-8.
* 收藏的书籍
*/
@Entity
public class CollBookBean implements Parcelable{

public static final int STATUS_UNCACHE = 0; //未缓存
public static final int STATUS_CACHING = 1; //正在缓存
public static final int STATUS_CACHED = 2; //已经缓存
/**
* _id : 53663ae356bdc93e49004474
* title : 逍遥派
* author : 白马出淤泥
* shortIntro : 金庸武侠中有不少的神秘高手,书中或提起名字,或不曾提起,总之他们要么留下了绝世秘笈,要么就名震武林。 独孤九剑的创始者,独孤求败,他真的只创出九剑吗? 残本葵花...
* cover : /cover/149273897447137
* hasCp : true
* latelyFollower : 60213
* retentionRatio : 22.87
* updated : 2017-05-07T18:24:34.720Z
*
* chaptersCount : 1660
* lastChapter : 第1659章 朱长老
*/
@Id
private String _id; // 本地书籍中,path 的 md5 值作为本地书籍的 id
private String title;
private String author;
private String shortIntro;
private String cover; // 在本地书籍中,该字段作为本地文件的路径
private boolean hasCp;
private int latelyFollower;
private double retentionRatio;
//最新更新日期
private String updated;
//最新阅读日期
private String lastRead;
private int chaptersCount;
private String lastChapter;
//是否更新或未阅读
private boolean isUpdate = true;
//是否是本地文件
private boolean isLocal = false;

@ToMany(referencedJoinProperty = "bookId")
private List<BookChapterBean> bookChapterList;
/** Used to resolve relations */
@Generated(hash = 2040040024)
private transient DaoSession daoSession;
/** Used for active entity operations. */
@Generated(hash = 1552163441)
private transient CollBookBeanDao myDao;

@Generated(hash = 757968961)
public CollBookBean(String _id, String title, String author, String shortIntro, String cover,
boolean hasCp, int latelyFollower, double retentionRatio, String updated, String lastRead,
int chaptersCount, String lastChapter, boolean isUpdate, boolean isLocal) {
this._id = _id;
this.title = title;
this.author = author;
this.shortIntro = shortIntro;
this.cover = cover;
this.hasCp = hasCp;
this.latelyFollower = latelyFollower;
this.retentionRatio = retentionRatio;
this.updated = updated;
this.lastRead = lastRead;
this.chaptersCount = chaptersCount;
this.lastChapter = lastChapter;
this.isUpdate = isUpdate;
this.isLocal = isLocal;
}

public CollBookBean() {
}

public String get_id() {
return _id;
}

public void set_id(String _id) {
this._id = _id;
}

public String getTitle() {
return StringUtils.convertCC(title, App.Companion.getContext());
}

public void setTitle(String title) {
this.title = title;
}

public String getAuthor() {
return StringUtils.convertCC(author, App.Companion.getContext());
}

public void setAuthor(String author) {
this.author = author;
}

public String getShortIntro() {
return StringUtils.convertCC(shortIntro, App.Companion.getContext());
}

public void setShortIntro(String shortIntro) {
this.shortIntro = shortIntro;
}

public String getCover() {
return StringUtils.convertCC(cover, App.Companion.getContext());
}

public void setCover(String cover) {
this.cover = cover;
}

public boolean isHasCp() {
return hasCp;
}

public void setHasCp(boolean hasCp) {
this.hasCp = hasCp;
}

public int getLatelyFollower() {
return latelyFollower;
}

public void setLatelyFollower(int latelyFollower) {
this.latelyFollower = latelyFollower;
}

public double getRetentionRatio() {
return retentionRatio;
}

public void setRetentionRatio(double retentionRatio) {
this.retentionRatio = retentionRatio;
}

public String getUpdated() {
return StringUtils.convertCC(updated, App.Companion.getContext());
}

public void setUpdated(String updated) {
this.updated = updated;
}

public int getChaptersCount() {
return chaptersCount;
}

public void setChaptersCount(int chaptersCount) {
this.chaptersCount = chaptersCount;
}

public String getLastChapter() {
return StringUtils.convertCC(lastChapter, App.Companion.getContext());
}

public void setLastChapter(String lastChapter) {
this.lastChapter = lastChapter;
}

public boolean isUpdate() {
return isUpdate;
}

public void setUpdate(boolean update) {
isUpdate = update;
}

public boolean getHasCp() {
return this.hasCp;
}

public boolean getIsUpdate() {
return this.isUpdate;
}

public void setIsUpdate(boolean isUpdate) {
this.isUpdate = isUpdate;
}

public boolean isLocal() {
return isLocal;
}

public void setLocal(boolean local) {
isLocal = local;
}

public String getLastRead() {
return StringUtils.convertCC(lastRead, App.Companion.getContext());
}

public void setLastRead(String lastRead) {
this.lastRead = lastRead;
}

public void setBookChapters(List<BookChapterBean> beans){
bookChapterList = beans;
for (BookChapterBean bean : bookChapterList){
bean.setBookId(get_id());
}
}

public List<BookChapterBean> getBookChapters(){
if (daoSession == null){
return bookChapterList;
}
else {
return getBookChapterList();
}
}

/**
* To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity.
*/
@Generated(hash = 711740787)
public List<BookChapterBean> getBookChapterList() {
if (bookChapterList == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
BookChapterBeanDao targetDao = daoSession.getBookChapterBeanDao();
List<BookChapterBean> bookChapterListNew = targetDao
._queryCollBookBean_BookChapterList(_id);
synchronized (this) {
if (bookChapterList == null) {
bookChapterList = bookChapterListNew;
}
}
}
return bookChapterList;
}


/** Resets a to-many relationship, making the next get call to query for a fresh result. */
@Generated(hash = 1077762221)
public synchronized void resetBookChapterList() {
bookChapterList = null;
}


/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 128553479)
public void delete() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.delete(this);
}


/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 1942392019)
public void refresh() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.refresh(this);
}


/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 713229351)
public void update() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.update(this);
}


public boolean getIsLocal() {
return this.isLocal;
}

public void setIsLocal(boolean isLocal) {
this.isLocal = isLocal;
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this._id);
dest.writeString(this.title);
dest.writeString(this.author);
dest.writeString(this.shortIntro);
dest.writeString(this.cover);
dest.writeByte(this.hasCp ? (byte) 1 : (byte) 0);
dest.writeInt(this.latelyFollower);
dest.writeDouble(this.retentionRatio);
dest.writeString(this.updated);
dest.writeString(this.lastRead);
dest.writeInt(this.chaptersCount);
dest.writeString(this.lastChapter);
dest.writeByte(this.isUpdate ? (byte) 1 : (byte) 0);
dest.writeByte(this.isLocal ? (byte) 1 : (byte) 0);
}

/** called by internal mechanisms, do not call yourself. */
@Generated(hash = 159260324)
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getCollBookBeanDao() : null;
}

protected CollBookBean(Parcel in) {
this._id = in.readString();
this.title = in.readString();
this.author = in.readString();
this.shortIntro = in.readString();
this.cover = in.readString();
this.hasCp = in.readByte() != 0;
this.latelyFollower = in.readInt();
this.retentionRatio = in.readDouble();
this.updated = in.readString();
this.lastRead = in.readString();
this.chaptersCount = in.readInt();
this.lastChapter = in.readString();
this.isUpdate = in.readByte() != 0;
this.isLocal = in.readByte() != 0;
}

public static final Creator<CollBookBean> CREATOR = new Creator<CollBookBean>() {
@Override
public CollBookBean createFromParcel(Parcel source) {
return new CollBookBean(source);
}

@Override
public CollBookBean[] newArray(int size) {
return new CollBookBean[size];
}
};
}

+ 81
- 0
app/src/main/java/com/yzx/webebook/model/bean/CommentBean.java Parādīt failu

@@ -0,0 +1,81 @@
package com.yzx.webebook.model.bean;

/**
* Created by newbiechen on 17-4-29.
*/

public class CommentBean {
/**
* _id : 57fd69356b613e9d1e69febb
* content : 2000年
* author : {"_id":"57b6794f138527405e83382c","avatar":"/avatar/bc/3f/bc3f0b58815e497b00dabb7a14476891","nickname":"孤独患者","activityAvatar":"","type":"normal","lv":6,"gender":"female"}
* floor : 7150
* likeCount : 0
* created : 2016-10-11T22:35:33.303Z
* replyTo : {"_id":"57caec937a142c2277757f2d","floor":7038,"author":{"_id":"576a96dd4cb19fa249303369","nickname":"刘"}}
*/

private String _id;
private String content;
private AuthorBean author;
private int floor;
private int likeCount;
private String created;
private ReplyToBean replyTo;

public String get_id() {
return _id;
}

public void set_id(String _id) {
this._id = _id;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public AuthorBean getAuthor() {
return author;
}

public void setAuthor(AuthorBean author) {
this.author = author;
}

public int getFloor() {
return floor;
}

public void setFloor(int floor) {
this.floor = floor;
}

public int getLikeCount() {
return likeCount;
}

public void setLikeCount(int likeCount) {
this.likeCount = likeCount;
}

public String getCreated() {
return created;
}

public void setCreated(String created) {
this.created = created;
}

public ReplyToBean getReplyTo() {
return replyTo;
}

public void setReplyTo(ReplyToBean replyTo) {
this.replyTo = replyTo;
}
}

+ 31
- 0
app/src/main/java/com/yzx/webebook/model/bean/DetailBean.java Parādīt failu

@@ -0,0 +1,31 @@
package com.yzx.webebook.model.bean;

import java.util.List;

/**
* Created by newbiechen on 17-4-29.
*/

public class DetailBean<T> {
private T detail;
private List<CommentBean> bestComments;
private List<CommentBean> comments;

public DetailBean(T details, List<CommentBean> bestComments, List<CommentBean> comments) {
this.detail = details;
this.bestComments = bestComments;
this.comments = comments;
}

public T getDetail() {
return detail;
}

public List<CommentBean> getBestComments() {
return bestComments;
}

public List<CommentBean> getComments() {
return comments;
}
}

+ 212
- 0
app/src/main/java/com/yzx/webebook/model/bean/DownloadTaskBean.java Parādīt failu

@@ -0,0 +1,212 @@
package com.yzx.webebook.model.bean;


import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.ToMany;

import java.util.List;
import com.yzx.webebook.model.gen.DaoSession;
import com.yzx.webebook.model.gen.BookChapterBeanDao;
import com.yzx.webebook.model.gen.DownloadTaskBeanDao;

/**
* Created by newbiechen on 17-5-11.
*/
@Entity
public class DownloadTaskBean {
public static final int STATUS_LOADING = 1;
public static final int STATUS_WAIT = 2;
public static final int STATUS_PAUSE = 3;
public static final int STATUS_ERROR = 4;
public static final int STATUS_FINISH = 5;

//任务名称 -> 名称唯一不重复
@Id
private String taskName;
//所属的bookId(外健)
private String bookId;

@ToMany(referencedJoinProperty = "taskName")
private List<BookChapterBean> bookChapterList;
//章节的下载进度,默认为初始状态
private int currentChapter = 0;
//最后的章节
private int lastChapter = 0;
//状态:正在下载、下载完成、暂停、等待、下载错误。

private volatile int status = STATUS_WAIT;
//总大小 -> (完成之后才会赋值)
private long size = 0;

/** Used to resolve relations */
@Generated(hash = 2040040024)
private transient DaoSession daoSession;
/** Used for active entity operations. */
@Generated(hash = 1584592296)
private transient DownloadTaskBeanDao myDao;

@Generated(hash = 597395122)
public DownloadTaskBean(String taskName, String bookId, int currentChapter, int lastChapter,
int status, long size) {
this.taskName = taskName;
this.bookId = bookId;
this.currentChapter = currentChapter;
this.lastChapter = lastChapter;
this.status = status;
this.size = size;
}

@Generated(hash = 2123101309)
public DownloadTaskBean() {
}

public String getBookId() {
return bookId;
}

public void setBookId(String bookId) {
this.bookId = bookId;
}

public String getTaskName() {
return taskName;
}

public void setTaskName(String taskName) {
this.taskName = taskName;
if (bookChapterList!=null){
for (BookChapterBean bean : bookChapterList){
bean.setTaskName(getTaskName());
}
}
}

/**
* To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity.
*/
@Generated(hash = 389263273)
public List<BookChapterBean> getBookChapterList() {
if (bookChapterList == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
BookChapterBeanDao targetDao = daoSession.getBookChapterBeanDao();
List<BookChapterBean> bookChapterListNew = targetDao
._queryDownloadTaskBean_BookChapterList(taskName);
synchronized (this) {
if (bookChapterList == null) {
bookChapterList = bookChapterListNew;
}
}
}
return bookChapterList;
}

/**
* 这才是真正的列表使用类。
*
*/
public void setBookChapters(List<BookChapterBean> beans){
bookChapterList = beans;
for (BookChapterBean bean : bookChapterList){
bean.setTaskName(getTaskName());
}
}

public List<BookChapterBean> getBookChapters(){
if (daoSession == null){
return bookChapterList;
}
else {
return getBookChapterList();
}
}

public int getCurrentChapter() {
return currentChapter;
}

public void setCurrentChapter(int current) {
this.currentChapter = current;
}

public int getLastChapter() {
return lastChapter;
}

public void setLastChapter(int lastChapter) {
this.lastChapter = lastChapter;
}

//多线程访问的问题,所以需要同步机制
public int getStatus() {
return status;
}

public void setStatus(int status){
this.status = status;
}

public long getSize() {
return size;
}

public void setSize(long size) {
this.size = size;
}

/** Resets a to-many relationship, making the next get call to query for a fresh result. */
@Generated(hash = 1077762221)
public synchronized void resetBookChapterList() {
bookChapterList = null;
}

/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 128553479)
public void delete() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.delete(this);
}

/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 1942392019)
public void refresh() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.refresh(this);
}

/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 713229351)
public void update() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.update(this);
}

/** called by internal mechanisms, do not call yourself. */
@Generated(hash = 1923117869)
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getDownloadTaskBeanDao() : null;
}

}

+ 67
- 0
app/src/main/java/com/yzx/webebook/model/bean/ReplyToBean.java Parādīt failu

@@ -0,0 +1,67 @@
package com.yzx.webebook.model.bean;

/**
* Created by newbiechen on 17-4-29.
*/

public class ReplyToBean {
/**
* _id : 57caec937a142c2277757f2d
* floor : 7038
* author : {"_id":"576a96dd4cb19fa249303369","nickname":"刘"}
*/

private String _id;
private int floor;
private ReplyAuthorBean author;

public String get_id() {
return _id;
}

public void set_id(String _id) {
this._id = _id;
}

public int getFloor() {
return floor;
}

public void setFloor(int floor) {
this.floor = floor;
}

public ReplyAuthorBean getAuthor() {
return author;
}

public void setAuthor(ReplyAuthorBean author) {
this.author = author;
}

public static class ReplyAuthorBean {
/**
* _id : 576a96dd4cb19fa249303369
* nickname : 刘
*/

private String _id;
private String nickname;

public String get_id() {
return _id;
}

public void set_id(String _id) {
this._id = _id;
}

public String getNickname() {
return nickname;
}

public void setNickname(String nickname) {
this.nickname = nickname;
}
}
}

+ 276
- 0
app/src/main/java/com/yzx/webebook/model/local/BookRepository.java Parādīt failu

@@ -0,0 +1,276 @@
package com.yzx.webebook.model.local;

import android.util.Log;


import com.yzx.webebook.model.bean.BookChapterBean;
import com.yzx.webebook.model.bean.BookRecordBean;
import com.yzx.webebook.model.bean.ChapterInfoBean;
import com.yzx.webebook.model.bean.CollBookBean;
import com.yzx.webebook.model.gen.BookChapterBeanDao;
import com.yzx.webebook.model.gen.BookRecordBeanDao;
import com.yzx.webebook.model.gen.CollBookBeanDao;
import com.yzx.webebook.model.gen.DaoSession;
import com.yzx.webebook.model.gen.DownloadTaskBeanDao;
import com.yzx.webebook.utils.BookManager;
import com.yzx.webebook.utils.Constant;
import com.yzx.webebook.utils.FileUtils;
import com.yzx.webebook.utils.IOUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.List;

import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;

/**
* Created by newbiechen on 17-5-8.
* 存储关于书籍内容的信息(CollBook(收藏书籍),BookChapter(书籍列表),ChapterInfo(书籍章节),BookRecord(记录))
*/

public class BookRepository {
private static final String TAG = "CollBookManager";
private static volatile BookRepository sInstance;
private DaoSession mSession;
private CollBookBeanDao mCollBookDao;
private BookRepository(){
mSession = DaoDbHelper.getInstance()
.getSession();
mCollBookDao = mSession.getCollBookBeanDao();
}

public static BookRepository getInstance(){
if (sInstance == null){
synchronized (BookRepository.class){
if (sInstance == null){
sInstance = new BookRepository();
}
}
}
return sInstance;
}

//存储已收藏书籍
public void saveCollBookWithAsync(CollBookBean bean){
//启动异步存储
mSession.startAsyncSession()
.runInTx(
() -> {
if (bean.getBookChapters() != null){
// 存储BookChapterBean
mSession.getBookChapterBeanDao()
.insertOrReplaceInTx(bean.getBookChapters());
}
//存储CollBook (确保先后顺序,否则出错)
mCollBookDao.insertOrReplace(bean);
}
);
}
/**
* 异步存储。
* 同时保存BookChapter
* @param beans
*/
public void saveCollBooksWithAsync(List<CollBookBean> beans){
mSession.startAsyncSession()
.runInTx(
() -> {
for (CollBookBean bean : beans){
if (bean.getBookChapters() != null){
//存储BookChapterBean(需要修改,如果存在id相同的则无视)
mSession.getBookChapterBeanDao()
.insertOrReplaceInTx(bean.getBookChapters());
}
}
//存储CollBook (确保先后顺序,否则出错)
mCollBookDao.insertOrReplaceInTx(beans);
}
);
}

public void saveCollBook(CollBookBean bean){
mCollBookDao.insertOrReplace(bean);
}

public void saveCollBooks(List<CollBookBean> beans){
mCollBookDao.insertOrReplaceInTx(beans);
}

/**
* 异步存储BookChapter
* @param beans
*/
public void saveBookChaptersWithAsync(List<BookChapterBean> beans){
mSession.startAsyncSession()
.runInTx(
() -> {
//存储BookChapterBean
mSession.getBookChapterBeanDao()
.insertOrReplaceInTx(beans);
Log.d(TAG, "saveBookChaptersWithAsync: "+"进行存储");
}
);
}

/**
* 存储章节
* @param folderName
* @param fileName
* @param content
*/
public void saveChapterInfo(String folderName,String fileName,String content){
File file = BookManager.getBookFile(folderName, fileName);
//获取流并存储
Writer writer = null;
try {
writer = new BufferedWriter(new FileWriter(file));
writer.write(content);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
IOUtils.close(writer);
}
}

public void saveBookRecord(BookRecordBean bean){
mSession.getBookRecordBeanDao()
.insertOrReplace(bean);
}

/*****************************get************************************************/
public CollBookBean getCollBook(String bookId){
CollBookBean bean = mCollBookDao.queryBuilder()
.where(CollBookBeanDao.Properties._id.eq(bookId))
.unique();
return bean;
}


public List<CollBookBean> getCollBooks(){
return mCollBookDao
.queryBuilder()
.orderDesc(CollBookBeanDao.Properties.LastRead)
.list();
}



//获取书籍列表
public Single<List<BookChapterBean>> getBookChaptersInRx(String bookId){
return Single.create(new SingleOnSubscribe<List<BookChapterBean>>() {
@Override
public void subscribe(SingleEmitter<List<BookChapterBean>> e) throws Exception {
List<BookChapterBean> beans = mSession
.getBookChapterBeanDao()
.queryBuilder()
.where(BookChapterBeanDao.Properties.BookId.eq(bookId))
.list();
e.onSuccess(beans);
}
});
}

//获取阅读记录
public BookRecordBean getBookRecord(String bookId){
return mSession.getBookRecordBeanDao()
.queryBuilder()
.where(BookRecordBeanDao.Properties.BookId.eq(bookId))
.unique();
}

//TODO:需要进行获取编码并转换的问题
public ChapterInfoBean getChapterInfoBean(String folderName, String fileName){
File file = new File(Constant.BOOK_CACHE_PATH + folderName
+ File.separator + fileName + FileUtils.SUFFIX_NB);
if (!file.exists()) return null;
Reader reader = null;
String str = null;
StringBuilder sb = new StringBuilder();
try {
reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
while ((str = br.readLine()) != null){
sb.append(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.close(reader);
}

ChapterInfoBean bean = new ChapterInfoBean();
bean.setTitle(fileName);
bean.setBody(sb.toString());
return bean;
}

/************************************************************/

/************************************************************/
public Single<Void> deleteCollBookInRx(CollBookBean bean) {
return Single.create(new SingleOnSubscribe<Void>() {
@Override
public void subscribe(SingleEmitter<Void> e) throws Exception {
//查看文本中是否存在删除的数据
deleteBook(bean.get_id());
//删除任务
deleteDownloadTask(bean.get_id());
//删除目录
deleteBookChapter(bean.get_id());
//删除CollBook
mCollBookDao.delete(bean);
e.onSuccess(new Void());
}
});
}

//这个需要用rx,进行删除
public void deleteBookChapter(String bookId){
mSession.getBookChapterBeanDao()
.queryBuilder()
.where(BookChapterBeanDao.Properties.BookId.eq(bookId))
.buildDelete()
.executeDeleteWithoutDetachingEntities();
}

public void deleteCollBook(CollBookBean collBook){
mCollBookDao.delete(collBook);
}

//删除书籍
public void deleteBook(String bookId){
FileUtils.deleteFile(Constant.BOOK_CACHE_PATH+bookId);
}

public void deleteBookRecord(String id){
mSession.getBookRecordBeanDao()
.queryBuilder()
.where(BookRecordBeanDao.Properties.BookId.eq(id))
.buildDelete()
.executeDeleteWithoutDetachingEntities();
}

//删除任务
public void deleteDownloadTask(String bookId){
mSession.getDownloadTaskBeanDao()
.queryBuilder()
.where(DownloadTaskBeanDao.Properties.BookId.eq(bookId))
.buildDelete()
.executeDeleteWithoutDetachingEntities();
}

public DaoSession getSession(){
return mSession;
}
}

+ 56
- 0
app/src/main/java/com/yzx/webebook/model/local/DaoDbHelper.java Parādīt failu

@@ -0,0 +1,56 @@
package com.yzx.webebook.model.local;

import android.database.sqlite.SQLiteDatabase;

import com.yzx.webebook.App;
import com.yzx.webebook.model.gen.DaoMaster;
import com.yzx.webebook.model.gen.DaoSession;


/**
* Created by newbiechen on 17-4-26.
*/

public class DaoDbHelper {
private static final String DB_NAME = "IReader_DB";

private static volatile DaoDbHelper sInstance;
private SQLiteDatabase mDb;
private DaoMaster mDaoMaster;
private DaoSession mSession;

private DaoDbHelper(){
//封装数据库的创建、更新、删除
DaoMaster.DevOpenHelper openHelper = new MyOpenHelper(App.Companion.getContext(),DB_NAME,null);
//获取数据库
mDb = openHelper.getWritableDatabase();
//封装数据库中表的创建、更新、删除
mDaoMaster = new DaoMaster(mDb); //合起来就是对数据库的操作
//对表操作的对象。
mSession = mDaoMaster.newSession(); //可以认为是对数据的操作
}


public static DaoDbHelper getInstance(){
if (sInstance == null){
synchronized (DaoDbHelper.class){
if (sInstance == null){
sInstance = new DaoDbHelper();
}
}
}
return sInstance;
}

public DaoSession getSession(){
return mSession;
}

public SQLiteDatabase getDatabase(){
return mDb;
}

public DaoSession getNewSession(){
return mDaoMaster.newSession();
}
}

+ 16
- 0
app/src/main/java/com/yzx/webebook/model/local/DeleteDbHelper.java Parādīt failu

@@ -0,0 +1,16 @@
package com.yzx.webebook.model.local;



import com.yzx.webebook.model.bean.AuthorBean;

import java.util.List;

/**
* Created by newbiechen on 17-4-28.
*/

public interface DeleteDbHelper {
void deleteAuthors(List<AuthorBean> beans);
void deleteAll();
}

+ 23
- 0
app/src/main/java/com/yzx/webebook/model/local/GetDbHelper.java Parādīt failu

@@ -0,0 +1,23 @@
package com.yzx.webebook.model.local;



import com.yzx.webebook.model.bean.AuthorBean;
import com.yzx.webebook.model.bean.DownloadTaskBean;

import java.util.List;

import io.reactivex.Single;

/**
* Created by newbiechen on 17-4-28.
*/

public interface GetDbHelper {


AuthorBean getAuthor(String id);

/******************************/
List<DownloadTaskBean> getDownloadTaskList();
}

+ 77
- 0
app/src/main/java/com/yzx/webebook/model/local/LocalRepository.java Parādīt failu

@@ -0,0 +1,77 @@
package com.yzx.webebook.model.local;


import com.google.gson.Gson;
import com.yzx.webebook.model.bean.AuthorBean;
import com.yzx.webebook.model.bean.DownloadTaskBean;
import com.yzx.webebook.model.gen.DaoSession;

import org.greenrobot.greendao.Property;
import org.greenrobot.greendao.query.Join;
import org.greenrobot.greendao.query.QueryBuilder;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;

/**
* Created by newbiechen on 17-4-26.
*/

public class LocalRepository implements SaveDbHelper,GetDbHelper,DeleteDbHelper{
private static final String TAG = "LocalRepository";
private static final String DISTILLATE_ALL = "normal";
private static final String DISTILLATE_BOUTIQUES = "distillate";

private static volatile LocalRepository sInstance;
private DaoSession mSession;
private LocalRepository(){
mSession = DaoDbHelper.getInstance().getSession();
}

public static LocalRepository getInstance(){
if (sInstance == null){
synchronized (LocalRepository.class){
if (sInstance == null){
sInstance = new LocalRepository();
}
}
}
return sInstance;
}


@Override
public void deleteAuthors(List<AuthorBean> beans) {

}

@Override
public void deleteAll() {
//清空全部数据。
}

@Override
public AuthorBean getAuthor(String id) {
return null;
}

@Override
public List<DownloadTaskBean> getDownloadTaskList() {
return null;
}

@Override
public void saveAuthors(List<AuthorBean> beans) {

}

@Override
public void saveDownloadTask(DownloadTaskBean bean) {

}
}

+ 34
- 0
app/src/main/java/com/yzx/webebook/model/local/MyOpenHelper.java Parādīt failu

@@ -0,0 +1,34 @@
package com.yzx.webebook.model.local;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;


import com.yzx.webebook.model.gen.DaoMaster;
import com.yzx.webebook.model.local.update.Update2Helper;

import org.greenrobot.greendao.database.Database;

/**
* Created by newbiechen on 2017/10/9.
*/

public class MyOpenHelper extends DaoMaster.DevOpenHelper{
public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}

@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
// 跨版本更新策略
switch (oldVersion){
case 1:
// 暂无 1.0
case 2:
// 更新数据到 3.0
Update2Helper.getInstance().update(db);
default:
break;
}
}
}

+ 134
- 0
app/src/main/java/com/yzx/webebook/model/local/ReadSettingManager.java Parādīt failu

@@ -0,0 +1,134 @@
package com.yzx.webebook.model.local;


import com.yzx.webebook.utils.ScreenUtils;
import com.yzx.webebook.utils.SharedPreUtils;
import com.yzx.webebook.widget.page.PageMode;
import com.yzx.webebook.widget.page.PageStyle;

/**
* Created by newbiechen on 17-5-17.
* 阅读器的配置管理
*/

public class ReadSettingManager {
/*************实在想不出什么好记的命名方式。。******************/
public static final int READ_BG_DEFAULT = 0;
public static final int READ_BG_1 = 1;
public static final int READ_BG_2 = 2;
public static final int READ_BG_3 = 3;
public static final int READ_BG_4 = 4;
public static final int NIGHT_MODE = 5;

public static final String SHARED_READ_BG = "shared_read_bg";
public static final String SHARED_READ_BRIGHTNESS = "shared_read_brightness";
public static final String SHARED_READ_IS_BRIGHTNESS_AUTO = "shared_read_is_brightness_auto";
public static final String SHARED_READ_TEXT_SIZE = "shared_read_text_size";
public static final String SHARED_READ_IS_TEXT_DEFAULT = "shared_read_text_default";
public static final String SHARED_READ_PAGE_MODE = "shared_read_mode";
public static final String SHARED_READ_NIGHT_MODE = "shared_night_mode";
public static final String SHARED_READ_VOLUME_TURN_PAGE = "shared_read_volume_turn_page";
public static final String SHARED_READ_FULL_SCREEN = "shared_read_full_screen";
public static final String SHARED_READ_CONVERT_TYPE = "shared_read_convert_type";

private static volatile ReadSettingManager sInstance;

private SharedPreUtils sharedPreUtils;

public static ReadSettingManager getInstance() {
if (sInstance == null) {
synchronized (ReadSettingManager.class) {
if (sInstance == null) {
sInstance = new ReadSettingManager();
}
}
}
return sInstance;
}

private ReadSettingManager() {
sharedPreUtils = SharedPreUtils.getInstance();
}

public void setPageStyle(PageStyle pageStyle) {
sharedPreUtils.putInt(SHARED_READ_BG, pageStyle.ordinal());
}

public void setBrightness(int progress) {
sharedPreUtils.putInt(SHARED_READ_BRIGHTNESS, progress);
}

public void setAutoBrightness(boolean isAuto) {
sharedPreUtils.putBoolean(SHARED_READ_IS_BRIGHTNESS_AUTO, isAuto);
}

public void setDefaultTextSize(boolean isDefault) {
sharedPreUtils.putBoolean(SHARED_READ_IS_TEXT_DEFAULT, isDefault);
}

public void setTextSize(int textSize) {
sharedPreUtils.putInt(SHARED_READ_TEXT_SIZE, textSize);
}

public void setPageMode(PageMode mode) {
sharedPreUtils.putInt(SHARED_READ_PAGE_MODE, mode.ordinal());
}

public void setNightMode(boolean isNight) {
sharedPreUtils.putBoolean(SHARED_READ_NIGHT_MODE, isNight);
}

public int getBrightness() {
return sharedPreUtils.getInt(SHARED_READ_BRIGHTNESS, 40);
}

public boolean isBrightnessAuto() {
return sharedPreUtils.getBoolean(SHARED_READ_IS_BRIGHTNESS_AUTO, false);
}

public int getTextSize() {
return sharedPreUtils.getInt(SHARED_READ_TEXT_SIZE, ScreenUtils.spToPx(28));
}

public boolean isDefaultTextSize() {
return sharedPreUtils.getBoolean(SHARED_READ_IS_TEXT_DEFAULT, false);
}

public PageMode getPageMode() {
int mode = sharedPreUtils.getInt(SHARED_READ_PAGE_MODE, PageMode.SIMULATION.ordinal());
return PageMode.values()[mode];
}

public PageStyle getPageStyle() {
int style = sharedPreUtils.getInt(SHARED_READ_BG, PageStyle.BG_0.ordinal());
return PageStyle.values()[style];
}

public boolean isNightMode() {
return sharedPreUtils.getBoolean(SHARED_READ_NIGHT_MODE, false);
}

public void setVolumeTurnPage(boolean isTurn) {
sharedPreUtils.putBoolean(SHARED_READ_VOLUME_TURN_PAGE, isTurn);
}

public boolean isVolumeTurnPage() {
return sharedPreUtils.getBoolean(SHARED_READ_VOLUME_TURN_PAGE, false);
}

public void setFullScreen(boolean isFullScreen) {
sharedPreUtils.putBoolean(SHARED_READ_FULL_SCREEN, isFullScreen);
}

public boolean isFullScreen() {
return sharedPreUtils.getBoolean(SHARED_READ_FULL_SCREEN, false);
}

public void setConvertType(int convertType) {
sharedPreUtils.putInt(SHARED_READ_CONVERT_TYPE, convertType);
}

public int getConvertType() {
return sharedPreUtils.getInt(SHARED_READ_CONVERT_TYPE, 0);
}
}

+ 18
- 0
app/src/main/java/com/yzx/webebook/model/local/SaveDbHelper.java Parādīt failu

@@ -0,0 +1,18 @@
package com.yzx.webebook.model.local;



import com.yzx.webebook.model.bean.AuthorBean;
import com.yzx.webebook.model.bean.DownloadTaskBean;

import java.util.List;

/**
* Created by newbiechen on 17-4-28.
*/

public interface SaveDbHelper {
void saveAuthors(List<AuthorBean> beans);
/*************DownloadTask*********************/
void saveDownloadTask(DownloadTaskBean bean);
}

+ 8
- 0
app/src/main/java/com/yzx/webebook/model/local/Void.java Parādīt failu

@@ -0,0 +1,8 @@
package com.yzx.webebook.model.local;

/**
* Created by newbiechen on 17-5-27.
*/

public final class Void {
}

+ 207
- 0
app/src/main/java/com/yzx/webebook/model/local/update/MigrationHelper.java Parādīt failu

@@ -0,0 +1,207 @@
package com.yzx.webebook.model.local.update;

import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.internal.DaoConfig;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Created by newbiechen on 2017/10/9.
* 数据库更新策略
*/

public class MigrationHelper {

private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
private static MigrationHelper instance;

public static MigrationHelper getInstance() {
if (instance == null) {
instance = new MigrationHelper();
}
return instance;
}

public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {

generateTempTables(db, daoClasses);
deleteOriginalTables(db, daoClasses);
createOrignalTables(db, daoClasses);
restoreData(db, daoClasses);
}

/**
* 生成临时列表
*
* @param db
* @param daoClasses
*/
private void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);

String divider = "";
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList<>();

StringBuilder createTableStringBuilder = new StringBuilder();

createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");

for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;

if (getColumns(db, tableName).contains(columnName)) {
properties.add(columnName);

String type = null;

try {
type = getTypeByClass(daoConfig.properties[j].type);
} catch (Exception exception) {
exception.printStackTrace();
}

createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);

if (daoConfig.properties[j].primaryKey) {
createTableStringBuilder.append(" PRIMARY KEY");
}

divider = ",";
}
}
createTableStringBuilder.append(");");

db.execSQL(createTableStringBuilder.toString());

StringBuilder insertTableStringBuilder = new StringBuilder();

insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(" FROM ").append(tableName).append(";");

db.execSQL(insertTableStringBuilder.toString());

}
}


/**
* 通过反射,删除要更新的表
*/
private void deleteOriginalTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (Class<? extends AbstractDao<?, ?>> daoClass : daoClasses) {
try {
Method method = daoClass.getMethod("dropTable", Database.class, boolean.class);
method.invoke(null, db, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}

/**
* 通过反射,重新创建要更新的表
*/
private void createOrignalTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (Class<? extends AbstractDao<?, ?>> daoClass : daoClasses) {
try {
Method method = daoClass.getMethod("createTable", Database.class, boolean.class);
method.invoke(null, db, false);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}

/**
* 存储新的数据库表 以及数据
*
* @param db
* @param daoClasses
*/
private void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList();

for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;

if (getColumns(db, tempTableName).contains(columnName)) {
properties.add(columnName);
}
}

StringBuilder insertTableStringBuilder = new StringBuilder();

insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");

StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(insertTableStringBuilder.toString());
db.execSQL(dropTableStringBuilder.toString());
}
}

private String getTypeByClass(Class<?> type) throws Exception {
if (type.equals(String.class)) {
return "TEXT";
}
if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
return "INTEGER";
}
if (type.equals(Boolean.class)) {
return "BOOLEAN";
}

Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
exception.printStackTrace();
throw exception;
}

private List<String> getColumns(Database db, String tableName) {
List<String> columns = new ArrayList<>();
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
if (cursor != null) {
columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return columns;
}
}

+ 284
- 0
app/src/main/java/com/yzx/webebook/model/local/update/Update2Helper.java Parādīt failu

@@ -0,0 +1,284 @@
package com.yzx.webebook.model.local.update;

import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;


import com.yzx.webebook.model.gen.BookChapterBeanDao;
import com.yzx.webebook.model.gen.CollBookBeanDao;
import com.yzx.webebook.utils.MD5Utils;

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.internal.DaoConfig;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Created by newbiechen on 2018/1/5.
* 由于 BookChapterBean 做了一次表的大更改,所以需要自定义更新。
* 作用:将数据库2.0 升级到 3.0
*/

public class Update2Helper {
private static final String TAG = "BookChapterHelper";
private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";

private static final String DIVIDER = ",";
private static final String QUOTE = "'%s'";

private static Update2Helper instance;

public static Update2Helper getInstance() {
if (instance == null) {
instance = new Update2Helper();
}
return instance;
}

public void update(Database db) {
updateCollBook(db);
updateBookChapter(db);
}

private void updateBookChapter(Database db) {
Class<? extends AbstractDao<?, ?>> bookChapterClass = BookChapterBeanDao.class;

generateTempTables(db, bookChapterClass);
deleteOriginalTables(db, bookChapterClass);
createOrignalTables(db, bookChapterClass);
restoreData(db, bookChapterClass);
}

private void updateCollBook(Database db) {
Class<? extends AbstractDao<?, ?>> collBookClass = CollBookBeanDao.class;

// 遍历查找本地文件,然后修改本地文件的数据
DaoConfig daoConfig = new DaoConfig(db, collBookClass);
String tableName = daoConfig.tablename;

Cursor cursor = db.rawQuery("select _ID,IS_LOCAL from " + tableName, null);
String id = null;
String cover = null;
String isLocal = null;

StringBuilder updateSb = new StringBuilder();
while (cursor.moveToNext()) {
cover = cursor.getString(0);
id = MD5Utils.strToMd5By16(cover);
isLocal = cursor.getString(1);

//如果是本地文件
if (isLocal.equals("1")) {
// 数据更新
updateSb.append("UPDATE " + tableName + " SET ");
updateSb.append("_ID=").append(String.format(QUOTE, id)).append(DIVIDER);
updateSb.append("COVER=").append(String.format(QUOTE, cover)).append(" ");
updateSb.append("WHERE _ID=").append(String.format(QUOTE,cover)).append(";");

db.execSQL(updateSb.toString());
updateSb.delete(0, updateSb.length());
}
}
}

/**
* 生成临时列表
*
* @param db
*/
private void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>> bookChapterClass) {
// 解析 GreenDao,获取 table 名
DaoConfig daoConfig = new DaoConfig(db, bookChapterClass);
String tableName = daoConfig.tablename;

// 创建临时 table 名。
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList<>();

StringBuilder createTableStringBuilder = new StringBuilder();
createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");

// 新增的三个字段
String ID = "ID";
String START = "START";
String END = "end";


// 新建的 id 主键字段
createTableStringBuilder.append(ID + " ").append("TEXT ").append("PRIMARY KEY");
properties.add(ID);

// 获取符合新表的旧字段。
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tableName).contains(columnName)) {
properties.add(columnName);

String type = null;

try {
type = getTypeByClass(daoConfig.properties[j].type);
} catch (Exception exception) {
exception.printStackTrace();
}

createTableStringBuilder.append(DIVIDER).append(columnName).append(" ").append(type);
}
}

// 新建的 START,和 END 字段。
createTableStringBuilder.append(DIVIDER).append(START).append(" ").append("INTEGER");
createTableStringBuilder.append(DIVIDER).append(END).append(" ").append("INTEGER");

properties.add(START);
properties.add(END);

createTableStringBuilder.append(");");
// 创建临时数据表
db.execSQL(createTableStringBuilder.toString());

StringBuilder insertTableStringBuilder = new StringBuilder();

// 将 link 字段的文件的内容转换成 Id
Cursor cursor = db.rawQuery("select * from " + daoConfig.tablename, null);

String id = null;
String link = null;
String title = null;
String taskName = null;
String unreadble = null;
String bookId = null;

while (cursor.moveToNext()) {
link = cursor.getString(0);
id = MD5Utils.strToMd5By16(link);
title = cursor.getString(1);
taskName = cursor.getString(2);
unreadble = cursor.getString(4);
bookId = cursor.getString(3);

insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") VALUES (");
insertTableStringBuilder.append(String.format(QUOTE, id)).append(DIVIDER);
insertTableStringBuilder.append(String.format(QUOTE, link)).append(DIVIDER);
insertTableStringBuilder.append(String.format(QUOTE, title)).append(DIVIDER);
insertTableStringBuilder.append(String.format(QUOTE, taskName)).append(DIVIDER);
insertTableStringBuilder.append(unreadble).append(DIVIDER);
insertTableStringBuilder.append(String.format(QUOTE, bookId)).append(DIVIDER);
insertTableStringBuilder.append("0").append(DIVIDER);
insertTableStringBuilder.append("0").append(");");

db.execSQL(insertTableStringBuilder.toString());

insertTableStringBuilder.delete(0, insertTableStringBuilder.length());
}
}


/**
* 通过反射,删除要更新的表
*/
private void deleteOriginalTables(Database db,Class<? extends AbstractDao<?, ?>> bookChapterClass) {
try {
Method method = bookChapterClass.getMethod("dropTable", Database.class, boolean.class);
method.invoke(null, db, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}

/**
* 通过反射,重新创建要更新的表
*/
private void createOrignalTables(Database db,Class<? extends AbstractDao<?, ?>> bookChapterClass) {
try {
Method method = bookChapterClass.getMethod("createTable", Database.class, boolean.class);
method.invoke(null, db, false);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}

/**
* 存储新的数据库表 以及数据
*
* @param db
*/
private void restoreData(Database db,Class<? extends AbstractDao<?, ?>> bookChapterClass) {
DaoConfig daoConfig = new DaoConfig(db, bookChapterClass);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList();

for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tableName).contains(columnName)) {
properties.add(columnName);
}
}

StringBuilder insertTableStringBuilder = new StringBuilder();

insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");

Log.d(TAG, "restoreData: " + insertTableStringBuilder.toString());

StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(insertTableStringBuilder.toString());
db.execSQL(dropTableStringBuilder.toString());
}

private String getTypeByClass(Class<?> type) throws Exception {
if (type.equals(String.class)) {
return "TEXT";
}
if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
return "INTEGER";
}
if (type.equals(boolean.class)) {
return "BOOLEAN";
}

Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
exception.printStackTrace();
throw exception;
}

private List<String> getColumns(Database db, String tableName) {
List<String> columns = new ArrayList<>();
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
if (cursor != null) {
columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return columns;
}
}

+ 25
- 0
app/src/main/java/com/yzx/webebook/modules/ActivityWXModule.kt Parādīt failu

@@ -0,0 +1,25 @@
package com.yzx.webebook.modules


import android.widget.Toast
import com.yzx.webebook.activity.WeexTestActivity
import org.apache.weex.annotation.JSMethod
import org.apache.weex.common.WXModule
import org.jetbrains.anko.startActivity


class ActivityWXModule : WXModule() {

@JSMethod(uiThread = true)
public fun navigateTo(url:String,params: String) {
// val intent = Intent(mWXSDKInstance.context, WeexTestActivity::class.java)
// intent.putExtra("url", url)
// intent.putExtra("params", params)
mWXSDKInstance.context.startActivity<WeexTestActivity>("url" to url,"params" to params)
}

@JSMethod(uiThread = true)
public fun toast(msg: String?) {
Toast.makeText(mWXSDKInstance.context, msg, Toast.LENGTH_SHORT).show()
}
}

+ 24
- 0
app/src/main/java/com/yzx/webebook/presenter/ReadPresenter.java Parādīt failu

@@ -0,0 +1,24 @@
package com.yzx.webebook.presenter;

import com.yzx.webebook.presenter.base.BasePresenter;
import com.yzx.webebook.widget.page.TxtChapter;

import org.jetbrains.annotations.NotNull;

import java.util.List;

public class ReadPresenter extends BasePresenter<ReadView> {

public ReadPresenter(@NotNull ReadView view) {
super(view);
}


public void loadCategory(String bookId){

}

public void loadChapter(String bookId, List<TxtChapter> bookChapterList){

}
}

+ 14
- 0
app/src/main/java/com/yzx/webebook/presenter/ReadView.java Parādīt failu

@@ -0,0 +1,14 @@
package com.yzx.webebook.presenter;

import com.yzx.webebook.model.bean.BookChapterBean;
import com.yzx.webebook.presenter.base.IView;

import java.util.List;

public interface ReadView extends IView {


void showCategory(List<BookChapterBean> bookChapterList);
void finishChapter();
void errorChapter();
}

+ 221
- 0
app/src/main/java/com/yzx/webebook/utils/BookManager.java Parādīt failu

@@ -0,0 +1,221 @@
package com.yzx.webebook.utils;


import java.io.File;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

/**
* Created by newbiechen on 17-5-20.
* 处理书籍的工具类,配合PageFactory使用
* 已弃用,
*/

public class BookManager{
private static final String TAG = "BookManager";
private String chapterName;
private String bookId;
private long chapterLen;
private long position;
private Map<String, Cache> cacheMap = new HashMap<>();
private static volatile BookManager sInstance;

public static BookManager getInstance(){
if (sInstance == null){
synchronized (BookManager.class){
if (sInstance == null){
sInstance = new BookManager();
}
}
}
return sInstance;
}

public boolean openChapter(String bookId,String chapterName){
return openChapter(bookId,chapterName,0);
}

public boolean openChapter(String bookId,String chapterName,long position){
//如果文件不存在,则打开失败
File file = new File(Constant.BOOK_CACHE_PATH + bookId
+ File.separator + chapterName + FileUtils.SUFFIX_NB);
if (!file.exists()){
return false;
}
this.bookId = bookId;
this.chapterName = chapterName;
this.position = position;
createCache();
return true;
}

private void createCache(){
//创建Cache
if (!cacheMap.containsKey(chapterName)){
Cache cache = new Cache();
File file = getBookFile(bookId, chapterName);
//TODO:数据加载默认utf-8(以后会增加判断),FileUtils采用Reader获取数据的,可能用byte会更好一点
char[] array = FileUtils.getFileContent(file).toCharArray();
WeakReference<char[]> charReference = new WeakReference<char[]>(array);
cache.size = array.length;
cache.data = charReference;
cacheMap.put(chapterName, cache);

chapterLen = cache.size;
}
else {
chapterLen = cacheMap.get(chapterName).getSize();
}
}

public void setPosition(long position){
this.position = position;
}

public long getPosition(){
return position;
}

//获取上一段
public String getPrevPara(){
//首先判断是否Position已经达到起始位置,已经越界
if (position < 0){
return null;
}

//初始化从后向前获取的起始点,终止点,文本
int end = (int)position;
int begin = end;
char[] array = getContent();

while (begin >= 0) { //判断指针是否达到章节的起始位置
char character = array[begin]; //获取当前指针下的字符

//判断当前字符是否为换行,如果为换行,就代表获取到了一个段落,并退出。
//有可能发生初始指针指的就是换行符的情况。
if ((character+"").equals("\n") && begin != end) {
position = begin;
//当当前指针指向换行符的时候向后退一步
begin++;
break;
}
//向前进一步
begin--;
}
//最后end获取到段落的起始点,begin是段落的终止点。

//当越界的时候,保证begin在章节内
if (begin < 0){
begin = 0;//在章节内
position = -1; //越界
}
int size = end+1 - begin;
return new String(array,begin,size);
}

//获取下一段
public String getNextPara(){
//首先判断是否Position已经达到终点位置
if (position >= chapterLen){
return null;
}

//初始化起始点,终止点。
int begin = (int)position;
int end = begin;
char[] array = getContent();

while (end < chapterLen) { //判断指针是否在章节的末尾位置
char character = array[end]; //获取当前指针下的字符
//判断当前字符是否为换行,如果为换行,就代表获取到了一个段落,并退出。
//有可能发生初始指针指的就是换行符的情况。
//这里当遇到\n的时候,不需要回退
if ((character+"").equals("\n") && begin != end){
++end;//指向下一字段
position = end;
break;
}
//指向下一字段
end++;
}
//所要获取的字段的长度
int size = end - begin;
return new String(array,begin,size);
}

//获取章节的内容
public char[] getContent() {
if (cacheMap.size() == 0){
return new char[1];
}
char[] block = cacheMap.get(chapterName).getData().get();
if (block == null) {
File file = getBookFile(bookId, chapterName);
block = FileUtils.getFileContent(file).toCharArray();
Cache cache = cacheMap.get(chapterName);
cache.data = new WeakReference<char[]>(block);
}
return block;
}

public long getChapterLen(){
return chapterLen;
}

public void clear(){
cacheMap.clear();
position = 0;
chapterLen = 0;
}

/**
* 创建或获取存储文件
* @param folderName
* @param fileName
* @return
*/
public static File getBookFile(String folderName, String fileName){
return FileUtils.getFile(Constant.BOOK_CACHE_PATH + folderName
+ File.separator + fileName + FileUtils.SUFFIX_NB);
}

public static long getBookSize(String folderName){
return FileUtils.getDirSize(FileUtils
.getFolder(Constant.BOOK_CACHE_PATH + folderName));
}

/**
* 根据文件名判断是否被缓存过 (因为可能数据库显示被缓存过,但是文件中却没有的情况,所以需要根据文件判断是否被缓存
* 过)
* @param folderName : bookId
* @param fileName: chapterName
* @return
*/
public static boolean isChapterCached(String folderName, String fileName){
File file = new File(Constant.BOOK_CACHE_PATH + folderName
+ File.separator + fileName + FileUtils.SUFFIX_NB);
return file.exists();
}

public class Cache {
private long size;
private WeakReference<char[]> data;

public WeakReference<char[]> getData() {
return data;
}

public void setData(WeakReference<char[]> data) {
this.data = data;
}

public long getSize() {
return size;
}

public void setSize(long size) {
this.size = size;
}
}
}

+ 108
- 0
app/src/main/java/com/yzx/webebook/utils/BrightnessUtils.java Parādīt failu

@@ -0,0 +1,108 @@
package com.yzx.webebook.utils;

import android.app.Activity;
import android.content.ContentResolver;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManager;

/**
* Created by newbiechen on 17-5-19.
* 调节亮度的工具类
*/

public class BrightnessUtils {
private static final String TAG = "BrightnessUtils";

/**
* 判断是否开启了自动亮度调节
*/
public static boolean isAutoBrightness(Activity activity) {
boolean isAuto = false;
try {
isAuto = Settings.System.getInt(activity.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE) == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
} catch (Settings.SettingNotFoundException e){
e.printStackTrace();
}
return isAuto;
}

/**
* 获取屏幕的亮度
* 系统亮度模式中,自动模式与手动模式获取到的系统亮度的值不同
*/
public static int getScreenBrightness(Activity activity) {
if(isAutoBrightness(activity)){
return getAutoScreenBrightness(activity);
}else{
return getManualScreenBrightness(activity);
}
}

/**
* 获取手动模式下的屏幕亮度
* @return value:0~255
*/
public static int getManualScreenBrightness(Activity activity) {
int nowBrightnessValue = 0;
ContentResolver resolver = activity.getContentResolver();
try {
nowBrightnessValue = Settings.System.getInt(resolver, Settings.System.SCREEN_BRIGHTNESS);
} catch (Exception e) {
e.printStackTrace();
}
return nowBrightnessValue;
}

/**
* 获取自动模式下的屏幕亮度
* @return value:0~255
*/
public static int getAutoScreenBrightness(Activity activity) {
float nowBrightnessValue = 0;

//获取自动调节下的亮度范围在 0~1 之间
ContentResolver resolver = activity.getContentResolver();
try {
nowBrightnessValue = Settings.System.getFloat(resolver, Settings.System.SCREEN_BRIGHTNESS);
Log.d(TAG, "getAutoScreenBrightness: " + nowBrightnessValue);
} catch (Exception e) {
e.printStackTrace();
}
//转换范围为 (0~255)
float fValue = nowBrightnessValue * 225.0f;
Log.d(TAG,"brightness: " + fValue);
return (int)fValue;
}

/**
* 设置亮度:通过设置 Windows 的 screenBrightness 来修改当前 Windows 的亮度
* lp.screenBrightness:参数范围为 0~1
*/
public static void setBrightness(Activity activity, int brightness) {
try{
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
//将 0~255 范围内的数据,转换为 0~1
lp.screenBrightness = Float.valueOf(brightness) * (1f / 255f);
Log.d(TAG, "lp.screenBrightness == " + lp.screenBrightness);
activity.getWindow().setAttributes(lp);
}catch(Exception ex){
ex.printStackTrace();
}
}

/**
* 获取当前系统的亮度
* @param activity
*/
public static void setDefaultBrightness(Activity activity) {
try {
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
activity.getWindow().setAttributes(lp);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

+ 22
- 0
app/src/main/java/com/yzx/webebook/utils/Charset.java Parādīt failu

@@ -0,0 +1,22 @@
package com.yzx.webebook.utils;

/**
* 编码类型
*/
public enum Charset {
UTF8("UTF-8"),
UTF16LE("UTF-16LE"),
UTF16BE("UTF-16BE"),
GBK("GBK");
private String mName;
public static final byte BLANK = 0x0a;

private Charset(String name) {
mName = name;
}
public String getName() {
return mName;
}
}

+ 101
- 0
app/src/main/java/com/yzx/webebook/utils/Constant.java Parādīt failu

@@ -0,0 +1,101 @@
package com.yzx.webebook.utils;


import androidx.annotation.StringDef;

import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;

/**
* Created by newbiechen on 17-4-16.
*/

public class Constant {
/*SharedPreference*/
public static final String SHARED_SEX = "sex";
public static final String SHARED_SAVE_BOOK_SORT = "book_sort";
public static final String SHARED_SAVE_BILLBOARD = "billboard";
public static final String SHARED_CONVERT_TYPE = "convert_type";
public static final String SEX_BOY = "boy";
public static final String SEX_GIRL = "girl";

/*URL_BASE*/
public static final String API_BASE_URL = "http://api.zhuishushenqi.com";
public static final String IMG_BASE_URL = "http://statics.zhuishushenqi.com";
//book type
public static final String BOOK_TYPE_COMMENT = "normal";
public static final String BOOK_TYPE_VOTE = "vote";
//book state
public static final String BOOK_STATE_NORMAL = "normal";
public static final String BOOK_STATE_DISTILLATE = "distillate";
//Book Date Convert Format
public static final String FORMAT_BOOK_DATE = "yyyy-MM-dd'T'HH:mm:ss";
public static final String FORMAT_TIME = "HH:mm";
public static final String FORMAT_FILE_DATE = "yyyy-MM-dd";
//RxBus
public static final int MSG_SELECTOR = 1;
//BookCachePath (因为getCachePath引用了Context,所以必须是静态变量,不能够是静态常量)
public static String BOOK_CACHE_PATH = FileUtils.getCachePath()+File.separator
+ "book_cache"+ File.separator ;
//文件阅读记录保存的路径
public static String BOOK_RECORD_PATH = FileUtils.getCachePath() + File.separator
+ "book_record" + File.separator;


//BookType
@StringDef({
BookType.ALL,
BookType.XHQH,
BookType.WXXX,
BookType.DSYN,
BookType.LSJS,
BookType.YXJJ,
BookType.KHLY,
BookType.CYJK,
BookType.HMZC,
BookType.XDYQ,
BookType.GDYQ,
BookType.HXYQ,
BookType.DMTR
})
@Retention(RetentionPolicy.SOURCE)
public @interface BookType {
String ALL = "all";

String XHQH = "xhqh";

String WXXX = "wxxx";

String DSYN = "dsyn";

String LSJS = "lsjs";

String YXJJ = "yxjj";
String KHLY = "khly";
String CYJK = "cyjk";
String HMZC = "hmzc";
String XDYQ = "xdyq";
String GDYQ = "gdyq";
String HXYQ = "hxyq";
String DMTR = "dmtr";
}

public static Map<String, String> bookType = new HashMap<String, String>() {{
put("qt", "其他");
put(BookType.XHQH, "玄幻奇幻");
put(BookType.WXXX, "武侠仙侠");
put(BookType.DSYN, "都市异能");
put(BookType.LSJS, "历史军事");
put(BookType.YXJJ, "游戏竞技");
put(BookType.KHLY, "科幻灵异");
put(BookType.CYJK, "穿越架空");
put(BookType.HMZC, "豪门总裁");
put(BookType.XDYQ, "现代言情");
put(BookType.GDYQ, "古代言情");
put(BookType.HXYQ, "幻想言情");
put(BookType.DMTR, "耽美同人");
}};
}

+ 49
- 0
app/src/main/java/com/yzx/webebook/utils/FileStack.java Parādīt failu

@@ -0,0 +1,49 @@
package com.yzx.webebook.utils;

import java.io.File;
import java.util.List;

/**
* Created by newbiechen on 17-5-28.
*/

public class FileStack {

private Node node = null;
private int count = 0;

public void push(FileSnapshot fileSnapshot){
if (fileSnapshot == null) return;
Node fileNode = new Node();
fileNode.fileSnapshot = fileSnapshot;
fileNode.next = node;
node = fileNode;
++count;
}

public FileSnapshot pop(){
Node fileNode = node;
if (fileNode == null) return null;
FileSnapshot fileSnapshot = fileNode.fileSnapshot;
node = fileNode.next;
--count;
return fileSnapshot;
}

public int getSize(){
return count;
}

//节点
public class Node {
FileSnapshot fileSnapshot;
Node next;
}

//文件快照
public static class FileSnapshot{
public String filePath;
public List<File> files;
public int scrollOffset;
}
}

+ 254
- 176
app/src/main/java/com/yzx/webebook/utils/FileUtils.java Parādīt failu

@@ -1,192 +1,270 @@
package com.yzx.webebook.utils;

import android.content.Context;
import android.os.Environment;

import com.wetao.note.WePoint;
import com.yzx.webebook.App;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;

/**
* Used 获取SD卡根目录、读写文件、移动、复制、删除文件、获取文件名、后缀名操作类(需要MyApplication中的getAppContext()方法)
*
* @author andrew 2015-9-2
* Created by newbiechen on 17-5-11.
*/
@SuppressWarnings("deprecation")

public class FileUtils {
private static final String TAG = "FileUtils";

/**
* read file to string list, a element of list is a line
*
* @param filePath
* @param charsetName The name of a supported {@link java.nio.charset.Charset </code>charset<code>}
* @return if file not exist, return null, else return content of file
* @throws RuntimeException if an error occurs while operator BufferedReader
*/
public static ArrayList<WePoint> readFileToList(String filePath, String charsetName) {
File file = new File(filePath);
ArrayList<WePoint> fileContent = new ArrayList<WePoint>();
if (file == null || !file.isFile()) {
return null;
}

BufferedReader reader = null;
try {
InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
reader = new BufferedReader(is);
String line = null;
while ((line = reader.readLine()) != null) {
WePoint wePoint = GsonHelper.fromJson(line, WePoint.class);
if (wePoint == null) continue;
fileContent.add(wePoint);
}
reader.close();
return fileContent;
} catch (IOException e) {
throw new RuntimeException("Flash test : +++++++++ IOException occurred. ", e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException("Flash test : +++++++++ IOException occurred. ", e);
}
}
}
}

public static ArrayList<WePoint> readAssetsFileToList(Context cxt, String filePath) {
ArrayList<WePoint> fileContent = new ArrayList<WePoint>();
BufferedReader reader = null;
try {
InputStreamReader is = new InputStreamReader(cxt.getAssets().open(filePath), "utf-8");
reader = new BufferedReader(is);
String line = null;
while ((line = reader.readLine()) != null) {
WePoint wePoint = GsonHelper.fromJson(line, WePoint.class);
if (wePoint == null) continue;
fileContent.add(wePoint);
}
reader.close();
return fileContent;
} catch (IOException e) {
throw new RuntimeException("Flash test : +++++++++ IOException occurred. ", e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException("Flash test : +++++++++ IOException occurred. ", e);
}
}
}

}

/**
* write file【写文件:字符串】
*
* @param filePath
* @param content
* @param append is append, if true, write to the end of file, else clear content of file and write into it
* @return return false if content is empty, true otherwise
* @throws RuntimeException if an error occurs while operator FileWriter
*/
public static boolean writeFile(String filePath, String content, boolean append) {
//字符串判空
if (StringUtils.isEmpty(content)) {
return false;
}

FileWriter fileWriter = null;
try {
makeDirs(filePath);
fileWriter = new FileWriter(filePath, append);
fileWriter.write(content);
fileWriter.close();
//updateGallery(filePath);//媒体库数据更新
return true;
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
}
}
}
}


/**
* write file
*
* @param file the file to be opened for writing.
* @param stream the input stream
* @param append if <code>true</code>, then bytes will be written to the end of the file rather than the beginning
* @return return true
* @throws RuntimeException if an error occurs while operator FileOutputStream
*/
public static boolean writeFile(File file, InputStream stream, boolean append) {
OutputStream o = null;
try {
makeDirs(file.getAbsolutePath());
if (!file.exists()) file.createNewFile();
o = new FileOutputStream(file, append);
byte data[] = new byte[1024];
int length = -1;
while ((length = stream.read(data)) != -1) {
o.write(data, 0, length);
}
o.flush();
//updateGallery(file.getAbsolutePath());//媒体库数据更新
return true;
} catch (FileNotFoundException e) {
throw new RuntimeException("FileNotFoundException occurred. ", e);
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
if (o != null) {
try {
o.close();
stream.close();
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
}
}
}
}

public static boolean makeDirs(String filePath) {
String folderName = getFolderName(filePath);
if (StringUtils.isEmpty(folderName)) {
return false;
}

File folder = new File(folderName);
return (folder.exists() && folder.isDirectory()) ? true : folder.mkdirs();
}

public static String getFolderName(String filePath) {

if (StringUtils.isEmpty(filePath)) {
return filePath;
}

int filePosi = filePath.lastIndexOf(File.separator);
return (filePosi == -1) ? "" : filePath.substring(0, filePosi);
}
//采用自己的格式去设置文件,防止文件被系统文件查询到
public static final String SUFFIX_NB = ".nb";
public static final String SUFFIX_TXT = ".txt";
public static final String SUFFIX_EPUB = ".epub";
public static final String SUFFIX_PDF = ".pdf";

//获取文件夹
public static File getFolder(String filePath){
File file = new File(filePath);
//如果文件夹不存在,就创建它
if (!file.exists()){
file.mkdirs();
}
return file;
}

//获取文件
public static synchronized File getFile(String filePath){
File file = new File(filePath);
try {
if (!file.exists()){
//创建父类文件夹
getFolder(file.getParent());
//创建文件
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
return file;
}

//获取Cache文件夹
public static String getCachePath(){
if (isSdCardExist()){
return App.Companion.getContext()
.getExternalCacheDir()
.getAbsolutePath();
}
else{
return App.Companion.getContext()
.getCacheDir()
.getAbsolutePath();
}
}

public static long getDirSize(File file){
//判断文件是否存在
if (file.exists()) {
//如果是目录则递归计算其内容的总大小
if (file.isDirectory()) {
File[] children = file.listFiles();
long size = 0;
for (File f : children)
size += getDirSize(f);
return size;
} else {
return file.length();
}
} else {
return 0;
}
}

public static String getFileSize(long size) {
if (size <= 0) return "0";
final String[] units = new String[]{"b", "kb", "M", "G", "T"};
//计算单位的,原理是利用lg,公式是 lg(1024^n) = nlg(1024),最后 nlg(1024)/lg(1024) = n。
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
//计算原理是,size/单位值。单位值指的是:比如说b = 1024,KB = 1024^2
return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
}

/**
* 本来是获取File的内容的。但是为了解决文本缩进、换行的问题
* 这个方法就是专门用来获取书籍的...
*
* 应该放在BookRepository中。。。
* @param file
* @return
*/
public static String getFileContent(File file){
Reader reader = null;
String str = null;
StringBuilder sb = new StringBuilder();
try {
reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
while ((str = br.readLine()) != null){
//过滤空语句
if (!str.equals("")){
//由于sb会自动过滤\n,所以需要加上去
sb.append(" "+str+"\n");
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.close(reader);
}
return sb.toString();
}

//判断是否挂载了SD卡
public static boolean isSdCardExist(){
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
return true;
}
return false;
}

//递归删除文件夹下的数据
public static synchronized void deleteFile(String filePath){
File file = new File(filePath);
if (!file.exists()) return;

if (file.isDirectory()){
File[] files = file.listFiles();
for (File subFile : files){
String path = subFile.getPath();
deleteFile(path);
}
}
//删除文件
file.delete();
}

//由于递归的耗时问题,取巧只遍历内部三层

//获取txt文件
public static List<File> getTxtFiles(String filePath,int layer){
List txtFiles = new ArrayList();
File file = new File(filePath);

//如果层级为 3,则直接返回
if (layer == 3){
return txtFiles;
}

//获取文件夹
File[] dirs = file.listFiles(
pathname -> {
if (pathname.isDirectory() && !pathname.getName().startsWith(".")) {
return true;
}
//获取txt文件
else if(pathname.getName().endsWith(".txt")){
txtFiles.add(pathname);
return false;
}
else{
return false;
}
}
);
//遍历文件夹
for (File dir : dirs){
//递归遍历txt文件
txtFiles.addAll(getTxtFiles(dir.getPath(),layer + 1));
}
return txtFiles;
}

//由于遍历比较耗时
public static Single<List<File>> getSDTxtFile(){
//外部存储卡路径
String rootPath = Environment.getExternalStorageDirectory().getPath();
return Single.create(new SingleOnSubscribe<List<File>>() {
@Override
public void subscribe(SingleEmitter<List<File>> e) throws Exception {
List<File> files = getTxtFiles(rootPath,0);
e.onSuccess(files);
}
});
}

//获取文件的编码格式
public static Charset getCharset(String fileName) {
BufferedInputStream bis = null;
Charset charset = Charset.GBK;
byte[] first3Bytes = new byte[3];
try {
boolean checked = false;
bis = new BufferedInputStream(new FileInputStream(fileName));
bis.mark(0);
int read = bis.read(first3Bytes, 0, 3);
if (read == -1)
return charset;
if (first3Bytes[0] == (byte) 0xEF
&& first3Bytes[1] == (byte) 0xBB
&& first3Bytes[2] == (byte) 0xBF) {
charset = Charset.UTF8;
checked = true;
}
/*
* 不支持 UTF16LE 和 UTF16BE
else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
charset = Charset.UTF16LE;
checked = true;
} else if (first3Bytes[0] == (byte) 0xFE
&& first3Bytes[1] == (byte) 0xFF) {
charset = Charset.UTF16BE;
checked = true;
} else */

bis.mark(0);
if (!checked) {
while ((read = bis.read()) != -1) {
if (read >= 0xF0)
break;
if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK
break;
if (0xC0 <= read && read <= 0xDF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
// (0x80 - 0xBF),也可能在GB编码内
continue;
else
break;
} else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错,但是几率较小
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
charset = Charset.UTF8;
break;
} else
break;
} else
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.close(bis);
}
return charset;
}
}

+ 21
- 0
app/src/main/java/com/yzx/webebook/utils/IOUtils.java Parādīt failu

@@ -0,0 +1,21 @@
package com.yzx.webebook.utils;

import java.io.Closeable;
import java.io.IOException;

/**
* Created by newbiechen on 17-5-11.
*/

public class IOUtils {

public static void close(Closeable closeable){
if (closeable == null) return;
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
//close error
}
}
}

+ 222
- 0
app/src/main/java/com/yzx/webebook/utils/LogUtils.java Parādīt failu

@@ -0,0 +1,222 @@
package com.yzx.webebook.utils;

import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.yzx.webebook.App;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
* Created by newbiechen on 17-4-27.
*/

public class LogUtils {
private static Boolean LOG_SWITCH = true; // 日志文件总开关
private static Boolean LOG_TO_FILE = false; // 日志写入文件开关
private static String LOG_TAG = "IReader"; // 默认的tag
private static char LOG_TYPE = 'v';// 输入日志类型,v代表输出所有信息,w则只输出警告...
private static int LOG_SAVE_DAYS = 7;// sd卡中日志文件的最多保存天数

private final static SimpleDateFormat LOG_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 日志的输出格式
private final static SimpleDateFormat FILE_SUFFIX = new SimpleDateFormat("yyyy-MM-dd");// 日志文件格式
private static String LOG_FILE_PATH; // 日志文件保存路径
private static String LOG_FILE_NAME;// 日志文件保存名称

public static void init(Context context) { // 在Application中初始化
LOG_FILE_PATH = Environment.getExternalStorageDirectory().getPath() + File.separator + App.Companion.getContext().getPackageName();
LOG_FILE_NAME = "Log";
}

/****************************
* Warn
*********************************/
public static void w(Object msg) {
w(LOG_TAG, msg);
}

public static void w(String tag, Object msg) {
w(tag, msg, null);
}

public static void w(String tag, Object msg, Throwable tr) {
if (msg == null) return;
log(tag, msg.toString(), tr, 'w');
}

/***************************
* Error
********************************/
public static void e(Object msg) {
e(LOG_TAG, msg);
}

public static void e(String tag, Object msg) {
e(tag, msg, null);
}

public static void e(String tag, Object msg, Throwable tr) {
if (msg == null) return;
log(tag, msg.toString(), tr, 'e');
}

/***************************
* Debug
********************************/
public static void d(Object msg) {
d(LOG_TAG, msg);
}

public static void d(String tag, Object msg) {// 调试信息
d(tag, msg, null);
}

public static void d(String tag, Object msg, Throwable tr) {
if (msg == null) return;
log(tag, msg.toString(), tr, 'd');
}

/****************************
* Info
*********************************/
public static void i(Object msg) {
i(LOG_TAG, msg);
}

public static void i(String tag, Object msg) {
i(tag, msg, null);
}

public static void i(String tag, Object msg, Throwable tr) {
if (msg == null) return;
log(tag, msg.toString(), tr, 'i');
}

/**************************
* Verbose
********************************/
public static void v(Object msg) {
v(LOG_TAG, msg);
}

public static void v(String tag, Object msg) {
v(tag, msg, null);
}

public static void v(String tag, Object msg, Throwable tr) {
if (msg == null) return;
log(tag, msg.toString(), tr, 'v');
}

/**
* 根据tag, msg和等级,输出日志
*
* @param tag
* @param msg
* @param level
*/
private static void log(String tag, String msg, Throwable tr, char level) {
if (tag == null || msg == null || tr == null) return;
if (LOG_SWITCH) {
if ('e' == level && ('e' == LOG_TYPE || 'v' == LOG_TYPE)) { // 输出错误信息
Log.e(tag, createMessage(msg), tr);
} else if ('w' == level && ('w' == LOG_TYPE || 'v' == LOG_TYPE)) {
Log.w(tag, createMessage(msg), tr);
} else if ('d' == level && ('d' == LOG_TYPE || 'v' == LOG_TYPE)) {
Log.d(tag, createMessage(msg), tr);
} else if ('i' == level && ('d' == LOG_TYPE || 'v' == LOG_TYPE)) {
Log.i(tag, createMessage(msg), tr);
} else {
Log.v(tag, createMessage(msg), tr);
}
if (LOG_TO_FILE)
log2File(String.valueOf(level), tag, msg + tr == null ? "" : "\n" + Log.getStackTraceString(tr));
}
}

private static String getFunctionName() {
StackTraceElement[] sts = Thread.currentThread().getStackTrace();
if (sts == null) {
return null;
}
for (StackTraceElement st : sts) {
if (st.isNativeMethod()) {
continue;
}
if (st.getClassName().equals(Thread.class.getName())) {
continue;
}
if (st.getFileName().equals("LogUtils.java")) {
continue;
}
return "[" + Thread.currentThread().getName() + "("
+ Thread.currentThread().getId() + "): " + st.getFileName()
+ ":" + st.getLineNumber() + "]";
}
return null;
}

private static String createMessage(String msg) {
String functionName = getFunctionName();
String message = (functionName == null ? msg
: (functionName + " - " + msg));
return message;
}

/**
* 打开日志文件并写入日志
*
* @return
**/
private synchronized static void log2File(String mylogtype, String tag, String text) {
Date nowtime = new Date();
String date = FILE_SUFFIX.format(nowtime);
String dateLogContent = LOG_FORMAT.format(nowtime) + ":" + mylogtype + ":" + tag + ":" + text; // 日志输出格式
File destDir = new File(LOG_FILE_PATH);
if (!destDir.exists()) {
destDir.mkdirs();
}
File file = new File(LOG_FILE_PATH, LOG_FILE_NAME + date);
try {
FileWriter filerWriter = new FileWriter(file, true);
BufferedWriter bufWriter = new BufferedWriter(filerWriter);
bufWriter.write(dateLogContent);
bufWriter.newLine();
bufWriter.close();
filerWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 删除指定的日志文件
*/
public static void delFile() {// 删除日志文件
String needDelFiel = FILE_SUFFIX.format(getDateBefore());
File file = new File(LOG_FILE_PATH, needDelFiel + LOG_FILE_NAME);
if (file.exists()) {
file.delete();
}
}

/**
* 得到LOG_SAVE_DAYS天前的日期
*
* @return
*/
private static Date getDateBefore() {
Date nowtime = new Date();
Calendar now = Calendar.getInstance();
now.setTime(nowtime);
now.set(Calendar.DATE, now.get(Calendar.DATE) - LOG_SAVE_DAYS);
return now.getTime();
}
}

+ 43
- 0
app/src/main/java/com/yzx/webebook/utils/MD5Utils.java Parādīt failu

@@ -0,0 +1,43 @@
package com.yzx.webebook.utils;

/**
* Created by newbiechen on 2018/1/1.
*/

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
*@Description: 将字符串转化为MD5
*/

public class MD5Utils {

public static String strToMd5By32(String str){
String reStr = null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytes = md5.digest(str.getBytes());
StringBuffer stringBuffer = new StringBuffer();
for (byte b : bytes){
int bt = b&0xff;
if (bt < 16){
stringBuffer.append(0);
}
stringBuffer.append(Integer.toHexString(bt));
}
reStr = stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return reStr;
}

public static String strToMd5By16(String str){
String reStr = strToMd5By32(str);
if (reStr != null){
reStr = reStr.substring(8, 24);
}
return reStr;
}
}

+ 63
- 0
app/src/main/java/com/yzx/webebook/utils/NetworkUtils.java Parādīt failu

@@ -0,0 +1,63 @@
package com.yzx.webebook.utils;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import com.yzx.webebook.App;

/**
* Created by newbiechen on 17-5-11.
*/

public class NetworkUtils {


/**
* 获取活动网络信息
* @return NetworkInfo
*/
public static NetworkInfo getNetworkInfo(){
ConnectivityManager cm = (ConnectivityManager) App.Companion
.getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);

return cm.getActiveNetworkInfo();
}

/**
* 网络是否可用
* @return
*/
public static boolean isAvailable(){
NetworkInfo info = getNetworkInfo();
return info != null && info.isAvailable();
}

/**
* 网络是否连接
* @return
*/
public static boolean isConnected(){
NetworkInfo info = getNetworkInfo();
return info != null && info.isConnected();
}

/**
* 判断wifi是否连接状态
* <p>需添加权限 {@code <uses-permission android:name="android.permission
* .ACCESS_NETWORK_STATE"/>}</p>
*
* @param context 上下文
* @return {@code true}: 连接<br>{@code false}: 未连接
*/
public static boolean isWifiConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm != null && cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
}



}

+ 34
- 0
app/src/main/java/com/yzx/webebook/utils/PermissionsChecker.java Parādīt failu

@@ -0,0 +1,34 @@
package com.yzx.webebook.utils;

import android.content.Context;
import android.content.pm.PackageManager;

import androidx.core.content.ContextCompat;

/**
* Created by newbiechen on 2017/10/8.
*/

public class PermissionsChecker {
private final Context mContext;

public PermissionsChecker(Context context) {
mContext = context.getApplicationContext();
}

// 判断权限集合
public boolean lacksPermissions(String... permissions) {
for (String permission : permissions) {
if (lacksPermission(permission)) {
return true;
}
}
return false;
}

// 判断是否缺少权限
private boolean lacksPermission(String permission) {
return ContextCompat.checkSelfPermission(mContext, permission) ==
PackageManager.PERMISSION_DENIED;
}
}

+ 60
- 0
app/src/main/java/com/yzx/webebook/utils/RxUtils.java Parādīt failu

@@ -0,0 +1,60 @@
package com.yzx.webebook.utils;



import com.yzx.webebook.model.bean.CommentBean;
import com.yzx.webebook.model.bean.DetailBean;

import java.util.List;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function3;
import io.reactivex.schedulers.Schedulers;

/**
* Created by newbiechen on 17-4-29.
*/

public class RxUtils {

public static <T> SingleSource<T> toSimpleSingle(Single<T> upstream){
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}

public static <T> ObservableSource<T> toSimpleSingle(Observable<T> upstream){
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}

public static <T,R> TwoTuple<T,R> twoTuple(T first,R second){
return new TwoTuple<T, R>(first, second);
}

public static <T> Single<DetailBean<T>> toCommentDetail(Single<T> detailSingle,
Single<List<CommentBean>> bestCommentsSingle,
Single<List<CommentBean>> commentsSingle){
return Single.zip(detailSingle, bestCommentsSingle, commentsSingle,
new Function3<T, List<CommentBean>, List<CommentBean>, DetailBean<T>>() {
@Override
public DetailBean<T> apply(T t, List<CommentBean> commentBeen,
List<CommentBean> commentBeen2) throws Exception {
return new DetailBean<T>(t,commentBeen,commentBeen2);
}
});
}

public static class TwoTuple<A, B> {
public final A first;
public final B second;

public TwoTuple(A a, B b) {
this.first = a;
this.second = b;
}
}
}

+ 127
- 0
app/src/main/java/com/yzx/webebook/utils/ScreenUtils.java Parādīt failu

@@ -0,0 +1,127 @@
package com.yzx.webebook.utils;

import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;

import com.yzx.webebook.App;

import java.lang.reflect.Method;

/**
* Created by newbiechen on 17-5-1.
*/

public class ScreenUtils {

public static int dpToPx(int dp) {
DisplayMetrics metrics = getDisplayMetrics();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
}

public static int pxToDp(int px) {
DisplayMetrics metrics = getDisplayMetrics();
return (int) (px / metrics.density);
}

public static int spToPx(int sp) {
DisplayMetrics metrics = getDisplayMetrics();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, metrics);
}

public static int pxToSp(int px) {
DisplayMetrics metrics = getDisplayMetrics();
return (int) (px / metrics.scaledDensity);
}

/**
* 获取手机显示App区域的大小(头部导航栏+ActionBar+根布局),不包括虚拟按钮
*
* @return
*/
public static int[] getAppSize() {
int[] size = new int[2];
DisplayMetrics metrics = getDisplayMetrics();
size[0] = metrics.widthPixels;
size[1] = metrics.heightPixels;
return size;
}

/**
* 获取整个手机屏幕的大小(包括虚拟按钮)
* 必须在onWindowFocus方法之后使用
*
* @param activity
* @return
*/
public static int[] getScreenSize(AppCompatActivity activity) {
int[] size = new int[2];
View decorView = activity.getWindow().getDecorView();
size[0] = decorView.getWidth();
size[1] = decorView.getHeight();
return size;
}

/**
* 获取导航栏的高度
*
* @return
*/
public static int getStatusBarHeight() {
Resources resources = App.Companion.getContext().getResources();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
return resources.getDimensionPixelSize(resourceId);
}

/**
* 获取虚拟按键的高度
*
* @return
*/
public static int getNavigationBarHeight() {
int navigationBarHeight = 0;
Resources rs = App.Companion.getContext().getResources();
int id = rs.getIdentifier("navigation_bar_height", "dimen", "android");
if (id > 0 && hasNavigationBar()) {
navigationBarHeight = rs.getDimensionPixelSize(id);
}
return navigationBarHeight;
}

/**
* 是否存在虚拟按键
*
* @return
*/
private static boolean hasNavigationBar() {
boolean hasNavigationBar = false;
Resources rs = App.Companion.getContext().getResources();
int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
hasNavigationBar = rs.getBoolean(id);
}
try {
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method m = systemPropertiesClass.getMethod("get", String.class);
String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
hasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
hasNavigationBar = true;
}
} catch (Exception e) {
}
return hasNavigationBar;
}

public static DisplayMetrics getDisplayMetrics() {
DisplayMetrics metrics = App.Companion
.getContext()
.getResources()
.getDisplayMetrics();
return metrics;
}
}

+ 61
- 0
app/src/main/java/com/yzx/webebook/utils/SharedPreUtils.java Parādīt failu

@@ -0,0 +1,61 @@
package com.yzx.webebook.utils;

import android.content.Context;
import android.content.SharedPreferences;

import com.yzx.webebook.App;

/**
* Created by newbiechen on 17-4-16.
*/

public class SharedPreUtils {
private static final String SHARED_NAME = "IReader_pref";
private static SharedPreUtils sInstance;
private SharedPreferences sharedReadable;
private SharedPreferences.Editor sharedWritable;

private SharedPreUtils(){
sharedReadable = App.Companion.getContext()
.getSharedPreferences(SHARED_NAME, Context.MODE_MULTI_PROCESS);
sharedWritable = sharedReadable.edit();
}

public static SharedPreUtils getInstance(){
if(sInstance == null){
synchronized (SharedPreUtils.class){
if (sInstance == null){
sInstance = new SharedPreUtils();
}
}
}
return sInstance;
}

public String getString(String key){
return sharedReadable.getString(key,"");
}

public void putString(String key,String value){
sharedWritable.putString(key,value);
sharedWritable.commit();
}

public void putInt(String key,int value){
sharedWritable.putInt(key, value);
sharedWritable.commit();
}

public void putBoolean(String key,boolean value){
sharedWritable.putBoolean(key, value);
sharedWritable.commit();
}

public int getInt(String key,int def){
return sharedReadable.getInt(key, def);
}

public boolean getBoolean(String key,boolean def){
return sharedReadable.getBoolean(key, def);
}
}

+ 181
- 323
app/src/main/java/com/yzx/webebook/utils/StringUtils.java Parādīt failu

@@ -1,332 +1,190 @@
package com.yzx.webebook.utils;

import android.text.TextUtils;
import android.content.Context;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.StringRes;

/**
* String Utils
*
* @author andrew 2015-9-2
*/
public class StringUtils {

private StringUtils() {
throw new AssertionError();
}

/**
* is null or its length is 0 or it is made by space【字符串为空、长度为0、一个空格】
*
* <pre>
* isBlank(null) = true;
* isBlank(&quot;&quot;) = true;
* isBlank(&quot; &quot;) = true;
* isBlank(&quot;a&quot;) = false;
* isBlank(&quot;a &quot;) = false;
* isBlank(&quot; a&quot;) = false;
* isBlank(&quot;a b&quot;) = false;
* </pre>
*
* @param str
* @return if string is null or its size is 0 or it is made by space, return true, else return false.
*/
public static boolean isBlank(String str) {
return (str == null || str.trim().length() == 0);
}

/**
* is null or its length is 0【字符串为空、长度为0】
*
* <pre>
* isEmpty(null) = true;
* isEmpty(&quot;&quot;) = true;
* isEmpty(&quot; &quot;) = false;
* </pre>
*
* @param str
* @return if string is null or its size is 0, return true, else return false.
*/
public static boolean isEmpty(CharSequence str) {
return (str == null || str.length() == 0);
}

public static boolean isEqual(String paramString1, String paramString2) {
return paramString1.equals(paramString2);
}

/**
* get length of CharSequence【字符串的长度】
*
* <pre>
* length(null) = 0;
* length(\"\") = 0;
* length(\"abc\") = 3;
* </pre>
*
* @param str
* @return if str is null or empty, return 0, else return {@link CharSequence#length()}.
*/
public static int length(CharSequence str) {
return str == null ? 0 : str.length();
}

/**
* null Object to empty string
*
* <pre>
* nullStrToEmpty(null) = &quot;&quot;;
* nullStrToEmpty(&quot;&quot;) = &quot;&quot;;
* nullStrToEmpty(&quot;aa&quot;) = &quot;aa&quot;;
* </pre>
*
* @param str
* @return
*/
public static String nullStrToEmpty(Object str) {
return (str == null ? "" : (str instanceof String ? (String)str : str.toString()));
}

/**
* capitalize first letter
*
* <pre>
* capitalizeFirstLetter(null) = null;
* capitalizeFirstLetter("") = "";
* capitalizeFirstLetter("2ab") = "2ab"
* capitalizeFirstLetter("a") = "A"
* capitalizeFirstLetter("ab") = "Ab"
* capitalizeFirstLetter("Abc") = "Abc"
* </pre>
*
* @param str
* @return
*/
public static String capitalizeFirstLetter(String str) {
if (isEmpty(str)) {
return str;
}

char c = str.charAt(0);
return (!Character.isLetter(c) || Character.isUpperCase(c)) ? str : new StringBuilder(str.length())
.append(Character.toUpperCase(c)).append(str.substring(1)).toString();
}

/**
* encoded in utf-8
*
* <pre>
* utf8Encode(null) = null
* utf8Encode("") = "";
* utf8Encode("aa") = "aa";
* utf8Encode("啊啊啊啊") = "%E5%95%8A%E5%95%8A%E5%95%8A%E5%95%8A";
* </pre>
*
* @param str
* @return
* @throws UnsupportedEncodingException if an error occurs
*/
public static String utf8Encode(String str) {
if (!isEmpty(str) && str.getBytes().length != str.length()) {
try {
return URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UnsupportedEncodingException occurred. ", e);
}
}
return str;
}
import com.yzx.webebook.App;

/**
* encoded in utf-8, if exception, return defultReturn
*
* @param str
* @param defultReturn
* @return
*/
public static String utf8Encode(String str, String defultReturn) {
if (!isEmpty(str) && str.getBytes().length != str.length()) {
try {
return URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
return defultReturn;
}
}
return str;
}
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
* get innerHtml from href
*
* <pre>
* getHrefInnerHtml(null) = ""
* getHrefInnerHtml("") = ""
* getHrefInnerHtml("mp3") = "mp3";
* getHrefInnerHtml("&lt;a innerHtml&lt;/a&gt;") = "&lt;a innerHtml&lt;/a&gt;";
* getHrefInnerHtml("&lt;a&gt;innerHtml&lt;/a&gt;") = "innerHtml";
* getHrefInnerHtml("&lt;a&lt;a&gt;innerHtml&lt;/a&gt;") = "innerHtml";
* getHrefInnerHtml("&lt;a href="baidu.com"&gt;innerHtml&lt;/a&gt;") = "innerHtml";
* getHrefInnerHtml("&lt;a href="baidu.com" title="baidu"&gt;innerHtml&lt;/a&gt;") = "innerHtml";
* getHrefInnerHtml(" &lt;a&gt;innerHtml&lt;/a&gt; ") = "innerHtml";
* getHrefInnerHtml("&lt;a&gt;innerHtml&lt;/a&gt;&lt;/a&gt;") = "innerHtml";
* getHrefInnerHtml("jack&lt;a&gt;innerHtml&lt;/a&gt;&lt;/a&gt;") = "innerHtml";
* getHrefInnerHtml("&lt;a&gt;innerHtml1&lt;/a&gt;&lt;a&gt;innerHtml2&lt;/a&gt;") = "innerHtml2";
* </pre>
*
* @param href
* @return <ul>
* <li>if href is null, return ""</li>
* <li>if not match regx, return source</li>
* <li>return the last string that match regx</li>
* </ul>
*/
public static String getHrefInnerHtml(String href) {
if (isEmpty(href)) {
return "";
}
//import com.zqc.opencc.android.lib.ChineseConverter;
//import com.zqc.opencc.android.lib.ConversionType;

String hrefReg = ".*<[\\s]*a[\\s]*.*>(.+?)<[\\s]*/a[\\s]*>.*";
Pattern hrefPattern = Pattern.compile(hrefReg, Pattern.CASE_INSENSITIVE);
Matcher hrefMatcher = hrefPattern.matcher(href);
if (hrefMatcher.matches()) {
return hrefMatcher.group(1);
}
return href;
}

/**
* process special char in html
*
* <pre>
* htmlEscapeCharsToString(null) = null;
* htmlEscapeCharsToString("") = "";
* htmlEscapeCharsToString("mp3") = "mp3";
* htmlEscapeCharsToString("mp3&lt;") = "mp3<";
* htmlEscapeCharsToString("mp3&gt;") = "mp3\>";
* htmlEscapeCharsToString("mp3&amp;mp4") = "mp3&mp4";
* htmlEscapeCharsToString("mp3&quot;mp4") = "mp3\"mp4";
* htmlEscapeCharsToString("mp3&lt;&gt;&amp;&quot;mp4") = "mp3\<\>&\"mp4";
* </pre>
*
* @param source
* @return
*/
public static String htmlEscapeCharsToString(String source) {
return StringUtils.isEmpty(source) ? source : source.replaceAll("&lt;", "<").replaceAll("&gt;", ">")
.replaceAll("&amp;", "&").replaceAll("&quot;", "\"");
}

/**
* transform half width char to full width char
*
* <pre>
* fullWidthToHalfWidth(null) = null;
* fullWidthToHalfWidth("") = "";
* fullWidthToHalfWidth(new String(new char[] {12288})) = " ";
* fullWidthToHalfWidth("!"#$%&) = "!\"#$%&";
* </pre>
*
* @param s
* @return
*/
public static String fullWidthToHalfWidth(String s) {
if (isEmpty(s)) {
return s;
}

char[] source = s.toCharArray();
for (int i = 0; i < source.length; i++) {
if (source[i] == 12288) {
source[i] = ' ';
// } else if (source[i] == 12290) {
// source[i] = '.';
} else if (source[i] >= 65281 && source[i] <= 65374) {
source[i] = (char)(source[i] - 65248);
} else {
source[i] = source[i];
}
}
return new String(source);
}

/**
* transform full width char to half width char
*
* <pre>
* halfWidthToFullWidth(null) = null;
* halfWidthToFullWidth("") = "";
* halfWidthToFullWidth(" ") = new String(new char[] {12288});
* halfWidthToFullWidth("!\"#$%&) = "!"#$%&";
* </pre>
*
* @param s
* @return
*/
public static String halfWidthToFullWidth(String s) {
if (isEmpty(s)) {
return s;
}

char[] source = s.toCharArray();
for (int i = 0; i < source.length; i++) {
if (source[i] == ' ') {
source[i] = (char)12288;
// } else if (source[i] == '.') {
// source[i] = (char)12290;
} else if (source[i] >= 33 && source[i] <= 126) {
source[i] = (char)(source[i] + 65248);
} else {
source[i] = source[i];
}
}
return new String(source);
}

private static String bytesToHexString(byte[] paramArrayOfByte) {

StringBuilder localStringBuilder = new StringBuilder();
for (int i = 0; i < paramArrayOfByte.length; i++)
{
String str = Integer.toHexString(0xFF & paramArrayOfByte[i]);
if (str.length() == 1)
localStringBuilder.append('0');
localStringBuilder.append(str);
}
return localStringBuilder.toString();
}

public static String hashKey(String paramString) {
String localObject = "";
if (!TextUtils.isEmpty(paramString));
try
{
MessageDigest localMessageDigest = MessageDigest.getInstance("MD5");
localMessageDigest.update(paramString.getBytes());
String str = bytesToHexString(localMessageDigest.digest());
localObject = str;
return localObject;
}
catch (NoSuchAlgorithmException localNoSuchAlgorithmException)
{
}
return String.valueOf(paramString.hashCode());
}

public static String stripLeadingSlash(String paramString) {
/**
* Created by newbiechen on 17-4-22.
* 对文字操作的工具类
*/

if (TextUtils.isEmpty(paramString))
{
return paramString;
}
int i = 0;
while (paramString.charAt(i) == '/'){
i ++;
}
return paramString.substring(i);
}
public class StringUtils {
private static final String TAG = "StringUtils";
private static final int HOUR_OF_DAY = 24;
private static final int DAY_OF_YESTERDAY = 2;
private static final int TIME_UNIT = 60;

//将时间转换成日期
public static String dateConvert(long time,String pattern){
Date date = new Date(time);
SimpleDateFormat format = new SimpleDateFormat(pattern);
return format.format(date);
}

//将日期转换成昨天、今天、明天
public static String dateConvert(String source,String pattern){

DateFormat format = new SimpleDateFormat(pattern);
Calendar calendar = Calendar.getInstance();
try {
Date date = format.parse(source);
long curTime = calendar.getTimeInMillis();
calendar.setTime(date);
//将MISC 转换成 sec
long difSec = Math.abs((curTime - date.getTime())/1000);
long difMin = difSec/60;
long difHour = difMin/60;
long difDate = difHour/60;
int oldHour = calendar.get(Calendar.HOUR);
//如果没有时间
if (oldHour == 0){
//比日期:昨天今天和明天
if (difDate == 0){
return "今天";
}
else if (difDate < DAY_OF_YESTERDAY){
return "昨天";
}
else {
DateFormat convertFormat = new SimpleDateFormat("yyyy-MM-dd");
String value = convertFormat.format(date);
return value;
}
}

if (difSec < TIME_UNIT){
return difSec+"秒前";
}
else if (difMin < TIME_UNIT){
return difMin+"分钟前";
}
else if (difHour < HOUR_OF_DAY){
return difHour+"小时前";
}
else if (difDate < DAY_OF_YESTERDAY){
return "昨天";
}
else {
DateFormat convertFormat = new SimpleDateFormat("yyyy-MM-dd");
String value = convertFormat.format(date);
return value;
}
} catch (ParseException e) {
e.printStackTrace();
}
return "";
}

public static String toFirstCapital(String str){
return str.substring(0,1).toUpperCase()+str.substring(1);
}

public static String getString(@StringRes int id){
return App.Companion.getContext().getResources().getString(id);
}

public static String getString(@StringRes int id, Object... formatArgs){
return App.Companion.getContext().getResources().getString(id,formatArgs);
}

/**
* 将文本中的半角字符,转换成全角字符
* @param input
* @return
*/
public static String halfToFull(String input)
{
char[] c = input.toCharArray();
for (int i = 0; i< c.length; i++)
{
if (c[i] == 32) //半角空格
{
c[i] = (char) 12288;
continue;
}
//根据实际情况,过滤不需要转换的符号
//if (c[i] == 46) //半角点号,不转换
// continue;

if (c[i]> 32 && c[i]< 127) //其他符号都转换为全角
c[i] = (char) (c[i] + 65248);
}
return new String(c);
}

//功能:字符串全角转换为半角
public static String fullToHalf(String input)
{
char[] c = input.toCharArray();
for (int i = 0; i< c.length; i++)
{
if (c[i] == 12288) //全角空格
{
c[i] = (char) 32;
continue;
}

if (c[i]> 65280&& c[i]< 65375)
c[i] = (char) (c[i] - 65248);
}
return new String(c);
}

//繁簡轉換
public static String convertCC(String input, Context context)
{
// ConversionType currentConversionType = ConversionType.S2TWP;
// int convertType = SharedPreUtils.getInstance().getInt(SHARED_READ_CONVERT_TYPE, 0);
//
// if (input.length() == 0)
// return "";
//
// switch (convertType) {
// case 1:
// currentConversionType = ConversionType.TW2SP;
// break;
// case 2:
// currentConversionType = ConversionType.S2HK;
// break;
// case 3:
// currentConversionType = ConversionType.S2T;
// break;
// case 4:
// currentConversionType = ConversionType.S2TW;
// break;
// case 5:
// currentConversionType = ConversionType.S2TWP;
// break;
// case 6:
// currentConversionType = ConversionType.T2HK;
// break;
// case 7:
// currentConversionType = ConversionType.T2S;
// break;
// case 8:
// currentConversionType = ConversionType.T2TW;
// break;
// case 9:
// currentConversionType = ConversionType.TW2S;
// break;
// case 10:
// currentConversionType = ConversionType.HK2S;
// break;
// }

// return (convertType != 0)?ChineseConverter.convert(input, currentConversionType, context):input;
return input;
}
}

+ 162
- 0
app/src/main/java/com/yzx/webebook/utils/SystemBarUtils.java Parādīt failu

@@ -0,0 +1,162 @@
package com.yzx.webebook.utils;

import android.app.Activity;
import android.os.Build;
import android.view.View;
import android.view.WindowManager;

/**
* Created by newbiechen on 17-5-16.
* 基于 Android 4.4
*
* 主要参数说明:
*
* SYSTEM_UI_FLAG_FULLSCREEN : 隐藏StatusBar
* SYSTEM_UI_FLAG_HIDE_NAVIGATION : 隐藏NavigationBar
* SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN: 视图扩展到StatusBar的位置,并且StatusBar不消失。
* 这里需要一些处理,一般是将StatusBar设置为全透明或者半透明。之后还需要使用fitSystemWindows=防止视图扩展到Status
* Bar上面(会在StatusBar上加一层View,该View可被移动)
* SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION: 视图扩展到NavigationBar的位置
* SYSTEM_UI_FLAG_LAYOUT_STABLE:稳定效果
* SYSTEM_UI_FLAG_IMMERSIVE_STICKY:保证点击任意位置不会退出
*
* 可设置特效说明:
* 1. 全屏特效
* 2. 全屏点击不退出特效
* 3. 注意在19 <=sdk <=21 时候,必须通过Window设置透明栏
*/

public class SystemBarUtils {

private static final int UNSTABLE_STATUS = View.SYSTEM_UI_FLAG_FULLSCREEN;
private static final int UNSTABLE_NAV = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
private static final int STABLE_STATUS = View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
private static final int STABLE_NAV = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
private static final int EXPAND_STATUS = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
private static final int EXPAND_NAV = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;


//设置隐藏StatusBar(点击任意地方会恢复)
public static void hideUnStableStatusBar(Activity activity){
//App全屏,隐藏StatusBar
setFlag(activity,UNSTABLE_STATUS);
}

public static void showUnStableStatusBar(Activity activity){
clearFlag(activity,UNSTABLE_STATUS);
}

//隐藏NavigationBar(点击任意地方会恢复)
public static void hideUnStableNavBar(Activity activity){
setFlag(activity,UNSTABLE_NAV);
}

public static void showUnStableNavBar(Activity activity){
clearFlag(activity,UNSTABLE_NAV);
}

public static void hideStableStatusBar(Activity activity){
//App全屏,隐藏StatusBar
setFlag(activity,STABLE_STATUS);
}

public static void showStableStatusBar(Activity activity){
clearFlag(activity,STABLE_STATUS);
}

public static void hideStableNavBar(Activity activity){
//App全屏,隐藏StatusBar
setFlag(activity,STABLE_NAV);
}

public static void showStableNavBar(Activity activity){
clearFlag(activity,STABLE_NAV);
}

/**
* 视图扩充到StatusBar
*/
public static void expandStatusBar(Activity activity){
setFlag(activity, EXPAND_STATUS);
}

/**
* 视图扩充到NavBar
* @param activity
*/
public static void expandNavBar(Activity activity){
setFlag(activity, EXPAND_NAV);
}

public static void transparentStatusBar(Activity activity){
if (Build.VERSION.SDK_INT >= 21){
expandStatusBar(activity);
activity.getWindow()
.setStatusBarColor(activity.getResources().getColor(android.R.color.transparent));
}
else if (Build.VERSION.SDK_INT >= 19){
WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
attrs.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | attrs.flags);
activity.getWindow().setAttributes(attrs);
}
}

public static void transparentNavBar(Activity activity){
if (Build.VERSION.SDK_INT >= 21){
expandNavBar(activity);
//下面这个方法在sdk:21以上才有
activity.getWindow()
.setNavigationBarColor(activity.getResources().getColor(android.R.color.transparent));
}
}

public static void setFlag(Activity activity, int flag){
if (Build.VERSION.SDK_INT >= 19){
View decorView = activity.getWindow().getDecorView();
int option = decorView.getSystemUiVisibility() | flag;
decorView.setSystemUiVisibility(option);
}
}

//取消flag
public static void clearFlag(Activity activity, int flag){
if (Build.VERSION.SDK_INT >= 19){
View decorView = activity.getWindow().getDecorView();
int option = decorView.getSystemUiVisibility() & (~flag);
decorView.setSystemUiVisibility(option);
}
}

public static void setToggleFlag(Activity activity, int option){
if (Build.VERSION.SDK_INT >= 19){
if (isFlagUsed(activity,option)){
clearFlag(activity,option);
}
else {
setFlag(activity,option);
}
}
}

/**
* @param activity
* @return flag是否已被使用
*/
public static boolean isFlagUsed(Activity activity, int flag) {
int currentFlag = activity.getWindow().getDecorView().getSystemUiVisibility();
if((currentFlag & flag)
== flag) {
return true;
}else {
return false;
}
}
}

+ 17
- 0
app/src/main/java/com/yzx/webebook/utils/ToastUtils.java Parādīt failu

@@ -0,0 +1,17 @@
package com.yzx.webebook.utils;

import android.widget.Toast;

import com.yzx.webebook.App;


/**
* Created by newbiechen on 17-5-11.
*/

public class ToastUtils {

public static void show(String msg){
Toast.makeText(App.Companion.getContext(), msg, Toast.LENGTH_SHORT).show();
}
}

+ 31
- 0
app/src/main/java/com/yzx/webebook/utils/media/LoaderCreator.java Parādīt failu

@@ -0,0 +1,31 @@
package com.yzx.webebook.utils.media;

import android.content.Context;
import android.os.Bundle;

import androidx.loader.content.CursorLoader;

/**
* Created by newbiechen on 2018/1/14.
*/

public class LoaderCreator {
public static final int ALL_BOOK_FILE = 1;

public static CursorLoader create(Context context, int id, Bundle bundle) {
LocalFileLoader loader = null;
switch (id){
case ALL_BOOK_FILE:
loader = new LocalFileLoader(context);
break;
default:
loader = null;
break;
}
if (loader != null) {
return loader;
}

throw new IllegalArgumentException("The id of Loader is invalid!");
}
}

+ 137
- 0
app/src/main/java/com/yzx/webebook/utils/media/LocalFileLoader.java Parādīt failu

@@ -0,0 +1,137 @@
package com.yzx.webebook.utils.media;

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.loader.content.CursorLoader;

import java.io.File;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.List;

/**
* Created by newbiechen on 2018/1/14.
*/

public class LocalFileLoader extends CursorLoader {
private static final String TAG = "LocalFileLoader";

private static final Uri FILE_URI = Uri.parse("content://media/external/file");
private static final String SELECTION = MediaStore.Files.FileColumns.DATA + " like ?";
private static final String SEARCH_TYPE = "%.txt";
private static final String SORT_ORDER = MediaStore.Files.FileColumns.DISPLAY_NAME + " DESC";
private static final String[] FILE_PROJECTION = {
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.DISPLAY_NAME
};

public LocalFileLoader(Context context) {
super(context);
initLoader();
}

/**
* 为 Cursor 设置默认参数
*/
private void initLoader() {
setUri(FILE_URI);
setProjection(FILE_PROJECTION);
setSelection(SELECTION);
setSelectionArgs(new String[]{SEARCH_TYPE});
setSortOrder(SORT_ORDER);
}

public void parseData(Cursor cursor, final MediaStoreHelper.MediaResultCallback resultCallback) {
List<File> files = new ArrayList<>();
// 判断是否存在数据
if (cursor == null) {
// TODO:当媒体库没有数据的时候,需要做相应的处理
// 暂时直接返回空数据
resultCallback.onResultCallback(files);
return;
}
// 重复使用Loader时,需要重置cursor的position;
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
String path;

path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
// 路径无效
if (TextUtils.isEmpty(path)) {
continue;
} else {
File file = new File(path);
if (file.isDirectory() || !file.exists()){
continue;
}
else {
files.add(file);
}
}
}
if (resultCallback != null) {
resultCallback.onResultCallback(files);
}
}

/**
* 从Cursor中读取对应columnName的值
*
* @param cursor
* @param columnName
* @param defaultValue
* @return 当columnName无效时返回默认值;
*/
protected Object getValueFromCursor(@NonNull Cursor cursor, String columnName, Object defaultValue) {
try {
int index = cursor.getColumnIndexOrThrow(columnName);
int type = cursor.getType(index);
switch (type) {
case Cursor.FIELD_TYPE_STRING:
// TO SOLVE:某些手机的数据库将数值类型存为String类型
String value = cursor.getString(index);
try {
if (defaultValue instanceof String) {
return value;
} else if (defaultValue instanceof Long) {
return Long.valueOf(value);
} else if (defaultValue instanceof Integer) {
return Integer.valueOf(value);
} else if (defaultValue instanceof Double) {
return Double.valueOf(value);
} else if (defaultValue instanceof Float) {
return Float.valueOf(value);
}
} catch (NumberFormatException e) {
return defaultValue;
}
case Cursor.FIELD_TYPE_INTEGER:
if (defaultValue instanceof Long) {
return cursor.getLong(index);
} else if (defaultValue instanceof Integer) {
return cursor.getInt(index);
}
case Cursor.FIELD_TYPE_FLOAT:
if (defaultValue instanceof Float) {
return cursor.getFloat(index);
} else if (defaultValue instanceof Double) {
return cursor.getDouble(index);
}
case Cursor.FIELD_TYPE_BLOB:
if (defaultValue instanceof Blob) {
return cursor.getBlob(index);
}
case Cursor.FIELD_TYPE_NULL:
default:
return defaultValue;
}
} catch (IllegalArgumentException e) {
return defaultValue;
}
}
}

+ 67
- 0
app/src/main/java/com/yzx/webebook/utils/media/MediaStoreHelper.java Parādīt failu

@@ -0,0 +1,67 @@
package com.yzx.webebook.utils.media;

import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;

import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.List;

/**
* Created by newbiechen on 2018/1/14.
* 获取媒体库的数据。
*/

public class MediaStoreHelper {

/**
* 获取媒体库中所有的书籍文件
* <p>
* 暂时只支持 TXT
*
* @param activity
* @param resultCallback
*/
public static void getAllBookFile(FragmentActivity activity, MediaResultCallback resultCallback) {
// 将文件的获取处理交给 LoaderManager。
activity.getSupportLoaderManager()
.initLoader(LoaderCreator.ALL_BOOK_FILE, null, new MediaLoaderCallbacks(activity, resultCallback));
}

public interface MediaResultCallback {
void onResultCallback(List<File> files);
}

/**
* Loader 回调处理
*/
static class MediaLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
protected WeakReference<Context> mContext;
protected MediaResultCallback mResultCallback;

public MediaLoaderCallbacks(Context context, MediaResultCallback resultCallback) {
mContext = new WeakReference<>(context);
mResultCallback = resultCallback;
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return LoaderCreator.create(mContext.get(), id, args);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
LocalFileLoader localFileLoader = (LocalFileLoader) loader;
localFileLoader.parseData(data, mResultCallback);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}
}

+ 333
- 0
app/src/main/java/com/yzx/webebook/widget/ReadSettingDialog.java Parādīt failu

@@ -0,0 +1,333 @@
package com.yzx.webebook.widget;

import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.TextView;


import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.yzx.webebook.R;
import com.yzx.webebook.adapter.PageStyleAdapter;
import com.yzx.webebook.model.local.ReadSettingManager;
import com.yzx.webebook.utils.BrightnessUtils;
import com.yzx.webebook.utils.ScreenUtils;
import com.yzx.webebook.widget.page.PageLoader;
import com.yzx.webebook.widget.page.PageMode;
import com.yzx.webebook.widget.page.PageStyle;

import java.util.Arrays;


/**
* Created by newbiechen on 17-5-18.
*/

public class ReadSettingDialog extends Dialog {
private static final String TAG = "ReadSettingDialog";
private static final int DEFAULT_TEXT_SIZE = 16;

ImageView mIvBrightnessMinus;
SeekBar mSbBrightness;
ImageView mIvBrightnessPlus;
CheckBox mCbBrightnessAuto;
TextView mTvFontMinus;
TextView mTvFont;
TextView mTvFontPlus;
CheckBox mCbFontDefault;
RadioGroup mRgPageMode;

RadioButton mRbSimulation;
RadioButton mRbCover;
RadioButton mRbSlide;
RadioButton mRbScroll;
RadioButton mRbNone;
RecyclerView mRvBg;
TextView mTvMore;
/************************************/
private PageStyleAdapter mPageStyleAdapter;
private ReadSettingManager mSettingManager;
private PageLoader mPageLoader;
private Activity mActivity;

private PageMode mPageMode;
private PageStyle mPageStyle;

private int mBrightness;
private int mTextSize;

private boolean isBrightnessAuto;
private boolean isTextDefault;


public ReadSettingDialog(@NonNull Activity activity, PageLoader mPageLoader) {
super(activity, R.style.ReadSettingDialog);
mActivity = activity;
this.mPageLoader = mPageLoader;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_read_setting);
initView();
setUpWindow();
initData();
initWidget();
initClick();
}

private void initView() {
mIvBrightnessMinus = findViewById(R.id.read_setting_iv_brightness_minus);
mSbBrightness = findViewById(R.id.read_setting_sb_brightness);
mIvBrightnessPlus = findViewById(R.id.read_setting_iv_brightness_plus);
mCbBrightnessAuto = findViewById(R.id.read_setting_cb_brightness_auto);
mTvFontMinus = findViewById(R.id.read_setting_tv_font_minus);
mTvFont = findViewById(R.id.read_setting_tv_font);
mTvFontPlus = findViewById(R.id.read_setting_tv_font_plus);
mCbFontDefault = findViewById(R.id.read_setting_cb_font_default);
mRgPageMode = findViewById(R.id.read_setting_rg_page_mode);
mRbSimulation = findViewById(R.id.read_setting_rb_simulation);
mRbCover = findViewById(R.id.read_setting_rb_cover);
mRbSlide = findViewById(R.id.read_setting_rb_slide);
mRbScroll = findViewById(R.id.read_setting_rb_scroll);
mRbNone = findViewById(R.id.read_setting_rb_none);
mRvBg = findViewById(R.id.read_setting_rv_bg);
mTvMore = findViewById(R.id.read_setting_tv_more);
}

//设置Dialog显示的位置
private void setUpWindow() {
Window window = getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.BOTTOM;
window.setAttributes(lp);
}

private void initData() {
mSettingManager = ReadSettingManager.getInstance();

isBrightnessAuto = mSettingManager.isBrightnessAuto();
mBrightness = mSettingManager.getBrightness();
mTextSize = mSettingManager.getTextSize();
isTextDefault = mSettingManager.isDefaultTextSize();
mPageMode = mSettingManager.getPageMode();
mPageStyle = mSettingManager.getPageStyle();
}

private void initWidget() {
mSbBrightness.setProgress(mBrightness);
mTvFont.setText(mTextSize + "");
mCbBrightnessAuto.setChecked(isBrightnessAuto);
mCbFontDefault.setChecked(isTextDefault);
initPageMode();
//RecyclerView
setUpAdapter();
}

private void setUpAdapter() {
Drawable[] drawables = {
getDrawable(R.color.nb_read_bg_1)
, getDrawable(R.color.nb_read_bg_2)
, getDrawable(R.color.nb_read_bg_3)
, getDrawable(R.color.nb_read_bg_4)
, getDrawable(R.color.nb_read_bg_5)};

mPageStyleAdapter = new PageStyleAdapter();
mRvBg.setLayoutManager(new GridLayoutManager(getContext(), 5));
mRvBg.setAdapter(mPageStyleAdapter);
mPageStyleAdapter.refreshItems(Arrays.asList(drawables));

mPageStyleAdapter.setPageStyleChecked(mPageStyle);

}

private void initPageMode() {
switch (mPageMode) {
case SIMULATION:
mRbSimulation.setChecked(true);
break;
case COVER:
mRbCover.setChecked(true);
break;
case SLIDE:
mRbSlide.setChecked(true);
break;
case NONE:
mRbNone.setChecked(true);
break;
case SCROLL:
mRbScroll.setChecked(true);
break;
}
}

private Drawable getDrawable(int drawRes) {
return ContextCompat.getDrawable(getContext(), drawRes);
}

private void initClick() {
//亮度调节
mIvBrightnessMinus.setOnClickListener(
(v) -> {
if (mCbBrightnessAuto.isChecked()) {
mCbBrightnessAuto.setChecked(false);
}
int progress = mSbBrightness.getProgress() - 1;
if (progress < 0) return;
mSbBrightness.setProgress(progress);
BrightnessUtils.setBrightness(mActivity, progress);
}
);
mIvBrightnessPlus.setOnClickListener(
(v) -> {
if (mCbBrightnessAuto.isChecked()) {
mCbBrightnessAuto.setChecked(false);
}
int progress = mSbBrightness.getProgress() + 1;
if (progress > mSbBrightness.getMax()) return;
mSbBrightness.setProgress(progress);
BrightnessUtils.setBrightness(mActivity, progress);
//设置进度
ReadSettingManager.getInstance().setBrightness(progress);
}
);

mSbBrightness.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
if (mCbBrightnessAuto.isChecked()) {
mCbBrightnessAuto.setChecked(false);
}
//设置当前 Activity 的亮度
BrightnessUtils.setBrightness(mActivity, progress);
//存储亮度的进度条
ReadSettingManager.getInstance().setBrightness(progress);
}
});

mCbBrightnessAuto.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
if (isChecked) {
//获取屏幕的亮度
BrightnessUtils.setBrightness(mActivity, BrightnessUtils.getScreenBrightness(mActivity));
} else {
//获取进度条的亮度
BrightnessUtils.setBrightness(mActivity, mSbBrightness.getProgress());
}
ReadSettingManager.getInstance().setAutoBrightness(isChecked);
}
);

//字体大小调节
mTvFontMinus.setOnClickListener(
(v) -> {
if (mCbFontDefault.isChecked()) {
mCbFontDefault.setChecked(false);
}
int fontSize = Integer.valueOf(mTvFont.getText().toString()) - 1;
if (fontSize < 0) return;
mTvFont.setText(fontSize + "");
mPageLoader.setTextSize(fontSize);
}
);

mTvFontPlus.setOnClickListener(
(v) -> {
if (mCbFontDefault.isChecked()) {
mCbFontDefault.setChecked(false);
}
int fontSize = Integer.valueOf(mTvFont.getText().toString()) + 1;
mTvFont.setText(fontSize + "");
mPageLoader.setTextSize(fontSize);
}
);

mCbFontDefault.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
if (isChecked) {
int fontSize = ScreenUtils.dpToPx(DEFAULT_TEXT_SIZE);
mTvFont.setText(fontSize + "");
mPageLoader.setTextSize(fontSize);
}
}
);

//Page Mode 切换
mRgPageMode.setOnCheckedChangeListener(
(group, checkedId) -> {
PageMode pageMode;
switch (checkedId) {
case R.id.read_setting_rb_simulation:
pageMode = PageMode.SIMULATION;
break;
case R.id.read_setting_rb_cover:
pageMode = PageMode.COVER;
break;
case R.id.read_setting_rb_slide:
pageMode = PageMode.SLIDE;
break;
case R.id.read_setting_rb_scroll:
pageMode = PageMode.SCROLL;
break;
case R.id.read_setting_rb_none:
pageMode = PageMode.NONE;
break;
default:
pageMode = PageMode.SIMULATION;
break;
}
mPageLoader.setPageMode(pageMode);
}
);

//背景的点击事件
mPageStyleAdapter.setOnItemClickListener(
(view, pos) -> mPageLoader.setPageStyle(PageStyle.values()[pos])
);

//更多设置
mTvMore.setOnClickListener(
(v) -> {
// Intent intent = new Intent(getContext(), MoreSettingActivity.class);
// mActivity.startActivityForResult(intent, ReadActivity.REQUEST_MORE_SETTING);
//关闭当前设置
dismiss();
}
);
}

public boolean isBrightFollowSystem() {
if (mCbBrightnessAuto == null) {
return false;
}
return mCbBrightnessAuto.isChecked();
}
}

+ 98
- 0
app/src/main/java/com/yzx/webebook/widget/animation/AnimationProvider.java Parādīt failu

@@ -0,0 +1,98 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.widget.Scroller;

/**
* Created by Administrator on 2016/8/1 0001.
*/
public abstract class AnimationProvider {

public enum Direction {
NONE(true),NEXT(true), PRE(true), UP(false), DOWN(false);

public final boolean isHorizontal;

Direction(boolean isHorizontal) {
this.isHorizontal = isHorizontal;
}
}

protected Bitmap mCurPageBitmap,mNextPageBitmap;
protected float myStartX;
protected float myStartY;
protected int myEndX;
protected int myEndY;
protected Direction myDirection;

protected int mScreenWidth;
protected int mScreenHeight;

protected PointF mTouch = new PointF(); // 拖拽点
private Direction direction = Direction.NONE;
private boolean isCancel = false;

public AnimationProvider(int width,int height) {
mCurPageBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
mNextPageBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

this.mScreenWidth = width;
this.mScreenHeight = height;
}

//绘制滑动页面
public abstract void drawMove(Canvas canvas);

//绘制不滑动页面
public abstract void drawStatic(Canvas canvas);

//设置开始拖拽点
public void setStartPoint(float x,float y){
myStartX = x;
myStartY = y;
}

//设置拖拽点
public void setTouchPoint(float x,float y){
mTouch.x = x;
mTouch.y = y;
}

//设置方向
public void setDirection(Direction direction){
this.direction = direction;
}

public Direction getDirection(){
return direction;
}

public void setCancel(boolean isCancel){
this.isCancel = isCancel;
}

public abstract void startAnimation(Scroller scroller);

/**
* 转换页面,在显示下一章的时候,必须首先调用此方法
*/
public void changePage(){
Bitmap bitmap = mCurPageBitmap;
mCurPageBitmap = mNextPageBitmap;
mNextPageBitmap = bitmap;
}

public Bitmap getNextBitmap(){
return mNextPageBitmap;
}

public Bitmap getBgBitmap(){
return mNextPageBitmap;
}

public boolean getCancel(){
return isCancel;
}
}

+ 100
- 0
app/src/main/java/com/yzx/webebook/widget/animation/CoverPageAnim.java Parādīt failu

@@ -0,0 +1,100 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.view.View;

/**
* Created by newbiechen on 17-7-24.
*/

public class CoverPageAnim extends HorizonPageAnim {

private Rect mSrcRect, mDestRect;
private GradientDrawable mBackShadowDrawableLR;

public CoverPageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
mSrcRect = new Rect(0, 0, mViewWidth, mViewHeight);
mDestRect = new Rect(0, 0, mViewWidth, mViewHeight);
int[] mBackShadowColors = new int[] { 0x66000000,0x00000000};
mBackShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
}

@Override
public void drawStatic(Canvas canvas) {
if (isCancel){
mNextBitmap = mCurBitmap.copy(Bitmap.Config.RGB_565, true);
canvas.drawBitmap(mCurBitmap, 0, 0, null);
}else {
canvas.drawBitmap(mNextBitmap, 0, 0, null);
}
}

@Override
public void drawMove(Canvas canvas) {

switch (mDirection){
case NEXT:
int dis = (int) (mViewWidth - mStartX + mTouchX);
if (dis > mViewWidth){
dis = mViewWidth;
}
//计算bitmap截取的区域
mSrcRect.left = mViewWidth - dis;
//计算bitmap在canvas显示的区域
mDestRect.right = dis;
canvas.drawBitmap(mNextBitmap,0,0,null);
canvas.drawBitmap(mCurBitmap,mSrcRect,mDestRect,null);
addShadow(dis,canvas);
break;
default:
mSrcRect.left = (int) (mViewWidth - mTouchX);
mDestRect.right = (int) mTouchX;
canvas.drawBitmap(mCurBitmap,0,0,null);
canvas.drawBitmap(mNextBitmap,mSrcRect,mDestRect,null);
addShadow((int) mTouchX,canvas);
break;
}
}

//添加阴影
public void addShadow(int left,Canvas canvas) {
mBackShadowDrawableLR.setBounds(left, 0, left + 30 , mScreenHeight);
mBackShadowDrawableLR.draw(canvas);
}

@Override
public void startAnim() {
super.startAnim();
int dx = 0;
switch (mDirection){
case NEXT:
if (isCancel){
int dis = (int) ((mViewWidth - mStartX) + mTouchX);
if (dis > mViewWidth){
dis = mViewWidth;
}
dx = mViewWidth - dis;
}else{
dx = (int) -(mTouchX + (mViewWidth - mStartX));
}
break;
default:
if (isCancel){
dx = (int) -mTouchX;
}else{
dx = (int) (mViewWidth - mTouchX);
}
break;
}

//滑动速度保持一致
int duration = (400 * Math.abs(dx)) / mViewWidth;
mScroller.startScroll((int) mTouchX, 0, dx, 0, duration);
}
}

+ 230
- 0
app/src/main/java/com/yzx/webebook/widget/animation/HorizonPageAnim.java Parādīt failu

@@ -0,0 +1,230 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

/**
* Created by newbiechen on 17-7-24.
* 横向动画的模板
*/

public abstract class HorizonPageAnim extends PageAnimation{
private static final String TAG = "HorizonPageAnim";

protected Bitmap mCurBitmap;
protected Bitmap mNextBitmap;
//是否取消翻页
protected boolean isCancel = false;

//可以使用 mLast代替
private int mMoveX = 0;
private int mMoveY = 0;
//是否移动了
private boolean isMove = false;
//是否翻阅下一页。true表示翻到下一页,false表示上一页。
private boolean isNext = false;

//是否没下一页或者上一页
private boolean noNext = false;

public HorizonPageAnim(int w, int h, View view, OnPageChangeListener listener) {
this(w, h, 0, 0, view, listener);
}

public HorizonPageAnim(int w, int h, int marginWidth, int marginHeight,
View view, OnPageChangeListener listener) {
super(w, h, marginWidth, marginHeight, view,listener);
//创建图片
mCurBitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.RGB_565);
mNextBitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.RGB_565);
}

/**
* 转换页面,在显示下一章的时候,必须首先调用此方法
*/
public void changePage(){
Bitmap bitmap = mCurBitmap;
mCurBitmap = mNextBitmap;
mNextBitmap = bitmap;
}

public abstract void drawStatic(Canvas canvas);

public abstract void drawMove(Canvas canvas);

@Override
public boolean onTouchEvent(MotionEvent event) {
//获取点击位置
int x = (int)event.getX();
int y = (int)event.getY();
//设置触摸点
setTouchPoint(x,y);

switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//移动的点击位置
mMoveX = 0;
mMoveY = 0;
//是否移动
isMove = false;
//是否存在下一章
noNext = false;
//是下一章还是前一章
isNext = false;
//是否正在执行动画
isRunning = false;
//取消
isCancel = false;
//设置起始位置的触摸点
setStartPoint(x,y);
//如果存在动画则取消动画
abortAnim();
break;
case MotionEvent.ACTION_MOVE:
final int slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
//判断是否移动了
if (!isMove) {
isMove = Math.abs(mStartX - x) > slop || Math.abs(mStartY - y) > slop;
}

if (isMove){
//判断是否是准备移动的状态(将要移动但是还没有移动)
if (mMoveX == 0 && mMoveY ==0) {
//判断翻得是上一页还是下一页
if (x - mStartX > 0){
//上一页的参数配置
isNext = false;
boolean hasPrev = mListener.hasPrev();
setDirection(Direction.PRE);
//如果上一页不存在
if (!hasPrev) {
noNext = true;
return true;
}
}else{
//进行下一页的配置
isNext = true;
//判断是否下一页存在
boolean hasNext = mListener.hasNext();
//如果存在设置动画方向
setDirection(Direction.NEXT);

//如果不存在表示没有下一页了
if (!hasNext) {
noNext = true;
return true;
}
}
}else{
//判断是否取消翻页
if (isNext){
if (x - mMoveX > 0){
isCancel = true;
}else {
isCancel = false;
}
}else{
if (x - mMoveX < 0){
isCancel = true;
}else {
isCancel = false;
}
}
}

mMoveX = x;
mMoveY = y;
isRunning = true;
mView.invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (!isMove){
if (x < mScreenWidth / 2){
isNext = false;
}else{
isNext = true;
}

if (isNext) {
//判断是否下一页存在
boolean hasNext = mListener.hasNext();
//设置动画方向
setDirection(Direction.NEXT);
if (!hasNext) {
return true;
}
} else {
boolean hasPrev = mListener.hasPrev();
setDirection(Direction.PRE);
if (!hasPrev) {
return true;
}
}
}

// 是否取消翻页
if (isCancel){
mListener.pageCancel();
}

// 开启翻页效果
if (!noNext) {
startAnim();
mView.invalidate();
}
break;
}
return true;
}

@Override
public void draw(Canvas canvas) {
if (isRunning) {
drawMove(canvas);
} else {
if (isCancel){
mNextBitmap = mCurBitmap.copy(Bitmap.Config.RGB_565, true);
}
drawStatic(canvas);
}
}

@Override
public void scrollAnim() {
if (mScroller.computeScrollOffset()) {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();

setTouchPoint(x, y);

if (mScroller.getFinalX() == x && mScroller.getFinalY() == y){
isRunning = false;
}
mView.postInvalidate();
}
}

@Override
public void abortAnim() {
if (!mScroller.isFinished()){
mScroller.abortAnimation();
isRunning = false;
setTouchPoint(mScroller.getFinalX(),mScroller.getFinalY());
mView.postInvalidate();
}
}

@Override
public Bitmap getBgBitmap() {
return mNextBitmap;
}

@Override
public Bitmap getNextBitmap() {
return mNextBitmap;
}
}

+ 37
- 0
app/src/main/java/com/yzx/webebook/widget/animation/NonePageAnim.java Parādīt failu

@@ -0,0 +1,37 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Canvas;
import android.view.View;

/**
* Created by newbiechen on 17-7-24.
*/

public class NonePageAnim extends HorizonPageAnim{

public NonePageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
}

@Override
public void drawStatic(Canvas canvas) {
if (isCancel){
canvas.drawBitmap(mCurBitmap, 0, 0, null);
}else {
canvas.drawBitmap(mNextBitmap, 0, 0, null);
}
}

@Override
public void drawMove(Canvas canvas) {
if (isCancel){
canvas.drawBitmap(mCurBitmap, 0, 0, null);
}else {
canvas.drawBitmap(mNextBitmap, 0, 0, null);
}
}

@Override
public void startAnim() {
}
}

+ 157
- 0
app/src/main/java/com/yzx/webebook/widget/animation/PageAnimation.java Parādīt failu

@@ -0,0 +1,157 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;

/**
* Created by newbiechen on 17-7-24.
* 翻页动画抽象类
*/

public abstract class PageAnimation {
//正在使用的View
protected View mView;
//滑动装置
protected Scroller mScroller;
//监听器
protected OnPageChangeListener mListener;
//移动方向
protected Direction mDirection = Direction.NONE;

protected boolean isRunning = false;

//屏幕的尺寸
protected int mScreenWidth;
protected int mScreenHeight;
//屏幕的间距
protected int mMarginWidth;
protected int mMarginHeight;
//视图的尺寸
protected int mViewWidth;
protected int mViewHeight;
//起始点
protected float mStartX;
protected float mStartY;
//触碰点
protected float mTouchX;
protected float mTouchY;
//上一个触碰点
protected float mLastX;
protected float mLastY;

public PageAnimation(int w, int h,View view,OnPageChangeListener listener){
this(w, h, 0, 0, view,listener);
}

public PageAnimation(int w, int h, int marginWidth, int marginHeight, View view,OnPageChangeListener listener){
mScreenWidth = w;
mScreenHeight = h;

mMarginWidth = marginWidth;
mMarginHeight = marginHeight;

mViewWidth = mScreenWidth - mMarginWidth * 2;
mViewHeight = mScreenHeight - mMarginHeight * 2;

mView = view;
mListener = listener;

mScroller = new Scroller(mView.getContext(), new LinearInterpolator());
}

public void setStartPoint(float x,float y){
mStartX = x;
mStartY = y;

mLastX = mStartX;
mLastY = mStartY;
}

public void setTouchPoint(float x,float y){
mLastX = mTouchX;
mLastY = mTouchY;

mTouchX = x;
mTouchY = y;
}

public boolean isRunning(){
return isRunning;
}

/**
* 开启翻页动画
*/
public void startAnim(){
if (isRunning){
return;
}
isRunning = true;
}

public void setDirection(Direction direction){
mDirection = direction;
}

public Direction getDirection(){
return mDirection;
}

public void clear(){
mView = null;
}
/**
* 点击事件的处理
* @param event
*/
public abstract boolean onTouchEvent(MotionEvent event);

/**
* 绘制图形
* @param canvas
*/
public abstract void draw(Canvas canvas);

/**
* 滚动动画
* 必须放在computeScroll()方法中执行
*/
public abstract void scrollAnim();

/**
* 取消动画
*/
public abstract void abortAnim();

/**
* 获取背景板
* @return
*/
public abstract Bitmap getBgBitmap();

/**
* 获取内容显示版面
*/
public abstract Bitmap getNextBitmap();

public enum Direction {
NONE(true),NEXT(true), PRE(true), UP(false), DOWN(false);

public final boolean isHorizontal;

Direction(boolean isHorizontal) {
this.isHorizontal = isHorizontal;
}
}

public interface OnPageChangeListener {
boolean hasPrev();
boolean hasNext();
void pageCancel();
}

}

+ 408
- 0
app/src/main/java/com/yzx/webebook/widget/animation/ScrollPageAnim.java Parādīt failu

@@ -0,0 +1,408 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;

/**
* Created by newbiechen on 17-7-23.
* 原理:仿照ListView源码实现的上下滑动效果
* Alter by: zeroAngus
* <p>
* 问题:
* 1. 向上翻页,重复的问题 (完成)
* 2. 滑动卡顿的问题。原因:由于绘制的数据过多造成的卡顿问题。 (主要是文字绘制需要的时长比较多) 解决办法:做文字缓冲
* 3. 弱网环境下,显示的问题
*/
public class ScrollPageAnim extends PageAnimation {
private static final String TAG = "ScrollAnimation";
// 滑动追踪的时间
private static final int VELOCITY_DURATION = 1000;
private VelocityTracker mVelocity;

// 整个Bitmap的背景显示
private Bitmap mBgBitmap;

// 下一个展示的图片
private Bitmap mNextBitmap;

// 被废弃的图片列表
private ArrayDeque<BitmapView> mScrapViews;
// 正在被利用的图片列表
private ArrayList<BitmapView> mActiveViews = new ArrayList<>(2);

// 是否处于刷新阶段
private boolean isRefresh = true;

public ScrollPageAnim(int w, int h, int marginWidth, int marginHeight,
View view, OnPageChangeListener listener) {
super(w, h, marginWidth, marginHeight, view, listener);
// 创建两个BitmapView
initWidget();
}

private void initWidget() {
mBgBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.RGB_565);

mScrapViews = new ArrayDeque<>(2);
for (int i = 0; i < 2; ++i) {
BitmapView view = new BitmapView();
view.bitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.RGB_565);
view.srcRect = new Rect(0, 0, mViewWidth, mViewHeight);
view.destRect = new Rect(0, 0, mViewWidth, mViewHeight);
view.top = 0;
view.bottom = view.bitmap.getHeight();

mScrapViews.push(view);
}
onLayout();
isRefresh = false;
}

// 修改布局,填充内容
private void onLayout() {
// 如果还没有开始加载,则从上到下进行绘制
if (mActiveViews.size() == 0) {
fillDown(0, 0);
mDirection = Direction.NONE;
} else {
int offset = (int) (mTouchY - mLastY);
// 判断是下滑还是上拉 (下滑)
if (offset > 0) {
int topEdge = mActiveViews.get(0).top;
fillUp(topEdge, offset);
}
// 上拉
else {
// 底部的距离 = 当前底部的距离 + 滑动的距离 (因为上滑,得到的值肯定是负的)
int bottomEdge = mActiveViews.get(mActiveViews.size() - 1).bottom;
fillDown(bottomEdge, offset);
}
}
}

// 底部填充
private Iterator<BitmapView> downIt;

/**
* 创建View填充底部空白部分
*
* @param bottomEdge :当前最后一个View的底部,在整个屏幕上的位置,即相对于屏幕顶部的距离
* @param offset :滑动的偏移量
*/
private void fillDown(int bottomEdge, int offset) {

downIt = mActiveViews.iterator();
BitmapView view;

// 进行删除
while (downIt.hasNext()) {
view = downIt.next();
view.top = view.top + offset;
view.bottom = view.bottom + offset;
// 设置允许显示的范围
view.destRect.top = view.top;
view.destRect.bottom = view.bottom;

// 判断是否越界了
if (view.bottom <= 0) {
// 添加到废弃的View中
mScrapViews.add(view);
// 从Active中移除
downIt.remove();
// 如果原先是从上加载,现在变成从下加载,则表示取消
if (mDirection == Direction.UP) {
mListener.pageCancel();
mDirection = Direction.NONE;
}
}
}

// 滑动之后的最后一个 View 的距离屏幕顶部上的实际位置
int realEdge = bottomEdge + offset;

// 进行填充
while (realEdge < mViewHeight && mActiveViews.size() < 2) {
// 从废弃的Views中获取一个
view = mScrapViews.getFirst();
/* //擦除其Bitmap(重新创建会不会更好一点)
eraseBitmap(view.bitmap,view.bitmap.getWidth(),view.bitmap.getHeight(),0,0);*/
if (view == null) return;

Bitmap cancelBitmap = mNextBitmap;
mNextBitmap = view.bitmap;

if (!isRefresh) {
boolean hasNext = mListener.hasNext(); //如果不成功则无法滑动

// 如果不存在next,则进行还原
if (!hasNext) {
mNextBitmap = cancelBitmap;
for (BitmapView activeView : mActiveViews) {
activeView.top = 0;
activeView.bottom = mViewHeight;
// 设置允许显示的范围
activeView.destRect.top = activeView.top;
activeView.destRect.bottom = activeView.bottom;
}
abortAnim();
return;
}
}

// 如果加载成功,那么就将View从ScrapViews中移除
mScrapViews.removeFirst();
// 添加到存活的Bitmap中
mActiveViews.add(view);
mDirection = Direction.DOWN;

// 设置Bitmap的范围
view.top = realEdge;
view.bottom = realEdge + view.bitmap.getHeight();
// 设置允许显示的范围
view.destRect.top = view.top;
view.destRect.bottom = view.bottom;

realEdge += view.bitmap.getHeight();
}
}

private Iterator<BitmapView> upIt;

/**
* 创建View填充顶部空白部分
*
* @param topEdge : 当前第一个View的顶部,到屏幕顶部的距离
* @param offset : 滑动的偏移量
*/
private void fillUp(int topEdge, int offset) {
// 首先进行布局的调整
upIt = mActiveViews.iterator();
BitmapView view;
while (upIt.hasNext()) {
view = upIt.next();
view.top = view.top + offset;
view.bottom = view.bottom + offset;
//设置允许显示的范围
view.destRect.top = view.top;
view.destRect.bottom = view.bottom;

// 判断是否越界了
if (view.top >= mViewHeight) {
// 添加到废弃的View中
mScrapViews.add(view);
// 从Active中移除
upIt.remove();

// 如果原先是下,现在变成从上加载了,则表示取消加载

if (mDirection == Direction.DOWN) {
mListener.pageCancel();
mDirection = Direction.NONE;
}
}
}

// 滑动之后,第一个 View 的顶部距离屏幕顶部的实际位置。
int realEdge = topEdge + offset;

// 对布局进行View填充
while (realEdge > 0 && mActiveViews.size() < 2) {
// 从废弃的Views中获取一个
view = mScrapViews.getFirst();
if (view == null) return;

// 判断是否存在上一章节
Bitmap cancelBitmap = mNextBitmap;
mNextBitmap = view.bitmap;
if (!isRefresh) {
boolean hasPrev = mListener.hasPrev(); // 如果不成功则无法滑动
// 如果不存在next,则进行还原
if (!hasPrev) {
mNextBitmap = cancelBitmap;
for (BitmapView activeView : mActiveViews) {
activeView.top = 0;
activeView.bottom = mViewHeight;
// 设置允许显示的范围
activeView.destRect.top = activeView.top;
activeView.destRect.bottom = activeView.bottom;
}
abortAnim();
return;
}
}
// 如果加载成功,那么就将View从ScrapViews中移除
mScrapViews.removeFirst();
// 加入到存活的对象中
mActiveViews.add(0, view);
mDirection = Direction.UP;
// 设置Bitmap的范围
view.top = realEdge - view.bitmap.getHeight();
view.bottom = realEdge;

// 设置允许显示的范围
view.destRect.top = view.top;
view.destRect.bottom = view.bottom;
realEdge -= view.bitmap.getHeight();
}
}

/**
* 对Bitmap进行擦除
*
* @param b
* @param width
* @param height
* @param paddingLeft
* @param paddingTop
*/
private void eraseBitmap(Bitmap b, int width, int height,
int paddingLeft, int paddingTop) {
/* if (mInitBitmapPix == null) return;
b.setPixels(mInitBitmapPix, 0, width, paddingLeft, paddingTop, width, height);*/
}

/**
* 重置位移
*/
public void resetBitmap() {
isRefresh = true;
// 将所有的Active加入到Scrap中
for (BitmapView view : mActiveViews) {
mScrapViews.add(view);
}
// 清除所有的Active
mActiveViews.clear();
// 重新进行布局
onLayout();
isRefresh = false;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();

// 初始化速度追踪器
if (mVelocity == null) {
mVelocity = VelocityTracker.obtain();
}

mVelocity.addMovement(event);
// 设置触碰点
setTouchPoint(x, y);

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isRunning = false;
// 设置起始点
setStartPoint(x, y);
// 停止动画
abortAnim();
break;
case MotionEvent.ACTION_MOVE:
mVelocity.computeCurrentVelocity(VELOCITY_DURATION);
isRunning = true;
// 进行刷新
mView.postInvalidate();
break;
case MotionEvent.ACTION_UP:
isRunning = false;
// 开启动画
startAnim();
// 删除检测器
mVelocity.recycle();
mVelocity = null;
break;

case MotionEvent.ACTION_CANCEL:
try {
mVelocity.recycle(); // if velocityTracker won't be used should be recycled
mVelocity = null;
} catch (Exception e) {
e.printStackTrace();
}
break;
}
return true;
}


BitmapView tmpView;

@Override
public void draw(Canvas canvas) {
//进行布局
onLayout();

//绘制背景
canvas.drawBitmap(mBgBitmap, 0, 0, null);
//绘制内容
canvas.save();
//移动位置
canvas.translate(0, mMarginHeight);
//裁剪显示区域
canvas.clipRect(0, 0, mViewWidth, mViewHeight);
/* //设置背景透明
canvas.drawColor(0x40);*/
//绘制Bitmap
for (int i = 0; i < mActiveViews.size(); ++i) {
tmpView = mActiveViews.get(i);
canvas.drawBitmap(tmpView.bitmap, tmpView.srcRect, tmpView.destRect, null);
}
canvas.restore();
}

@Override
public synchronized void startAnim() {
isRunning = true;
mScroller.fling(0, (int) mTouchY, 0, (int) mVelocity.getYVelocity()
, 0, 0, Integer.MAX_VALUE * -1, Integer.MAX_VALUE);
}

@Override
public void scrollAnim() {
if (mScroller.computeScrollOffset()) {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
setTouchPoint(x, y);
if (mScroller.getFinalX() == x && mScroller.getFinalY() == y) {
isRunning = false;
}
mView.postInvalidate();
}
}

@Override
public void abortAnim() {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
isRunning = false;
}
}

@Override
public Bitmap getBgBitmap() {
return mBgBitmap;
}

@Override
public Bitmap getNextBitmap() {
return mNextBitmap;
}

private static class BitmapView {
Bitmap bitmap;
Rect srcRect;
Rect destRect;
int top;
int bottom;
}
}

+ 668
- 0
app/src/main/java/com/yzx/webebook/widget/animation/SimulationPageAnim.java Parādīt failu

@@ -0,0 +1,668 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.View;

/**
* Created by newbiechen on 17-7-24.
*/

public class SimulationPageAnim extends HorizonPageAnim {
private static final String TAG = "SimulationPageAnim";

private int mCornerX = 1; // 拖拽点对应的页脚
private int mCornerY = 1;
private Path mPath0;
private Path mPath1;

private PointF mBezierStart1 = new PointF(); // 贝塞尔曲线起始点
private PointF mBezierControl1 = new PointF(); // 贝塞尔曲线控制点
private PointF mBeziervertex1 = new PointF(); // 贝塞尔曲线顶点
private PointF mBezierEnd1 = new PointF(); // 贝塞尔曲线结束点

private PointF mBezierStart2 = new PointF(); // 另一条贝塞尔曲线
private PointF mBezierControl2 = new PointF();
private PointF mBeziervertex2 = new PointF();
private PointF mBezierEnd2 = new PointF();

private float mMiddleX;
private float mMiddleY;
private float mDegrees;
private float mTouchToCornerDis;
private ColorMatrixColorFilter mColorMatrixFilter;
private Matrix mMatrix;
private float[] mMatrixArray = {0, 0, 0, 0, 0, 0, 0, 0, 1.0f};

private boolean mIsRTandLB; // 是否属于右上左下
private float mMaxLength;
private GradientDrawable mBackShadowDrawableLR; // 有阴影的GradientDrawable
private GradientDrawable mBackShadowDrawableRL;
private GradientDrawable mFolderShadowDrawableLR;
private GradientDrawable mFolderShadowDrawableRL;

private GradientDrawable mFrontShadowDrawableHBT;
private GradientDrawable mFrontShadowDrawableHTB;
private GradientDrawable mFrontShadowDrawableVLR;
private GradientDrawable mFrontShadowDrawableVRL;

private Paint mPaint;

// 适配 android 高版本无法使用 XOR 的问题
private Path mXORPath;

public SimulationPageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
mPath0 = new Path();
mPath1 = new Path();
mXORPath = new Path();
mMaxLength = (float) Math.hypot(mScreenWidth, mScreenHeight);
mPaint = new Paint();

mPaint.setStyle(Paint.Style.FILL);

createDrawable();

ColorMatrix cm = new ColorMatrix();//设置颜色数组
float array[] = {1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0};
cm.set(array);
mColorMatrixFilter = new ColorMatrixColorFilter(cm);
mMatrix = new Matrix();

mTouchX = 0.01f; // 不让x,y为0,否则在点计算时会有问题
mTouchY = 0.01f;
}

@Override
public void drawMove(Canvas canvas) {
switch (mDirection) {
case NEXT:
calcPoints();
drawCurrentPageArea(canvas, mCurBitmap, mPath0);
drawNextPageAreaAndShadow(canvas, mNextBitmap);
drawCurrentPageShadow(canvas);
drawCurrentBackArea(canvas, mCurBitmap);
break;
default:
calcPoints();
drawCurrentPageArea(canvas, mNextBitmap, mPath0);
drawNextPageAreaAndShadow(canvas, mCurBitmap);
drawCurrentPageShadow(canvas);
drawCurrentBackArea(canvas, mNextBitmap);
break;
}
}

@Override
public void drawStatic(Canvas canvas) {
if (isCancel) {
mNextBitmap = mCurBitmap.copy(Bitmap.Config.RGB_565, true);
canvas.drawBitmap(mCurBitmap, 0, 0, null);
} else {
canvas.drawBitmap(mNextBitmap, 0, 0, null);
}
}

@Override
public void startAnim() {
super.startAnim();
int dx, dy;
// dx 水平方向滑动的距离,负值会使滚动向左滚动
// dy 垂直方向滑动的距离,负值会使滚动向上滚动
if (isCancel) {

if (mCornerX > 0 && mDirection.equals(Direction.NEXT)) {
dx = (int) (mScreenWidth - mTouchX);
} else {
dx = -(int) mTouchX;
}

if (!mDirection.equals(Direction.NEXT)) {
dx = (int) -(mScreenWidth + mTouchX);
}

if (mCornerY > 0) {
dy = (int) (mScreenHeight - mTouchY);
} else {
dy = -(int) mTouchY; // 防止mTouchY最终变为0
}
} else {
if (mCornerX > 0 && mDirection.equals(Direction.NEXT)) {
dx = -(int) (mScreenWidth + mTouchX);
} else {
dx = (int) (mScreenWidth - mTouchX + mScreenWidth);
}
if (mCornerY > 0) {
dy = (int) (mScreenHeight - mTouchY);
} else {
dy = (int) (1 - mTouchY); // 防止mTouchY最终变为0
}
}
mScroller.startScroll((int) mTouchX, (int) mTouchY, dx, dy, 400);
}

@Override
public void setDirection(Direction direction) {
super.setDirection(direction);

switch (direction) {
case PRE:
//上一页滑动不出现对角
if (mStartX > mScreenWidth / 2) {
calcCornerXY(mStartX, mScreenHeight);
} else {
calcCornerXY(mScreenWidth - mStartX, mScreenHeight);
}
break;
case NEXT:
if (mScreenWidth / 2 > mStartX) {
calcCornerXY(mScreenWidth - mStartX, mStartY);
}
break;
}
}

@Override
public void setStartPoint(float x, float y) {
super.setStartPoint(x, y);
calcCornerXY(x, y);
}

@Override
public void setTouchPoint(float x, float y) {
super.setTouchPoint(x, y);
//触摸y中间位置吧y变成屏幕高度
if ((mStartY > mScreenHeight / 3 && mStartY < mScreenHeight * 2 / 3) || mDirection.equals(Direction.PRE)) {
mTouchY = mScreenHeight;
}

if (mStartY > mScreenHeight / 3 && mStartY < mScreenHeight / 2 && mDirection.equals(Direction.NEXT)) {
mTouchY = 1;
}
}

/**
* 创建阴影的GradientDrawable
*/
private void createDrawable() {
int[] color = {0x333333, 0xb0333333};
mFolderShadowDrawableRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, color);
mFolderShadowDrawableRL
.setGradientType(GradientDrawable.LINEAR_GRADIENT);

mFolderShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, color);
mFolderShadowDrawableLR
.setGradientType(GradientDrawable.LINEAR_GRADIENT);

// 背面颜色组
int[] mBackShadowColors = new int[]{0xff111111, 0x111111};
mBackShadowDrawableRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mBackShadowColors);
mBackShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);

mBackShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);

// 前面颜色组
int[] mFrontShadowColors = new int[]{0x80111111, 0x111111};
mFrontShadowDrawableVLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mFrontShadowColors);
mFrontShadowDrawableVLR
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableVRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mFrontShadowColors);
mFrontShadowDrawableVRL
.setGradientType(GradientDrawable.LINEAR_GRADIENT);

mFrontShadowDrawableHTB = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, mFrontShadowColors);
mFrontShadowDrawableHTB
.setGradientType(GradientDrawable.LINEAR_GRADIENT);

mFrontShadowDrawableHBT = new GradientDrawable(
GradientDrawable.Orientation.BOTTOM_TOP, mFrontShadowColors);
mFrontShadowDrawableHBT
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
}

/**
* 是否能够拖动过去
*
* @return
*/
public boolean canDragOver() {
return mTouchToCornerDis > mScreenWidth / 10;
}

public boolean right() {
return mCornerX <= -4;
}

/**
* 绘制翻起页背面
*
* @param canvas
* @param bitmap
*/
private void drawCurrentBackArea(Canvas canvas, Bitmap bitmap) {
int i = (int) (mBezierStart1.x + mBezierControl1.x) / 2;
float f1 = Math.abs(i - mBezierControl1.x);
int i1 = (int) (mBezierStart2.y + mBezierControl2.y) / 2;
float f2 = Math.abs(i1 - mBezierControl2.y);
float f3 = Math.min(f1, f2);
mPath1.reset();
mPath1.moveTo(mBeziervertex2.x, mBeziervertex2.y);
mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
mPath1.lineTo(mBezierEnd1.x, mBezierEnd1.y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath1.close();
GradientDrawable mFolderShadowDrawable;
int left;
int right;
if (mIsRTandLB) {
left = (int) (mBezierStart1.x - 1);
right = (int) (mBezierStart1.x + f3 + 1);
mFolderShadowDrawable = mFolderShadowDrawableLR;
} else {
left = (int) (mBezierStart1.x - f3 - 1);
right = (int) (mBezierStart1.x + 1);
mFolderShadowDrawable = mFolderShadowDrawableRL;
}
canvas.save();
try {
canvas.clipPath(mPath0);
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
}

mPaint.setColorFilter(mColorMatrixFilter);
//对Bitmap进行取色
int color = bitmap.getPixel(1, 1);
//获取对应的三色
int red = (color & 0xff0000) >> 16;
int green = (color & 0x00ff00) >> 8;
int blue = (color & 0x0000ff);
//转换成含有透明度的颜色
int tempColor = Color.argb(200, red, green, blue);


float dis = (float) Math.hypot(mCornerX - mBezierControl1.x,
mBezierControl2.y - mCornerY);
float f8 = (mCornerX - mBezierControl1.x) / dis;
float f9 = (mBezierControl2.y - mCornerY) / dis;
mMatrixArray[0] = 1 - 2 * f9 * f9;
mMatrixArray[1] = 2 * f8 * f9;
mMatrixArray[3] = mMatrixArray[1];
mMatrixArray[4] = 1 - 2 * f8 * f8;
mMatrix.reset();
mMatrix.setValues(mMatrixArray);
mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);
mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);
canvas.drawBitmap(bitmap, mMatrix, mPaint);
//背景叠加
canvas.drawColor(tempColor);

mPaint.setColorFilter(null);

canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
mFolderShadowDrawable.setBounds(left, (int) mBezierStart1.y, right,
(int) (mBezierStart1.y + mMaxLength));
mFolderShadowDrawable.draw(canvas);
canvas.restore();
}

/**
* 绘制翻起页的阴影
*
* @param canvas
*/
private void drawCurrentPageShadow(Canvas canvas) {
double degree;
if (mIsRTandLB) {
degree = Math.PI
/ 4
- Math.atan2(mBezierControl1.y - mTouchY, mTouchX
- mBezierControl1.x);
} else {
degree = Math.PI
/ 4
- Math.atan2(mTouchY - mBezierControl1.y, mTouchX
- mBezierControl1.x);
}
// 翻起页阴影顶点与touch点的距离
double d1 = (float) 25 * 1.414 * Math.cos(degree);
double d2 = (float) 25 * 1.414 * Math.sin(degree);
float x = (float) (mTouchX + d1);
float y;
if (mIsRTandLB) {
y = (float) (mTouchY + d2);
} else {
y = (float) (mTouchY - d2);
}
mPath1.reset();
mPath1.moveTo(x, y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierControl1.x, mBezierControl1.y);
mPath1.lineTo(mBezierStart1.x, mBezierStart1.y);
mPath1.close();
float rotateDegrees;
canvas.save();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mXORPath.reset();
mXORPath.moveTo(0f, 0f);
mXORPath.lineTo(canvas.getWidth(), 0f);
mXORPath.lineTo(canvas.getWidth(), canvas.getHeight());
mXORPath.lineTo(0f, canvas.getHeight());
mXORPath.close();

// 取 path 的补集,作为 canvas 的交集
mXORPath.op(mPath0, Path.Op.XOR);
canvas.clipPath(mXORPath);
} else {
canvas.clipPath(mPath0, Region.Op.XOR);
}
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
// TODO: handle exception
}

int leftx;
int rightx;
GradientDrawable mCurrentPageShadow;
if (mIsRTandLB) {
leftx = (int) (mBezierControl1.x);
rightx = (int) mBezierControl1.x + 25;
mCurrentPageShadow = mFrontShadowDrawableVLR;
} else {
leftx = (int) (mBezierControl1.x - 25);
rightx = (int) mBezierControl1.x + 1;
mCurrentPageShadow = mFrontShadowDrawableVRL;
}

rotateDegrees = (float) Math.toDegrees(Math.atan2(mTouchX
- mBezierControl1.x, mBezierControl1.y - mTouchY));
canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y);
mCurrentPageShadow.setBounds(leftx,
(int) (mBezierControl1.y - mMaxLength), rightx,
(int) (mBezierControl1.y));
mCurrentPageShadow.draw(canvas);
canvas.restore();

mPath1.reset();
mPath1.moveTo(x, y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierControl2.x, mBezierControl2.y);
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
mPath1.close();
canvas.save();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mXORPath.reset();
mXORPath.moveTo(0f, 0f);
mXORPath.lineTo(canvas.getWidth(), 0f);
mXORPath.lineTo(canvas.getWidth(), canvas.getHeight());
mXORPath.lineTo(0f, canvas.getHeight());
mXORPath.close();

// 取 path 的补给,作为 canvas 的交集
mXORPath.op(mPath0, Path.Op.XOR);
canvas.clipPath(mXORPath);
} else {
canvas.clipPath(mPath0, Region.Op.XOR);
}
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
}

if (mIsRTandLB) {
leftx = (int) (mBezierControl2.y);
rightx = (int) (mBezierControl2.y + 25);
mCurrentPageShadow = mFrontShadowDrawableHTB;
} else {
leftx = (int) (mBezierControl2.y - 25);
rightx = (int) (mBezierControl2.y + 1);
mCurrentPageShadow = mFrontShadowDrawableHBT;
}
rotateDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl2.y
- mTouchY, mBezierControl2.x - mTouchX));
canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
float temp;
if (mBezierControl2.y < 0)
temp = mBezierControl2.y - mScreenHeight;
else
temp = mBezierControl2.y;

int hmg = (int) Math.hypot(mBezierControl2.x, temp);
if (hmg > mMaxLength)
mCurrentPageShadow
.setBounds((int) (mBezierControl2.x - 25) - hmg, leftx,
(int) (mBezierControl2.x + mMaxLength) - hmg,
rightx);
else
mCurrentPageShadow.setBounds(
(int) (mBezierControl2.x - mMaxLength), leftx,
(int) (mBezierControl2.x), rightx);

mCurrentPageShadow.draw(canvas);
canvas.restore();
}

private void drawNextPageAreaAndShadow(Canvas canvas, Bitmap bitmap) {
mPath1.reset();
mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
mPath1.lineTo(mBeziervertex2.x, mBeziervertex2.y);
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
mPath1.lineTo(mCornerX, mCornerY);
mPath1.close();

mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x
- mCornerX, mBezierControl2.y - mCornerY));
int leftx;
int rightx;
GradientDrawable mBackShadowDrawable;
if (mIsRTandLB) { //左下及右上
leftx = (int) (mBezierStart1.x);
rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4);
mBackShadowDrawable = mBackShadowDrawableLR;
} else {
leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4);
rightx = (int) mBezierStart1.x;
mBackShadowDrawable = mBackShadowDrawableRL;
}
canvas.save();
try {
canvas.clipPath(mPath0);
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
}


canvas.drawBitmap(bitmap, 0, 0, null);
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
mBackShadowDrawable.setBounds(leftx, (int) mBezierStart1.y, rightx,
(int) (mMaxLength + mBezierStart1.y));//左上及右下角的xy坐标值,构成一个矩形
mBackShadowDrawable.draw(canvas);
canvas.restore();
}

private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
mPath0.reset();
mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,
mBezierEnd1.y);
mPath0.lineTo(mTouchX, mTouchY);
mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,
mBezierStart2.y);
mPath0.lineTo(mCornerX, mCornerY);
mPath0.close();

canvas.save();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mXORPath.reset();
mXORPath.moveTo(0f, 0f);
mXORPath.lineTo(canvas.getWidth(), 0f);
mXORPath.lineTo(canvas.getWidth(), canvas.getHeight());
mXORPath.lineTo(0f, canvas.getHeight());
mXORPath.close();

// 取 path 的补给,作为 canvas 的交集
mXORPath.op(path, Path.Op.XOR);
canvas.clipPath(mXORPath);
} else {
canvas.clipPath(path, Region.Op.XOR);
}
canvas.drawBitmap(bitmap, 0, 0, null);
try {
canvas.restore();
} catch (Exception e) {

}

}

/**
* 计算拖拽点对应的拖拽脚
*
* @param x
* @param y
*/
public void calcCornerXY(float x, float y) {
if (x <= mScreenWidth / 2) {
mCornerX = 0;
} else {
mCornerX = mScreenWidth;
}
if (y <= mScreenHeight / 2) {
mCornerY = 0;
} else {
mCornerY = mScreenHeight;
}

if ((mCornerX == 0 && mCornerY == mScreenHeight)
|| (mCornerX == mScreenWidth && mCornerY == 0)) {
mIsRTandLB = true;
} else {
mIsRTandLB = false;
}

}

private void calcPoints() {
mMiddleX = (mTouchX + mCornerX) / 2;
mMiddleY = (mTouchY + mCornerY) / 2;
mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY)
* (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
mBezierControl1.y = mCornerY;
mBezierControl2.x = mCornerX;

float f4 = mCornerY - mMiddleY;
if (f4 == 0) {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / 0.1f;

} else {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
}
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x)
/ 2;
mBezierStart1.y = mCornerY;

// 当mBezierStart1.x < 0或者mBezierStart1.x > 480时
// 如果继续翻页,会出现BUG故在此限制
if (mTouchX > 0 && mTouchX < mScreenWidth) {
if (mBezierStart1.x < 0 || mBezierStart1.x > mScreenWidth) {
if (mBezierStart1.x < 0)
mBezierStart1.x = mScreenWidth - mBezierStart1.x;

float f1 = Math.abs(mCornerX - mTouchX);
float f2 = mScreenWidth * f1 / mBezierStart1.x;
mTouchX = Math.abs(mCornerX - f2);

float f3 = Math.abs(mCornerX - mTouchX)
* Math.abs(mCornerY - mTouchY) / f1;
mTouchY = Math.abs(mCornerY - f3);

mMiddleX = (mTouchX + mCornerX) / 2;
mMiddleY = (mTouchY + mCornerY) / 2;

mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY)
* (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
mBezierControl1.y = mCornerY;

mBezierControl2.x = mCornerX;

float f5 = mCornerY - mMiddleY;
if (f5 == 0) {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / 0.1f;
} else {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
}

mBezierStart1.x = mBezierControl1.x
- (mCornerX - mBezierControl1.x) / 2;
}
}
mBezierStart2.x = mCornerX;
mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y)
/ 2;

mTouchToCornerDis = (float) Math.hypot((mTouchX - mCornerX),
(mTouchY - mCornerY));

mBezierEnd1 = getCross(new PointF(mTouchX, mTouchY), mBezierControl1, mBezierStart1,
mBezierStart2);
mBezierEnd2 = getCross(new PointF(mTouchX, mTouchY), mBezierControl2, mBezierStart1,
mBezierStart2);

mBeziervertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4;
mBeziervertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4;
mBeziervertex2.x = (mBezierStart2.x + 2 * mBezierControl2.x + mBezierEnd2.x) / 4;
mBeziervertex2.y = (2 * mBezierControl2.y + mBezierStart2.y + mBezierEnd2.y) / 4;
}

/**
* 求解直线P1P2和直线P3P4的交点坐标
*
* @param P1
* @param P2
* @param P3
* @param P4
* @return
*/
private PointF getCross(PointF P1, PointF P2, PointF P3, PointF P4) {
PointF CrossP = new PointF();
// 二元函数通式: y=ax+b
float a1 = (P2.y - P1.y) / (P2.x - P1.x);
float b1 = ((P1.x * P2.y) - (P2.x * P1.y)) / (P1.x - P2.x);

float a2 = (P4.y - P3.y) / (P4.x - P3.x);
float b2 = ((P3.x * P4.y) - (P4.x * P3.y)) / (P3.x - P4.x);
CrossP.x = (b2 - b1) / (a1 - a2);
CrossP.y = a1 * CrossP.x + b1;
return CrossP;
}
}

+ 101
- 0
app/src/main/java/com/yzx/webebook/widget/animation/SlidePageAnim.java Parādīt failu

@@ -0,0 +1,101 @@
package com.yzx.webebook.widget.animation;

import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;

/**
* Created by newbiechen on 17-7-24.
*/

public class SlidePageAnim extends HorizonPageAnim {
private Rect mSrcRect, mDestRect,mNextSrcRect,mNextDestRect;

public SlidePageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
mSrcRect = new Rect(0, 0, mViewWidth, mViewHeight);
mDestRect = new Rect(0, 0, mViewWidth, mViewHeight);
mNextSrcRect = new Rect(0, 0, mViewWidth, mViewHeight);
mNextDestRect = new Rect(0, 0, mViewWidth, mViewHeight);
}

@Override
public void drawStatic(Canvas canvas) {
if (isCancel){
canvas.drawBitmap(mCurBitmap, 0, 0, null);
}else {
canvas.drawBitmap(mNextBitmap, 0, 0, null);
}
}

@Override
public void drawMove(Canvas canvas) {
int dis = 0;
switch (mDirection){
case NEXT:
//左半边的剩余区域
dis = (int) (mScreenWidth - mStartX + mTouchX);
if (dis > mScreenWidth){
dis = mScreenWidth;
}
//计算bitmap截取的区域
mSrcRect.left = mScreenWidth - dis;
//计算bitmap在canvas显示的区域
mDestRect.right = dis;
//计算下一页截取的区域
mNextSrcRect.right = mScreenWidth - dis;
//计算下一页在canvas显示的区域
mNextDestRect.left = dis;

canvas.drawBitmap(mNextBitmap,mNextSrcRect,mNextDestRect,null);
canvas.drawBitmap(mCurBitmap,mSrcRect,mDestRect,null);
break;
default:
dis = (int) (mTouchX - mStartX);
if (dis < 0){
dis = 0;
mStartX = mTouchX;
}
mSrcRect.left = mScreenWidth - dis;
mDestRect.right = dis;

//计算下一页截取的区域
mNextSrcRect.right = mScreenWidth - dis;
//计算下一页在canvas显示的区域
mNextDestRect.left = dis;

canvas.drawBitmap(mCurBitmap,mNextSrcRect,mNextDestRect,null);
canvas.drawBitmap(mNextBitmap,mSrcRect,mDestRect,null);
break;
}
}

@Override
public void startAnim() {
super.startAnim();
int dx = 0;
switch (mDirection){
case NEXT:
if (isCancel){
int dis = (int)((mScreenWidth - mStartX) + mTouchX);
if (dis > mScreenWidth){
dis = mScreenWidth;
}
dx = mScreenWidth - dis;
}else{
dx = (int) -(mTouchX + (mScreenWidth - mStartX));
}
break;
default:
if (isCancel){
dx = (int)-Math.abs(mTouchX - mStartX);
}else{
dx = (int) (mScreenWidth - (mTouchX - mStartX));
}
break;
}
//滑动速度保持一致
int duration = (400 * Math.abs(dx)) / mScreenWidth;
mScroller.startScroll((int) mTouchX, 0, dx, 0, duration);
}
}

+ 440
- 0
app/src/main/java/com/yzx/webebook/widget/page/LocalPageLoader.java Parādīt failu

@@ -0,0 +1,440 @@
package com.yzx.webebook.widget.page;


import com.yzx.webebook.model.bean.BookChapterBean;
import com.yzx.webebook.model.bean.CollBookBean;
import com.yzx.webebook.model.local.BookRepository;
import com.yzx.webebook.model.local.Void;
import com.yzx.webebook.utils.Charset;
import com.yzx.webebook.utils.Constant;
import com.yzx.webebook.utils.FileUtils;
import com.yzx.webebook.utils.IOUtils;
import com.yzx.webebook.utils.LogUtils;
import com.yzx.webebook.utils.MD5Utils;
import com.yzx.webebook.utils.RxUtils;
import com.yzx.webebook.utils.StringUtils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleObserver;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.disposables.Disposable;

/**
* Created by newbiechen on 17-7-1.
* 问题:
* 1. 异常处理没有做好
*/

public class LocalPageLoader extends PageLoader {
private static final String TAG = "LocalPageLoader";
//默认从文件中获取数据的长度
private final static int BUFFER_SIZE = 512 * 1024;
//没有标题的时候,每个章节的最大长度
private final static int MAX_LENGTH_WITH_NO_CHAPTER = 10 * 1024;

// "序(章)|前言"
private final static Pattern mPreChapterPattern = Pattern.compile("^(\\s{0,10})((\u5e8f[\u7ae0\u8a00]?)|(\u524d\u8a00)|(\u6954\u5b50))(\\s{0,10})$", Pattern.MULTILINE);

//正则表达式章节匹配模式
// "(第)([0-9零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10})([章节回集卷])(.*)"
private static final String[] CHAPTER_PATTERNS = new String[]{"^(.{0,8})(\u7b2c)([0-9\u96f6\u4e00\u4e8c\u4e24\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u58f9\u8d30\u53c1\u8086\u4f0d\u9646\u67d2\u634c\u7396\u62fe\u4f70\u4edf]{1,10})([\u7ae0\u8282\u56de\u96c6\u5377])(.{0,30})$",
"^(\\s{0,4})([\\(\u3010\u300a]?(\u5377)?)([0-9\u96f6\u4e00\u4e8c\u4e24\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u58f9\u8d30\u53c1\u8086\u4f0d\u9646\u67d2\u634c\u7396\u62fe\u4f70\u4edf]{1,10})([\\.:\uff1a\u0020\f\t])(.{0,30})$",
"^(\\s{0,4})([\\(\uff08\u3010\u300a])(.{0,30})([\\)\uff09\u3011\u300b])(\\s{0,2})$",
"^(\\s{0,4})(\u6b63\u6587)(.{0,20})$",
"^(.{0,4})(Chapter|chapter)(\\s{0,4})([0-9]{1,4})(.{0,30})$"};

//章节解析模式
private Pattern mChapterPattern = null;
//获取书本的文件
private File mBookFile;
//编码类型
private Charset mCharset;

private Disposable mChapterDisp = null;

public LocalPageLoader(PageView pageView, CollBookBean collBook) {
super(pageView, collBook);
mStatus = STATUS_PARING;
}

private List<TxtChapter> convertTxtChapter(List<BookChapterBean> bookChapters) {
List<TxtChapter> txtChapters = new ArrayList<>(bookChapters.size());
for (BookChapterBean bean : bookChapters) {
TxtChapter chapter = new TxtChapter();
chapter.title = bean.getTitle();
chapter.start = bean.getStart();
chapter.end = bean.getEnd();
txtChapters.add(chapter);
}
return txtChapters;
}

/**
* 未完成的部分:
* 1. 序章的添加
* 2. 章节存在的书本的虚拟分章效果
*
* @throws IOException
*/
private void loadChapters() throws IOException {
List<TxtChapter> chapters = new ArrayList<>();
//获取文件流
RandomAccessFile bookStream = new RandomAccessFile(mBookFile, "r");
//寻找匹配文章标题的正则表达式,判断是否存在章节名
boolean hasChapter = checkChapterType(bookStream);
//加载章节
byte[] buffer = new byte[BUFFER_SIZE];
//获取到的块起始点,在文件中的位置
long curOffset = 0;
//block的个数
int blockPos = 0;
//读取的长度
int length;

//获取文件中的数据到buffer,直到没有数据为止
while ((length = bookStream.read(buffer, 0, buffer.length)) > 0) {
++blockPos;
//如果存在Chapter
if (hasChapter) {
//将数据转换成String
String blockContent = new String(buffer, 0, length, mCharset.getName());
//当前Block下使过的String的指针
int seekPos = 0;
//进行正则匹配
Matcher matcher = mChapterPattern.matcher(blockContent);
//如果存在相应章节
while (matcher.find()) {
//获取匹配到的字符在字符串中的起始位置
int chapterStart = matcher.start();

//如果 seekPos == 0 && nextChapterPos != 0 表示当前block处前面有一段内容
//第一种情况一定是序章 第二种情况可能是上一个章节的内容
if (seekPos == 0 && chapterStart != 0) {
//获取当前章节的内容
String chapterContent = blockContent.substring(seekPos, chapterStart);
//设置指针偏移
seekPos += chapterContent.length();

//如果当前对整个文件的偏移位置为0的话,那么就是序章
if (curOffset == 0) {
//创建序章
TxtChapter preChapter = new TxtChapter();
preChapter.title = "序章";
preChapter.start = 0;
preChapter.end = chapterContent.getBytes(mCharset.getName()).length; //获取String的byte值,作为最终值

//如果序章大小大于30才添加进去
if (preChapter.end - preChapter.start > 30) {
chapters.add(preChapter);
}

//创建当前章节
TxtChapter curChapter = new TxtChapter();
curChapter.title = matcher.group();
curChapter.start = preChapter.end;
chapters.add(curChapter);
}
//否则就block分割之后,上一个章节的剩余内容
else {
//获取上一章节
TxtChapter lastChapter = chapters.get(chapters.size() - 1);
//将当前段落添加上一章去
lastChapter.end += chapterContent.getBytes(mCharset.getName()).length;

//如果章节内容太小,则移除
if (lastChapter.end - lastChapter.start < 30) {
chapters.remove(lastChapter);
}

//创建当前章节
TxtChapter curChapter = new TxtChapter();
curChapter.title = matcher.group();
curChapter.start = lastChapter.end;
chapters.add(curChapter);
}
} else {
//是否存在章节
if (chapters.size() != 0) {
//获取章节内容
String chapterContent = blockContent.substring(seekPos, matcher.start());
seekPos += chapterContent.length();

//获取上一章节
TxtChapter lastChapter = chapters.get(chapters.size() - 1);
lastChapter.end = lastChapter.start + chapterContent.getBytes(mCharset.getName()).length;

//如果章节内容太小,则移除
if (lastChapter.end - lastChapter.start < 30) {
chapters.remove(lastChapter);
}

//创建当前章节
TxtChapter curChapter = new TxtChapter();
curChapter.title = matcher.group();
curChapter.start = lastChapter.end;
chapters.add(curChapter);
}
//如果章节不存在则创建章节
else {
TxtChapter curChapter = new TxtChapter();
curChapter.title = matcher.group();
curChapter.start = 0;
chapters.add(curChapter);
}
}
}
}
//进行本地虚拟分章
else {
//章节在buffer的偏移量
int chapterOffset = 0;
//当前剩余可分配的长度
int strLength = length;
//分章的位置
int chapterPos = 0;

while (strLength > 0) {
++chapterPos;
//是否长度超过一章
if (strLength > MAX_LENGTH_WITH_NO_CHAPTER) {
//在buffer中一章的终止点
int end = length;
//寻找换行符作为终止点
for (int i = chapterOffset + MAX_LENGTH_WITH_NO_CHAPTER; i < length; ++i) {
if (buffer[i] == Charset.BLANK) {
end = i;
break;
}
}
TxtChapter chapter = new TxtChapter();
chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")";
chapter.start = curOffset + chapterOffset + 1;
chapter.end = curOffset + end;
chapters.add(chapter);
//减去已经被分配的长度
strLength = strLength - (end - chapterOffset);
//设置偏移的位置
chapterOffset = end;
} else {
TxtChapter chapter = new TxtChapter();
chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")";
chapter.start = curOffset + chapterOffset + 1;
chapter.end = curOffset + length;
chapters.add(chapter);
strLength = 0;
}
}
}

//block的偏移点
curOffset += length;

if (hasChapter) {
//设置上一章的结尾
TxtChapter lastChapter = chapters.get(chapters.size() - 1);
lastChapter.end = curOffset;
}

//当添加的block太多的时候,执行GC
if (blockPos % 15 == 0) {
System.gc();
System.runFinalization();
}
}

mChapterList = chapters;
IOUtils.close(bookStream);

System.gc();
System.runFinalization();
}

/**
* 从文件中提取一章的内容
*
* @param chapter
* @return
*/
private byte[] getChapterContent(TxtChapter chapter) {
RandomAccessFile bookStream = null;
try {
bookStream = new RandomAccessFile(mBookFile, "r");
bookStream.seek(chapter.start);
int extent = (int) (chapter.end - chapter.start);
byte[] content = new byte[extent];
bookStream.read(content, 0, extent);
return content;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.close(bookStream);
}

return new byte[0];
}

/**
* 1. 检查文件中是否存在章节名
* 2. 判断文件中使用的章节名类型的正则表达式
*
* @return 是否存在章节名
*/
private boolean checkChapterType(RandomAccessFile bookStream) throws IOException {
//首先获取128k的数据
byte[] buffer = new byte[BUFFER_SIZE / 4];
int length = bookStream.read(buffer, 0, buffer.length);
//进行章节匹配
for (String str : CHAPTER_PATTERNS) {
Pattern pattern = Pattern.compile(str, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(new String(buffer, 0, length, mCharset.getName()));
//如果匹配存在,那么就表示当前章节使用这种匹配方式
if (matcher.find()) {
mChapterPattern = pattern;
//重置指针位置
bookStream.seek(0);
return true;
}
}

//重置指针位置
bookStream.seek(0);
return false;
}

@Override
public void saveRecord() {
super.saveRecord();
//修改当前COllBook记录
if (mCollBook != null && isChapterListPrepare) {
//表示当前CollBook已经阅读
mCollBook.setIsUpdate(false);
mCollBook.setLastChapter(mChapterList.get(mCurChapterPos).getTitle());
mCollBook.setLastRead(StringUtils.
dateConvert(System.currentTimeMillis(), Constant.FORMAT_BOOK_DATE));
//直接更新
BookRepository.getInstance()
.saveCollBook(mCollBook);
}
}

@Override
public void closeBook() {
super.closeBook();
if (mChapterDisp != null) {
mChapterDisp.dispose();
mChapterDisp = null;
}
}

@Override
public void refreshChapterList() {
// 对于文件是否存在,或者为空的判断,不作处理。 ==> 在文件打开前处理过了。
mBookFile = new File(mCollBook.getCover());
//获取文件编码
mCharset = FileUtils.getCharset(mBookFile.getAbsolutePath());

String lastModified = StringUtils.dateConvert(mBookFile.lastModified(), Constant.FORMAT_BOOK_DATE);

// 判断文件是否已经加载过,并具有缓存
if (!mCollBook.isUpdate() && mCollBook.getUpdated() != null
&& mCollBook.getUpdated().equals(lastModified)
&& mCollBook.getBookChapters() != null) {

mChapterList = convertTxtChapter(mCollBook.getBookChapters());
isChapterListPrepare = true;

//提示目录加载完成
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}

// 加载并显示当前章节
openChapter();

return;
}

// 通过RxJava异步处理分章事件
Single.create(new SingleOnSubscribe<Void>() {
@Override
public void subscribe(SingleEmitter<Void> e) throws Exception {
loadChapters();
e.onSuccess(new Void());
}
}).compose(RxUtils::toSimpleSingle)
.subscribe(new SingleObserver<Void>() {
@Override
public void onSubscribe(Disposable d) {
mChapterDisp = d;
}

@Override
public void onSuccess(Void value) {
mChapterDisp = null;
isChapterListPrepare = true;

// 提示目录加载完成
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}

// 存储章节到数据库
List<BookChapterBean> bookChapterBeanList = new ArrayList<>();
for (int i = 0; i < mChapterList.size(); ++i) {
TxtChapter chapter = mChapterList.get(i);
BookChapterBean bean = new BookChapterBean();
bean.setId(MD5Utils.strToMd5By16(mBookFile.getAbsolutePath()
+ File.separator + chapter.title)); // 将路径+i 作为唯一值
bean.setTitle(chapter.getTitle());
bean.setStart(chapter.getStart());
bean.setUnreadble(false);
bean.setEnd(chapter.getEnd());
bookChapterBeanList.add(bean);
}
mCollBook.setBookChapters(bookChapterBeanList);
mCollBook.setUpdated(lastModified);

BookRepository.getInstance().saveBookChaptersWithAsync(bookChapterBeanList);
BookRepository.getInstance().saveCollBook(mCollBook);

// 加载并显示当前章节
openChapter();
}

@Override
public void onError(Throwable e) {
chapterError();
LogUtils.d(TAG, "file load error:" + e.toString());
}
});
}

@Override
protected BufferedReader getChapterReader(TxtChapter chapter) throws Exception {
//从文件中获取数据
byte[] content = getChapterContent(chapter);
ByteArrayInputStream bais = new ByteArrayInputStream(content);
BufferedReader br = new BufferedReader(new InputStreamReader(bais, mCharset.getName()));
return br;
}

@Override
protected boolean hasChapterData(TxtChapter chapter) {
return true;
}
}

+ 226
- 0
app/src/main/java/com/yzx/webebook/widget/page/NetPageLoader.java Parādīt failu

@@ -0,0 +1,226 @@
package com.yzx.webebook.widget.page;




import com.yzx.webebook.model.bean.BookChapterBean;
import com.yzx.webebook.model.bean.CollBookBean;
import com.yzx.webebook.model.local.BookRepository;
import com.yzx.webebook.utils.BookManager;
import com.yzx.webebook.utils.Constant;
import com.yzx.webebook.utils.FileUtils;
import com.yzx.webebook.utils.StringUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

/**
* Created by newbiechen on 17-5-29.
* 网络页面加载器
*/

public class NetPageLoader extends PageLoader {
private static final String TAG = "PageFactory";

public NetPageLoader(PageView pageView, CollBookBean collBook) {
super(pageView, collBook);
}

private List<TxtChapter> convertTxtChapter(List<BookChapterBean> bookChapters) {
List<TxtChapter> txtChapters = new ArrayList<>(bookChapters.size());
for (BookChapterBean bean : bookChapters) {
TxtChapter chapter = new TxtChapter();
chapter.bookId = bean.getBookId();
chapter.title = bean.getTitle();
chapter.link = bean.getLink();
txtChapters.add(chapter);
}
return txtChapters;
}

@Override
public void refreshChapterList() {
if (mCollBook.getBookChapters() == null) return;

// 将 BookChapter 转换成当前可用的 Chapter
mChapterList = convertTxtChapter(mCollBook.getBookChapters());
isChapterListPrepare = true;

// 目录加载完成,执行回调操作。
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}

// 如果章节未打开
if (!isChapterOpen()) {
// 打开章节
openChapter();
}
}

@Override
protected BufferedReader getChapterReader(TxtChapter chapter) throws Exception {
File file = new File(Constant.BOOK_CACHE_PATH + mCollBook.get_id()
+ File.separator + chapter.title + FileUtils.SUFFIX_NB);
if (!file.exists()) return null;

Reader reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
return br;
}

@Override
protected boolean hasChapterData(TxtChapter chapter) {
return BookManager.isChapterCached(mCollBook.get_id(), chapter.title);
}

// 装载上一章节的内容
@Override
boolean parsePrevChapter() {
boolean isRight = super.parsePrevChapter();

if (mStatus == STATUS_FINISH) {
loadPrevChapter();
} else if (mStatus == STATUS_LOADING) {
loadCurrentChapter();
}
return isRight;
}

// 装载当前章内容。
@Override
boolean parseCurChapter() {
boolean isRight = super.parseCurChapter();

if (mStatus == STATUS_LOADING) {
loadCurrentChapter();
}
return isRight;
}

// 装载下一章节的内容
@Override
boolean parseNextChapter() {
boolean isRight = super.parseNextChapter();

if (mStatus == STATUS_FINISH) {
loadNextChapter();
} else if (mStatus == STATUS_LOADING) {
loadCurrentChapter();
}

return isRight;
}

/**
* 加载当前页的前面两个章节
*/
private void loadPrevChapter() {
if (mPageChangeListener != null) {
int end = mCurChapterPos;
int begin = end - 2;
if (begin < 0) {
begin = 0;
}

requestChapters(begin, end);
}
}

/**
* 加载前一页,当前页,后一页。
*/
private void loadCurrentChapter() {
if (mPageChangeListener != null) {
int begin = mCurChapterPos;
int end = mCurChapterPos;

// 是否当前不是最后一章
if (end < mChapterList.size()) {
end = end + 1;
if (end >= mChapterList.size()) {
end = mChapterList.size() - 1;
}
}

// 如果当前不是第一章
if (begin != 0) {
begin = begin - 1;
if (begin < 0) {
begin = 0;
}
}

requestChapters(begin, end);
}
}

/**
* 加载当前页的后两个章节
*/
private void loadNextChapter() {
if (mPageChangeListener != null) {

// 提示加载后两章
int begin = mCurChapterPos + 1;
int end = begin + 1;

// 判断是否大于最后一章
if (begin >= mChapterList.size()) {
// 如果下一章超出目录了,就没有必要加载了
return;
}

if (end > mChapterList.size()) {
end = mChapterList.size() - 1;
}

requestChapters(begin, end);
}
}

private void requestChapters(int start, int end) {
// 检验输入值
if (start < 0) {
start = 0;
}

if (end >= mChapterList.size()) {
end = mChapterList.size() - 1;
}


List<TxtChapter> chapters = new ArrayList<>();

// 过滤,哪些数据已经加载了
for (int i = start; i <= end; ++i) {
TxtChapter txtChapter = mChapterList.get(i);
if (!hasChapterData(txtChapter)) {
chapters.add(txtChapter);
}
}

if (!chapters.isEmpty()) {
mPageChangeListener.requestChapters(chapters);
}
}

@Override
public void saveRecord() {
super.saveRecord();
if (mCollBook != null && isChapterListPrepare) {
//表示当前CollBook已经阅读
mCollBook.setIsUpdate(false);
mCollBook.setLastRead(StringUtils.
dateConvert(System.currentTimeMillis(), Constant.FORMAT_BOOK_DATE));
//直接更新
BookRepository.getInstance()
.saveCollBook(mCollBook);
}
}
}


+ 1450
- 0
app/src/main/java/com/yzx/webebook/widget/page/PageLoader.java
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 10
- 0
app/src/main/java/com/yzx/webebook/widget/page/PageMode.java Parādīt failu

@@ -0,0 +1,10 @@
package com.yzx.webebook.widget.page;

/**
* Created by newbiechen on 2018/2/5.
* 作用:翻页动画的模式
*/

public enum PageMode {
SIMULATION, COVER, SLIDE, NONE, SCROLL
}

+ 38
- 0
app/src/main/java/com/yzx/webebook/widget/page/PageStyle.java Parādīt failu

@@ -0,0 +1,38 @@
package com.yzx.webebook.widget.page;



import androidx.annotation.ColorRes;

import com.yzx.webebook.R;


/**
* Created by newbiechen on 2018/2/5.
* 作用:页面的展示风格。
*/

public enum PageStyle {
BG_0(R.color.nb_read_font_1, R.color.nb_read_bg_1),
BG_1(R.color.nb_read_font_2, R.color.nb_read_bg_2),
BG_2(R.color.nb_read_font_3, R.color.nb_read_bg_3),
BG_3(R.color.nb_read_font_4, R.color.nb_read_bg_4),
BG_4(R.color.nb_read_font_5, R.color.nb_read_bg_5),
NIGHT(R.color.nb_read_font_night, R.color.nb_read_bg_night),;

private int fontColor;
private int bgColor;

PageStyle(@ColorRes int fontColor, @ColorRes int bgColor) {
this.fontColor = fontColor;
this.bgColor = bgColor;
}

public int getFontColor() {
return fontColor;
}

public int getBgColor() {
return bgColor;
}
}

+ 378
- 0
app/src/main/java/com/yzx/webebook/widget/page/PageView.java Parādīt failu

@@ -0,0 +1,378 @@
package com.yzx.webebook.widget.page;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import com.yzx.webebook.model.bean.CollBookBean;
import com.yzx.webebook.widget.animation.CoverPageAnim;
import com.yzx.webebook.widget.animation.HorizonPageAnim;
import com.yzx.webebook.widget.animation.NonePageAnim;
import com.yzx.webebook.widget.animation.PageAnimation;
import com.yzx.webebook.widget.animation.ScrollPageAnim;
import com.yzx.webebook.widget.animation.SimulationPageAnim;
import com.yzx.webebook.widget.animation.SlidePageAnim;


/**
* Created by Administrator on 2016/8/29 0029.
* 原作者的GitHub Project Path:(https://github.com/PeachBlossom/treader)
* 绘制页面显示内容的类
*/
public class PageView extends View {

private final static String TAG = "BookPageWidget";

private int mViewWidth = 0; // 当前View的宽
private int mViewHeight = 0; // 当前View的高

private int mStartX = 0;
private int mStartY = 0;
private boolean isMove = false;
// 初始化参数
private int mBgColor = 0xFFCEC29C;
private PageMode mPageMode = PageMode.SIMULATION;
// 是否允许点击
private boolean canTouch = true;
// 唤醒菜单的区域
private RectF mCenterRect = null;
private boolean isPrepare;
// 动画类
private PageAnimation mPageAnim;
// 动画监听类
private PageAnimation.OnPageChangeListener mPageAnimListener = new PageAnimation.OnPageChangeListener() {
@Override
public boolean hasPrev() {
return PageView.this.hasPrevPage();
}

@Override
public boolean hasNext() {
return PageView.this.hasNextPage();
}

@Override
public void pageCancel() {
PageView.this.pageCancel();
}
};

//点击监听
private TouchListener mTouchListener;
//内容加载器
private PageLoader mPageLoader;

public PageView(Context context) {
this(context, null);
}

public PageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public PageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
mViewHeight = h;

isPrepare = true;

if (mPageLoader != null) {
mPageLoader.prepareDisplay(w, h);
}
}

//设置翻页的模式
void setPageMode(PageMode pageMode) {
mPageMode = pageMode;
//视图未初始化的时候,禁止调用
if (mViewWidth == 0 || mViewHeight == 0) return;

switch (mPageMode) {
case SIMULATION:
mPageAnim = new SimulationPageAnim(mViewWidth, mViewHeight, this, mPageAnimListener);
break;
case COVER:
mPageAnim = new CoverPageAnim(mViewWidth, mViewHeight, this, mPageAnimListener);
break;
case SLIDE:
mPageAnim = new SlidePageAnim(mViewWidth, mViewHeight, this, mPageAnimListener);
break;
case NONE:
mPageAnim = new NonePageAnim(mViewWidth, mViewHeight, this, mPageAnimListener);
break;
case SCROLL:
mPageAnim = new ScrollPageAnim(mViewWidth, mViewHeight, 0,
mPageLoader.getMarginHeight(), this, mPageAnimListener);
break;
default:
mPageAnim = new SimulationPageAnim(mViewWidth, mViewHeight, this, mPageAnimListener);
}
}

public Bitmap getNextBitmap() {
if (mPageAnim == null) return null;
return mPageAnim.getNextBitmap();
}

public Bitmap getBgBitmap() {
if (mPageAnim == null) return null;
return mPageAnim.getBgBitmap();
}

public boolean autoPrevPage() {
//滚动暂时不支持自动翻页
if (mPageAnim instanceof ScrollPageAnim) {
return false;
} else {
startPageAnim(PageAnimation.Direction.PRE);
return true;
}
}

public boolean autoNextPage() {
if (mPageAnim instanceof ScrollPageAnim) {
return false;
} else {
startPageAnim(PageAnimation.Direction.NEXT);
return true;
}
}

private void startPageAnim(PageAnimation.Direction direction) {
if (mTouchListener == null) return;
//是否正在执行动画
abortAnimation();
if (direction == PageAnimation.Direction.NEXT) {
int x = mViewWidth;
int y = mViewHeight;
//初始化动画
mPageAnim.setStartPoint(x, y);
//设置点击点
mPageAnim.setTouchPoint(x, y);
//设置方向
Boolean hasNext = hasNextPage();

mPageAnim.setDirection(direction);
if (!hasNext) {
return;
}
} else {
int x = 0;
int y = mViewHeight;
//初始化动画
mPageAnim.setStartPoint(x, y);
//设置点击点
mPageAnim.setTouchPoint(x, y);
mPageAnim.setDirection(direction);
//设置方向方向
Boolean hashPrev = hasPrevPage();
if (!hashPrev) {
return;
}
}
mPageAnim.startAnim();
this.postInvalidate();
}

public void setBgColor(int color) {
mBgColor = color;
}

@Override
protected void onDraw(Canvas canvas) {

//绘制背景
canvas.drawColor(mBgColor);

//绘制动画
mPageAnim.draw(canvas);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);

if (!canTouch && event.getAction() != MotionEvent.ACTION_DOWN) return true;

int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = x;
mStartY = y;
isMove = false;
canTouch = mTouchListener.onTouch();
mPageAnim.onTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
// 判断是否大于最小滑动值。
int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
if (!isMove) {
isMove = Math.abs(mStartX - event.getX()) > slop || Math.abs(mStartY - event.getY()) > slop;
}

// 如果滑动了,则进行翻页。
if (isMove) {
mPageAnim.onTouchEvent(event);
}
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
//设置中间区域范围
if (mCenterRect == null) {
mCenterRect = new RectF(mViewWidth / 5, mViewHeight / 3,
mViewWidth * 4 / 5, mViewHeight * 2 / 3);
}

//是否点击了中间
if (mCenterRect.contains(x, y)) {
if (mTouchListener != null) {
mTouchListener.center();
}
return true;
}
}
mPageAnim.onTouchEvent(event);
break;
}
return true;
}

/**
* 判断是否存在上一页
*
* @return
*/
private boolean hasPrevPage() {
mTouchListener.prePage();
return mPageLoader.prev();
}

/**
* 判断是否下一页存在
*
* @return
*/
private boolean hasNextPage() {
mTouchListener.nextPage();
return mPageLoader.next();
}

private void pageCancel() {
mTouchListener.cancel();
mPageLoader.pageCancel();
}

@Override
public void computeScroll() {
//进行滑动
mPageAnim.scrollAnim();
super.computeScroll();
}

//如果滑动状态没有停止就取消状态,重新设置Anim的触碰点
public void abortAnimation() {
mPageAnim.abortAnim();
}

public boolean isRunning() {
if (mPageAnim == null) {
return false;
}
return mPageAnim.isRunning();
}

public boolean isPrepare() {
return isPrepare;
}

public void setTouchListener(TouchListener mTouchListener) {
this.mTouchListener = mTouchListener;
}

public void drawNextPage() {
if (!isPrepare) return;

if (mPageAnim instanceof HorizonPageAnim) {
((HorizonPageAnim) mPageAnim).changePage();
}
mPageLoader.drawPage(getNextBitmap(), false);
}

/**
* 绘制当前页。
*
* @param isUpdate
*/
public void drawCurPage(boolean isUpdate) {
if (!isPrepare) return;

if (!isUpdate){
if (mPageAnim instanceof ScrollPageAnim) {
((ScrollPageAnim) mPageAnim).resetBitmap();
}
}

mPageLoader.drawPage(getNextBitmap(), isUpdate);
}

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mPageAnim.abortAnim();
mPageAnim.clear();

mPageLoader = null;
mPageAnim = null;
}

/**
* 获取 PageLoader
*
* @param collBook
* @return
*/
public PageLoader getPageLoader(CollBookBean collBook) {
// 判是否已经存在
if (mPageLoader != null) {
return mPageLoader;
}
// 根据书籍类型,获取具体的加载器
if (collBook.isLocal()) {
mPageLoader = new LocalPageLoader(this, collBook);
} else {
mPageLoader = new NetPageLoader(this, collBook);
}
// 判断是否 PageView 已经初始化完成
if (mViewWidth != 0 || mViewHeight != 0) {
// 初始化 PageLoader 的屏幕大小
mPageLoader.prepareDisplay(mViewWidth, mViewHeight);
}
Log.e(TAG, "getPageLoader: "+mPageLoader.toString() );
return mPageLoader;
}

public interface TouchListener {
boolean onTouch();

void center();

void prePage();

void nextPage();

void cancel();
}
}

+ 70
- 0
app/src/main/java/com/yzx/webebook/widget/page/TxtChapter.java Parādīt failu

@@ -0,0 +1,70 @@
package com.yzx.webebook.widget.page;

/**
* Created by newbiechen on 17-7-1.
*/

public class TxtChapter{

//章节所属的小说(网络)
String bookId;
//章节的链接(网络)
String link;

//章节名(共用)
String title;

//章节内容在文章中的起始位置(本地)
long start;
//章节内容在文章中的终止位置(本地)
long end;

public String getBookId() {
return bookId;
}

public void setBookId(String id) {
this.bookId = id;
}

public String getLink() {
return link;
}

public void setLink(String link) {
this.link = link;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public long getStart() {
return start;
}

public void setStart(long start) {
this.start = start;
}

public long getEnd() {
return end;
}

public void setEnd(long end) {
this.end = end;
}

@Override
public String toString() {
return "TxtChapter{" +
"title='" + title + '\'' +
", start=" + start +
", end=" + end +
'}';
}
}

+ 14
- 0
app/src/main/java/com/yzx/webebook/widget/page/TxtPage.java Parādīt failu

@@ -0,0 +1,14 @@
package com.yzx.webebook.widget.page;

import java.util.List;

/**
* Created by newbiechen on 17-7-1.
*/

public class TxtPage {
int position;
String title;
int titleLines; //当前 lines 中为 title 的行数。
List<String> lines;
}

+ 8
- 0
app/src/main/res/anim/rotate_0_to_180.xml Parādīt failu

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate android:fromDegrees="0"
android:toDegrees="180"
android:pivotX="50%"
android:pivotY="50%"
android:duration="300"/>
</set>

+ 8
- 0
app/src/main/res/anim/rotate_180_to_360.xml Parādīt failu

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300">
<rotate android:fromDegrees="180"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"/>
</set>

+ 7
- 0
app/src/main/res/anim/slide_bottom_in.xml Parādīt failu

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="100%"
android:toYDelta="0"
android:duration="400"/>
</set>

+ 7
- 0
app/src/main/res/anim/slide_bottom_out.xml Parādīt failu

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="100%"
android:duration="400"/>
</set>

+ 6
- 0
app/src/main/res/anim/slide_left_in.xml Parādīt failu

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-100%p"
android:toXDelta="0"
android:duration="500"/>
</set>

+ 6
- 0
app/src/main/res/anim/slide_left_out.xml Parādīt failu

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0"
android:toXDelta="-100%p"
android:duration="500"/>
</set>

+ 6
- 0
app/src/main/res/anim/slide_right_in.xml Parādīt failu

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p"
android:toXDelta="0"
android:duration="500"/>
</set>

+ 6
- 0
app/src/main/res/anim/slide_right_out.xml Parādīt failu

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0"
android:toXDelta="100%p"
android:duration="500"/>
</set>

+ 8
- 0
app/src/main/res/anim/slide_top_in.xml Parādīt failu

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0"
android:fromYDelta="-100%"
android:toXDelta="0"
android:toYDelta="0"
android:duration="400"/>
</set>

+ 6
- 0
app/src/main/res/anim/slide_top_out.xml Parādīt failu

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="0"
android:toYDelta="-100%"
android:duration="400"/>
</set>

+ 8
- 0
app/src/main/res/color/selector_btn_read_setting.xml Parādīt failu

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/light_red" android:state_checked="true" />
<item android:color="@color/light_red" android:state_pressed="true" />
<item android:color="@color/light_red" android:state_selected="true" />
<!--必须放在最后一行,否则上面的效果全都不能用-->
<item android:color="@color/white"/>
</selector>

+ 7
- 0
app/src/main/res/color/selector_chapter.xml Parādīt failu

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:color="@color/light_red" android:state_pressed="true" />
<item android:color="@color/light_red" android:state_selected="true" />
<item android:color="@color/nb.text.default"/>
</selector>

+ 25
- 0
app/src/main/res/drawable/seekbar_bg.xml Parādīt failu

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@android:id/background">
<shape>
<size android:height="1dp" />

<gradient
android:endColor="@android:color/darker_gray"
android:startColor="@android:color/darker_gray" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<size android:height="3dp" />

<gradient
android:endColor="@color/orange"
android:startColor="@color/orange" />
</shape>
</clip>
</item>

</layer-list>

+ 8
- 0
app/src/main/res/drawable/seekbar_thumb.xml Parādīt failu

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@mipmap/seekbar_thumb_selected" android:state_pressed="true"/>
<item android:drawable="@mipmap/seekbar_thumb_selected" android:state_focused="true"/>
<item android:drawable="@mipmap/seekbar_thumb_normal"></item>

</selector>

+ 6
- 0
app/src/main/res/drawable/selector_btn_read_setting.xml Parādīt failu

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/shape_btn_read_setting_checked" />
<item android:state_checked="true" android:drawable="@drawable/shape_btn_read_setting_checked" />
<item android:drawable="@drawable/shape_btn_read_setting_normal" />
</selector>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels

Notiek ielāde…
Atcelt
Saglabāt