commit
d5e7395807
@ -1,5 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<resources> |
|
||||||
<string name="app_name">閲讀.D</string> |
|
||||||
<string name="receiving_shared_label">閲讀·D·搜索</string> |
|
||||||
</resources> |
|
@ -1,5 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<resources> |
|
||||||
<string name="app_name">閲讀.D</string> |
|
||||||
<string name="receiving_shared_label">閲讀·D·搜尋</string> |
|
||||||
</resources> |
|
@ -0,0 +1,17 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" |
||||||
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"> |
||||||
|
<head> |
||||||
|
<title>Chapter</title> |
||||||
|
<link href="../Styles/fonts.css" type="text/css" rel="stylesheet"/> |
||||||
|
<link href="../Styles/main.css" type="text/css" rel="stylesheet"/> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div class="logo"> |
||||||
|
<img alt="" class="logo" src="../Images/logo.png"/> |
||||||
|
</div> |
||||||
|
<h2 class="head">{title}</h2> |
||||||
|
{content} |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,22 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" |
||||||
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"> |
||||||
|
<head> |
||||||
|
<title>Cover</title> |
||||||
|
<style type="text/css"> |
||||||
|
.pic { |
||||||
|
margin: 50% 30% 0 30%; |
||||||
|
padding: 2px 2px; |
||||||
|
border: 1px solid #f5f5dc; |
||||||
|
background-color: rgba(250,250,250, 0); |
||||||
|
border-radius: 1px; |
||||||
|
} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body style="text-align: center;"> |
||||||
|
<div class="pic"><img src="../Images/cover.jpg" style="width: 100%; height: auto;"/></div> |
||||||
|
<h1 style="margin-top: 5%; font-size: 110%;">{name}</h1> |
||||||
|
<div class="author" style="margin-top: 0;"><b>{author}</b> <span style="font-size: smaller;">/ 著</span></div> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,267 @@ |
|||||||
|
@charset "utf-8"; |
||||||
|
/*---常用---*/ |
||||||
|
|
||||||
|
@font-face { |
||||||
|
font-family: "zw"; |
||||||
|
src: |
||||||
|
local("宋体"),local("明体"),local("明朝"), |
||||||
|
local("Songti"),local("Songti SC"),local("Songti TC"), /*iOS6+iBooks3*/ |
||||||
|
local("Song S"),local("Song T"),local("STBShusong"),local("TBMincho"),local("HYMyeongJo"), /*Kindle Paperwihite*/ |
||||||
|
local("DK-SONGTI"), |
||||||
|
url(../Fonts/zw.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/zw.ttf), |
||||||
|
url(res:///Data/FONT/zw.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/tt0011m_.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/sdcard/fonts/zw.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/extsd/fonts/zw.ttf), |
||||||
|
url(res:///ebook/fonts/zw.ttf), |
||||||
|
url(res:///ebook/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///fonts/ttf/zw.ttf), |
||||||
|
url(res:///../../media/mmcblk0p1/fonts/zw.ttf), |
||||||
|
url(file:///mnt/us/DK_System/system/fonts/zw.ttf), /*Duokan Old Path*/ |
||||||
|
url(file:///mnt/us/DK_System/xKindle/res/userfonts/zw.ttf), /*Duokan 2012 Path*/ |
||||||
|
url(res:///abook/fonts/zw.ttf), |
||||||
|
url(res:///system/fonts/zw.ttf), |
||||||
|
url(res:///system/media/sdcard/fonts/zw.ttf), |
||||||
|
url(res:///media/fonts/zw.ttf), |
||||||
|
url(res:///sdcard/fonts/zw.ttf), |
||||||
|
url(res:///system/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///mnt/MOVIFAT/font/zw.ttf), |
||||||
|
url(res:///media/flash/fonts/zw.ttf), |
||||||
|
url(res:///media/sd/fonts/zw.ttf), |
||||||
|
url(res:///opt/onyx/arm/lib/fonts/AdobeHeitiStd-Regular.otf), |
||||||
|
url(res:///../../fonts/zw.ttf), |
||||||
|
url(res:///../fonts/zw.ttf), |
||||||
|
url(../../../../../zw.ttf), /*EpubReaderI*/ |
||||||
|
url(res:///mnt/sdcard/fonts/zw.ttf), /*Nook for Android: fonts in TF Card*/ |
||||||
|
url(res:///fonts/zw.ttf), /*ADE1,8, 2.0 Program Path*/ |
||||||
|
url(res:///../../../../Windows/fonts/zw.ttf); |
||||||
|
/*ADE1,8, 2.0 Windows Path*/; |
||||||
|
} |
||||||
|
|
||||||
|
@font-face { |
||||||
|
font-family: "fs"; |
||||||
|
src: |
||||||
|
local("amasis30"),local("仿宋"),local("仿宋_GB2312"), |
||||||
|
local("Yuanti"),local("Yuanti SC"),local("Yuanti TC"), /*iOS6+iBooks3*/ |
||||||
|
local("DK-FANGSONG"), |
||||||
|
url(../Fonts/fs.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/fs.ttf), |
||||||
|
url(res:///Data/FONT/fs.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/tt0011m_.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/sdcard/fonts/fs.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/extsd/fonts/fs.ttf), |
||||||
|
url(res:///ebook/fonts/fs.ttf), |
||||||
|
url(res:///ebook/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///fonts/ttf/fs.ttf), |
||||||
|
url(res:///../../media/mmcblk0p1/fonts/fs.ttf), |
||||||
|
url(file:///mnt/us/DK_System/system/fonts/fs.ttf), /*Duokan Old Path*/ |
||||||
|
url(file:///mnt/us/DK_System/xKindle/res/userfonts/fs.ttf), /*Duokan 2012 Path*/ |
||||||
|
url(res:///abook/fonts/fs.ttf), |
||||||
|
url(res:///system/fonts/fs.ttf), |
||||||
|
url(res:///system/media/sdcard/fonts/fs.ttf), |
||||||
|
url(res:///media/fonts/fs.ttf), |
||||||
|
url(res:///sdcard/fonts/fs.ttf), |
||||||
|
url(res:///system/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///mnt/MOVIFAT/font/fs.ttf), |
||||||
|
url(res:///media/flash/fonts/fs.ttf), |
||||||
|
url(res:///media/sd/fonts/fs.ttf), |
||||||
|
url(res:///opt/onyx/arm/lib/fonts/AdobeHeitiStd-Regular.otf), |
||||||
|
url(res:///../../fonts/fs.ttf), |
||||||
|
url(res:///../fonts/fs.ttf), |
||||||
|
url(../../../../../fs.ttf), /*EpubReaderI*/ |
||||||
|
url(res:///mnt/sdcard/fonts/fs.ttf), /*Nook for Android: fonts in TF Card*/ |
||||||
|
url(res:///fonts/fs.ttf), /*ADE1,8, 2.0 Program Path*/ |
||||||
|
url(res:///../../../../Windows/fonts/fs.ttf); |
||||||
|
/*ADE1,8, 2.0 Windows Path*/; |
||||||
|
} |
||||||
|
|
||||||
|
@font-face { |
||||||
|
font-family: "kt"; |
||||||
|
src: |
||||||
|
local("Caecilia"),local("楷体"),local("楷体_GB2312"), |
||||||
|
local("Kaiti"),local("Kaiti SC"),local("Kaiti TC"), /*iOS6+iBooks3*/ |
||||||
|
local("MKai PRC"),local("MKaiGB18030C-Medium"),local("MKaiGB18030C-Bold"), /*Kindle Paperwihite*/ |
||||||
|
local("DK-KAITI"), |
||||||
|
url(../Fonts/kt.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/kt.ttf), |
||||||
|
url(res:///Data/FONT/kt.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/tt0011m_.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/sdcard/fonts/kt.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/extsd/fonts/kt.ttf), |
||||||
|
url(res:///ebook/fonts/kt.ttf), |
||||||
|
url(res:///ebook/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///fonts/ttf/kt.ttf), |
||||||
|
url(res:///../../media/mmcblk0p1/fonts/kt.ttf), |
||||||
|
url(file:///mnt/us/DK_System/system/fonts/kt.ttf), /*Duokan Old Path*/ |
||||||
|
url(file:///mnt/us/DK_System/xKindle/res/userfonts/kt.ttf), /*Duokan 2012 Path*/ |
||||||
|
url(res:///abook/fonts/kt.ttf), |
||||||
|
url(res:///system/fonts/kt.ttf), |
||||||
|
url(res:///system/media/sdcard/fonts/kt.ttf), |
||||||
|
url(res:///media/fonts/kt.ttf), |
||||||
|
url(res:///sdcard/fonts/kt.ttf), |
||||||
|
url(res:///system/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///mnt/MOVIFAT/font/kt.ttf), |
||||||
|
url(res:///media/flash/fonts/kt.ttf), |
||||||
|
url(res:///media/sd/fonts/kt.ttf), |
||||||
|
url(res:///opt/onyx/arm/lib/fonts/AdobeHeitiStd-Regular.otf), |
||||||
|
url(res:///../../fonts/kt.ttf), |
||||||
|
url(res:///../fonts/kt.ttf), |
||||||
|
url(../../../../../kt.ttf), /*EpubReaderI*/ |
||||||
|
url(res:///mnt/sdcard/fonts/kt.ttf), /*Nook for Android: fonts in TF Card*/ |
||||||
|
url(res:///fonts/kt.ttf), /*ADE1,8, 2.0 Program Path*/ |
||||||
|
url(res:///../../../../Windows/fonts/kt.ttf); |
||||||
|
/*ADE1,8, 2.0 Windows Path*/; |
||||||
|
} |
||||||
|
|
||||||
|
@font-face { |
||||||
|
font-family: "ht"; |
||||||
|
src: |
||||||
|
local("黑体"),local("微软雅黑"), |
||||||
|
local("Heiti"),local("Heiti SC"),local("Heiti TC"), /*iOS6+iBooks3*/ |
||||||
|
local("MYing Hei S"),local("MYing Hei T"),local("TBGothic"), /*Kindle Paperwihite*/ |
||||||
|
local("DK-HEITI"), |
||||||
|
url(../Fonts/ht.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/ht.ttf), |
||||||
|
url(res:///Data/FONT/ht.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/tt0011m_.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/sdcard/fonts/ht.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/extsd/fonts/ht.ttf), |
||||||
|
url(res:///ebook/fonts/ht.ttf), |
||||||
|
url(res:///ebook/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///fonts/ttf/ht.ttf), |
||||||
|
url(res:///../../media/mmcblk0p1/fonts/ht.ttf), |
||||||
|
url(file:///mnt/us/DK_System/system/fonts/ht.ttf), /*Duokan Old Path*/ |
||||||
|
url(file:///mnt/us/DK_System/xKindle/res/userfonts/ht.ttf), /*Duokan 2012 Path*/ |
||||||
|
url(res:///abook/fonts/ht.ttf), |
||||||
|
url(res:///system/fonts/ht.ttf), |
||||||
|
url(res:///system/media/sdcard/fonts/ht.ttf), |
||||||
|
url(res:///media/fonts/ht.ttf), |
||||||
|
url(res:///sdcard/fonts/ht.ttf), |
||||||
|
url(res:///system/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///mnt/MOVIFAT/font/ht.ttf), |
||||||
|
url(res:///media/flash/fonts/ht.ttf), |
||||||
|
url(res:///media/sd/fonts/ht.ttf), |
||||||
|
url(res:///opt/onyx/arm/lib/fonts/AdobeHeitiStd-Regular.otf), |
||||||
|
url(res:///../../fonts/ht.ttf), |
||||||
|
url(res:///../fonts/ht.ttf), |
||||||
|
url(../../../../../ht.ttf), /*EpubReaderI*/ |
||||||
|
url(res:///mnt/sdcard/fonts/ht.ttf), /*Nook for Android: fonts in TF Card*/ |
||||||
|
url(res:///fonts/ht.ttf), /*ADE1,8, 2.0 Program Path*/ |
||||||
|
url(res:///../../../../Windows/fonts/ht.ttf); |
||||||
|
/*ADE1,8, 2.0 Windows Path*/; |
||||||
|
} |
||||||
|
@font-face { |
||||||
|
font-family:"h1"; |
||||||
|
src: |
||||||
|
local("方正兰亭特黑长_GBK"),local("方正兰亭特黑长简体"),local("方正兰亭特黑长繁体"), |
||||||
|
local("LantingTeheichang"), |
||||||
|
local("Yuanti"),local("Yuanti SC"),local("Yuanti TC"), |
||||||
|
local("DK-HEITI"), |
||||||
|
url(../Fonts/h1.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/h1.ttf), |
||||||
|
url(res:///Data/FONT/h1.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/tt0011m_.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/sdcard/fonts/h1.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/extsd/fonts/h1.ttf), |
||||||
|
url(res:///ebook/fonts/h1.ttf), |
||||||
|
url(res:///ebook/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///fonts/ttf/h1.ttf), |
||||||
|
url(res:///../../media/mmcblk0p1/fonts/h1.ttf), |
||||||
|
url(file:///mnt/us/DK_System/system/fonts/h1.ttf), /*Duokan Old Path*/ |
||||||
|
url(file:///mnt/us/DK_System/xKindle/res/userfonts/h1.ttf), /*Duokan 2012 Path*/ |
||||||
|
url(res:///abook/fonts/h1.ttf), |
||||||
|
url(res:///system/fonts/h1.ttf), |
||||||
|
url(res:///system/media/sdcard/fonts/h1.ttf), |
||||||
|
url(res:///media/fonts/h1.ttf), |
||||||
|
url(res:///sdcard/fonts/h1.ttf), |
||||||
|
url(res:///system/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///mnt/MOVIFAT/font/h1.ttf), |
||||||
|
url(res:///media/flash/fonts/h1.ttf), |
||||||
|
url(res:///media/sd/fonts/h1.ttf), |
||||||
|
url(res:///opt/onyx/arm/lib/fonts/AdobeHeitiStd-Regular.otf), |
||||||
|
url(res:///../../fonts/h1.ttf), |
||||||
|
url(res:///../fonts/h1.ttf), |
||||||
|
url(../../../../../h1.ttf), /*EpubReaderI*/ |
||||||
|
url(res:///mnt/sdcard/fonts/h1.ttf), /*Nook for Android: fonts in TF Card*/ |
||||||
|
url(res:///fonts/h1.ttf), /*ADE1,8, 2.0 Program Path*/ |
||||||
|
url(res:///../../../../Windows/fonts/h1.ttf); /*ADE1,8, 2.0 Windows Path*/ |
||||||
|
} |
||||||
|
@font-face { |
||||||
|
font-family:"h2"; |
||||||
|
src: |
||||||
|
local("方正大标宋_GBK"),local("方正大标宋简体"),local("方正大标宋繁体"), |
||||||
|
local("Dabiaosong"), |
||||||
|
local("Heiti"),local("Heiti SC"),local("Heiti TC"), |
||||||
|
local("DK-XIAOBIAOSONG"), |
||||||
|
url(../Fonts/h2.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/h2.ttf), |
||||||
|
url(res:///Data/FONT/h2.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/tt0011m_.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/sdcard/fonts/h2.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/extsd/fonts/h2.ttf), |
||||||
|
url(res:///ebook/fonts/h2.ttf), |
||||||
|
url(res:///ebook/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///fonts/ttf/h2.ttf), |
||||||
|
url(res:///../../media/mmcblk0p1/fonts/h2.ttf), |
||||||
|
url(file:///mnt/us/DK_System/system/fonts/h2.ttf), /*Duokan Old Path*/ |
||||||
|
url(file:///mnt/us/DK_System/xKindle/res/userfonts/h2.ttf), /*Duokan 2012 Path*/ |
||||||
|
url(res:///abook/fonts/h2.ttf), |
||||||
|
url(res:///system/fonts/h2.ttf), |
||||||
|
url(res:///system/media/sdcard/fonts/h2.ttf), |
||||||
|
url(res:///media/fonts/h2.ttf), |
||||||
|
url(res:///sdcard/fonts/h2.ttf), |
||||||
|
url(res:///system/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///mnt/MOVIFAT/font/h2.ttf), |
||||||
|
url(res:///media/flash/fonts/h2.ttf), |
||||||
|
url(res:///media/sd/fonts/h2.ttf), |
||||||
|
url(res:///opt/onyx/arm/lib/fonts/AdobeHeitiStd-Regular.otf), |
||||||
|
url(res:///../../fonts/h2.ttf), |
||||||
|
url(res:///../fonts/h2.ttf), |
||||||
|
url(../../../../../h2.ttf), /*EpubReaderI*/ |
||||||
|
url(res:///mnt/sdcard/fonts/h2.ttf), /*Nook for Android: fonts in TF Card*/ |
||||||
|
url(res:///fonts/h2.ttf), /*ADE1,8, 2.0 Program Path*/ |
||||||
|
url(res:///../../../../Windows/fonts/h2.ttf); /*ADE1,8, 2.0 Windows Path*/ |
||||||
|
} |
||||||
|
|
||||||
|
@font-face { |
||||||
|
font-family:"h3"; |
||||||
|
src: |
||||||
|
local("方正华隶_GBK"),local("方正行黑简体"),local("方正行黑繁体"), |
||||||
|
local("Yuanti"),local("Yuanti SC"),local("Yuanti TC"), |
||||||
|
local("DK-FANGSONG"), |
||||||
|
url(../Fonts/h3.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/h3.ttf), |
||||||
|
url(res:///Data/FONT/h3.ttf), |
||||||
|
url(res:///opt/sony/ebook/FONT/tt0011m_.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/sdcard/fonts/h3.ttf), |
||||||
|
url(res:///ebook/fonts/../../mnt/extsd/fonts/h3.ttf), |
||||||
|
url(res:///ebook/fonts/h3.ttf), |
||||||
|
url(res:///ebook/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///fonts/ttf/h3.ttf), |
||||||
|
url(res:///../../media/mmcblk0p1/fonts/h3.ttf), |
||||||
|
url(file:///mnt/us/DK_System/system/fonts/h3.ttf), /*Duokan Old Path*/ |
||||||
|
url(file:///mnt/us/DK_System/xKindle/res/userfonts/h3.ttf), /*Duokan 2012 Path*/ |
||||||
|
url(res:///abook/fonts/h3.ttf), |
||||||
|
url(res:///system/fonts/h3.ttf), |
||||||
|
url(res:///system/media/sdcard/fonts/h3.ttf), |
||||||
|
url(res:///media/fonts/h3.ttf), |
||||||
|
url(res:///sdcard/fonts/h3.ttf), |
||||||
|
url(res:///system/fonts/DroidSansFallback.ttf), |
||||||
|
url(res:///mnt/MOVIFAT/font/h3.ttf), |
||||||
|
url(res:///media/flash/fonts/h3.ttf), |
||||||
|
url(res:///media/sd/fonts/h3.ttf), |
||||||
|
url(res:///opt/onyx/arm/lib/fonts/AdobeHeitiStd-Regular.otf), |
||||||
|
url(res:///../../fonts/h3.ttf), |
||||||
|
url(res:///../fonts/h3.ttf), |
||||||
|
url(../../../../../h3.ttf), /*EpubReaderI*/ |
||||||
|
url(res:///mnt/sdcard/fonts/h3.ttf), /*Nook for Android: fonts in TF Card*/ |
||||||
|
url(res:///fonts/h3.ttf), /*ADE1,8, 2.0 Program Path*/ |
||||||
|
url(res:///../../../../Windows/fonts/h3.ttf); /*ADE1,8, 2.0 Windows Path*/ |
||||||
|
} |
||||||
|
|
||||||
|
@font-face { |
||||||
|
font-family:"luohua"; |
||||||
|
src:local("汉仪落花体"), |
||||||
|
url("../Fonts/hylh.ttf"); |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" |
||||||
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN"> |
||||||
|
<head> |
||||||
|
<title>Intro</title> |
||||||
|
<link href="../Styles/fonts.css" type="text/css" rel="stylesheet" /> |
||||||
|
<link href="../Styles/main.css" type="text/css" rel="stylesheet" /> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1 class="head" style="margin-bottom:2em;">内容简介</h1>{intro}</body> |
||||||
|
</html> |
After Width: | Height: | Size: 92 KiB |
@ -0,0 +1,551 @@ |
|||||||
|
@charset "utf-8"; |
||||||
|
@import url("../Styles/fonts.css"); |
||||||
|
body { |
||||||
|
padding: 0%; |
||||||
|
margin-top: 0%; |
||||||
|
margin-bottom: 0%; |
||||||
|
margin-left: 0.5%; |
||||||
|
margin-right: 0.5%; |
||||||
|
line-height: 130%; |
||||||
|
text-align: justify; |
||||||
|
font-family: "DK-SONGTI","st","宋体","zw",sans-serif; |
||||||
|
} |
||||||
|
|
||||||
|
p { |
||||||
|
text-align: justify; |
||||||
|
text-indent: 2em; |
||||||
|
line-height: 130%; |
||||||
|
margin-right: 0.5%; |
||||||
|
margin-left: 0.5%; |
||||||
|
font-family: "DK-SONGTI","st","宋体","zw",sans-serif; |
||||||
|
} |
||||||
|
p.kaiti { |
||||||
|
font-family: "DK-KAITI","kt","楷体","zw",serif; |
||||||
|
} |
||||||
|
|
||||||
|
p.fangsong { |
||||||
|
font-family: "DK-FANGSONG","fs","仿宋","zw",serif; |
||||||
|
} |
||||||
|
|
||||||
|
span.xinli { |
||||||
|
font-family: "DK-KAITI","kt","楷体","zw",serif; |
||||||
|
color: #4e753f; |
||||||
|
} |
||||||
|
/** 英文斜体字 **/ |
||||||
|
span.english{ |
||||||
|
font-style: italic; |
||||||
|
} |
||||||
|
div { |
||||||
|
margin: 0px; |
||||||
|
padding: 0px; |
||||||
|
line-height: 120%; |
||||||
|
text-align: justify; |
||||||
|
font-family: "zw"; |
||||||
|
} |
||||||
|
div.foot { |
||||||
|
text-indent: 2em; |
||||||
|
margin: 30% 5% 0 5%; |
||||||
|
padding: 8px 0; |
||||||
|
} |
||||||
|
p.foot { |
||||||
|
font-family: "DK-KAITI","kt","楷体","zw",serif; |
||||||
|
} |
||||||
|
|
||||||
|
/*扉页*/ |
||||||
|
.booksubtitle { |
||||||
|
padding: 10px 0 0px 0; |
||||||
|
text-indent: 0em; |
||||||
|
font-size: 75%; |
||||||
|
font-family: "ht"; |
||||||
|
} |
||||||
|
|
||||||
|
.booktitle { |
||||||
|
padding: 9% 0 0 0; |
||||||
|
font-size: 1.3em; |
||||||
|
font-family: "方正小标宋_GBK","DK-XIAOBIAOSONG"; |
||||||
|
font-weight: normal; |
||||||
|
text-indent: 0em; |
||||||
|
color: #000; |
||||||
|
text-align: center; |
||||||
|
line-height: 1.6; |
||||||
|
} |
||||||
|
|
||||||
|
.booktitle0 { |
||||||
|
font-size: 1.2em; |
||||||
|
font-family: "fs"; |
||||||
|
text-indent: 0em; |
||||||
|
text-align: center; |
||||||
|
line-height: 1.8; |
||||||
|
} |
||||||
|
|
||||||
|
.booktitle1 { |
||||||
|
padding: 0 0 0 0; |
||||||
|
font-size: 0.85em; |
||||||
|
font-family: "fs"; |
||||||
|
text-indent: 0em; |
||||||
|
text-align: center; |
||||||
|
line-height: 1.6; |
||||||
|
} |
||||||
|
|
||||||
|
.bookauthor { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
padding: 5% 5px 0px 5px; |
||||||
|
text-indent: 0em; |
||||||
|
text-align: center; |
||||||
|
color: #000; |
||||||
|
font-size: 90%; |
||||||
|
line-height: 1.3; |
||||||
|
} |
||||||
|
|
||||||
|
.booktranslator { |
||||||
|
padding: 1% 5px 0px 5px; |
||||||
|
text-indent: 0em; |
||||||
|
text-align: center; |
||||||
|
font-size: 85%; |
||||||
|
line-height: 1.3; |
||||||
|
} |
||||||
|
|
||||||
|
.bookpub { |
||||||
|
font-family: "DK-KAITI","kt","楷体","楷体_gb2312"; |
||||||
|
padding: 30% 5px 5px 5px; |
||||||
|
text-indent: 0em; |
||||||
|
color: #000; |
||||||
|
text-align: center; |
||||||
|
font-size: 80%; |
||||||
|
} |
||||||
|
|
||||||
|
/*标题页*/ |
||||||
|
body.head { |
||||||
|
background-repeat:no-repeat no-repeat; |
||||||
|
background-size:160px 229px; |
||||||
|
background-position:bottom right; |
||||||
|
background-attachment:fixed; |
||||||
|
} |
||||||
|
|
||||||
|
body.xhead { |
||||||
|
background-color: #FDF5E6; |
||||||
|
} |
||||||
|
|
||||||
|
h1.head { |
||||||
|
font-family: "DK-HEITI",黑体,sans-serif; |
||||||
|
font-size: 1.2em; |
||||||
|
font-weight: bold; |
||||||
|
color: #311a02; |
||||||
|
text-indent: 0em; |
||||||
|
font-weight: normal; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
padding: auto; |
||||||
|
text-align: center; |
||||||
|
margin-top: -8em; |
||||||
|
} |
||||||
|
|
||||||
|
div.head { |
||||||
|
border: solid 2px #ffffff; |
||||||
|
padding: 2px; |
||||||
|
margin: 2em auto 0.7em auto; |
||||||
|
text-align: center; |
||||||
|
width: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
h1.head b { |
||||||
|
font-family: "方正小标宋_GBK","DK-XIAOBIAOSONG"; |
||||||
|
font-weight: bold; |
||||||
|
font-size: 1.2em; |
||||||
|
text-align: center; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
color: #311a02; |
||||||
|
margin: 0.5em auto; |
||||||
|
line-height: 140%; |
||||||
|
} |
||||||
|
|
||||||
|
div.back { |
||||||
|
text-align: center; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
margin: 4em auto; |
||||||
|
} |
||||||
|
|
||||||
|
img.back { |
||||||
|
width: 70%; |
||||||
|
} |
||||||
|
img.back2 { |
||||||
|
width: 40%; |
||||||
|
margin: 2em 0 0 0; |
||||||
|
} |
||||||
|
/*正文*/ |
||||||
|
/**楷体引文**/ |
||||||
|
.titou { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
} |
||||||
|
.yinwen { |
||||||
|
font-family: "DK-KAITI","kt","楷体","zw",serif; |
||||||
|
margin-left: 2em; |
||||||
|
text-indent: 0em; |
||||||
|
} |
||||||
|
.nicename { |
||||||
|
font-family: "DK-HEITI",黑体,sans-serif; |
||||||
|
font-weight: bold; |
||||||
|
font-size: 0.9em; |
||||||
|
} |
||||||
|
body.head3 { |
||||||
|
background-color: #a7bdcc; |
||||||
|
color: #354f66; |
||||||
|
} |
||||||
|
|
||||||
|
body.head4 { |
||||||
|
background-color: #bfd19b; |
||||||
|
color: #4e753f; |
||||||
|
} |
||||||
|
|
||||||
|
h2.head { |
||||||
|
font-family: "小标宋"; |
||||||
|
text-align: left; |
||||||
|
font-weight: bold; |
||||||
|
font-size: 1.1em; |
||||||
|
margin: -3em 2em 2em 0; |
||||||
|
color: #3f83e8; |
||||||
|
line-height: 140%; |
||||||
|
} |
||||||
|
|
||||||
|
h2.head span { |
||||||
|
font-family: "仿宋"; |
||||||
|
font-size: 0.7em; |
||||||
|
background-color: #3f83e8; |
||||||
|
border-radius: 9px; |
||||||
|
padding: 4px; |
||||||
|
color: #fff; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
div.logo { |
||||||
|
margin: -2em 0% 0 0; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
img.logo { |
||||||
|
width: 40%; |
||||||
|
} |
||||||
|
.imgl { |
||||||
|
/*图片居右*/ |
||||||
|
margin: -8.8em 1em 4em 0em; |
||||||
|
width: 80%; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
h1.head { |
||||||
|
line-height:130%; |
||||||
|
font-size:1.4em; |
||||||
|
text-align: center; |
||||||
|
color: #BA2213; |
||||||
|
font-weight: bold; |
||||||
|
margin-top: 2em; |
||||||
|
margin-bottom: 1em; |
||||||
|
font-family: "方正小标宋_GBK","DK-XIAOBIAOSONG"; |
||||||
|
|
||||||
|
} |
||||||
|
h3 { |
||||||
|
font-family: "DK-HEITI",黑体,sans-serif; |
||||||
|
font-size: 1.1em; |
||||||
|
margin: 1em 0; |
||||||
|
border-left: 1.2em solid #00a1e9; |
||||||
|
line-height: 120%; |
||||||
|
padding-left: 3px; |
||||||
|
color: #00a1e9; |
||||||
|
} |
||||||
|
h4 { |
||||||
|
font-family: "DK-HEITI",黑体,sans-serif; |
||||||
|
font-size: 1.1em; |
||||||
|
text-align: center; |
||||||
|
margin: 1em 0; |
||||||
|
line-height: 120%; |
||||||
|
color: #000; |
||||||
|
} |
||||||
|
h1.post { |
||||||
|
font-family: "方正小标宋_GBK","DK-XIAOBIAOSONG"; |
||||||
|
text-align: center; |
||||||
|
font-size: 1.3em; |
||||||
|
color: #026fca; |
||||||
|
margin: 3em auto 2em auto; |
||||||
|
} |
||||||
|
.banquan { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
text-align: left; |
||||||
|
color: #000; |
||||||
|
font-size:1.1em; |
||||||
|
margin-bottom:1em; |
||||||
|
text-indent: 1em; |
||||||
|
duokan-text-indent: 1em; |
||||||
|
} |
||||||
|
p.post { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
} |
||||||
|
p.zy { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
margin: 1em 0 0 1em; |
||||||
|
padding: 5px 0px 5px 10px; |
||||||
|
text-indent: 0em; |
||||||
|
border-left: 5px solid #a9b5c1; |
||||||
|
} |
||||||
|
.sign { |
||||||
|
font-family: "DK-KAITI","kt","楷体","zw",serif; |
||||||
|
margin: 1em 2px 0 auto; |
||||||
|
text-align: right; |
||||||
|
font-size: 0.8em; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
} |
||||||
|
|
||||||
|
.mark { |
||||||
|
font-family: "DK-HEITI",黑体,sans-serif; |
||||||
|
font-size: 0.9em; |
||||||
|
color: #fff; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
background-color: maroon; |
||||||
|
text-align: center; |
||||||
|
padding: 0px; |
||||||
|
margin: 2em 30%; |
||||||
|
} |
||||||
|
|
||||||
|
/*出版社*/ |
||||||
|
.chubanshe img{ |
||||||
|
width:106px; |
||||||
|
height:28px; |
||||||
|
} |
||||||
|
.chubanshe { |
||||||
|
margin-top:20px; |
||||||
|
} |
||||||
|
.cr { |
||||||
|
font-size:0.9em; |
||||||
|
} |
||||||
|
|
||||||
|
/*多看画廊*/ |
||||||
|
div.duokan-image-single { |
||||||
|
text-align: center; |
||||||
|
margin: 0.5em auto; /*插图盒子上下外边距为0.5em,左右设置auto是为了水平居中这个盒子*/ |
||||||
|
} |
||||||
|
img.picture-80 { |
||||||
|
margin: 0; /*清除img元素的外边距*/ |
||||||
|
width: 80%; /*预览窗口的宽度*/ |
||||||
|
box-shadow: 3px 3px 10px #bfbfbf; /*给图片添加阴影效果*/ |
||||||
|
} |
||||||
|
p.duokan-image-maintitle { |
||||||
|
margin: 1em 0 0; /*图片说明的段间距*/ |
||||||
|
font-family: "楷体"; /*图片说明使用的字体*/ |
||||||
|
font-size: 0.9em; /*字体大小*/ |
||||||
|
text-indent: 0; /*首行缩进为零,当你使用单标签p来指定首行缩进为2em时,记得在需要居中的文本中清除缩进,因为样式是叠加的*/ |
||||||
|
text-align: center; /*图片说明水平居中*/ |
||||||
|
color: #a52a2a; /*字体颜色*/ |
||||||
|
line-height: 1.25em; /*行高,防止有很长的图片说明*/ |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/*制作说明页*/ |
||||||
|
body.description { |
||||||
|
background-image: url(../Images/001.png); |
||||||
|
background-position: bottom center; |
||||||
|
background-repeat: no-repeat; |
||||||
|
background-size: cover; |
||||||
|
padding: 25% 10% 0; |
||||||
|
font-size: 0.9em; |
||||||
|
} |
||||||
|
|
||||||
|
div.description-body { |
||||||
|
width: 55%; |
||||||
|
padding: 2em 1.3em; |
||||||
|
border-radius: 0.5em; |
||||||
|
font-size: 0.9em; |
||||||
|
border-style: solid; |
||||||
|
border-color: #393939; |
||||||
|
border-width: 0.3em; |
||||||
|
border-radius: 5em; |
||||||
|
background-color: #5a5a5a; |
||||||
|
box-shadow: 2px 2px 3px #828281; |
||||||
|
} |
||||||
|
|
||||||
|
h1.description-title { |
||||||
|
text-align: center; |
||||||
|
font-family: "黑体"; |
||||||
|
font-size: 1.2em; |
||||||
|
margin: 0 0 1em 0; |
||||||
|
color: #FF9; |
||||||
|
text-shadow: 1px 1px 0 black; |
||||||
|
} |
||||||
|
|
||||||
|
p.description-text { |
||||||
|
color: #f9ddd2; |
||||||
|
font-family: "准圆"; |
||||||
|
margin: 0; |
||||||
|
text-align: justify; |
||||||
|
text-indent: 0; |
||||||
|
duokan-text-indent: 0; |
||||||
|
} |
||||||
|
|
||||||
|
hr.description-hr { |
||||||
|
margin: 0.5em -1em; |
||||||
|
border-style: dotted; |
||||||
|
border-color: #9C9; |
||||||
|
border-width: 0.05em 0 0 0; |
||||||
|
} |
||||||
|
|
||||||
|
p.tips { |
||||||
|
text-align: justify; |
||||||
|
text-indent: 0; |
||||||
|
duokan-text-indent: 0; |
||||||
|
font-family: "楷体"; |
||||||
|
font-size: 0.7em; |
||||||
|
color: #FFC; |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
|
||||||
|
/*版本说明页*/ |
||||||
|
.ver { |
||||||
|
font-family: "DK-CODE","DK-XIHEITI",细黑体,"xihei",sans-serif; |
||||||
|
font-weight: bold; |
||||||
|
font-size: 100%; |
||||||
|
color: #000; |
||||||
|
margin: 1em 0 1em 0; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
|
||||||
|
.vertitle { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
font-size: 100%; |
||||||
|
text-indent: 0em; |
||||||
|
text-align: left; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
} |
||||||
|
|
||||||
|
.vertxt { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
line-height: 100%; |
||||||
|
font-size: 85%; |
||||||
|
text-indent: 0em; |
||||||
|
text-align: left; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
} |
||||||
|
.verchar { |
||||||
|
font-family: "DK-KAITI","kt","楷体","楷体_gb2312"; |
||||||
|
text-align: left; |
||||||
|
text-indent: 1em; |
||||||
|
duokan-text-indent: 1em; |
||||||
|
margin-bottom: 1em; |
||||||
|
margin-top: 1em; |
||||||
|
} |
||||||
|
.vernote { |
||||||
|
font-family: "DK-FANGSONG",仿宋,"fs","fangsong",sans-serif; |
||||||
|
font-size: 75%; |
||||||
|
color: #686d70; |
||||||
|
text-indent: 0em; |
||||||
|
text-align: left; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
padding-bottom: 15px; |
||||||
|
} |
||||||
|
|
||||||
|
.line { |
||||||
|
border: dotted #A2906A; |
||||||
|
border-width: 1px 0 0 0; |
||||||
|
} |
||||||
|
|
||||||
|
.entry { |
||||||
|
margin-left: 18px; |
||||||
|
font-size: 83%; |
||||||
|
color: #8fe0a3; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
} |
||||||
|
/*版权信息*/ |
||||||
|
.vol { |
||||||
|
text-indent: 0em; |
||||||
|
text-align: center; |
||||||
|
padding: 0.8em; |
||||||
|
margin: 0 auto 3px auto; |
||||||
|
color: #000; |
||||||
|
font-family: "方正小标宋_GBK","DK-XIAOBIAOSONG"; |
||||||
|
font-size: 130%; |
||||||
|
text-shadow: none; |
||||||
|
} |
||||||
|
|
||||||
|
.cp { |
||||||
|
font-family: "DK-CODE","DK-XIHEITI",细黑体,"xihei",sans-serif; |
||||||
|
color: #412938; |
||||||
|
font-size: 70%; |
||||||
|
text-align: left; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
} |
||||||
|
|
||||||
|
.xchar { |
||||||
|
font-family: "DK-KAITI","kt","楷体","楷体_gb2312"; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
} |
||||||
|
/*多看弹注*/ |
||||||
|
sup img { |
||||||
|
line-height: 100%; |
||||||
|
width: auto; |
||||||
|
height: 1.0em; |
||||||
|
margin: 0em; |
||||||
|
padding: 0em; |
||||||
|
vertical-align: text-top; |
||||||
|
} |
||||||
|
|
||||||
|
ol { |
||||||
|
margin-bottom:0; |
||||||
|
padding:0 auto; |
||||||
|
list-style-type: decimal; |
||||||
|
} |
||||||
|
.hr { |
||||||
|
width:50%; |
||||||
|
margin:2em 0 0 0.5em; |
||||||
|
padding:0; |
||||||
|
height:2px; |
||||||
|
background-color: #F3221D; |
||||||
|
} |
||||||
|
|
||||||
|
.duokan-footnote-content{ |
||||||
|
padding:0 auto; |
||||||
|
text-align: left; |
||||||
|
} |
||||||
|
|
||||||
|
.duokan-footnote-item { |
||||||
|
font-family:"DK-XIHEITI",细黑体,"xihei",sans-serif; |
||||||
|
text-align: left; |
||||||
|
font-size: 80%; |
||||||
|
line-height: 100%; |
||||||
|
clear: both; |
||||||
|
color:#000; |
||||||
|
list-style-type:decimal; |
||||||
|
} |
||||||
|
|
||||||
|
li.duokan-footnote-item a { |
||||||
|
font-family:"DK-HEITI"; |
||||||
|
text-align: left; |
||||||
|
} |
||||||
|
a{ |
||||||
|
text-decoration: none; |
||||||
|
color: #222; |
||||||
|
} |
||||||
|
|
||||||
|
a:hover {background: #81caf9} |
||||||
|
a:active {background: yellow} |
||||||
|
.duokan-image-maintitle { |
||||||
|
font-family:"DK-HEITI",黑体,"hei",sans-serif; |
||||||
|
text-align: center; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
font-size:90%; |
||||||
|
color: #1F4150; |
||||||
|
margin-top: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
.duokan-image-subtitle { |
||||||
|
font-family:"DK-XIHEITI",细黑体,"xihei",sans-serif; |
||||||
|
text-align: center; |
||||||
|
text-indent: 0em; |
||||||
|
duokan-text-indent: 0em; |
||||||
|
font-size:70%; |
||||||
|
color: #3A3348; |
||||||
|
margin-top: 1em; |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
package io.legado.app.ui.main.bookshelf |
||||||
|
|
||||||
|
import android.annotation.SuppressLint |
||||||
|
import android.view.MenuItem |
||||||
|
import androidx.fragment.app.activityViewModels |
||||||
|
import androidx.fragment.app.viewModels |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.base.VMBaseFragment |
||||||
|
import io.legado.app.constant.EventBus |
||||||
|
import io.legado.app.constant.PreferKey |
||||||
|
import io.legado.app.data.entities.Book |
||||||
|
import io.legado.app.databinding.DialogBookshelfConfigBinding |
||||||
|
import io.legado.app.databinding.DialogEditTextBinding |
||||||
|
import io.legado.app.help.AppConfig |
||||||
|
import io.legado.app.lib.dialogs.alert |
||||||
|
import io.legado.app.ui.book.arrange.ArrangeBookActivity |
||||||
|
import io.legado.app.ui.book.cache.CacheActivity |
||||||
|
import io.legado.app.ui.book.group.GroupManageDialog |
||||||
|
import io.legado.app.ui.book.local.ImportBookActivity |
||||||
|
import io.legado.app.ui.book.search.SearchActivity |
||||||
|
import io.legado.app.ui.document.FilePicker |
||||||
|
import io.legado.app.ui.document.FilePickerParam |
||||||
|
import io.legado.app.ui.main.MainViewModel |
||||||
|
import io.legado.app.utils.* |
||||||
|
|
||||||
|
abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfViewModel>(layoutId) { |
||||||
|
|
||||||
|
val activityViewModel: MainViewModel by activityViewModels() |
||||||
|
override val viewModel: BookshelfViewModel by viewModels() |
||||||
|
|
||||||
|
private val importBookshelf = registerForActivityResult(FilePicker()) { |
||||||
|
it?.readText(requireContext())?.let { text -> |
||||||
|
viewModel.importBookshelf(text, groupId) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstract val groupId: Long |
||||||
|
abstract val books: List<Book> |
||||||
|
|
||||||
|
abstract fun gotoTop() |
||||||
|
|
||||||
|
override fun onCompatOptionsItemSelected(item: MenuItem) { |
||||||
|
super.onCompatOptionsItemSelected(item) |
||||||
|
when (item.itemId) { |
||||||
|
R.id.menu_search -> startActivity<SearchActivity>() |
||||||
|
R.id.menu_update_toc -> activityViewModel.upToc(books) |
||||||
|
R.id.menu_bookshelf_layout -> configBookshelf() |
||||||
|
R.id.menu_group_manage -> GroupManageDialog() |
||||||
|
.show(childFragmentManager, "groupManageDialog") |
||||||
|
R.id.menu_add_local -> startActivity<ImportBookActivity>() |
||||||
|
R.id.menu_add_url -> addBookByUrl() |
||||||
|
R.id.menu_arrange_bookshelf -> startActivity<ArrangeBookActivity> { |
||||||
|
putExtra("groupId", groupId) |
||||||
|
} |
||||||
|
R.id.menu_download -> startActivity<CacheActivity> { |
||||||
|
putExtra("groupId", groupId) |
||||||
|
} |
||||||
|
R.id.menu_export_bookshelf -> viewModel.exportBookshelf(books) { |
||||||
|
activity?.share(it) |
||||||
|
} |
||||||
|
R.id.menu_import_bookshelf -> importBookshelfAlert(groupId) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressLint("InflateParams") |
||||||
|
fun addBookByUrl() { |
||||||
|
alert(titleResource = R.string.add_book_url) { |
||||||
|
val alertBinding = DialogEditTextBinding.inflate(layoutInflater) |
||||||
|
customView { alertBinding.root } |
||||||
|
okButton { |
||||||
|
alertBinding.editView.text?.toString()?.let { |
||||||
|
viewModel.addBookByUrl(it) |
||||||
|
} |
||||||
|
} |
||||||
|
noButton() |
||||||
|
}.show() |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressLint("InflateParams") |
||||||
|
fun configBookshelf() { |
||||||
|
alert(titleResource = R.string.bookshelf_layout) { |
||||||
|
val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout) |
||||||
|
val bookshelfSort = getPrefInt(PreferKey.bookshelfSort) |
||||||
|
val alertBinding = |
||||||
|
DialogBookshelfConfigBinding.inflate(layoutInflater) |
||||||
|
.apply { |
||||||
|
spGroupStyle.setSelection(AppConfig.bookGroupStyle) |
||||||
|
rgLayout.checkByIndex(bookshelfLayout) |
||||||
|
rgSort.checkByIndex(bookshelfSort) |
||||||
|
} |
||||||
|
customView { alertBinding.root } |
||||||
|
okButton { |
||||||
|
alertBinding.apply { |
||||||
|
var changed = false |
||||||
|
if (AppConfig.bookGroupStyle != spGroupStyle.selectedItemPosition) { |
||||||
|
AppConfig.bookGroupStyle = spGroupStyle.selectedItemPosition |
||||||
|
postEvent(EventBus.NOTIFY_MAIN, false) |
||||||
|
} |
||||||
|
if (bookshelfLayout != rgLayout.getCheckedIndex()) { |
||||||
|
putPrefInt(PreferKey.bookshelfLayout, rgLayout.getCheckedIndex()) |
||||||
|
changed = true |
||||||
|
} |
||||||
|
if (bookshelfSort != rgSort.getCheckedIndex()) { |
||||||
|
putPrefInt(PreferKey.bookshelfSort, rgSort.getCheckedIndex()) |
||||||
|
changed = true |
||||||
|
} |
||||||
|
if (changed) { |
||||||
|
postEvent(EventBus.RECREATE, "") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
noButton() |
||||||
|
}.show() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private fun importBookshelfAlert(groupId: Long) { |
||||||
|
alert(titleResource = R.string.import_bookshelf) { |
||||||
|
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply { |
||||||
|
editView.hint = "url/json" |
||||||
|
} |
||||||
|
customView { alertBinding.root } |
||||||
|
okButton { |
||||||
|
alertBinding.editView.text?.toString()?.let { |
||||||
|
viewModel.importBookshelf(it, groupId) |
||||||
|
} |
||||||
|
} |
||||||
|
noButton() |
||||||
|
neutralButton(R.string.select_file) { |
||||||
|
importBookshelf.launch( |
||||||
|
FilePickerParam( |
||||||
|
mode = FilePicker.FILE, |
||||||
|
allowExtensions = arrayOf("txt", "json") |
||||||
|
) |
||||||
|
) |
||||||
|
} |
||||||
|
}.show() |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,278 +0,0 @@ |
|||||||
package io.legado.app.ui.main.bookshelf |
|
||||||
|
|
||||||
import android.annotation.SuppressLint |
|
||||||
import android.os.Bundle |
|
||||||
import android.view.Menu |
|
||||||
import android.view.MenuItem |
|
||||||
import android.view.View |
|
||||||
import androidx.appcompat.widget.SearchView |
|
||||||
import androidx.fragment.app.Fragment |
|
||||||
import androidx.fragment.app.activityViewModels |
|
||||||
import androidx.fragment.app.viewModels |
|
||||||
import androidx.lifecycle.LiveData |
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter |
|
||||||
import com.google.android.material.tabs.TabLayout |
|
||||||
import com.google.android.material.tabs.TabLayoutMediator |
|
||||||
import io.legado.app.R |
|
||||||
import io.legado.app.base.VMBaseFragment |
|
||||||
import io.legado.app.constant.AppConst |
|
||||||
import io.legado.app.constant.PreferKey |
|
||||||
import io.legado.app.data.appDb |
|
||||||
import io.legado.app.data.entities.BookGroup |
|
||||||
import io.legado.app.databinding.DialogBookshelfConfigBinding |
|
||||||
import io.legado.app.databinding.DialogEditTextBinding |
|
||||||
import io.legado.app.databinding.FragmentBookshelfBinding |
|
||||||
import io.legado.app.help.AppConfig |
|
||||||
import io.legado.app.lib.dialogs.alert |
|
||||||
import io.legado.app.lib.theme.ATH |
|
||||||
import io.legado.app.lib.theme.accentColor |
|
||||||
import io.legado.app.ui.book.arrange.ArrangeBookActivity |
|
||||||
import io.legado.app.ui.book.cache.CacheActivity |
|
||||||
import io.legado.app.ui.book.group.GroupManageDialog |
|
||||||
import io.legado.app.ui.book.local.ImportBookActivity |
|
||||||
import io.legado.app.ui.book.search.SearchActivity |
|
||||||
import io.legado.app.ui.document.FilePicker |
|
||||||
import io.legado.app.ui.document.FilePickerParam |
|
||||||
import io.legado.app.ui.main.MainViewModel |
|
||||||
import io.legado.app.ui.main.bookshelf.books.BooksFragment |
|
||||||
import io.legado.app.utils.* |
|
||||||
import io.legado.app.utils.viewbindingdelegate.viewBinding |
|
||||||
|
|
||||||
/** |
|
||||||
* 书架界面 |
|
||||||
*/ |
|
||||||
class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_bookshelf), |
|
||||||
TabLayout.OnTabSelectedListener, |
|
||||||
SearchView.OnQueryTextListener { |
|
||||||
|
|
||||||
private val binding by viewBinding(FragmentBookshelfBinding::bind) |
|
||||||
override val viewModel: BookshelfViewModel by viewModels() |
|
||||||
private val activityViewModel: MainViewModel by activityViewModels() |
|
||||||
private lateinit var adapter: FragmentStateAdapter |
|
||||||
private lateinit var tabLayout: TabLayout |
|
||||||
private var bookGroupLiveData: LiveData<List<BookGroup>>? = null |
|
||||||
private val bookGroups = mutableListOf<BookGroup>() |
|
||||||
private val fragmentMap = hashMapOf<Long, BooksFragment>() |
|
||||||
private val importBookshelf = registerForActivityResult(FilePicker()) { |
|
||||||
it?.readText(requireContext())?.let { text -> |
|
||||||
viewModel.importBookshelf(text, selectedGroup.groupId) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { |
|
||||||
tabLayout = binding.titleBar.findViewById(R.id.tab_layout) |
|
||||||
setSupportToolbar(binding.titleBar.toolbar) |
|
||||||
initView() |
|
||||||
initBookGroupData() |
|
||||||
} |
|
||||||
|
|
||||||
override fun onCompatCreateOptionsMenu(menu: Menu) { |
|
||||||
menuInflater.inflate(R.menu.main_bookshelf, menu) |
|
||||||
} |
|
||||||
|
|
||||||
override fun onCompatOptionsItemSelected(item: MenuItem) { |
|
||||||
super.onCompatOptionsItemSelected(item) |
|
||||||
when (item.itemId) { |
|
||||||
R.id.menu_search -> startActivity<SearchActivity>() |
|
||||||
R.id.menu_update_toc -> { |
|
||||||
val fragment = fragmentMap[selectedGroup.groupId] |
|
||||||
fragment?.getBooks()?.let { |
|
||||||
activityViewModel.upToc(it) |
|
||||||
} |
|
||||||
} |
|
||||||
R.id.menu_bookshelf_layout -> configBookshelf() |
|
||||||
R.id.menu_group_manage -> GroupManageDialog() |
|
||||||
.show(childFragmentManager, "groupManageDialog") |
|
||||||
R.id.menu_add_local -> startActivity<ImportBookActivity>() |
|
||||||
R.id.menu_add_url -> addBookByUrl() |
|
||||||
R.id.menu_arrange_bookshelf -> startActivity<ArrangeBookActivity> { |
|
||||||
putExtra("groupId", selectedGroup.groupId) |
|
||||||
putExtra("groupName", selectedGroup.groupName) |
|
||||||
} |
|
||||||
R.id.menu_download -> startActivity<CacheActivity> { |
|
||||||
putExtra("groupId", selectedGroup.groupId) |
|
||||||
putExtra("groupName", selectedGroup.groupName) |
|
||||||
} |
|
||||||
R.id.menu_export_bookshelf -> { |
|
||||||
val fragment = fragmentMap[selectedGroup.groupId] |
|
||||||
viewModel.exportBookshelf(fragment?.getBooks()) { |
|
||||||
activity?.share(it) |
|
||||||
} |
|
||||||
} |
|
||||||
R.id.menu_import_bookshelf -> importBookshelfAlert() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private val selectedGroup: BookGroup |
|
||||||
get() = bookGroups[tabLayout.selectedTabPosition] |
|
||||||
|
|
||||||
private fun initView() { |
|
||||||
ATH.applyEdgeEffectColor(binding.viewPagerBookshelf) |
|
||||||
tabLayout.isTabIndicatorFullWidth = false |
|
||||||
tabLayout.tabMode = TabLayout.MODE_SCROLLABLE |
|
||||||
tabLayout.setSelectedTabIndicatorColor(requireContext().accentColor) |
|
||||||
binding.viewPagerBookshelf.offscreenPageLimit = 1 |
|
||||||
adapter = TabFragmentPageAdapter() |
|
||||||
binding.viewPagerBookshelf.adapter = adapter |
|
||||||
TabLayoutMediator(tabLayout, binding.viewPagerBookshelf) { tab, i -> |
|
||||||
tab.text = bookGroups[i].groupName |
|
||||||
}.attach() |
|
||||||
} |
|
||||||
|
|
||||||
private fun initBookGroupData() { |
|
||||||
bookGroupLiveData?.removeObservers(viewLifecycleOwner) |
|
||||||
bookGroupLiveData = appDb.bookGroupDao.liveDataShow().apply { |
|
||||||
observe(viewLifecycleOwner) { |
|
||||||
viewModel.checkGroup(it) |
|
||||||
upGroup(it) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
override fun onQueryTextSubmit(query: String?): Boolean { |
|
||||||
startActivity<SearchActivity> { |
|
||||||
putExtra("key", query) |
|
||||||
} |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String?): Boolean { |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
@Synchronized |
|
||||||
private fun upGroup(data: List<BookGroup>) { |
|
||||||
if (data.isEmpty()) { |
|
||||||
appDb.bookGroupDao.enableGroup(AppConst.bookGroupAllId) |
|
||||||
} else { |
|
||||||
if (data != bookGroups) { |
|
||||||
bookGroups.clear() |
|
||||||
bookGroups.addAll(data) |
|
||||||
adapter.notifyDataSetChanged() |
|
||||||
selectLastTab() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Synchronized |
|
||||||
private fun selectLastTab() { |
|
||||||
tabLayout.removeOnTabSelectedListener(this) |
|
||||||
tabLayout.getTabAt(getPrefInt(PreferKey.saveTabPosition, 0))?.select() |
|
||||||
tabLayout.addOnTabSelectedListener(this) |
|
||||||
} |
|
||||||
|
|
||||||
@SuppressLint("InflateParams") |
|
||||||
private fun configBookshelf() { |
|
||||||
alert(titleResource = R.string.bookshelf_layout) { |
|
||||||
val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout) |
|
||||||
val bookshelfSort = getPrefInt(PreferKey.bookshelfSort) |
|
||||||
val alertBinding = |
|
||||||
DialogBookshelfConfigBinding.inflate(layoutInflater) |
|
||||||
.apply { |
|
||||||
rgLayout.checkByIndex(bookshelfLayout) |
|
||||||
rgSort.checkByIndex(bookshelfSort) |
|
||||||
swShowUnread.isChecked = AppConfig.showUnread |
|
||||||
} |
|
||||||
customView { alertBinding.root } |
|
||||||
okButton { |
|
||||||
alertBinding.apply { |
|
||||||
var changed = false |
|
||||||
if (bookshelfLayout != rgLayout.getCheckedIndex()) { |
|
||||||
putPrefInt(PreferKey.bookshelfLayout, rgLayout.getCheckedIndex()) |
|
||||||
changed = true |
|
||||||
} |
|
||||||
if (bookshelfSort != rgSort.getCheckedIndex()) { |
|
||||||
putPrefInt(PreferKey.bookshelfSort, rgSort.getCheckedIndex()) |
|
||||||
changed = true |
|
||||||
} |
|
||||||
if (AppConfig.showUnread != swShowUnread.isChecked) { |
|
||||||
AppConfig.showUnread = swShowUnread.isChecked |
|
||||||
changed = true |
|
||||||
} |
|
||||||
if (changed) { |
|
||||||
activity?.recreate() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
noButton() |
|
||||||
}.show() |
|
||||||
} |
|
||||||
|
|
||||||
@SuppressLint("InflateParams") |
|
||||||
private fun addBookByUrl() { |
|
||||||
alert(titleResource = R.string.add_book_url) { |
|
||||||
val alertBinding = DialogEditTextBinding.inflate(layoutInflater) |
|
||||||
customView { alertBinding.root } |
|
||||||
okButton { |
|
||||||
alertBinding.editView.text?.toString()?.let { |
|
||||||
viewModel.addBookByUrl(it) |
|
||||||
} |
|
||||||
} |
|
||||||
noButton() |
|
||||||
}.show() |
|
||||||
} |
|
||||||
|
|
||||||
override fun onTabReselected(tab: TabLayout.Tab) { |
|
||||||
fragmentMap[selectedGroup.groupId]?.let { |
|
||||||
toastOnUi("${selectedGroup.groupName}(${it.getBooksCount()})") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
override fun onTabUnselected(tab: TabLayout.Tab) = Unit |
|
||||||
|
|
||||||
override fun onTabSelected(tab: TabLayout.Tab) { |
|
||||||
putPrefInt(PreferKey.saveTabPosition, tab.position) |
|
||||||
} |
|
||||||
|
|
||||||
fun gotoTop() { |
|
||||||
fragmentMap[selectedGroup.groupId]?.gotoTop() |
|
||||||
} |
|
||||||
|
|
||||||
private fun importBookshelfAlert() { |
|
||||||
alert(titleResource = R.string.import_bookshelf) { |
|
||||||
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply { |
|
||||||
editView.hint = "url/json" |
|
||||||
} |
|
||||||
customView { alertBinding.root } |
|
||||||
okButton { |
|
||||||
alertBinding.editView.text?.toString()?.let { |
|
||||||
viewModel.importBookshelf(it, selectedGroup.groupId) |
|
||||||
} |
|
||||||
} |
|
||||||
noButton() |
|
||||||
neutralButton(R.string.select_file) { |
|
||||||
importBookshelf.launch( |
|
||||||
FilePickerParam( |
|
||||||
mode = FilePicker.FILE, |
|
||||||
allowExtensions = arrayOf("txt", "json") |
|
||||||
) |
|
||||||
) |
|
||||||
} |
|
||||||
}.show() |
|
||||||
} |
|
||||||
|
|
||||||
private inner class TabFragmentPageAdapter : |
|
||||||
FragmentStateAdapter(this) { |
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long { |
|
||||||
val group = bookGroups[position] |
|
||||||
return group.groupId |
|
||||||
} |
|
||||||
|
|
||||||
override fun containsItem(itemId: Long): Boolean { |
|
||||||
return fragmentMap.containsKey(itemId) |
|
||||||
} |
|
||||||
|
|
||||||
override fun getItemCount(): Int { |
|
||||||
return bookGroups.size |
|
||||||
} |
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment { |
|
||||||
val group = bookGroups[position] |
|
||||||
val fragment = BooksFragment.newInstance(position, group.groupId) |
|
||||||
fragmentMap[group.groupId] = fragment |
|
||||||
return fragment |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
@ -1,82 +0,0 @@ |
|||||||
package io.legado.app.ui.main.bookshelf |
|
||||||
|
|
||||||
import android.content.Context |
|
||||||
import android.gesture.GestureOverlayView.ORIENTATION_HORIZONTAL |
|
||||||
import android.util.AttributeSet |
|
||||||
import android.view.MotionEvent |
|
||||||
import android.view.View |
|
||||||
import android.view.ViewConfiguration |
|
||||||
import android.widget.LinearLayout |
|
||||||
import androidx.viewpager2.widget.ViewPager2 |
|
||||||
import io.legado.app.R |
|
||||||
import io.legado.app.ui.main.MainActivity |
|
||||||
import io.legado.app.utils.activity |
|
||||||
import kotlin.math.absoluteValue |
|
||||||
import kotlin.math.sign |
|
||||||
|
|
||||||
class RootView : LinearLayout { |
|
||||||
|
|
||||||
constructor(context: Context) : super(context) |
|
||||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) |
|
||||||
|
|
||||||
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop |
|
||||||
private var initialX = 0f |
|
||||||
private var initialY = 0f |
|
||||||
|
|
||||||
private val parentViewPager: ViewPager2? |
|
||||||
get() = (activity as? MainActivity)?.getViewPager() |
|
||||||
private val childViewPager: View? |
|
||||||
get() = findViewById(R.id.view_pager_bookshelf) |
|
||||||
|
|
||||||
private fun canChildScroll(orientation: Int, delta: Float): Boolean { |
|
||||||
val direction = -delta.sign.toInt() |
|
||||||
return when (orientation) { |
|
||||||
0 -> childViewPager?.canScrollHorizontally(direction) ?: false |
|
||||||
1 -> childViewPager?.canScrollVertically(direction) ?: false |
|
||||||
else -> throw IllegalArgumentException() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
override fun onInterceptTouchEvent(e: MotionEvent): Boolean { |
|
||||||
handleInterceptTouchEvent(e) |
|
||||||
return super.onInterceptTouchEvent(e) |
|
||||||
} |
|
||||||
|
|
||||||
private fun handleInterceptTouchEvent(e: MotionEvent) { |
|
||||||
val orientation = parentViewPager?.orientation ?: return |
|
||||||
// Early return if child can't scroll in same direction as parent |
|
||||||
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
if (e.action == MotionEvent.ACTION_DOWN) { |
|
||||||
initialX = e.x |
|
||||||
initialY = e.y |
|
||||||
parent.requestDisallowInterceptTouchEvent(true) |
|
||||||
} else if (e.action == MotionEvent.ACTION_MOVE) { |
|
||||||
val dx = e.x - initialX |
|
||||||
val dy = e.y - initialY |
|
||||||
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL |
|
||||||
// assuming ViewPager2 touch-slop is 2x touch-slop of child |
|
||||||
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f |
|
||||||
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f |
|
||||||
|
|
||||||
if (scaledDx > touchSlop || scaledDy > touchSlop) { |
|
||||||
|
|
||||||
if (isVpHorizontal == (scaledDy > scaledDx)) { |
|
||||||
// Gesture is perpendicular, allow all parents to intercept |
|
||||||
parent.requestDisallowInterceptTouchEvent(false) |
|
||||||
} else { |
|
||||||
// Gesture is parallel, query child if movement in that direction is possible |
|
||||||
if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) { |
|
||||||
// Child can scroll, disallow all parents to intercept |
|
||||||
parent.requestDisallowInterceptTouchEvent(true) |
|
||||||
} else { |
|
||||||
// Child cannot scroll, allow all parents to intercept |
|
||||||
parent.requestDisallowInterceptTouchEvent(false) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,166 @@ |
|||||||
|
@file:Suppress("DEPRECATION") |
||||||
|
|
||||||
|
package io.legado.app.ui.main.bookshelf.style1 |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import android.view.Menu |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.appcompat.widget.SearchView |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import androidx.fragment.app.FragmentManager |
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import com.google.android.material.tabs.TabLayout |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.constant.AppConst |
||||||
|
import io.legado.app.constant.PreferKey |
||||||
|
import io.legado.app.data.appDb |
||||||
|
import io.legado.app.data.entities.Book |
||||||
|
import io.legado.app.data.entities.BookGroup |
||||||
|
import io.legado.app.databinding.FragmentBookshelfBinding |
||||||
|
import io.legado.app.lib.theme.ATH |
||||||
|
import io.legado.app.lib.theme.accentColor |
||||||
|
import io.legado.app.ui.book.search.SearchActivity |
||||||
|
import io.legado.app.ui.main.bookshelf.BaseBookshelfFragment |
||||||
|
import io.legado.app.ui.main.bookshelf.style1.books.BooksFragment |
||||||
|
import io.legado.app.utils.getPrefInt |
||||||
|
import io.legado.app.utils.putPrefInt |
||||||
|
import io.legado.app.utils.startActivity |
||||||
|
import io.legado.app.utils.toastOnUi |
||||||
|
import io.legado.app.utils.viewbindingdelegate.viewBinding |
||||||
|
|
||||||
|
/** |
||||||
|
* 书架界面 |
||||||
|
*/ |
||||||
|
class BookshelfFragment1 : BaseBookshelfFragment(R.layout.fragment_bookshelf), |
||||||
|
TabLayout.OnTabSelectedListener, |
||||||
|
SearchView.OnQueryTextListener { |
||||||
|
|
||||||
|
private val binding by viewBinding(FragmentBookshelfBinding::bind) |
||||||
|
private lateinit var adapter: FragmentStatePagerAdapter |
||||||
|
private lateinit var tabLayout: TabLayout |
||||||
|
private var bookGroupLiveData: LiveData<List<BookGroup>>? = null |
||||||
|
private val bookGroups = mutableListOf<BookGroup>() |
||||||
|
private val fragmentMap = hashMapOf<Long, BooksFragment>() |
||||||
|
|
||||||
|
override val groupId: Long get() = selectedGroup.groupId |
||||||
|
|
||||||
|
override val books: List<Book> |
||||||
|
get() { |
||||||
|
val fragment = fragmentMap[selectedGroup.groupId] |
||||||
|
return fragment?.getBooks() ?: emptyList() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { |
||||||
|
tabLayout = binding.titleBar.findViewById(R.id.tab_layout) |
||||||
|
setSupportToolbar(binding.titleBar.toolbar) |
||||||
|
initView() |
||||||
|
initBookGroupData() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCompatCreateOptionsMenu(menu: Menu) { |
||||||
|
menuInflater.inflate(R.menu.main_bookshelf, menu) |
||||||
|
} |
||||||
|
|
||||||
|
private val selectedGroup: BookGroup |
||||||
|
get() = bookGroups[tabLayout.selectedTabPosition] |
||||||
|
|
||||||
|
private fun initView() { |
||||||
|
ATH.applyEdgeEffectColor(binding.viewPagerBookshelf) |
||||||
|
tabLayout.isTabIndicatorFullWidth = false |
||||||
|
tabLayout.tabMode = TabLayout.MODE_SCROLLABLE |
||||||
|
tabLayout.setSelectedTabIndicatorColor(requireContext().accentColor) |
||||||
|
tabLayout.setupWithViewPager(binding.viewPagerBookshelf) |
||||||
|
binding.viewPagerBookshelf.offscreenPageLimit = 1 |
||||||
|
adapter = TabFragmentPageAdapter(childFragmentManager) |
||||||
|
binding.viewPagerBookshelf.adapter = adapter |
||||||
|
} |
||||||
|
|
||||||
|
private fun initBookGroupData() { |
||||||
|
bookGroupLiveData?.removeObservers(viewLifecycleOwner) |
||||||
|
bookGroupLiveData = appDb.bookGroupDao.liveDataShow().apply { |
||||||
|
observe(viewLifecycleOwner) { |
||||||
|
viewModel.checkGroup(it) |
||||||
|
upGroup(it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean { |
||||||
|
startActivity<SearchActivity> { |
||||||
|
putExtra("key", query) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
private fun upGroup(data: List<BookGroup>) { |
||||||
|
if (data.isEmpty()) { |
||||||
|
appDb.bookGroupDao.enableGroup(AppConst.bookGroupAllId) |
||||||
|
} else { |
||||||
|
if (data != bookGroups) { |
||||||
|
bookGroups.clear() |
||||||
|
bookGroups.addAll(data) |
||||||
|
adapter.notifyDataSetChanged() |
||||||
|
selectLastTab() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
private fun selectLastTab() { |
||||||
|
tabLayout.removeOnTabSelectedListener(this) |
||||||
|
tabLayout.getTabAt(getPrefInt(PreferKey.saveTabPosition, 0))?.select() |
||||||
|
tabLayout.addOnTabSelectedListener(this) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onTabReselected(tab: TabLayout.Tab) { |
||||||
|
fragmentMap[selectedGroup.groupId]?.let { |
||||||
|
toastOnUi("${selectedGroup.groupName}(${it.getBooksCount()})") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onTabUnselected(tab: TabLayout.Tab) = Unit |
||||||
|
|
||||||
|
override fun onTabSelected(tab: TabLayout.Tab) { |
||||||
|
putPrefInt(PreferKey.saveTabPosition, tab.position) |
||||||
|
} |
||||||
|
|
||||||
|
override fun gotoTop() { |
||||||
|
fragmentMap[selectedGroup.groupId]?.gotoTop() |
||||||
|
} |
||||||
|
|
||||||
|
private inner class TabFragmentPageAdapter(fm: FragmentManager) : |
||||||
|
FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { |
||||||
|
|
||||||
|
override fun getPageTitle(position: Int): CharSequence { |
||||||
|
return bookGroups[position].groupName |
||||||
|
} |
||||||
|
|
||||||
|
override fun getItemPosition(`object`: Any): Int { |
||||||
|
return POSITION_NONE |
||||||
|
} |
||||||
|
|
||||||
|
override fun getItem(position: Int): Fragment { |
||||||
|
val group = bookGroups[position] |
||||||
|
return BooksFragment.newInstance(position, group.groupId) |
||||||
|
} |
||||||
|
|
||||||
|
override fun getCount(): Int { |
||||||
|
return bookGroups.size |
||||||
|
} |
||||||
|
|
||||||
|
override fun instantiateItem(container: ViewGroup, position: Int): Any { |
||||||
|
val fragment = super.instantiateItem(container, position) as BooksFragment |
||||||
|
val group = bookGroups[position] |
||||||
|
fragmentMap[group.groupId] = fragment |
||||||
|
return fragment |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package io.legado.app.ui.main.bookshelf.books |
package io.legado.app.ui.main.bookshelf.style1.books |
||||||
|
|
||||||
import android.content.Context |
import android.content.Context |
||||||
import androidx.core.os.bundleOf |
import androidx.core.os.bundleOf |
@ -1,4 +1,4 @@ |
|||||||
package io.legado.app.ui.main.bookshelf.books |
package io.legado.app.ui.main.bookshelf.style1.books |
||||||
|
|
||||||
import android.content.Context |
import android.content.Context |
||||||
import android.os.Bundle |
import android.os.Bundle |
@ -1,4 +1,4 @@ |
|||||||
package io.legado.app.ui.main.bookshelf.books |
package io.legado.app.ui.main.bookshelf.style1.books |
||||||
|
|
||||||
import android.content.Context |
import android.content.Context |
||||||
import android.os.Bundle |
import android.os.Bundle |
@ -0,0 +1,85 @@ |
|||||||
|
package io.legado.app.ui.main.bookshelf.style2 |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import androidx.core.os.bundleOf |
||||||
|
import androidx.recyclerview.widget.DiffUtil |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.legado.app.data.entities.Book |
||||||
|
|
||||||
|
abstract class BaseBooksAdapter<VH : RecyclerView.ViewHolder>( |
||||||
|
val context: Context, |
||||||
|
val callBack: CallBack |
||||||
|
) : RecyclerView.Adapter<VH>() { |
||||||
|
|
||||||
|
val diffItemCallback: DiffUtil.ItemCallback<Book> |
||||||
|
get() = object : DiffUtil.ItemCallback<Book>() { |
||||||
|
|
||||||
|
override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean { |
||||||
|
return oldItem.name == newItem.name |
||||||
|
&& oldItem.author == newItem.author |
||||||
|
} |
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean { |
||||||
|
return when { |
||||||
|
oldItem.durChapterTime != newItem.durChapterTime -> false |
||||||
|
oldItem.name != newItem.name -> false |
||||||
|
oldItem.author != newItem.author -> false |
||||||
|
oldItem.durChapterTitle != newItem.durChapterTitle -> false |
||||||
|
oldItem.latestChapterTitle != newItem.latestChapterTitle -> false |
||||||
|
oldItem.lastCheckCount != newItem.lastCheckCount -> false |
||||||
|
oldItem.getDisplayCover() != newItem.getDisplayCover() -> false |
||||||
|
oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum() -> false |
||||||
|
else -> true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun getChangePayload(oldItem: Book, newItem: Book): Any? { |
||||||
|
val bundle = bundleOf() |
||||||
|
if (oldItem.name != newItem.name) { |
||||||
|
bundle.putString("name", newItem.name) |
||||||
|
} |
||||||
|
if (oldItem.author != newItem.author) { |
||||||
|
bundle.putString("author", newItem.author) |
||||||
|
} |
||||||
|
if (oldItem.durChapterTitle != newItem.durChapterTitle) { |
||||||
|
bundle.putString("dur", newItem.durChapterTitle) |
||||||
|
} |
||||||
|
if (oldItem.latestChapterTitle != newItem.latestChapterTitle) { |
||||||
|
bundle.putString("last", newItem.latestChapterTitle) |
||||||
|
} |
||||||
|
if (oldItem.getDisplayCover() != newItem.getDisplayCover()) { |
||||||
|
bundle.putString("cover", newItem.getDisplayCover()) |
||||||
|
} |
||||||
|
if (oldItem.lastCheckCount != newItem.lastCheckCount |
||||||
|
|| oldItem.durChapterTime != newItem.durChapterTime |
||||||
|
|| oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum() |
||||||
|
|| oldItem.lastCheckCount != newItem.lastCheckCount |
||||||
|
) { |
||||||
|
bundle.putBoolean("refresh", true) |
||||||
|
} |
||||||
|
if (bundle.isEmpty) return null |
||||||
|
return bundle |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun notification(bookUrl: String) { |
||||||
|
for (i in 0 until itemCount) { |
||||||
|
callBack.getItem(i).let { |
||||||
|
if (it is Book && it.bookUrl == bookUrl) { |
||||||
|
notifyItemChanged(i, bundleOf(Pair("refresh", null))) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
interface CallBack { |
||||||
|
fun onItemClick(position: Int) |
||||||
|
fun onItemLongClick(position: Int) |
||||||
|
fun isUpdate(bookUrl: String): Boolean |
||||||
|
fun getItemCount(): Int |
||||||
|
fun getItem(position: Int): Any |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
package io.legado.app.ui.main.bookshelf.style2 |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.os.Bundle |
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.legado.app.constant.BookType |
||||||
|
import io.legado.app.data.entities.Book |
||||||
|
import io.legado.app.data.entities.BookGroup |
||||||
|
import io.legado.app.databinding.ItemBookshelfGridBinding |
||||||
|
import io.legado.app.databinding.ItemBookshelfGridGroupBinding |
||||||
|
import io.legado.app.help.AppConfig |
||||||
|
import io.legado.app.utils.invisible |
||||||
|
import splitties.views.onLongClick |
||||||
|
|
||||||
|
class BooksAdapterGrid(context: Context, callBack: CallBack) : |
||||||
|
BaseBooksAdapter<RecyclerView.ViewHolder>(context, callBack) { |
||||||
|
|
||||||
|
override fun getItemCount(): Int { |
||||||
|
return callBack.getItemCount() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateViewHolder( |
||||||
|
parent: ViewGroup, |
||||||
|
viewType: Int |
||||||
|
): RecyclerView.ViewHolder { |
||||||
|
return BookViewHolder( |
||||||
|
ItemBookshelfGridBinding.inflate(LayoutInflater.from(context), parent, false) |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder( |
||||||
|
holder: RecyclerView.ViewHolder, |
||||||
|
position: Int, |
||||||
|
payloads: MutableList<Any> |
||||||
|
) { |
||||||
|
val bundle = payloads.getOrNull(0) as? Bundle |
||||||
|
when { |
||||||
|
bundle == null -> super.onBindViewHolder(holder, position, payloads) |
||||||
|
holder is BookViewHolder -> onBindBook(holder.binding, position, bundle) |
||||||
|
holder is GroupViewHolder -> onBindGroup(holder.binding, position, bundle) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindGroup(binding: ItemBookshelfGridGroupBinding, position: Int, bundle: Bundle) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is BookGroup) { |
||||||
|
tvName.text = item.groupName |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindBook(binding: ItemBookshelfGridBinding, position: Int, bundle: Bundle) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is Book) { |
||||||
|
bundle.keySet().forEach { |
||||||
|
when (it) { |
||||||
|
"name" -> tvName.text = item.name |
||||||
|
"cover" -> ivCover.load( |
||||||
|
item.getDisplayCover(), |
||||||
|
item.name, |
||||||
|
item.author |
||||||
|
) |
||||||
|
"refresh" -> upRefresh(this, item) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { |
||||||
|
when (holder) { |
||||||
|
is BookViewHolder -> onBindBook(holder.binding, position) |
||||||
|
is GroupViewHolder -> onBindGroup(holder.binding, position) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindGroup(binding: ItemBookshelfGridGroupBinding, position: Int) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is BookGroup) { |
||||||
|
tvName.text = item.groupName |
||||||
|
} |
||||||
|
root.setOnClickListener { |
||||||
|
callBack.onItemClick(position) |
||||||
|
} |
||||||
|
root.onLongClick { |
||||||
|
callBack.onItemLongClick(position) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindBook(binding: ItemBookshelfGridBinding, position: Int) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is Book) { |
||||||
|
tvName.text = item.name |
||||||
|
ivCover.load(item.getDisplayCover(), item.name, item.author) |
||||||
|
upRefresh(this, item) |
||||||
|
} |
||||||
|
root.setOnClickListener { |
||||||
|
callBack.onItemClick(position) |
||||||
|
} |
||||||
|
root.onLongClick { |
||||||
|
callBack.onItemLongClick(position) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun upRefresh(binding: ItemBookshelfGridBinding, item: Book) { |
||||||
|
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { |
||||||
|
binding.bvUnread.invisible() |
||||||
|
binding.rlLoading.show() |
||||||
|
} else { |
||||||
|
binding.rlLoading.hide() |
||||||
|
if (AppConfig.showUnread) { |
||||||
|
binding.bvUnread.setBadgeCount(item.getUnreadChapterNum()) |
||||||
|
binding.bvUnread.setHighlight(item.lastCheckCount > 0) |
||||||
|
} else { |
||||||
|
binding.bvUnread.invisible() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class BookViewHolder(val binding: ItemBookshelfGridBinding) : |
||||||
|
RecyclerView.ViewHolder(binding.root) |
||||||
|
|
||||||
|
class GroupViewHolder(val binding: ItemBookshelfGridGroupBinding) : |
||||||
|
RecyclerView.ViewHolder(binding.root) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
package io.legado.app.ui.main.bookshelf.style2 |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.os.Bundle |
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.legado.app.constant.BookType |
||||||
|
import io.legado.app.data.entities.Book |
||||||
|
import io.legado.app.data.entities.BookGroup |
||||||
|
import io.legado.app.databinding.ItemBookshelfListBinding |
||||||
|
import io.legado.app.databinding.ItemBookshelfListGroupBinding |
||||||
|
import io.legado.app.help.AppConfig |
||||||
|
import io.legado.app.utils.gone |
||||||
|
import io.legado.app.utils.invisible |
||||||
|
import io.legado.app.utils.visible |
||||||
|
import splitties.views.onLongClick |
||||||
|
|
||||||
|
class BooksAdapterList(context: Context, callBack: CallBack) : |
||||||
|
BaseBooksAdapter<RecyclerView.ViewHolder>(context, callBack) { |
||||||
|
|
||||||
|
override fun getItemCount(): Int { |
||||||
|
return callBack.getItemCount() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { |
||||||
|
return BookViewHolder( |
||||||
|
ItemBookshelfListBinding.inflate(LayoutInflater.from(context), parent, false) |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder( |
||||||
|
holder: RecyclerView.ViewHolder, |
||||||
|
position: Int, |
||||||
|
payloads: MutableList<Any> |
||||||
|
) { |
||||||
|
val bundle = payloads.getOrNull(0) as? Bundle |
||||||
|
when { |
||||||
|
bundle == null -> super.onBindViewHolder(holder, position, payloads) |
||||||
|
holder is BookViewHolder -> onBindBook(holder.binding, position, bundle) |
||||||
|
holder is GroupViewHolder -> onBindGroup(holder.binding, position, bundle) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindGroup(binding: ItemBookshelfListGroupBinding, position: Int, bundle: Bundle) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is BookGroup) { |
||||||
|
tvName.text = item.groupName |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindBook(binding: ItemBookshelfListBinding, position: Int, bundle: Bundle) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is Book) { |
||||||
|
tvRead.text = item.durChapterTitle |
||||||
|
tvLast.text = item.latestChapterTitle |
||||||
|
bundle.keySet().forEach { |
||||||
|
when (it) { |
||||||
|
"name" -> tvName.text = item.name |
||||||
|
"author" -> tvAuthor.text = item.author |
||||||
|
"cover" -> ivCover.load( |
||||||
|
item.getDisplayCover(), |
||||||
|
item.name, |
||||||
|
item.author |
||||||
|
) |
||||||
|
"refresh" -> upRefresh(this, item) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { |
||||||
|
when (holder) { |
||||||
|
is BookViewHolder -> onBindBook(holder.binding, position) |
||||||
|
is GroupViewHolder -> onBindGroup(holder.binding, position) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindGroup(binding: ItemBookshelfListGroupBinding, position: Int) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is BookGroup) { |
||||||
|
tvName.text = item.groupName |
||||||
|
flHasNew.gone() |
||||||
|
ivAuthor.gone() |
||||||
|
ivLast.gone() |
||||||
|
ivRead.gone() |
||||||
|
tvAuthor.gone() |
||||||
|
tvLast.gone() |
||||||
|
tvRead.gone() |
||||||
|
} |
||||||
|
root.setOnClickListener { |
||||||
|
callBack.onItemClick(position) |
||||||
|
} |
||||||
|
root.onLongClick { |
||||||
|
callBack.onItemLongClick(position) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun onBindBook(binding: ItemBookshelfListBinding, position: Int) { |
||||||
|
binding.run { |
||||||
|
val item = callBack.getItem(position) |
||||||
|
if (item is Book) { |
||||||
|
tvName.text = item.name |
||||||
|
tvAuthor.text = item.author |
||||||
|
tvRead.text = item.durChapterTitle |
||||||
|
tvLast.text = item.latestChapterTitle |
||||||
|
ivCover.load(item.getDisplayCover(), item.name, item.author) |
||||||
|
flHasNew.visible() |
||||||
|
ivAuthor.visible() |
||||||
|
ivLast.visible() |
||||||
|
ivRead.visible() |
||||||
|
upRefresh(this, item) |
||||||
|
} |
||||||
|
root.setOnClickListener { |
||||||
|
callBack.onItemClick(position) |
||||||
|
} |
||||||
|
root.onLongClick { |
||||||
|
callBack.onItemLongClick(position) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun upRefresh(binding: ItemBookshelfListBinding, item: Book) { |
||||||
|
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { |
||||||
|
binding.bvUnread.invisible() |
||||||
|
binding.rlLoading.show() |
||||||
|
} else { |
||||||
|
binding.rlLoading.hide() |
||||||
|
if (AppConfig.showUnread) { |
||||||
|
binding.bvUnread.setHighlight(item.lastCheckCount > 0) |
||||||
|
binding.bvUnread.setBadgeCount(item.getUnreadChapterNum()) |
||||||
|
} else { |
||||||
|
binding.bvUnread.invisible() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class BookViewHolder(val binding: ItemBookshelfListBinding) : |
||||||
|
RecyclerView.ViewHolder(binding.root) |
||||||
|
|
||||||
|
class GroupViewHolder(val binding: ItemBookshelfListGroupBinding) : |
||||||
|
RecyclerView.ViewHolder(binding.root) |
||||||
|
|
||||||
|
} |
@ -0,0 +1,235 @@ |
|||||||
|
package io.legado.app.ui.main.bookshelf.style2 |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import android.view.Menu |
||||||
|
import android.view.View |
||||||
|
import androidx.appcompat.widget.SearchView |
||||||
|
import androidx.core.view.isGone |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.recyclerview.widget.GridLayoutManager |
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.legado.app.R |
||||||
|
import io.legado.app.constant.AppConst |
||||||
|
import io.legado.app.constant.BookType |
||||||
|
import io.legado.app.constant.EventBus |
||||||
|
import io.legado.app.constant.PreferKey |
||||||
|
import io.legado.app.data.appDb |
||||||
|
import io.legado.app.data.entities.Book |
||||||
|
import io.legado.app.data.entities.BookGroup |
||||||
|
import io.legado.app.databinding.FragmentBookshelf1Binding |
||||||
|
import io.legado.app.help.AppConfig |
||||||
|
import io.legado.app.lib.theme.ATH |
||||||
|
import io.legado.app.lib.theme.accentColor |
||||||
|
import io.legado.app.lib.theme.primaryTextColor |
||||||
|
import io.legado.app.ui.audio.AudioPlayActivity |
||||||
|
import io.legado.app.ui.book.info.BookInfoActivity |
||||||
|
import io.legado.app.ui.book.read.ReadBookActivity |
||||||
|
import io.legado.app.ui.book.search.SearchActivity |
||||||
|
import io.legado.app.ui.main.bookshelf.BaseBookshelfFragment |
||||||
|
import io.legado.app.utils.cnCompare |
||||||
|
import io.legado.app.utils.getPrefInt |
||||||
|
import io.legado.app.utils.observeEvent |
||||||
|
import io.legado.app.utils.startActivity |
||||||
|
import io.legado.app.utils.viewbindingdelegate.viewBinding |
||||||
|
import kotlin.math.max |
||||||
|
|
||||||
|
/** |
||||||
|
* 书架界面 |
||||||
|
*/ |
||||||
|
class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), |
||||||
|
SearchView.OnQueryTextListener, |
||||||
|
BaseBooksAdapter.CallBack { |
||||||
|
|
||||||
|
private val binding by viewBinding(FragmentBookshelf1Binding::bind) |
||||||
|
private lateinit var searchView: SearchView |
||||||
|
private lateinit var booksAdapter: BaseBooksAdapter<*> |
||||||
|
override var groupId = AppConst.bookGroupNoneId |
||||||
|
private var bookGroupLiveData: LiveData<List<BookGroup>>? = null |
||||||
|
private var bookshelfLiveData: LiveData<List<Book>>? = null |
||||||
|
private var bookGroups: List<BookGroup> = emptyList() |
||||||
|
override var books: List<Book> = emptyList() |
||||||
|
|
||||||
|
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { |
||||||
|
searchView = binding.titleBar.findViewById(R.id.search_view) |
||||||
|
setSupportToolbar(binding.titleBar.toolbar) |
||||||
|
initSearchView() |
||||||
|
initRecyclerView() |
||||||
|
initGroupData() |
||||||
|
initBooksData() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCompatCreateOptionsMenu(menu: Menu) { |
||||||
|
menuInflater.inflate(R.menu.main_bookshelf, menu) |
||||||
|
menu.findItem(R.id.menu_search).isVisible = false |
||||||
|
} |
||||||
|
|
||||||
|
private fun initSearchView() { |
||||||
|
ATH.setTint(searchView, primaryTextColor) |
||||||
|
searchView.onActionViewExpanded() |
||||||
|
searchView.isSubmitButtonEnabled = true |
||||||
|
searchView.queryHint = getString(R.string.screen_find) |
||||||
|
searchView.clearFocus() |
||||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { |
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean { |
||||||
|
|
||||||
|
return false |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
private fun initRecyclerView() { |
||||||
|
ATH.applyEdgeEffectColor(binding.rvBookshelf) |
||||||
|
binding.refreshLayout.setColorSchemeColors(accentColor) |
||||||
|
binding.refreshLayout.setOnRefreshListener { |
||||||
|
binding.refreshLayout.isRefreshing = false |
||||||
|
activityViewModel.upToc(books) |
||||||
|
} |
||||||
|
val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout) |
||||||
|
if (bookshelfLayout == 0) { |
||||||
|
binding.rvBookshelf.layoutManager = LinearLayoutManager(context) |
||||||
|
booksAdapter = BooksAdapterList(requireContext(), this) |
||||||
|
} else { |
||||||
|
binding.rvBookshelf.layoutManager = GridLayoutManager(context, bookshelfLayout + 2) |
||||||
|
booksAdapter = BooksAdapterGrid(requireContext(), this) |
||||||
|
} |
||||||
|
binding.rvBookshelf.adapter = booksAdapter |
||||||
|
booksAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { |
||||||
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { |
||||||
|
val layoutManager = binding.rvBookshelf.layoutManager |
||||||
|
if (positionStart == 0 && layoutManager is LinearLayoutManager) { |
||||||
|
val scrollTo = layoutManager.findFirstVisibleItemPosition() - itemCount |
||||||
|
binding.rvBookshelf.scrollToPosition(max(0, scrollTo)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { |
||||||
|
val layoutManager = binding.rvBookshelf.layoutManager |
||||||
|
if (toPosition == 0 && layoutManager is LinearLayoutManager) { |
||||||
|
val scrollTo = layoutManager.findFirstVisibleItemPosition() - itemCount |
||||||
|
binding.rvBookshelf.scrollToPosition(max(0, scrollTo)) |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
private fun initGroupData() { |
||||||
|
bookGroupLiveData?.removeObservers(this) |
||||||
|
bookGroupLiveData = appDb.bookGroupDao.liveDataShow().apply { |
||||||
|
observe(viewLifecycleOwner) { |
||||||
|
if (it != bookGroups) { |
||||||
|
bookGroups = it |
||||||
|
booksAdapter.notifyDataSetChanged() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun initBooksData() { |
||||||
|
bookshelfLiveData?.removeObservers(this) |
||||||
|
bookshelfLiveData = when (groupId) { |
||||||
|
AppConst.bookGroupAllId -> appDb.bookDao.observeAll() |
||||||
|
AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal() |
||||||
|
AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio() |
||||||
|
AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup() |
||||||
|
else -> appDb.bookDao.observeByGroup(groupId) |
||||||
|
}.apply { |
||||||
|
observe(viewLifecycleOwner) { list -> |
||||||
|
binding.tvEmptyMsg.isGone = list.isNotEmpty() |
||||||
|
books = when (getPrefInt(PreferKey.bookshelfSort)) { |
||||||
|
1 -> list.sortedByDescending { |
||||||
|
it.latestChapterTime |
||||||
|
} |
||||||
|
2 -> list.sortedWith { o1, o2 -> |
||||||
|
o1.name.cnCompare(o2.name) |
||||||
|
} |
||||||
|
3 -> list.sortedBy { |
||||||
|
it.order |
||||||
|
} |
||||||
|
else -> list.sortedByDescending { |
||||||
|
it.durChapterTime |
||||||
|
} |
||||||
|
} |
||||||
|
booksAdapter.notifyDataSetChanged() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean { |
||||||
|
startActivity<SearchActivity> { |
||||||
|
putExtra("key", query) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
override fun gotoTop() { |
||||||
|
if (AppConfig.isEInkMode) { |
||||||
|
binding.rvBookshelf.scrollToPosition(0) |
||||||
|
} else { |
||||||
|
binding.rvBookshelf.smoothScrollToPosition(0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onItemClick(position: Int) { |
||||||
|
if (position < bookGroups.size) { |
||||||
|
val bookGroup = bookGroups[position] |
||||||
|
groupId = bookGroup.groupId |
||||||
|
initBooksData() |
||||||
|
} else { |
||||||
|
val book = books[position - bookGroups.size] |
||||||
|
when (book.type) { |
||||||
|
BookType.audio -> |
||||||
|
startActivity<AudioPlayActivity> { |
||||||
|
putExtra("bookUrl", book.bookUrl) |
||||||
|
} |
||||||
|
else -> startActivity<ReadBookActivity> { |
||||||
|
putExtra("bookUrl", book.bookUrl) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onItemLongClick(position: Int) { |
||||||
|
if (position < bookGroups.size) { |
||||||
|
val bookGroup = bookGroups[position] |
||||||
|
|
||||||
|
} else { |
||||||
|
val book = books[position - bookGroups.size] |
||||||
|
startActivity<BookInfoActivity> { |
||||||
|
putExtra("name", book.name) |
||||||
|
putExtra("author", book.author) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun isUpdate(bookUrl: String): Boolean { |
||||||
|
return bookUrl in activityViewModel.updateList |
||||||
|
} |
||||||
|
|
||||||
|
override fun getItemCount(): Int { |
||||||
|
return bookGroups.size + books.size |
||||||
|
} |
||||||
|
|
||||||
|
override fun getItem(position: Int): Any { |
||||||
|
return if (position < bookGroups.size) { |
||||||
|
bookGroups[position] |
||||||
|
} else { |
||||||
|
books[position - bookGroups.size] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun observeLiveBus() { |
||||||
|
super.observeLiveBus() |
||||||
|
observeEvent<String>(EventBus.UP_BOOK) { |
||||||
|
booksAdapter.notification(it) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package io.legado.app.ui.widget.recycler |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.util.AttributeSet |
||||||
|
import android.view.MotionEvent |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import kotlin.math.abs |
||||||
|
|
||||||
|
class RecyclerViewAtPager2 : RecyclerView { |
||||||
|
|
||||||
|
constructor(context: Context) : super(context) |
||||||
|
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) |
||||||
|
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( |
||||||
|
context, |
||||||
|
attrs, |
||||||
|
defStyleAttr |
||||||
|
) |
||||||
|
|
||||||
|
private var startX = 0 |
||||||
|
private var startY = 0 |
||||||
|
|
||||||
|
override fun dispatchTouchEvent(ev: MotionEvent): Boolean { |
||||||
|
when (ev.action) { |
||||||
|
MotionEvent.ACTION_DOWN -> { |
||||||
|
startX = ev.x.toInt() |
||||||
|
startY = ev.y.toInt() |
||||||
|
parent.requestDisallowInterceptTouchEvent(true) |
||||||
|
} |
||||||
|
MotionEvent.ACTION_MOVE -> { |
||||||
|
val endX = ev.x.toInt() |
||||||
|
val endY = ev.y.toInt() |
||||||
|
val disX = abs(endX - startX) |
||||||
|
val disY = abs(endY - startY) |
||||||
|
if (disX > disY) { |
||||||
|
if (disX > 50) { |
||||||
|
parent.requestDisallowInterceptTouchEvent(false) |
||||||
|
} |
||||||
|
} else { |
||||||
|
parent.requestDisallowInterceptTouchEvent(true) |
||||||
|
} |
||||||
|
} |
||||||
|
MotionEvent.ACTION_UP, |
||||||
|
MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent(false) |
||||||
|
} |
||||||
|
return super.dispatchTouchEvent(ev) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,6 +1,6 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
<item android:state_enabled="false" android:color="@color/ate_button_disabled_light" /> |
<item android:state_enabled="false" android:color="@color/disabled" /> |
||||||
<item android:state_pressed="true" android:color="@color/btn_bg_press" /> |
<item android:state_pressed="true" android:color="@color/btn_bg_press" /> |
||||||
<item android:state_focused="true" android:color="@color/btn_bg_press_2" /> |
<item android:state_focused="true" android:color="@color/btn_bg_press_2" /> |
||||||
<item android:color="@color/primaryText" /> |
<item android:color="@color/primaryText" /> |
||||||
|
@ -0,0 +1,48 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
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:attachToActivity="false" |
||||||
|
app:contentLayout="@layout/view_search" |
||||||
|
app:layout_constraintTop_toTopOf="parent" |
||||||
|
app:title="@string/bookshelf" /> |
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
||||||
|
android:id="@+id/refresh_layout" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="0dp" |
||||||
|
app:layout_constraintTop_toBottomOf="@+id/title_bar" |
||||||
|
app:layout_constraintBottom_toBottomOf="parent"> |
||||||
|
|
||||||
|
<io.legado.app.ui.widget.recycler.RecyclerViewAtPager2 |
||||||
|
android:id="@+id/rv_bookshelf" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
tools:listitem="@layout/item_bookshelf_list" /> |
||||||
|
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/tv_empty_msg" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_gravity="center" |
||||||
|
android:layout_margin="16dp" |
||||||
|
android:gravity="center" |
||||||
|
android:text="@string/bookshelf_empty" |
||||||
|
android:visibility="gone" |
||||||
|
app:layout_constraintTop_toBottomOf="@+id/title_bar" |
||||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||||
|
app:layout_constraintLeft_toLeftOf="parent" |
||||||
|
app:layout_constraintRight_toRightOf="parent" |
||||||
|
tools:text="TextView" /> |
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue