1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-12 11:40:27 +08:00
新增
1. hotmanga 部分支持,访问 https://api.2024manga.com/api/v3/system/network2?format=json&platform=3 获得相应 API (fix #108)
修复
1. 书架无法应用自定义 API
优化
1. 非一级页面禁用抽屉 (fix #99)
升级
1. androidx.constraintlayout -> 2.2.0
This commit is contained in:
源文雨
2025-01-10 01:30:14 +09:00
parent f91f7be6ba
commit 01da01c4fb
23 changed files with 122 additions and 68 deletions

View File

@@ -8,7 +8,6 @@
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">

View File

@@ -6,6 +6,7 @@
<w>comandy</w>
<w>downloaders</w>
<w>grps</w>
<w>hotmanga</w>
<w>imgs</w>
<w>kohima</w>
<w>libcomandy</w>
@@ -14,6 +15,7 @@
<w>mangafuna</w>
<w>nisi</w>
<w>pausable</w>
<w>reclass</w>
<w>reilia</w>
</words>
</dictionary>

View File

@@ -11,8 +11,8 @@ android {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 34
versionCode 64
versionName '2.3.6'
versionCode 65
versionName '2.3.7'
resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -23,6 +23,7 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.collection.size
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit
@@ -31,6 +32,7 @@ import androidx.core.view.WindowCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.contains
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
@@ -127,20 +129,25 @@ class MainActivity : AppCompatActivity() {
ime = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
var latestDestination: Int
navController!!.addOnDestinationChangedListener { _, destination, _ ->
navController!!.addOnDestinationChangedListener { controller, destination, _ ->
latestDestination = destination.id
Log.d("MyMA", "latestDestination: $latestDestination")
Log.d("MyMain", "latestDestination: ${destination.label}")
if (isMenuWaiting) {
return@addOnDestinationChangedListener
}
if (latestDestination !in appBarConfiguration.topLevelDestinations) {
drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
} else {
drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
}
isMenuWaiting = true
Log.d("MyMA", "start menu waiting")
Log.d("MyMain", "start menu waiting")
lifecycleScope.launch {
withContext(Dispatchers.IO) {
delay(1000)
withContext(Dispatchers.Main) {
isMenuWaiting = false
Log.d("MyMA", "finish menu waiting")
Log.d("MyMain", "finish menu waiting")
changeMenuList(latestDestination)
}
}
@@ -270,49 +277,49 @@ class MainActivity : AppCompatActivity() {
private fun changeMenuList(latestDestination: Int) {
when (latestDestination) {
R.id.nav_home -> {
Log.d("MyMA", "enter home")
Log.d("MyMain", "enter home")
menuMain?.findItem(R.id.action_info)?.isVisible = true
menuMain?.findItem(R.id.action_download)?.isVisible = false
menuMain?.findItem(R.id.action_sort)?.isVisible = false
menuMain?.findItem(R.id.action_del)?.isVisible = false
}
R.id.nav_book -> {
Log.d("MyMA", "enter book")
Log.d("MyMain", "enter book")
menuMain?.findItem(R.id.action_info)?.isVisible = false
menuMain?.findItem(R.id.action_download)?.isVisible = true
menuMain?.findItem(R.id.action_sort)?.isVisible = false
menuMain?.findItem(R.id.action_del)?.isVisible = false
}
R.id.nav_group -> {
Log.d("MyMA", "enter group")
Log.d("MyMain", "enter group")
menuMain?.findItem(R.id.action_info)?.isVisible = false
menuMain?.findItem(R.id.action_download)?.isVisible = false
menuMain?.findItem(R.id.action_sort)?.isVisible = true
menuMain?.findItem(R.id.action_del)?.isVisible = false
}
R.id.nav_new_download -> {
Log.d("MyMA", "enter new_download")
Log.d("MyMain", "enter new_download")
menuMain?.findItem(R.id.action_info)?.isVisible = false
menuMain?.findItem(R.id.action_download)?.isVisible = false
menuMain?.findItem(R.id.action_sort)?.isVisible = true
menuMain?.findItem(R.id.action_del)?.isVisible = false
}
R.id.nav_rank -> {
Log.d("MyMA", "enter rank")
Log.d("MyMain", "enter rank")
menuMain?.findItem(R.id.action_info)?.isVisible = false
menuMain?.findItem(R.id.action_download)?.isVisible = false
menuMain?.findItem(R.id.action_sort)?.isVisible = true
menuMain?.findItem(R.id.action_del)?.isVisible = false
}
R.id.nav_download -> {
Log.d("MyMA", "enter old download")
Log.d("MyMain", "enter old download")
menuMain?.findItem(R.id.action_info)?.isVisible = false
menuMain?.findItem(R.id.action_download)?.isVisible = false
menuMain?.findItem(R.id.action_sort)?.isVisible = false
menuMain?.findItem(R.id.action_del)?.isVisible = true
}
else -> {
Log.d("MyMA", "enter others")
Log.d("MyMain", "enter others")
menuMain?.findItem(R.id.action_info)?.isVisible = false
menuMain?.findItem(R.id.action_download)?.isVisible = false
menuMain?.findItem(R.id.action_sort)?.isVisible = false

View File

@@ -7,6 +7,7 @@ public class ComicStructure {
public int img_type;
public ValueDisplayPair region;
public ValueDisplayPair status;
public ValueDisplayPair reclass; // identify hotmanga
public ThemeStructure[] theme;
public String path_word;
public ThemeStructure[] author;

View File

@@ -6,12 +6,16 @@ public class IndexStructure extends ReturnBase {
public Banners[] banners;
public Topics topics;
public RecComics recComics;
// copymanga
public RankComics rankDayComics;
public RankComics rankWeekComics;
public RankComics rankMonthComics;
public ComicWrap[] hotComics;
public ComicWrap[] newComics;
public FinishComics finishComics;
// hotmanga
public RankComics rankWeeklyFreeComics;
public WeeklyComics updateWeeklyFreeComics;
public static class Banners{
public int type;
@@ -55,5 +59,8 @@ public class IndexStructure extends ReturnBase {
public String name;
public String type;
}
public static class WeeklyComics extends InfoBase {
public TypeBookStructure[] list;
}
}
}

View File

@@ -2,16 +2,7 @@ package top.fumiama.copymanga.json;
public class TypeBookListStructure extends ReturnBase {
public Results results;
public static class Results {
public int total;
public TypeBook[] list;
public int limit;
public int offset;
}
public static class TypeBook {
public int type;
public String name;
public String datetime_created;
public ComicStructure comic;
public static class Results extends InfoBase {
public TypeBookStructure[] list;
}
}

View File

@@ -0,0 +1,8 @@
package top.fumiama.copymanga.json;
public class TypeBookStructure {
public int type;
public String name;
public String datetime_created;
public ComicStructure comic;
}

View File

@@ -52,6 +52,7 @@ class Book(val path: String, private val getString: (Int) -> String, private val
val volumes get() = mVolumes
val uuid get() = mBook?.results?.comic?.uuid
val json get() = mJsonString
val version get() = if (mBook?.results?.comic?.reclass != null) 1 else 2
constructor(name: String, getString: (Int) -> String, exDir: File): this(
Reader.getComicPathWordInFolder(File(exDir, name)),

View File

@@ -70,6 +70,9 @@ class MangaDlTools {
var re: Array<String> = arrayOf()
val hm: HashMap<Int, String> = hashMapOf()
val chapter = chapter2Return.results.chapter
if (chapter.words == null) {
return chapter.contents.map { it.url?:"" }.toTypedArray()
}
if(chapter.words.size < chapter.contents.size) {
chapter.words = chapter.words.toMutableList().apply {
chapter.contents.indices.forEach {

View File

@@ -9,9 +9,8 @@ import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.dmzj.copymanga.R
class Shelf(private val token: String, getString: (Int) -> String) {
private val hostUrl: String = getString(R.string.hostUrl)
private val apiUrl: String = getString(R.string.shelfOperateApiUrl).format(hostUrl)
class Shelf(private val token: String, private val getString: (Int) -> String) {
private val apiUrl: String get() = getString(R.string.shelfOperateApiUrl).format(CMApi.myHostApiUrl)
private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
private val referer: String = getString(R.string.referer).format(DownloadTools.app_ver)
private val addApiUrl get() = "$apiUrl?platform=3".let { CMApi.apiProxy?.wrap(it)?:it }
@@ -63,7 +62,7 @@ class Shelf(private val token: String, getString: (Int) -> String) {
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
try {
Gson().fromJson(DownloadTools.getHttpContent(
queryApiUrlTemplate.format(hostUrl, pathWord).let {
queryApiUrlTemplate.format(CMApi.myHostApiUrl, pathWord).let {
CMApi.apiProxy?.wrap(it)?:it
}, referer
).decodeToString(), BookQueryStructure::class.java)

View File

@@ -5,7 +5,6 @@ import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
@@ -87,15 +86,16 @@ open class MangaPagesFragmentTemplate(inflateRes:Int, private val isLazy: Boolea
//jsonReaderNow = null
}
open suspend fun setLayouts() = withContext(Dispatchers.IO) {
private suspend fun setLayouts() = withContext(Dispatchers.IO) {
if (!isFirstInflate) return@withContext
val toolsBox = this@MangaPagesFragmentTemplate.context?.let { UITools(it) }
val widthData = toolsBox?.calcWidthFromDp(8, 135)
cardPerRow = widthData?.get(0) ?: 3
cardWidth = widthData?.get(2) ?: 128
cardHeight = (cardWidth / 0.75 + 0.5).toInt()
withContext(Dispatchers.Main) {
mysp.footerView.lht.text = "加载"
mysp.headerView.lht.text = "刷新"
mysp?.footerView?.lht?.text = "加载"
mysp?.headerView?.lht?.text = "刷新"
mydll?.setPadding(0, 0, 0, navBarHeight)
}
Log.d("MyMPAT", "Card per row: $cardPerRow")

View File

@@ -40,11 +40,11 @@ open class NoBackRefreshFragment(private val layoutToLoad: Int): Fragment() {
return rootView
}
override fun onDestroy() {
super.onDestroy()
Thread { runBlocking { hideKanban() } }.start()
hideKanban()
_rootView = null
isFirstInflate = true
Log.d("MyNBRF", "destroyed")
super.onDestroy()
}
fun showKanban() {
if (disableAnimation) return

View File

@@ -52,12 +52,14 @@ class CardList(
private suspend fun inflateRow(index: Int, whenFinish: suspend (index: Int)->Unit) = withContext(Dispatchers.IO) {
Log.d("MyCL", "inflateRow: $index, cardPR: $cardPerRow")
that?.apply {
layoutInflater.inflate(R.layout.line_horizonal_empty, mydll, false)?.let {
if(exitCardList) return@withContext
it.layoutParams.height = cardHeight + 16
mydll?.apply { post { addView(it) } }
recycleOneRow(it, index)
whenFinish(index)
mydll?.let { m ->
layoutInflater.inflate(R.layout.line_horizonal_empty, m, false)?.let {
if(exitCardList) return@withContext
it.layoutParams.height = cardHeight + 16
m.apply { post { addView(it) } }
recycleOneRow(it, index)
whenFinish(index)
}
}
}
}

View File

@@ -72,7 +72,7 @@ object CMApi {
fun getZipFile(exDir: File?, manga: String, caption: CharSequence, name: CharSequence) =
File(exDir, "$manga/$caption/$name.zip")
fun getChapterInfoApiUrl(arg1: String?, arg2: String?) =
fun getChapterInfoApiUrl(path: String?, uuid: String?, version: Int) =
MainActivity.mainWeakReference?.get()?.getString(R.string.chapterInfoApiUrl)
?.format(myHostApiUrl, arg1, arg2)
?.format(myHostApiUrl, path, if (version >= 2) "$version" else "" , uuid)
}

View File

@@ -7,8 +7,6 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.core.animation.addListener
import androidx.core.animation.doOnEnd
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.app_bar_main.*
@@ -16,7 +14,6 @@ import kotlinx.android.synthetic.main.card_book.*
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.line_booktandb.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.fumiama.copymanga.MainActivity
@@ -79,7 +76,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
oa.start()
Log.d("MyBF", "set progress $it")
} } }) {
mBookHandler?.sendEmptyMessage(BookHandler.SET_VOLUMES)
mBookHandler?.obtainMessage(BookHandler.SET_VOLUMES, book?.version?:2, 0)?.sendToTarget()
}
} catch (e: Exception) {
e.printStackTrace()
@@ -227,6 +224,7 @@ class BookFragment: NoBackRefreshFragment(R.layout.fragment_book) {
if(book?.volumes != null && book?.json != null) {
bundle.putString("loadJson", book!!.json)
}
bundle.putInt("version", book?.version?:2)
findNavController().let {
Navigate.safeNavigateTo(it, R.id.action_nav_book_to_nav_group, bundle)
}

View File

@@ -64,9 +64,9 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
1 -> setCover()
2 -> setTexts()
3 -> setAuthorsAndTags()
6 -> if(complete) that?.navigate2dl()
9 -> endSetLayouts()
10 -> setVolumes()
NAVIGATE_TO_DOWNLOAD -> if(complete) that?.navigate2dl()
END_SET_LAYOUTS -> endSetLayouts()
SET_VOLUMES -> setVolumes(msg.arg1)
}
}
@@ -263,7 +263,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
}
}
private suspend fun setViewManga() = withContext(Dispatchers.IO) {
private suspend fun setViewManga(version: Int) = withContext(Dispatchers.IO) {
if (exit) return@withContext
that?.apply {
book?.apply {
@@ -279,7 +279,8 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
v.results.list.forEach {
urlArray += CMApi.getChapterInfoApiUrl(
path,
it.uuid
it.uuid,
version
)?:""
val f = CMApi.getZipFile(context?.getExternalFilesDir(""), comicName, keys[groupIndex], it.name)
Reader.fileArray += f
@@ -307,7 +308,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
}
}
private fun setVolumes() {
private fun setVolumes(version: Int) {
that?.apply {
fbtab?.let { tab ->
fbvp?.let { vp ->
@@ -317,7 +318,7 @@ class BookHandler(private val th: WeakReference<BookFragment>): Handler(Looper.m
}.attach()
}
}
lifecycleScope.launch { setViewManga() }
lifecycleScope.launch { setViewManga(version) }
}
}

View File

@@ -60,7 +60,8 @@ class ComicDlFragment: NoBackRefreshFragment(R.layout.fragment_dlcomic) {
withContext(Dispatchers.Main) {
handler = ComicDlHandler(Looper.myLooper()!!, WeakReference(this@ComicDlFragment),
volumes, arguments?.getString("name")?:"null",
if(isFromFile) groupArray else arguments?.getStringArray("groupNames")
if(isFromFile) groupArray else arguments?.getStringArray("groupNames"),
arguments?.getInt("version")?:2,
)
}
if(!isFromFile) {

View File

@@ -42,8 +42,13 @@ import java.io.File
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicInteger
class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragment>, private val vols: Array<VolumeStructure>, private val comicName: String, private val groupNames: Array<String>?):Handler(looper) {
constructor(looper: Looper, th: WeakReference<ComicDlFragment>, comicName: String) : this(looper, th, arrayOf(), comicName, null) {
class ComicDlHandler(
looper: Looper, private val th: WeakReference<ComicDlFragment>,
private val vols: Array<VolumeStructure>, private val comicName: String,
private val groupNames: Array<String>?, private val version: Int,
):Handler(looper) {
constructor(looper: Looper, th: WeakReference<ComicDlFragment>, comicName: String)
: this(looper, th, arrayOf(), comicName, null, 2) {
isOld = true
}
private var isOld = false
@@ -123,7 +128,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
Log.d("MyCDH", "caption: $caption, group name: ${groupNames?.get(i)}")
withContext(Dispatchers.Main) {
addCaption(caption) {
addButtons(vol.results.list, caption)
addButtons(vol.results.list, caption, version)
}
}
}
@@ -334,9 +339,9 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
//ObjectAnimator.ofFloat(dlsdwn, "alpha", 0.9f, 0.3f).setDuration(233).start()
ObjectAnimator.ofFloat(that?.dlsdwn, "translationX", 0f, cdwnWidth.toFloat() * 0.9f).setDuration(233).start()
}
private suspend fun addButtons(chapters: Array<ChapterStructure>, caption: String) = withContext(Dispatchers.IO) {
private suspend fun addButtons(chapters: Array<ChapterStructure>, caption: String, version: Int) = withContext(Dispatchers.IO) {
chapters.forEach { chapter ->
val u = CMApi.getChapterInfoApiUrl(chapter.comic_path_word, chapter.uuid)?:""
val u = CMApi.getChapterInfoApiUrl(chapter.comic_path_word, chapter.uuid, version)?:""
addButton(chapter.name, chapter.uuid, caption, u)
urlArray += u
}
@@ -437,7 +442,7 @@ class ComicDlHandler(looper: Looper, private val th: WeakReference<ComicDlFragme
for (chapter in group.chapters) {
val newUrl = CMApi.getChapterInfoApiUrl(
chapter.url.substringAfter("/comic/").substringBefore('/'),
chapter.url.substringAfterLast('/')
chapter.url.substringAfterLast('/'), version,
)?:""
Log.d("MyCD", "Generate new url: $newUrl")
addButton(chapter.name, "", group.name, newUrl)

View File

@@ -149,6 +149,19 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
private suspend fun inflateRank(){
var comics = arrayOf<ComicStructure>()
if (index?.results?.rankDayComics == null) {
// is in hotmanga
index?.results?.rankWeeklyFreeComics?.list?.let {
for((i, book) in it.withIndex()){
if(i > 2) break
comics += book.comic
}
}
if(comics.size == 3) allocateLine(homeF?.getString(R.string.hot_rank_list)?:"", R.drawable.img_novel_bill, comics) {
homeF?.findNavController()?.navigate(R.id.nav_rank)
}
return
}
index?.results?.rankDayComics?.list?.let {
for((i, book) in it.withIndex()){
if(i > 2) break
@@ -173,6 +186,18 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
}
private suspend fun inflateHot(){
if (index?.results?.hotComics == null) {
// is in hotmanga
index?.results?.updateWeeklyFreeComics?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.list.withIndex()){
if(i > 5) break
comics += rec.comic
}
if(comics.size == 6) allocateLine(homeF?.getString(R.string.hot_list)?:"", R.drawable.img_hot, comics)
}
return
}
index?.results?.hotComics?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){

View File

@@ -134,11 +134,13 @@ class VMHandler(activity: ViewMangaActivity, private val chapterUrl: String, pri
override fun setGsonItem(gsonObj: Any): Boolean {
super.setGsonItem(gsonObj)
val m = gsonObj as Chapter2Return
if(m.results.chapter.words.size != m.results.chapter.contents.size) {
return false
}
if(m.results.chapter.words.size != m.results.chapter.size) {
m.results.chapter.size = m.results.chapter.words.size // 有时 size 不对
if (m.results.chapter.words != null) {
if(m.results.chapter.words.size != m.results.chapter.contents.size) {
return false
}
if(m.results.chapter.words.size != m.results.chapter.size) {
m.results.chapter.size = m.results.chapter.words.size // 有时 size 不对
}
}
manga = m
return true

View File

@@ -401,7 +401,7 @@ class ViewMangaActivity : TitleActivityTemplate() {
//private fun getTempFile(position: Int) = File(cacheDir, "$position")
private fun getImgUrl(position: Int) = mHandler.manga?.results?.chapter?.let {
it.contents[it.words.indexOf(position)].url
it.contents[it.words?.indexOf(position)?:position].url
}
private fun getImgUrlArray() = mHandler.manga?.results?.chapter?.let{

View File

@@ -62,6 +62,7 @@
<string name="touch_img_error">预载图片头失败</string>
<string name="analyze_img_size_error">读取图片大小失败</string>
<string name="networkApiUrl">https://%1$s/api/v3/system/network2?platform=3</string>
<string name="mainPageApiUrl">https://%1$s/api/v3/h5/homeIndex?platform=3</string>
<string name="referUrl">https://%1$s</string>
<string name="hostUrl">&hosturl;</string>
@@ -72,7 +73,7 @@
<string name="bookInfoApiUrl">https://%1$s/api/v3/comic2/%2$s?platform=3</string>
<string name="bookUserQueryApiUrl">https://%1$s/api/v3/comic2/%2$s/query?platform=3</string>
<string name="groupInfoApiUrl">https://%1$s/api/v3/comic/%2$s/group/%3$s/chapters?limit=100&amp;offset=%4$d&amp;platform=3</string>
<string name="chapterInfoApiUrl">https://%1$s/api/v3/comic/%2$s/chapter2/%3$s?platform=3</string>
<string name="chapterInfoApiUrl">https://%1$s/api/v3/comic/%2$s/chapter%3$s/%4$s?platform=3</string>
<string name="topicApiUrl">https://%1$s/api/v3/topic/%2$s?platform=3</string>
<string name="topicContentApiUrl">https://%1$s/api/v3/topic/%2$s/contents?type=%3$d&amp;limit=21&amp;offset=%4$d&amp;platform=3</string>
<string name="recommendApiUrl">https://%1$s/api/v3/recs?pos=3200102&amp;limit=21&amp;offset=%2$d&amp;platform=3</string>
@@ -128,6 +129,7 @@
<string name="topics_series">专题系列</string>
<string name="manga_rec">漫画推荐</string>
<string name="rank_list">排行榜 ☟日周月 ☛冠亚季</string>
<string name="hot_rank_list">本周排行</string>
<string name="hot_list">热门更新</string>
<string name="new_list">全新上架</string>