From 8b255abe3f64ebc3c7545bb61a8dbc18e4675151 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 12 May 2021 20:30:57 +0800 Subject: [PATCH 001/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=9C=AA=E8=AF=BB=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../res/layout/dialog_bookshelf_config.xml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/layout/dialog_bookshelf_config.xml b/app/src/main/res/layout/dialog_bookshelf_config.xml index 134d4d1c7..9d795bd2a 100644 --- a/app/src/main/res/layout/dialog_bookshelf_config.xml +++ b/app/src/main/res/layout/dialog_bookshelf_config.xml @@ -55,13 +55,24 @@ + + + - - - \ No newline at end of file From a0d282453f3277cea456f7d6eaff901fe97d72cb Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 14 May 2021 10:24:34 +0800 Subject: [PATCH 002/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=80=BB=E6=98=AF?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=BB=98=E8=AE=A4=E5=B0=81=E9=9D=A2=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/constant/PreferKey.kt | 1 + .../main/java/io/legado/app/help/AppConfig.kt | 3 + .../app/ui/widget/image/CoverImageView.kt | 59 +++++++++++-------- app/src/main/res/values-zh-rHK/strings.xml | 2 + app/src/main/res/values-zh-rTW/strings.xml | 2 + app/src/main/res/values-zh/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/pref_config_other.xml | 9 +++ 8 files changed, 54 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index f60b7c361..70b4a6b89 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -5,6 +5,7 @@ object PreferKey { const val themeMode = "themeMode" const val userAgent = "userAgent" const val showUnread = "showUnread" + const val useDefaultCover = "useDefaultCover" const val hideStatusBar = "hideStatusBar" const val clickActionTL = "clickActionTopLeft" const val clickActionTC = "clickActionTopCenter" diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index 102f9507c..b2c620f63 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -78,6 +78,9 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { appCtx.putPrefBoolean(PreferKey.showUnread, value) } + val useDefaultCover: Boolean + get() = appCtx.getPrefBoolean(PreferKey.useDefaultCover, false) + val isTransparentStatusBar: Boolean get() = appCtx.getPrefBoolean(PreferKey.transparentStatusBar, true) diff --git a/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt b/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt index 24bdd08fd..ac4b6159d 100644 --- a/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt +++ b/app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt @@ -12,6 +12,7 @@ import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target import io.legado.app.R import io.legado.app.constant.PreferKey +import io.legado.app.help.AppConfig import io.legado.app.help.ImageLoader import io.legado.app.utils.getPrefString import splitties.init.appCtx @@ -132,34 +133,40 @@ class CoverImageView @JvmOverloads constructor( fun load(path: String?, name: String?, author: String?) { setText(name, author) - ImageLoader.load(context, path)//Glide自动识别http://,content://和file:// - .placeholder(defaultDrawable) - .error(defaultDrawable) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - loadFailed = true - return false - } + if (AppConfig.useDefaultCover) { + ImageLoader.load(context, defaultDrawable) + .centerCrop() + .into(this) + } else { + ImageLoader.load(context, path)//Glide自动识别http://,content://和file:// + .placeholder(defaultDrawable) + .error(defaultDrawable) + .listener(object : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + loadFailed = true + return false + } - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - loadFailed = false - return false - } + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + loadFailed = false + return false + } - }) - .centerCrop() - .into(this) + }) + .centerCrop() + .into(this) + } } companion object { diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 302969db3..00e5789b7 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -825,5 +825,7 @@ 导出所有 完成 显示未读标志 + 总是使用默认封面 + 总是显示默认封面,不显示网络封面 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index b826956ca..55ac557d9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -829,5 +829,7 @@ 导出所有 完成 显示未读标志 + 总是使用默认封面 + 总是显示默认封面,不显示网络封面 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 1f17b8764..1da9bdf53 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -829,5 +829,7 @@ 导出所有 完成 显示未读标志 + 总是使用默认封面 + 总是显示默认封面,不显示网络封面 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cd76c1764..8e891cbb0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -830,5 +830,7 @@ export_all complete Show unread + always use default cover + always use default cover,not use network diff --git a/app/src/main/res/xml/pref_config_other.xml b/app/src/main/res/xml/pref_config_other.xml index 8dadf0940..32b5aa3c1 100644 --- a/app/src/main/res/xml/pref_config_other.xml +++ b/app/src/main/res/xml/pref_config_other.xml @@ -43,6 +43,15 @@ app:allowDividerBelow="false" app:iconSpaceReserved="false" /> + + Date: Fri, 14 May 2021 16:23:42 +0800 Subject: [PATCH 003/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e9650f9c8..c27b2a08b 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.android.tools.build:gradle:4.2.0' + classpath 'com.android.tools.build:gradle:4.2.1' //noinspection DifferentKotlinGradleVersion classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "de.timfreiheit.resourceplaceholders:placeholders:0.3" From 3b26bbcbc391ebcaf5ee475aabfed26e91c2f7f2 Mon Sep 17 00:00:00 2001 From: mezysinc <81477718+mezysinc@users.noreply.github.com> Date: Fri, 14 May 2021 17:08:50 -0300 Subject: [PATCH 004/112] ptbr translation strings in ptbr --- app/src/main/res/values-pt-rBR/strings.xml | 836 +++++++++++++++++++++ 1 file changed, 836 insertions(+) create mode 100644 app/src/main/res/values-pt-rBR/strings.xml diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 000000000..041277c1e --- /dev/null +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,836 @@ + + + + Legado + Legado·pesquisa + Legado precisa de acesso ao armazenamento para encontrar e ler livros. por favor, vá às "Configurações do App" para conceder "Permissão de armazenamento". + + + Início + Restaurar + Importar dados ao Legado + Criar uma subpasta + Criar uma pasta de Backup com o nome Legado. + Backup cache dos livros para leitura off-line + Exportar localmente e fazer Backup à pasta de exportação + Backup para + Por favor, selecione uma pasta de Backup. + Importar dados legado + Importar dados de Github + Substituição + Enviar + + Lembrete + Cancelar + Confirmar + Vá até as Configurações + Não foi possível ir para as Configurações. + + tentar novamente + Carregando + Aviso + Editar + Excluir + Excluir tudo + Substituir + Substituição + Configurar regras de substituição + Indisponível + Ativar + Procurar o substituto + Estante + Favoritos + Favorito + em Favoritos + Não está em Favoritos + Assinatura + Tudo + Leitura recente + Última leitura + Novidades + A estante está vazia. Procure por livros ou adicione-os via descoberta! \n se você o usa pela primeira vez, por favor, preste atenção à conta pública [开源阅读] para obter a fonte do livro! + Pesquisar + Download + Lista + Grid-3 + Grid-4 + Grid-5 + Grid-6 + Layout + Vista + Biblioteca + Importar livros + Fontes de livros + Gerenciar fontes + Criar/Importar/Editar/Gerenciar fontes de livros + Configurações + Configurações de Temas + Alguns ajustes relacionados à interface ou cor + Outras configurações + Algumas configurações relacionadas à funcionamento + Sobre + Doações + Sair + Não foi salvo. Você quer continuar editando? + Tipos de livros + Versão + Local + Procurar + Origem: %s + Origem: %s + Título + Último: %s + Você gostaria de adicionar %s à sua Estante? + %s arquivo(s) de texto no total + Carregando… + Tentar novamente + Serviço Web + Fonte de edição web e livro lido + Editar fontes de livros na web + http://%1$s:%2$d + Cache off-line + Cache off-line + salvar o(s) capítulo(s) selecionado(s) em cache no Armazenamento + Mudar a origem + + \u3000\u3000 Este é um app de leitura de software livre, desenvolvido em Kotlin, você é bem-vindo a participar em projeto. Siga a conta oficial de WeChat [开源阅读]! + + + Legado (YueDu 3.0) link para download:\n https://play.google.com/store/apps/details?id=io.legado.play.release + + Versão %s + Verificação em segundo plano + você pode utilizar livremente ao verificar a fonte do livro + Atualização automática + Atualizar os livros automaticamente ao abrir o App + Download automático + Baixar os últimos capítulos automaticamente ao atualizar os livros + Backup e restauração + Configurações WebDav + Configurações/importação de dados legado WebDav + Backup + Restaurar + O Backup precisa de permissão de armazenamento + Para restaurar é necessária a permissão de armazenamento + OK + Cancelar + Confirmação de Backup + Os novos arquivos de Backup substituirão os originais.\n Pasta de Backup: YueDu + Confirmação da Restauração + A restauração dos dados da estante irá substituir a estante atual. + Backup bem sucedido + O Backup falhou + Restaurando + Restauração bem sucedido + A restauração falhou + Orientação da tela + Auto(sensor) + Paisagem + Retrato + Adaptar do sistema + Isenção de responsabilidade + %d capítulos + Interface + Brilho + Capítulos + Próximo + Anterior + Ocultar barra de status + Ocultar a barra de navegação do sistema durante leitura + Voz + Falando + Clique para abrir a leitura + Reproduzir + Reproduzindo + Clique para abrir a reprodução + Pausar + Anterior + Atualizar + Começar + Parar + Pausar + Retomar + Cronômetro + Voz pausada + Falando(%d min left) + Reproduzindo(%d min restantes) + Esconder botões virtuais durante a leitura + Ocultar a barra de navegação + Cor da barra de navegação + GitHub + Classificação + E-mail + Falha ao abrir + Falha ao compartilhar + Sem capítulos + Adicionar Url + Adicionar Url de livro + Segundo plano + Autor + Autor: %s + Voz parada + Limpar o cache + O cache foi removido + Salvar + Editar fonte + Editar fonte do livro + Desativar fonte do livro + Adicionar fonte de livro + Adicionar fonte de assinatura + Adicionar livros + Pesquisar + Copiar a fonte + Colar a fonte + Fonte da descrição das regras + Verificar por atualizações + Digitalizar código QR + Digitalizar imagens locais + Descrição das regras + Compartilhar + Compartilhar via + Adaptar do sistema + Adicionar + Importar as fontes de livro + Importar localmente + Importar on-line + Substituição + Editar regra de substituição + Padrão + Substituição + Capa + Livro + Botões de volume para virar páginas + Toque a tela para virar página + Virar animação + Virar animação (livro) + Manter a tela ligada + Voltar + Menu + Ajuste + Barra de deslize + Ao limpar o cache você excluirá todos os capítulos salvos. Você tem certeza disso? + Compartilhamento de fontes de livros + Nome da regra + A regra padrão está vazia ou não está em conformidade com as especificações de Regex. + Ação de seleção + Selecionar tudo + Selecionar tudo(%1$d/%2$d) + Cancelar a seleção de todos(%1$d/%2$d) + Modo escuro + Página de boas-vindas + Iníciar o download + Cancelar o Download + Sem download + Baixado %1$d/%2$d + Importar livro(s) selecionado(s) + Número de tarefas simultâneas + Mudar o ícone + Excluir + Começar leitura + Carregando… + Falha ao carregar, toque para tentar novamente + Descrição do livro + Descrição:%s + Descrição: sem introdução + Abrir o livro externo + Origem: %s + Importar regras locais + Importar regras on-line + Verificar intervalo de atualizações + Lista recente + Tempo de atualização + Título do livro + Ordenar manualmente + Estratégia de leitura + Tipografia + Excluir selecionados + Você tem certeza? + Fonte padrão + Descoberta + Descoberta + Sem conteúdo. Vá para Gerenciador de fontes para adicioná-lo! + Excluir tudo + Histórico de busca + Limpar + Mostrar título do livro no texto + Sincronização das fontes do livro + Não há último capítulo. + Mostrar o tempo e a bateria + Divisor da tela + Escurecer a cor do ícone da barra de status + Conteúdo + Copiar + Baixar tudo + Este é um texto de teste, \n\u3000\u3000 apenas para mostrar o resultado + Cor e fundo (toque longo para personalizar) + Barra de status imersiva + %d capítulos restantes + Nenhum selecionado + Toque longo, para introduzir o valor da cor + Carregando… + Aguardando + Quase lá + Favoritos + Adicionar aos Favoritos + Excluir + Tempo limite de carregamento + Siga:%s + Copiado com sucesso + A organização da estante + Isto excluirá todos os livros. Por favor, tenha cuidado. + Pesquisar fontes de livros + Buscar fontes de assinatura + Pesquisa( %d fontes no total) + Capítulos(%d) + Negrito + Fonte + Texto + Página inicial + Direita + Esquerda + Parte inferior + Parte superior + Espaço + Espaço superior + Espaço inferior + Espaço da esquerda + Espaço da direita + Verificar fontes do livro + Verificar a fonte selecionada + %1$s Progresso %2$d/%3$d + Favor instalar e selecionar o TTS chinês! + A inicialização do TTS falhou! + Conversão simplificada + Desligada + Simplificado ao tradicional + Tradicional ao simplificado + Modo de virar + %1$d itens + Armazenamento: + Adicionar ao Estante + Adicionar ao Estante(%1$d) + %1$d livros adicionados com sucesso + Favor colocar os arquivos de fontes na pasta Root de Fontes no armazenamento e selecionar novamente + Fonte padrão + Selecionar fontes + Tamanho do texto + Espaço entre linhas + Espaço entre parágrafos + Ao parte superior + Ao fundo + Expansão automática de Descoberta + A expansão padrão para a primeira Descoberta. + Linhas atuais %s + Velocidade da voz + Rolagem automática + Impedir rolagem automática + Velocidade automática de rolagem + Informações sobre livro + Editar informações do livro + Definir a Estante como página inicial + Vá automaticamente à lista de Recentes + Objeto substituto. Nome do livro ou Url de origem estão disponíveis + Grupos + Pasta de cache + Seletor de arquivos do sistema + Nova versão + Baixar atualizações + Botões de volume para virar páginas durante leitura + Ajuste de margem + Ativar atualizações + Desativar atualizações + Inverso + Pesquisar livro por nome/autor + Nome do livro,Autor,URL + FAQ + Mostrar todas as Descobertas + Mostrar a fonte da Descoberta selecionada quando fechado + Atualizar os capítulos + Capítulos Txt Regex + Codificação de texto + Ordem ascendente/descendente + Ordenar + Ordenar automaticamente + Ordenar manualmente + Ordenar pelo nome + Vá ao topo + Vá ao fundo + Ler: %s + Aguardando atualização + Quase lá + Terminado + Tudo + Aguardando atualização dos livros + Aguardando mais capítulos de livros + Livros concluídos + Livros locais + A cor da barra de status fica transparente + barra de navegação por imersão + A barra de navegação fica transparente + Adicionar à Estante + Continuar lendo + Pasta da capa + Capa + Deslizar + Simulação + Deslizar + Nenhum + Esta fonte de livro usa recursos avançados, por favor vá até Doações e toque no código de busca do envelope vermelho Alipay para receber o envelope vermelho, depois você pode usá-lo. + Atualizar o último capítulo após a mudança de origem em segundo plano + se ativado, a atualização será iniciada 1 minuto depois o App for aberto + Auto esconder a barra de ferramentas + A barra de ferramentas será escondida automaticamente ao deslizar a Estante + Login + Login%s + Sucesso + A fonte atual não foi configurada com um endereço de login + Nenhuma página anterior + Nenhuma página seguinte + + + 源名称(Nome da fonte) + 源URL(fonteUrl) + 源分组(fonteGrupo) + 自定义源分组 + 输入自定义源分组名称 + 【%s】 + 分类Url + 登录URL(loginUrl) + 源注释(fonteComentário) + 搜索地址(url) + 发现地址规则(url) + 书籍列表规则(ListaLivros) + 书名规则(nome) + 详情页url规则(livroUrl) + 作者规则(autor) + 分类规则(tipo) + 简介规则(intro) + 封面规则(capaUrl) + 最新章节规则(últimoCapítulo) + 字数规则(contagemPalavras) + 书籍URL正则(LivroPadrãoUrl) + 预处理规则(livroInfoInit) + 目录URL规则(tocUrl) + 允许修改书名作者(podeRenomear) + 目录下一页规则(proxTocUrl) + 目录列表规则(chapterList) + 章节名称规则(capítuloLista) + 章节URL规则(capítuloUrl) + VIP标识(éVip) + 更新时间(capítuloInfo) + 正文规则(conteúdo) + 正文下一页URL规则(proxConteúdoUrl) + WebViewJs(webJs) + 资源正则(fonteRegex) + 替换规则(substRegex) + 图片样式(imagemEstilo) + + 图标(fonteÍcone) + 列表规则(regrasArtigos) + 列表下一页规则(regrasArtigos) + 标题规则(regrasTítulo) + guid规则(regraGuid) + 时间规则(regraPubData) + 类别规则(regraCategorias) + 描述规则(regraDescrição) + 图片url规则(regraImagem) + 内容规则(regraConteúdo) + 样式(estilo) + 链接规则(regraLink) + + + + Sem fonte + Falha ao obter informações sobre os livros + Falha ao obter conteúdo + Falha ao obter lista de capítulos + Falha ao acessar o site:%s + Falha na leitura do arquivo + Falha ao carregar a lista de capítulos + Falha ao obter dados + Falha ao carregar\n%s + Sem rede + Tempo limite de conexão à rede + O processamento de dados falhou + + + Cabeçalho HTTP + Fonte de depuração + Importar de código QR + Compartilhar fontes selecionadas + Digitalizar código QR + Toque para exibir o menu quando selecionado + Tema + Tipo de tema + Selecionar tema que você preferir + Junte-se ao grupo QQ + Para definir a imagem de fundo é necessária permissão de armazenamento + Digite o endereço da fonte de livro + Excluir arquivo + Arquivo excluído + Você tem certeza que quer excluir este arquivo? + Pasta + Importação inteligente + Descoberta + Mudar o modo de exibição + Importação de livros locais requer permissão de armazenamento + Tema Noturno + E-Ink + Otimização para dispositivos E-ink + Este App requer permissão de armazenamento para salvar as informações do livro como Backup + Toque novamente para sair do App + Importação de livros locais requer permissão de armazenamento + A conexão de rede não está disponível + Sim + Não + OK + Você realmente quer excluir? + Você tem certeza que quer excluir %s? + Você realmente quer excluir todos os livros? + Você quer ao mesmo tempo excluir os capítulos de livros baixados? + Para digitalizar o código QR é necessário ter permissão da câmera + A voz está em execução, não pode virar páginas automaticamente + Codificação de entrada + Capítulos Txt Regex + Para abrir livros locais é necessária permissão de armazenamento + Livro sem nome + URL da regra de substituição de entrada + A lista procurada obtida com sucesso%d + nome e URL não podem estar vazios + Galeria + obter envelopes vermelhos de AliPay + Nenhuma atualização para o endereço + Ao abrir a página inicial, ela voltará automaticamente após o sucesso + Após o login bem sucedido, por favor toque no ícone no canto superior direito para testar o acesso à página inicial + Capítulo + Para + Usando Regex + Indent + Nenhum + Indent com 1 chars + Indent com 2 chars + Indent com 3 chars + Indent com 4 chars + Selecionar uma pasta + Selecionar um arquivo + Nenhuma Descoberta, você pode adicioná-la em Fontes de Livros + Restaurar padrão + A pasta de cache personalizada requer permissão de armazenamento + Preto + Sem conteúdo + Mudança de fonte, aguarde por favor + Os capítulos sem conteúdo + Espaço entre palavras + + Básico + Pesquisar + Descoberta + Informação + Capítulos + Conteúdo + + Modo E-Ink + Remover animações e otimizar a experiência com livros E-papel + Serviço Web + Porta Web + Porta atual %s + Compartilhar o código QR + Compartilhar sequência + Compartilhar Wifi + Por favor, conceda permissão de armazenamento + Retrocesso rápido + Avanço rápido + Anterior + Próximo + Musica + Áudio + Habilitar + Habilitar js + Carregar a Url Base + Todas as fontes + O conteúdo de entrada não pode estar vazio + Limpar o cache da Descoberta + Editar Descoberta + Alternar o ícone do software exibido na área de trabalho + Ajuda + Minhas + Leituras + %d%% + %d min + Auto-Brilho %s + Ler páginas em voz alta + Mecanismo de voz + Imagens de fundo + Cor de fundo + Cor de texto + Selecione uma foto + Administração de grupo + Seleção de grupo + Edição de grupo + Mover ao grupo + Adicionar aos grupos + Remover do grupo + Novo substituto + Grupo + Grupo: %s + Capítulos: %s + Habilitar a Descoberta + Deshabilitar a Descoberta + Habilitar selecionados + Deshabilitar selecionados + Exportar selecionados + Exportar + Carregar os capítulos + Carregar os detalhes do livro + TTS + Senha WebDav + Digite sua senha autorizada de WebDav + Digite o endereço do seu servidor + Endereço do servidor WebDav + Conta WebDav + Digite sua conta WebDav + Fonte de assinatura + Editar a fonte de assinatura + Filtrar + Pesquisar as fontes da Descoberta + Localização atual: + Busca precisa + Iniciação do serviço + Vazio + Selector de arquivos + Selector de pastas + Concluído! + Endereço Uri falhou + Atualizar capa + Mudar a fonte + Imagem local + Tipo: + Segundo plano + Importando + Exportando + Definir botões que viram as páginas + Botão de página anterior + Botão de página seguinte + Primeiro adicionar este livro à Estante + Sem grupo + Frase anterior + Frase seguinte + Outra pasta + Demasiadas palavras para criar um código QR + Compartilhar fontes de assinatura + Compartilhar fontes de livros + Mudança automática ao modo escuro + Seguir o modo escuro do sistema + Voltar + Tom de voz online + (%1$d/%2$d) + Exibir assinatura + Serviço parou + Iniciando o serviço\nVerificando a barra de notificações para detalhes + Pasta padrão + Seletor de pastas do sistema + Seletor de pastas do App + Seletor de arquivos do App + Android 10+ não é capaz de ler e salvar arquivos devido a restrições de permissão + Toque longo para exibir o Legado·Pesquisa no menu de operação + Visualização da operação de texto Pesquisa + Log + Log + Conversão simplificada + O ícone é um ícone vetorial, que não era suportado antes de Android 8.0 + Configurações da voz + Página inicial + Toque longo para selecionar o texto + Cabeçalho + Conteúdo + Rodapé + Selecionar o fim + Selecione o início + Layout compartilhado + Navegador + Importar regras padrão + Nome + Regex + Mais menu + Menos + Mais + Tipo de sistema + Excluir arquivo fonte + Padrão-1 + Padrão-2 + Padrão-3 + Título + Esquerda + Centro + Esconder + Adicionar ao grupo + Salvar imagem + Nenhum pasta padrão + Configurações de grupo + Ver os capítulos + Sombra da barra de navegação + Tamanho da sombra atual(elevação): %s + Padrão + Menu principal + Toque para conceder permissão + Legado precisa de permissão de armazenamento, por favor toque no botão "Conceder Permissão" abaixo, ou vá para as "Configurações"-"Permissões do Apps"-para abrir a permissão necessária. Se a permissão ainda não funcionar, por favor, toque no "Selecionar Pasta" no canto superior direito para usar o seletor de pastas do sistema. + O texto selecionado não pode ser falado como texto completo + Ampliar para recortar + Atualização de capítulos + Os botões de fones de ouvido estão sempre disponíveis + Os botões de fones de ouvido estão disponíveis mesmo ao sair do App. + colaboradores + Contato + Licença + Outros + 开源阅读 + Siga a conta oficial de WeChat + WeChat + O suporte ao meu trabalho é bem-vindo + Conta oficial[开源阅读] + Mudança de fonte + Toque para participar + Médio + Informação + Mudar o layout + Alteração de tamanho da fonte de texto + Suporte aos gestos em tela completa + + + Primário + Acento + Cor de fundo + Cor de NavBar + Dia + Dia,Primário + Dia,Acento + Dia,Cor de fundo + Dia,Cor de NavBar + Noite + Noite,Primário + Noite,Acento + Noite,Cor de fundo + Noite,Cor de NavBar + Mudar a fonte automaticamente + Texto justificado + Texto alinhar fundo + Velocidade de rolagem automática + Ordenar por URL + Faça backup local e de WebDav simultaneamente + Primeiro restaurar de WebDAV, restaurar o local backup via clique longo + Selecione uma pasta legado de backup + Habilitado + Deshabilitado + Iniciando o download + Este livro já está na lista de download + clique para abrir + Siga [开源阅读] para ajudar, clicando nos anúncios + Código de gorjeta no WeChat + AliPay + AliPay código de busca do envelope vermelho + 537954522 Clique para copiar + AliPay código QR envelope vermelho + AliPay código QR + Coleção QQ Código QR + gedoor,Invinciblelee etc. Para mais detalhes vá ao Github + Limpar o cache dos livros e fontes baixados + Capa padrão + Lista de desvios + Ignore alguns conteúdos durante restauração + Configuração da interface de leitura + Nome de grupo + Seção de observações + Habilitar regra de substituição por padrão + Para livros recém adicionados + Selecione Restaurar arquivo + O fundo do dia não pode ser muito escuro! + A parte inferior do dia não pode ser muito escuro! + O fundo noturno não pode ser muito brilhante! + A parte inferior noturno não pode ser muito brilhante! + Tem que haver diferença entre sotaque e cor de fundo + Tem que haver diferença entre sotaque e cor texto + Formato incorreto + Erro + Mostrar o widget de brilho + Idioma + Importar fonte RSS + Sua doação torna este App melhor + Conta oficial de Wechat [开源阅读软件] + Registro de leitura + Leia o resumo dos registros + TTS local + Contagem de tópicos + Tempo total da leitura + Desmarcar todos + Importar str + Exportar str + Salvar configuração do tema + Salvar a configuração do tema do dia + Salvar a configuração do tema da noite + Lista de temas + Salvar, Importar, Compartilhar tema + Mudar o tema padrão + Ordenar por tempo de atualização + Pesquisar conteúdo + Siga [开源阅读] no WeChat para mais fontes de RSS! + Atualmente não há nenhuma fonte de descoberta, siga o número público [开源阅读] para adicionar uma fonte de livro via descoberta! + 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. + Nome de tema + Excluir automaticamente dados vencidos da busca + 超过一天的搜索数据 + 重新分段 + 样式名称: + 点击右上角文件夹图标,选择文件夹 + 智能扫描 + 拷贝书籍URL + 拷贝目录URL + Sem livros + Manter o nome original + Clique em Configurações Regionais + Fechar + Próxima página + Página anterior + Nenhuma Ação + Título do texto principal + Mostrar/ocultar + rodapé cabeçalho + Assinatura de regras + 添加大佬们提供的规则导入地址\n添加后点击可导入规则 + 拉取云端进度 + 当前进度超过云端进度,是否同步? + Progresso de leitura sincronizada + Sincronizar o progresso da leitura ao entrar e sair da tela de leitura + Falha ao criar um marcador de página + URL única + Lista de livros para exportação + 导入书单 + 预下载 + 预先下载%s章正文 + Habilitar a seleção + Imagem de fundo + Pasta de exportação + 导出编码 + Exportar para WebDav + Reverse content + Debug + Crash log + Utilização de filiais chinesas personalizadas + Estilo de imagem + Sistema TTS + Formato de exportação + Revisado por + Procurar código fonte + Código fonte do livro + Código fonte do diretório + Código fonte completo + Listar código fonte + Esta Url é subscrita + 高刷 + Use a maior taxa de atualização da tela + exportar tudo + concluído + Mostrar não lido + sempre usar capa padrão + sempre usar capa padrão,não usar rede + + From 7b1e7b64cbeb2f430d1ec7f82b5c5711f3912ec6 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 15 May 2021 22:08:45 +0800 Subject: [PATCH 005/112] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1327ab258..a55df0711 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ com.positiondev.epublib:epublib-core ## 免责声明 https://gedoor.github.io/MyBookshelf/disclaimer.html +## 其它 +其它网友做的IOS版本: https://github.com/kaich/Yuedu + ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B1.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B2.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B3.jpg) From c47d3d15b783d1a2027ed2574c73ec1bec0388e8 Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 15 May 2021 22:09:23 +0800 Subject: [PATCH 006/112] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a55df0711..d32316966 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,12 @@ com.positiondev.epublib:epublib-core ## 免责声明 https://gedoor.github.io/MyBookshelf/disclaimer.html -## 其它 -其它网友做的IOS版本: https://github.com/kaich/Yuedu - ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B1.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B2.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B3.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B4.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B5.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B6.jpg) + +## 其它 +其它网友做的IOS版本: https://github.com/kaich/Yuedu From 56966c6d871d9ebc0fe1dca84483a83d7595deec Mon Sep 17 00:00:00 2001 From: kunfei Date: Sat, 15 May 2021 22:10:11 +0800 Subject: [PATCH 007/112] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d32316966..22a506a7c 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ com.positiondev.epublib:epublib-core ## 免责声明 https://gedoor.github.io/MyBookshelf/disclaimer.html +## 界面 ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B1.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B2.jpg) ![image](https://github.com/gedoor/gedoor.github.io/blob/master/images/%E9%98%85%E8%AF%BB%E7%AE%80%E4%BB%8B3.jpg) From 7bb6ef0b99a9193c95e341515745f8f91faf7797 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 16 May 2021 17:53:00 +0800 Subject: [PATCH 008/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/config/OtherConfigFragment.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index 527d84732..86e3ca53a 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -157,6 +157,11 @@ class OtherConfigFragment : BasePreferenceFragment(), getString(R.string.pre_download_s, value) PreferKey.threadCount -> preference.summary = getString(R.string.threads_num, value) PreferKey.webPort -> preference.summary = getString(R.string.web_port_summary, value) + PreferKey.defaultCover -> preference.summary = if (value.isNullOrBlank()) { + getString(R.string.select_image) + } else { + value + } else -> if (preference is ListPreference) { val index = preference.findIndexOfValue(value) // Set the summary to reflect the new value. From 5e3182c9da84cf53e17c012068da317c5c8dd910 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 16 May 2021 17:55:40 +0800 Subject: [PATCH 009/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 4b204b77e..cabdfc725 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,6 +3,11 @@ * 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2021/05/16** +* 添加总是使用默认封面配置 +* 添加一种语言 ptbr translation by mezysinc +* epublib 修bug by ag2s20150909 + **2021/05/12** * 修复预下载bug * 添加显示未读开关,书架布局中 From 7e1b24c83c459e8a1665523017c21373dfe2ff84 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 16 May 2021 18:04:14 +0800 Subject: [PATCH 010/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/base/BasePreferenceFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/io/legado/app/base/BasePreferenceFragment.kt b/app/src/main/java/io/legado/app/base/BasePreferenceFragment.kt index 6166b7a5d..f444985e3 100644 --- a/app/src/main/java/io/legado/app/base/BasePreferenceFragment.kt +++ b/app/src/main/java/io/legado/app/base/BasePreferenceFragment.kt @@ -53,6 +53,7 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat() { ) } } + @Suppress("DEPRECATION") f.setTargetFragment(this, 0) f.show(parentFragmentManager, dialogFragmentTag) From 7a89e236c663104e5c82e14b3d25da02cad5c498 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 17 May 2021 13:44:09 +0800 Subject: [PATCH 011/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/rss/source/manage/GroupManageDialog.kt | 2 ++ app/src/main/res/layout/item_group_manage.xml | 13 ++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt index 2b49eea70..464f58a0b 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt @@ -21,6 +21,7 @@ import io.legado.app.databinding.DialogRecyclerViewBinding import io.legado.app.databinding.ItemGroupManageBinding import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.accentColor +import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.* @@ -128,6 +129,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener payloads: MutableList ) { with(binding) { + root.setBackgroundColor(context.backgroundColor) tvGroup.text = item } } diff --git a/app/src/main/res/layout/item_group_manage.xml b/app/src/main/res/layout/item_group_manage.xml index 763a19acd..183243cb6 100644 --- a/app/src/main/res/layout/item_group_manage.xml +++ b/app/src/main/res/layout/item_group_manage.xml @@ -3,15 +3,16 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/background" - android:padding="8dp" android:gravity="center_vertical" - android:orientation="horizontal"> + android:orientation="horizontal" + android:padding="8dp"> + android:layout_weight="1" + android:textColor="@color/primaryText" /> + android:text="@string/edit" + android:textColor="@color/primaryText" /> + android:text="@string/delete" + android:textColor="@color/primaryText" /> \ No newline at end of file From 073754aeafe61e3db728871d0babe78b91aa58f5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 17 May 2021 13:53:29 +0800 Subject: [PATCH 012/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/item_1line_text_and_del.xml | 7 +++++-- app/src/main/res/layout/item_arrange_book.xml | 3 +++ app/src/main/res/layout/item_book_group_manage.xml | 12 ++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/item_1line_text_and_del.xml b/app/src/main/res/layout/item_1line_text_and_del.xml index 9bb5e9c2a..85af06345 100644 --- a/app/src/main/res/layout/item_1line_text_and_del.xml +++ b/app/src/main/res/layout/item_1line_text_and_del.xml @@ -1,5 +1,6 @@ + android:singleLine="true" + android:textColor="@color/primaryText" /> + app:tint="@color/primaryText" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_arrange_book.xml b/app/src/main/res/layout/item_arrange_book.xml index 7323a53c7..bc397d43a 100644 --- a/app/src/main/res/layout/item_arrange_book.xml +++ b/app/src/main/res/layout/item_arrange_book.xml @@ -52,6 +52,7 @@ android:padding="3dp" android:hint="@string/no_group" android:textSize="12sp" + android:textColor="@color/secondaryText" android:singleLine="true" android:layout_marginTop="8dp" app:layout_constraintLeft_toRightOf="@+id/checkbox" @@ -66,6 +67,7 @@ android:background="?attr/selectableItemBackgroundBorderless" android:padding="10dp" android:text="@string/group" + android:textColor="@color/secondaryText" app:layout_constraintTop_toBottomOf="@id/tv_author" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toLeftOf="@+id/tv_delete" /> @@ -77,6 +79,7 @@ android:background="?attr/selectableItemBackgroundBorderless" android:padding="10dp" android:text="@string/delete" + android:textColor="@color/secondaryText" app:layout_constraintTop_toBottomOf="@id/tv_author" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" /> diff --git a/app/src/main/res/layout/item_book_group_manage.xml b/app/src/main/res/layout/item_book_group_manage.xml index 7c41898db..1bfe4d9c8 100644 --- a/app/src/main/res/layout/item_book_group_manage.xml +++ b/app/src/main/res/layout/item_book_group_manage.xml @@ -1,17 +1,20 @@ + android:orientation="horizontal" + android:padding="8dp"> + android:layout_weight="1" + android:textColor="@color/secondaryText" + tool:text="group" /> + android:text="@string/edit" + android:textColor="@color/secondaryText" /> Date: Mon, 17 May 2021 14:03:15 +0800 Subject: [PATCH 013/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/dialog_tip_config.xml | 36 ++++++++++++++----- .../main/res/layout/view_detail_seek_bar.xml | 2 ++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/layout/dialog_tip_config.xml b/app/src/main/res/layout/dialog_tip_config.xml index 1a5eeebd5..f28421f1f 100644 --- a/app/src/main/res/layout/dialog_tip_config.xml +++ b/app/src/main/res/layout/dialog_tip_config.xml @@ -85,13 +85,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/show_hide" /> + android:text="@string/show_hide" + android:textColor="@color/primaryText" /> @@ -109,13 +111,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/left" /> + android:text="@string/left" + android:textColor="@color/primaryText" /> @@ -133,13 +137,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/middle" /> + android:text="@string/middle" + android:textColor="@color/primaryText" /> @@ -157,13 +163,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/right" /> + android:text="@string/right" + android:textColor="@color/primaryText" /> @@ -187,13 +195,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/show_hide" /> + android:text="@string/show_hide" + android:textColor="@color/primaryText" /> @@ -211,13 +221,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/left" /> + android:text="@string/left" + android:textColor="@color/primaryText" /> @@ -235,13 +247,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/middle" /> + android:text="@string/middle" + android:textColor="@color/primaryText" /> @@ -259,13 +273,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/right" /> + android:text="@string/right" + android:textColor="@color/primaryText" /> @@ -289,13 +305,15 @@ android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dp" - android:text="@string/text_color" /> + android:text="@string/text_color" + android:textColor="@color/primaryText" /> diff --git a/app/src/main/res/layout/view_detail_seek_bar.xml b/app/src/main/res/layout/view_detail_seek_bar.xml index cfcec885f..675268252 100644 --- a/app/src/main/res/layout/view_detail_seek_bar.xml +++ b/app/src/main/res/layout/view_detail_seek_bar.xml @@ -12,6 +12,7 @@ android:layout_height="wrap_content" android:singleLine="true" android:text="seek" + android:textColor="@color/primaryText" tools:ignore="HardcodedText" /> \ No newline at end of file From e88df8b2e294a65f0206740d867d8223dab508ae Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 17 May 2021 14:10:00 +0800 Subject: [PATCH 014/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/item_book_group_manage.xml | 4 ++-- app/src/main/res/layout/item_theme_config.xml | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/item_book_group_manage.xml b/app/src/main/res/layout/item_book_group_manage.xml index 1bfe4d9c8..7b743a515 100644 --- a/app/src/main/res/layout/item_book_group_manage.xml +++ b/app/src/main/res/layout/item_book_group_manage.xml @@ -1,6 +1,6 @@ + tools:text="group" /> + android:maxLines="1" + android:textColor="@color/primaryText" + tools:text="theme" /> Date: Mon, 17 May 2021 14:48:38 +0800 Subject: [PATCH 015/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/item_read_record.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/item_read_record.xml b/app/src/main/res/layout/item_read_record.xml index 7c6316058..e3cd01e07 100644 --- a/app/src/main/res/layout/item_read_record.xml +++ b/app/src/main/res/layout/item_read_record.xml @@ -13,10 +13,12 @@ android:layout_height="wrap_content" android:padding="6dp" android:textIsSelectable="true" + android:textColor="@color/primaryText" android:singleLine="true" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toLeftOf="@+id/tv_remove" + tools:text="name" tools:ignore="RtlHardcoded" /> + app:layout_constraintRight_toLeftOf="@+id/tv_remove" + tools:text="time" /> From 7342cee660a494398ab4c796a2cb839037d0a2e5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 17 May 2021 15:36:31 +0800 Subject: [PATCH 016/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/lib/theme/MaterialValueHelper.kt | 4 +-- .../ui/widget/text/AccentStrokeTextView.kt | 31 +++++++++++++++++-- app/src/main/res/color/selector_image.xml | 2 +- .../res/layout/view_select_action_bar.xml | 23 ++++++++------ app/src/main/res/values-night/colors.xml | 2 ++ app/src/main/res/values/attrs.xml | 5 +++ app/src/main/res/values/colors.xml | 3 ++ 7 files changed, 54 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt index 5dadc5d85..786db41c9 100644 --- a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt +++ b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt @@ -101,9 +101,9 @@ val Fragment.secondaryDisabledTextColor: Int val Context.buttonDisabledColor: Int get() = if (isDarkTheme) { - ContextCompat.getColor(this, R.color.ate_button_disabled_dark) + ContextCompat.getColor(this, R.color.md_dark_disabled) } else { - ContextCompat.getColor(this, R.color.ate_button_disabled_light) + ContextCompat.getColor(this, R.color.md_light_disabled) } val Context.isDarkTheme: Boolean diff --git a/app/src/main/java/io/legado/app/ui/widget/text/AccentStrokeTextView.kt b/app/src/main/java/io/legado/app/ui/widget/text/AccentStrokeTextView.kt index 37b18051a..abd14615a 100644 --- a/app/src/main/java/io/legado/app/ui/widget/text/AccentStrokeTextView.kt +++ b/app/src/main/java/io/legado/app/ui/widget/text/AccentStrokeTextView.kt @@ -6,25 +6,50 @@ import androidx.appcompat.widget.AppCompatTextView import io.legado.app.R import io.legado.app.lib.theme.Selector import io.legado.app.lib.theme.ThemeStore +import io.legado.app.lib.theme.bottomBackground +import io.legado.app.utils.ColorUtils import io.legado.app.utils.dp import io.legado.app.utils.getCompatColor class AccentStrokeTextView(context: Context, attrs: AttributeSet) : AppCompatTextView(context, attrs) { + private var radius = 3.dp + private val isBottomBackground: Boolean + init { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.AccentStrokeTextView) + radius = typedArray.getDimensionPixelOffset(R.styleable.StrokeTextView_radius, radius) + isBottomBackground = + typedArray.getBoolean(R.styleable.StrokeTextView_isBottomBackground, false) + typedArray.recycle() + upStyle() + } + + private fun upStyle() { + val isLight = ColorUtils.isColorLight(context.bottomBackground) + val disableColor = if (isBottomBackground) { + if (isLight) { + context.getCompatColor(R.color.md_light_disabled) + } else { + context.getCompatColor(R.color.md_dark_disabled) + } + } else { + context.getCompatColor(R.color.disabled) + } background = Selector.shapeBuild() - .setCornerRadius(3.dp) + .setCornerRadius(radius) .setStrokeWidth(1.dp) - .setDisabledStrokeColor(context.getCompatColor(R.color.md_grey_500)) + .setDisabledStrokeColor(disableColor) .setDefaultStrokeColor(ThemeStore.accentColor(context)) .setPressedBgColor(context.getCompatColor(R.color.transparent30)) .create() setTextColor( Selector.colorBuild() .setDefaultColor(ThemeStore.accentColor(context)) - .setDisabledColor(context.getCompatColor(R.color.md_grey_500)) + .setDisabledColor(disableColor) .create() ) } + } diff --git a/app/src/main/res/color/selector_image.xml b/app/src/main/res/color/selector_image.xml index 081b03cf0..ffc800f9f 100644 --- a/app/src/main/res/color/selector_image.xml +++ b/app/src/main/res/color/selector_image.xml @@ -1,6 +1,6 @@ - + diff --git a/app/src/main/res/layout/view_select_action_bar.xml b/app/src/main/res/layout/view_select_action_bar.xml index eddfb0fc4..a58f77df7 100644 --- a/app/src/main/res/layout/view_select_action_bar.xml +++ b/app/src/main/res/layout/view_select_action_bar.xml @@ -1,15 +1,16 @@ + android:padding="5dp" + android:text="@string/revert_selection" + app:isBottomBackground="true" /> + app:isBottomBackground="true" /> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index db09b5da3..bc6e12b19 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -4,6 +4,8 @@ @color/md_blue_grey_700 @color/md_deep_orange_800 + @color/md_dark_disabled + @color/md_grey_900 @color/md_grey_850 @color/md_grey_800 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index bdb8fd0b1..fd11b974c 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -167,6 +167,11 @@ + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5eb13fbda..92799cbea 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,6 +3,9 @@ @color/md_light_blue_600 @color/md_light_blue_700 @color/md_pink_800 + + @color/md_light_disabled + #66666666 #FF578FCC From 9c0da1de98932d61cf3770621584015c0d7a1f6b Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 17 May 2021 15:49:35 +0800 Subject: [PATCH 017/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/colors.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 92799cbea..81726a580 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -19,6 +19,7 @@ @color/md_grey_100 @color/md_grey_200 #7fffffff + #00000000 #10000000 From 3a9d9c6eae5d7e87b8a4753cee9029b55719aa44 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 18 May 2021 17:42:53 +0800 Subject: [PATCH 018/112] =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=99=BB=E5=BD=95=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../legado/app/ui/book/info/BookInfoActivity.kt | 14 ++++++++------ .../legado/app/ui/book/info/BookInfoViewModel.kt | 16 ++++++++++++++-- .../{SourceLogin.kt => SourceLoginActivity.kt} | 2 +- app/src/main/res/menu/book_info.xml | 5 +++++ 5 files changed, 29 insertions(+), 10 deletions(-) rename app/src/main/java/io/legado/app/ui/login/{SourceLogin.kt => SourceLoginActivity.kt} (97%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 25c89461a..18c558fe6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -286,7 +286,7 @@ android:launchMode="singleTop" /> diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index 5d06dc899..127405931 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -105,6 +105,14 @@ class BookInfoActivity : return super.onCompatCreateOptionsMenu(menu) } + override fun onMenuOpened(featureId: Int, menu: Menu): Boolean { + menu.findItem(R.id.menu_can_update)?.isChecked = + viewModel.bookData.value?.canUpdate ?: true + menu.findItem(R.id.menu_login)?.isVisible = + !viewModel.bookSource?.loginUrl.isNullOrBlank() + return super.onMenuOpened(featureId, menu) + } + override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.menu_edit -> { @@ -156,12 +164,6 @@ class BookInfoActivity : return super.onCompatOptionsItemSelected(item) } - override fun onMenuOpened(featureId: Int, menu: Menu): Boolean { - menu.findItem(R.id.menu_can_update)?.isChecked = - viewModel.bookData.value?.canUpdate ?: true - return super.onMenuOpened(featureId, menu) - } - private fun showBook(book: Book) = with(binding) { showCover(book) tvName.text = book.name diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt index 3cb698ef3..39c09fc69 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt @@ -8,6 +8,7 @@ import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter +import io.legado.app.data.entities.BookSource import io.legado.app.help.BookHelp import io.legado.app.model.localBook.LocalBook import io.legado.app.model.webBook.WebBook @@ -19,6 +20,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { val chapterListData = MutableLiveData>() var durChapterIndex = 0 var inBookshelf = false + var bookSource: BookSource? = null fun initData(intent: Intent) { execute { @@ -36,6 +38,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { private fun setBook(book: Book) { durChapterIndex = book.durChapterIndex bookData.postValue(book) + initBookSource(book) if (book.tocUrl.isEmpty()) { loadBookInfo(book) } else { @@ -48,6 +51,14 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { } } + private fun initBookSource(book: Book) { + bookSource = if (book.isLocalBook()) { + null + } else { + appDb.bookSourceDao.getBookSource(book.origin) + } + } + fun loadBookInfo( book: Book, canReName: Boolean = true, changeDruChapterIndex: ((chapters: List) -> Unit)? = null, @@ -56,7 +67,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { if (book.isLocalBook()) { loadChapter(book, changeDruChapterIndex) } else { - appDb.bookSourceDao.getBookSource(book.origin)?.let { bookSource -> + bookSource?.let { bookSource -> WebBook(bookSource).getBookInfo(this, book, canReName = canReName) .onSuccess(IO) { bookData.postValue(book) @@ -87,7 +98,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { chapterListData.postValue(it) } } else { - appDb.bookSourceDao.getBookSource(book.origin)?.let { bookSource -> + bookSource?.let { bookSource -> WebBook(bookSource).getChapterList(this, book) .onSuccess(IO) { if (it.isNotEmpty()) { @@ -135,6 +146,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { } } bookData.postValue(newBook) + initBookSource(newBook) if (newBook.tocUrl.isEmpty()) { loadBookInfo(newBook, false) { upChangeDurChapterIndex(newBook, oldTocSize, it) diff --git a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt b/app/src/main/java/io/legado/app/ui/login/SourceLoginActivity.kt similarity index 97% rename from app/src/main/java/io/legado/app/ui/login/SourceLogin.kt rename to app/src/main/java/io/legado/app/ui/login/SourceLoginActivity.kt index 028b85b98..d4ee0dd23 100644 --- a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt +++ b/app/src/main/java/io/legado/app/ui/login/SourceLoginActivity.kt @@ -15,7 +15,7 @@ import io.legado.app.help.http.CookieStore import io.legado.app.utils.snackbar -class SourceLogin : BaseActivity() { +class SourceLoginActivity : BaseActivity() { var sourceUrl: String? = null var loginUrl: String? = null diff --git a/app/src/main/res/menu/book_info.xml b/app/src/main/res/menu/book_info.xml index ad5eae7b2..0d8c97281 100644 --- a/app/src/main/res/menu/book_info.xml +++ b/app/src/main/res/menu/book_info.xml @@ -19,6 +19,11 @@ android:title="@string/refresh" app:showAsAction="never" /> + + Date: Tue, 18 May 2021 17:47:42 +0800 Subject: [PATCH 019/112] =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=99=BB=E5=BD=95=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/book/read/ReadBookActivity.kt | 4 ++-- .../legado/app/ui/book/source/edit/BookSourceEditActivity.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 2e616ab44..8dc70a332 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -47,7 +47,7 @@ import io.legado.app.ui.book.read.page.provider.TextPageFactory import io.legado.app.ui.book.searchContent.SearchContentActivity import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.toc.TocActivityResult -import io.legado.app.ui.login.SourceLogin +import io.legado.app.ui.login.SourceLoginActivity import io.legado.app.ui.replace.ReplaceRuleActivity import io.legado.app.ui.replace.edit.ReplaceEditActivity import io.legado.app.ui.widget.dialog.TextDialog @@ -773,7 +773,7 @@ class ReadBookActivity : ReadBookBaseActivity(), override fun showLogin() { ReadBook.webBook?.bookSource?.let { - startActivity { + startActivity { putExtra("sourceUrl", it.bookSourceUrl) putExtra("loginUrl", it.loginUrl) putExtra("userAgent", it.getHeaderMap()[AppConst.UA_NAME]) diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt index 89fa6dc21..5635a1e9e 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt @@ -26,7 +26,7 @@ import io.legado.app.lib.theme.backgroundColor import io.legado.app.ui.book.source.debug.BookSourceDebugActivity import io.legado.app.ui.document.FilePicker import io.legado.app.ui.document.FilePickerParam -import io.legado.app.ui.login.SourceLogin +import io.legado.app.ui.login.SourceLoginActivity import io.legado.app.ui.qrcode.QrCodeResult import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.ui.widget.dialog.TextDialog @@ -120,7 +120,7 @@ class BookSourceEditActivity : if (it.loginUrl.isNullOrEmpty()) { toastOnUi(R.string.source_no_login) } else { - startActivity { + startActivity { putExtra("sourceUrl", it.bookSourceUrl) putExtra("loginUrl", it.loginUrl) putExtra("userAgent", it.getHeaderMap()[AppConst.UA_NAME]) From 5951008c51b01aeb40817b752df22cc5cb4659db Mon Sep 17 00:00:00 2001 From: fisher Date: Wed, 19 May 2021 00:13:05 +0800 Subject: [PATCH 020/112] Update English --- app/src/main/res/values/strings.xml | 68 ++++++++++++++--------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b2ba15475..9825fd29f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -763,8 +763,8 @@ Thread count Total read time Unselect all - Import str - Export str + Import + Export Save theme config Save day theme config Save night theme config @@ -773,40 +773,40 @@ Share selected sources Sort by update time Search content - 关注公众号[开源阅读]获取订阅源! - 当前没有发现源,关注公众号[开源阅读]添加带发现的书源! + follow Wechat official account [开源阅读] to get Subscription source + empty now,Wechat official account [开源阅读] to get Discovery source 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. - 主题名称 - 自动清除过期搜索数据 - 超过一天的搜索数据 - 重新分段 - 样式名称: - 点击右上角文件夹图标,选择文件夹 - 智能扫描 - 拷贝URL - 没有书籍 - 保留原名 - 点击区域设置 - 关闭 - 下一页 - 上一页 - 无操作 - 正文标题 - 显示/隐藏 + Theme name + "Clear expired search histories automatically " + Search histories more than one day + Re-segment + Style name: + Click the folder icon in the upper right corner and select the folder + Intelligent scanning + Copy URL + No books + Keep the original name + Screen touch control + Close + Next page + Prior page + None + Title + Show/Hide footer header - 规则订阅 + Rule Subscription 添加大佬们提供的规则导入地址\n添加后点击可导入规则 - 拉取云端进度 - 当前进度超过云端进度,是否同步? - 同步阅读进度 - 进入退出阅读界面时同步阅读进度 - 创建书签失败 - 单URL - 导出书单 - 导入书单 - 预下载 - 预先下载10章正文 - 启用排序 - 背景图片 + Pull the cloud progress + The current progress exceeds the cloud progress. Do you want to synchronize? + "Synchronous reading progress " + "Synchronize reading progress when entering / exiting the reading interface " + Failed to create bookmark + Single URL + Export the list of books + Import the list of books + Download in advance + Download 10 chapters in advance + Enable sorting + Background image From 8595f5d4279c4c42b72ae651844c77105e304ae6 Mon Sep 17 00:00:00 2001 From: fisher Date: Wed, 19 May 2021 01:56:24 +0800 Subject: [PATCH 021/112] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BF=BB=E8=AF=91/?= =?UTF-8?q?=E5=B8=AE=E5=8A=A9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/help/appHelp.md | 5 +-- app/src/main/res/layout/dialog_tip_config.xml | 6 ++-- app/src/main/res/values-zh-rHK/strings.xml | 3 ++ app/src/main/res/values-zh-rTW/strings.xml | 3 ++ app/src/main/res/values-zh/strings.xml | 3 ++ app/src/main/res/values/strings.xml | 34 +++++++++++++++++-- 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/app/src/main/assets/help/appHelp.md b/app/src/main/assets/help/appHelp.md index 9b44508c5..d7e4fb12b 100644 --- a/app/src/main/assets/help/appHelp.md +++ b/app/src/main/assets/help/appHelp.md @@ -10,10 +10,7 @@ 2. 正文出现缺字漏字、内容缺失、排版错乱等情况,如何处理? * 有可能是净化规则出现问题,先关闭替换净化并刷新,再观察是否正常。如果正常说明净化规则存在误杀,如果关闭后仍然出现相关问题,请点击源链接查看原文与正文是否相同,如果不同,再进行反馈。 -3. “勐”、“收十”、“猫腻”、“发动机”、“沃尔沃”等词无法替换净化,如何解决? -* 请在 界面 - 简/繁 中关闭简繁互转功能. - -4. 漫画源看书显示乱码,如何解决? +3. 漫画源看书显示乱码,如何解决? * 异次元和阅读是两个不同的软件,**两个软件的源并不通用**,请导入阅读的支持的漫画源! ## 书源相关 diff --git a/app/src/main/res/layout/dialog_tip_config.xml b/app/src/main/res/layout/dialog_tip_config.xml index f28421f1f..55bfb6b4e 100644 --- a/app/src/main/res/layout/dialog_tip_config.xml +++ b/app/src/main/res/layout/dialog_tip_config.xml @@ -47,7 +47,7 @@ android:layout_height="wrap_content" android:padding="3dp" app:max="10" - app:title="字号" /> + app:title="@string/title_font_size" /> + app:title="@string/title_margin_top" /> + app:title="@string/title_margin_bottom" /> 显示未读标志 总是使用默认封面 总是显示默认封面,不显示网络封面 + 字號 + 上邊距 + 下邊距 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 55ac557d9..93a04e6d6 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -831,5 +831,8 @@ 显示未读标志 总是使用默认封面 总是显示默认封面,不显示网络封面 + 字號 + 上邊距 + 下邊距 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 1da9bdf53..79cb504bf 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -831,5 +831,8 @@ 显示未读标志 总是使用默认封面 总是显示默认封面,不显示网络封面 + 字号 + 上边距 + 下边距 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 712d51e07..5af97d9d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -352,7 +352,7 @@ Sort Sort automatically Sort manually - Sort name + Sort by name Scroll to the top Scroll to the bottom Read: %s @@ -765,7 +765,7 @@ Import Export Save theme config - Save day theme config + 在状态栏 Save night theme config Theme list Save, Import, Share theme @@ -807,5 +807,33 @@ Download 10 chapters in advance Enable sorting Background image - + Copy book URL + Copy chapters URL + Export to a folder + Exported text coding + Export to WebDav + Reverse content + Debug + Crash log + Custom Chinese line feed + Style of Images + System tts + Exported file format + Check by author + "This URL has subscribed " + High screen refresh rate + Use maximum screen refresh rate + Export all + Finished + "Show unread flag " + Always show default cover + Always show the default cover, do not show the network cover + Search source code + Book source code + Chapters source code + Content source code + List source code + Font size + Margin top + Marigin bottom From d2995f3328401d50ac7f6f9bdd653ecab25f895b Mon Sep 17 00:00:00 2001 From: fisher Date: Wed, 19 May 2021 02:00:52 +0800 Subject: [PATCH 022/112] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5af97d9d2..624db85d0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -723,7 +723,7 @@ Disabled Starting download This book is already in Download list - click to open + Click to open Follow [开源阅读] to support me by clicking on ads WeChat Tipping Code AliPay @@ -765,7 +765,7 @@ Import Export Save theme config - 在状态栏 + Save day theme config Save night theme config Theme list Save, Import, Share theme From d0e41c2fca88be5685a8f53d84960c8d87977e61 Mon Sep 17 00:00:00 2001 From: fisher Date: Wed, 19 May 2021 02:18:27 +0800 Subject: [PATCH 023/112] Update English Translate --- app/src/main/res/values/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 624db85d0..2d5825c47 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -772,8 +772,8 @@ Switch default theme Sort by update time Search content - follow Wechat official account [开源阅读] to get Subscription source - empty now,Wechat official account [开源阅读] to get Discovery source + Follow Wechat official account [开源阅读] to get Subscription source + Empty now,Wechat official account [开源阅读] to get Discovery source 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. Theme name "Clear expired search histories automatically " @@ -797,8 +797,8 @@ 添加大佬们提供的规则导入地址\n添加后点击可导入规则 Pull the cloud progress The current progress exceeds the cloud progress. Do you want to synchronize? - "Synchronous reading progress " - "Synchronize reading progress when entering / exiting the reading interface " + Synchronous reading progress + Synchronize reading progress when entering / exiting the reading interface Failed to create bookmark Single URL Export the list of books @@ -820,12 +820,12 @@ System tts Exported file format Check by author - "This URL has subscribed " + This URL has subscribed High screen refresh rate Use maximum screen refresh rate Export all Finished - "Show unread flag " + Show unread flag Always show default cover Always show the default cover, do not show the network cover Search source code From 2a6ceff1c5960ae3bff3419867a4f0527edd12aa Mon Sep 17 00:00:00 2001 From: fisher Date: Wed, 19 May 2021 02:22:39 +0800 Subject: [PATCH 024/112] Update English Translate --- app/src/main/res/values/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 624db85d0..6511060d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -772,8 +772,8 @@ Switch default theme Sort by update time Search content - follow Wechat official account [开源阅读] to get Subscription source - empty now,Wechat official account [开源阅读] to get Discovery source + Follow Wechat official account [开源阅读] to get Subscription source + Empty now,Follow Wechat official account [开源阅读] to get Discovery source 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. Theme name "Clear expired search histories automatically " @@ -797,8 +797,8 @@ 添加大佬们提供的规则导入地址\n添加后点击可导入规则 Pull the cloud progress The current progress exceeds the cloud progress. Do you want to synchronize? - "Synchronous reading progress " - "Synchronize reading progress when entering / exiting the reading interface " + Synchronous reading progress + Synchronize reading progress when entering / exiting the reading interface Failed to create bookmark Single URL Export the list of books @@ -820,12 +820,12 @@ System tts Exported file format Check by author - "This URL has subscribed " + This URL has subscribed High screen refresh rate Use maximum screen refresh rate Export all Finished - "Show unread flag " + Show unread flag Always show default cover Always show the default cover, do not show the network cover Search source code From aba59e2ec00a0dd22e98afa57b63c1c573657dfc Mon Sep 17 00:00:00 2001 From: fisher Date: Wed, 19 May 2021 02:29:55 +0800 Subject: [PATCH 025/112] Update English Translate --- app/src/main/res/values/strings.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c44f4856d..6511060d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -773,11 +773,7 @@ Sort by update time Search content Follow Wechat official account [开源阅读] to get Subscription source -<<<<<<< HEAD Empty now,Follow Wechat official account [开源阅读] to get Discovery source -======= - Empty now,Wechat official account [开源阅读] to get Discovery source ->>>>>>> 39008a9f0006a3eee1ef43d908c6a98475e51f10 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. Theme name "Clear expired search histories automatically " From 90aa72fa6fecec59b89e5543432e656feff337a3 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 19 May 2021 08:06:24 +0800 Subject: [PATCH 026/112] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/menu/book_source.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-zh-rHK/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values-zh/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/menu/book_source.xml b/app/src/main/res/menu/book_source.xml index 7913a9943..137b1b8ce 100644 --- a/app/src/main/res/menu/book_source.xml +++ b/app/src/main/res/menu/book_source.xml @@ -54,7 +54,7 @@ + android:title="@string/is_enabled" /> diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 041277c1e..80e78ca7e 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -806,7 +806,7 @@ 导入书单 预下载 预先下载%s章正文 - Habilitar a seleção + Habilitar a seleção Imagem de fundo Pasta de exportação 导出编码 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 5e4fa1183..60ed31d37 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -801,7 +801,7 @@ 导入书单 预下载 预先下载%s章正文 - 启用排序 + 是否启用 背景图片 导出文件夹 导出编码 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 93a04e6d6..57710372a 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -805,7 +805,7 @@ 匯入書單 預下載 預先下載%s章正文 - 啟用排序 + 是否啟用 背景圖片 匯出資料夾 匯出編碼 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 79cb504bf..55b77b4d2 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -805,7 +805,7 @@ 导入书单 预下载 预先下载%s章正文 - 启用排序 + 是否启用 背景图片 导出文件夹 导出编码 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6511060d2..99fab1d9f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -805,7 +805,7 @@ Import the list of books Download in advance Download 10 chapters in advance - Enable sorting + Is enabled Background image Copy book URL Copy chapters URL From 4d2a7e5d7d87fd5f93db4f7aae22c9fcbb185218 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 19 May 2021 08:18:46 +0800 Subject: [PATCH 027/112] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/help/ReadTipConfig.kt | 11 +++++++++-- app/src/main/res/values-pt-rBR/strings.xml | 6 ++++++ app/src/main/res/values-zh-rHK/strings.xml | 3 +++ app/src/main/res/values-zh-rTW/strings.xml | 3 +++ app/src/main/res/values-zh/strings.xml | 3 +++ app/src/main/res/values/strings.xml | 5 ++++- 6 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/ReadTipConfig.kt b/app/src/main/java/io/legado/app/help/ReadTipConfig.kt index d962f64c0..e52ac22ec 100644 --- a/app/src/main/java/io/legado/app/help/ReadTipConfig.kt +++ b/app/src/main/java/io/legado/app/help/ReadTipConfig.kt @@ -8,10 +8,17 @@ object ReadTipConfig { appCtx.resources.getStringArray(R.array.read_tip).toList() } val headerModes by lazy { - linkedMapOf(0 to "状态栏显示时隐藏", 1 to "显示", 2 to "隐藏") + linkedMapOf( + Pair(0, appCtx.getString(R.string.hide_when_status_bar_show)), + Pair(1, appCtx.getString(R.string.show)), + Pair(2, appCtx.getString(R.string.hide)) + ) } val footerModes by lazy { - linkedMapOf(0 to "显示", 1 to "隐藏") + linkedMapOf( + Pair(0, appCtx.getString(R.string.show)), + Pair(1, appCtx.getString(R.string.hide)) + ) } const val none = 0 const val chapterTitle = 1 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 80e78ca7e..11a473d30 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -832,5 +832,11 @@ Mostrar não lido sempre usar capa padrão sempre usar capa padrão,não usar rede + Font size + Margin top + Marigin bottom + Show + Hide + Hide when status bar show diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 60ed31d37..9baf31451 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -830,5 +830,8 @@ 字號 上邊距 下邊距 + 显示 + 隐藏 + 状态栏显示时隐藏 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 57710372a..8393ace59 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -834,5 +834,8 @@ 字號 上邊距 下邊距 + 显示 + 隐藏 + 状态栏显示时隐藏 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 55b77b4d2..bc4d43fe5 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -834,5 +834,8 @@ 字号 上边距 下边距 + 显示 + 隐藏 + 状态栏显示时隐藏 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 99fab1d9f..21ec7dbb1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -830,10 +830,13 @@ Always show the default cover, do not show the network cover Search source code Book source code - Chapters source code + Chapters source code Content source code List source code Font size Margin top Marigin bottom + Show + Hide + Hide when status bar show From e73f7d8ba5035f5bffa439edc57690597d490b8b Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 19 May 2021 08:31:34 +0800 Subject: [PATCH 028/112] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/help/ReadTipConfig.kt | 29 ++++++++++--------- .../ui/book/read/config/TipConfigDialog.kt | 18 +++++++----- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/ReadTipConfig.kt b/app/src/main/java/io/legado/app/help/ReadTipConfig.kt index e52ac22ec..5bff7869d 100644 --- a/app/src/main/java/io/legado/app/help/ReadTipConfig.kt +++ b/app/src/main/java/io/legado/app/help/ReadTipConfig.kt @@ -1,5 +1,6 @@ package io.legado.app.help +import android.content.Context import io.legado.app.R import splitties.init.appCtx @@ -7,19 +8,6 @@ object ReadTipConfig { val tips by lazy { appCtx.resources.getStringArray(R.array.read_tip).toList() } - val headerModes by lazy { - linkedMapOf( - Pair(0, appCtx.getString(R.string.hide_when_status_bar_show)), - Pair(1, appCtx.getString(R.string.show)), - Pair(2, appCtx.getString(R.string.hide)) - ) - } - val footerModes by lazy { - linkedMapOf( - Pair(0, appCtx.getString(R.string.show)), - Pair(1, appCtx.getString(R.string.hide)) - ) - } const val none = 0 const val chapterTitle = 1 const val time = 2 @@ -90,4 +78,19 @@ object ReadTipConfig { set(value) { ReadBookConfig.config.tipColor = value } + + fun getHeaderModes(context: Context): LinkedHashMap { + return linkedMapOf( + Pair(0, context.getString(R.string.hide_when_status_bar_show)), + Pair(1, context.getString(R.string.show)), + Pair(2, context.getString(R.string.hide)) + ) + } + + fun getFooterModes(context: Context): LinkedHashMap { + return linkedMapOf( + Pair(0, context.getString(R.string.show)), + Pair(1, context.getString(R.string.hide)) + ) + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/TipConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/TipConfigDialog.kt index 8e0d74b74..f058b2da1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/TipConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/TipConfigDialog.kt @@ -52,8 +52,8 @@ class TipConfigDialog : BaseDialogFragment() { dsbTitleTop.progress = ReadBookConfig.titleTopSpacing dsbTitleBottom.progress = ReadBookConfig.titleBottomSpacing - tvHeaderShow.text = ReadTipConfig.headerModes[ReadTipConfig.headerMode] - tvFooterShow.text = ReadTipConfig.footerModes[ReadTipConfig.footerMode] + tvHeaderShow.text = ReadTipConfig.getHeaderModes(requireContext())[ReadTipConfig.headerMode] + tvFooterShow.text = ReadTipConfig.getFooterModes(requireContext())[ReadTipConfig.footerMode] tvHeaderLeft.text = ReadTipConfig.tipHeaderLeftStr tvHeaderMiddle.text = ReadTipConfig.tipHeaderMiddleStr @@ -92,16 +92,18 @@ class TipConfigDialog : BaseDialogFragment() { postEvent(EventBus.UP_CONFIG, true) } llHeaderShow.setOnClickListener { - selector(items = ReadTipConfig.headerModes.values.toList()) { _, i -> - ReadTipConfig.headerMode = ReadTipConfig.headerModes.keys.toList()[i] - tvHeaderShow.text = ReadTipConfig.headerModes[ReadTipConfig.headerMode] + val headerModes = ReadTipConfig.getHeaderModes(requireContext()) + selector(items = headerModes.values.toList()) { _, i -> + ReadTipConfig.headerMode = headerModes.keys.toList()[i] + tvHeaderShow.text = headerModes[ReadTipConfig.headerMode] postEvent(EventBus.UP_CONFIG, true) } } llFooterShow.setOnClickListener { - selector(items = ReadTipConfig.footerModes.values.toList()) { _, i -> - ReadTipConfig.footerMode = ReadTipConfig.footerModes.keys.toList()[i] - tvFooterShow.text = ReadTipConfig.footerModes[ReadTipConfig.footerMode] + val footerModes = ReadTipConfig.getFooterModes(requireContext()) + selector(items = footerModes.values.toList()) { _, i -> + ReadTipConfig.footerMode = footerModes.keys.toList()[i] + tvFooterShow.text = footerModes[ReadTipConfig.footerMode] postEvent(EventBus.UP_CONFIG, true) } } From 57655f5000295d6642b30932276b5397399508df Mon Sep 17 00:00:00 2001 From: Celeter <48249130+Celeter@users.noreply.github.com> Date: Wed, 19 May 2021 12:17:12 +0800 Subject: [PATCH 029/112] Update legado.yml --- .github/workflows/legado.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/legado.yml b/.github/workflows/legado.yml index 2bc86b5a2..3702e0f5c 100644 --- a/.github/workflows/legado.yml +++ b/.github/workflows/legado.yml @@ -76,5 +76,5 @@ jobs: done python3 $GITHUB_WORKSPACE/.github/scripts/lzy_web.py "$path/$file" "$LANZOU_FOLDER_ID" - echo "[$(date -u -d '+8 hour' '+%Y.%m.%d %H:%M:%S')] 分享链接: https://kunfei.lanzous.com/b0f810h4b" + echo "[$(date -u -d '+8 hour' '+%Y.%m.%d %H:%M:%S')] 分享链接: https://kunfei.lanzoux.com/b0f810h4b" From 086899b8384d6febead33384f9b4fa784a0d60f2 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 19 May 2021 19:51:12 +0800 Subject: [PATCH 030/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A5=E8=AF=AD?= =?UTF-8?q?=E7=9A=84=E8=AF=AD=E8=A8=80=E6=96=87=E4=BB=B6,=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E7=BF=BB=E8=AF=91,=E6=9C=89=E6=83=B3=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E7=9A=84=E5=8F=AF=E4=BB=A5=E7=9B=B4=E6=8E=A5=E7=BF=BB?= =?UTF-8?q?=E8=AF=91ja-rJP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/debug/res/values-zh-rHK/strings.xml | 5 ----- app/src/debug/res/values-zh-rTW/strings.xml | 5 ----- app/src/main/res/values-ja-rJP/strings.xml | 5 +++++ app/src/main/res/values/strings.xml | 1 - 4 files changed, 5 insertions(+), 11 deletions(-) delete mode 100644 app/src/debug/res/values-zh-rHK/strings.xml delete mode 100644 app/src/debug/res/values-zh-rTW/strings.xml create mode 100644 app/src/main/res/values-ja-rJP/strings.xml diff --git a/app/src/debug/res/values-zh-rHK/strings.xml b/app/src/debug/res/values-zh-rHK/strings.xml deleted file mode 100644 index 07b0a5dbe..000000000 --- a/app/src/debug/res/values-zh-rHK/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 閲讀.D - 閲讀·D·搜索 - \ No newline at end of file diff --git a/app/src/debug/res/values-zh-rTW/strings.xml b/app/src/debug/res/values-zh-rTW/strings.xml deleted file mode 100644 index a68f05319..000000000 --- a/app/src/debug/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 閲讀.D - 閲讀·D·搜尋 - diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml new file mode 100644 index 000000000..78554f9eb --- /dev/null +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 21ec7dbb1..0925fdb99 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -782,7 +782,6 @@ Style name: Click the folder icon in the upper right corner and select the folder Intelligent scanning - Copy URL No books Keep the original name Screen touch control From 84b549da450bab3a61b02d158e4bcffb4578759d Mon Sep 17 00:00:00 2001 From: hingbong Date: Thu, 20 May 2021 11:20:43 +0800 Subject: [PATCH 031/112] =?UTF-8?q?=E8=A1=A5=E5=85=85zh-rHK=E7=BF=BB?= =?UTF-8?q?=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values-zh-rHK/arrays.xml | 24 +-- app/src/main/res/values-zh-rHK/strings.xml | 169 +++++++++++---------- 2 files changed, 98 insertions(+), 95 deletions(-) diff --git a/app/src/main/res/values-zh-rHK/arrays.xml b/app/src/main/res/values-zh-rHK/arrays.xml index c227d0269..29d1e441f 100644 --- a/app/src/main/res/values-zh-rHK/arrays.xml +++ b/app/src/main/res/values-zh-rHK/arrays.xml @@ -10,7 +10,7 @@ 跟隨系統 亮色主題 暗色主題 - E-Ink(墨水屏) + E-Ink(墨水螢幕) @@ -41,15 +41,15 @@ - - 标题 - 时间 - 电量 - 页数 - 进度 - 页数及进度 + + 標題 + 時間 + 電量 + 頁數 + 進度 + 頁數同埋進度 書名 - 时间及电量 + 時間同埋電量 @@ -59,9 +59,9 @@ - 跟随系统 - 简体中文 - 繁体中文 + 跟隨系統 + 簡體中文 + 繁體中文 英文 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 9baf31451..daac50b21 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -149,8 +149,8 @@ 繼續 定時 朗讀暫停 - 正在朗讀(剩餘 %d 分鐘) - 正在播放(剩餘 %d 分鐘) + 讀緊(剩餘 %d 分鐘) + 播緊(剩餘 %d 分鐘) 閲讀介面隱藏導航欄 隱藏導航欄 導航欄顏色 @@ -262,7 +262,7 @@ 文字顏色和背景(長按自定義) 沉浸式狀態欄 還剩 %d 章未下載 - 沒有選擇 + 仲未揀 長按輸入顏色值 加載中… 追更區 @@ -272,7 +272,7 @@ 刪除 加載超時 關注: %s - 已拷貝 + 拷貝咗 整理書架 這將會刪除所有書籍,請謹慎操作。 搜索書源 @@ -303,7 +303,7 @@ 繁轉簡 翻頁模式 %1$d 項 - 存儲卡: + 存儲咭: 加入書架 加入書架 (%1$d) 成功添加 %1$d 本書 @@ -470,8 +470,8 @@ 再按一次退出程式 導入本地書籍需存儲權限 網絡連接不可用 - - + + 唔係 確認 是否確認刪除? 是否確認刪除 %s? @@ -742,96 +742,99 @@ 夜间底栏不能太亮 强调色不能和背景颜色相似 强调色不能和文字颜色相似 - 格式不对 - 错误 - 显示亮度调节控件 - 语言 - 导入订阅源 - 您的支持是我更新的动力 + 格式不對 + 錯誤 + 顯示亮度調節控制項 + 語言 + 匯入訂閱源 + 您嘅支援是我更新嘅動力 公众号[开源阅读软件] - 阅读记录 - 阅读时间记录 + 閲讀記錄 + 閱讀時間記錄 本地TTS - 线程数 - 总阅读时间 - 全不选 + 線程數 + 總閲讀時間 + 全部唔要 删除所有 - 导入 - 导出 - 保存主题配置 - 保存白天主题配置以共调用和分享 - 保存夜间主题配置以共调用和分享 - 主题列表 - 使用保存主题,导入,分享主题 + 導入 + 導出 + 儲存主題配置 + 儲存白天主題配置以供使用同埋分享 + 儲存夜間主題配置以供使用同埋分享 + 主題列表 + 使用儲存主題,匯入,分享主題 切換默認主題 分享選中源 時間排序 全文搜索 - 关注公众号[开源阅读]获取订阅源! - 当前没有发现源,关注公众号[开源阅读]添加带发现的书源! - 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. - 主题名称 - 自动清除过期搜索数据 - 超过一天的搜索数据 + 關注公眾號[开源阅读]獲取訂閲源! + 目前沒有發現源,關注公眾號[开源阅读]添加包含發現的書源! + 將焦點放到輸入框按下物理按鍵會自動輸入鍵值,多個按鍵會自動用英文逗號隔開. + 主題名 + 自動清除過期搜尋資料 + 超過一天的搜尋資料 重新分段 - 样式名称: - 点击右上角文件夹图标,选择文件夹 - 智能扫描 - 拷贝书籍URL - 拷贝目录URL - 没有书籍 + 樣式名稱: + 點擊右上角資料夾圖示,選擇資料夾 + 智能掃描 + 複製書籍URL + 複製目錄URL + 冇書 保留原名 - 点击区域设置 - 关闭 - 下一页 - 上一页 - 无操作 - 正文标题 - 显示/隐藏 + 點擊區域設定 + 閂咗 + 下一頁 + 上一頁 + 無操作 + 正文標題 + 顯示/隱藏 页眉页脚 - 规则订阅 - 添加大佬们提供的规则导入地址\n添加后点击可导入规则 - 拉取云端进度 - 当前进度超过云端进度,是否同步? - 同步阅读进度 - 进入退出阅读界面时同步阅读进度 - 创建书签失败 - 单URL - 导出书单 - 导入书单 - 预下载 - 预先下载%s章正文 - 是否启用 - 背景图片 - 导出文件夹 - 导出编码 - 导出到WebDav - 反转内容 - 调试 - 崩溃日志 - 使用自定义中文分行 - 图片样式 - 系统TTS - 导出格式 - 校验作者 - 搜索源码 - 书籍源码 - 目录源码 - 正文源码 - 列表源码 - 此url已订阅 + 規則訂閱 + 添加大佬們提供的規則匯入地址 添加後點擊可匯入規則 + 拉取雲端進度 + 目前進度超過雲端進度,係咪同步? + 同步閱讀進度 + 進入退出閱讀介面時同步閱讀進度 + 建立書籤失敗 + 單URL + 導出書單 + 導入書單 + 預下載 + 預先下載%s章正文 + 係咪啟用 + 背景圖片 + 導出資料夾 + 導出編碼 + 導出到WebDav + 反轉內容 + 調試 + 崩潰日誌 + 使用自訂中文分行 + 圖片樣式 + 系統TTS + 導出格式 + 校驗作者 + 搜尋源碼 + 書輯源碼 + 目錄源碼 + 正文源碼 + 列表源碼 + 此url訂閱咗 高刷 - 使用屏幕最高刷新率 - 导出所有 + 使用螢幕最高刷新率 + 導出所有 完成 - 显示未读标志 - 总是使用默认封面 - 总是显示默认封面,不显示网络封面 + 顯示未讀標誌 + 總是使用默認封面 + 總是使用默認封面,唔顯示網路封面 字號 上邊距 下邊距 - 显示 - 隐藏 - 状态栏显示时隐藏 + 顯示 + 隱藏 + 狀態欄顯示時隱藏 + 自訂源分組 + 輸入自訂源分組名稱 + 【%s】 From 334356ce92c8dcc76a954b95b3ed9f110e4759f9 Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 21 May 2021 08:53:25 +0800 Subject: [PATCH 032/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E7=9A=84API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/api/ReaderProvider.kt | 15 +++-- ...okshelfController.kt => BookController.kt} | 59 ++++++++++++++++++- .../main/java/io/legado/app/web/HttpServer.kt | 29 ++++----- 3 files changed, 82 insertions(+), 21 deletions(-) rename app/src/main/java/io/legado/app/api/controller/{BookshelfController.kt => BookController.kt} (65%) diff --git a/app/src/main/java/io/legado/app/api/ReaderProvider.kt b/app/src/main/java/io/legado/app/api/ReaderProvider.kt index 897b0d757..27110af24 100644 --- a/app/src/main/java/io/legado/app/api/ReaderProvider.kt +++ b/app/src/main/java/io/legado/app/api/ReaderProvider.kt @@ -10,7 +10,7 @@ import android.database.Cursor import android.database.MatrixCursor import android.net.Uri import com.google.gson.Gson -import io.legado.app.api.controller.BookshelfController +import io.legado.app.api.controller.BookController import io.legado.app.api.controller.SourceController import java.util.* @@ -19,7 +19,8 @@ import java.util.* */ class ReaderProvider : ContentProvider() { private enum class RequestCode { - SaveSource, SaveSources, SaveBook, DeleteSources, GetSource, GetSources, GetBookshelf, GetChapterList, GetBookContent + SaveSource, SaveSources, SaveBook, DeleteSources, GetSource, GetSources, + GetBookshelf, RefreshToc, GetChapterList, GetBookContent } private val postBodyKey = "json" @@ -33,6 +34,7 @@ class ReaderProvider : ContentProvider() { addURI(authority, "source/query", RequestCode.GetSource.ordinal) addURI(authority, "sources/query", RequestCode.GetSources.ordinal) addURI(authority, "books/query", RequestCode.GetBookshelf.ordinal) + addURI(authority, "book/refreshToc/query", RequestCode.RefreshToc.ordinal) addURI(authority, "book/chapter/query", RequestCode.GetChapterList.ordinal) addURI(authority, "book/content/query", RequestCode.GetBookContent.ordinal) } @@ -65,7 +67,7 @@ class ReaderProvider : ContentProvider() { SourceController.saveSource(values.getAsString(postBodyKey)) } RequestCode.SaveBook -> values?.let { - BookshelfController.saveBook(values.getAsString(postBodyKey)) + BookController.saveBook(values.getAsString(postBodyKey)) } RequestCode.SaveSources -> values?.let { SourceController.saveSources(values.getAsString(postBodyKey)) @@ -91,9 +93,10 @@ class ReaderProvider : ContentProvider() { return if (sMatcher.match(uri) < 0) null else when (RequestCode.values()[sMatcher.match(uri)]) { RequestCode.GetSource -> SimpleCursor(SourceController.getSource(map)) RequestCode.GetSources -> SimpleCursor(SourceController.sources) - RequestCode.GetBookshelf -> SimpleCursor(BookshelfController.bookshelf) - RequestCode.GetBookContent -> SimpleCursor(BookshelfController.getBookContent(map)) - RequestCode.GetChapterList -> SimpleCursor(BookshelfController.getChapterList(map)) + RequestCode.GetBookshelf -> SimpleCursor(BookController.bookshelf) + RequestCode.GetBookContent -> SimpleCursor(BookController.getBookContent(map)) + RequestCode.RefreshToc -> SimpleCursor(BookController.refreshToc(map)) + RequestCode.GetChapterList -> SimpleCursor(BookController.getChapterList(map)) else -> throw IllegalStateException( "Unexpected value: " + RequestCode.values()[sMatcher.match(uri)].name ) diff --git a/app/src/main/java/io/legado/app/api/controller/BookshelfController.kt b/app/src/main/java/io/legado/app/api/controller/BookController.kt similarity index 65% rename from app/src/main/java/io/legado/app/api/controller/BookshelfController.kt rename to app/src/main/java/io/legado/app/api/controller/BookController.kt index 82005aac2..60e7c9ec8 100644 --- a/app/src/main/java/io/legado/app/api/controller/BookshelfController.kt +++ b/app/src/main/java/io/legado/app/api/controller/BookController.kt @@ -1,10 +1,12 @@ package io.legado.app.api.controller +import io.legado.app.R import io.legado.app.api.ReturnData import io.legado.app.constant.PreferKey import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.help.BookHelp +import io.legado.app.model.localBook.LocalBook import io.legado.app.model.webBook.WebBook import io.legado.app.service.help.ReadBook import io.legado.app.utils.GSON @@ -14,8 +16,11 @@ import io.legado.app.utils.getPrefInt import kotlinx.coroutines.runBlocking import splitties.init.appCtx -object BookshelfController { +object BookController { + /** + * 书架所有书籍 + */ val bookshelf: ReturnData get() { val books = appDb.bookDao.all @@ -35,6 +40,55 @@ object BookshelfController { } } + /** + * 更新目录 + */ + fun refreshToc(parameters: Map>): ReturnData { + val returnData = ReturnData() + try { + val bookUrl = parameters["url"]?.getOrNull(0) + if (bookUrl.isNullOrEmpty()) { + return returnData.setErrorMsg("参数url不能为空,请指定书籍地址") + } + val book = appDb.bookDao.getBook(bookUrl) + ?: return returnData.setErrorMsg("bookUrl不对") + if (book.isLocalBook()) { + val toc = LocalBook.getChapterList(book) + appDb.bookChapterDao.delByBook(book.bookUrl) + appDb.bookChapterDao.insert(*toc.toTypedArray()) + appDb.bookDao.update(book) + return if (toc.isEmpty()) { + returnData.setErrorMsg(appCtx.getString(R.string.error_load_toc)) + } else { + returnData.setData(toc) + } + } else { + val bookSource = appDb.bookSourceDao.getBookSource(book.origin) + ?: return returnData.setErrorMsg("未找到对应书源,请换源") + val webBook = WebBook(bookSource) + val toc = runBlocking { + if (book.tocUrl.isBlank()) { + webBook.getBookInfoAwait(this, book) + } + webBook.getChapterListAwait(this, book) + } + appDb.bookChapterDao.delByBook(book.bookUrl) + appDb.bookChapterDao.insert(*toc.toTypedArray()) + appDb.bookDao.update(book) + return if (toc.isEmpty()) { + returnData.setErrorMsg(appCtx.getString(R.string.error_load_toc)) + } else { + returnData.setData(toc) + } + } + } catch (e: Exception) { + return returnData.setErrorMsg(e.localizedMessage ?: "refresh toc error") + } + } + + /** + * 获取目录 + */ fun getChapterList(parameters: Map>): ReturnData { val bookUrl = parameters["url"]?.getOrNull(0) val returnData = ReturnData() @@ -45,6 +99,9 @@ object BookshelfController { return returnData.setData(chapterList) } + /** + * 获取正文 + */ fun getBookContent(parameters: Map>): ReturnData { val bookUrl = parameters["url"]?.getOrNull(0) val index = parameters["index"]?.getOrNull(0)?.toInt() diff --git a/app/src/main/java/io/legado/app/web/HttpServer.kt b/app/src/main/java/io/legado/app/web/HttpServer.kt index 1b8b47eed..9b0d09e0e 100644 --- a/app/src/main/java/io/legado/app/web/HttpServer.kt +++ b/app/src/main/java/io/legado/app/web/HttpServer.kt @@ -3,7 +3,7 @@ package io.legado.app.web import com.google.gson.Gson import fi.iki.elonen.NanoHTTPD import io.legado.app.api.ReturnData -import io.legado.app.api.controller.BookshelfController +import io.legado.app.api.controller.BookController import io.legado.app.api.controller.SourceController import io.legado.app.web.utils.AssetsWeb import java.util.* @@ -32,25 +32,26 @@ class HttpServer(port: Int) : NanoHTTPD(port) { session.parseBody(files) val postData = files["postData"] - when (uri) { - "/saveSource" -> returnData = SourceController.saveSource(postData) - "/saveSources" -> returnData = SourceController.saveSources(postData) - "/saveBook" -> returnData = BookshelfController.saveBook(postData) - "/deleteSources" -> returnData = SourceController.deleteSources(postData) + returnData = when (uri) { + "/saveSource" -> SourceController.saveSource(postData) + "/saveSources" -> SourceController.saveSources(postData) + "/saveBook" -> BookController.saveBook(postData) + "/deleteSources" -> SourceController.deleteSources(postData) + else -> null } } "GET" -> { val parameters = session.parameters - when (uri) { - "/getSource" -> returnData = SourceController.getSource(parameters) - "/getSources" -> returnData = SourceController.sources - "/getBookshelf" -> returnData = BookshelfController.bookshelf - "/getChapterList" -> - returnData = BookshelfController.getChapterList(parameters) - "/getBookContent" -> - returnData = BookshelfController.getBookContent(parameters) + returnData = when (uri) { + "/getSource" -> SourceController.getSource(parameters) + "/getSources" -> SourceController.sources + "/getBookshelf" -> BookController.bookshelf + "/getChapterList" -> BookController.getChapterList(parameters) + "/refreshToc" -> BookController.refreshToc(parameters) + "/getBookContent" -> BookController.getBookContent(parameters) + else -> null } } } From e3eee57d80340acb46225f788b24e56214df588f Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 21 May 2021 10:30:18 +0800 Subject: [PATCH 033/112] =?UTF-8?q?=E5=88=86=E4=BA=AB=E6=96=B0=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=89=8D=E5=85=88=E5=88=A0=E9=99=A4=E5=8E=9F=E6=9C=89?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/constant/AppConst.kt | 4 ++++ .../java/io/legado/app/service/DownloadService.kt | 10 ++-------- .../app/ui/book/source/manage/BookSourceViewModel.kt | 12 +++++------- .../app/ui/rss/source/manage/RssSourceViewModel.kt | 12 +++++------- .../java/io/legado/app/utils/ContextExtensions.kt | 8 ++------ 5 files changed, 18 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt index d7018c996..4938d3e15 100644 --- a/app/src/main/java/io/legado/app/constant/AppConst.kt +++ b/app/src/main/java/io/legado/app/constant/AppConst.kt @@ -2,6 +2,7 @@ package io.legado.app.constant import android.annotation.SuppressLint import android.provider.Settings +import io.legado.app.BuildConfig import io.legado.app.R import splitties.init.appCtx import java.text.SimpleDateFormat @@ -103,4 +104,7 @@ object AppConst { var versionCode: Long = 0L, var versionName: String = "" ) + + const val authority = BuildConfig.APPLICATION_ID + ".fileProvider" + } diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt index 134ca55eb..990271808 100644 --- a/app/src/main/java/io/legado/app/service/DownloadService.kt +++ b/app/src/main/java/io/legado/app/service/DownloadService.kt @@ -12,7 +12,6 @@ import android.os.Looper import androidx.core.app.NotificationCompat import androidx.core.content.FileProvider import androidx.core.os.bundleOf -import io.legado.app.BuildConfig import io.legado.app.R import io.legado.app.base.BaseService import io.legado.app.constant.AppConst @@ -133,14 +132,9 @@ class DownloadService : BaseService() { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //7.0版本以上 - val uriForFile: Uri = - FileProvider.getUriForFile( - this, - "${BuildConfig.APPLICATION_ID}.fileProvider", - file - ) + val contentUrl = FileProvider.getUriForFile(this, AppConst.authority, file) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.setDataAndType(uriForFile, "application/vnd.android.package-archive") + intent.setDataAndType(contentUrl, "application/vnd.android.package-archive") } else { val uri: Uri = Uri.fromFile(file) intent.setDataAndType(uri, "application/vnd.android.package-archive") diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt index 92fb7e4fa..1432429ee 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt @@ -5,8 +5,8 @@ import android.content.Intent import android.text.TextUtils import androidx.core.content.FileProvider import androidx.documentfile.provider.DocumentFile -import io.legado.app.BuildConfig import io.legado.app.base.BaseViewModel +import io.legado.app.constant.AppConst import io.legado.app.constant.AppPattern import io.legado.app.data.appDb import io.legado.app.data.entities.BookSource @@ -164,14 +164,12 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) fun shareSelection(sources: List, success: ((intent: Intent) -> Unit)) { execute { + val tmpSharePath = "${context.filesDir}/shareBookSource.json" + FileUtils.delete(tmpSharePath) val intent = Intent(Intent.ACTION_SEND) - val file = FileUtils.createFileWithReplace("${context.filesDir}/shareBookSource.json") + val file = FileUtils.createFileWithReplace(tmpSharePath) file.writeText(GSON.toJson(sources)) - val fileUri = FileProvider.getUriForFile( - context, - BuildConfig.APPLICATION_ID + ".fileProvider", - file - ) + val fileUri = FileProvider.getUriForFile(context, AppConst.authority, file) intent.type = "text/*" intent.putExtra(Intent.EXTRA_STREAM, fileUri) intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt index a89dc2e7f..408ca10f8 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt @@ -5,8 +5,8 @@ import android.content.Intent import android.text.TextUtils import androidx.core.content.FileProvider import androidx.documentfile.provider.DocumentFile -import io.legado.app.BuildConfig import io.legado.app.base.BaseViewModel +import io.legado.app.constant.AppConst import io.legado.app.data.appDb import io.legado.app.data.entities.RssSource import io.legado.app.help.DefaultData @@ -106,14 +106,12 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application) fun shareSelection(sources: List, success: ((intent: Intent) -> Unit)) { execute { + val tmpSharePath = "${context.filesDir}/shareRssSource.json" + FileUtils.delete(tmpSharePath) val intent = Intent(Intent.ACTION_SEND) - val file = FileUtils.createFileWithReplace("${context.filesDir}/shareRssSource.json") + val file = FileUtils.createFileWithReplace(tmpSharePath) file.writeText(GSON.toJson(sources)) - val fileUri = FileProvider.getUriForFile( - context, - BuildConfig.APPLICATION_ID + ".fileProvider", - file - ) + val fileUri = FileProvider.getUriForFile(context, AppConst.authority, file) intent.type = "text/*" intent.putExtra(Intent.EXTRA_STREAM, fileUri) intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION diff --git a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt index d6644e35a..c1d5e50e3 100644 --- a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt @@ -21,8 +21,8 @@ import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import androidx.core.content.edit import androidx.preference.PreferenceManager -import io.legado.app.BuildConfig import io.legado.app.R +import io.legado.app.constant.AppConst import java.io.File import java.io.FileOutputStream @@ -158,11 +158,7 @@ fun Context.shareWithQr(text: String, title: String = getString(R.string.share)) fOut.flush() fOut.close() file.setReadable(true, false) - val contentUri = FileProvider.getUriForFile( - this, - "${BuildConfig.APPLICATION_ID}.fileProvider", - file - ) + val contentUri = FileProvider.getUriForFile(this, AppConst.authority, file) val intent = Intent(Intent.ACTION_SEND) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.putExtra(Intent.EXTRA_STREAM, contentUri) From b83dd304faa6e56de15e8c50b9aa48f8691e9f44 Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 21 May 2021 20:41:37 +0800 Subject: [PATCH 034/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=8D=E8=BD=AC?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../app/ui/book/toc/BookmarkFragment.kt | 6 +- .../app/ui/book/toc/ChapterListFragment.kt | 6 +- ...{ChapterListActivity.kt => TocActivity.kt} | 14 +- .../app/ui/book/toc/TocActivityResult.kt | 2 +- ...hapterListViewModel.kt => TocViewModel.kt} | 13 +- app/src/main/res/menu/book_toc.xml | 18 + app/src/main/res/values-ja-rJP/strings.xml | 839 +++++++++++++++++- app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-zh-rHK/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 13 files changed, 892 insertions(+), 13 deletions(-) rename app/src/main/java/io/legado/app/ui/book/toc/{ChapterListActivity.kt => TocActivity.kt} (88%) rename app/src/main/java/io/legado/app/ui/book/toc/{ChapterListViewModel.kt => TocViewModel.kt} (70%) create mode 100644 app/src/main/res/menu/book_toc.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 18c558fe6..c87caef2e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -246,7 +246,7 @@ android:screenOrientation="behind" /> diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index 976436e56..609683d90 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt @@ -22,10 +22,10 @@ import io.legado.app.utils.requestInputMethod import io.legado.app.utils.viewbindingdelegate.viewBinding -class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark), +class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark), BookmarkAdapter.Callback, - ChapterListViewModel.BookmarkCallBack { - override val viewModel: ChapterListViewModel by activityViewModels() + TocViewModel.BookmarkCallBack { + override val viewModel: TocViewModel by activityViewModels() private val binding by viewBinding(FragmentBookmarkBinding::bind) private lateinit var adapter: BookmarkAdapter private var bookmarkLiveData: LiveData>? = null diff --git a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt index 57ccc8305..e2ba1cfa1 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt @@ -28,10 +28,10 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlin.math.min -class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapter_list), +class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapter_list), ChapterListAdapter.Callback, - ChapterListViewModel.ChapterListCallBack { - override val viewModel: ChapterListViewModel by activityViewModels() + TocViewModel.ChapterListCallBack { + override val viewModel: TocViewModel by activityViewModels() private val binding by viewBinding(FragmentChapterListBinding::bind) lateinit var adapter: ChapterListAdapter private var durChapterIndex = 0 diff --git a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListActivity.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt similarity index 88% rename from app/src/main/java/io/legado/app/ui/book/toc/ChapterListActivity.kt rename to app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt index 4f429447a..57a213a49 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.toc import android.os.Bundle import android.view.Menu +import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.widget.SearchView import androidx.core.view.isGone @@ -19,8 +20,8 @@ import io.legado.app.utils.gone import io.legado.app.utils.visible -class ChapterListActivity : VMBaseActivity() { - override val viewModel: ChapterListViewModel +class TocActivity : VMBaseActivity() { + override val viewModel: TocViewModel by viewModels() private lateinit var tabLayout: TabLayout @@ -47,7 +48,7 @@ class ChapterListActivity : VMBaseActivity viewModel.reverseToc() + } + return super.onCompatOptionsItemSelected(item) + } + private inner class TabFragmentPageAdapter : FragmentStateAdapter(this) { override fun getItemCount(): Int { diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocActivityResult.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocActivityResult.kt index 1e8f688de..e727de0b2 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocActivityResult.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocActivityResult.kt @@ -8,7 +8,7 @@ import androidx.activity.result.contract.ActivityResultContract class TocActivityResult : ActivityResultContract?>() { override fun createIntent(context: Context, input: String?): Intent { - return Intent(context, ChapterListActivity::class.java) + return Intent(context, TocActivity::class.java) .putExtra("bookUrl", input) } diff --git a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListViewModel.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt similarity index 70% rename from app/src/main/java/io/legado/app/ui/book/toc/ChapterListViewModel.kt rename to app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt index f85ff5749..f56c68ff3 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt @@ -7,7 +7,7 @@ import io.legado.app.base.BaseViewModel import io.legado.app.data.appDb import io.legado.app.data.entities.Book -class ChapterListViewModel(application: Application) : BaseViewModel(application) { +class TocViewModel(application: Application) : BaseViewModel(application) { var bookUrl: String = "" var bookData = MutableLiveData() var chapterCallBack: ChapterListCallBack? = null @@ -22,6 +22,17 @@ class ChapterListViewModel(application: Application) : BaseViewModel(application } } + fun reverseToc() { + execute { + val toc = appDb.bookChapterDao.getChapterList(bookUrl) + val newToc = toc.reversed() + newToc.forEachIndexed { index, bookChapter -> + bookChapter.index = index + } + appDb.bookChapterDao.insert(*newToc.toTypedArray()) + } + } + fun startChapterListSearch(newText: String?) { chapterCallBack?.startChapterListSearch(newText) } diff --git a/app/src/main/res/menu/book_toc.xml b/app/src/main/res/menu/book_toc.xml new file mode 100644 index 000000000..59efd4075 --- /dev/null +++ b/app/src/main/res/menu/book_toc.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 78554f9eb..0781727fb 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -1,5 +1,842 @@ + + Legado + Legado·search + Legado needs storage access to find and read books. please go "App Settings" to allow "Storage permission". + + Home + Restore + Import Legado data + Create a subfolder + Create a folder named Legado as the backup folder. + Offline cache book backup + Export to local and back up to the exports directory under the legado folder + Backup to + Please select a backup path. + Import legacy data + Import github data + Replacement + Send - \ No newline at end of file + Prompt + Cancel + Confirm + Go to Settings + Cannot jump to Settings. + + Retry + Loading + Warning + Edit + Delete + Delete all + Replace + Replacement + Configure replacement rules + Not available now + Enable + Search Replacement + Bookshelf + Favorites + Favorite + in Favorites + Not in Favorites + Subscription + All + Recent reading + Last reading + What\'s new + The bookshelf is still empty. Search for books or add them from discovery! \n if you use it for the first time, please pay attention to the public account [开源阅读] to get the book source! + Search + Download + List + Grid-3 + Grid-4 + Grid-5 + Grid-6 + Layout + View + Library + Import books + Book sources + Sources management + Create/Import/Edit/Manage Book sources + Settings + Theme settings + Some settings related to interface or color + Other settings + Some function-related settings + About + Donations + Exit + It has not been saved. Do you want to continue editing? + Book styles + Version + Local + Search + Origin: %s + Origin: %s + Title + Latest: %s + Would you like to add %s to your Bookshelf? + %s text file(s) in total + Loading… + Retry + Web service + Web edit source and read book + Edit book sources on the web + http://%1$s:%2$d + Offline cache + Offline cache + cache the selected chapter(s) to Storage + Change Origin + + \u3000\u3000 This is an open source reading software newly developed by Kotlin, welcome to join us. Follow the WeChat Official Account [开源阅读]! + + + Legado (YueDu 3.0) download link:\n https://play.google.com/store/apps/details?id=io.legado.play.release + + Version %s + Background-verification + you can operate freely when verifying the book source + Auto-refresh + Update books automatically when opening the software + Auto-download + Download the latest chapters automatically when updating books + Backup and restore + WebDav settings + WebDav settings/Import legacy data + Backup + Restore + Backup needs storage permission + Restore needs storage permission + OK + Cancel + Backup confirmation + The new backup files will replace the original.\n Backup folder: YueDu + Restore confirmation + Restoring the bookshelf data will overwrite the current Bookshelf. + Backup succeed + Backup failed + Restoring + Restore succeed + Backup failed + Screen orientation + Auto(sensor) + Landscape + Portrait + Follow system + Disclaimer + %d chapters + Interface + Brightness + Chapters + Next + Prior + Hide status bar + Hide system navigation bar when reading + Speech + Speaking + Click to open the Reading + Play + Playing + Click to open the Playing + Pause + Back + Refresh + Start + Stop + Pause + Resume + Timer + Speak Paused + Speaking(%d min left) + Playing(%d min left) + Hide virtual buttons when reading + Hide navigation bar + Navigation bar color + GitHub + Rating + Email + Open failed + Share failed + No chapters + Add url + Add book url + Background + Author + Author: %s + Speak Stopped + Clear cache + Cache cleared + Save + Edit source + Edit Book source + Disable Book source + Add Book source + Add subscription source + Add books + Scan + Copy source + Paste source + Source rules description + Check for Updates + Scan QR code + Scan local images + Rules description + Share + Share to + Follow system + Add + Import book sources + Import local + Import online + Replacement + Edit replacement rule + Pattern + Replacement + Cover + Book + Volume keys to turn page + Tap screen to turn page + Flip animation + Flip animation (book) + Keep screen awake + Back + Menu + Adjust + Scroll bar + Clearing the cache will delete all saved chapters. Are you sure to delete it? + Book sources sharing + Rule name + Pattern rule is empty or does not conform the regex specification. + Selection action + Select all + Select all(%1$d/%2$d) + Cancel select all(%1$d/%2$d) + Dark mode + Welcome page + Download start + Download cancel + No download + Downloaded %1$d/%2$d + Import selected book(s) + Number of concurrent tasks + Change icon + Remove + Start reading + Loading… + Load failed, tap to retry + Book description + Description:%s + Description: no introduction + Open external book + Origin: %s + Import local rules + Import online rules + Check interval for updates + By recent list + By update time + By book title + By sort manually + Reading strategy + Typesetting + Delete selected + Are you sure to delete? + Default font + Discovery + Discovery + No content.Go to Sources management to add it! + Delete all + Search history + Clear + Display book title on text + Book Sources sync + No latest chapter. + Display time and battery + Display divider + Darken the status bar\'s icon color + Content + Copy + Download all + This is a test text, \n\u3000\u3000 just to show you the effect + Color and background (long tap to customize) + Immersive status bar + %d chapter(s) left + No selected + Long tap to input color value + Loading… + Awaiting + Awaiting more + Bookmarks + Add to Bookmarks + Delete + Loading timeout + Follow:%s + Copied successfully + Bookshelf arrangement + It will delete all books. Be careful,please. + Search book sources + Search subscription sources + Search( %d sources in total) + Chapters(%d) + Bold + Font + Text + Home page + Right + Left + Bottom + Top + Padding + Padding top + Padding bottom + Padding left + Padding right + Check book sources + Check the selected source + %1$s Progress %2$d/%3$d + Please install and select Chinese TTS! + TTS initialization failed! + Simplified conversion + Off + Simplified to traditional + Traditional to simplified + Flipping mode + %1$d items + Storage: + Add to Bookshelf + Add to Bookshelf(%1$d) + %1$d books added successfully + Please put the font files in the Fonts folder of the storage root directory and reselect + Default font + Select fonts + Text size + Line spacing + Paragraph spacing + To Top + To Bottom + Auto expand Discovery + Default expand the first Discovery. + Current threads %s + Speech rate + Auto scroll + Stop Auto scroll + Auto scroll speed + Book information + Edit book information + Use Bookshelf as start page + Auto jump to Recent list + Replacement object. Book name or source url is available + Groups + Cache path + System file picker + New version + Download updates + Volume keys to turn page when reading + Margin adjustment + Enable update + Disable update + Inverse + Search book name/author + Book name,Author,URL + FAQ + Display all Discovery + Display the selected origin\'s Discovery if closed + Update chapters + Txt Chapters Regex + Text encoding + Ascending/Descending order + Sort + Sort automatically + Sort manually + Sort by name + Scroll to the top + Scroll to the bottom + Read: %s + Awaiting update + Awaiting more + Finished + All + Awaiting update books + Awaiting more chapters books + Finished books + Local books + The status bar color becomes transparent + immersion navigation bar + The navigation bar becomes transparent + Add to Bookshelf + Continue reading + Cover path + Cover + Slide + Simulation + Scroll + None + This book source uses advanced features, please go to the Donations and tap the Alipay red envelope search code to receive the red envelope,then you can use it. + Update the latest chapter after changed origin in the background + if enabled,the update will start 1 minute later when the software is run + Auto hide ToolBar + The toolbar will be hidden automatically when scroll the Bookshelf + Login + Login%s + Success + The current source has not configured with a login address + No prior page + No next page + + + 源名称(sourceName) + 源URL(sourceUrl) + 源分组(sourceGroup) + 自定义源分组 + 输入自定义源分组名称 + 【%s】 + 分类Url + 登录URL(loginUrl) + 源注释(sourceComment) + 搜索地址(url) + 发现地址规则(url) + 书籍列表规则(bookList) + 书名规则(name) + 详情页url规则(bookUrl) + 作者规则(author) + 分类规则(kind) + 简介规则(intro) + 封面规则(coverUrl) + 最新章节规则(lastChapter) + 字数规则(wordCount) + 书籍URL正则(bookUrlPattern) + 预处理规则(bookInfoInit) + 目录URL规则(tocUrl) + 允许修改书名作者(canReName) + 目录下一页规则(nextTocUrl) + 目录列表规则(chapterList) + 章节名称规则(ChapterName) + 章节URL规则(chapterUrl) + VIP标识(isVip) + 更新时间(ChapterInfo) + 正文规则(content) + 正文下一页URL规则(nextContentUrl) + WebViewJs(webJs) + 资源正则(sourceRegex) + 替换规则(replaceRegex) + 图片样式(imageStyle) + + 图标(sourceIcon) + 列表规则(ruleArticles) + 列表下一页规则(ruleArticles) + 标题规则(ruleTitle) + guid规则(ruleGuid) + 时间规则(rulePubDate) + 类别规则(ruleCategories) + 描述规则(ruleDescription) + 图片url规则(ruleImage) + 内容规则(ruleContent) + 样式(style) + 链接规则(ruleLink) + + + + No source + Failed to obtain book information + Failed to obtain content + Failed to obtain chapters list + Failed to access website:%s + Failed to read file + Failed to load chapters list + Failed to get data + Failed to load\n%s + No network + Network connection timeout + Data parsing failed + + + HTTP Header + Debug source + Import from QR code + Share selected sources + Scan QR code + Tap to display Menu when selected + Theme + Theme mode + Select a theme you want + Join QQ group + Set the background image requires storage permission + Input book source address + Delete file + Deleted file + Are you sure to delete this file? + Directory + Intelligent import + Discovery + Switch display styles + Import local books requires storage permission + Night Theme + E-Ink + Optimization for E-ink devices + This software requires storage permission to store the book information to be backed up + Tap again to exit the program + Import local books requires storage permission + Network connection is not available + Yes + No + OK + Are you sure to delete it? + Are you sure to delete %s? + Are you sure to delete all books? + Do you want to delete the downloaded book chapters at the same time? + Scan QR code requires Camera permissions + Speech is running, cannot turn pages automatically + Input encoding + Txt Chapters Regex + Open local books requires storage permission + No bookName + Input replacement rule URL + Search list obtained successfully%d + name and URL cannot be empty + Gallery + get AliPay red envelopes + No update address + Opening the homepage, it will return to start page automatically after success + After successful login, please tap the icon on the upper right corner to test the homepage access + Chapter + To + Using Regex + Indent + None + Indent with 1 chars + Indent with 2 chars + Indent with 3 chars + Indent with 4 chars + Select a folder + Select a file + No Discovery, you can add it in BookSource + Restore default + Custom cache path requires Storage permission + Black + No content + Changing source, wait please + Chapters is empty + Word spacing + + Basic + Search + Discovery + Information + Chapters + Content + + E-Ink mode + Remove animations and optimize the experience of using E-paper books + Web service + Web port + Current port %s + QR code sharing + Strings sharing + Wifi sharing + Please grant Storage Permission + Speed down + Speed up + Prior + Next + Music + Audio + Enable + Enable js + Load BaseUrl + All Sources + The input content cannot be empty + Clear Discovery cache + Edit Discovery + Switch the software icon displayed on the desktop + Help + Me + Read + %d%% + %d min + Auto-Brightness %s + Speak by pages + Speak Engine + Background images + Background color + Text color + Select a picture + Group management + Group selection + Group editing + Move to group + Add to Groups + Remove from Groups + New replacement + Group + Group: %s + Chapters: %s + Enable Discovery + Disable Discovery + Enable selected + Disable selected + Export selected + Export + Load chapters + Load book detail + TTS + WebDav password + Input you WebDav authorized password + Input you server address + WebDav server address + WebDav account + Input your WebDav account + Subscription source + Edit Subscription source + Filter + Search Discovery sources + Current location: + Precise search + Starting service + Empty + File selection + Folder selection + I AM OVER! + Uri To Path failed + Refresh cover + Change origin + Local image + Type: + Background + Importing + Exporting + Set page-turning buttons + Page up button + Page down button + Add this book to Bookshelf first + Ungrouped + Prior sentence + Next sentence + Other folder + There are too many words to create a QR code + Subscription sources sharing + Book sources sharing + Automatic switching dark mode + Following system dark mode + Go back + Online Speech tone + (%1$d/%2$d) + Display Subscription + Service stopped + Starting service\nChecking notification bar for details + Default path + System folder picker + App folder picker + App file picker + Android 10+ unable to read and write file due to permission restrictions + Long tap to display Legado·Search in the operation menu + Text operation display Search + Log + Log + Simplified conversion + The icon is a vector icon, which was not supported before Android 8.0 + Speech settings + Start page + Long Tap to select text + Header + Content + Footer + Select end + Select start + Shared layout + Browser + Import default rules + Name + Regex + More menu + Minus + Plus + System typeface + Delete source file + Default-1 + Default-2 + Default-3 + Title + Left + Center + Hide + Add to Group + Save image + No default path + Group settings + View Chapters + Navigation bar shadow + Current shadow size(elevation): %s + Default + Main menu + Tap to grant permission + Legado needs Storage permission, please tap the "Grant Permission" button below, or go to "Settings"-"Application Permissions"-to open the required permission. If the permission is still not work, please tap "Select Folder" in the upper right corner to use the system folder picker. + The selected text cannot be spoken in full text speech + Extend to cutout + Updating Chapters + Headset buttons are always available + Headset buttons are available even exit the app. + Contributors + Contact + License + Other + 开源阅读 + Follow WeChat Official Accounts + WeChat + Supporting me will be appreciated + Official Accounts[开源阅读] + Changing source + Tap to join + Middle + Information + Switch Layout + Text font weight switching + Full screen gestures support + + + Primary + Accent + Background color + NavBar color + Day + Day,Primary + Day,Accent + Day,Background color + Day,NavBar color + Night + Night,Primary + Night,Accent + Night,Background color + Night,NavBar color + Change source automatically + Text justified + Text align bottom + Auto scroll speed + Sort by URL + Backup the local and WebDav simultaneously + Restore from WebDAV first, Restore form the local backup on long click + Select a legacy backup folder + Enabled + Disabled + Starting download + This book is already in Download list + Click to open + Follow [开源阅读] to support me by clicking on ads + WeChat Tipping Code + AliPay + AliPay red envelope search code + 537954522 Click to copy + AliPay red envelope QR code + AliPay QR code + QQ Collection QR code + gedoor,Invinciblelee etc. Checking in github for details + Clear the cache of the downloaded books and fonts + Default cover + Bypass list + Ignore some contents while restoring + Reading interface settings + Group name + Remarks section + Enable replace rule by default + For new added books + Select restore file + Day background can not be too dark! + Day bottom can not be too dark! + Night background can not be too bright! + Night bottom can not be too bright! + Need Difference between accent and background color + Need Difference between accent and text color + Wrong format + Error + Show brightness widget + Language + Import rss source + Your donation makes this app better + Wechat official account [开源阅读软件] + Read record + Read record summary + Local TTS + Thread count + Total read time + Unselect all + Import + Export + Save theme config + Save day theme config + Save night theme config + Theme list + Save, Import, Share theme + Switch default theme + Sort by update time + Search content + Follow Wechat official account [开源阅读] to get Subscription source + Empty now,Follow Wechat official account [开源阅读] to get Discovery source + 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. + Theme name + "Clear expired search histories automatically " + Search histories more than one day + Re-segment + Style name: + Click the folder icon in the upper right corner and select the folder + Intelligent scanning + No books + Keep the original name + Screen touch control + Close + Next page + Prior page + None + Title + Show/Hide + footer header + Rule Subscription + 添加大佬们提供的规则导入地址\n添加后点击可导入规则 + Pull the cloud progress + The current progress exceeds the cloud progress. Do you want to synchronize? + Synchronous reading progress + Synchronize reading progress when entering / exiting the reading interface + Failed to create bookmark + Single URL + Export the list of books + Import the list of books + Download in advance + Download 10 chapters in advance + Is enabled + Background image + Copy book URL + Copy chapters URL + Export to a folder + Exported text coding + Export to WebDav + Reverse content + Debug + Crash log + Custom Chinese line feed + Style of Images + System tts + Exported file format + Check by author + This URL has subscribed + High screen refresh rate + Use maximum screen refresh rate + Export all + Finished + Show unread flag + Always show default cover + Always show the default cover, do not show the network cover + Search source code + Book source code + Chapters source code + Content source code + List source code + Font size + Margin top + Marigin bottom + Show + Hide + Hide when status bar show + Reverse toc + diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 11a473d30..366b70c61 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -838,5 +838,6 @@ Show Hide Hide when status bar show + 反转目录 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index daac50b21..4a8e636c0 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -836,5 +836,6 @@ 自訂源分組 輸入自訂源分組名稱 【%s】 + 反转目录 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8393ace59..182ea03aa 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -837,5 +837,6 @@ 显示 隐藏 状态栏显示时隐藏 + 反转目录 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index bc4d43fe5..170d773e0 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -837,5 +837,6 @@ 显示 隐藏 状态栏显示时隐藏 + 反转目录 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0925fdb99..0781727fb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -838,4 +838,5 @@ Show Hide Hide when status bar show + Reverse toc From 275ab6523458fdf5ca07eebe3005d8033a5c8c84 Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 21 May 2021 20:45:20 +0800 Subject: [PATCH 035/112] edit updateLog.md --- app/src/main/assets/updateLog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index cabdfc725..7f5b5a669 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,6 +3,11 @@ * 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2021/05/21** +* 添加反转目录功能 +* 修复分享bug +* 详情页添加登录菜单 + **2021/05/16** * 添加总是使用默认封面配置 * 添加一种语言 ptbr translation by mezysinc From 8666ca06afc7af0aae6da81d2014c3e4b4cf0504 Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 21 May 2021 22:18:41 +0800 Subject: [PATCH 036/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ac5387bee..5c59be8c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,7 +37,7 @@ android { multiDexEnabled true javaCompileOptions { annotationProcessorOptions { - arguments = [ + arguments += [ "room.incremental" : "true", "room.expandProjection": "true", "room.schemaLocation" : "$projectDir/schemas".toString() From 229e37acc37d71ffc97507d86a62ef695b6c047e Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 22 May 2021 16:13:12 +0800 Subject: [PATCH 037/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/about/ReadRecordActivity.kt | 20 +++++++++++++++++++ .../app/ui/book/info/BookInfoActivity.kt | 2 -- .../app/ui/book/read/ReadBookViewModel.kt | 9 ++------- .../ui/main/bookshelf/books/BooksFragment.kt | 2 -- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt b/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt index 8d3295299..08de241e8 100644 --- a/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt +++ b/app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt @@ -14,7 +14,10 @@ import io.legado.app.data.entities.ReadRecordShow import io.legado.app.databinding.ActivityReadRecordBinding import io.legado.app.databinding.ItemReadRecordBinding import io.legado.app.lib.dialogs.alert +import io.legado.app.ui.book.read.ReadBookActivity +import io.legado.app.ui.book.search.SearchActivity import io.legado.app.utils.cnCompare +import io.legado.app.utils.startActivity import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch @@ -111,6 +114,23 @@ class ReadRecordActivity : BaseActivity() { override fun registerListener(holder: ItemViewHolder, binding: ItemReadRecordBinding) { binding.apply { + root.setOnClickListener { + val item = getItem(holder.layoutPosition) ?: return@setOnClickListener + launch { + val book = withContext(IO) { + appDb.bookDao.findByName(item.bookName).firstOrNull() + } + if (book == null) { + startActivity { + putExtra("key", item.bookName) + } + } else { + startActivity { + putExtra("bookUrl", book.bookUrl) + } + } + } + } tvRemove.setOnClickListener { getItem(holder.layoutPosition)?.let { item -> sureDelAlert(item) diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index 127405931..d4e6cedd3 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -22,7 +22,6 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.databinding.ActivityBookInfoBinding import io.legado.app.help.BlurTransformation import io.legado.app.help.ImageLoader -import io.legado.app.help.IntentDataHelp import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.bottomBackground @@ -361,7 +360,6 @@ class BookInfoActivity : Intent(this, ReadBookActivity::class.java) .putExtra("bookUrl", book.bookUrl) .putExtra("inBookshelf", viewModel.inBookshelf) - .putExtra("key", IntentDataHelp.putData(book)) ) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index 5b6281320..b91edd0e6 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -10,7 +10,6 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookProgress import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp -import io.legado.app.help.IntentDataHelp import io.legado.app.help.storage.BookWebDav import io.legado.app.model.localBook.LocalBook import io.legado.app.model.webBook.PreciseSearch @@ -30,9 +29,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { fun initData(intent: Intent) { execute { ReadBook.inBookshelf = intent.getBooleanExtra("inBookshelf", true) - IntentDataHelp.getData(intent.getStringExtra("key"))?.let { - initBook(it) - } ?: intent.getStringExtra("bookUrl")?.let { + intent.getStringExtra("bookUrl")?.let { appDb.bookDao.getBook(it)?.let { book -> initBook(book) } @@ -40,9 +37,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { initBook(it) } }.onFinally { - if (ReadBook.inBookshelf) { - ReadBook.saveRead() - } + ReadBook.saveRead() } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt index a37a27087..bc38e5767 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt @@ -18,7 +18,6 @@ import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.databinding.FragmentBooksBinding import io.legado.app.help.AppConfig -import io.legado.app.help.IntentDataHelp import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.accentColor import io.legado.app.ui.audio.AudioPlayActivity @@ -149,7 +148,6 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), } else -> startActivity { putExtra("bookUrl", book.bookUrl) - putExtra("key", IntentDataHelp.putData(book)) } } } From ec643ab263d8c22eb74a2f8a726295c5cdc41c76 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 22 May 2021 17:19:04 +0800 Subject: [PATCH 038/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/data/entities/Book.kt | 35 ++++++++++--------- .../app/ui/book/info/BookInfoViewModel.kt | 2 +- .../app/ui/book/read/ReadBookViewModel.kt | 2 +- .../io/legado/app/ui/book/toc/TocViewModel.kt | 13 ++++--- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index 2ee782122..240af5e9a 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -124,6 +124,14 @@ data class Book( return readConfig!! } + fun setReverseToc(reverseToc: Boolean) { + config().reverseToc = reverseToc + } + + fun getReverseToc(): Boolean { + return config().reverseToc + } + fun setUseReplaceRule(useReplaceRule: Boolean) { config().useReplaceRule = useReplaceRule } @@ -156,14 +164,6 @@ data class Book( config().imageStyle = imageStyle } - fun getDelParagraph(): Int { - return config().delParagraph - } - - fun setDelParagraph(num: Int) { - config().delParagraph = num - } - fun setDelTag(tag: Long) { config().delTag = if ((config().delTag and tag) == tag) config().delTag and tag.inv() else config().delTag or tag @@ -208,17 +208,10 @@ data class Book( newBook.customTag = customTag newBook.canUpdate = canUpdate newBook.readConfig = readConfig - delete() + delete(this) appDb.bookDao.insert(newBook) } - fun delete() { - if (ReadBook.book?.bookUrl == bookUrl) { - ReadBook.book = null - } - appDb.bookDao.delete(this) - } - fun upInfoFromOld(oldBook: Book?) { oldBook?.let { group = oldBook.group @@ -241,15 +234,23 @@ data class Book( const val imgStyleDefault = "DEFAULT" const val imgStyleFull = "FULL" const val imgStyleText = "TEXT" + + fun delete(book: Book?) { + book ?: return + if (ReadBook.book?.bookUrl == book.bookUrl) { + ReadBook.book = null + } + appDb.bookDao.delete(book) + } } @Parcelize data class ReadConfig( + var reverseToc: Boolean = false, var pageAnim: Int = -1, var reSegment: Boolean = false, var imageStyle: String? = null, var useReplaceRule: Boolean = AppConfig.replaceEnableDefault,// 正文使用净化替换规则 - var delParagraph: Int = 0,//去除段首 var delTag: Long = 0L,//去除标签 ) : Parcelable diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt index 39c09fc69..f6fb03b3b 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt @@ -235,7 +235,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { fun delBook(deleteOriginal: Boolean = false, success: (() -> Unit)? = null) { execute { bookData.value?.let { - it.delete() + Book.delete(it) inBookshelf = false if (it.isLocalBook()) { LocalBook.deleteBook(it, deleteOriginal) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index b91edd0e6..2b6057acd 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -262,7 +262,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { fun removeFromBookshelf(success: (() -> Unit)?) { execute { - ReadBook.book?.delete() + Book.delete(ReadBook.book) }.onSuccess { success?.invoke() } diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt index f56c68ff3..cb0f8d708 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt @@ -24,12 +24,15 @@ class TocViewModel(application: Application) : BaseViewModel(application) { fun reverseToc() { execute { - val toc = appDb.bookChapterDao.getChapterList(bookUrl) - val newToc = toc.reversed() - newToc.forEachIndexed { index, bookChapter -> - bookChapter.index = index + bookData.value?.let { + it.setReverseToc(it.getReverseToc()) + val toc = appDb.bookChapterDao.getChapterList(bookUrl) + val newToc = toc.reversed() + newToc.forEachIndexed { index, bookChapter -> + bookChapter.index = index + } + appDb.bookChapterDao.insert(*newToc.toTypedArray()) } - appDb.bookChapterDao.insert(*newToc.toTypedArray()) } } From 9e20cc1972c9e52fd11f84c824b1b6c83423e00a Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 22 May 2021 19:16:16 +0800 Subject: [PATCH 039/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=91=E7=8E=B0?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E9=9A=90=E8=97=8F=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/constant/EventBus.kt | 2 +- .../java/io/legado/app/constant/PreferKey.kt | 1 + .../main/java/io/legado/app/help/AppConfig.kt | 5 +- .../app/ui/config/OtherConfigFragment.kt | 2 +- .../io/legado/app/ui/main/MainActivity.kt | 89 ++++++++++++------- .../res/layout/activity_book_source_edit.xml | 2 +- .../main/res/layout/activity_explore_show.xml | 2 +- app/src/main/res/layout/fragment_explore.xml | 2 +- app/src/main/res/menu/main_bnv.xml | 4 +- app/src/main/res/values-ja-rJP/strings.xml | 5 +- app/src/main/res/values-pt-rBR/strings.xml | 3 +- app/src/main/res/values-zh-rHK/strings.xml | 3 +- app/src/main/res/values-zh-rTW/strings.xml | 3 +- app/src/main/res/values-zh/strings.xml | 3 +- app/src/main/res/values/strings.xml | 5 +- app/src/main/res/xml/pref_config_other.xml | 6 ++ 16 files changed, 90 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/EventBus.kt b/app/src/main/java/io/legado/app/constant/EventBus.kt index 70fa1d1a5..adb92d961 100644 --- a/app/src/main/java/io/legado/app/constant/EventBus.kt +++ b/app/src/main/java/io/legado/app/constant/EventBus.kt @@ -16,7 +16,7 @@ object EventBus { const val AUDIO_PROGRESS = "audioProgress" const val AUDIO_SIZE = "audioSize" const val AUDIO_SPEED = "audioSpeed" - const val SHOW_RSS = "showRss" + const val NOTIFY_MAIN = "notifyMain" const val WEB_SERVICE = "webService" const val UP_DOWNLOAD = "upDownload" const val SAVE_CONTENT = "saveContent" diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 70b4a6b89..a8d2d0ee5 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -23,6 +23,7 @@ object PreferKey { const val ttsSpeechRate = "ttsSpeechRate" const val prevKeys = "prevKeyCodes" const val nextKeys = "nextKeyCodes" + const val showDiscovery = "showDiscovery" const val showRss = "showRss" const val bookshelfLayout = "bookshelfLayout" const val bookshelfSort = "bookshelfSort" diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index b2c620f63..798d08c5b 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -100,7 +100,10 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { } } - val isShowRSS: Boolean + val showDiscovery: Boolean + get() = appCtx.getPrefBoolean(PreferKey.showDiscovery, true) + + val showRSS: Boolean get() = appCtx.getPrefBoolean(PreferKey.showRss, true) val autoRefreshBook: Boolean diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index 86e3ca53a..f08b3c250 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -133,7 +133,7 @@ class OtherConfigFragment : BasePreferenceFragment(), PreferKey.processText -> sharedPreferences?.let { setProcessTextEnable(it.getBoolean(key, true)) } - PreferKey.showRss -> postEvent(EventBus.SHOW_RSS, "") + PreferKey.showDiscovery, PreferKey.showRss -> postEvent(EventBus.NOTIFY_MAIN, "") PreferKey.defaultCover -> upPreferenceSummary( key, getPrefString(PreferKey.defaultCover) ) diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 65ca55b42..1987cb286 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -41,35 +41,24 @@ class MainActivity : VMBaseActivity(), private var exploreReselected: Long = 0 private var pagePosition = 0 private val fragmentMap = hashMapOf() + private var bottomMenuCount = 2 override fun getViewBinding(): ActivityMainBinding { return ActivityMainBinding.inflate(layoutInflater) } override fun onActivityCreated(savedInstanceState: Bundle?) { + upBottomMenu() binding.apply { ATH.applyEdgeEffectColor(viewPagerMain) ATH.applyBottomNavigationColor(bottomNavigationView) viewPagerMain.offscreenPageLimit = 3 viewPagerMain.adapter = TabFragmentPageAdapter() - viewPagerMain.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - pagePosition = position - when (position) { - 0, 1, 3 -> bottomNavigationView.menu.getItem(position).isChecked = true - 2 -> if (AppConfig.isShowRSS) { - bottomNavigationView.menu.getItem(position).isChecked = true - } else { - bottomNavigationView.menu.getItem(3).isChecked = true - } - } - } - }) + viewPagerMain.registerOnPageChangeCallback(PageChangeCallback()) bottomNavigationView.elevation = if (AppConfig.elevation < 0) elevation else AppConfig.elevation.toFloat() bottomNavigationView.setOnNavigationItemSelectedListener(this@MainActivity) bottomNavigationView.setOnNavigationItemReselectedListener(this@MainActivity) - bottomNavigationView.menu.findItem(R.id.menu_rss).isVisible = AppConfig.isShowRSS } } @@ -90,9 +79,13 @@ class MainActivity : VMBaseActivity(), override fun onNavigationItemSelected(item: MenuItem): Boolean = with(binding) { when (item.itemId) { R.id.menu_bookshelf -> viewPagerMain.setCurrentItem(0, false) - R.id.menu_explore -> viewPagerMain.setCurrentItem(1, false) - R.id.menu_rss -> viewPagerMain.setCurrentItem(2, false) - R.id.menu_my_config -> viewPagerMain.setCurrentItem(3, false) + R.id.menu_discovery -> viewPagerMain.setCurrentItem(1, false) + R.id.menu_rss -> if (AppConfig.showDiscovery) { + viewPagerMain.setCurrentItem(2, false) + } else { + viewPagerMain.setCurrentItem(1, false) + } + R.id.menu_my_config -> viewPagerMain.setCurrentItem(bottomMenuCount - 1, false) } return false } @@ -106,7 +99,7 @@ class MainActivity : VMBaseActivity(), (fragmentMap[0] as? BookshelfFragment)?.gotoTop() } } - R.id.menu_explore -> { + R.id.menu_discovery -> { if (System.currentTimeMillis() - exploreReselected > 300) { exploreReselected = System.currentTimeMillis() } else { @@ -176,12 +169,11 @@ class MainActivity : VMBaseActivity(), observeEvent(EventBus.RECREATE) { recreate() } - observeEvent(EventBus.SHOW_RSS) { - binding.bottomNavigationView.menu.findItem(R.id.menu_rss).isVisible = - AppConfig.isShowRSS - binding.viewPagerMain.adapter?.notifyDataSetChanged() - if (AppConfig.isShowRSS) { - binding.viewPagerMain.setCurrentItem(3, false) + observeEvent(EventBus.NOTIFY_MAIN) { + binding.apply { + upBottomMenu() + viewPagerMain.adapter?.notifyDataSetChanged() + viewPagerMain.setCurrentItem(bottomMenuCount - 1, false) } } observeEvent(PreferKey.threadCount) { @@ -189,22 +181,57 @@ class MainActivity : VMBaseActivity(), } } - private inner class TabFragmentPageAdapter : - FragmentStateAdapter(this) { + private fun upBottomMenu() { + binding.bottomNavigationView.menu.let { menu -> + menu.findItem(R.id.menu_discovery).isVisible = AppConfig.showDiscovery + menu.findItem(R.id.menu_rss).isVisible = AppConfig.showRSS + } + bottomMenuCount = 2 + if (AppConfig.showDiscovery) { + bottomMenuCount++ + } + if (AppConfig.showRSS) { + bottomMenuCount++ + } + } - override fun getItemId(position: Int): Long { - return when (position) { - 2 -> if (AppConfig.isShowRSS) 2 else 3 - else -> position.toLong() + private fun getRealBottomMenuPosition(position: Int): Int { + return when (position) { + 1 -> when { + AppConfig.showDiscovery -> 1 + AppConfig.showRSS -> 2 + else -> 3 } + 2 -> if (AppConfig.showDiscovery) { + if (AppConfig.showRSS) 2 else 3 + } else { + 3 + } + else -> position + } + } + + private inner class PageChangeCallback : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + pagePosition = position + binding.bottomNavigationView.menu + .getItem(getRealBottomMenuPosition(position)).isChecked = true } + } + + private inner class TabFragmentPageAdapter : + FragmentStateAdapter(this) { override fun containsItem(itemId: Long): Boolean { return fragmentMap.containsKey(itemId) } override fun getItemCount(): Int { - return if (AppConfig.isShowRSS) 4 else 3 + return bottomMenuCount + } + + override fun getItemId(position: Int): Long { + return getRealBottomMenuPosition(position).toLong() } override fun createFragment(position: Int): Fragment { diff --git a/app/src/main/res/layout/activity_book_source_edit.xml b/app/src/main/res/layout/activity_book_source_edit.xml index fe1470b47..2e8ce28aa 100644 --- a/app/src/main/res/layout/activity_book_source_edit.xml +++ b/app/src/main/res/layout/activity_book_source_edit.xml @@ -31,7 +31,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="true" - android:text="@string/find" /> + android:text="@string/discovery" /> + app:title="@string/discovery" /> + app:title="@string/discovery" /> + android:title="@string/discovery" /> Are you sure to delete this file? Directory Intelligent import - Discovery + Discovery Switch display styles Import local books requires storage permission Night Theme @@ -803,7 +803,7 @@ Export the list of books Import the list of books Download in advance - Download 10 chapters in advance + Download %s chapters in advance Is enabled Background image Copy book URL @@ -839,4 +839,5 @@ Hide Hide when status bar show Reverse toc + 显示发现 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 366b70c61..70cd3ed3c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -471,7 +471,7 @@ Você tem certeza que quer excluir este arquivo? Pasta Importação inteligente - Descoberta + Descoberta Mudar o modo de exibição Importação de livros locais requer permissão de armazenamento Tema Noturno @@ -839,5 +839,6 @@ Hide Hide when status bar show 反转目录 + 显示发现 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 4a8e636c0..f46c68166 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -460,7 +460,7 @@ 確定刪除文件嗎? 手機目錄 智能導入 - 發現 + 發現 切換顯示樣式 導入本地書籍需存儲權限 夜間模式 @@ -837,5 +837,6 @@ 輸入自訂源分組名稱 【%s】 反转目录 + 显示发现 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 182ea03aa..aafc932be 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -470,7 +470,7 @@ 確定刪除文件嗎? 手機目錄 智慧匯入 - 發現 + 發現 切換顯示樣式 匯入本機書籍需儲存權限 夜間模式 @@ -838,5 +838,6 @@ 隐藏 状态栏显示时隐藏 反转目录 + 显示发现 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 170d773e0..1c23cf5f2 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -470,7 +470,7 @@ 确定删除文件吗? 手机目录 智能导入 - 发现 + 发现 切换显示样式 导入本地书籍需存储权限 夜间模式 @@ -838,5 +838,6 @@ 隐藏 状态栏显示时隐藏 反转目录 + 显示发现 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0781727fb..557de3291 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -471,7 +471,7 @@ Are you sure to delete this file? Directory Intelligent import - Discovery + Discovery Switch display styles Import local books requires storage permission Night Theme @@ -803,7 +803,7 @@ Export the list of books Import the list of books Download in advance - Download 10 chapters in advance + Download %s chapters in advance Is enabled Background image Copy book URL @@ -839,4 +839,5 @@ Hide Hide when status bar show Reverse toc + Show diff --git a/app/src/main/res/xml/pref_config_other.xml b/app/src/main/res/xml/pref_config_other.xml index 32b5aa3c1..217fc4156 100644 --- a/app/src/main/res/xml/pref_config_other.xml +++ b/app/src/main/res/xml/pref_config_other.xml @@ -30,6 +30,12 @@ android:title="@string/pt_default_read" app:iconSpaceReserved="false" /> + + Date: Sat, 22 May 2021 19:16:31 +0800 Subject: [PATCH 040/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=91=E7=8E=B0?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E9=9A=90=E8=97=8F=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 7f5b5a669..0e09f0084 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -7,6 +7,7 @@ * 添加反转目录功能 * 修复分享bug * 详情页添加登录菜单 +* 添加发现界面隐藏配置 **2021/05/16** * 添加总是使用默认封面配置 From 6e69efb34e29269c4f6cd8abcabc19e4ec2a9d63 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 22 May 2021 22:49:33 +0800 Subject: [PATCH 041/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=91=E7=8E=B0?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E9=9A=90=E8=97=8F=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/main/MainActivity.kt | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 1987cb286..9741e726d 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -42,6 +42,7 @@ class MainActivity : VMBaseActivity(), private var pagePosition = 0 private val fragmentMap = hashMapOf() private var bottomMenuCount = 2 + private val realPositions = arrayOf(0, 1, 2, 3) override fun getViewBinding(): ActivityMainBinding { return ActivityMainBinding.inflate(layoutInflater) @@ -182,32 +183,30 @@ class MainActivity : VMBaseActivity(), } private fun upBottomMenu() { + val showDiscovery = AppConfig.showDiscovery + val showRss = AppConfig.showRSS binding.bottomNavigationView.menu.let { menu -> - menu.findItem(R.id.menu_discovery).isVisible = AppConfig.showDiscovery - menu.findItem(R.id.menu_rss).isVisible = AppConfig.showRSS + menu.findItem(R.id.menu_discovery).isVisible = showDiscovery + menu.findItem(R.id.menu_rss).isVisible = showRss } bottomMenuCount = 2 - if (AppConfig.showDiscovery) { - bottomMenuCount++ - } - if (AppConfig.showRSS) { - bottomMenuCount++ - } - } - - private fun getRealBottomMenuPosition(position: Int): Int { - return when (position) { - 1 -> when { - AppConfig.showDiscovery -> 1 - AppConfig.showRSS -> 2 - else -> 3 + realPositions[1] = 1 + realPositions[2] = 2 + when { + showDiscovery -> bottomMenuCount++ + showRss -> { + realPositions[1] = 2 + realPositions[2] = 3 } - 2 -> if (AppConfig.showDiscovery) { - if (AppConfig.showRSS) 2 else 3 - } else { - 3 + else -> { + realPositions[1] = 3 + realPositions[2] = 3 } - else -> position + } + if (showRss) { + bottomMenuCount++ + } else { + realPositions[2] = 3 } } @@ -215,7 +214,7 @@ class MainActivity : VMBaseActivity(), override fun onPageSelected(position: Int) { pagePosition = position binding.bottomNavigationView.menu - .getItem(getRealBottomMenuPosition(position)).isChecked = true + .getItem(realPositions[position]).isChecked = true } } @@ -231,7 +230,7 @@ class MainActivity : VMBaseActivity(), } override fun getItemId(position: Int): Long { - return getRealBottomMenuPosition(position).toLong() + return realPositions[position].toLong() } override fun createFragment(position: Int): Fragment { From 971dc3f2c5c052bedd3bb0a17ea994e2954e5c2f Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 23 May 2021 22:25:12 +0800 Subject: [PATCH 042/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- .../java/io/legado/app/model/webBook/BookChapterList.kt | 8 +++++--- .../main/java/io/legado/app/ui/book/toc/TocViewModel.kt | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5c59be8c1..0b7ba28a4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -181,7 +181,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.1' //Glide - implementation 'com.github.bumptech.glide:glide:4.11.0' + implementation 'com.github.bumptech.glide:glide:4.12.0' //webServer implementation 'org.nanohttpd:nanohttpd:2.3.1' diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index 0867fd95e..06ae294b4 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt @@ -107,10 +107,12 @@ object BookChapterList { } val lh = LinkedHashSet(chapterList) val list = ArrayList(lh) - list.reverse() + if (!book.getReverseToc()) { + list.reverse() + } Debug.log(book.origin, "◇目录总数:${list.size}") - for ((index, item) in list.withIndex()) { - item.index = index + list.forEachIndexed { index, bookChapter -> + bookChapter.index = index } book.latestChapterTitle = list.last().title book.durChapterTitle = diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt index cb0f8d708..a20df2249 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt @@ -25,7 +25,7 @@ class TocViewModel(application: Application) : BaseViewModel(application) { fun reverseToc() { execute { bookData.value?.let { - it.setReverseToc(it.getReverseToc()) + it.setReverseToc(!it.getReverseToc()) val toc = appDb.bookChapterDao.getChapterList(bookUrl) val newToc = toc.reversed() newToc.forEachIndexed { index, bookChapter -> From ba37b489694999aad8d96d52045d2de35f9d67de Mon Sep 17 00:00:00 2001 From: gongym <1967988842@qq.com> Date: Mon, 24 May 2021 12:51:47 +0800 Subject: [PATCH 043/112] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=BB=91=E5=8A=A8=E4=BC=9A=E5=AF=BC=E8=87=B4=E5=B7=A6=E5=8F=B3?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E9=97=AE=E9=A2=98=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E6=96=B0=E7=9A=84=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BB=84?= =?UTF-8?q?=E4=BB=B6RecyclerViewAtViewPager2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/widget/RecyclerViewAtViewPager2.java | 55 +++++++++++++++++++ app/src/main/res/layout/fragment_books.xml | 2 +- app/src/main/res/layout/fragment_explore.xml | 2 +- app/src/main/res/layout/fragment_rss.xml | 2 +- .../main/res/layout/fragment_rss_articles.xml | 2 +- 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.java diff --git a/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.java b/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.java new file mode 100644 index 000000000..f701b1a14 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.java @@ -0,0 +1,55 @@ +package io.legado.app.ui.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +public class RecyclerViewAtViewPager2 extends RecyclerView { + + public RecyclerViewAtViewPager2(@NonNull Context context) { + super(context); + } + + public RecyclerViewAtViewPager2(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public RecyclerViewAtViewPager2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + private int startX, startY; + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + startX = (int) ev.getX(); + startY = (int) ev.getY(); + getParent().requestDisallowInterceptTouchEvent(true); + break; + case MotionEvent.ACTION_MOVE: + int endX = (int) ev.getX(); + int endY = (int) ev.getY(); + int disX = Math.abs(endX - startX); + int disY = Math.abs(endY - startY); + if (disX > disY) { + if (disX > 50) { + getParent().requestDisallowInterceptTouchEvent(false); + } + } else { + getParent().requestDisallowInterceptTouchEvent(true); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + getParent().requestDisallowInterceptTouchEvent(false); + break; + } + return super.dispatchTouchEvent(ev); + } +} diff --git a/app/src/main/res/layout/fragment_books.xml b/app/src/main/res/layout/fragment_books.xml index 8da85f913..1e2fbe64b 100644 --- a/app/src/main/res/layout/fragment_books.xml +++ b/app/src/main/res/layout/fragment_books.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - From ecd8f0391a3f307b1635d4791982223bb9641466 Mon Sep 17 00:00:00 2001 From: gongym <1967988842@qq.com> Date: Mon, 24 May 2021 13:14:42 +0800 Subject: [PATCH 044/112] Rename .java to .kt --- ...{RecyclerViewAtViewPager2.java => RecyclerViewAtViewPager2.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/io/legado/app/ui/widget/{RecyclerViewAtViewPager2.java => RecyclerViewAtViewPager2.kt} (100%) diff --git a/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.java b/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt similarity index 100% rename from app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.java rename to app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt From 35ad224e5559dee0b6e705db3452de32493fdc8a Mon Sep 17 00:00:00 2001 From: gongym <1967988842@qq.com> Date: Mon, 24 May 2021 13:14:42 +0800 Subject: [PATCH 045/112] java->kotlin --- .../app/ui/widget/RecyclerViewAtViewPager2.kt | 81 ++++++++----------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt b/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt index f701b1a14..10abec353 100644 --- a/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt +++ b/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt @@ -1,55 +1,40 @@ -package io.legado.app.ui.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -public class RecyclerViewAtViewPager2 extends RecyclerView { - - public RecyclerViewAtViewPager2(@NonNull Context context) { - super(context); - } - - public RecyclerViewAtViewPager2(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public RecyclerViewAtViewPager2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - private int startX, startY; - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - startX = (int) ev.getX(); - startY = (int) ev.getY(); - getParent().requestDisallowInterceptTouchEvent(true); - break; - case MotionEvent.ACTION_MOVE: - int endX = (int) ev.getX(); - int endY = (int) ev.getY(); - int disX = Math.abs(endX - startX); - int disY = Math.abs(endY - startY); +package io.legado.app.ui.widget + +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import android.util.AttributeSet +import android.view.MotionEvent +import kotlin.math.abs + +class RecyclerViewAtViewPager2 : 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) { - getParent().requestDisallowInterceptTouchEvent(false); + parent.requestDisallowInterceptTouchEvent(false) } } else { - getParent().requestDisallowInterceptTouchEvent(true); + parent.requestDisallowInterceptTouchEvent(true) } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - getParent().requestDisallowInterceptTouchEvent(false); - break; + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent(false) } - return super.dispatchTouchEvent(ev); + return super.dispatchTouchEvent(ev) } -} +} \ No newline at end of file From a91e7b4e781c13a4c244109ee5e1d3cfb24d8d9b Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 24 May 2021 17:28:58 +0800 Subject: [PATCH 046/112] =?UTF-8?q?=E5=8F=8D=E8=BD=AC=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=88=B7=E6=96=B0=E6=AD=A3=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/ReadBookActivity.kt | 4 +-- .../app/ui/book/read/ReadBookViewModel.kt | 4 +-- .../app/ui/book/toc/BookmarkFragment.kt | 12 +++++---- .../app/ui/book/toc/ChapterListFragment.kt | 6 +++-- .../io/legado/app/ui/book/toc/TocActivity.kt | 25 ++++++++++++------- .../io/legado/app/ui/book/toc/TocViewModel.kt | 8 +++--- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 8dc70a332..185f0bd15 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -73,9 +73,7 @@ class ReadBookActivity : ReadBookBaseActivity(), private val tocActivity = registerForActivityResult(TocActivityResult()) { it?.let { - if (it.first != ReadBook.durChapterIndex) { - viewModel.openChapter(it.first, it.second) - } + viewModel.openChapter(it.first, it.second) } } private val sourceEditActivity = diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index 2b6057acd..ffe391f68 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -246,9 +246,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { } fun openChapter(index: Int, durChapterPos: Int = 0, success: (() -> Unit)? = null) { - ReadBook.prevTextChapter = null - ReadBook.curTextChapter = null - ReadBook.nextTextChapter = null + ReadBook.clearTextChapter() ReadBook.callBack?.upContent() if (index != ReadBook.durChapterIndex) { ReadBook.durChapterIndex = index diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index 609683d90..b6e831613 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt @@ -66,11 +66,13 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark override fun onClick(bookmark: Bookmark) { - val bookmarkData = Intent() - bookmarkData.putExtra("index", bookmark.chapterIndex) - bookmarkData.putExtra("chapterPos", bookmark.chapterPos) - activity?.setResult(Activity.RESULT_OK, bookmarkData) - activity?.finish() + activity?.run { + setResult(Activity.RESULT_OK, Intent().apply { + putExtra("index", bookmark.chapterIndex) + putExtra("chapterPos", bookmark.chapterPos) + }) + finish() + } } override fun onLongClick(bookmark: Bookmark) { diff --git a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt index e2ba1cfa1..63e18bf22 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt @@ -137,8 +137,10 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt } override fun openChapter(bookChapter: BookChapter) { - activity?.setResult(RESULT_OK, Intent().putExtra("index", bookChapter.index)) - activity?.finish() + activity?.run { + setResult(RESULT_OK, Intent().putExtra("index", bookChapter.index)) + finish() + } } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt index 57a213a49..7152219a0 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt @@ -1,5 +1,6 @@ package io.legado.app.ui.book.toc +import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -78,11 +79,25 @@ class TocActivity : VMBaseActivity() { override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.menu_reverse_toc -> viewModel.reverseToc() + R.id.menu_reverse_toc -> viewModel.reverseToc { + setResult(RESULT_OK, Intent().apply { + putExtra("index", it.durChapterIndex) + putExtra("chapterPos", it.durChapterPos) + }) + } } return super.onCompatOptionsItemSelected(item) } + override fun onBackPressed() { + if (tabLayout.isGone) { + searchView?.onActionViewCollapsed() + tabLayout.visible() + } else { + super.onBackPressed() + } + } + private inner class TabFragmentPageAdapter : FragmentStateAdapter(this) { override fun getItemCount(): Int { @@ -98,12 +113,4 @@ class TocActivity : VMBaseActivity() { } - override fun onBackPressed() { - if (tabLayout.isGone) { - searchView?.onActionViewCollapsed() - tabLayout.visible() - } else { - super.onBackPressed() - } - } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt index a20df2249..992101490 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt @@ -22,10 +22,10 @@ class TocViewModel(application: Application) : BaseViewModel(application) { } } - fun reverseToc() { + fun reverseToc(success: (book: Book) -> Unit) { execute { - bookData.value?.let { - it.setReverseToc(!it.getReverseToc()) + bookData.value?.apply { + setReverseToc(!getReverseToc()) val toc = appDb.bookChapterDao.getChapterList(bookUrl) val newToc = toc.reversed() newToc.forEachIndexed { index, bookChapter -> @@ -33,6 +33,8 @@ class TocViewModel(application: Application) : BaseViewModel(application) { } appDb.bookChapterDao.insert(*newToc.toTypedArray()) } + }.onSuccess { + it?.let(success) } } From 4afe79bcf0b6b6c423dcc09e6ccb8df7b7d2a1fd Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 24 May 2021 21:11:48 +0800 Subject: [PATCH 047/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c27b2a08b..894cbf12f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.5.0' + ext.kotlin_version = '1.5.10' repositories { google() maven { url 'https://maven.aliyun.com/repository/public/'} @@ -11,7 +11,6 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:4.2.1' - //noinspection DifferentKotlinGradleVersion classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "de.timfreiheit.resourceplaceholders:placeholders:0.3" } From 182a8f06221bb63a7fc47258e1cdf77bc8bfe9f3 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 24 May 2021 21:31:06 +0800 Subject: [PATCH 048/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 0e09f0084..bd024a9bd 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,6 +3,9 @@ * 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2021/05/24** +* 反转目录后刷新内容 + **2021/05/21** * 添加反转目录功能 * 修复分享bug From 7bf6eef2aa48b66c51d17f752eb4926ba9addac1 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 24 May 2021 21:56:12 +0800 Subject: [PATCH 049/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + .../RecyclerViewAtPager2.kt} | 12 ++++++++---- app/src/main/res/layout/fragment_books.xml | 2 +- app/src/main/res/layout/fragment_explore.xml | 2 +- app/src/main/res/layout/fragment_rss.xml | 2 +- app/src/main/res/layout/fragment_rss_articles.xml | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) rename app/src/main/java/io/legado/app/ui/widget/{RecyclerViewAtViewPager2.kt => recycler/RecyclerViewAtPager2.kt} (89%) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index bd024a9bd..e1016e4e3 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -5,6 +5,7 @@ **2021/05/24** * 反转目录后刷新内容 +* 修复上下滑动会导致左右切换问题 **2021/05/21** * 添加反转目录功能 diff --git a/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt b/app/src/main/java/io/legado/app/ui/widget/recycler/RecyclerViewAtPager2.kt similarity index 89% rename from app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt rename to app/src/main/java/io/legado/app/ui/widget/recycler/RecyclerViewAtPager2.kt index 10abec353..7ab5ed8e9 100644 --- a/app/src/main/java/io/legado/app/ui/widget/RecyclerViewAtViewPager2.kt +++ b/app/src/main/java/io/legado/app/ui/widget/recycler/RecyclerViewAtPager2.kt @@ -1,15 +1,19 @@ -package io.legado.app.ui.widget +package io.legado.app.ui.widget.recycler import android.content.Context -import androidx.recyclerview.widget.RecyclerView import android.util.AttributeSet import android.view.MotionEvent +import androidx.recyclerview.widget.RecyclerView import kotlin.math.abs -class RecyclerViewAtViewPager2 : RecyclerView { +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) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) private var startX = 0 private var startY = 0 diff --git a/app/src/main/res/layout/fragment_books.xml b/app/src/main/res/layout/fragment_books.xml index 1e2fbe64b..e67df9c4e 100644 --- a/app/src/main/res/layout/fragment_books.xml +++ b/app/src/main/res/layout/fragment_books.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - From d6e3349f8cbec1a2ef5beff5b6318b37b9202793 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 24 May 2021 22:23:42 +0800 Subject: [PATCH 050/112] =?UTF-8?q?=E7=B2=BE=E7=A1=AE=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8C=85=E5=90=AB=E5=85=B3=E9=94=AE=E8=AF=8D?= =?UTF-8?q?=E7=9A=84,=E6=AF=94=E5=A6=82=E6=90=9C=E7=B4=A2=E4=BA=94?= =?UTF-8?q?=E8=A1=8C=20=E4=BA=94=E8=A1=8C=E5=A4=A9=E4=B9=9F=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=87=BA=E6=9D=A5,=20=E4=BA=94=E5=A4=A9=E8=A1=8C?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 1 + .../app/ui/book/search/SearchViewModel.kt | 46 ++++++++++++------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index e1016e4e3..53f055054 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -6,6 +6,7 @@ **2021/05/24** * 反转目录后刷新内容 * 修复上下滑动会导致左右切换问题 +* 精确搜索增加包含关键词的,比如搜索五行 五行天也显示出来, 五天行不显示 **2021/05/21** * 添加反转目录功能 diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index 2eee0dbeb..c4b6c5f15 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -83,23 +83,37 @@ class SearchViewModel(application: Application) : BaseViewModel(application), @Synchronized private fun mergeItems(scope: CoroutineScope, newDataS: List, precision: Boolean) { if (newDataS.isNotEmpty()) { - val prevData = ArrayList(searchBooks) - val precisionData = arrayListOf() - prevData.forEach { + val copyData = ArrayList(searchBooks) + val equalData = arrayListOf() + val containsData = arrayListOf() + val otherData = arrayListOf() + copyData.forEach { if (!scope.isActive) return if (it.name == searchKey || it.author == searchKey) { - precisionData.add(it) + equalData.add(it) + } else if (it.name.contains(searchKey) || it.author.contains(searchKey)) { + containsData.add(it) + } else { + otherData.add(it) } } - repeat(precisionData.size) { - if (!scope.isActive) return - prevData.removeAt(0) - } newDataS.forEach { nBook -> if (!scope.isActive) return if (nBook.name == searchKey || nBook.author == searchKey) { var hasSame = false - precisionData.forEach { pBook -> + equalData.forEach { pBook -> + if (!scope.isActive) return + if (pBook.name == nBook.name && pBook.author == nBook.author) { + pBook.addOrigin(nBook.origin) + hasSame = true + } + } + if (!hasSame) { + equalData.add(nBook) + } + } else if (nBook.name.contains(searchKey) || nBook.author.contains(searchKey)) { + var hasSame = false + containsData.forEach { pBook -> if (!scope.isActive) return if (pBook.name == nBook.name && pBook.author == nBook.author) { pBook.addOrigin(nBook.origin) @@ -107,11 +121,11 @@ class SearchViewModel(application: Application) : BaseViewModel(application), } } if (!hasSame) { - precisionData.add(nBook) + containsData.add(nBook) } } else if (!precision) { var hasSame = false - prevData.forEach { pBook -> + otherData.forEach { pBook -> if (!scope.isActive) return if (pBook.name == nBook.name && pBook.author == nBook.author) { pBook.addOrigin(nBook.origin) @@ -119,17 +133,17 @@ class SearchViewModel(application: Application) : BaseViewModel(application), } } if (!hasSame) { - prevData.add(nBook) + otherData.add(nBook) } } } if (!scope.isActive) return - precisionData.sortByDescending { it.origins.size } - if (!scope.isActive) return + equalData.sortByDescending { it.origins.size } + equalData.addAll(containsData) if (!precision) { - precisionData.addAll(prevData) + equalData.addAll(otherData) } - searchBooks = precisionData + searchBooks = equalData upAdapter() } } From a91d3d3d30c2630ca1a878e0b87d946af3a6a9cb Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 24 May 2021 22:38:10 +0800 Subject: [PATCH 051/112] =?UTF-8?q?=E7=B2=BE=E7=A1=AE=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8C=85=E5=90=AB=E5=85=B3=E9=94=AE=E8=AF=8D?= =?UTF-8?q?=E7=9A=84,=E6=AF=94=E5=A6=82=E6=90=9C=E7=B4=A2=E4=BA=94?= =?UTF-8?q?=E8=A1=8C=20=E4=BA=94=E8=A1=8C=E5=A4=A9=E4=B9=9F=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=87=BA=E6=9D=A5,=20=E4=BA=94=E5=A4=A9=E8=A1=8C?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/search/SearchViewModel.kt | 2 +- .../widget/recycler/UpLinearLayoutManager.kt | 35 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index c4b6c5f15..42fa04e83 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -139,7 +139,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application), } if (!scope.isActive) return equalData.sortByDescending { it.origins.size } - equalData.addAll(containsData) + equalData.addAll(containsData.sortedByDescending { it.origins.size }) if (!precision) { equalData.addAll(otherData) } diff --git a/app/src/main/java/io/legado/app/ui/widget/recycler/UpLinearLayoutManager.kt b/app/src/main/java/io/legado/app/ui/widget/recycler/UpLinearLayoutManager.kt index 1dfd9ec9a..f0236bbea 100644 --- a/app/src/main/java/io/legado/app/ui/widget/recycler/UpLinearLayoutManager.kt +++ b/app/src/main/java/io/legado/app/ui/widget/recycler/UpLinearLayoutManager.kt @@ -17,23 +17,30 @@ class UpLinearLayoutManager(val context: Context) : LinearLayoutManager(context) scroller.offset = offset startSmoothScroll(scroller) } -} -class UpLinearSmoothScroller(context: Context?): LinearSmoothScroller(context) { - var offset = 0 + class UpLinearSmoothScroller(context: Context?) : LinearSmoothScroller(context) { + var offset = 0 - override fun getVerticalSnapPreference(): Int { - return SNAP_TO_START - } + override fun getVerticalSnapPreference(): Int { + return SNAP_TO_START + } - override fun getHorizontalSnapPreference(): Int { - return SNAP_TO_START - } + override fun getHorizontalSnapPreference(): Int { + return SNAP_TO_START + } - override fun calculateDtToFit(viewStart: Int, viewEnd: Int, boxStart: Int, boxEnd: Int, snapPreference: Int): Int { - if (snapPreference == SNAP_TO_START) { - return boxStart - viewStart + offset + override fun calculateDtToFit( + viewStart: Int, + viewEnd: Int, + boxStart: Int, + boxEnd: Int, + snapPreference: Int + ): Int { + if (snapPreference == SNAP_TO_START) { + return boxStart - viewStart + offset + } + throw IllegalArgumentException("snap preference should be SNAP_TO_START") } - throw IllegalArgumentException("snap preference should be SNAP_TO_START") } -} \ No newline at end of file + +} From 42f4a16363f3e016604ffdfb31c8a62dc4bbbe04 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 24 May 2021 22:50:11 +0800 Subject: [PATCH 052/112] =?UTF-8?q?RecyclerView=20at=20viewPager2=20?= =?UTF-8?q?=E8=BF=98=E6=98=AF=E6=9C=89=E7=82=B9=E9=97=AE=E9=A2=98,?= =?UTF-8?q?=E5=85=88=E4=B8=8D=E7=94=A8=E5=9C=A8=E4=B9=A6=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/layout/fragment_books.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_books.xml b/app/src/main/res/layout/fragment_books.xml index e67df9c4e..8da85f913 100644 --- a/app/src/main/res/layout/fragment_books.xml +++ b/app/src/main/res/layout/fragment_books.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - Date: Tue, 25 May 2021 21:49:05 +0800 Subject: [PATCH 053/112] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/ui/book/info/BookInfoActivity.kt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index d4e6cedd3..65fdfff87 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -17,6 +17,7 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.constant.BookType import io.legado.app.constant.Theme +import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.databinding.ActivityBookInfoBinding @@ -37,6 +38,9 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.toc.TocActivityResult import io.legado.app.ui.widget.image.CoverImageView import io.legado.app.utils.* +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class BookInfoActivity : @@ -49,11 +53,14 @@ class BookInfoActivity : private val tocActivityResult = registerForActivityResult(TocActivityResult()) { it?.let { viewModel.bookData.value?.let { book -> - if (book.durChapterIndex != it.first) { - book.durChapterIndex = it.first - book.durChapterPos = it.second + launch { + withContext(IO) { + book.durChapterIndex = it.first + book.durChapterPos = it.second + appDb.bookDao.update(book) + } + startReadActivity(book) } - startReadActivity(book) } } ?: let { if (!viewModel.inBookshelf) { From c96961f1434cd8e26bfe6caadfa2293a6f60b77d Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 26 May 2021 17:36:25 +0800 Subject: [PATCH 054/112] =?UTF-8?q?=E4=B9=A6=E7=AD=BE=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E4=B9=A6=E5=90=8D=E5=92=8C=E4=BD=9C=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 4 +++ .../java/io/legado/app/data/AppDatabase.kt | 31 +++++++++++++++++-- .../io/legado/app/data/dao/BookmarkDao.kt | 19 +++++++----- .../java/io/legado/app/data/entities/Book.kt | 7 +++++ .../io/legado/app/data/entities/Bookmark.kt | 10 +++--- .../app/ui/book/read/ReadBookActivity.kt | 13 +++----- .../app/ui/book/read/page/ContentTextView.kt | 15 ++++----- .../app/ui/book/toc/BookmarkFragment.kt | 16 +++++----- 8 files changed, 77 insertions(+), 38 deletions(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 53f055054..ec1047cbc 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,6 +3,10 @@ * 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2021/05/26** +* 书签绑定书名与作者 +* 修复详情页目录问题 + **2021/05/24** * 反转目录后刷新内容 * 修复上下滑动会导致左右切换问题 diff --git a/app/src/main/java/io/legado/app/data/AppDatabase.kt b/app/src/main/java/io/legado/app/data/AppDatabase.kt index 8add07077..fe4319b13 100644 --- a/app/src/main/java/io/legado/app/data/AppDatabase.kt +++ b/app/src/main/java/io/legado/app/data/AppDatabase.kt @@ -24,7 +24,7 @@ val appDb by lazy { RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class, RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class, Cache::class, RuleSub::class], - version = 32, + version = 33, exportSchema = true ) abstract class AppDatabase : RoomDatabase() { @@ -60,7 +60,7 @@ abstract class AppDatabase : RoomDatabase() { migration_19_20, migration_20_21, migration_21_22, migration_22_23, migration_23_24, migration_24_25, migration_25_26, migration_26_27, migration_27_28, migration_28_29, migration_29_30, migration_30_31, - migration_31_32 + migration_31_32, migration_32_33 ) .allowMainThreadQueries() .addCallback(dbCallback) @@ -304,6 +304,33 @@ abstract class AppDatabase : RoomDatabase() { database.execSQL("DROP TABLE `epubChapters`") } } + + private val migration_32_33 = object : Migration(32, 33) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE bookmarks RENAME TO bookmarks_old") + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `bookmarks` (`time` INTEGER NOT NULL, + `bookName` TEXT NOT NULL, `bookAuthor` TEXT NOT NULL, `chapterIndex` INTEGER NOT NULL, + `chapterPos` INTEGER NOT NULL, `chapterName` TEXT NOT NULL, `bookText` TEXT NOT NULL, + `content` TEXT NOT NULL, PRIMARY KEY(`time`)) + """ + ) + database.execSQL( + """ + CREATE INDEX IF NOT EXISTS `index_bookmarks_bookName_bookAuthor` ON `bookmarks` (`bookName`, `bookAuthor`) + """ + ) + database.execSQL( + """ + insert into bookmarks (time, bookName, bookAuthor, chapterIndex, chapterPos, chapterName, bookText, content) + select time, ifNull(b.name, bookName) bookName, ifNull(b.author, bookAuthor) bookAuthor, + chapterIndex, chapterPos, chapterName, bookText, content from bookmarks_old o + left join books b on o.bookUrl = b.bookUrl + """ + ) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt index 633c1187c..4ce8b05bb 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt @@ -11,15 +11,23 @@ interface BookmarkDao { @get:Query("select * from bookmarks") val all: List - @Query("select * from bookmarks where bookUrl = :bookUrl or (bookName = :bookName and bookAuthor = :bookAuthor)") + @Query("select * from bookmarks where bookName = :bookName and bookAuthor = :bookAuthor") fun observeByBook( - bookUrl: String, bookName: String, bookAuthor: String ): LiveData> - @Query("SELECT * FROM bookmarks where bookUrl = :bookUrl and chapterName like '%'||:key||'%' or content like '%'||:key||'%'") - fun liveDataSearch(bookUrl: String, key: String): LiveData> + @Query( + """ + SELECT * FROM bookmarks where bookName = :bookName and bookAuthor = :bookAuthor + and chapterName like '%'||:key||'%' or content like '%'||:key||'%' + """ + ) + fun liveDataSearch( + bookName: String, + bookAuthor: String, + key: String + ): LiveData> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg bookmark: Bookmark) @@ -30,7 +38,4 @@ interface BookmarkDao { @Delete fun delete(vararg bookmark: Bookmark) - @Query("delete from bookmarks where bookUrl = :bookUrl and chapterName like '%'||:chapterName||'%'") - fun delByBookmark(bookUrl: String, chapterName: String) - } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index 240af5e9a..73b89205f 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -227,6 +227,13 @@ data class Book( } } + fun createBookMark(): Bookmark { + return Bookmark( + bookName = name, + bookAuthor = author, + ) + } + companion object { const val hTag = 2L const val rubyTag = 4L diff --git a/app/src/main/java/io/legado/app/data/entities/Bookmark.kt b/app/src/main/java/io/legado/app/data/entities/Bookmark.kt index 799bf7146..9ee4a8ea0 100644 --- a/app/src/main/java/io/legado/app/data/entities/Bookmark.kt +++ b/app/src/main/java/io/legado/app/data/entities/Bookmark.kt @@ -7,12 +7,14 @@ import androidx.room.PrimaryKey import kotlinx.parcelize.Parcelize @Parcelize -@Entity(tableName = "bookmarks", indices = [(Index(value = ["time"], unique = true))]) +@Entity( + tableName = "bookmarks", + indices = [(Index(value = ["bookName", "bookAuthor"], unique = false))] +) data class Bookmark( @PrimaryKey - var time: Long = System.currentTimeMillis(), - var bookUrl: String = "", - var bookName: String = "", + val time: Long = System.currentTimeMillis(), + val bookName: String = "", val bookAuthor: String = "", var chapterIndex: Int = 0, var chapterPos: Int = 0, diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 185f0bd15..4726949cb 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -23,7 +23,6 @@ import io.legado.app.constant.Status import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookProgress -import io.legado.app.data.entities.Bookmark import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadTipConfig import io.legado.app.help.storage.Backup @@ -241,14 +240,12 @@ class ReadBookActivity : ReadBookBaseActivity(), val book = ReadBook.book val page = ReadBook.curTextChapter?.page(ReadBook.durPageIndex()) if (book != null && page != null) { - val bookmark = Bookmark( - bookUrl = book.bookUrl, - bookName = book.name, - chapterIndex = ReadBook.durChapterIndex, - chapterPos = ReadBook.durChapterPos, - chapterName = page.title, + val bookmark = book.createBookMark().apply { + chapterIndex = ReadBook.durChapterIndex + chapterPos = ReadBook.durChapterPos + chapterName = page.title bookText = page.text.trim() - ) + } showBookMark(bookmark) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index 1be7ce084..970ec18ed 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -497,17 +497,14 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at fun createBookmark(): Bookmark? { val page = relativePage(selectStart[0]) page.getTextChapter()?.let { chapter -> - val chapterPos = chapter.getReadLength(page.index) + - page.getSelectStartLength(selectStart[1], selectStart[2]) ReadBook.book?.let { book -> - return Bookmark( - bookUrl = book.bookUrl, - bookName = book.name, - chapterIndex = page.chapterIndex, - chapterPos = chapterPos, - chapterName = chapter.title, + return book.createBookMark().apply { + chapterIndex = page.chapterIndex + chapterPos = chapter.getReadLength(page.index) + + page.getSelectStartLength(selectStart[1], selectStart[2]) + chapterName = chapter.title bookText = selectedText - ) + } } } return null diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index b6e831613..70797a6e0 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt @@ -48,19 +48,19 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark private fun initData(book: Book) { bookmarkLiveData?.removeObservers(viewLifecycleOwner) - bookmarkLiveData = appDb.bookmarkDao.observeByBook(book.bookUrl, book.name, book.author) + bookmarkLiveData = appDb.bookmarkDao.observeByBook(book.name, book.author) bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) }) } override fun startBookmarkSearch(newText: String?) { - if (newText.isNullOrBlank()) { - viewModel.bookData.value?.let { - initData(it) + viewModel.bookData.value?.let { book -> + if (newText.isNullOrBlank()) { + initData(book) + } else { + bookmarkLiveData?.removeObservers(viewLifecycleOwner) + bookmarkLiveData = appDb.bookmarkDao.liveDataSearch(book.name, book.author, newText) + bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) }) } - } else { - bookmarkLiveData?.removeObservers(viewLifecycleOwner) - bookmarkLiveData = appDb.bookmarkDao.liveDataSearch(viewModel.bookUrl, newText) - bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) }) } } From abd30ef58f2f2b810b2c7e4ad7fa54e55b8d1a05 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 26 May 2021 22:35:03 +0800 Subject: [PATCH 055/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/ui/book/info/BookInfoActivity.kt | 4 ++++ .../main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index 65fdfff87..de21ed9b5 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -59,6 +59,10 @@ class BookInfoActivity : book.durChapterPos = it.second appDb.bookDao.update(book) } + viewModel.chapterListData.value?.let { chapterList -> + binding.tvToc.text = + getString(R.string.toc_s, chapterList[book.durChapterIndex].title) + } startReadActivity(book) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index ffe391f68..6fe2feaec 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -68,9 +68,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { if (ReadBook.durChapterIndex != book.durChapterIndex) { ReadBook.durChapterIndex = book.durChapterIndex ReadBook.durChapterPos = book.durChapterPos - ReadBook.prevTextChapter = null - ReadBook.curTextChapter = null - ReadBook.nextTextChapter = null + ReadBook.clearTextChapter() } ReadBook.titleDate.postValue(book.name) ReadBook.upWebBook(book) From fa7f69c5efe3d2e1c96141c18a15509a574d24f5 Mon Sep 17 00:00:00 2001 From: gedoor Date: Thu, 27 May 2021 10:57:16 +0800 Subject: [PATCH 056/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/book/info/BookInfoActivity.kt | 2 ++ .../io/legado/app/ui/book/info/BookInfoViewModel.kt | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index de21ed9b5..51d23caaa 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -55,6 +55,7 @@ class BookInfoActivity : viewModel.bookData.value?.let { book -> launch { withContext(IO) { + viewModel.durChapterIndex = it.first book.durChapterIndex = it.first book.durChapterPos = it.second appDb.bookDao.update(book) @@ -75,6 +76,7 @@ class BookInfoActivity : private val readBookResult = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { + viewModel.refreshData(intent) if (it.resultCode == RESULT_OK) { viewModel.inBookshelf = true upTvBookshelf() diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt index f6fb03b3b..0db62f64a 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt @@ -35,6 +35,16 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { } } + fun refreshData(intent: Intent) { + execute { + val name = intent.getStringExtra("name") ?: "" + val author = intent.getStringExtra("author") ?: "" + appDb.bookDao.getBook(name, author)?.let { book -> + setBook(book) + } + } + } + private fun setBook(book: Book) { durChapterIndex = book.durChapterIndex bookData.postValue(book) From dba2ac98352449ca76240afecb256d21c9992814 Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 28 May 2021 09:07:55 +0800 Subject: [PATCH 057/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/base/BaseActivity.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/base/BaseActivity.kt b/app/src/main/java/io/legado/app/base/BaseActivity.kt index 8319d077b..b15403956 100644 --- a/app/src/main/java/io/legado/app/base/BaseActivity.kt +++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt @@ -175,12 +175,14 @@ abstract class BaseActivity( } } if (imageBg) { - ThemeConfig.getBgImage(this)?.let { - try { + try { + ThemeConfig.getBgImage(this)?.let { window.decorView.background = it - } catch (e: OutOfMemoryError) { - toastOnUi("Image Bg Out Of Memory") } + } catch (e: OutOfMemoryError) { + toastOnUi(e.localizedMessage) + } catch (e: Exception) { + toastOnUi(e.localizedMessage) } } } From 31b7ab9c117ba50b89dfcdcfeb104b072f9a3ad7 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 29 May 2021 22:56:10 +0800 Subject: [PATCH 058/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0qq=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/ui/about/AboutFragment.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt b/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt index 2a4c3dbf7..fdf797777 100644 --- a/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt +++ b/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt @@ -20,10 +20,12 @@ class AboutFragment : PreferenceFragmentCompat() { private val licenseUrl = "https://github.com/gedoor/legado/blob/master/LICENSE" private val disclaimerUrl = "https://gedoor.github.io/MyBookshelf/disclaimer.html" private val qqGroups = linkedMapOf( + Pair("(QQ群VIP中转)1017837876", "0d9-zpmqbYfK3i_wt8uCvQoB2lmXadrg"), Pair("(QQ群VIP1)701903217", "-iolizL4cbJSutKRpeImHlXlpLDZnzeF"), Pair("(QQ群VIP2)263949160", "xwfh7_csb2Gf3Aw2qexEcEtviLfLfd4L"), Pair("(QQ群VIP3)680280282", "_N0i7yZObjKSeZQvzoe2ej7j02kLnOOK"), Pair("(QQ群VIP4)682555679", "VF2UwvUCuaqlo6pddWTe_kw__a1_Fr8O"), + Pair("(QQ群VIP5)161622578", "S81xdnhJ5EBC389LTUvoyiyM-wr71pvJ"), Pair("(QQ群1)805192012", "6GlFKjLeIk5RhQnR3PNVDaKB6j10royo"), Pair("(QQ群2)773736122", "5Bm5w6OgLupXnICbYvbgzpPUgf0UlsJF"), Pair("(QQ群3)981838750", "g_Sgmp2nQPKqcZQ5qPcKLHziwX_mpps9"), @@ -32,7 +34,8 @@ class AboutFragment : PreferenceFragmentCompat() { Pair("(QQ群6)870270970", "FeCF8iSxfQbe90HPvGsvcqs5P5oSeY5n"), Pair("(QQ群7)15987187", "S2g2TMD0LGd3sefUADd1AbyPEW2o2XfC"), Pair("(QQ群8)1079926194", "gg2qFH8q9IPFaCHV3H7CqCN-YljvazE1"), - Pair("(QQ群9)892108780", "Ci_O3aysKjEBfplOWeCud-rxl71TjU2Q") + Pair("(QQ群9)892108780", "Ci_O3aysKjEBfplOWeCud-rxl71TjU2Q"), + Pair("(QQ群10)812720266", "oW9ksY0sAWUEq0hfM5irN5aOdvKVgMEE") ) override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { From c852500a88d0daa92a14f5342fad249d3f45fd97 Mon Sep 17 00:00:00 2001 From: mezysinc <81477718+mezysinc@users.noreply.github.com> Date: Sat, 29 May 2021 19:54:37 -0300 Subject: [PATCH 059/112] minor updates in ptbr --- app/src/main/res/values-pt-rBR/strings.xml | 293 ++++++++++----------- 1 file changed, 146 insertions(+), 147 deletions(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 70cd3ed3c..af7c1a855 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -11,7 +11,7 @@ Importar dados ao Legado Criar uma subpasta Criar uma pasta de Backup com o nome Legado. - Backup cache dos livros para leitura off-line + Backup de cache dos livros para leitura off-line Exportar localmente e fazer Backup à pasta de exportação Backup para Por favor, selecione uma pasta de Backup. @@ -23,10 +23,10 @@ Lembrete Cancelar Confirmar - Vá até as Configurações - Não foi possível ir para as Configurações. + Vá até às Configurações + Não foi possível ir às Configurações. - tentar novamente + Tentar novamente Carregando Aviso Editar @@ -85,18 +85,18 @@ Carregando… Tentar novamente Serviço Web - Fonte de edição web e livro lido + Fonte de edição web e livro lidos Editar fontes de livros na web http://%1$s:%2$d Cache off-line Cache off-line - salvar o(s) capítulo(s) selecionado(s) em cache no Armazenamento - Mudar a origem + salvar o(s) capítulo(s) selecionado(s) no Armazenamento em cache + Alterar a origem \u3000\u3000 Este é um app de leitura de software livre, desenvolvido em Kotlin, você é bem-vindo a participar em projeto. Siga a conta oficial de WeChat [开源阅读]! - Legado (YueDu 3.0) link para download:\n https://play.google.com/store/apps/details?id=io.legado.play.release + Legado (YueDu 3.0) link de download:\n https://play.google.com/store/apps/details?id=io.legado.play.release Versão %s Verificação em segundo plano @@ -106,8 +106,8 @@ Download automático Baixar os últimos capítulos automaticamente ao atualizar os livros Backup e restauração - Configurações WebDav - Configurações/importação de dados legado WebDav + Configurações de WebDav + Configurações/importação WebDav de dados legado Backup Restaurar O Backup precisa de permissão de armazenamento @@ -116,12 +116,12 @@ Cancelar Confirmação de Backup Os novos arquivos de Backup substituirão os originais.\n Pasta de Backup: YueDu - Confirmação da Restauração + Confirmação de Restauração A restauração dos dados da estante irá substituir a estante atual. Backup bem sucedido O Backup falhou Restaurando - Restauração bem sucedido + Restauração bem sucedida A restauração falhou Orientação da tela Auto(sensor) @@ -152,13 +152,13 @@ Retomar Cronômetro Voz pausada - Falando(%d min left) + Falando(%d min restantes) Reproduzindo(%d min restantes) Esconder botões virtuais durante a leitura Ocultar a barra de navegação Cor da barra de navegação GitHub - Classificação + Avaliação E-mail Falha ao abrir Falha ao compartilhar @@ -181,7 +181,7 @@ Pesquisar Copiar a fonte Colar a fonte - Fonte da descrição das regras + A descrição de fonte das regras Verificar por atualizações Digitalizar código QR Digitalizar imagens locais @@ -195,7 +195,7 @@ Importar on-line Substituição Editar regra de substituição - Padrão + Modelo Substituição Capa Livro @@ -220,11 +220,11 @@ Página de boas-vindas Iníciar o download Cancelar o Download - Sem download + Nenhum download Baixado %1$d/%2$d Importar livro(s) selecionado(s) Número de tarefas simultâneas - Mudar o ícone + Alterar o ícone Excluir Começar leitura Carregando… @@ -236,35 +236,35 @@ Origem: %s Importar regras locais Importar regras on-line - Verificar intervalo de atualizações - Lista recente - Tempo de atualização + Intervalo de atualizações + Lista de recentes + Data de atualização Título do livro Ordenar manualmente Estratégia de leitura Tipografia Excluir selecionados - Você tem certeza? + Você quer excluir? Fonte padrão Descoberta Descoberta - Sem conteúdo. Vá para Gerenciador de fontes para adicioná-lo! + Sem conteúdo. Vá ao Gerenciador de fontes para adicioná-lo! Excluir tudo Histórico de busca Limpar Mostrar título do livro no texto - Sincronização das fontes do livro + Sincronização de fontes dos livros Não há último capítulo. Mostrar o tempo e a bateria Divisor da tela - Escurecer a cor do ícone da barra de status + Escurecer a cor do ícone na barra de status Conteúdo Copiar Baixar tudo Este é um texto de teste, \n\u3000\u3000 apenas para mostrar o resultado Cor e fundo (toque longo para personalizar) Barra de status imersiva - %d capítulos restantes + %d capítulo(s) restante(s) Nenhum selecionado Toque longo, para introduzir o valor da cor Carregando… @@ -301,7 +301,7 @@ Favor instalar e selecionar o TTS chinês! A inicialização do TTS falhou! Conversão simplificada - Desligada + Desligado Simplificado ao tradicional Tradicional ao simplificado Modo de virar @@ -319,12 +319,12 @@ Ao parte superior Ao fundo Expansão automática de Descoberta - A expansão padrão para a primeira Descoberta. + A expansão padrão da primeira Descoberta. Linhas atuais %s Velocidade da voz - Rolagem automática - Impedir rolagem automática - Velocidade automática de rolagem + Deslize automático + Impedir deslize automático + Velocidade automática ao deslizar Informações sobre livro Editar informações do livro Definir a Estante como página inicial @@ -344,7 +344,7 @@ Nome do livro,Autor,URL FAQ Mostrar todas as Descobertas - Mostrar a fonte da Descoberta selecionada quando fechado + Mostrar a fonte da Descoberta selecionada se encerrado Atualizar os capítulos Capítulos Txt Regex Codificação de texto @@ -352,13 +352,13 @@ Ordenar Ordenar automaticamente Ordenar manualmente - Ordenar pelo nome + Ordenar por nome Vá ao topo Vá ao fundo Ler: %s Aguardando atualização Quase lá - Terminado + Concluído Tudo Aguardando atualização dos livros Aguardando mais capítulos de livros @@ -369,14 +369,14 @@ A barra de navegação fica transparente Adicionar à Estante Continuar lendo - Pasta da capa + Pasta das capas Capa Deslizar Simulação Deslizar Nenhum Esta fonte de livro usa recursos avançados, por favor vá até Doações e toque no código de busca do envelope vermelho Alipay para receber o envelope vermelho, depois você pode usá-lo. - Atualizar o último capítulo após a mudança de origem em segundo plano + Atualizar o último capítulo após a alteração de origem em segundo plano se ativado, a atualização será iniciada 1 minuto depois o App for aberto Auto esconder a barra de ferramentas A barra de ferramentas será escondida automaticamente ao deslizar a Estante @@ -408,13 +408,13 @@ 封面规则(capaUrl) 最新章节规则(últimoCapítulo) 字数规则(contagemPalavras) - 书籍URL正则(LivroPadrãoUrl) + 书籍URL正则(LivroModeloUrl) 预处理规则(livroInfoInit) 目录URL规则(tocUrl) 允许修改书名作者(podeRenomear) 目录下一页规则(proxTocUrl) - 目录列表规则(chapterList) - 章节名称规则(capítuloLista) + 目录列表规则(capítuloLista) + 章节名称规则(capítuloNome) 章节URL规则(capítuloUrl) VIP标识(éVip) 更新时间(capítuloInfo) @@ -440,7 +440,7 @@ - Sem fonte + Nenhum fonte Falha ao obter informações sobre os livros Falha ao obter conteúdo Falha ao obter lista de capítulos @@ -449,7 +449,7 @@ Falha ao carregar a lista de capítulos Falha ao obter dados Falha ao carregar\n%s - Sem rede + Sem internet Tempo limite de conexão à rede O processamento de dados falhou @@ -468,26 +468,26 @@ Digite o endereço da fonte de livro Excluir arquivo Arquivo excluído - Você tem certeza que quer excluir este arquivo? + Tem certeza que quer excluir este arquivo? Pasta Importação inteligente Descoberta - Mudar o modo de exibição + Alterar o modo de exibição Importação de livros locais requer permissão de armazenamento Tema Noturno E-Ink Otimização para dispositivos E-ink - Este App requer permissão de armazenamento para salvar as informações do livro como Backup + Este App requer permissão de armazenamento para salvar as informações do livro no Backup Toque novamente para sair do App Importação de livros locais requer permissão de armazenamento - A conexão de rede não está disponível + A conexão de internet não está disponível Sim Não OK - Você realmente quer excluir? - Você tem certeza que quer excluir %s? + Realmente quer excluir? + Tem certeza que quer excluir %s? Você realmente quer excluir todos os livros? - Você quer ao mesmo tempo excluir os capítulos de livros baixados? + Você também quer excluir os capítulos de livros baixados? Para digitalizar o código QR é necessário ter permissão da câmera A voz está em execução, não pode virar páginas automaticamente Codificação de entrada @@ -499,26 +499,26 @@ nome e URL não podem estar vazios Galeria obter envelopes vermelhos de AliPay - Nenhuma atualização para o endereço - Ao abrir a página inicial, ela voltará automaticamente após o sucesso + Nenhuma atualização no endereço + Abrindo a página inicial, ela voltará automaticamente após o sucesso Após o login bem sucedido, por favor toque no ícone no canto superior direito para testar o acesso à página inicial Capítulo Para Usando Regex Indent Nenhum - Indent com 1 chars - Indent com 2 chars - Indent com 3 chars - Indent com 4 chars + Indent com 1 carac + Indent com 2 carac + Indent com 3 carac + Indent com 4 carac Selecionar uma pasta Selecionar um arquivo Nenhuma Descoberta, você pode adicioná-la em Fontes de Livros Restaurar padrão - A pasta de cache personalizada requer permissão de armazenamento + A pasta personalizada de cache requer permissão de armazenamento Preto Sem conteúdo - Mudança de fonte, aguarde por favor + Alteração de fonte, aguarde por favor Os capítulos sem conteúdo Espaço entre palavras @@ -535,18 +535,18 @@ Porta Web Porta atual %s Compartilhar o código QR - Compartilhar sequência + Compartilhar sequências Compartilhar Wifi Por favor, conceda permissão de armazenamento - Retrocesso rápido - Avanço rápido + Retroceder rápido + Avançar rápido Anterior Próximo Musica Áudio - Habilitar - Habilitar js - Carregar a Url Base + Ativar + Ativar js + Carregar a Url-base Todas as fontes O conteúdo de entrada não pode estar vazio Limpar o cache da Descoberta @@ -557,7 +557,7 @@ Leituras %d%% %d min - Auto-Brilho %s + Brilho autom. %s Ler páginas em voz alta Mecanismo de voz Imagens de fundo @@ -569,15 +569,15 @@ Edição de grupo Mover ao grupo Adicionar aos grupos - Remover do grupo + Remover dos grupos Novo substituto Grupo Grupo: %s Capítulos: %s - Habilitar a Descoberta - Deshabilitar a Descoberta - Habilitar selecionados - Deshabilitar selecionados + Ativar a Descoberta + Desativar a Descoberta + Ativar selecionados + Desativar selecionados Exportar selecionados Exportar Carregar os capítulos @@ -602,13 +602,13 @@ Concluído! Endereço Uri falhou Atualizar capa - Mudar a fonte + Alterar a fonte Imagem local Tipo: Segundo plano Importando Exportando - Definir botões que viram as páginas + Definir que botões viram as páginas Botão de página anterior Botão de página seguinte Primeiro adicionar este livro à Estante @@ -619,7 +619,7 @@ Demasiadas palavras para criar um código QR Compartilhar fontes de assinatura Compartilhar fontes de livros - Mudança automática ao modo escuro + Alteração automática ao modo escuro Seguir o modo escuro do sistema Voltar Tom de voz online @@ -633,7 +633,7 @@ Seletor de arquivos do App Android 10+ não é capaz de ler e salvar arquivos devido a restrições de permissão Toque longo para exibir o Legado·Pesquisa no menu de operação - Visualização da operação de texto Pesquisa + Visualização da operação de texto na Pesquisa Log Log Conversão simplificada @@ -645,7 +645,7 @@ Conteúdo Rodapé Selecionar o fim - Selecione o início + Selecionar o início Layout compartilhado Navegador Importar regras padrão @@ -668,18 +668,18 @@ Nenhum pasta padrão Configurações de grupo Ver os capítulos - Sombra da barra de navegação + Sombra na barra de navegação Tamanho da sombra atual(elevação): %s Padrão Menu principal Toque para conceder permissão - Legado precisa de permissão de armazenamento, por favor toque no botão "Conceder Permissão" abaixo, ou vá para as "Configurações"-"Permissões do Apps"-para abrir a permissão necessária. Se a permissão ainda não funcionar, por favor, toque no "Selecionar Pasta" no canto superior direito para usar o seletor de pastas do sistema. + Legado precisa de permissão de armazenamento, por favor toque no botão "Conceder Permissão" abaixo, ou vá às "Configurações"-"Permissões do Apps"-para abrir a permissão necessária. Se a permissão não funcionar, por favor, toque no "Selecionar Pasta" no canto superior direito para usar o seletor de pastas do sistema. O texto selecionado não pode ser falado como texto completo Ampliar para recortar Atualização de capítulos Os botões de fones de ouvido estão sempre disponíveis Os botões de fones de ouvido estão disponíveis mesmo ao sair do App. - colaboradores + Colaboradores Contato Licença Outros @@ -688,48 +688,48 @@ WeChat O suporte ao meu trabalho é bem-vindo Conta oficial[开源阅读] - Mudança de fonte + Alteração de fonte Toque para participar Médio Informação - Mudar o layout + Alterar o layout Alteração de tamanho da fonte de texto Suporte aos gestos em tela completa Primário - Acento + Destaque Cor de fundo Cor de NavBar Dia - Dia,Primário - Dia,Acento + Dia,Principal + Dia,Destaque Dia,Cor de fundo Dia,Cor de NavBar Noite - Noite,Primário - Noite,Acento + Noite,Principal + Noite,Destaque Noite,Cor de fundo Noite,Cor de NavBar - Mudar a fonte automaticamente + Alterar a fonte automaticamente Texto justificado - Texto alinhar fundo + Texto alinhado com parte inferior Velocidade de rolagem automática Ordenar por URL Faça backup local e de WebDav simultaneamente - Primeiro restaurar de WebDAV, restaurar o local backup via clique longo + Primeiro restaurar de WebDAV, ao clicar longo restaurar do local backup Selecione uma pasta legado de backup - Habilitado - Deshabilitado + Ativado + Desativado Iniciando o download Este livro já está na lista de download - clique para abrir + Clique para abrir Siga [开源阅读] para ajudar, clicando nos anúncios Código de gorjeta no WeChat AliPay - AliPay código de busca do envelope vermelho + Envelope vermelho de AliPay, código de busca 537954522 Clique para copiar - AliPay código QR envelope vermelho + Envelope vermelho de AliPay código QR AliPay código QR Coleção QQ Código QR gedoor,Invinciblelee etc. Para mais detalhes vá ao Github @@ -740,36 +740,36 @@ Configuração da interface de leitura Nome de grupo Seção de observações - Habilitar regra de substituição por padrão + Ativar regra de substituição por padrão Para livros recém adicionados - Selecione Restaurar arquivo - O fundo do dia não pode ser muito escuro! - A parte inferior do dia não pode ser muito escuro! + Selecione restaurar arquivo + O fundo diurno não pode ser muito escuro! + A parte inferior diurno não pode ser muito escuro! O fundo noturno não pode ser muito brilhante! A parte inferior noturno não pode ser muito brilhante! - Tem que haver diferença entre sotaque e cor de fundo - Tem que haver diferença entre sotaque e cor texto + Tem que haver diferença entre destaque e cor de fundo + Tem que haver diferença entre destaque e cor de texto Formato incorreto Erro Mostrar o widget de brilho Idioma - Importar fonte RSS + Importar fonte de RSS Sua doação torna este App melhor Conta oficial de Wechat [开源阅读软件] - Registro de leitura - Leia o resumo dos registros + Histórico de leitura + Leia o resumo do histórico TTS local Contagem de tópicos Tempo total da leitura Desmarcar todos - Importar str - Exportar str + Importar + Exportar Salvar configuração do tema - Salvar a configuração do tema do dia - Salvar a configuração do tema da noite + Salvar a configuração do tema diurno + Salvar a configuração do tema noturno Lista de temas Salvar, Importar, Compartilhar tema - Mudar o tema padrão + Alterar o tema padrão Ordenar por tempo de atualização Pesquisar conteúdo Siga [开源阅读] no WeChat para mais fontes de RSS! @@ -777,68 +777,67 @@ 将焦点放到输入框按下物理按键会自动录入键值,多个按键会自动用英文逗号隔开. Nome de tema Excluir automaticamente dados vencidos da busca - 超过一天的搜索数据 - 重新分段 - 样式名称: - 点击右上角文件夹图标,选择文件夹 - 智能扫描 - 拷贝书籍URL - 拷贝目录URL + Histórico de busca mais de um dia + Re-segmentar + Estilo do nome: + Clique no ícone da pasta no canto superior direito e selecione a pasta + Busca inteligente Sem livros Manter o nome original - Clique em Configurações Regionais + O controle de toque da tela Fechar Próxima página Página anterior Nenhuma Ação - Título do texto principal + Título Mostrar/ocultar rodapé cabeçalho Assinatura de regras 添加大佬们提供的规则导入地址\n添加后点击可导入规则 - 拉取云端进度 - 当前进度超过云端进度,是否同步? + Obter o progresso da nuvem + O progresso atual excede o progresso da nuvem. Você quer sincronizar? Progresso de leitura sincronizada Sincronizar o progresso da leitura ao entrar e sair da tela de leitura - Falha ao criar um marcador de página + Falha ao marcar como favorito URL única - Lista de livros para exportação - 导入书单 - 预下载 - 预先下载%s章正文 - Habilitar a seleção + Exportar a lista de livros + Importar a lista de livros + Baixar antecipadamente + Baixar %s capítulos antecipadamente + Está ativado Imagem de fundo - Pasta de exportação - 导出编码 + Copiar a URL do livro + Copiar a URL dos capítulos + Exportar para uma pasta + Codificação de texto exportada Exportar para WebDav - Reverse content - Debug - Crash log + Conteúdo reverso + Depurar + Log de falhas Utilização de filiais chinesas personalizadas Estilo de imagem Sistema TTS Formato de exportação - Revisado por + Verificar por autor + Esta URL foi subscrita + Alta taxa de atualização da tela + Use a maior taxa de atualização da tela + Exportar tudo + Concluído + Mostrar não lido + Sempre mostrar capa padrão + Sempre mostrar capa padrão, não mostrar capa da rede Procurar código fonte Código fonte do livro - Código fonte do diretório - Código fonte completo + Código fonte dos capítulos + Código fonte do conteúdo Listar código fonte - Esta Url é subscrita - 高刷 - Use a maior taxa de atualização da tela - exportar tudo - concluído - Mostrar não lido - sempre usar capa padrão - sempre usar capa padrão,não usar rede - Font size - Margin top - Marigin bottom - Show - Hide - Hide when status bar show - 反转目录 - 显示发现 - + Tamanho de letra + Borda superior + Borda inferior + Mostrar + Ocultar + Ocultar quando a barra de status é mostrada + TOC reverso + Mostrar From 10861ffe9f5baac4e1b3fa5969ee76c11a58ce10 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 31 May 2021 17:11:17 +0800 Subject: [PATCH 060/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/legado/app/help/MediaHelp.kt | 6 +- .../app/service/BaseReadAloudService.kt | 7 +- .../ui/book/arrange/ArrangeBookActivity.kt | 10 +- .../legado/app/ui/book/cache/CacheActivity.kt | 7 +- .../ui/main/bookshelf/BookshelfFragment.kt | 2 - .../ui/main/bookshelf1/BaseBooksAdapter.kt | 83 +++++ .../ui/main/bookshelf1/BooksAdapterGrid.kt | 104 ++++++ .../ui/main/bookshelf1/BooksAdapterList.kt | 119 +++++++ .../ui/main/bookshelf1/BookshelfFragment.kt | 329 ++++++++++++++++++ .../widget/recycler/RecyclerViewAtPager2.kt | 6 +- .../main/res/layout/fragment_bookshelf1.xml | 48 +++ 11 files changed, 712 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf1/BaseBooksAdapter.kt create mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterGrid.kt create mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterList.kt create mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment.kt create mode 100644 app/src/main/res/layout/fragment_bookshelf1.xml diff --git a/app/src/main/java/io/legado/app/help/MediaHelp.kt b/app/src/main/java/io/legado/app/help/MediaHelp.kt index 31272d1d6..9153b9fd2 100644 --- a/app/src/main/java/io/legado/app/help/MediaHelp.kt +++ b/app/src/main/java/io/legado/app/help/MediaHelp.kt @@ -51,9 +51,9 @@ object MediaHelp { audioManager: AudioManager, focusRequest: AudioFocusRequestCompat? ): Boolean { - val request: Int = - focusRequest?.let { AudioManagerCompat.requestAudioFocus(audioManager, focusRequest) } - ?: AudioManager.AUDIOFOCUS_REQUEST_GRANTED + val request = focusRequest?.let { + AudioManagerCompat.requestAudioFocus(audioManager, focusRequest) + } ?: AudioManager.AUDIOFOCUS_REQUEST_GRANTED return request == AudioManager.AUDIOFOCUS_REQUEST_GRANTED } diff --git a/app/src/main/java/io/legado/app/service/BaseReadAloudService.kt b/app/src/main/java/io/legado/app/service/BaseReadAloudService.kt index c62bca0b6..a6c35c7ae 100644 --- a/app/src/main/java/io/legado/app/service/BaseReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/BaseReadAloudService.kt @@ -26,6 +26,7 @@ import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.postEvent +import io.legado.app.utils.toastOnUi import splitties.init.appCtx abstract class BaseReadAloudService : BaseService(), @@ -202,7 +203,11 @@ abstract class BaseReadAloudService : BaseService(), * @return 音频焦点 */ fun requestFocus(): Boolean { - return MediaHelp.requestFocus(audioManager, mFocusRequest) + val requestFocus = MediaHelp.requestFocus(audioManager, mFocusRequest) + if (!requestFocus) { + toastOnUi("未获取到音频焦点") + } + return requestFocus } /** diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt index d75e13828..0f995ea8d 100644 --- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt @@ -26,6 +26,9 @@ import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.cnCompare import io.legado.app.utils.getPrefInt +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class ArrangeBookActivity : VMBaseActivity(), @@ -50,7 +53,12 @@ class ArrangeBookActivity : VMBaseActivity() override fun onActivityCreated(savedInstanceState: Bundle?) { groupId = intent.getLongExtra("groupId", -1) - binding.titleBar.subtitle = intent.getStringExtra("groupName") ?: getString(R.string.all) + launch { + binding.titleBar.subtitle = withContext(IO) { + appDb.bookGroupDao.getByID(groupId)?.groupName + ?: getString(R.string.no_group) + } + } initRecyclerView() initGroupData() initBookData() diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt index 4a3eab128..c291a4f99 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt @@ -87,11 +87,9 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b R.id.menu_add_url -> addBookByUrl() R.id.menu_arrange_bookshelf -> startActivity { putExtra("groupId", selectedGroup.groupId) - putExtra("groupName", selectedGroup.groupName) } R.id.menu_download -> startActivity { putExtra("groupId", selectedGroup.groupId) - putExtra("groupName", selectedGroup.groupName) } R.id.menu_export_bookshelf -> { val fragment = fragmentMap[selectedGroup.groupId] diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BaseBooksAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BaseBooksAdapter.kt new file mode 100644 index 000000000..e33243e83 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BaseBooksAdapter.kt @@ -0,0 +1,83 @@ +package io.legado.app.ui.main.bookshelf1 + +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(val context: Context) : + RecyclerView.Adapter() { + + val diffItemCallback: DiffUtil.ItemCallback + get() = object : DiffUtil.ItemCallback() { + + 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) { +// getItem(i)?.let { +// if (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 + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterGrid.kt new file mode 100644 index 000000000..0bdb7ca5b --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterGrid.kt @@ -0,0 +1,104 @@ +package io.legado.app.ui.main.bookshelf1 + +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.help.AppConfig +import io.legado.app.utils.invisible +import splitties.views.onLongClick + +class BooksAdapterGrid(context: Context, private val callBack: CallBack) : + BaseBooksAdapter(context) { + + override fun getItemCount(): Int { + return callBack.getItemCount() + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ItemViewHolder { + return ItemViewHolder( + ItemBookshelfGridBinding.inflate(LayoutInflater.from(context), parent, false) + ) + } + + override fun onBindViewHolder( + holder: ItemViewHolder, + position: Int, + payloads: MutableList + ) { + holder.binding.run { + val bundle = payloads.getOrNull(0) as? Bundle + if (bundle == null) { + super.onBindViewHolder(holder, position, payloads) + } else { + when (val item = callBack.getItem(position)) { + 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) + } + } + } + is BookGroup -> { + tvName.text = item.groupName + } + } + + } + } + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.binding.run { + when (val item = callBack.getItem(position)) { + is Book -> { + tvName.text = item.name + ivCover.load(item.getDisplayCover(), item.name, item.author) + upRefresh(this, item) + } + is BookGroup -> { + tvName.text = item.groupName + } + } + 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 ItemViewHolder(val binding: ItemBookshelfGridBinding) : + RecyclerView.ViewHolder(binding.root) + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterList.kt new file mode 100644 index 000000000..0748db7f3 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterList.kt @@ -0,0 +1,119 @@ +package io.legado.app.ui.main.bookshelf1 + +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.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, private val callBack: CallBack) : + BaseBooksAdapter(context) { + + override fun getItemCount(): Int { + return callBack.getItemCount() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { + return ItemViewHolder( + ItemBookshelfListBinding.inflate(LayoutInflater.from(context), parent, false) + ) + } + + override fun onBindViewHolder( + holder: ItemViewHolder, + position: Int, + payloads: MutableList + ) { + val bundle = payloads.getOrNull(0) as? Bundle + if (bundle == null) { + super.onBindViewHolder(holder, position, payloads) + } else { + holder.binding.run { + when (val item = callBack.getItem(position)) { + 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) + } + } + } + is BookGroup -> { + tvName.text = item.groupName + } + } + } + } + } + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + holder.binding.run { + when (val item = callBack.getItem(position)) { + 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) + } + 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 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 ItemViewHolder(val binding: ItemBookshelfListBinding) : + RecyclerView.ViewHolder(binding.root) + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment.kt new file mode 100644 index 000000000..ede5e76d4 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment.kt @@ -0,0 +1,329 @@ +package io.legado.app.ui.main.bookshelf1 + +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.core.view.isGone +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +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.base.VMBaseFragment +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.DialogBookshelfConfigBinding +import io.legado.app.databinding.DialogEditTextBinding +import io.legado.app.databinding.FragmentBookshelf1Binding +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.audio.AudioPlayActivity +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.info.BookInfoActivity +import io.legado.app.ui.book.local.ImportBookActivity +import io.legado.app.ui.book.read.ReadBookActivity +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.BookshelfViewModel +import io.legado.app.utils.* +import io.legado.app.utils.viewbindingdelegate.viewBinding +import kotlin.math.max + +/** + * 书架界面 + */ +class BookshelfFragment : VMBaseFragment(R.layout.fragment_bookshelf1), + SearchView.OnQueryTextListener, + BaseBooksAdapter.CallBack { + + private val binding by viewBinding(FragmentBookshelf1Binding::bind) + override val viewModel: BookshelfViewModel by viewModels() + private val activityViewModel: MainViewModel by activityViewModels() + private lateinit var booksAdapter: BaseBooksAdapter<*> + private var groupId = AppConst.bookGroupNoneId + private var bookGroupLiveData: LiveData>? = null + private var bookshelfLiveData: LiveData>? = null + private var bookGroups: List = emptyList() + private var books: List = emptyList() + + private val importBookshelf = registerForActivityResult(FilePicker()) { + it?.readText(requireContext())?.let { text -> + viewModel.importBookshelf(text, groupId) + } + } + + override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { + setSupportToolbar(binding.titleBar.toolbar) + initView() + initGroupData() + initBooksData() + } + + 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() + 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() + R.id.menu_add_url -> addBookByUrl() + R.id.menu_arrange_bookshelf -> startActivity { + putExtra("groupId", groupId) + } + R.id.menu_download -> startActivity { + putExtra("groupId", groupId) + } + R.id.menu_export_bookshelf -> viewModel.exportBookshelf(books) { + activity?.share(it) + } + R.id.menu_import_bookshelf -> importBookshelfAlert() + } + } + + private fun initView() { + 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.size != bookGroups.size) { + bookGroups = it + booksAdapter.notifyDataSetChanged() + } else { + + } + } + } + } + + 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 { + putExtra("key", query) + } + return false + } + + override fun onQueryTextChange(newText: String?): Boolean { + return false + } + + @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() + } + + fun gotoTop() { + if (AppConfig.isEInkMode) { + binding.rvBookshelf.scrollToPosition(0) + } else { + binding.rvBookshelf.smoothScrollToPosition(0) + } + } + + 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, groupId) + } + } + noButton() + neutralButton(R.string.select_file) { + importBookshelf.launch( + FilePickerParam( + mode = FilePicker.FILE, + allowExtensions = arrayOf("txt", "json") + ) + ) + } + }.show() + } + + override fun onItemClick(position: Int) { + if (position < bookGroups.size) { + val bookGroup = bookGroups[position] + + } else { + val book = books[position - bookGroups.size] + when (book.type) { + BookType.audio -> + startActivity { + putExtra("bookUrl", book.bookUrl) + } + else -> startActivity { + putExtra("bookUrl", book.bookUrl) + } + } + } + } + + override fun onItemLongClick(position: Int) { + if (position < bookGroups.size) { + + } else { + val book = books[position - bookGroups.size] + startActivity { + 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(EventBus.UP_BOOK) { + booksAdapter.notification(it) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/recycler/RecyclerViewAtPager2.kt b/app/src/main/java/io/legado/app/ui/widget/recycler/RecyclerViewAtPager2.kt index 7ab5ed8e9..1dc4d7b5f 100644 --- a/app/src/main/java/io/legado/app/ui/widget/recycler/RecyclerViewAtPager2.kt +++ b/app/src/main/java/io/legado/app/ui/widget/recycler/RecyclerViewAtPager2.kt @@ -7,6 +7,7 @@ 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( @@ -17,6 +18,7 @@ class RecyclerViewAtPager2 : RecyclerView { private var startX = 0 private var startY = 0 + override fun dispatchTouchEvent(ev: MotionEvent): Boolean { when (ev.action) { MotionEvent.ACTION_DOWN -> { @@ -37,8 +39,10 @@ class RecyclerViewAtPager2 : RecyclerView { parent.requestDisallowInterceptTouchEvent(true) } } - MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent(false) + MotionEvent.ACTION_UP, + MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent(false) } return super.dispatchTouchEvent(ev) } + } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_bookshelf1.xml b/app/src/main/res/layout/fragment_bookshelf1.xml new file mode 100644 index 000000000..f889fca92 --- /dev/null +++ b/app/src/main/res/layout/fragment_bookshelf1.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + \ No newline at end of file From 13b77657c3d17500e424d7dabf149be192628c6b Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 5 Jun 2021 11:10:58 +0800 Subject: [PATCH 061/112] =?UTF-8?q?viewPager2=203=E5=B1=82=E5=B5=8C?= =?UTF-8?q?=E5=A5=97=E6=9C=89=E9=97=AE=E9=A2=98,=E4=B9=A6=E6=9E=B6?= =?UTF-8?q?=E6=8D=A2=E5=9B=9EviewPager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/main/MainActivity.kt | 60 ++++++++------ .../ui/main/bookshelf/BookshelfFragment.kt | 45 +++++----- .../legado/app/ui/main/bookshelf/RootView.kt | 82 ------------------- ...shelfFragment.kt => BookshelfFragment1.kt} | 2 +- app/src/main/res/layout/activity_main.xml | 2 +- .../main/res/layout/fragment_bookshelf.xml | 6 +- 6 files changed, 57 insertions(+), 140 deletions(-) delete mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf/RootView.kt rename app/src/main/java/io/legado/app/ui/main/bookshelf1/{BookshelfFragment.kt => BookshelfFragment1.kt} (99%) diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 9741e726d..cf4d16957 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -1,12 +1,16 @@ +@file:Suppress("DEPRECATION") + package io.legado.app.ui.main import android.os.Bundle import android.view.KeyEvent import android.view.MenuItem +import android.view.ViewGroup import androidx.activity.viewModels import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter -import androidx.viewpager2.widget.ViewPager2 +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentStatePagerAdapter +import androidx.viewpager.widget.ViewPager import com.google.android.material.bottomnavigation.BottomNavigationView import io.legado.app.BuildConfig import io.legado.app.R @@ -40,7 +44,7 @@ class MainActivity : VMBaseActivity(), private var bookshelfReselected: Long = 0 private var exploreReselected: Long = 0 private var pagePosition = 0 - private val fragmentMap = hashMapOf() + private val fragmentMap = hashMapOf() private var bottomMenuCount = 2 private val realPositions = arrayOf(0, 1, 2, 3) @@ -54,8 +58,8 @@ class MainActivity : VMBaseActivity(), ATH.applyEdgeEffectColor(viewPagerMain) ATH.applyBottomNavigationColor(bottomNavigationView) viewPagerMain.offscreenPageLimit = 3 - viewPagerMain.adapter = TabFragmentPageAdapter() - viewPagerMain.registerOnPageChangeCallback(PageChangeCallback()) + viewPagerMain.adapter = TabFragmentPageAdapter(supportFragmentManager) + viewPagerMain.addOnPageChangeListener(PageChangeCallback()) bottomNavigationView.elevation = if (AppConfig.elevation < 0) elevation else AppConfig.elevation.toFloat() bottomNavigationView.setOnNavigationItemSelectedListener(this@MainActivity) @@ -110,10 +114,6 @@ class MainActivity : VMBaseActivity(), } } - fun getViewPager(): ViewPager2 { - return binding.viewPagerMain - } - private fun upVersion() { if (LocalConfig.versionCode != appInfo.versionCode) { LocalConfig.versionCode = appInfo.versionCode @@ -210,38 +210,44 @@ class MainActivity : VMBaseActivity(), } } - private inner class PageChangeCallback : ViewPager2.OnPageChangeCallback() { + private inner class PageChangeCallback : ViewPager.SimpleOnPageChangeListener() { + override fun onPageSelected(position: Int) { pagePosition = position binding.bottomNavigationView.menu .getItem(realPositions[position]).isChecked = true } + } - private inner class TabFragmentPageAdapter : - FragmentStateAdapter(this) { + @Suppress("DEPRECATION") + private inner class TabFragmentPageAdapter(fm: FragmentManager) : + FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - override fun containsItem(itemId: Long): Boolean { - return fragmentMap.containsKey(itemId) + private fun getId(position: Int): Int { + return realPositions[position] } - override fun getItemCount(): Int { - return bottomMenuCount + override fun getItemPosition(`object`: Any): Int { + return POSITION_NONE } - override fun getItemId(position: Int): Long { - return realPositions[position].toLong() - } - - override fun createFragment(position: Int): Fragment { - val id = getItemId(position) - val fragment = when (id) { - 0L -> BookshelfFragment() - 1L -> ExploreFragment() - 2L -> RssFragment() + override fun getItem(position: Int): Fragment { + return when (getId(position)) { + 0 -> BookshelfFragment() + 1 -> ExploreFragment() + 2 -> RssFragment() else -> MyFragment() } - fragmentMap[id] = fragment + } + + override fun getCount(): Int { + return bottomMenuCount + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val fragment = super.instantiateItem(container, position) as Fragment + fragmentMap[getId(position)] = fragment return fragment } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt index c291a4f99..87ee959f9 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt @@ -5,14 +5,11 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.View +import android.view.ViewGroup import androidx.appcompat.widget.SearchView -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels +import androidx.fragment.app.* 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 @@ -22,7 +19,6 @@ 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 @@ -48,7 +44,7 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b 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 adapter: FragmentStatePagerAdapter private lateinit var tabLayout: TabLayout private var bookGroupLiveData: LiveData>? = null private val bookGroups = mutableListOf() @@ -109,12 +105,10 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b tabLayout.isTabIndicatorFullWidth = false tabLayout.tabMode = TabLayout.MODE_SCROLLABLE tabLayout.setSelectedTabIndicatorColor(requireContext().accentColor) + tabLayout.setupWithViewPager(binding.viewPagerBookshelf) binding.viewPagerBookshelf.offscreenPageLimit = 1 - adapter = TabFragmentPageAdapter() + adapter = TabFragmentPageAdapter(childFragmentManager) binding.viewPagerBookshelf.adapter = adapter - TabLayoutMediator(tabLayout, binding.viewPagerBookshelf) { tab, i -> - tab.text = bookGroups[i].groupName - }.attach() } private fun initBookGroupData() { @@ -169,7 +163,6 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b .apply { rgLayout.checkByIndex(bookshelfLayout) rgSort.checkByIndex(bookshelfSort) - swShowUnread.isChecked = AppConfig.showUnread } customView { alertBinding.root } okButton { @@ -183,10 +176,6 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b putPrefInt(PreferKey.bookshelfSort, rgSort.getCheckedIndex()) changed = true } - if (AppConfig.showUnread != swShowUnread.isChecked) { - AppConfig.showUnread = swShowUnread.isChecked - changed = true - } if (changed) { activity?.recreate() } @@ -249,25 +238,29 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b }.show() } - private inner class TabFragmentPageAdapter : - FragmentStateAdapter(this) { + private inner class TabFragmentPageAdapter(fm: FragmentManager) : + FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - override fun getItemId(position: Int): Long { - val group = bookGroups[position] - return group.groupId + override fun getPageTitle(position: Int): CharSequence { + return bookGroups[position].groupName } - override fun containsItem(itemId: Long): Boolean { - return fragmentMap.containsKey(itemId) + 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 getItemCount(): Int { + override fun getCount(): Int { return bookGroups.size } - override fun createFragment(position: Int): Fragment { + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val fragment = super.instantiateItem(container, position) as BooksFragment val group = bookGroups[position] - val fragment = BooksFragment.newInstance(position, group.groupId) fragmentMap[group.groupId] = fragment return fragment } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/RootView.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/RootView.kt deleted file mode 100644 index 4162e6e6e..000000000 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/RootView.kt +++ /dev/null @@ -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) - } - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment1.kt similarity index 99% rename from app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment1.kt index ede5e76d4..f37c52e2c 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment1.kt @@ -48,7 +48,7 @@ import kotlin.math.max /** * 书架界面 */ -class BookshelfFragment : VMBaseFragment(R.layout.fragment_bookshelf1), +class BookshelfFragment1 : VMBaseFragment(R.layout.fragment_bookshelf1), SearchView.OnQueryTextListener, BaseBooksAdapter.CallBack { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 80c952ab8..a387ecb36 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - \ No newline at end of file + \ No newline at end of file From 95ecbebac2f98d810e32adbe31db32dbb5a66129 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 5 Jun 2021 20:50:33 +0800 Subject: [PATCH 062/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E5=88=86=E7=BB=84=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/constant/PreferKey.kt | 1 + .../main/java/io/legado/app/help/AppConfig.kt | 6 + .../io/legado/app/ui/main/MainActivity.kt | 19 +- .../main/bookshelf/BaseBookshelfFragment.kt | 139 +++++++++ .../ui/main/bookshelf/BookshelfFragment.kt | 269 ------------------ .../bookshelf/style1/BookshelfFragment1.kt | 166 +++++++++++ .../{ => style1}/books/BaseBooksAdapter.kt | 2 +- .../{ => style1}/books/BooksAdapterGrid.kt | 2 +- .../{ => style1}/books/BooksAdapterList.kt | 2 +- .../{ => style1}/books/BooksFragment.kt | 2 +- .../style2}/BaseBooksAdapter.kt | 2 +- .../style2}/BooksAdapterGrid.kt | 2 +- .../style2}/BooksAdapterList.kt | 2 +- .../style2/BookshelfFragment2.kt} | 160 +++-------- .../res/layout/dialog_bookshelf_config.xml | 21 ++ app/src/main/res/values-ja-rJP/strings.xml | 2 + app/src/main/res/values-pt-rBR/strings.xml | 2 + app/src/main/res/values-zh-rHK/arrays.xml | 5 + app/src/main/res/values-zh-rHK/strings.xml | 2 + app/src/main/res/values-zh-rTW/arrays.xml | 5 + app/src/main/res/values-zh-rTW/strings.xml | 2 + app/src/main/res/values-zh/arrays.xml | 5 + app/src/main/res/values-zh/strings.xml | 2 + app/src/main/res/values/arrays.xml | 5 + app/src/main/res/values/strings.xml | 3 + 25 files changed, 421 insertions(+), 407 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt delete mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt create mode 100644 app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt rename app/src/main/java/io/legado/app/ui/main/bookshelf/{ => style1}/books/BaseBooksAdapter.kt (98%) rename app/src/main/java/io/legado/app/ui/main/bookshelf/{ => style1}/books/BooksAdapterGrid.kt (97%) rename app/src/main/java/io/legado/app/ui/main/bookshelf/{ => style1}/books/BooksAdapterList.kt (98%) rename app/src/main/java/io/legado/app/ui/main/bookshelf/{ => style1}/books/BooksFragment.kt (99%) rename app/src/main/java/io/legado/app/ui/main/{bookshelf1 => bookshelf/style2}/BaseBooksAdapter.kt (98%) rename app/src/main/java/io/legado/app/ui/main/{bookshelf1 => bookshelf/style2}/BooksAdapterGrid.kt (98%) rename app/src/main/java/io/legado/app/ui/main/{bookshelf1 => bookshelf/style2}/BooksAdapterList.kt (98%) rename app/src/main/java/io/legado/app/ui/main/{bookshelf1/BookshelfFragment1.kt => bookshelf/style2/BookshelfFragment2.kt} (57%) diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index a8d2d0ee5..67732823a 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -5,6 +5,7 @@ object PreferKey { const val themeMode = "themeMode" const val userAgent = "userAgent" const val showUnread = "showUnread" + const val bookGroupStyle = "bookGroupStyle" const val useDefaultCover = "useDefaultCover" const val hideStatusBar = "hideStatusBar" const val clickActionTL = "clickActionTopLeft" diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index 798d08c5b..218efbf7a 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -90,6 +90,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { val screenOrientation: String? get() = appCtx.getPrefString(PreferKey.screenOrientation) + var bookGroupStyle: Int + get() = appCtx.getPrefInt(PreferKey.bookGroupStyle, 0) + set(value) { + appCtx.putPrefInt(PreferKey.bookGroupStyle, value) + } + var backupPath: String? get() = appCtx.getPrefString(PreferKey.backupPath) set(value) { diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index cf4d16957..6b4bf86a6 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -27,7 +27,9 @@ import io.legado.app.help.storage.Backup import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.elevation import io.legado.app.service.BaseReadAloudService -import io.legado.app.ui.main.bookshelf.BookshelfFragment +import io.legado.app.ui.main.bookshelf.BaseBookshelfFragment +import io.legado.app.ui.main.bookshelf.style1.BookshelfFragment1 +import io.legado.app.ui.main.bookshelf.style2.BookshelfFragment2 import io.legado.app.ui.main.explore.ExploreFragment import io.legado.app.ui.main.my.MyFragment import io.legado.app.ui.main.rss.RssFragment @@ -101,7 +103,7 @@ class MainActivity : VMBaseActivity(), if (System.currentTimeMillis() - bookshelfReselected > 300) { bookshelfReselected = System.currentTimeMillis() } else { - (fragmentMap[0] as? BookshelfFragment)?.gotoTop() + (fragmentMap[getFragmentId(0)] as? BaseBookshelfFragment)?.gotoTop() } } R.id.menu_discovery -> { @@ -210,6 +212,14 @@ class MainActivity : VMBaseActivity(), } } + private fun getFragmentId(position: Int): Int { + val p = realPositions[position] + if (p == 0) { + return if (AppConfig.bookGroupStyle == 1) 11 else 0 + } + return p + } + private inner class PageChangeCallback : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { @@ -225,7 +235,7 @@ class MainActivity : VMBaseActivity(), FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { private fun getId(position: Int): Int { - return realPositions[position] + return getFragmentId(position) } override fun getItemPosition(`object`: Any): Int { @@ -234,7 +244,8 @@ class MainActivity : VMBaseActivity(), override fun getItem(position: Int): Fragment { return when (getId(position)) { - 0 -> BookshelfFragment() + 0 -> BookshelfFragment1() + 11 -> BookshelfFragment2() 1 -> ExploreFragment() 2 -> RssFragment() else -> MyFragment() diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt new file mode 100644 index 000000000..150eaeb8e --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt @@ -0,0 +1,139 @@ +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.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(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 + + abstract fun gotoTop() + + override fun onCompatOptionsItemSelected(item: MenuItem) { + super.onCompatOptionsItemSelected(item) + when (item.itemId) { + R.id.menu_search -> startActivity() + 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() + R.id.menu_add_url -> addBookByUrl() + R.id.menu_arrange_bookshelf -> startActivity { + putExtra("groupId", groupId) + } + R.id.menu_download -> startActivity { + 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 + changed = true + } + if (bookshelfLayout != rgLayout.getCheckedIndex()) { + putPrefInt(PreferKey.bookshelfLayout, rgLayout.getCheckedIndex()) + changed = true + } + if (bookshelfSort != rgSort.getCheckedIndex()) { + putPrefInt(PreferKey.bookshelfSort, rgSort.getCheckedIndex()) + changed = true + } + if (changed) { + activity?.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() + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt deleted file mode 100644 index 87ee959f9..000000000 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt +++ /dev/null @@ -1,269 +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 android.view.ViewGroup -import androidx.appcompat.widget.SearchView -import androidx.fragment.app.* -import androidx.lifecycle.LiveData -import com.google.android.material.tabs.TabLayout -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.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(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: FragmentStatePagerAdapter - private lateinit var tabLayout: TabLayout - private var bookGroupLiveData: LiveData>? = null - private val bookGroups = mutableListOf() - private val fragmentMap = hashMapOf() - 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() - 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() - R.id.menu_add_url -> addBookByUrl() - R.id.menu_arrange_bookshelf -> startActivity { - putExtra("groupId", selectedGroup.groupId) - } - R.id.menu_download -> startActivity { - putExtra("groupId", selectedGroup.groupId) - } - 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) - 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 { - putExtra("key", query) - } - return false - } - - override fun onQueryTextChange(newText: String?): Boolean { - return false - } - - @Synchronized - private fun upGroup(data: List) { - 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) - } - 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 (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(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 - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt new file mode 100644 index 000000000..cea4a6b98 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/BookshelfFragment1.kt @@ -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>? = null + private val bookGroups = mutableListOf() + private val fragmentMap = hashMapOf() + + override val groupId: Long get() = selectedGroup.groupId + + override val books: List + 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 { + putExtra("key", query) + } + return false + } + + override fun onQueryTextChange(newText: String?): Boolean { + return false + } + + @Synchronized + private fun upGroup(data: List) { + 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 + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BaseBooksAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BaseBooksAdapter.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/main/bookshelf/books/BaseBooksAdapter.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BaseBooksAdapter.kt index 538887c1c..b2dbb3863 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BaseBooksAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BaseBooksAdapter.kt @@ -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 androidx.core.os.bundleOf diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksAdapterGrid.kt similarity index 97% rename from app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksAdapterGrid.kt index 9eabfbd45..a79155319 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksAdapterGrid.kt @@ -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.os.Bundle diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksAdapterList.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksAdapterList.kt index b93d3430a..4cfcb5693 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksAdapterList.kt @@ -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.os.Bundle diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt similarity index 99% rename from app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt index bc38e5767..9deb8cc40 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.main.bookshelf.books +package io.legado.app.ui.main.bookshelf.style1.books import android.os.Bundle import android.view.View diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BaseBooksAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/main/bookshelf1/BaseBooksAdapter.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt index e33243e83..1ad12187f 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BaseBooksAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.main.bookshelf1 +package io.legado.app.ui.main.bookshelf.style2 import android.content.Context import androidx.core.os.bundleOf diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterGrid.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt index 0bdb7ca5b..5e89d351b 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterGrid.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.main.bookshelf1 +package io.legado.app.ui.main.bookshelf.style2 import android.content.Context import android.os.Bundle diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterList.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt index 0748db7f3..3130076dd 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BooksAdapterList.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.main.bookshelf1 +package io.legado.app.ui.main.bookshelf.style2 import android.content.Context import android.os.Bundle diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment1.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt similarity index 57% rename from app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment1.kt rename to app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt index f37c52e2c..6b81eafb7 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf1/BookshelfFragment1.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt @@ -1,20 +1,15 @@ -package io.legado.app.ui.main.bookshelf1 +package io.legado.app.ui.main.bookshelf.style2 -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.core.view.isGone -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels 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.base.VMBaseFragment import io.legado.app.constant.AppConst import io.legado.app.constant.BookType import io.legado.app.constant.EventBus @@ -22,87 +17,72 @@ 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.DialogBookshelfConfigBinding -import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.FragmentBookshelf1Binding 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.lib.theme.primaryTextColor import io.legado.app.ui.audio.AudioPlayActivity -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.info.BookInfoActivity -import io.legado.app.ui.book.local.ImportBookActivity import io.legado.app.ui.book.read.ReadBookActivity 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.BookshelfViewModel -import io.legado.app.utils.* +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 BookshelfFragment1 : VMBaseFragment(R.layout.fragment_bookshelf1), +class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), SearchView.OnQueryTextListener, BaseBooksAdapter.CallBack { private val binding by viewBinding(FragmentBookshelf1Binding::bind) - override val viewModel: BookshelfViewModel by viewModels() - private val activityViewModel: MainViewModel by activityViewModels() + private lateinit var searchView: SearchView private lateinit var booksAdapter: BaseBooksAdapter<*> - private var groupId = AppConst.bookGroupNoneId + override var groupId = AppConst.bookGroupNoneId private var bookGroupLiveData: LiveData>? = null private var bookshelfLiveData: LiveData>? = null private var bookGroups: List = emptyList() - private var books: List = emptyList() - - private val importBookshelf = registerForActivityResult(FilePicker()) { - it?.readText(requireContext())?.let { text -> - viewModel.importBookshelf(text, groupId) - } - } + override var books: List = emptyList() override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { + searchView = binding.titleBar.findViewById(R.id.search_view) setSupportToolbar(binding.titleBar.toolbar) - initView() + initSearchView() + initRecyclerView() initGroupData() initBooksData() } 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() - 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() - R.id.menu_add_url -> addBookByUrl() - R.id.menu_arrange_bookshelf -> startActivity { - putExtra("groupId", groupId) - } - R.id.menu_download -> startActivity { - putExtra("groupId", groupId) + 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 } - R.id.menu_export_bookshelf -> viewModel.exportBookshelf(books) { - activity?.share(it) + + override fun onQueryTextChange(newText: String?): Boolean { + + return false } - R.id.menu_import_bookshelf -> importBookshelfAlert() - } + }) } - private fun initView() { + private fun initRecyclerView() { ATH.applyEdgeEffectColor(binding.rvBookshelf) binding.refreshLayout.setColorSchemeColors(accentColor) binding.refreshLayout.setOnRefreshListener { @@ -192,58 +172,7 @@ class BookshelfFragment1 : VMBaseFragment(R.layout.fragment_ return false } - @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() - } - - fun gotoTop() { + override fun gotoTop() { if (AppConfig.isEInkMode) { binding.rvBookshelf.scrollToPosition(0) } else { @@ -251,29 +180,6 @@ class BookshelfFragment1 : VMBaseFragment(R.layout.fragment_ } } - 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, groupId) - } - } - noButton() - neutralButton(R.string.select_file) { - importBookshelf.launch( - FilePickerParam( - mode = FilePicker.FILE, - allowExtensions = arrayOf("txt", "json") - ) - ) - } - }.show() - } - override fun onItemClick(position: Int) { if (position < bookGroups.size) { val bookGroup = bookGroups[position] diff --git a/app/src/main/res/layout/dialog_bookshelf_config.xml b/app/src/main/res/layout/dialog_bookshelf_config.xml index 9d795bd2a..cbf550197 100644 --- a/app/src/main/res/layout/dialog_bookshelf_config.xml +++ b/app/src/main/res/layout/dialog_bookshelf_config.xml @@ -14,6 +14,27 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent"> + + + + + + + + Hide when status bar show Reverse toc 显示发现 + 样式 + 分组样式 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index af7c1a855..2316617f8 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -840,4 +840,6 @@ Ocultar quando a barra de status é mostrada TOC reverso Mostrar + 样式 + 分组样式 diff --git a/app/src/main/res/values-zh-rHK/arrays.xml b/app/src/main/res/values-zh-rHK/arrays.xml index 29d1e441f..6a0784ea8 100644 --- a/app/src/main/res/values-zh-rHK/arrays.xml +++ b/app/src/main/res/values-zh-rHK/arrays.xml @@ -6,6 +6,11 @@ 音頻 + + 标签 + 文件夹 + + 跟隨系統 亮色主題 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index f46c68166..45cf4f55a 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -838,5 +838,7 @@ 【%s】 反转目录 显示发现 + 样式 + 分组样式 diff --git a/app/src/main/res/values-zh-rTW/arrays.xml b/app/src/main/res/values-zh-rTW/arrays.xml index 29d2c7b44..ff880fda3 100644 --- a/app/src/main/res/values-zh-rTW/arrays.xml +++ b/app/src/main/res/values-zh-rTW/arrays.xml @@ -5,6 +5,11 @@ 音訊 + + 标签 + 文件夹 + + .txt .json diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index aafc932be..9e89c0e57 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -839,5 +839,7 @@ 状态栏显示时隐藏 反转目录 显示发现 + 样式 + 分组样式 diff --git a/app/src/main/res/values-zh/arrays.xml b/app/src/main/res/values-zh/arrays.xml index 3b5f13838..dd54deede 100644 --- a/app/src/main/res/values-zh/arrays.xml +++ b/app/src/main/res/values-zh/arrays.xml @@ -5,6 +5,11 @@ 音频 + + 标签 + 文件夹 + + .txt .json diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 1c23cf5f2..b4ab34633 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -839,5 +839,7 @@ 状态栏显示时隐藏 反转目录 显示发现 + 样式 + 分组样式 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index dde55ce68..2633c8003 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -5,6 +5,11 @@ Audio + + Tab + Folder + + @string/indent_0 @string/indent_1 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 557de3291..cd20abb71 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -840,4 +840,7 @@ Hide when status bar show Reverse toc Show + Style + Group style + From a8d6b38ba08c0642039df990f96680999d9e8265 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sat, 5 Jun 2021 21:00:49 +0800 Subject: [PATCH 063/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E5=88=86=E7=BB=84=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/bookshelf/style2/BaseBooksAdapter.kt | 22 ++++++++++--------- .../main/bookshelf/style2/BooksAdapterGrid.kt | 4 ++-- .../main/bookshelf/style2/BooksAdapterList.kt | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt index 1ad12187f..15132959a 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt @@ -6,8 +6,10 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.legado.app.data.entities.Book -abstract class BaseBooksAdapter(val context: Context) : - RecyclerView.Adapter() { +abstract class BaseBooksAdapter( + val context: Context, + val callBack: CallBack +) : RecyclerView.Adapter() { val diffItemCallback: DiffUtil.ItemCallback get() = object : DiffUtil.ItemCallback() { @@ -62,14 +64,14 @@ abstract class BaseBooksAdapter(val context: Conte } fun notification(bookUrl: String) { -// for (i in 0 until itemCount) { -// getItem(i)?.let { -// if (it.bookUrl == bookUrl) { -// notifyItemChanged(i, bundleOf(Pair("refresh", null))) -// return -// } -// } -// } + for (i in 0 until itemCount) { + callBack.getItem(i).let { + if (it is Book && it.bookUrl == bookUrl) { + notifyItemChanged(i, bundleOf(Pair("refresh", null))) + return + } + } + } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt index 5e89d351b..eaa8e8a9d 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt @@ -13,8 +13,8 @@ import io.legado.app.help.AppConfig import io.legado.app.utils.invisible import splitties.views.onLongClick -class BooksAdapterGrid(context: Context, private val callBack: CallBack) : - BaseBooksAdapter(context) { +class BooksAdapterGrid(context: Context, callBack: CallBack) : + BaseBooksAdapter(context, callBack) { override fun getItemCount(): Int { return callBack.getItemCount() diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt index 3130076dd..755772eaa 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt @@ -15,8 +15,8 @@ import io.legado.app.utils.invisible import io.legado.app.utils.visible import splitties.views.onLongClick -class BooksAdapterList(context: Context, private val callBack: CallBack) : - BaseBooksAdapter(context) { +class BooksAdapterList(context: Context, callBack: CallBack) : + BaseBooksAdapter(context, callBack) { override fun getItemCount(): Int { return callBack.getItemCount() From c0bcb32db5e02504366fce5decfbdb47c4520ab9 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 6 Jun 2021 17:40:21 +0800 Subject: [PATCH 064/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=AF=BC=E5=87=BA=E6=96=87=E4=BB=B6=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/constant/PreferKey.kt | 1 + .../main/java/io/legado/app/help/AppConfig.kt | 6 ++++++ .../app/model/analyzeRule/AnalyzeRule.kt | 4 +--- .../legado/app/ui/book/cache/CacheActivity.kt | 14 +++++++++++++ .../app/ui/book/cache/CacheViewModel.kt | 20 +++++++++++++++---- app/src/main/res/menu/book_cache.xml | 5 +++++ app/src/main/res/values-ja-rJP/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-zh-rHK/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/strings.xml | 3 ++- 12 files changed, 50 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 67732823a..cadb256e0 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -28,6 +28,7 @@ object PreferKey { const val showRss = "showRss" const val bookshelfLayout = "bookshelfLayout" const val bookshelfSort = "bookshelfSort" + const val bookExportFileName = "bookExportFileName" const val recordLog = "recordLog" const val processText = "process_text" const val cleanCache = "cleanCache" diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index 218efbf7a..4e90f74f9 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -96,6 +96,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { appCtx.putPrefInt(PreferKey.bookGroupStyle, value) } + var bookExportFileName: String? + get() = appCtx.getPrefString(PreferKey.bookExportFileName) + set(value) { + appCtx.putPrefString(PreferKey.bookExportFileName, value) + } + var backupPath: String? get() = appCtx.getPrefString(PreferKey.backupPath) set(value) { diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index f704c7f24..57b3d25a1 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -650,9 +650,7 @@ class AnalyzeRule(val ruleData: RuleDataInterface) : JsExtensions { bindings["title"] = chapter?.title bindings["src"] = content bindings["nextChapterUrl"] = nextChapterUrl - return runBlocking { - SCRIPT_ENGINE.eval(jsStr, bindings) - } + return SCRIPT_ENGINE.eval(jsStr, bindings) } /** diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt index fe70f9be6..7016927b5 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt @@ -127,6 +127,7 @@ class CacheActivity : VMBaseActivity() exportPosition = -1 selectExportFolder() } + R.id.menu_export_file_name -> alertExportFileName() R.id.menu_export_type -> showExportTypeConfig() R.id.menu_export_charset -> showCharsetConfig() R.id.menu_log -> @@ -294,6 +295,19 @@ class CacheActivity : VMBaseActivity() } } + private fun alertExportFileName() { + alert(R.string.export_file_name) { + val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply { + editView.setText(AppConfig.bookExportFileName) + } + customView { alertBinding.root } + okButton { + AppConfig.bookExportFileName = alertBinding.editView.text?.toString() + } + cancelButton() + }.show() + } + private fun showExportTypeConfig() { selector(R.string.export_type, arrayListOf("txt", "epub")) { _, i -> AppConfig.exportType = i diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt index 819686dcf..fab7802b6 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt @@ -10,6 +10,7 @@ import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import io.legado.app.R import io.legado.app.base.BaseViewModel +import io.legado.app.constant.AppConst import io.legado.app.constant.AppPattern import io.legado.app.data.appDb import io.legado.app.data.entities.Book @@ -26,10 +27,21 @@ import java.io.ByteArrayOutputStream import java.io.File import java.io.FileOutputStream import java.nio.charset.Charset +import javax.script.SimpleBindings class CacheViewModel(application: Application) : BaseViewModel(application) { + fun getExportFileName(book: Book): String { + val jsStr = AppConfig.bookExportFileName + if (jsStr.isNullOrBlank()) { + return "${book.name} by ${book.author}" + } + val bindings = SimpleBindings() + bindings["name"] = book.name + bindings["author"] = book.author + return AppConst.SCRIPT_ENGINE.eval(jsStr, bindings).toString() + } fun export(path: String, book: Book, finally: (msg: String) -> Unit) { execute { @@ -50,7 +62,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { @Suppress("BlockingMethodInNonBlockingContext") private suspend fun export(doc: DocumentFile, book: Book) { - val filename = "${book.name} by ${book.author}.txt" + val filename = "${getExportFileName(book)}.txt" DocumentUtils.delete(doc, filename) DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc -> val stringBuilder = StringBuilder() @@ -80,7 +92,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { } private suspend fun export(file: File, book: Book) { - val filename = "${book.name} by ${book.author}.txt" + val filename = "${getExportFileName(book)}.txt" val bookPath = FileUtils.getPath(file, filename) val bookFile = FileUtils.createFileWithReplace(bookPath) val stringBuilder = StringBuilder() @@ -161,7 +173,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { @Suppress("BlockingMethodInNonBlockingContext") private fun exportEpub(doc: DocumentFile, book: Book) { - val filename = "${book.name} by ${book.author}.epub" + val filename = "${getExportFileName(book)}.epub" DocumentUtils.delete(doc, filename) val epubBook = EpubBook() epubBook.version = "2.0" @@ -185,7 +197,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { private fun exportEpub(file: File, book: Book) { - val filename = "${book.name} by ${book.author}.epub" + val filename = "${getExportFileName(book)}.epub" val epubBook = EpubBook() epubBook.version = "2.0" //set metadata diff --git a/app/src/main/res/menu/book_cache.xml b/app/src/main/res/menu/book_cache.xml index 71598a744..0f58daf7a 100644 --- a/app/src/main/res/menu/book_cache.xml +++ b/app/src/main/res/menu/book_cache.xml @@ -42,6 +42,11 @@ android:title="@string/export_folder" app:showAsAction="never" /> + + 显示发现 样式 分组样式 + 导出文件名 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 2316617f8..a949fa0ba 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -842,4 +842,5 @@ Mostrar 样式 分组样式 + 导出文件名 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 45cf4f55a..e6de6eeff 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -840,5 +840,6 @@ 显示发现 样式 分组样式 + 导出文件名 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 9e89c0e57..168223c92 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -841,5 +841,6 @@ 显示发现 样式 分组样式 + 导出文件名 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index b4ab34633..20ccc50d9 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -841,5 +841,6 @@ 显示发现 样式 分组样式 + 导出文件名 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cd20abb71..d7d1ba6a8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -808,7 +808,7 @@ Background image Copy book URL Copy chapters URL - Export to a folder + Export folder Exported text coding Export to WebDav Reverse content @@ -842,5 +842,6 @@ Show Style Group style + Export file name From 3757995e695d213e71440aa1d7ebe5040dc49fa3 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 6 Jun 2021 18:02:46 +0800 Subject: [PATCH 065/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index ec1047cbc..ba1506f2d 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,6 +3,11 @@ * 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2021/06/06** +* 添加自定义导出文件名 +* 添加书架文件夹分组样式,未完成 +* viewPager2 3层嵌套有问题,书架换回viewPager + **2021/05/26** * 书签绑定书名与作者 * 修复详情页目录问题 From 9371d1795d2a46b3c66fb2d22545b0ee18de053d Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 6 Jun 2021 21:02:47 +0800 Subject: [PATCH 066/112] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E5=88=86=E7=BB=84=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/config/OtherConfigFragment.kt | 2 +- app/src/main/java/io/legado/app/ui/main/MainActivity.kt | 6 ++++-- .../legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index f08b3c250..9a9d7b9bd 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -133,7 +133,7 @@ class OtherConfigFragment : BasePreferenceFragment(), PreferKey.processText -> sharedPreferences?.let { setProcessTextEnable(it.getBoolean(key, true)) } - PreferKey.showDiscovery, PreferKey.showRss -> postEvent(EventBus.NOTIFY_MAIN, "") + PreferKey.showDiscovery, PreferKey.showRss -> postEvent(EventBus.NOTIFY_MAIN, true) PreferKey.defaultCover -> upPreferenceSummary( key, getPrefString(PreferKey.defaultCover) ) diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 6b4bf86a6..c9ea98bd0 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -172,11 +172,13 @@ class MainActivity : VMBaseActivity(), observeEvent(EventBus.RECREATE) { recreate() } - observeEvent(EventBus.NOTIFY_MAIN) { + observeEvent(EventBus.NOTIFY_MAIN) { binding.apply { upBottomMenu() viewPagerMain.adapter?.notifyDataSetChanged() - viewPagerMain.setCurrentItem(bottomMenuCount - 1, false) + if (it) { + viewPagerMain.setCurrentItem(bottomMenuCount - 1, false) + } } } observeEvent(PreferKey.threadCount) { diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt index 150eaeb8e..3fffdc73e 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt @@ -6,6 +6,7 @@ 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 @@ -93,7 +94,7 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment Date: Mon, 7 Jun 2021 12:54:37 +0800 Subject: [PATCH 067/112] Update appHelp.md --- app/src/main/assets/help/appHelp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/help/appHelp.md b/app/src/main/assets/help/appHelp.md index d7e4fb12b..ee4ddc880 100644 --- a/app/src/main/assets/help/appHelp.md +++ b/app/src/main/assets/help/appHelp.md @@ -25,7 +25,7 @@ * 导入后返回书源管理界面; * 新版qq下载路径:Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/ -![导入书源](https://gitee.com/gekunfei/web/raw/master/legado/importSource.jpg) +![导入书源](https://cdn.jsdelivr.net/gh/gedoor/gedoor.github.io@master/images/importSource.jpg) 2. 如何新建大佬发的单独书源? * 复制书源代码; From 5b4e529e5f93955a2b162adbfb4c265dcef2095e Mon Sep 17 00:00:00 2001 From: fisher Date: Mon, 7 Jun 2021 13:34:25 +0800 Subject: [PATCH 068/112] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/help/appHelp.md | 26 ++++++++++++++++++++------ app/src/main/assets/updateLog.md | 7 +++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/src/main/assets/help/appHelp.md b/app/src/main/assets/help/appHelp.md index d7e4fb12b..d1e2963f7 100644 --- a/app/src/main/assets/help/appHelp.md +++ b/app/src/main/assets/help/appHelp.md @@ -25,7 +25,7 @@ * 导入后返回书源管理界面; * 新版qq下载路径:Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/ -![导入书源](https://gitee.com/gekunfei/web/raw/master/legado/importSource.jpg) +![QQ导入书源](https://cdn.jsdelivr.net/gh/gedoor/gedoor.github.io@master/images/importSource.jpg) 2. 如何新建大佬发的单独书源? * 复制书源代码; @@ -51,7 +51,7 @@ * 找到myBookSource.json长按选择分享; * 选择微信分享或者QQ分享; * 选择你要分享的好友点击发送; -* 好友接收后在手机自带内存根目录找到myBookSource.json文件(QQ 安卓10及以下版本在Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/,安卓11版本用户由于系统限制无法访问data目录,微信在Tencent/MicroMsg/Download); +* 好友接收后在手机自带内存根目录找到myBookSource.json文件(最新版QQ 安卓10及以下版本在Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/,安卓11版本用户由于系统限制无法访问data目录,微信在Tencent/MicroMsg/Download); * 复制该文件到手机自带内存根目录找到YueDu3.0文件夹(如已有该文件请先删除该文件或者备份到其他地方再复制到文件夹); * 打开阅读软件点击恢复。 * 注:备份路径如已修改过请在修改后的路径下查找书源文件。 @@ -67,7 +67,7 @@ ## 本地书籍相关 1. 目前阅读支持哪些格式的本地书籍? -* 目前支持TXT、EPUB格式(只支持显示EPUB里的文本内容,还不支持显示图片) +* 目前支持TXT、EPUB格式 2. 如何导入本地书籍? * 在书架页面点击右上角,选择“添加本地”,授予相关权限后即可导入本地书籍。也可在文件管理器中使用 **阅读** 打开相关书籍。 @@ -80,10 +80,22 @@ 4. 如何下载书籍到本地? * 把在线书籍加入到书架后,在书架页面点击右上角,选择“离线缓存”即可。 -5. 为什么我打开本地的TXT文件,显示内容却是乱码? +5. 如何自定义导出的txt/epub文件名称? +* 点击 **离线缓存** - **导出文件名**. +* 使用方法: + - 导出文件名支持js语法 + - 可用变量: name - 书名 author-作者 + - 示例: + > {name+"作者:"+author} + - 导出文件名: + > Legado是最好的在线阅读软件 作者: kunfei. + +**注意:** name、author等变量与字符串的拼接都需要在js环境中进行,即必须使用{ } 将变量与字符串包裹起来. + +6. 为什么我打开本地的TXT文件,显示内容却是乱码? * 部分编码在阅读上会识别错误,建议先用文本编辑器转换为常用的UTF-8格式。 -6. 阅读对部分把正文(如所有含引号的句子)识别成标题,如何解决? +7. 阅读对部分把正文(如所有含引号的句子)识别成标题,如何解决? * 点击右上角更换目录规则即可。 ## 书籍界面相关 @@ -128,7 +140,9 @@ * 请先查看原网页是否正常,若正常,请在书籍阅读界面点击右上角的 **⁝** 按钮,在弹出的菜单中,选择 **翻页动画(本书)**,将翻页动画更改为 **滚动**。 14. 阅读图片章节、漫画或epub插图时,图片被缩放到一页中,以至无法看清,如何处理? -* 临时处理方案:长按图片可以进行双指缩放。图片章节请先参考Q13中的方案将翻页动画更改为**滚动**,再点击书籍界面的章节标题进入 编辑书源 界面,在 正文-图片样式 中填入 *full*,保存更改,刷新当前章节即可。 +* 临时处理方案:长按图片可以进行双指缩放。图片章节请先参考Q13中的方案将翻页动画更改为**滚动** +* 3.0旧版可以点击书籍界面的章节标题进入 **编辑书源** 界面,在 正文-图片样式 中填入 *full*,保存更改,刷新当前章节即可。 +* 3.0新版可以直接在书籍阅读界面点击右上角的 **⁝** 按钮,选择 图片样式- *full*. ## 替换净化相关 diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index ba1506f2d..5724c9041 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -2,6 +2,13 @@ * 关注公众号 **[开源阅读]** 菜单•软件下载 提前享受新版本。 * 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +## **必读** +【温馨提醒】 *更新前一定要做好备份,以免数据丢失!* + +* 阅读只是一个转码工具,不提供内容,第一次安装app,需要自己手动导入书源,可以从公众号 **[开源阅读]**、QQ群、酷安评论里获取由书友制作分享的书源。 + +* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则出现问题。先关闭替换净化并刷新,再观察是否正常。如果正常说明净化规则存在误杀,如果关闭后仍然出现相关问题,请点击源链接查看原文与正文是否相同,如果不同,再进行反馈。 +* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! **2021/06/06** * 添加自定义导出文件名 From efa2af643bcdbff75efb08f26445aa69e53b11da Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Mon, 7 Jun 2021 14:02:12 +0800 Subject: [PATCH 069/112] Update Book.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 自定义简介有自动更新的需求时,可通过更新intro再调用upCustomIntro()完成 --- app/src/main/java/io/legado/app/data/entities/Book.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index 73b89205f..b51544110 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -113,6 +113,9 @@ data class Book( fun getDisplayIntro() = if (customIntro.isNullOrEmpty()) intro else customIntro + //自定义简介有自动更新的需求时,可通过更新intro再调用upCustomIntro()完成 + fun upCustomIntro() = customIntro = intro + fun fileCharset(): Charset { return charset(charset ?: "UTF-8") } @@ -269,4 +272,4 @@ data class Book( @TypeConverter fun stringToReadConfig(json: String?) = GSON.fromJsonObject(json) } -} \ No newline at end of file +} From 0b97712880c10935d7d8aea3734f630464190e42 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 7 Jun 2021 15:16:16 +0800 Subject: [PATCH 070/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/data/entities/Book.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index b51544110..e11193a3c 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -114,7 +114,9 @@ data class Book( fun getDisplayIntro() = if (customIntro.isNullOrEmpty()) intro else customIntro //自定义简介有自动更新的需求时,可通过更新intro再调用upCustomIntro()完成 - fun upCustomIntro() = customIntro = intro + fun upCustomIntro() { + customIntro = intro + } fun fileCharset(): Charset { return charset(charset ?: "UTF-8") From afe49d85eca54c02baaa0d5bd6d958b019356942 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 7 Jun 2021 17:33:56 +0800 Subject: [PATCH 071/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/data/entities/Book.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt index e11193a3c..b192b9a1b 100644 --- a/app/src/main/java/io/legado/app/data/entities/Book.kt +++ b/app/src/main/java/io/legado/app/data/entities/Book.kt @@ -114,6 +114,7 @@ data class Book( fun getDisplayIntro() = if (customIntro.isNullOrEmpty()) intro else customIntro //自定义简介有自动更新的需求时,可通过更新intro再调用upCustomIntro()完成 + @Suppress("unused") fun upCustomIntro() { customIntro = intro } From 7b5f95cff717b24edaf0a04b5d0a7122339232c9 Mon Sep 17 00:00:00 2001 From: mezysinc <81477718+mezysinc@users.noreply.github.com> Date: Mon, 7 Jun 2021 16:00:46 -0300 Subject: [PATCH 072/112] arrays in ptbr --- app/src/main/res/values-pt-rBR/arrays.xml | 115 ++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 app/src/main/res/values-pt-rBR/arrays.xml diff --git a/app/src/main/res/values-pt-rBR/arrays.xml b/app/src/main/res/values-pt-rBR/arrays.xml new file mode 100644 index 000000000..132358ee0 --- /dev/null +++ b/app/src/main/res/values-pt-rBR/arrays.xml @@ -0,0 +1,115 @@ + + + + Texto + Áudio + + + + Aba + Pasta + + + + @string/indent_0 + @string/indent_1 + @string/indent_2 + @string/indent_3 + @string/indent_4 + + + + .txt + .json + .xml + + + + @string/jf_convert_o + @string/jf_convert_j + @string/jf_convert_f + + + + Adaptar do sistema + Tema Claro + Tema Escuro + Tema E-Ink + + + + Autom. + Escuro + Claro + Adaptado + + + + Padrão + 1 min + 2 min + 3 min + Sempre + + + + @string/screen_unspecified + @string/screen_portrait + @string/screen_landscape + @string/screen_sensor + + + + íconePrincipal + ícone1 + ícone2 + ícone3 + ícone4 + ícone5 + ícone6 + + + + Desativado + Tradicional a Simplificado + Simplificado a Tradicional + + + + Fonte padrão + Fonte Serif + Fonte Monospaced + + + + Em branco + Título + Tempo + Bateria + Páginas + Progresso + Páginas e progresso + Nome do livro + Tempo e Bateria + + + + Normal + Negrito + Claro + + + + Autom. + Chinês simplificado + Chinês tradicional + Inglês + + + + FonteLivro + FonteRSS + SubstituirRegra + + + From 25ddd47092735e5db832a5f14871c42d72b5e039 Mon Sep 17 00:00:00 2001 From: mezysinc <81477718+mezysinc@users.noreply.github.com> Date: Mon, 7 Jun 2021 16:01:54 -0300 Subject: [PATCH 073/112] updated strings in ptbr --- app/src/main/res/values-pt-rBR/strings.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a949fa0ba..7ed9c7676 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -423,7 +423,7 @@ WebViewJs(webJs) 资源正则(fonteRegex) 替换规则(substRegex) - 图片样式(imagemEstilo) + 图片样式(imagemFormato) 图标(fonteÍcone) 列表规则(regrasArtigos) @@ -435,7 +435,7 @@ 描述规则(regraDescrição) 图片url规则(regraImagem) 内容规则(regraConteúdo) - 样式(estilo) + 样式(formato) 链接规则(regraLink) @@ -779,7 +779,7 @@ Excluir automaticamente dados vencidos da busca Histórico de busca mais de um dia Re-segmentar - Estilo do nome: + Formato do nome: Clique no ícone da pasta no canto superior direito e selecione a pasta Busca inteligente Sem livros @@ -808,18 +808,18 @@ Imagem de fundo Copiar a URL do livro Copiar a URL dos capítulos - Exportar para uma pasta + Exportar a pasta Codificação de texto exportada Exportar para WebDav Conteúdo reverso Depurar Log de falhas Utilização de filiais chinesas personalizadas - Estilo de imagem + Formato de imagem Sistema TTS Formato de exportação Verificar por autor - Esta URL foi subscrita + Esta URL foi substituída Alta taxa de atualização da tela Use a maior taxa de atualização da tela Exportar tudo @@ -840,7 +840,7 @@ Ocultar quando a barra de status é mostrada TOC reverso Mostrar - 样式 - 分组样式 - 导出文件名 + Formato + Formato de grupo + Exportar o nome do arquivo From 0d74307481b7a69909e0b7f94efb36bf014c1e4b Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 8 Jun 2021 14:38:05 +0800 Subject: [PATCH 074/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/page/provider/ChapterProvider.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt index dfeb4cb6d..4f0596f42 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt @@ -119,10 +119,10 @@ object ChapterProvider { if (matcher.find()) { matcher.group(1)?.let { src -> //if (!book.isEpub()) { - durY = setTypeImage( - book, bookChapter, src, - durY, textPages, book.getImageStyle() - ) + durY = setTypeImage( + book, bookChapter, src, + durY, textPages, book.getImageStyle() + ) //} } } else { @@ -474,7 +474,7 @@ object ChapterProvider { ReadBookConfig.textFont = "" ReadBookConfig.save() Typeface.SANS_SERIF - } + } ?: Typeface.DEFAULT } private fun getPaint(typeface: Typeface): Pair { From c3ca4ca11eb4defd227e7d27d4356cfc32a6d0ef Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Tue, 8 Jun 2021 17:05:20 +0800 Subject: [PATCH 075/112] Update BookHelp.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化章节序号和纯章节名匹配正则,使其能处理更复杂的字符串 --- .../main/java/io/legado/app/help/BookHelp.kt | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index dba76f97f..0f62fb235 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -327,18 +327,28 @@ object BookHelp { } } - private val chapterNamePattern by lazy { - Pattern.compile("^(.*?第([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟0-9\\s]+)[章节篇回集])[、,。 ::.\\s]*") + private val chapterNamePattern1 by lazy { + Pattern.compile(".*?第([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)[章节篇回集话]") } - + + private val chapterNamePattern2 by lazy { + Pattern.compile("^(?:[\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+[,:、])*([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)(?:[,:、]|\\.[^\\d])") + } + + private val regexA by lazy { + return@lazy "\\s".toRegex() + } + private fun getChapterNum(chapterName: String?): Int { - if (chapterName != null) { - val matcher: Matcher = chapterNamePattern.matcher(chapterName) - if (matcher.find()) { - return StringUtils.stringToInt(matcher.group(2)) - } - } - return -1 + chapterName ?: return -1 + chapterName = StringUtils.fullToHalf(chapterName).replace(regexA, "") + return StringUtils.stringToInt( + ( + chapterNamePattern1.matcher(chapterName).takeIf{it.find()} + ?:chapterNamePattern2.matcher(chapterName).takeIf{it.find()} + )?.group(1) + ?:"-1" + ) } @Suppress("SpellCheckingInspection") @@ -347,19 +357,22 @@ object BookHelp { return@lazy "[^\\w\\u4E00-\\u9FEF〇\\u3400-\\u4DBF\\u20000-\\u2A6DF\\u2A700-\\u2EBEF]".toRegex() } - private val regexA by lazy { - return@lazy "\\s".toRegex() - } - private val regexB by lazy { - return@lazy "^第.*?章|[(\\[][^()\\[\\]]{2,}[)\\]]$".toRegex() + //章节序号,排除处于结尾的状况,避免将章节名替换为空字串 + return@lazy "^.*?第(?:[\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)[章节篇回集话](?!$)|^(?:[\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+[,:、])*(?:[\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+)(?:[,:、](?!$)|\\.(?=[^\\d]))".toRegex() } + private val regexC by lazy { + //前后附加内容,整个章节名都在括号中时只剔除首尾括号,避免将章节名替换为空字串 + return@lazy "(?!^)(?:[〖【《〔\\[{(][^〖【《〔\\[{()〕》】〗\\]}]+)?[)〕》】〗\\]}]$|^[〖【《〔\\[{(](?:[^〖【《〔\\[{()〕》】〗\\]}]+[〕》】〗\\]})])?(?!$)".toRegex() + } + private fun getPureChapterName(chapterName: String?): String { return if (chapterName == null) "" else StringUtils.fullToHalf(chapterName) .replace(regexA, "") .replace(regexB, "") + .replace(regexC, "") .replace(regexOther, "") } -} \ No newline at end of file +} From 1ded30fe86c8b843f48419c09bcae1172c33c87e Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 8 Jun 2021 17:30:20 +0800 Subject: [PATCH 076/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/help/BookHelp.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index 0f62fb235..99adf90b1 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -15,7 +15,6 @@ import org.apache.commons.text.similarity.JaccardSimilarity import splitties.init.appCtx import java.io.File import java.util.concurrent.CopyOnWriteArraySet -import java.util.regex.Matcher import java.util.regex.Pattern import kotlin.math.abs import kotlin.math.max @@ -56,14 +55,14 @@ object BookHelp { } } - fun getEpubFile(book: Book,): File { + fun getEpubFile(book: Book): File { val file = FileUtils.getFile( downloadDir, cacheFolderName, book.getFolderName(), "index.epubx" ) - if(!file.exists()){ + if (!file.exists()) { val input = if (book.bookUrl.isContentScheme()) { val uri = Uri.parse(book.bookUrl) appCtx.contentResolver.openInputStream(uri) @@ -341,12 +340,12 @@ object BookHelp { private fun getChapterNum(chapterName: String?): Int { chapterName ?: return -1 - chapterName = StringUtils.fullToHalf(chapterName).replace(regexA, "") + val chapterName1 = StringUtils.fullToHalf(chapterName).replace(regexA, "") return StringUtils.stringToInt( ( - chapterNamePattern1.matcher(chapterName).takeIf{it.find()} - ?:chapterNamePattern2.matcher(chapterName).takeIf{it.find()} - )?.group(1) + chapterNamePattern1.matcher(chapterName1).takeIf { it.find() } + ?: chapterNamePattern2.matcher(chapterName1).takeIf { it.find() } + )?.group(1) ?:"-1" ) } @@ -354,6 +353,7 @@ object BookHelp { @Suppress("SpellCheckingInspection") private val regexOther by lazy { // 所有非字母数字中日韩文字 CJK区+扩展A-F区 + @Suppress("RegExpDuplicateCharacterInClass") return@lazy "[^\\w\\u4E00-\\u9FEF〇\\u3400-\\u4DBF\\u20000-\\u2A6DF\\u2A700-\\u2EBEF]".toRegex() } From da3fb420f226299915d087614178118a4cb596cf Mon Sep 17 00:00:00 2001 From: gedoor Date: Thu, 10 Jun 2021 22:55:13 +0800 Subject: [PATCH 077/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/bookshelf/style2/BooksAdapterGrid.kt | 104 +++++++---- .../main/bookshelf/style2/BooksAdapterList.kt | 137 ++++++++------ .../res/layout/item_bookshelf_grid_group.xml | 90 ++++++++++ .../res/layout/item_bookshelf_list_group.xml | 170 ++++++++++++++++++ 4 files changed, 411 insertions(+), 90 deletions(-) create mode 100644 app/src/main/res/layout/item_bookshelf_grid_group.xml create mode 100644 app/src/main/res/layout/item_bookshelf_list_group.xml diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt index eaa8e8a9d..3bd6e01c4 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt @@ -9,12 +9,13 @@ 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(context, callBack) { + BaseBooksAdapter(context, callBack) { override fun getItemCount(): Int { return callBack.getItemCount() @@ -23,56 +24,82 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) : override fun onCreateViewHolder( parent: ViewGroup, viewType: Int - ): ItemViewHolder { - return ItemViewHolder( + ): RecyclerView.ViewHolder { + return BookViewHolder( ItemBookshelfGridBinding.inflate(LayoutInflater.from(context), parent, false) ) } override fun onBindViewHolder( - holder: ItemViewHolder, + holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList ) { - holder.binding.run { - val bundle = payloads.getOrNull(0) as? Bundle - if (bundle == null) { - super.onBindViewHolder(holder, position, payloads) - } else { - when (val item = callBack.getItem(position)) { - 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) - } - } - } - is BookGroup -> { - tvName.text = item.groupName + 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) } } } - override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { - holder.binding.run { - when (val item = callBack.getItem(position)) { - is Book -> { - tvName.text = item.name - ivCover.load(item.getDisplayCover(), item.name, item.author) - upRefresh(this, item) - } - is BookGroup -> { - tvName.text = item.groupName - } + 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) @@ -98,7 +125,10 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) : } } - class ItemViewHolder(val binding: ItemBookshelfGridBinding) : + class BookViewHolder(val binding: ItemBookshelfGridBinding) : + RecyclerView.ViewHolder(binding.root) + + class GroupViewHolder(val binding: ItemBookshelfGridGroupBinding) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt index 755772eaa..0c85fd5fe 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt @@ -9,6 +9,7 @@ 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 @@ -16,78 +17,105 @@ import io.legado.app.utils.visible import splitties.views.onLongClick class BooksAdapterList(context: Context, callBack: CallBack) : - BaseBooksAdapter(context, callBack) { + BaseBooksAdapter(context, callBack) { override fun getItemCount(): Int { return callBack.getItemCount() } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { - return ItemViewHolder( + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return BookViewHolder( ItemBookshelfListBinding.inflate(LayoutInflater.from(context), parent, false) ) } override fun onBindViewHolder( - holder: ItemViewHolder, + holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList ) { val bundle = payloads.getOrNull(0) as? Bundle - if (bundle == null) { - super.onBindViewHolder(holder, position, payloads) - } else { - holder.binding.run { - when (val item = callBack.getItem(position)) { - 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) - } - } - } - is BookGroup -> { - tvName.text = item.groupName + 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: ItemViewHolder, position: Int) { - holder.binding.run { - when (val item = callBack.getItem(position)) { - 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) - } - is BookGroup -> { - tvName.text = item.groupName - flHasNew.gone() - ivAuthor.gone() - ivLast.gone() - ivRead.gone() - tvAuthor.gone() - tvLast.gone() - tvRead.gone() - } + 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) @@ -113,7 +141,10 @@ class BooksAdapterList(context: Context, callBack: CallBack) : } } - class ItemViewHolder(val binding: ItemBookshelfListBinding) : + class BookViewHolder(val binding: ItemBookshelfListBinding) : + RecyclerView.ViewHolder(binding.root) + + class GroupViewHolder(val binding: ItemBookshelfListGroupBinding) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/app/src/main/res/layout/item_bookshelf_grid_group.xml b/app/src/main/res/layout/item_bookshelf_grid_group.xml new file mode 100644 index 000000000..648d3b85f --- /dev/null +++ b/app/src/main/res/layout/item_bookshelf_grid_group.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_bookshelf_list_group.xml b/app/src/main/res/layout/item_bookshelf_list_group.xml new file mode 100644 index 000000000..00eb4966f --- /dev/null +++ b/app/src/main/res/layout/item_bookshelf_list_group.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 8934189e4fb303b1c65230b18592511d4874fe98 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:32:16 +0800 Subject: [PATCH 078/112] Update strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/values-zh-rTW/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 168223c92..27608165e 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -781,6 +781,7 @@ 樣式名稱: 點擊右上角資料夾圖示,選擇資料夾 智慧掃描 + 導入文件名 複製書籍URL 複製目錄URL 沒有書籍 From bcfe9a135e202dd48785b4d8e8f91442bf7ea6f0 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:41:12 +0800 Subject: [PATCH 079/112] Update strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/values-zh-rHK/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index e6de6eeff..6114e1dfc 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -777,6 +777,7 @@ 樣式名稱: 點擊右上角資料夾圖示,選擇資料夾 智能掃描 + 導入文件名 複製書籍URL 複製目錄URL 冇書 From 7b4c87a0f13e4bcd1813503d5e0b34a13a4ea533 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:42:40 +0800 Subject: [PATCH 080/112] Update strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/values-zh/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 20ccc50d9..5d74d9a46 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -781,6 +781,7 @@ 样式名称: 点击右上角文件夹图标,选择文件夹 智能扫描 + 导入文件名 拷贝书籍URL 拷贝目录URL 没有书籍 From b3bd158aac4471295232cf7617bb76022907dc9d Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:43:56 +0800 Subject: [PATCH 081/112] Update strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/values-pt-rBR/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 7ed9c7676..af54188db 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -781,7 +781,8 @@ Re-segmentar Formato do nome: Clique no ícone da pasta no canto superior direito e selecione a pasta - Busca inteligente + Busca inteligente + 导入文件名 Sem livros Manter o nome original O controle de toque da tela From 5440f95776853918f5b9cc78ea1043c2106b285a Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:45:13 +0800 Subject: [PATCH 082/112] Update strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/values-ja-rJP/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index ef4dbae70..0b7147b23 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -782,6 +782,7 @@ Style name: Click the folder icon in the upper right corner and select the folder Intelligent scanning + Imported-file name No books Keep the original name Screen touch control From 28549cdc0a764df38b70084a31ee648f4d78330b Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:46:29 +0800 Subject: [PATCH 083/112] Update strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/values/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7d1ba6a8..33121abcf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -781,7 +781,8 @@ Re-segment Style name: Click the folder icon in the upper right corner and select the folder - Intelligent scanning + 智能扫描 + Imported-file name No books Keep the original name Screen touch control From b14575de904c6e17d636cd8eb1415b3fd39a269b Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:47:35 +0800 Subject: [PATCH 084/112] Update import_book.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/menu/import_book.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/menu/import_book.xml b/app/src/main/res/menu/import_book.xml index 49ab4ec96..7c6816f00 100644 --- a/app/src/main/res/menu/import_book.xml +++ b/app/src/main/res/menu/import_book.xml @@ -13,4 +13,9 @@ android:title="@string/scan_folder" app:showAsAction="never" /> - \ No newline at end of file + + + From 3a3d0c36be41cd519141bd5174916d3bc233c7a9 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:53:09 +0800 Subject: [PATCH 085/112] Update ImportBookActivity.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- .../app/ui/book/local/ImportBookActivity.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index 8dfa25975..724152c15 100644 --- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt @@ -15,8 +15,10 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.appDb import io.legado.app.databinding.ActivityImportBookBinding +import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.help.AppConfig import io.legado.app.lib.permission.Permissions +import io.legado.app.lib.dialogs.alert import io.legado.app.lib.permission.PermissionsCompat import io.legado.app.lib.theme.backgroundColor import io.legado.app.ui.document.FilePicker @@ -78,6 +80,7 @@ class ImportBookActivity : VMBaseActivity selectFolder.launch(null) R.id.menu_scan_folder -> scanFolder() + R.id.menu_import_file_name -> alertImportFileName() } return super.onCompatOptionsItemSelected(item) } @@ -275,6 +278,19 @@ class ImportBookActivity : VMBaseActivity Unit = { launch { adapter.addItem(it) @@ -322,4 +338,4 @@ class ImportBookActivity : VMBaseActivity Date: Sun, 13 Jun 2021 18:01:40 +0800 Subject: [PATCH 086/112] Update strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 33121abcf..3070fd2c8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -781,7 +781,7 @@ Re-segment Style name: Click the folder icon in the upper right corner and select the folder - 智能扫描 + Intelligent scanning Imported-file name No books Keep the original name From c0d9f0cb3bb5fcee701a2d855bb4b44ffefdbdce Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 18:05:51 +0800 Subject: [PATCH 087/112] Update LocalBook.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化本地文件名解析,新增自定义“导入文件名”选项 --- .../legado/app/model/localBook/LocalBook.kt | 97 +++++++++---------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index 001cc93b5..1c0a6a906 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -10,7 +10,11 @@ import io.legado.app.utils.* import splitties.init.appCtx import java.io.File import java.util.regex.Pattern - +import io.legado.app.constant.AppConst +import io.legado.app.constant.AppPattern +import io.legado.app.help.AppConfig +import javax.script.SimpleBindings +import java.util.regex.Matcher object LocalBook { private const val folderName = "bookTxt" @@ -39,7 +43,7 @@ object LocalBook { fun importFile(uri: Uri): Book { val path: String - val fileName = if (uri.isContentScheme()) { + val fileName = (if (uri.isContentScheme()) { path = uri.toString() val doc = DocumentFile.fromSingleUri(appCtx, uri) doc?.let { @@ -55,66 +59,53 @@ object LocalBook { } else { path = uri.path!! File(path).name - } + }).replace(Regex("\\.txt$"), "") val name: String val author: String - if (("《" in fileName && "》" in fileName) - || "作者" in fileName - || (fileName.contains(" by ", true)) - ) { - - - //匹配(知轩藏书常用格式) 《书名》其它信息作者:作者名.txt - val m1 = Pattern - .compile("《(.*?)》.*?作者:(.*?)\\.txt") - .matcher(fileName) - //匹配 书名 by 作者名.txt - val m2 = Pattern - .compile("txt\\.(.*?) yb (.*?)$") - .matcher(fileName.reversed()) - - if (m1.find()) { - name = m1.group(1) ?: fileName.replace(".txt", "") - author = m1.group(2) ?: "" - BookHelp.formatBookAuthor(author) - } else if (m2.find()) { - var temp = m2.group(2) - name = if (temp==null||temp == "") { - fileName.replace(".txt", "") - } else { - temp.reversed() - } - temp = m2.group(1) ?: "" - author = temp.reversed() - BookHelp.formatBookAuthor(author) - } else { - - val st = fileName.indexOf("《") - val e = fileName.indexOf("》") - name = if (e > st && st != -1) { - fileName.substring(st + 1, e) - } else { - fileName.replace(".txt", "") - } + //匹配(知轩藏书常用格式) 《书名》其它信息作者:作者名.txt + val m1 = Pattern + .compile("(.*?)《([^《》]+)》(.*)") + .matcher(fileName) + + //匹配 书名 by 作者名.txt + val m2 = Pattern + .compile("(^)(.+) by (.+)$") + .matcher(fileName) + + (m1.takeIf { m1.find() } + ?: m2.takeIf { m2.find() } + ). run{ + + if(this is Matcher) { + //按默认格式将文件名分解成书名、作者名 + name = group(2)!! + author = BookHelp.formatBookAuthor((group(1) ?: "") + (group(3) ?: "")) + + } else if(!AppConfig.bookImportFileName.isNullOrBlank()) { + + //在脚本中定义如何分解文件名成书名、作者名 + val bindings = SimpleBindings() + bindings["src"] = fileName + val jsonStr = AppConst.SCRIPT_ENGINE.eval( + + //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获,未定义则赋值为空字符串 + AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})" + , bindings).toString() + val bookMess =GSON.fromJsonObject>(jsonStr) ?: HashMap() + name = bookMess["name"]?: fileName + author = bookMess["author"]?.takeIf { it.length != fileName.length } ?: "" - val s = fileName.indexOf("作者") - author = if (s != -1 && s + 2 < fileName.length) { - fileName.substring(s + 2).replace(".txt", "") } else { - "" - } - BookHelp.formatBookAuthor(author) - } - } else { + name = fileName.replace(AppPattern.nameRegex,"") + author = fileName.replace(AppPattern.authorRegex,"").takeIf { it.length != fileName.length } ?: "" - name = fileName.replace(".txt", "") - author = "" - } + } + } val book = Book( bookUrl = path, @@ -156,4 +147,4 @@ object LocalBook { } } } -} \ No newline at end of file +} From 30d5435b02e4cb88309b5a8fb37beda688b9e387 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 18:10:04 +0800 Subject: [PATCH 088/112] Update AppConfig.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/java/io/legado/app/help/AppConfig.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index 4e90f74f9..4dc5d3877 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -102,6 +102,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { appCtx.putPrefString(PreferKey.bookExportFileName, value) } + var bookImportFileName: String? + get() = appCtx.getPrefString(PreferKey.bookImportFileName) + set(value) { + appCtx.putPrefString(PreferKey.bookImportFileName, value) + } + var backupPath: String? get() = appCtx.getPrefString(PreferKey.backupPath) set(value) { From 6590bafaa28dcfae250eb772435fa1123eef6985 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 18:12:32 +0800 Subject: [PATCH 089/112] Update PreferKey.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增自定义“导入文件名”选项 --- app/src/main/java/io/legado/app/constant/PreferKey.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index cadb256e0..30f96e2b0 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -29,6 +29,7 @@ object PreferKey { const val bookshelfLayout = "bookshelfLayout" const val bookshelfSort = "bookshelfSort" const val bookExportFileName = "bookExportFileName" + const val bookImportFileName = "bookImportFileName" const val recordLog = "recordLog" const val processText = "process_text" const val cleanCache = "cleanCache" @@ -92,4 +93,4 @@ object PreferKey { const val cNBBackground = "colorBottomBackgroundNight" const val bgImageN = "backgroundImageNight" -} \ No newline at end of file +} From 5eeeb2ae8b4dcd36d36df47306d649d3e29b87d5 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 18:14:51 +0800 Subject: [PATCH 090/112] Update AppConst.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加动态链接指令快捷输入 --- app/src/main/java/io/legado/app/constant/AppConst.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt index 4938d3e15..a7db60a2d 100644 --- a/app/src/main/java/io/legado/app/constant/AppConst.kt +++ b/app/src/main/java/io/legado/app/constant/AppConst.kt @@ -40,7 +40,7 @@ object AppConst { arrayListOf( "❓", "@css:", "", "{{}}", "&&", "%%", "||", "//", "$.", "@", "\\", ":", "class", "id", "href", "textNodes", "ownText", "all", "html", - "[", "]", "<", ">", "#", "!", ".", "+", "-", "*", "=" + "[", "]", "<", ">", "##", "!", ".", "+", "-", "*", "=",",{\"webView\":true}" ) } From 6ecea51b04fe946dbfc27e5dfbaf4fb88fad82c5 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 20:12:03 +0800 Subject: [PATCH 091/112] Update AppPattern.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化图片正则、书名正则、作者名正则 图片有data-开头的数据属性时优先用数据属性作为src,没有数据属性时才匹配原生src属性 --- app/src/main/java/io/legado/app/constant/AppPattern.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index 0866b42d4..6f5113100 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -7,11 +7,12 @@ object AppPattern { val JS_PATTERN: Pattern = Pattern.compile("([\\w\\W]*?|@js:[\\w\\W]*$)", Pattern.CASE_INSENSITIVE) val EXP_PATTERN: Pattern = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}") + //图片有data-开头的数据属性时优先用数据属性作为src,没有数据属性时才匹配src val imgPattern: Pattern = - Pattern.compile("", Pattern.CASE_INSENSITIVE) + Pattern.compile("]*data-)src|(?=[^>]*data-)[^>]*data-[^= ]+) *= *\"([^\"{]+(?:\\{(?:[^{}]|\\{[^{}]*\\})*\\})?)\"[^>]*>", Pattern.CASE_INSENSITIVE) - val nameRegex = Regex("\\s+作\\s*者.*") - val authorRegex = Regex(".*?作\\s*?者[::]") + val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著") + val authorRegex = Regex("^.*?作\\s*者[::\\s]*|\\s+著") val fileNameRegex = Regex("[\\\\/:*?\"<>|.]") val splitGroupRegex = Regex("[,;,;]") -} \ No newline at end of file +} From c24eb1b807b5a377bc5882fe7f85f0699169be44 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Sun, 13 Jun 2021 20:16:59 +0800 Subject: [PATCH 092/112] Update HtmlFormatter.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 简化网页格式化程序,并修复格式化图片只格式了第一张的问题。 --- .../java/io/legado/app/utils/HtmlFormatter.kt | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 5a69f373e..626bbf70c 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -2,55 +2,49 @@ package io.legado.app.utils import io.legado.app.model.analyzeRule.AnalyzeUrl import java.net.URL -import java.util.regex.Pattern +import io.legado.app.constant.AppPattern object HtmlFormatter { private val wrapHtmlRegex = "]*>".toRegex() private val notImgHtmlRegex = "])[^<>]*>".toRegex() private val otherHtmlRegex = "])[^<>]*>".toRegex() - private val imgPattern = Pattern.compile("]*src=.*?\"(.*?(?:,\\{.*\\})?)\".*?>") - fun format(html: String?): String { + fun format(html: String?, otherRegex: Regex = otherHtmlRegex): String { html ?: return "" return html.replace(wrapHtmlRegex, "\n") - .replace(otherHtmlRegex, "") - .replace("\\s*\\n+\\s*".toRegex(), "\n  ") + .replace(otherRegex, "") .replace("^[\\n\\s]+".toRegex(), "  ") .replace("[\\n\\s]+$".toRegex(), "") - } - - fun formatKeepImg(html: String?): String { - html ?: return "" - return html.replace(wrapHtmlRegex, "\n") - .replace(notImgHtmlRegex, "") .replace("\\s*\\n+\\s*".toRegex(), "\n  ") - .replace("^[\\n\\s]+".toRegex(), "  ") - .replace("[\\n\\s]+$".toRegex(), "") } + fun formatKeepImg(html: String?) = format(html,notImgHtmlRegex) + fun formatKeepImg(html: String?, redirectUrl: URL?): String { html ?: return "" + var formatHtml = formatKeepImg(html) val sb = StringBuffer() - val matcher = imgPattern.matcher(html) var appendPos = 0 - while (matcher.find()) { - val urlArray = matcher.group(1)!!.split(AnalyzeUrl.splitUrlRegex) - var url = NetworkUtils.getAbsoluteURL(redirectUrl, urlArray[0]) - if (urlArray.size > 1) { - url = "$url,${urlArray[1]}" + while (appendPos < formatHtml.length) { + val matcher = AppPattern.imgPattern.matcher(formatHtml) + if(matcher.find()) { + val urlArray = matcher.group(1)!!.split(AnalyzeUrl.splitUrlRegex) + var url = NetworkUtils.getAbsoluteURL(redirectUrl, urlArray[0]) + if (urlArray.size > 1) { + url = "$url,${urlArray[1]}" + } + sb.append(formatHtml.substring(appendPos, matcher.start())) + sb.append("") + appendPos = matcher.end() + formatHtml = formatHtml.substring(appendPos, formatHtml.length) + appendPos = 0 + } else { + sb.append(formatHtml) + appendPos = formatHtml.length } - sb.append(html.substring(appendPos, matcher.start())) - sb.append("") - appendPos = matcher.end() - } - if (appendPos < html.length) { - sb.append(html.substring(appendPos, html.length)) } - return sb.replace(wrapHtmlRegex, "\n") - .replace(notImgHtmlRegex, "") - .replace("\\s*\\n+\\s*".toRegex(), "\n  ") - .replace("^[\\n\\s]+".toRegex(), "  ") - .replace("[\\n\\s]+$".toRegex(), "") + + return sb.toString() } -} \ No newline at end of file +} From 13bf96c475cc24f0a7cb1730f8cb461fc69fa821 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 13 Jun 2021 23:30:40 +0800 Subject: [PATCH 093/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/book/toc/TocActivity.kt | 31 +++++++++++-------- .../main/res/layout/activity_chapter_list.xml | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt index 7152219a0..e9138501d 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/TocActivity.kt @@ -1,3 +1,5 @@ +@file:Suppress("DEPRECATION") + package io.legado.app.ui.book.toc import android.content.Intent @@ -8,9 +10,8 @@ import androidx.activity.viewModels import androidx.appcompat.widget.SearchView import androidx.core.view.isGone import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.fragment.app.FragmentPagerAdapter import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayoutMediator import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.databinding.ActivityChapterListBinding @@ -37,12 +38,7 @@ class TocActivity : VMBaseActivity() { tabLayout.isTabIndicatorFullWidth = false tabLayout.setSelectedTabIndicatorColor(accentColor) binding.viewPager.adapter = TabFragmentPageAdapter() - TabLayoutMediator(tabLayout, binding.viewPager) { tab, position -> - when (position) { - 0 -> tab.setText(R.string.chapter_list) - else -> tab.setText(R.string.bookmark) - } - }.attach() + tabLayout.setupWithViewPager(binding.viewPager) intent.getStringExtra("bookUrl")?.let { viewModel.initBook(it) } @@ -98,16 +94,25 @@ class TocActivity : VMBaseActivity() { } } - private inner class TabFragmentPageAdapter : FragmentStateAdapter(this) { + @Suppress("DEPRECATION") + private inner class TabFragmentPageAdapter : + FragmentPagerAdapter(supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { + + override fun getItem(position: Int): Fragment { + return when (position) { + 1 -> BookmarkFragment() + else -> ChapterListFragment() + } + } - override fun getItemCount(): Int { + override fun getCount(): Int { return 2 } - override fun createFragment(position: Int): Fragment { + override fun getPageTitle(position: Int): CharSequence { return when (position) { - 0 -> ChapterListFragment() - else -> BookmarkFragment() + 1 -> getString(R.string.bookmark) + else -> getString(R.string.chapter_list) } } diff --git a/app/src/main/res/layout/activity_chapter_list.xml b/app/src/main/res/layout/activity_chapter_list.xml index ff03b765e..c78973583 100644 --- a/app/src/main/res/layout/activity_chapter_list.xml +++ b/app/src/main/res/layout/activity_chapter_list.xml @@ -12,7 +12,7 @@ app:contentInsetStartWithNavigation="0dp" app:contentLayout="@layout/view_tab_layout" /> - From 8282386f623bb757ea019c65bb0f32028a9d03fc Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 13 Jun 2021 23:56:33 +0800 Subject: [PATCH 094/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/rss/article/RssSortActivity.kt | 47 ++++++++----------- .../main/res/layout/activity_rss_artivles.xml | 2 +- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt index eb3f34b9f..81947f41b 100644 --- a/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt @@ -1,3 +1,5 @@ +@file:Suppress("DEPRECATION") + package io.legado.app.ui.rss.article import android.content.Intent @@ -7,8 +9,7 @@ import android.view.MenuItem import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.google.android.material.tabs.TabLayoutMediator +import androidx.fragment.app.FragmentStatePagerAdapter import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.databinding.ActivityRssArtivlesBinding @@ -20,9 +21,8 @@ class RssSortActivity : VMBaseActivity() private lateinit var adapter: TabFragmentPageAdapter - private val fragmentMap = hashMapOf() + private val fragments = linkedMapOf() private val upSourceResult = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { @@ -40,9 +40,7 @@ class RssSortActivity : VMBaseActivity - tab.text = sorts.keys.elementAt(position) - }.attach() + binding.tabLayout.setupWithViewPager(binding.viewPager) viewModel.titleLiveData.observe(this, { binding.titleBar.title = it }) @@ -78,10 +76,11 @@ class RssSortActivity : VMBaseActivity - From 92597a82d3760e965dab75f7e0c378700c7b54d7 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Mon, 14 Jun 2021 02:06:52 +0800 Subject: [PATCH 095/112] Update HtmlFormatter.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 精简图片格式化程序,提供执行效率,改重复匹配为依次查找 --- .../java/io/legado/app/utils/HtmlFormatter.kt | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 626bbf70c..fd3c6c269 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -24,25 +24,18 @@ object HtmlFormatter { html ?: return "" var formatHtml = formatKeepImg(html) val sb = StringBuffer() - var appendPos = 0 - while (appendPos < formatHtml.length) { - val matcher = AppPattern.imgPattern.matcher(formatHtml) - if(matcher.find()) { - val urlArray = matcher.group(1)!!.split(AnalyzeUrl.splitUrlRegex) - var url = NetworkUtils.getAbsoluteURL(redirectUrl, urlArray[0]) - if (urlArray.size > 1) { - url = "$url,${urlArray[1]}" - } - sb.append(formatHtml.substring(appendPos, matcher.start())) - sb.append("") - appendPos = matcher.end() - formatHtml = formatHtml.substring(appendPos, formatHtml.length) - appendPos = 0 - } else { - sb.append(formatHtml) - appendPos = formatHtml.length + val matcher = AppPattern.imgPattern.matcher(formatHtml) + while (matcher.find()) { + val urlArray = matcher.group(1)!!.split(AnalyzeUrl.splitUrlRegex) + var url = NetworkUtils.getAbsoluteURL(redirectUrl, urlArray[0]) + if (urlArray.size > 1) { + url = "$url,${urlArray[1]}" } + //将Matcher上次匹配结尾到本次匹配结尾这段字符串序列追加到sb中,且是先将其中匹配到的部分替换后再追加 + matcher.appendReplacement(sb, "") } + //将Matcher最后那个匹配之后的字串匹配到追加到sb中 + matcher.appendTail(sb) return sb.toString() } From 0b0ca0c6cf51f241d30990f0b68ad85802c5e75f Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Jun 2021 09:09:33 +0800 Subject: [PATCH 096/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/updateLog.md | 4 ++++ .../java/io/legado/app/utils/HtmlFormatter.kt | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 989c3b244..291a32ef1 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -8,6 +8,10 @@ * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则出现问题。先关闭替换净化并刷新,再观察是否正常。如果正常说明净化规则存在误杀,如果关闭后仍然出现相关问题,请点击源链接查看原文与正文是否相同,如果不同,再进行反馈。 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! +**2021/06/15** +* 添加配置导入文件规则功能 by bushixuanqi +* 文件夹分组样式优化(未完成) + **2021/06/06** * 添加自定义导出文件名 * 添加书架文件夹分组样式,未完成 diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index fd3c6c269..2f4e126dc 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -1,8 +1,8 @@ package io.legado.app.utils +import io.legado.app.constant.AppPattern import io.legado.app.model.analyzeRule.AnalyzeUrl import java.net.URL -import io.legado.app.constant.AppPattern object HtmlFormatter { private val wrapHtmlRegex = "]*>".toRegex() @@ -22,22 +22,23 @@ object HtmlFormatter { fun formatKeepImg(html: String?, redirectUrl: URL?): String { html ?: return "" - var formatHtml = formatKeepImg(html) val sb = StringBuffer() - val matcher = AppPattern.imgPattern.matcher(formatHtml) + val matcher = AppPattern.imgPattern.matcher(html) + var appendPos = 0 while (matcher.find()) { val urlArray = matcher.group(1)!!.split(AnalyzeUrl.splitUrlRegex) var url = NetworkUtils.getAbsoluteURL(redirectUrl, urlArray[0]) if (urlArray.size > 1) { url = "$url,${urlArray[1]}" } - //将Matcher上次匹配结尾到本次匹配结尾这段字符串序列追加到sb中,且是先将其中匹配到的部分替换后再追加 - matcher.appendReplacement(sb, "") + sb.append(html.substring(appendPos, matcher.start())) + sb.append("") + appendPos = matcher.end() } - //将Matcher最后那个匹配之后的字串匹配到追加到sb中 - matcher.appendTail(sb) - - return sb.toString() + if (appendPos < html.length) { + sb.append(html.substring(appendPos, html.length)) + } + return formatKeepImg(sb.toString()) } } From 7203a948a572d6a86118dee5a188369d00785aac Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Jun 2021 09:25:35 +0800 Subject: [PATCH 097/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/utils/HtmlFormatter.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 2f4e126dc..77eb6ad2a 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -18,12 +18,12 @@ object HtmlFormatter { .replace("\\s*\\n+\\s*".toRegex(), "\n  ") } - fun formatKeepImg(html: String?) = format(html,notImgHtmlRegex) + fun formatKeepImg(html: String?) = format(html, notImgHtmlRegex) fun formatKeepImg(html: String?, redirectUrl: URL?): String { html ?: return "" val sb = StringBuffer() - val matcher = AppPattern.imgPattern.matcher(html) + val matcher = AppPattern.imgPattern.matcher(formatKeepImg(html)) var appendPos = 0 while (matcher.find()) { val urlArray = matcher.group(1)!!.split(AnalyzeUrl.splitUrlRegex) @@ -38,7 +38,7 @@ object HtmlFormatter { if (appendPos < html.length) { sb.append(html.substring(appendPos, html.length)) } - return formatKeepImg(sb.toString()) + return sb.toString() } } From 47106522bc2c02ee435b4cdc2b90a27ce5490587 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Jun 2021 09:33:15 +0800 Subject: [PATCH 098/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../legado/app/model/localBook/LocalBook.kt | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index 1c0a6a906..aa0875e48 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -2,19 +2,19 @@ package io.legado.app.model.localBook import android.net.Uri import androidx.documentfile.provider.DocumentFile +import io.legado.app.constant.AppConst +import io.legado.app.constant.AppPattern import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter +import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp import io.legado.app.utils.* import splitties.init.appCtx import java.io.File +import java.util.regex.Matcher import java.util.regex.Pattern -import io.legado.app.constant.AppConst -import io.legado.app.constant.AppPattern -import io.legado.app.help.AppConfig import javax.script.SimpleBindings -import java.util.regex.Matcher object LocalBook { private const val folderName = "bookTxt" @@ -74,39 +74,39 @@ object LocalBook { .compile("(^)(.+) by (.+)$") .matcher(fileName) - (m1.takeIf { m1.find() } - ?: m2.takeIf { m2.find() } - ). run{ - - if(this is Matcher) { + (m1.takeIf { m1.find() } ?: m2.takeIf { m2.find() }).run { - //按默认格式将文件名分解成书名、作者名 - name = group(2)!! - author = BookHelp.formatBookAuthor((group(1) ?: "") + (group(3) ?: "")) + if (this is Matcher) { - } else if(!AppConfig.bookImportFileName.isNullOrBlank()) { + //按默认格式将文件名分解成书名、作者名 + name = group(2)!! + author = BookHelp.formatBookAuthor((group(1) ?: "") + (group(3) ?: "")) - //在脚本中定义如何分解文件名成书名、作者名 - val bindings = SimpleBindings() - bindings["src"] = fileName - val jsonStr = AppConst.SCRIPT_ENGINE.eval( + } else if (!AppConfig.bookImportFileName.isNullOrBlank()) { - //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获,未定义则赋值为空字符串 - AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})" - , bindings).toString() - val bookMess =GSON.fromJsonObject>(jsonStr) ?: HashMap() - name = bookMess["name"]?: fileName - author = bookMess["author"]?.takeIf { it.length != fileName.length } ?: "" + //在脚本中定义如何分解文件名成书名、作者名 + val bindings = SimpleBindings() + bindings["src"] = fileName + val jsonStr = AppConst.SCRIPT_ENGINE.eval( - } else { + //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获,未定义则赋值为空字符串 + AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})", + bindings + ).toString() + val bookMess = GSON.fromJsonObject>(jsonStr) ?: HashMap() + name = bookMess["name"] ?: fileName + author = bookMess["author"]?.takeIf { it.length != fileName.length } ?: "" - name = fileName.replace(AppPattern.nameRegex,"") - author = fileName.replace(AppPattern.authorRegex,"").takeIf { it.length != fileName.length } ?: "" + } else { - } + name = fileName.replace(AppPattern.nameRegex, "") + author = fileName.replace(AppPattern.authorRegex, "") + .takeIf { it.length != fileName.length } ?: "" } + } + val book = Book( bookUrl = path, name = name, @@ -129,10 +129,10 @@ object LocalBook { val bookFile = FileUtils.getFile(cacheFolder, book.originName) bookFile.delete() } - if(book.isEpub()){ - val bookFile=BookHelp.getEpubFile(book).parentFile - if (bookFile!=null&&bookFile.exists()){ - FileUtils.delete(bookFile,true) + if (book.isEpub()) { + val bookFile = BookHelp.getEpubFile(book).parentFile + if (bookFile != null && bookFile.exists()) { + FileUtils.delete(bookFile, true) } } From dd08dd2e14d3dc9479937ba799a64cbe4af154e8 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Tue, 15 Jun 2021 13:28:47 +0800 Subject: [PATCH 099/112] Update LocalBook.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 简化 --- .../main/java/io/legado/app/model/localBook/LocalBook.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index aa0875e48..980b39395 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -85,13 +85,13 @@ object LocalBook { } else if (!AppConfig.bookImportFileName.isNullOrBlank()) { //在脚本中定义如何分解文件名成书名、作者名 - val bindings = SimpleBindings() - bindings["src"] = fileName val jsonStr = AppConst.SCRIPT_ENGINE.eval( - //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获,未定义则赋值为空字符串 + //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获 AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})", - bindings + + //将文件名注入到脚步的src变量中 + SimpleBindings().also{ it["src"] = fileName } ).toString() val bookMess = GSON.fromJsonObject>(jsonStr) ?: HashMap() name = bookMess["name"] ?: fileName From 0e79e92c84de51e65967e32882dae621cb6bccb8 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Jun 2021 14:11:25 +0800 Subject: [PATCH 100/112] =?UTF-8?q?=E6=8C=89=E9=94=AE=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=87=8D=E7=BD=AE=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/config/PageKeyDialog.kt | 21 ++++++--- .../bookshelf/style2/BookshelfFragment2.kt | 8 ++-- app/src/main/res/layout/dialog_page_key.xml | 47 +++++++++++++++---- app/src/main/res/values-ja-rJP/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-zh-rHK/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 9 files changed, 61 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/PageKeyDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/PageKeyDialog.kt index 092134e77..1d85a5963 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/PageKeyDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/PageKeyDialog.kt @@ -10,6 +10,7 @@ import io.legado.app.lib.theme.backgroundColor import io.legado.app.utils.getPrefString import io.legado.app.utils.hideSoftInput import io.legado.app.utils.putPrefString +import splitties.views.onClick class PageKeyDialog(context: Context) : Dialog(context, R.style.AppTheme_AlertDialog) { @@ -18,13 +19,19 @@ class PageKeyDialog(context: Context) : Dialog(context, R.style.AppTheme_AlertDi init { setContentView(binding.root) - binding.contentView.setBackgroundColor(context.backgroundColor) - binding.etPrev.setText(context.getPrefString(PreferKey.prevKeys)) - binding.etNext.setText(context.getPrefString(PreferKey.nextKeys)) - binding.tvOk.setOnClickListener { - context.putPrefString(PreferKey.prevKeys, binding.etPrev.text?.toString()) - context.putPrefString(PreferKey.nextKeys, binding.etNext.text?.toString()) - dismiss() + binding.run { + contentView.setBackgroundColor(context.backgroundColor) + etPrev.setText(context.getPrefString(PreferKey.prevKeys)) + etNext.setText(context.getPrefString(PreferKey.nextKeys)) + tvReset.onClick { + etPrev.setText("") + etNext.setText("") + } + tvOk.setOnClickListener { + context.putPrefString(PreferKey.prevKeys, etPrev.text?.toString()) + context.putPrefString(PreferKey.nextKeys, etNext.text?.toString()) + dismiss() + } } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt index 6b81eafb7..1676710eb 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt @@ -121,11 +121,9 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), bookGroupLiveData?.removeObservers(this) bookGroupLiveData = appDb.bookGroupDao.liveDataShow().apply { observe(viewLifecycleOwner) { - if (it.size != bookGroups.size) { + if (it != bookGroups) { bookGroups = it booksAdapter.notifyDataSetChanged() - } else { - } } } @@ -183,7 +181,8 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), 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) { @@ -200,6 +199,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1), override fun onItemLongClick(position: Int) { if (position < bookGroups.size) { + val bookGroup = bookGroups[position] } else { val book = books[position - bookGroups.size] diff --git a/app/src/main/res/layout/dialog_page_key.xml b/app/src/main/res/layout/dialog_page_key.xml index 8c5b7a1fa..bca82ea91 100644 --- a/app/src/main/res/layout/dialog_page_key.xml +++ b/app/src/main/res/layout/dialog_page_key.xml @@ -51,17 +51,44 @@ android:textColor="@color/secondaryText" android:text="@string/page_key_set_help" /> - + android:orientation="horizontal"> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 0b7147b23..5a1faaab2 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -844,4 +844,5 @@ 样式 分组样式 导出文件名 + 重置 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index af54188db..b63473b0c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -844,4 +844,5 @@ Formato Formato de grupo Exportar o nome do arquivo + 重置 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 6114e1dfc..c9541a334 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -842,5 +842,6 @@ 样式 分组样式 导出文件名 + 重置 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 27608165e..66690b74c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -843,5 +843,6 @@ 样式 分组样式 导出文件名 + 重置 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 5d74d9a46..011f56181 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -843,5 +843,6 @@ 样式 分组样式 导出文件名 + 重置 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3070fd2c8..959b1b77c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -844,5 +844,6 @@ Style Group style Export file name + Reset From 3ca9015de979038b32f282751c8ad7a8c9f040cd Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Jun 2021 14:13:36 +0800 Subject: [PATCH 101/112] edit updateLog.md --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 291a32ef1..0daafa351 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -9,6 +9,7 @@ * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! **2021/06/15** +* viewPager2 改回 viewPager * 添加配置导入文件规则功能 by bushixuanqi * 文件夹分组样式优化(未完成) From 4170301b4b4a1008b481c627abe54107778d9823 Mon Sep 17 00:00:00 2001 From: david082321 Date: Tue, 15 Jun 2021 15:21:24 +0800 Subject: [PATCH 102/112] Update strings.xml --- app/src/main/res/values-zh-rTW/strings.xml | 38 +++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 66690b74c..23ed85bc9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -5,7 +5,7 @@ 閱讀需要存取記憶卡權限,請前往「設定」—「應用程式權限」—打開所需權限 - Home + 備份 復原 匯入閱讀資料 建立子資料夾 @@ -17,7 +17,7 @@ 匯入舊版資料 匯入Github資料 淨化取代 - Send + 傳送 提示 取消 @@ -581,7 +581,7 @@ 匯出 載入目錄 載入詳情頁 - TTS + 文字轉語音 WebDav 密碼 輸入你的WebDav授權密碼 輸入你的伺服器地址 @@ -694,7 +694,7 @@ 切換布局 文章字重轉換 全面屏手勢最佳化 - + 主色調 強調色 @@ -754,7 +754,7 @@ 語言 匯入訂閱源 您的支援是我更新的動力 - 公眾號[开源阅读軟體] + 公眾號[开源阅读软件] 閱讀記錄 閱讀時間記錄 本機TTS @@ -781,7 +781,7 @@ 樣式名稱: 點擊右上角資料夾圖示,選擇資料夾 智慧掃描 - 導入文件名 + 匯入檔案名 複製書籍URL 複製目錄URL 沒有書籍 @@ -827,22 +827,22 @@ 此url已訂閱 高刷 使用螢幕最高刷新率 - 导出所有 + 匯出所有 完成 - 显示未读标志 - 总是使用默认封面 - 总是显示默认封面,不显示网络封面 + 顯示未讀標誌 + 總是使用預設封面 + 總是顯示預設封面,不顯示網路封面 字號 上邊距 下邊距 - 显示 - 隐藏 - 状态栏显示时隐藏 - 反转目录 - 显示发现 - 样式 - 分组样式 - 导出文件名 - 重置 + 顯示 + 隱藏 + 狀態欄顯示時隱藏 + 反轉目錄 + 顯示發現 + 樣式 + 分組樣式 + 匯出檔案名 + 重設 From 952581db056d8edece4b6007cb094262e3657eb7 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Jun 2021 15:39:53 +0800 Subject: [PATCH 103/112] =?UTF-8?q?=E4=BF=AE=E5=A4=8DhtmlFormatter?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/utils/HtmlFormatter.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 77eb6ad2a..21d45d62c 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -22,8 +22,9 @@ object HtmlFormatter { fun formatKeepImg(html: String?, redirectUrl: URL?): String { html ?: return "" + val keepImgHtml = formatKeepImg(html) val sb = StringBuffer() - val matcher = AppPattern.imgPattern.matcher(formatKeepImg(html)) + val matcher = AppPattern.imgPattern.matcher(keepImgHtml) var appendPos = 0 while (matcher.find()) { val urlArray = matcher.group(1)!!.split(AnalyzeUrl.splitUrlRegex) @@ -31,12 +32,12 @@ object HtmlFormatter { if (urlArray.size > 1) { url = "$url,${urlArray[1]}" } - sb.append(html.substring(appendPos, matcher.start())) + sb.append(keepImgHtml.substring(appendPos, matcher.start())) sb.append("") appendPos = matcher.end() } - if (appendPos < html.length) { - sb.append(html.substring(appendPos, html.length)) + if (appendPos < keepImgHtml.length) { + sb.append(keepImgHtml.substring(appendPos, keepImgHtml.length)) } return sb.toString() } From 7da9ff6912bd11e65cd46506da0e332421ffdd23 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Jun 2021 16:06:10 +0800 Subject: [PATCH 104/112] =?UTF-8?q?=E4=BF=AE=E5=A4=8DhtmlFormatter?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/constant/AppPattern.kt | 2 +- .../io/legado/app/ui/book/read/page/provider/ChapterProvider.kt | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index 6f5113100..cea60d9c5 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -9,7 +9,7 @@ object AppPattern { val EXP_PATTERN: Pattern = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}") //图片有data-开头的数据属性时优先用数据属性作为src,没有数据属性时才匹配src val imgPattern: Pattern = - Pattern.compile("]*data-)src|(?=[^>]*data-)[^>]*data-[^= ]+) *= *\"([^\"{]+(?:\\{(?:[^{}]|\\{[^{}]*\\})*\\})?)\"[^>]*>", Pattern.CASE_INSENSITIVE) + Pattern.compile("", Pattern.CASE_INSENSITIVE) val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著") val authorRegex = Regex("^.*?作\\s*者[::\\s]*|\\s+著") diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt index 4f0596f42..fad8cc606 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt @@ -118,12 +118,10 @@ object ChapterProvider { val matcher = AppPattern.imgPattern.matcher(text) if (matcher.find()) { matcher.group(1)?.let { src -> - //if (!book.isEpub()) { durY = setTypeImage( book, bookChapter, src, durY, textPages, book.getImageStyle() ) - //} } } else { val isTitle = index == 0 From 4a74b96c220b9fb37a33f6194b2f7254a94fe1e4 Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Tue, 15 Jun 2021 16:16:17 +0800 Subject: [PATCH 105/112] Update AppPattern.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复上次修改时,忘记在src前加[^>]*,导致没有数据属性的图片标签没被匹配到的问题。 --- app/src/main/java/io/legado/app/constant/AppPattern.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index cea60d9c5..027b68556 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -9,7 +9,7 @@ object AppPattern { val EXP_PATTERN: Pattern = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}") //图片有data-开头的数据属性时优先用数据属性作为src,没有数据属性时才匹配src val imgPattern: Pattern = - Pattern.compile("", Pattern.CASE_INSENSITIVE) + Pattern.compile("]*data-)[^>]*src|(?=[^>]*data-)[^>]*data-[^= ]+) *= *\"([^\"{]+(?:\\{(?:[^{}]|\\{[^{}]*\\})*\\})?)\"[^>]*>", Pattern.CASE_INSENSITIVE) val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著") val authorRegex = Regex("^.*?作\\s*者[::\\s]*|\\s+著") From 8bff54309dbe686ad2bd9e777a1b6b2b022fd9db Mon Sep 17 00:00:00 2001 From: bushixuanqi <57338301+bushixuanqi@users.noreply.github.com> Date: Tue, 15 Jun 2021 16:24:44 +0800 Subject: [PATCH 106/112] Update AppPattern.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 去掉正则中一个无用的前视断言,加快执行速度 --- app/src/main/java/io/legado/app/constant/AppPattern.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index 027b68556..450347735 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -9,7 +9,7 @@ object AppPattern { val EXP_PATTERN: Pattern = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}") //图片有data-开头的数据属性时优先用数据属性作为src,没有数据属性时才匹配src val imgPattern: Pattern = - Pattern.compile("]*data-)[^>]*src|(?=[^>]*data-)[^>]*data-[^= ]+) *= *\"([^\"{]+(?:\\{(?:[^{}]|\\{[^{}]*\\})*\\})?)\"[^>]*>", Pattern.CASE_INSENSITIVE) + Pattern.compile("]*data-)[^>]*src|[^>]*data-)[^=]*= *\"([^\"{]+(?:\\{(?:[^{}]|\\{[^{}]*\\})*\\})?)\"[^>]*>", Pattern.CASE_INSENSITIVE) val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著") val authorRegex = Regex("^.*?作\\s*者[::\\s]*|\\s+著") From 545a312e5570cfa54bf9e4c9d7fe33cdf83a0cab Mon Sep 17 00:00:00 2001 From: Celeter Date: Tue, 15 Jun 2021 16:34:48 +0800 Subject: [PATCH 107/112] =?UTF-8?q?epub=E6=94=AF=E6=8C=81=E5=A4=96?= =?UTF-8?q?=E9=83=A8=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/epub/chapter.html | 17 + app/src/main/assets/epub/cover.html | 22 + app/src/main/assets/epub/fonts.css | 267 +++++++++ app/src/main/assets/epub/intro.html | 12 + app/src/main/assets/epub/logo.png | Bin 0 -> 94214 bytes app/src/main/assets/epub/main.css | 551 ++++++++++++++++++ .../app/ui/book/cache/CacheViewModel.kt | 226 +++++-- .../main/java/me/ag2s/epublib/Constants.java | 1 + .../me/ag2s/epublib/domain/LazyResource.java | 11 +- .../java/me/ag2s/epublib/domain/Resource.java | 12 + .../me/ag2s/epublib/domain/Resources.java | 4 +- .../epublib/epub/PackageDocumentBase.java | 3 +- .../epub/PackageDocumentMetadataWriter.java | 9 + .../me/ag2s/epublib/util/ResourceUtil.java | 71 +-- .../java/me/ag2s/epublib/util/StringUtil.java | 32 +- 15 files changed, 1115 insertions(+), 123 deletions(-) create mode 100644 app/src/main/assets/epub/chapter.html create mode 100644 app/src/main/assets/epub/cover.html create mode 100644 app/src/main/assets/epub/fonts.css create mode 100644 app/src/main/assets/epub/intro.html create mode 100644 app/src/main/assets/epub/logo.png create mode 100644 app/src/main/assets/epub/main.css diff --git a/app/src/main/assets/epub/chapter.html b/app/src/main/assets/epub/chapter.html new file mode 100644 index 000000000..57b8bce01 --- /dev/null +++ b/app/src/main/assets/epub/chapter.html @@ -0,0 +1,17 @@ + + + + + Chapter + + + + + +

{title}

+{content} + + \ No newline at end of file diff --git a/app/src/main/assets/epub/cover.html b/app/src/main/assets/epub/cover.html new file mode 100644 index 000000000..268cc3f85 --- /dev/null +++ b/app/src/main/assets/epub/cover.html @@ -0,0 +1,22 @@ + + + + + Cover + + + +
+

{name}

+
{author} / 著
+ + \ No newline at end of file diff --git a/app/src/main/assets/epub/fonts.css b/app/src/main/assets/epub/fonts.css new file mode 100644 index 000000000..3457a6511 --- /dev/null +++ b/app/src/main/assets/epub/fonts.css @@ -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"); +} \ No newline at end of file diff --git a/app/src/main/assets/epub/intro.html b/app/src/main/assets/epub/intro.html new file mode 100644 index 000000000..85eb28a63 --- /dev/null +++ b/app/src/main/assets/epub/intro.html @@ -0,0 +1,12 @@ + + + + + Intro + + + + +

内容简介

{intro} + diff --git a/app/src/main/assets/epub/logo.png b/app/src/main/assets/epub/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..104d1777c7381bfee781e0218c13063c464d34aa GIT binary patch literal 94214 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 `M2&xJi#@?`AvI@f*Ga6 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�XWgc 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 Unit) { val useReplace = AppConfig.exportUseReplace val contentProcessor = ContentProcessor(book.name, book.origin) - append("${book.name}\n${context.getString(R.string.author_show, book.author)}") + append( + "${book.name}\n${ + context.getString( + R.string.author_show, + book.getRealAuthor() + ) + }\n${ + context.getString( + R.string.intro_show, + "\n" + HtmlFormatter.format(book.getDisplayIntro()) + ) + }" + ) appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter -> BookHelp.getContent(book, chapter).let { content -> val content1 = contentProcessor - .getContent(book, chapter.title, content ?: "null", false, useReplace) + .getContent( + book, + chapter.title.replace("\\r?\\n".toRegex(), " "), + content ?: "null", + false, + useReplace + ) .joinToString("\n") append.invoke("\n\n$content1") } @@ -138,9 +157,9 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter -> BookHelp.getContent(book, chapter)?.let { content -> content.split("\n").forEachIndexed { index, text -> - val matches = AppPattern.imgPattern.toRegex().findAll(input = text) - matches.forEach { matchResult -> - matchResult.groupValues[1].let { + val matcher = AppPattern.imgPattern.matcher(text) + while (matcher.find()) { + matcher.group(1)?.let { val src = NetworkUtils.getAbsoluteURL(chapter.url, it) srcList.add(Triple(chapter.title, index, src)) } @@ -181,12 +200,11 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { setEpubMetadata(book, epubBook) //set cover setCover(book, epubBook) - //set css - setCSS(epubBook) - //设置正文 - setEpubContent(book, epubBook) + val contentModel = setAssets(doc, book, epubBook) + //设置正文 + setEpubContent(contentModel, book, epubBook) DocumentUtils.createFileIfNotExist(doc, filename)?.let { bookDoc -> context.contentResolver.openOutputStream(bookDoc.uri, "wa")?.use { bookOs -> EpubWriter().write(epubBook, bookOs) @@ -205,28 +223,144 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { //set cover setCover(book, epubBook) //set css - setCSS(epubBook) - + val contentModel = setAssets(book, epubBook) val bookPath = FileUtils.getPath(file, filename) val bookFile = FileUtils.createFileWithReplace(bookPath) //设置正文 - setEpubContent(book, epubBook) + setEpubContent(contentModel, book, epubBook) EpubWriter().write(epubBook, FileOutputStream(bookFile)) } - private fun setCSS(epubBook: EpubBook) { - //set css + private fun setAssets(doc: DocumentFile, book: Book, epubBook: EpubBook): String { + if(!AppConfig.isGooglePlay) return setAssets(book, epubBook) + + var contentModel = "" + DocumentUtils.getDirDocument(doc, "Asset").let { customPath -> + if (customPath == null) {//使用内置模板 + contentModel = setAssets(book, epubBook) + } else {//外部模板 + customPath.listFiles().forEach { folder -> + if (folder.isDirectory && folder.name == "Text") { + folder.listFiles().sortedWith { o1, o2 -> + val name1 = o1.name ?: "" + val name2 = o2.name ?: "" + name1.cnCompare(name2) + }.forEach { file -> + if (file.isFile) { + when { + //正文模板 + file.name.equals( + "chapter.html", + true + ) || file.name.equals("chapter.xhtml", true) -> { + contentModel = file.readText(context) ?: "" + } + //封面等其他模板 + true == file.name?.endsWith("html", true) -> { + epubBook.addSection( + FileUtils.getNameExcludeExtension( + file.name ?: "Cover.html" + ), + ResourceUtil.createPublicResource( + book.name, + book.getRealAuthor(), + book.getDisplayIntro(), + book.kind, + book.wordCount, + file.readText(context) ?: "", + "${folder.name}/${file.name}" + ) + ) + } + else -> { + //其他格式文件当做资源文件 + folder.listFiles().forEach { + if (it.isFile) + epubBook.resources.add( + Resource( + it.readBytes(context), + "${folder.name}/${it.name}" + ) + ) + } + } + } + } + } + } else if (folder.isDirectory) { + //资源文件 + folder.listFiles().forEach { + if (it.isFile) + epubBook.resources.add( + Resource( + it.readBytes(context), + "${folder.name}/${it.name}" + ) + ) + } + } else {//Asset下面的资源文件 + epubBook.resources.add( + Resource( + folder.readBytes(context), + "${folder.name}" + ) + ) + } + } + } + } + + return contentModel + } + + private fun setAssets(book: Book, epubBook: EpubBook): String { + epubBook.resources.add( + Resource( + appCtx.assets.open("epub/fonts.css").readBytes(), + "Styles/fonts.css" + ) + ) + epubBook.resources.add( + Resource( + appCtx.assets.open("epub/main.css").readBytes(), + "Styles/main.css" + ) + ) epubBook.resources.add( Resource( - "body,div{background:white;outline:none;width:100%;}h2{color:#005a9c;text-align:left;}p{text-indent:2em;text-align:justify;}img{display:inline-block;width:100%;height:auto;max-width: 100%;max-height:100%;}".encodeToByteArray(), - "css/style.css" + appCtx.assets.open("epub/logo.png").readBytes(), + "Images/logo.png" + ) + ) + epubBook.addSection( + context.getString(R.string.img_cover), + ResourceUtil.createPublicResource( + book.name, + book.getRealAuthor(), + book.getDisplayIntro(), + book.kind, + book.wordCount, + String(appCtx.assets.open("epub/cover.html").readBytes()), + "Text/cover.html" + ) + ) + epubBook.addSection( + context.getString(R.string.book_intro), + ResourceUtil.createPublicResource( + book.name, + book.getRealAuthor(), + book.getDisplayIntro(), + book.kind, + book.wordCount, + String(appCtx.assets.open("epub/intro.html").readBytes()), + "Text/intro.html" ) ) + return String(appCtx.assets.open("epub/chapter.html").readBytes()) } private fun setCover(book: Book, epubBook: EpubBook) { - Glide.with(context) .asBitmap() .load(book.getDisplayCover()) @@ -237,21 +371,19 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { val byteArray: ByteArray = stream.toByteArray() resource.recycle() stream.close() - epubBook.coverImage = Resource(byteArray, "cover.jpg") + epubBook.coverImage = Resource(byteArray, "Images/cover.jpg") } override fun onLoadCleared(placeholder: Drawable?) { - } - }) } - - private fun setEpubContent(book: Book, epubBook: EpubBook) { + private fun setEpubContent(contentModel: String, book: Book, epubBook: EpubBook) { + //正文 val useReplace = AppConfig.exportUseReplace val contentProcessor = ContentProcessor(book.name, book.origin) - appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter -> + appDb.bookChapterDao.getChapterList(book.bookUrl).forEachIndexed { index, chapter -> BookHelp.getContent(book, chapter).let { content -> var content1 = fixPic(epubBook, book, content ?: "null", chapter) content1 = contentProcessor @@ -259,23 +391,17 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { .joinToString("\n") epubBook.addSection( chapter.title, - ResourceUtil.createHTMLResource(chapter.title, content1) + ResourceUtil.createChapterResource( + chapter.title.replace("\uD83D\uDD12", ""), + content1, + contentModel, + "Text/chapter_${index}.html" + ) ) } } } - private fun setPic(src: String, book: Book, epubBook: EpubBook) { - - val href = "${MD5Utils.md5Encode16(src)}${BookHelp.getImageSuffix(src)}" - val vFile = BookHelp.getImage(book, src) - val fp = FileResourceProvider(vFile.parent) - if (vFile.exists()) { - val img = LazyResource(fp, href) - epubBook.resources.add(img) - } - } - private fun fixPic( epubBook: EpubBook, book: Book, @@ -285,19 +411,21 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { val data = StringBuilder("") content.split("\n").forEach { text -> var text1 = text - val matches = AppPattern.imgPattern.toRegex().findAll(input = text) - matches.forEach { matchResult -> - matchResult.groupValues[1].let { + val matcher = AppPattern.imgPattern.matcher(text) + while (matcher.find()) { + matcher.group(1)?.let { val src = NetworkUtils.getAbsoluteURL(chapter.url, it) - setPic(src, book, epubBook) - text1 = text1.replace( - src, - "${MD5Utils.md5Encode16(src)}${BookHelp.getImageSuffix(src)}" - ) - + val originalHref = "${MD5Utils.md5Encode16(src)}${BookHelp.getImageSuffix(src)}" + val href = "Images/${MD5Utils.md5Encode16(src)}.${BookHelp.getImageSuffix(src)}" + val vFile = BookHelp.getImage(book, src) + val fp = FileResourceProvider(vFile.parent) + if (vFile.exists()) { + val img = LazyResource(fp, href, originalHref) + epubBook.resources.add(img) + } + text1 = text1.replace(src, "../${href}") } } - data.append(text1).append("\n") } return data.toString() @@ -309,13 +437,11 @@ class CacheViewModel(application: Application) : BaseViewModel(application) { metadata.authors.add(Author(book.getRealAuthor()))//书籍的作者 metadata.language = "zh"//数据的语言 metadata.dates.add(Date())//数据的创建日期 - metadata.publishers.add("Legado APP")//数据的创建者 + metadata.publishers.add("Legado")//数据的创建者 metadata.descriptions.add(book.getDisplayIntro())//书籍的简介 //metadata.subjects.add("")//书籍的主题,在静读天下里面有使用这个分类书籍 epubBook.metadata = metadata } //////end of EPUB - - } \ No newline at end of file diff --git a/epublib/src/main/java/me/ag2s/epublib/Constants.java b/epublib/src/main/java/me/ag2s/epublib/Constants.java index e26c56815..d9f873e41 100644 --- a/epublib/src/main/java/me/ag2s/epublib/Constants.java +++ b/epublib/src/main/java/me/ag2s/epublib/Constants.java @@ -7,6 +7,7 @@ public interface Constants { String DOCTYPE_XHTML = ""; String NAMESPACE_XHTML = "http://www.w3.org/1999/xhtml"; String EPUB_GENERATOR_NAME = "Ag2S EpubLib"; + String EPUB_DUOKAN_NAME = "DK-SONGTI"; char FRAGMENT_SEPARATOR_CHAR = '#'; String DEFAULT_TOC_ID = "toc"; } diff --git a/epublib/src/main/java/me/ag2s/epublib/domain/LazyResource.java b/epublib/src/main/java/me/ag2s/epublib/domain/LazyResource.java index 1c833401c..a0faf070e 100644 --- a/epublib/src/main/java/me/ag2s/epublib/domain/LazyResource.java +++ b/epublib/src/main/java/me/ag2s/epublib/domain/LazyResource.java @@ -28,6 +28,9 @@ public class LazyResource extends Resource { public LazyResource(LazyResourceProvider resourceProvider, String href) { this(resourceProvider, -1, href); } + public LazyResource(LazyResourceProvider resourceProvider, String href, String originalHref) { + this(resourceProvider, -1, href, originalHref); + } /** * Creates a Lazy resource, by not actually loading the data for this entry. @@ -39,11 +42,17 @@ public class LazyResource extends Resource { * @param href The resource's href within the epub. */ public LazyResource( - LazyResourceProvider resourceProvider, long size, String href) { + LazyResourceProvider resourceProvider, long size, String href) { super(null, null, href, MediaTypes.determineMediaType(href)); this.resourceProvider = resourceProvider; this.cachedSize = size; } + public LazyResource( + LazyResourceProvider resourceProvider, long size, String href, String originalHref) { + super(null, null, href, originalHref, MediaTypes.determineMediaType(href)); + this.resourceProvider = resourceProvider; + this.cachedSize = size; + } /** * Gets the contents of the Resource as an InputStream. diff --git a/epublib/src/main/java/me/ag2s/epublib/domain/Resource.java b/epublib/src/main/java/me/ag2s/epublib/domain/Resource.java index 4820a7aff..2349c9e91 100644 --- a/epublib/src/main/java/me/ag2s/epublib/domain/Resource.java +++ b/epublib/src/main/java/me/ag2s/epublib/domain/Resource.java @@ -120,6 +120,9 @@ public class Resource implements Serializable { public Resource(String id, byte[] data, String href, MediaType mediaType) { this(id, data, href, mediaType, Constants.CHARACTER_ENCODING); } + public Resource(String id, byte[] data, String href, String originalHref, MediaType mediaType) { + this(id, data, href, originalHref, mediaType, Constants.CHARACTER_ENCODING); + } /** @@ -141,6 +144,15 @@ public class Resource implements Serializable { this.inputEncoding = inputEncoding; this.data = data; } + public Resource(String id, byte[] data, String href, String originalHref, MediaType mediaType, + String inputEncoding) { + this.id = id; + this.href = href; + this.originalHref = originalHref; + this.mediaType = mediaType; + this.inputEncoding = inputEncoding; + this.data = data; + } /** * Gets the contents of the Resource as an InputStream. diff --git a/epublib/src/main/java/me/ag2s/epublib/domain/Resources.java b/epublib/src/main/java/me/ag2s/epublib/domain/Resources.java index 7566f4f3a..27d512700 100644 --- a/epublib/src/main/java/me/ag2s/epublib/domain/Resources.java +++ b/epublib/src/main/java/me/ag2s/epublib/domain/Resources.java @@ -193,9 +193,9 @@ public class Resources implements Serializable { private String createHref(MediaType mediaType, int counter) { if (MediaTypes.isBitmapImage(mediaType)) { - return "image_" + counter + mediaType.getDefaultExtension(); + return IMAGE_PREFIX + counter + mediaType.getDefaultExtension(); } else { - return "item_" + counter + mediaType.getDefaultExtension(); + return ITEM_PREFIX + counter + mediaType.getDefaultExtension(); } } diff --git a/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java b/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java index c7ce55b78..a172ce32d 100644 --- a/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java +++ b/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentBase.java @@ -9,7 +9,7 @@ package me.ag2s.epublib.epub; */ public class PackageDocumentBase { - public static final String BOOK_ID_ID = "BookId"; + public static final String BOOK_ID_ID = "duokan-book-id"; public static final String NAMESPACE_OPF = "http://www.idpf.org/2007/opf"; public static final String NAMESPACE_DUBLIN_CORE = "http://purl.org/dc/elements/1.1/"; public static final String PREFIX_DUBLIN_CORE = "dc"; @@ -92,5 +92,6 @@ public class PackageDocumentBase { String reference_cover = "cover"; String no = "no"; String generator = "generator"; + String duokan = "duokan-body-font"; } } \ No newline at end of file diff --git a/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java b/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java index f4811a6c7..fc758bc03 100644 --- a/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java +++ b/epublib/src/main/java/me/ag2s/epublib/epub/PackageDocumentMetadataWriter.java @@ -120,6 +120,15 @@ public class PackageDocumentMetadataWriter extends PackageDocumentBase { Constants.EPUB_GENERATOR_NAME); serializer.endTag(NAMESPACE_OPF, OPFTags.meta); + // write duokan + serializer.startTag(NAMESPACE_OPF, OPFTags.meta); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.name, + OPFValues.duokan); + serializer + .attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.content, + Constants.EPUB_DUOKAN_NAME); + serializer.endTag(NAMESPACE_OPF, OPFTags.meta); + serializer.endTag(NAMESPACE_OPF, OPFTags.metadata); } diff --git a/epublib/src/main/java/me/ag2s/epublib/util/ResourceUtil.java b/epublib/src/main/java/me/ag2s/epublib/util/ResourceUtil.java index 9b8f7896e..f270bdbb3 100644 --- a/epublib/src/main/java/me/ag2s/epublib/util/ResourceUtil.java +++ b/epublib/src/main/java/me/ag2s/epublib/util/ResourceUtil.java @@ -30,60 +30,31 @@ public class ResourceUtil { /** * 快速创建HTML类型的Resource * - * @param title 章节的标题 - * @param string 章节的正文 + * @param title 章节的标题 + * @param txt 章节的正文 + * @param model html模板 * @return 返回Resource */ - public static Resource createHTMLResource(String title, String string) { - String html = createHtml(title, string); - MediaType mediaTypeProperty = MediaTypes.XHTML; - byte[] data = html.getBytes(); - return new Resource(data, mediaTypeProperty); - } - - /** - * 快速创建HTML类型的Resource - * - * @param title 章节的标题 - * @param string 章节的正文 - * @param href Resource的href - * @return 返回Resource - */ - - @SuppressWarnings("unused") - public static Resource createHTMLResource(String title, String string, String href) { - String html = createHtml(title, string); - MediaType mediaTypeProperty = MediaTypes.XHTML; - byte[] data = html.getBytes(); - return new Resource(null, data, href, mediaTypeProperty); - } - - @SuppressWarnings("unused") - private static String createHtml(String title, String txt) { - StringBuilder body = new StringBuilder(); - for (String s : txt.split("\\r?\\n")) { - //移除多余的开头结尾的空白字符,节省epub的体积 - s = StringUtil.FixTrim(s); - if (s.length() != 0) { - if (s.contains("").append(s).append(""); - } else { - body.append("

").append(s).append("

"); - } - - } - + public static Resource createChapterResource(String title, String txt, String model, String href) { + if (title.contains("\n")) { + title = "" + title.replaceFirst("\\s*\\n\\s*", "
"); + } else { + title = title.replaceFirst("\\s+", "
"); + if (title.contains("")) + title = "" + title; } + String html = model.replaceAll("\\{title\\}", title) + .replaceAll("\\{content\\}", StringUtil.formatHtml(txt)); + return new Resource(html.getBytes(), href); + } - return "" + - "" + - "" + title + "" + - "" + - "" + - "

" + title + "

" + - body + - ""; + public static Resource createPublicResource(String name, String author, String intro, String kind, String wordCount, String model, String href) { + String html = model.replaceAll("\\{name\\}", name) + .replaceAll("\\{author\\}", author) + .replaceAll("\\{kind\\}", kind) + .replaceAll("\\{wordCount\\}", wordCount) + .replaceAll("\\{intro\\}", StringUtil.formatHtml(intro)); + return new Resource(html.getBytes(), href); } /** diff --git a/epublib/src/main/java/me/ag2s/epublib/util/StringUtil.java b/epublib/src/main/java/me/ag2s/epublib/util/StringUtil.java index 929278b98..f3c0a6058 100644 --- a/epublib/src/main/java/me/ag2s/epublib/util/StringUtil.java +++ b/epublib/src/main/java/me/ag2s/epublib/util/StringUtil.java @@ -273,25 +273,19 @@ public class StringUtil { } return text.substring(cPos + 1); } - // 移除字符串首尾空字符的高效方法(利用ASCII值判断,包括全角空格) - public static String FixTrim(String s) { - if (s == null || s.isEmpty()) { - return ""; - } - int start = 0; - int len = s.length(); - int end = len - 1; - while (start < end && (s.charAt(start) <= 0x20 || s.charAt(start) == ' ')) { - ++start; - } - while (start < end && (s.charAt(end) <= 0x20 || s.charAt(end) == ' ')) { - --end; - } - if (end < len) { - ++end; - } - return (start > 0 || end < len) ? s.substring(start, end) : s; + public static String formatHtml(String text) { + StringBuilder body = new StringBuilder(); + for (String s : text.split("\\r?\\n")) { + s = s.replaceAll("^\\s+|\\s+$", ""); + if (s.length() > 0) { + if (s.toLowerCase().contains("]+)/?>", "
")); + } else { + body.append("

").append(s).append("

"); + } + } + } + return body.toString(); } - } From 9d69345023cf883b658931ca76a5d783c810cfd7 Mon Sep 17 00:00:00 2001 From: Celeter Date: Tue, 15 Jun 2021 16:35:53 +0800 Subject: [PATCH 108/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/help/ruleHelp.md | 69 ++++++++++++++++--- app/src/main/assets/updateLog.md | 12 ++++ .../java/io/legado/app/constant/AppConst.kt | 16 +++-- .../java/io/legado/app/constant/AppPattern.kt | 2 +- .../main/java/io/legado/app/help/BookHelp.kt | 2 +- .../java/io/legado/app/help/JsExtensions.kt | 22 +++--- .../main/java/io/legado/app/model/Debug.kt | 2 +- .../app/model/webBook/BookChapterList.kt | 8 +-- .../java/io/legado/app/utils/HtmlFormatter.kt | 2 +- build.gradle | 14 ++-- 10 files changed, 106 insertions(+), 43 deletions(-) diff --git a/app/src/main/assets/help/ruleHelp.md b/app/src/main/assets/help/ruleHelp.md index 4d50dde6c..01eb44eba 100644 --- a/app/src/main/assets/help/ruleHelp.md +++ b/app/src/main/assets/help/ruleHelp.md @@ -11,6 +11,12 @@ : regex规则,不可省略,只可以用在书籍列表和目录列表 ``` +* 获取登录后的cookie +``` +java.getCookie("http://baidu.com", null) => userid=1234;pwd=adbcd +java.getCookie("http://baidu.com", "userid") => 1234 +``` + * 请求头,支持http代理,socks4 socks5代理设置 ``` socks5代理 @@ -39,6 +45,49 @@ cache 变量-缓存操作类,方法见 io.legado.app.help.CacheManager chapter 变量-当前目录类,方法见 io.legado.app.data.entities.BookChapter title 变量-当前标题,String src 内容,源码 +``` + +* url添加js参数,解析url时执行,可在访问url时处理url,例 +``` +https://www.baidu.com,{"js":"java.headerMap.put('xxx', 'yyy')"} +https://www.baidu.com,{"js":"java.url=java.url+'yyyy'"} +``` + +* 增加js方法,用于重定向拦截 + * `java.get(urlStr: String, headers: Map)` + * `java.post(urlStr: String, body: String, headers: Map)` +* 对于搜索重定向的源,可以使用此方法获得重定向后的url +``` +(()=>{ + if(page==1){ + let url='https://www.yooread.net/e/search/index.php,'+JSON.stringify({ + "method":"POST", + "body":"show=title&tempid=1&keyboard="+key + }); + return java.put('surl',String(java.connect(url).raw().request().url())); + } else { + return java.get('surl')+'&page='+(page-1) + } +})() +或者 +(()=>{ + let base='https://www.yooread.net/e/search/'; + if(page==1){ + let url=base+'index.php'; + let body='show=title&tempid=1&keyboard='+key; + return base+java.put('surl',java.post(url,body,{}).header("Location")); + } else { + return base+java.get('surl')+'&page='+(page-1); + } +})() +``` + +* 正文图片链接支持修改headers +``` +let options = { +"headers": {"User-Agent": "xxxx","Referrer":baseUrl,"Cookie":"aaa=vbbb;"} +}; +'' ``` ## 部分js对象属性说明 @@ -95,14 +144,16 @@ variable // 自定义书籍变量信息(用于书源规则检索书籍信息) ### 字体解析使用 > 使用方法,在正文替换规则中使用,原理根据f1字体的字形数据到f2中查找字形对应的编码 ``` -@js: -var b64=String(src).match(/ttf;base64,([^\)]+)/); -if (b64) { - var f1 = java.queryBase64TTF(b64[1]) - var f2 = java.queryTTF("/storage/emulated/0/Fonts/Source Han Sans CN Regular.ttf") - java.replaceFont(result, f1, f2) -}else{ - result -} + +(function(){ + var b64=String(src).match(/ttf;base64,([^\)]+)/); + if(b64){ + var f1 = java.queryBase64TTF(b64[1]); + var f2 = java.queryTTF("https://alanskycn.gitee.io/teachme/assets/font/Source Han Sans CN Regular.ttf"); + return java.replaceFont(result, f1, f2); + } + return result; +})() + ``` diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 0daafa351..20e915987 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -18,6 +18,18 @@ * 添加书架文件夹分组样式,未完成 * viewPager2 3层嵌套有问题,书架换回viewPager +**2021/05/29** +* 谷歌版可使用外部epub模板 +* Asset文件夹下二级以内目录全文件读取,Asset->文件夹->文件 +* epub元数据修改,使修改字体只对正文生效 +* 修复epub模板文件的排序问题 +* epub可自定义模板,模板路径为书籍导出目录的Asset文件夹,[模板范例](https://wwa.lanzoux.com/ibjBspkn05i) +``` +Asset中里面必须有Text文件夹,Text文件夹里必须有chapter.html,否则导出正文会为空 +chapter.html的关键字有{title}、{content} +其他html文件的关键字有{name}、{author}、{intro}、{kind}、{wordCount} +``` + **2021/05/26** * 书签绑定书名与作者 * 修复详情页目录问题 diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt index a7db60a2d..e76b28df3 100644 --- a/app/src/main/java/io/legado/app/constant/AppConst.kt +++ b/app/src/main/java/io/legado/app/constant/AppConst.kt @@ -38,9 +38,9 @@ object AppConst { val keyboardToolChars: List by lazy { arrayListOf( - "❓", "@css:", "", "{{}}", "&&", "%%", "||", "//", "$.", "@", - "\\", ":", "class", "id", "href", "textNodes", "ownText", "all", "html", - "[", "]", "<", ">", "##", "!", ".", "+", "-", "*", "=",",{\"webView\":true}" + "❓", "@css:", "", "{{}}", "##", "&&", "%%", "||", "//", "$.", + "@", ":", "class", "text", "href", "textNodes", "ownText", "all", "html", + "[", "]", "<", ">", "#", "!", ".", "+", "-", "*", "=", "{'webView': true}" ) } @@ -57,10 +57,12 @@ object AppConst { val urlOption: String by lazy { """ ,{ - "charset": "", - "method": "POST", - "body": "", - "headers": {"User-Agent": ""} + 'charset': '', + 'method': 'POST', + 'body': '', + 'headers': { + 'User-Agent': '' + } } """.trimIndent() } diff --git a/app/src/main/java/io/legado/app/constant/AppPattern.kt b/app/src/main/java/io/legado/app/constant/AppPattern.kt index cea60d9c5..342f2a207 100644 --- a/app/src/main/java/io/legado/app/constant/AppPattern.kt +++ b/app/src/main/java/io/legado/app/constant/AppPattern.kt @@ -9,7 +9,7 @@ object AppPattern { val EXP_PATTERN: Pattern = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}") //图片有data-开头的数据属性时优先用数据属性作为src,没有数据属性时才匹配src val imgPattern: Pattern = - Pattern.compile("", Pattern.CASE_INSENSITIVE) + Pattern.compile("]*src\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>", Pattern.CASE_INSENSITIVE) val nameRegex = Regex("\\s+作\\s*者.*|\\s+\\S+\\s+著") val authorRegex = Regex("^.*?作\\s*者[::\\s]*|\\s+著") diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index 99adf90b1..6379d4cc3 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -38,7 +38,7 @@ object BookHelp { } /** - * 清楚已删除书的缓存 + * 清除已删除书的缓存 */ fun clearRemovedCache() { Coroutine.async { diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 43fcd60a9..a3698c76c 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -311,9 +311,9 @@ interface JsExtensions { * @param font2 正确的字体 */ fun replaceFont( - text: String, - font1: QueryTTF?, - font2: QueryTTF? + text: String, + font1: QueryTTF?, + font2: QueryTTF? ): String { if (font1 == null || font2 == null) return text val contentArray = text.toCharArray() @@ -346,7 +346,7 @@ interface JsExtensions { str: String, key: String, transformation: String, - iv: String = "" + iv: String ): ByteArray? { return EncoderUtils.decryptAES( @@ -369,7 +369,7 @@ interface JsExtensions { str: String, key: String, transformation: String, - iv: String = "" + iv: String ): String? { return aesDecodeToByteArray(str, key, transformation, iv)?.let { String(it) } } @@ -386,7 +386,7 @@ interface JsExtensions { str: String, key: String, transformation: String, - iv: String = "" + iv: String ): ByteArray? { return EncoderUtils.decryptBase64AES( data = str.encodeToByteArray(), @@ -408,7 +408,7 @@ interface JsExtensions { str: String, key: String, transformation: String, - iv: String = "" + iv: String ): String? { return aesBase64DecodeToByteArray(str, key, transformation, iv)?.let { String(it) } } @@ -422,7 +422,7 @@ interface JsExtensions { */ fun aesEncodeToByteArray( data: String, key: String, transformation: String, - iv: String = "" + iv: String ): ByteArray? { return EncoderUtils.encryptAES( data.encodeToByteArray(), @@ -441,7 +441,7 @@ interface JsExtensions { */ fun aesEncodeToString( data: String, key: String, transformation: String, - iv: String = "" + iv: String ): String? { return aesEncodeToByteArray(data, key, transformation, iv)?.let { String(it) } } @@ -455,7 +455,7 @@ interface JsExtensions { */ fun aesEncodeToBase64ByteArray( data: String, key: String, transformation: String, - iv: String = "" + iv: String ): ByteArray? { return EncoderUtils.encryptAES2Base64( data.encodeToByteArray(), @@ -474,7 +474,7 @@ interface JsExtensions { */ fun aesEncodeToBase64String( data: String, key: String, transformation: String, - iv: String = "" + iv: String ): String? { return aesEncodeToBase64ByteArray(data, key, transformation, iv)?.let { String(it) } } diff --git a/app/src/main/java/io/legado/app/model/Debug.kt b/app/src/main/java/io/legado/app/model/Debug.kt index d8e606b0f..dc6259cc4 100644 --- a/app/src/main/java/io/legado/app/model/Debug.kt +++ b/app/src/main/java/io/legado/app/model/Debug.kt @@ -187,7 +187,7 @@ object Debug { private fun infoDebug(scope: CoroutineScope, webBook: WebBook, book: Book) { if (book.tocUrl.isNotBlank()) { - log(debugSource, "目录url不为空,详情页已解析") + log(debugSource, "≡已获取目录链接,跳过详情页") log(debugSource, showTime = false) tocDebug(scope, webBook, book) return diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index 06ae294b4..24bf4981e 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt @@ -190,12 +190,12 @@ object BookChapterList { chapterList.add(bookChapter) } } - Debug.log(bookSource.bookSourceUrl, "└解析目录列表完成", log) - Debug.log(bookSource.bookSourceUrl, "┌首章名称", log) + Debug.log(bookSource.bookSourceUrl, "└目录列表解析完成", log) + Debug.log(bookSource.bookSourceUrl, "┌获取首章名称", log) Debug.log(bookSource.bookSourceUrl, "└${chapterList[0].title}", log) - Debug.log(bookSource.bookSourceUrl, "┌首章链接", log) + Debug.log(bookSource.bookSourceUrl, "┌获取首章链接", log) Debug.log(bookSource.bookSourceUrl, "└${chapterList[0].url}", log) - Debug.log(bookSource.bookSourceUrl, "┌首章信息", log) + Debug.log(bookSource.bookSourceUrl, "┌获取首章信息", log) Debug.log(bookSource.bookSourceUrl, "└${chapterList[0].tag}", log) } return ChapterData(chapterList, nextUrlList) diff --git a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt index 21d45d62c..7d11df2b4 100644 --- a/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legado/app/utils/HtmlFormatter.kt @@ -13,7 +13,7 @@ object HtmlFormatter { html ?: return "" return html.replace(wrapHtmlRegex, "\n") .replace(otherRegex, "") - .replace("^[\\n\\s]+".toRegex(), "  ") + .replace("^[\\n\\s]*".toRegex(), "  ") .replace("[\\n\\s]+$".toRegex(), "") .replace("\\s*\\n+\\s*".toRegex(), "\n  ") } diff --git a/build.gradle b/build.gradle index 894cbf12f..ae689e1a9 100644 --- a/build.gradle +++ b/build.gradle @@ -4,24 +4,22 @@ buildscript { ext.kotlin_version = '1.5.10' repositories { google() - maven { url 'https://maven.aliyun.com/repository/public/'} - maven { url "https://s3.amazonaws.com/fabric-artifacts/public" } - maven { url "https://maven.aliyun.com/repository/gradle-plugin" } - maven { url "https://plugins.gradle.org/m2/" } + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } + maven { url 'https://plugins.gradle.org/m2/' } } dependencies { classpath 'com.android.tools.build:gradle:4.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "de.timfreiheit.resourceplaceholders:placeholders:0.3" + classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.3' } } allprojects { repositories { google() - maven { url 'https://maven.aliyun.com/repository/public/' } - maven { url "https://jitpack.io" } - maven { url "https://maven.google.com/" } + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://jitpack.io' } } } From 20a8e6bc95a20e79673353ae1770eef2e2a3f5a9 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 16 Jun 2021 08:27:25 +0800 Subject: [PATCH 109/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/help/JsExtensions.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index a3698c76c..64fb8d954 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -311,9 +311,9 @@ interface JsExtensions { * @param font2 正确的字体 */ fun replaceFont( - text: String, - font1: QueryTTF?, - font2: QueryTTF? + text: String, + font1: QueryTTF?, + font2: QueryTTF? ): String { if (font1 == null || font2 == null) return text val contentArray = text.toCharArray() From 4a7cfa7d86f6056a0eef657cd6fb3249f1d5f7fd Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 16 Jun 2021 16:43:59 +0800 Subject: [PATCH 110/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/lib/webdav/WebDav.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt index 389c273a0..e09b83c46 100644 --- a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt +++ b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt @@ -7,6 +7,7 @@ import okhttp3.* import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody +import org.intellij.lang.annotations.Language import org.jsoup.Jsoup import java.io.File import java.io.InputStream @@ -19,6 +20,7 @@ import java.util.* class WebDav(urlStr: String) { companion object { // 指定返回哪些属性 + @Language("xml") private const val DIR = """ From d7a6075f0a05e060aa1af383ad542d589a006a82 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 16 Jun 2021 17:22:06 +0800 Subject: [PATCH 111/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/ui/qrcode/QrCodeFragment.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt index d279ca9eb..4539d9fc4 100644 --- a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt +++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt @@ -2,9 +2,27 @@ package io.legado.app.ui.qrcode import com.google.zxing.Result import com.king.zxing.CaptureFragment +import com.king.zxing.DecodeConfig +import com.king.zxing.DecodeFormatManager +import com.king.zxing.analyze.MultiFormatAnalyzer + class QrCodeFragment : CaptureFragment() { + override fun initCameraScan() { + super.initCameraScan() + //初始化解码配置 + val decodeConfig = DecodeConfig() + decodeConfig.setHints(DecodeFormatManager.QR_CODE_HINTS) //如果只有识别二维码的需求,这样设置效率会更高,不设置默认为DecodeFormatManager.DEFAULT_HINTS + .setFullAreaScan(true) //设置是否全区域识别,默认false + .setAreaRectRatio(0.8f) //设置识别区域比例,默认0.8,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 + .setAreaRectVerticalOffset(0).areaRectHorizontalOffset = + 0 //设置识别区域水平方向偏移量,默认为0,为0表示居中,可以为负数 + + //在启动预览之前,设置分析器,只识别二维码 + cameraScan.setAnalyzer(MultiFormatAnalyzer(decodeConfig)) //设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现 + } + override fun onScanResultCallback(result: Result?): Boolean { (activity as? QrCodeActivity)?.onScanResultCallback(result) return true From 798eed1cd7f3c370b20c14b44ca7c5050e6daa11 Mon Sep 17 00:00:00 2001 From: gedoor Date: Wed, 16 Jun 2021 17:24:03 +0800 Subject: [PATCH 112/112] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/ui/qrcode/QrCodeFragment.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt index 4539d9fc4..ba5d4269d 100644 --- a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt +++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt @@ -13,14 +13,15 @@ class QrCodeFragment : CaptureFragment() { super.initCameraScan() //初始化解码配置 val decodeConfig = DecodeConfig() - decodeConfig.setHints(DecodeFormatManager.QR_CODE_HINTS) //如果只有识别二维码的需求,这样设置效率会更高,不设置默认为DecodeFormatManager.DEFAULT_HINTS - .setFullAreaScan(true) //设置是否全区域识别,默认false - .setAreaRectRatio(0.8f) //设置识别区域比例,默认0.8,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 - .setAreaRectVerticalOffset(0).areaRectHorizontalOffset = - 0 //设置识别区域水平方向偏移量,默认为0,为0表示居中,可以为负数 + //如果只有识别二维码的需求,这样设置效率会更高,不设置默认为DecodeFormatManager.DEFAULT_HINTS + decodeConfig.hints = DecodeFormatManager.QR_CODE_HINTS + //设置是否全区域识别,默认false + decodeConfig.isFullAreaScan = true + //设置识别区域比例,默认0.8,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别 + decodeConfig.areaRectRatio = 0.8f //在启动预览之前,设置分析器,只识别二维码 - cameraScan.setAnalyzer(MultiFormatAnalyzer(decodeConfig)) //设置分析器,如果内置实现的一些分析器不满足您的需求,你也可以自定义去实现 + cameraScan.setAnalyzer(MultiFormatAnalyzer(decodeConfig)) } override fun onScanResultCallback(result: Result?): Boolean {