Merge remote-tracking branch 'origin/master'

# Conflicts:
#	app/src/main/java/io/legado/app/App.kt
#	app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
#	app/src/main/java/io/legado/app/ui/widget/anima/RotateLoading.kt
pull/32/head
Invinciblelee 6 years ago
commit c13e6356a2
  1. 5
      app/build.gradle
  2. 14
      app/src/main/AndroidManifest.xml
  3. BIN
      app/src/main/ic_launcher-web.png
  4. 18
      app/src/main/java/io/legado/app/App.kt
  5. 16
      app/src/main/java/io/legado/app/data/AppDatabase.kt
  6. 3
      app/src/main/java/io/legado/app/data/dao/BookDao.kt
  7. 18
      app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt
  8. 8
      app/src/main/java/io/legado/app/data/entities/Book.kt
  9. 52
      app/src/main/java/io/legado/app/data/entities/SearchBook.kt
  10. 2
      app/src/main/java/io/legado/app/model/analyzeRule/analyzeJsoup.kt
  11. 2
      app/src/main/java/io/legado/app/model/content/BookList.kt
  12. 12
      app/src/main/java/io/legado/app/service/CheckSourceService.kt
  13. 12
      app/src/main/java/io/legado/app/service/DownloadService.kt
  14. 12
      app/src/main/java/io/legado/app/service/ReadAloudService.kt
  15. 12
      app/src/main/java/io/legado/app/service/ShareService.kt
  16. 12
      app/src/main/java/io/legado/app/service/UpdateService.kt
  17. 12
      app/src/main/java/io/legado/app/service/WebService.kt
  18. 33
      app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
  19. 35
      app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
  20. 95
      app/src/main/java/io/legado/app/ui/about/DonateActivity.kt
  21. 19
      app/src/main/java/io/legado/app/ui/bookinfo/BookInfoActivity.kt
  22. 18
      app/src/main/java/io/legado/app/ui/bookinfo/BookInfoEditActivity.kt
  23. 8
      app/src/main/java/io/legado/app/ui/bookinfo/BookInfoViewModel.kt
  24. 39
      app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt
  25. 87
      app/src/main/java/io/legado/app/ui/bookshelf/BookshelfAdapter.kt
  26. 17
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  27. 22
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
  28. 34
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt
  29. 14
      app/src/main/java/io/legado/app/ui/main/booksource/BookSourceFragment.kt
  30. 20
      app/src/main/java/io/legado/app/ui/main/myconfig/PreferenceFragment.kt
  31. 3
      app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
  32. 50
      app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
  33. 0
      app/src/main/java/io/legado/app/ui/widget/anima/RotateLoading.kt
  34. 150
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/ExplosionAnimator.kt
  35. 191
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/ExplosionField.kt
  36. 8
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/OnAnimatorListener.kt
  37. 76
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/Utils.kt
  38. 31
      app/src/main/java/io/legado/app/utils/GsonExtensions.kt
  39. 41
      app/src/main/res/layout/activity_about.xml
  40. 7
      app/src/main/res/layout/activity_book_info.xml
  41. 7
      app/src/main/res/layout/activity_book_info_edit.xml
  42. 5
      app/src/main/res/layout/activity_bookshelf.xml
  43. 234
      app/src/main/res/layout/activity_donate.xml
  44. 1
      app/src/main/res/layout/activity_qrcode_capture.xml
  45. 15
      app/src/main/res/layout/activity_welcome.xml
  46. 9
      app/src/main/res/layout/fragment_bookshelf.xml
  47. 2
      app/src/main/res/layout/item_bookshelf_list.xml
  48. 1
      app/src/main/res/layout/item_bookshelf_list_add.xml
  49. 9
      app/src/main/res/menu/about.xml
  50. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  51. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
  52. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  53. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  54. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
  55. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  56. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  57. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
  58. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  59. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  60. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
  61. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  62. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  63. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
  64. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  65. 2
      app/src/main/res/values/ic_launcher_background.xml
  66. 2
      app/src/main/res/values/strings.xml
  67. 51
      app/src/main/res/xml/about.xml
  68. 10
      app/src/main/res/xml/pref_config_theme.xml
  69. 4
      build.gradle

@ -81,7 +81,7 @@ dependencies {
//androidX
implementation 'androidx.core:core-ktx:1.2.0-alpha01'
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
implementation 'androidx.preference:preference:1.1.0-alpha05'
implementation 'androidx.preference:preference:1.1.0-beta01'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
implementation 'com.google.android.material:material:1.1.0-alpha07'
@ -119,6 +119,9 @@ dependencies {
implementation 'net.minidev:json-smart:2.3'
// implementation 'com.jayway.jsonpath:json-path:2.4.0'
//JS
implementation 'com.github.gedoor:rhino-android:1.3'
//Retrofit
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.0'

@ -27,24 +27,32 @@
android:theme="@style/AppTheme.Light"
tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute">
<activity
android:name=".ui.main.MainActivity"
android:name=".ui.welcome.WelcomeActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ui.main.MainActivity" />
<activity android:name=".ui.replacerule.ReplaceRuleActivity"/>
<activity android:name=".ui.search.SearchActivity"/>
<activity android:name=".ui.about.AboutActivity"/>
<activity android:name=".help.permission.PermissionActivity"
android:theme="@style/Activity.Permission"/>
<activity android:name=".ui.config.ConfigActivity"/>
<activity android:name=".ui.sourceedit.SourceEditActivity"/>
<activity android:name=".ui.bookshelf.BookshelfActivity" />
<activity android:name=".ui.qrcode.QrCodeActivity" />
<activity android:name=".ui.about.DonateActivity" />
<activity android:name=".ui.bookinfo.BookInfoActivity" />
<activity android:name=".ui.bookinfo.BookInfoEditActivity" />
<service android:name=".service.CheckSourceService" />
<service android:name=".service.DownloadService" />
<service android:name=".service.ReadAloudService" />
<service android:name=".service.UpdateService" />
<service android:name=".service.WebService" />
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 200 KiB

@ -4,9 +4,6 @@ import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatDelegate
@ -17,7 +14,6 @@ import io.legado.app.constant.AppConst.channelIdWeb
import io.legado.app.data.AppDatabase
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getCompatDrawable
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefInt
import java.util.*
@ -35,29 +31,25 @@ class App : Application() {
private set
}
private var versionCode = 0
var versionCode = 0
var versionName = ""
override fun onCreate() {
super.onCreate()
INSTANCE = this
db = AppDatabase.createDatabase(INSTANCE)
versionCode = try {
packageManager.getPackageInfo(packageName, 0).versionCode
} catch (e: PackageManager.NameNotFoundException) {
0
packageManager.getPackageInfo(packageName, 0)?.let {
versionCode = it.versionCode
versionName = it.versionName
}
if (!ThemeStore.isConfigured(this, versionCode)) upThemeStore()
initNightTheme()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) createChannelId()
LiveEventBus.get()
.config()
.supportBroadcast(this)
.lifecycleObserverAlwaysActive(true)
.autoClear(false)
}
fun initNightTheme() {

@ -6,15 +6,12 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.data.dao.BookDao
import io.legado.app.data.dao.BookGroupDao
import io.legado.app.data.dao.BookSourceDao
import io.legado.app.data.dao.ReplaceRuleDao
import io.legado.app.data.dao.*
import io.legado.app.data.entities.*
@Database(
entities = [Book::class, BookGroup::class, BookSource::class, Chapter::class, ReplaceRule::class],
entities = [Book::class, BookGroup::class, BookSource::class, Chapter::class, ReplaceRule::class, SearchBook::class, SearchKeyword::class],
version = 1,
exportSchema = true
)
@ -35,7 +32,11 @@ abstract class AppDatabase : RoomDatabase() {
}
fun createDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, DATABASE_NAME)
return Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
DATABASE_NAME
)
// .addMigrations(MIGRATION_1_2)
// .addMigrations(MIGRATION_2_3)
// .addMigrations(MIGRATION_3_4)
@ -50,5 +51,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun bookGroupDao(): BookGroupDao
abstract fun bookSourceDao(): BookSourceDao
abstract fun replaceRuleDao(): ReplaceRuleDao
abstract fun searchBookDao(): SearchBookDao
abstract fun searchKeywordDao(): SearchKeywordDao
}

@ -11,6 +11,9 @@ import io.legado.app.data.entities.Book
@Dao
interface BookDao {
@Query("SELECT * FROM books")
fun observeAll(): DataSource.Factory<Int, Book>
@Query("SELECT * FROM books WHERE `group` = :group")
fun observeByGroup(group: Int): DataSource.Factory<Int, Book>

@ -0,0 +1,18 @@
package io.legado.app.data.dao
import androidx.paging.DataSource
import androidx.room.Dao
import androidx.room.Query
import io.legado.app.data.entities.SearchBook
@Dao
interface SearchBookDao {
@Query("SELECT * FROM searchBooks")
fun observeAll(): DataSource.Factory<Int, SearchBook>
@Query("SELECT * FROM searchBooks where time >= :time")
fun observeNew(time: Long): DataSource.Factory<Int, SearchBook>
}

@ -18,7 +18,7 @@ data class Book(
@PrimaryKey
var descUrl: String = "", // 详情页Url(本地书源存储完整文件路径)
var tocUrl: String = "", // 目录页Url (toc=table of Contents)
var sourceId: Int = -1, // 书源规则id(默认-1,表示本地书籍)
var origin: String = "", // 书源规则id(默认-1,表示本地书籍)
var name: String? = null, // 书籍名称(书源获取)
var customName: String? = null, // 书籍名称(用户修改)
var author: String? = null, // 作者名称(书源获取)
@ -49,6 +49,10 @@ data class Book(
@IgnoredOnParcel
@Ignore
override var variableMap: HashMap<String, String>? = null
get() = run {
initVariableMap()
return field
}
fun getUnreadChapterNum() = Math.max(totalChapterNum - durChapterIndex - 1, 0)
@ -72,5 +76,7 @@ data class Book(
override fun putVariable(key: String, value: String) {
initVariableMap()
variableMap?.put(key, value)
variable = Gson().toJson(variableMap)
}
}

@ -1,3 +1,53 @@
package io.legado.app.data.entities
class SearchBook
import android.os.Parcelable
import android.text.TextUtils
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.Index
import androidx.room.PrimaryKey
import com.google.gson.Gson
import io.legado.app.utils.fromJson
import kotlinx.android.parcel.IgnoredOnParcel
import kotlinx.android.parcel.Parcelize
@Parcelize
@Entity(tableName = "searchBooks", indices = [(Index(value = ["descUrl"], unique = true))])
data class SearchBook(
@PrimaryKey
var descUrl: String = "",
var origin: String = "", // 书源规则id(默认-1,表示本地书籍)
var name: String? = null,
var author: String? = null,
var tag: String? = null,
var coverUrl: String? = null,
var description: String? = null,
var latestChapterTitle: String? = null,
var time: Long = 0L,
var variable: String? = null
) : Parcelable, BaseBook {
@IgnoredOnParcel
@Ignore
override var variableMap: HashMap<String, String>? = null
get() = run {
initVariableMap()
return field
}
private fun initVariableMap() {
if (variableMap == null) {
variableMap = if (TextUtils.isEmpty(variable)) {
HashMap()
} else {
Gson().fromJson<HashMap<String, String>>(variable!!)
}
}
}
override fun putVariable(key: String, value: String) {
initVariableMap()
variableMap?.put(key, value)
variable = Gson().toJson(variableMap)
}
}

@ -0,0 +1,2 @@
package io.legado.app.model.analyzeRule

@ -0,0 +1,2 @@
package io.legado.app.model.content

@ -0,0 +1,12 @@
package io.legado.app.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
class CheckSourceService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}

@ -0,0 +1,12 @@
package io.legado.app.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
class DownloadService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}

@ -0,0 +1,12 @@
package io.legado.app.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
class ReadAloudService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}

@ -0,0 +1,12 @@
package io.legado.app.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
class ShareService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}

@ -0,0 +1,12 @@
package io.legado.app.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
class UpdateService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}

@ -0,0 +1,12 @@
package io.legado.app.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
class WebService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}

@ -1,10 +1,15 @@
package io.legado.app.ui.about
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.AndroidViewModel
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.utils.getViewModel
import org.jetbrains.anko.toast
class AboutActivity : BaseActivity<AndroidViewModel>() {
override val viewModel: AndroidViewModel
@ -13,7 +18,35 @@ class AboutActivity : BaseActivity<AndroidViewModel>() {
get() = R.layout.activity_about
override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) {
val fTag = "aboutFragment"
var aboutFragment = supportFragmentManager.findFragmentByTag(fTag)
if (aboutFragment == null) aboutFragment = AboutFragment()
supportFragmentManager.beginTransaction()
.replace(R.id.fl_fragment, aboutFragment, fTag)
.commit()
}
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.about, menu)
return super.onCompatCreateOptionsMenu(menu)
}
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_scoring -> {
openIntent(Intent.ACTION_VIEW, "market://details?id=$packageName")
}
}
return super.onCompatOptionsItemSelected(item)
}
private fun openIntent(intentName: String, address: String) {
try {
val intent = Intent(intentName)
intent.data = Uri.parse(address)
startActivity(intent)
} catch (e: Exception) {
toast(R.string.can_not_open)
}
}
}

@ -0,0 +1,35 @@
package io.legado.app.ui.about
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.utils.toast
class AboutFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.about)
findPreference<Preference>("version")?.summary = App.INSTANCE.versionName
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
}
return super.onPreferenceTreeClick(preference)
}
private fun openIntent(intentName: String, address: String) {
try {
val intent = Intent(intentName)
intent.data = Uri.parse(address)
startActivity(intent)
} catch (e: Exception) {
toast(R.string.can_not_open)
}
}
}

@ -0,0 +1,95 @@
package io.legado.app.ui.about
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.AndroidViewModel
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_donate.*
import kotlinx.android.synthetic.main.view_title_bar.*
import org.jetbrains.anko.toast
import java.net.URLEncoder
/**
* Created by GKF on 2018/1/13.
* 捐赠页面
*/
class DonateActivity : BaseActivity<AndroidViewModel>() {
override val viewModel: AndroidViewModel
get() = getViewModel(AndroidViewModel::class.java)
override val layoutID: Int
get() = R.layout.activity_donate
override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) {
setSupportActionBar(toolbar)
vw_zfb_tz.setOnClickListener { aliDonate(this) }
cv_wx_gzh.setOnClickListener {
val clipboard = this.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
val clipData = ClipData.newPlainText(null, "开源阅读软件")
clipboard?.let {
clipboard.primaryClip = clipData
toast(R.string.copy_complete)
}
}
vw_zfb_hb.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/zfbhbrwm.png") }
vw_zfb_rwm.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/zfbskrwm.jpg") }
vw_wx_rwm.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/wxskrwm.jpg") }
vw_qq_rwm.setOnClickListener { openActionViewIntent("https://gedoor.github.io/MyBookshelf/qqskrwm.jpg") }
vw_zfb_hb_ssm.setOnClickListener { getZfbHb(this) }
}
private fun getZfbHb(context: Context) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
val clipData = ClipData.newPlainText(null, "537954522")
clipboard?.let {
clipboard.primaryClip = clipData
Toast.makeText(context, "高级功能已开启\n红包码已复制\n支付宝首页搜索“537954522” 立即领红包", Toast.LENGTH_LONG)
.show()
}
try {
val packageManager = context.applicationContext.packageManager
val intent = packageManager.getLaunchIntentForPackage("com.eg.android.AlipayGphone")!!
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
} finally {
}
}
private fun openActionViewIntent(address: String) {
try {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(address)
startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, R.string.can_not_open, Toast.LENGTH_SHORT).show()
}
}
private fun aliDonate(context: Context) {
try {
val qrCode = URLEncoder.encode("tsx06677nwdk3javroq4ef0", "utf-8")
val aliPayQr =
"alipayqr://platformapi/startapp?saId=10000007&qrcode=https://qr.alipay.com/$qrCode"
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(aliPayQr))
context.startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

@ -0,0 +1,19 @@
package io.legado.app.ui.bookinfo
import android.os.Bundle
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.utils.getViewModel
class BookInfoActivity : BaseActivity<BookInfoViewModel>() {
override val viewModel: BookInfoViewModel
get() = getViewModel(BookInfoViewModel::class.java)
override val layoutID: Int
get() = R.layout.activity_book_info
override fun onViewModelCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) {
}
}

@ -0,0 +1,18 @@
package io.legado.app.ui.bookinfo
import android.os.Bundle
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.utils.getViewModel
class BookInfoEditActivity : BaseActivity<BookInfoViewModel>() {
override val viewModel: BookInfoViewModel
get() = getViewModel(BookInfoViewModel::class.java)
override val layoutID: Int
get() = R.layout.activity_book_info_edit
override fun onViewModelCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) {
}
}

@ -0,0 +1,8 @@
package io.legado.app.ui.bookinfo
import android.app.Application
import io.legado.app.base.BaseViewModel
class BookInfoViewModel(application: Application) : BaseViewModel(application) {
}

@ -1,8 +1,17 @@
package io.legado.app.ui.bookshelf
import android.os.Bundle
import android.widget.LinearLayout
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.data.entities.Book
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_bookshelf.*
@ -12,6 +21,9 @@ class BookshelfActivity : BaseActivity<BookshelfViewModel>() {
override val layoutID: Int
get() = R.layout.activity_bookshelf
private lateinit var bookshelfAdapter: BookshelfAdapter
private var bookshelfLiveData: LiveData<PagedList<Book>>? = null
override fun onViewModelCreated(viewModel: BookshelfViewModel, savedInstanceState: Bundle?) {
if (viewModel.bookGroup == null) {
viewModel.bookGroup = intent.getParcelableExtra("data")
@ -19,6 +31,33 @@ class BookshelfActivity : BaseActivity<BookshelfViewModel>() {
viewModel.bookGroup?.let {
title_bar.title = it.groupName
}
initRecyclerView()
upRecyclerData()
}
private fun initRecyclerView() {
rv_bookshelf.layoutManager = LinearLayoutManager(this)
rv_bookshelf.addItemDecoration(DividerItemDecoration(this, LinearLayout.VERTICAL))
bookshelfAdapter = BookshelfAdapter()
rv_bookshelf.adapter = bookshelfAdapter
}
private fun upRecyclerData() {
viewModel.bookGroup?.let {
when (it.groupId) {
-1 -> {
bookshelfLiveData?.removeObservers(this)
bookshelfLiveData =
LivePagedListBuilder(App.db.bookDao().observeAll(), 10).build()
bookshelfLiveData?.observe(
this,
Observer { pageList -> bookshelfAdapter.submitList(pageList) })
}
else -> {
}
}
}
}
}

@ -0,0 +1,87 @@
package io.legado.app.ui.bookshelf
import android.text.TextUtils.isEmpty
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import io.legado.app.R
import io.legado.app.data.entities.Book
import io.legado.app.lib.theme.ThemeStore
import kotlinx.android.synthetic.main.item_bookshelf_list.view.*
import kotlinx.android.synthetic.main.item_relace_rule.view.tv_name
import java.io.File
class BookshelfAdapter : PagedListAdapter<Book, BookshelfAdapter.MyViewHolder>(DIFF_CALLBACK) {
companion object {
@JvmField
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Book>() {
override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean =
oldItem.descUrl == newItem.descUrl
override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean =
oldItem.descUrl == newItem.descUrl
&& oldItem.durChapterTitle == newItem.durChapterTitle
&& oldItem.latestChapterTitle == newItem.latestChapterTitle
}
}
var callBack: CallBack? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_bookshelf_list,
parent,
false
)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
currentList?.get(position)?.let {
holder.bind(it, callBack)
}
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {
itemView.setBackgroundColor(ThemeStore.backgroundColor(itemView.context))
}
fun bind(book: Book, callBack: CallBack?) = with(itemView) {
tv_name.text = book.name
tv_author.text = book.author
tv_read.text = book.durChapterTitle
tv_last.text = book.latestChapterTitle
val cover = if (isEmpty(book.customCoverUrl)) book.coverUrl else book.customCoverUrl
cover?.let {
if (it.startsWith("http")) {
Glide.with(itemView).load(it)
.placeholder(R.drawable.img_cover_default)
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(iv_cover)
} else {
Glide.with(itemView).load(File(it))
.placeholder(R.drawable.img_cover_default)
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(iv_cover)
}
}
itemView.setOnClickListener { callBack?.open(book) }
}
}
interface CallBack {
fun open(book: Book)
fun search()
}
}

@ -9,12 +9,12 @@ import androidx.lifecycle.Observer
import androidx.viewpager.widget.ViewPager
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.jeremyliao.liveeventbus.LiveEventBus
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.constant.Bus
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.help.storage.Restore
import io.legado.app.lib.theme.Selector
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.ui.main.bookshelf.BookshelfFragment
@ -24,6 +24,9 @@ import io.legado.app.ui.main.myconfig.MyConfigFragment
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : BaseActivity<MainViewModel>(), BottomNavigationView.OnNavigationItemSelectedListener,
ViewPager.OnPageChangeListener {
@ -45,6 +48,7 @@ class MainActivity : BaseActivity<MainViewModel>(), BottomNavigationView.OnNavig
view_pager_main.adapter = TabFragmentPageAdapter(supportFragmentManager)
view_pager_main.addOnPageChangeListener(this)
bottom_navigation_view.setOnNavigationItemSelectedListener(this)
importYueDu()
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
@ -58,10 +62,17 @@ class MainActivity : BaseActivity<MainViewModel>(), BottomNavigationView.OnNavig
}
private fun importYueDu() {
PermissionsCompat.Builder(this)
GlobalScope.launch {
if (App.db.bookDao().allBookCount == 0) {
GlobalScope.launch(Dispatchers.Main) {
PermissionsCompat.Builder(this@MainActivity)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted { Restore.importYueDuData(this) }.request()
.onGranted { viewModel.restore() }
.request()
}
}
}
}
override fun onPageScrollStateChanged(state: Int) {

@ -1,22 +1,16 @@
package io.legado.app.ui.main
import android.app.Application
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import kotlinx.coroutines.async
import org.jetbrains.anko.toast
import androidx.lifecycle.AndroidViewModel
import io.legado.app.help.storage.Restore
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainViewModel(application: Application) : BaseViewModel(application) {
class MainViewModel(application: Application) : AndroidViewModel(application) {
fun test() {
launchOnUI({
val result = async {
"结果"
fun restore() {
GlobalScope.launch {
Restore.importYueDuData(getApplication())
}
// App.INSTANCE.toast("result: $result")
})
}
}

@ -16,7 +16,7 @@ import kotlinx.android.synthetic.main.item_bookshelf_list.view.*
import kotlinx.android.synthetic.main.item_relace_rule.view.tv_name
import java.io.File
class BookshelfAdapter : PagedListAdapter<Book, RecyclerView.ViewHolder>(DIFF_CALLBACK) {
class BookshelfAdapter : PagedListAdapter<Book, BookshelfAdapter.MyViewHolder>(DIFF_CALLBACK) {
companion object {
@JvmField
@ -33,43 +33,15 @@ class BookshelfAdapter : PagedListAdapter<Book, RecyclerView.ViewHolder>(DIFF_CA
var callBack: CallBack? = null
override fun getItemViewType(position: Int): Int {
if (position == itemCount - 1) {
return 1
}
return super.getItemViewType(position)
}
override fun getItemCount(): Int {
return super.getItemCount() + 1
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == 1) {
return AddViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_bookshelf_list_add, parent, false))
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_bookshelf_list, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is MyViewHolder -> {
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
currentList?.get(position)?.let {
holder.bind(it, callBack)
}
}
is AddViewHolder -> holder.bind(callBack)
}
}
class AddViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(callBack: CallBack?) = with(itemView) {
setOnClickListener { callBack?.search() }
}
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {

@ -55,7 +55,12 @@ class BookSourceFragment : BaseFragment(R.layout.fragment_book_source), BookSour
private fun initRecyclerView() {
recycler_view.layoutManager = LinearLayoutManager(context)
recycler_view.addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL))
recycler_view.addItemDecoration(
DividerItemDecoration(
context,
LinearLayoutManager.VERTICAL
)
)
adapter = BookSourceAdapter()
adapter.callBack = this
recycler_view.adapter = adapter
@ -76,9 +81,10 @@ class BookSourceFragment : BaseFragment(R.layout.fragment_book_source), BookSour
private fun initDataObserve(searchKey: String = "") {
bookSourceLiveDate?.removeObservers(viewLifecycleOwner)
val dataFactory =
if (searchKey.isEmpty()) App.db.bookSourceDao().observeAll() else App.db.bookSourceDao().observeSearch(
searchKey
)
if (searchKey.isEmpty())
App.db.bookSourceDao().observeAll()
else
App.db.bookSourceDao().observeSearch(searchKey)
bookSourceLiveDate = LivePagedListBuilder(dataFactory, 30).build()
bookSourceLiveDate?.observe(viewLifecycleOwner, Observer { adapter.submitList(it) })
}

@ -7,11 +7,13 @@ import androidx.preference.PreferenceFragmentCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.ui.about.AboutActivity
import io.legado.app.ui.about.DonateActivity
import io.legado.app.ui.config.ConfigActivity
import io.legado.app.ui.config.ConfigViewModel
import org.jetbrains.anko.startActivity
class PreferenceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener {
class PreferenceFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_main)
@ -38,22 +40,18 @@ class PreferenceFragment : PreferenceFragmentCompat(), SharedPreferences.OnShare
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
preference?.let {
when (preference.key) {
"setting" -> {
requireContext().startActivity<ConfigActivity>(
"setting" -> context?.startActivity<ConfigActivity>(
Pair("configType", ConfigViewModel.TYPE_CONFIG)
)
}
"web_dav_setting" -> {
requireContext().startActivity<ConfigActivity>(
"web_dav_setting" -> context?.startActivity<ConfigActivity>(
Pair("configType", ConfigViewModel.TYPE_WEB_DAV_CONFIG)
)
}
"theme_setting" -> {
requireContext().startActivity<ConfigActivity>(
"theme_setting" -> context?.startActivity<ConfigActivity>(
Pair("configType", ConfigViewModel.TYPE_THEME_CONFIG)
)
}
"about" -> requireContext().startActivity<AboutActivity>()
"donate" -> context?.startActivity<DonateActivity>()
"about" -> context?.startActivity<AboutActivity>()
else -> null
}
}
return super.onPreferenceTreeClick(preference)

@ -5,6 +5,7 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.lifecycle.AndroidViewModel
import cn.bingoogolapple.qrcode.core.QRCodeView
import io.legado.app.R
@ -66,7 +67,7 @@ class QrCodeActivity : BaseActivity<AndroidViewModel>(), QRCodeView.Delegate {
.addPermissions(*Permissions.Group.CAMERA)
.rationale(R.string.qr_per)
.onGranted {
zxingview.startCamera() // 打开后置摄像头开始预览,但是并未开始识别
zxingview.visibility = View.VISIBLE
zxingview.startSpotAndShowRect() // 显示扫描框,并开始识别
}.request()
}

@ -0,0 +1,50 @@
package io.legado.app.ui.welcome
import android.animation.Animator
import android.animation.ValueAnimator
import android.os.Bundle
import androidx.lifecycle.AndroidViewModel
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.ui.main.MainActivity
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_welcome.*
import org.jetbrains.anko.startActivity
class WelcomeActivity : BaseActivity<AndroidViewModel>() {
override val viewModel: AndroidViewModel
get() = getViewModel(AndroidViewModel::class.java)
override val layoutID: Int
get() = R.layout.activity_welcome
override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) {
iv_bg.setColorFilter(ThemeStore.accentColor(this))
val welAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(800)
welAnimator.startDelay = 100
welAnimator.addUpdateListener { animation ->
val alpha = animation.animatedValue as Float
iv_bg.alpha = alpha
}
welAnimator.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
startActivity<MainActivity>()
finish()
}
override fun onAnimationEnd(animation: Animator) {
}
override fun onAnimationCancel(animation: Animator) {
}
override fun onAnimationRepeat(animation: Animator) {
}
})
welAnimator.start()
}
}

@ -0,0 +1,150 @@
/*
* Copyright (C) 2015 tyrantgit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.legado.app.ui.widget.anima.explosion_field
import android.animation.ValueAnimator
import android.graphics.*
import android.view.View
import android.view.animation.AccelerateInterpolator
import java.util.*
class ExplosionAnimator(private val mContainer: View, bitmap: Bitmap, bound: Rect) :
ValueAnimator() {
private val mPaint: Paint
private val mParticles: Array<Particle?>
private val mBound: Rect
init {
mPaint = Paint()
mBound = Rect(bound)
val partLen = 15
mParticles = arrayOfNulls(partLen * partLen)
val random = Random(System.currentTimeMillis())
val w = bitmap.width / (partLen + 2)
val h = bitmap.height / (partLen + 2)
for (i in 0 until partLen) {
for (j in 0 until partLen) {
mParticles[i * partLen + j] =
generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random)
}
}
setFloatValues(0f, END_VALUE)
interpolator = DEFAULT_INTERPOLATOR
duration = DEFAULT_DURATION
}
private fun generateParticle(color: Int, random: Random): Particle {
val particle = Particle()
particle.color = color
particle.radius = V
if (random.nextFloat() < 0.2f) {
particle.baseRadius = V + (X - V) * random.nextFloat()
} else {
particle.baseRadius = W + (V - W) * random.nextFloat()
}
val nextFloat = random.nextFloat()
particle.top = mBound.height() * (0.18f * random.nextFloat() + 0.2f)
particle.top =
if (nextFloat < 0.2f) particle.top else particle.top + particle.top * 0.2f * random.nextFloat()
particle.bottom = mBound.height() * (random.nextFloat() - 0.5f) * 1.8f
var f =
if (nextFloat < 0.2f) particle.bottom else if (nextFloat < 0.8f) particle.bottom * 0.6f else particle.bottom * 0.3f
particle.bottom = f
particle.mag = 4.0f * particle.top / particle.bottom
particle.neg = -particle.mag / particle.bottom
f = mBound.centerX() + Y * (random.nextFloat() - 0.5f)
particle.baseCx = f
particle.cx = f
f = mBound.centerY() + Y * (random.nextFloat() - 0.5f)
particle.baseCy = f
particle.cy = f
particle.life = END_VALUE / 10 * random.nextFloat()
particle.overflow = 0.4f * random.nextFloat()
particle.alpha = 1f
return particle
}
fun draw(canvas: Canvas): Boolean {
if (!isStarted) {
return false
}
for (particle in mParticles) {
particle?.let {
particle.advance(animatedValue as Float)
if (particle.alpha > 0f) {
mPaint.color = particle.color
mPaint.alpha = (Color.alpha(particle.color) * particle.alpha).toInt()
canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint)
}
}
}
mContainer.invalidate()
return true
}
override fun start() {
super.start()
mContainer.invalidate(mBound)
}
private inner class Particle {
internal var alpha: Float = 0.toFloat()
internal var color: Int = 0
internal var cx: Float = 0.toFloat()
internal var cy: Float = 0.toFloat()
internal var radius: Float = 0.toFloat()
internal var baseCx: Float = 0.toFloat()
internal var baseCy: Float = 0.toFloat()
internal var baseRadius: Float = 0.toFloat()
internal var top: Float = 0.toFloat()
internal var bottom: Float = 0.toFloat()
internal var mag: Float = 0.toFloat()
internal var neg: Float = 0.toFloat()
internal var life: Float = 0.toFloat()
internal var overflow: Float = 0.toFloat()
fun advance(factor: Float) {
var f = 0f
var normalization = factor / END_VALUE
if (normalization < life || normalization > 1f - overflow) {
alpha = 0f
return
}
normalization = (normalization - life) / (1f - life - overflow)
val f2 = normalization * END_VALUE
if (normalization >= 0.7f) {
f = (normalization - 0.7f) / 0.3f
}
alpha = 1f - f
f = bottom * f2
cx = baseCx + f
cy = (baseCy - this.neg * Math.pow(f.toDouble(), 2.0)).toFloat() - f * mag
radius = V + (baseRadius - V) * f2
}
}
companion object {
internal var DEFAULT_DURATION: Long = 0x400
private val DEFAULT_INTERPOLATOR = AccelerateInterpolator(0.6f)
private val END_VALUE = 1.4f
private val X = Utils.dp2Px(5).toFloat()
private val Y = Utils.dp2Px(20).toFloat()
private val V = Utils.dp2Px(2).toFloat()
private val W = Utils.dp2Px(1).toFloat()
}
}

@ -0,0 +1,191 @@
/*
* Copyright (C) 2015 tyrantgit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.legado.app.ui.widget.anima.explosion_field
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.Activity
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.media.MediaPlayer
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.Window
import java.util.*
class ExplosionField : View {
private var customDuration = ExplosionAnimator.DEFAULT_DURATION
private var idPlayAnimationEffect = 0
private var mZAnimatorListener: OnAnimatorListener? = null
private var mOnClickListener: View.OnClickListener? = null
private val mExplosions = ArrayList<ExplosionAnimator>()
private val mExpandInset = IntArray(2)
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init()
}
private fun init() {
Arrays.fill(mExpandInset, Utils.dp2Px(32))
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
for (explosion in mExplosions) {
explosion.draw(canvas)
}
}
fun playSoundAnimationEffect(id: Int) {
this.idPlayAnimationEffect = id
}
fun setCustomDuration(customDuration: Long) {
this.customDuration = customDuration
}
fun addActionEvent(ievents: OnAnimatorListener) {
this.mZAnimatorListener = ievents
}
fun expandExplosionBound(dx: Int, dy: Int) {
mExpandInset[0] = dx
mExpandInset[1] = dy
}
@JvmOverloads
fun explode(bitmap: Bitmap?, bound: Rect, startDelay: Long, view: View? = null) {
val currentDuration = customDuration
val explosion = ExplosionAnimator(this, bitmap!!, bound)
explosion.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
mExplosions.remove(animation)
if (view != null) {
view.scaleX = 1f
view.scaleY = 1f
view.alpha = 1f
view.setOnClickListener(mOnClickListener)//set event
}
}
})
explosion.startDelay = startDelay
explosion.duration = currentDuration
mExplosions.add(explosion)
explosion.start()
}
@JvmOverloads
fun explode(view: View, restartState: Boolean? = false) {
val r = Rect()
view.getGlobalVisibleRect(r)
val location = IntArray(2)
getLocationOnScreen(location)
// getLocationInWindow(location);
// view.getLocationInWindow(location);
r.offset(-location[0], -location[1])
r.inset(-mExpandInset[0], -mExpandInset[1])
val startDelay = 100
val animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150)
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
internal var random = Random()
override fun onAnimationUpdate(animation: ValueAnimator) {
view.translationX = (random.nextFloat() - 0.5f) * view.width.toFloat() * 0.05f
view.translationY = (random.nextFloat() - 0.5f) * view.height.toFloat() * 0.05f
}
})
animator.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animator: Animator) {
if (idPlayAnimationEffect != 0)
MediaPlayer.create(context, idPlayAnimationEffect).start()
}
override fun onAnimationEnd(animator: Animator) {
if (mZAnimatorListener != null) {
mZAnimatorListener!!.onAnimationEnd(animator, this@ExplosionField)
}
}
override fun onAnimationCancel(animator: Animator) {
Log.i("PRUEBA", "CANCEL")
}
override fun onAnimationRepeat(animator: Animator) {
Log.i("PRUEBA", "REPEAT")
}
})
animator.start()
view.animate().setDuration(150).setStartDelay(startDelay.toLong()).scaleX(0f).scaleY(0f)
.alpha(0f).start()
if (restartState!!)
explode(Utils.createBitmapFromView(view), r, startDelay.toLong(), view)
else
explode(Utils.createBitmapFromView(view), r, startDelay.toLong())
}
fun clear() {
mExplosions.clear()
invalidate()
}
override fun setOnClickListener(mOnClickListener: View.OnClickListener?) {
this.mOnClickListener = mOnClickListener
}
companion object {
fun attach2Window(activity: Activity): ExplosionField {
val rootView = activity.findViewById<View>(Window.ID_ANDROID_CONTENT) as ViewGroup
val explosionField = ExplosionField(activity)
rootView.addView(
explosionField, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
)
return explosionField
}
}
}

@ -0,0 +1,8 @@
package io.legado.app.ui.widget.anima.explosion_field
import android.animation.Animator
import android.view.View
interface OnAnimatorListener {
fun onAnimationEnd(animator: Animator, view: View)
}

@ -0,0 +1,76 @@
/*
* Copyright (C) 2015 tyrantgit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.legado.app.ui.widget.anima.explosion_field
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.view.View
import android.widget.ImageView
object Utils {
private val DENSITY = Resources.getSystem().displayMetrics.density
private val sCanvas = Canvas()
fun dp2Px(dp: Int): Int {
return Math.round(dp * DENSITY)
}
fun createBitmapFromView(view: View): Bitmap? {
if (view is ImageView) {
val drawable = view.drawable
if (drawable != null && drawable is BitmapDrawable) {
return drawable.bitmap
}
}
view.clearFocus()
val bitmap = createBitmapSafely(
view.width,
view.height, Bitmap.Config.ARGB_8888, 1
)
if (bitmap != null) {
synchronized(sCanvas) {
val canvas = sCanvas
canvas.setBitmap(bitmap)
view.draw(canvas)
canvas.setBitmap(null)
}
}
return bitmap
}
fun createBitmapSafely(
width: Int,
height: Int,
config: Bitmap.Config,
retryCount: Int
): Bitmap? {
try {
return Bitmap.createBitmap(width, height, config)
} catch (e: OutOfMemoryError) {
e.printStackTrace()
if (retryCount > 0) {
System.gc()
return createBitmapSafely(width, height, config, retryCount - 1)
}
return null
}
}
}

@ -1,5 +1,34 @@
package io.legado.app.utils
import android.text.TextUtils
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonParser
inline fun <reified T> Gson.fromJson(json: String) = fromJson(json, T::class.java)
inline fun <reified T> Gson.fromJson(json: String): T = fromJson(json, T::class.java)
inline fun <reified T> Gson.arrayFromJson(json: String): ArrayList<T>? = kotlin.run {
var result: ArrayList<T>? = null
if (!TextUtils.isEmpty(json)) {
val gson = GsonBuilder().create()
try {
val parser = JsonParser()
val jArray = parser.parse(json).asJsonArray
jArray?.let {
result = java.util.ArrayList()
for (obj in it) {
try {
val cse = gson.fromJson(obj, T::class.java)
result?.add(cse)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
return result
}

@ -1,14 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="io.legado.app.ui.about.AboutActivity">
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/about"/>
app:title="@string/about" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/app_name"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_app_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/about_description" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<FrameLayout
android:id="@+id/fl_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>

@ -9,4 +9,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_bookshelf"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

@ -0,0 +1,234 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/donate" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="你的支持是我更新的动力" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cv_wx_gzh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="请关注微信公众号 开源阅读软件 点击复制" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_zfb_hb_ssm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="支付宝红包搜索码 537954522 点击复制" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_zfb_tz"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="支付宝收款,支持红包,点击直接跳转支付宝" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_zfb_hb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="支付宝红包二维码" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_zfb_rwm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="支付宝收款二维码" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_wx_rwm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="微信赞赏码" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_qq_rwm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="QQ收款码" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
</LinearLayout>

@ -8,6 +8,7 @@
android:id="@+id/zxingview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
app:qrcv_animTime="1000"
app:qrcv_barCodeTipText="将条码放入框内,即可自动扫描"
app:qrcv_barcodeRectHeight="120dp"

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_bg"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="@drawable/image_welcome"
android:scaleType="fitCenter"
android:contentDescription="@string/welcome" />
</LinearLayout>

@ -10,12 +10,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:attachToActivity="false"
app:title="@string/bookshelf"/>
app:title="@string/bookshelf" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_book_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_recent_reading"
@ -23,8 +23,9 @@
android:layout_height="wrap_content"
android:background="@color/background"
android:elevation="3dp"
android:focusable="true"
android:padding="5dp"
android:text="@string/recent_reading"/>
android:text="@string/recent_reading" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
@ -35,7 +36,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_bookshelf"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>

@ -40,7 +40,7 @@
android:includeFontPadding="false"
tools:ignore="RtlHardcoded" />
<com.victor.loading.rotate.RotateLoading
<io.legado.app.ui.widget.anima.RotateLoading
android:id="@+id/rl_loading"
android:layout_width="26dp"
android:layout_height="26dp"

@ -2,6 +2,7 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/bg_ib_pre"
android:gravity="center"
android:text="@string/add"
android:textSize="24sp" />

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_scoring"
android:title="@string/scoring"
app:showAsAction="always" />
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 979 B

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 48 KiB

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#EAE7EE</color>
<color name="ic_launcher_background">#EC5436</color>
</resources>

@ -55,7 +55,7 @@
<string name="exit">退出</string>
<string name="exit_no_save">还未保存,是否继续编辑</string>
<string name="read_style">阅读样式设置</string>
<string name="version">版本</string>
<string name="local">本地</string>
<string name="search">搜索</string>
<string name="net_error_10001">没有网络</string>

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.preference.Preference
android:key="isNightTheme"
android:title="开发人员"
android:summary="gedoor, Invinciblelee, atbest, Antecer, mabDc"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="version"
android:title="@string/version"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="update_log"
android:title="@string/update_log"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="check_update"
android:title="@string/check_update"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="qq"
android:title="@string/join_qq_group"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="mail"
android:title="@string/send_mail"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="git"
android:title="@string/git_hub"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="home_page"
android:title="@string/home_page"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="disclaimer"
android:title="@string/disclaimer"
app:iconSpaceReserved="false" />
</androidx.preference.PreferenceScreen>

@ -6,8 +6,7 @@
<io.legado.app.lib.theme.prefs.ATESwitchPreference
android:defaultValue="false"
android:key="transparentStatusBar"
android:summary="@string/status_bar_immersion"
android:title="@string/immersion_status_bar"
android:title="@string/status_bar_immersion"
app:iconSpaceReserved="false" />
<io.legado.app.lib.theme.prefs.ATESwitchPreference
@ -17,13 +16,6 @@
android:title="@string/navigation_bar_color_change"
app:iconSpaceReserved="false" />
<io.legado.app.lib.theme.prefs.ATESwitchPreference
android:defaultValue="true"
android:key="behaviorMain"
android:summary="@string/behavior_main_s"
android:title="@string/behavior_main_t"
app:iconSpaceReserved="false" />
<io.legado.app.lib.theme.prefs.ATESwitchPreference
android:defaultValue="false"
android:key="E-InkMode"

@ -5,7 +5,7 @@ buildscript {
repositories {
google()
jcenter()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
@ -19,7 +19,7 @@ allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}

Loading…
Cancel
Save