mirror of
https://github.com/stonega/tsacdop
synced 2025-02-10 16:40:44 +01:00
Sync with gpodder.net.
This commit is contained in:
parent
064d63350d
commit
719d9c8cc0
BIN
assets/gpodder.png
Normal file
BIN
assets/gpodder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
365
lib/generated/intl/messages_pt.dart
Normal file
365
lib/generated/intl/messages_pt.dart
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||||
|
// This is a library that provides messages for a pt locale. All the
|
||||||
|
// messages from the main program should be duplicated here with the same
|
||||||
|
// function name.
|
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file.
|
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
|
||||||
|
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
|
||||||
|
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
|
||||||
|
// ignore_for_file:unused_import, file_names
|
||||||
|
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:intl/message_lookup_by_library.dart';
|
||||||
|
|
||||||
|
final messages = new MessageLookup();
|
||||||
|
|
||||||
|
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||||
|
|
||||||
|
class MessageLookup extends MessageLookupByLibrary {
|
||||||
|
String get localeName => 'pt';
|
||||||
|
|
||||||
|
static m0(groupName, count) => "${Intl.plural(count, zero: '', one: '${count} episódio de ${groupName} adicionado à lista', other: '${count} episódios de ${groupName} adicionados à lista')}";
|
||||||
|
|
||||||
|
static m1(count) => "${Intl.plural(count, zero: '', one: '${count} episódio adicionado à lista', other: '${count} episódios adicionados à lista')}";
|
||||||
|
|
||||||
|
static m2(count) => "${Intl.plural(count, zero: 'Hoje', one: 'Há ${count} dia', other: 'Há ${count} dias')}";
|
||||||
|
|
||||||
|
static m3(count) => "${Intl.plural(count, zero: 'Nunca', one: '${count} dia', other: '${count} dias')}";
|
||||||
|
|
||||||
|
static m4(count) => "${Intl.plural(count, zero: '', one: 'Episódio', other: 'Episódios')}";
|
||||||
|
|
||||||
|
static m5(time) => "De ${time}";
|
||||||
|
|
||||||
|
static m6(count) => "${Intl.plural(count, zero: 'Grupo', one: 'Grupo', other: 'Grupos')}";
|
||||||
|
|
||||||
|
static m7(host) => "Hospedado em ${host}";
|
||||||
|
|
||||||
|
static m8(count) => "${Intl.plural(count, zero: '', one: 'há ${count} hora', other: 'há ${count} horas')}";
|
||||||
|
|
||||||
|
static m9(count) => "${Intl.plural(count, zero: '0 horas', one: '${count} hora', other: '${count} horas')}";
|
||||||
|
|
||||||
|
static m10(service) => "Integrate with ${service}";
|
||||||
|
|
||||||
|
static m11(userName) => "Logged in as ${userName}";
|
||||||
|
|
||||||
|
static m12(count) => "${Intl.plural(count, zero: 'Agora', one: 'Há ${count} minuto', other: 'Há ${count} minutos')}";
|
||||||
|
|
||||||
|
static m13(count) => "${Intl.plural(count, zero: '0 minutos', one: '${count} minuto', other: '${count} minutos')}";
|
||||||
|
|
||||||
|
static m14(title) => "Obter dados ${title}";
|
||||||
|
|
||||||
|
static m15(title) => "A subscrição falhou, erro de rede ${title}";
|
||||||
|
|
||||||
|
static m16(title) => "Subscrever ${title}";
|
||||||
|
|
||||||
|
static m17(title) => "Subscrição falhou, podcast já existe ${title}";
|
||||||
|
|
||||||
|
static m18(title) => "Subscrito com sucesso ${title}";
|
||||||
|
|
||||||
|
static m19(title) => "Atualizar ${title}";
|
||||||
|
|
||||||
|
static m20(title) => "Erro de atualização ${title}";
|
||||||
|
|
||||||
|
static m21(count) => "${Intl.plural(count, zero: '', one: 'Podcast', other: 'Podcasts')}";
|
||||||
|
|
||||||
|
static m22(date) => "Publicado em ${date}";
|
||||||
|
|
||||||
|
static m23(date) => "Removido em ${date}";
|
||||||
|
|
||||||
|
static m24(count) => "${Intl.plural(count, zero: '0 segundos', one: '${count} segundo', other: '${count} segundos')}";
|
||||||
|
|
||||||
|
static m25(count) => "${Intl.plural(count, zero: 'Agora', one: 'Há ${count} segundo', other: 'Há ${count} segundos')}";
|
||||||
|
|
||||||
|
static m26(time) => "Última vez ${time}";
|
||||||
|
|
||||||
|
static m27(time) => "${time} Restante";
|
||||||
|
|
||||||
|
static m28(time) => "Para ${time}";
|
||||||
|
|
||||||
|
static m29(count) => "${Intl.plural(count, zero: 'Sem atualizações', one: '${count} episódio atualizado', other: '${count} episódios atualizados')}";
|
||||||
|
|
||||||
|
static m30(version) => "Versão: ${version}";
|
||||||
|
|
||||||
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
|
static _notInlinedMessages(_) => <String, Function> {
|
||||||
|
"add" : MessageLookupByLibrary.simpleMessage("Adicionar"),
|
||||||
|
"addEpisodeGroup" : m0,
|
||||||
|
"addNewEpisodeAll" : m1,
|
||||||
|
"addNewEpisodeTooltip" : MessageLookupByLibrary.simpleMessage("Adiciona novos episódios à lista de reprodução"),
|
||||||
|
"addSomeGroups" : MessageLookupByLibrary.simpleMessage("Adiciona alguns grupos"),
|
||||||
|
"all" : MessageLookupByLibrary.simpleMessage("Todos"),
|
||||||
|
"autoDownload" : MessageLookupByLibrary.simpleMessage("Download automático"),
|
||||||
|
"back" : MessageLookupByLibrary.simpleMessage("Atrás"),
|
||||||
|
"boostVolume" : MessageLookupByLibrary.simpleMessage("Aumentar volume"),
|
||||||
|
"buffering" : MessageLookupByLibrary.simpleMessage("A carregar"),
|
||||||
|
"cancel" : MessageLookupByLibrary.simpleMessage("CANCELAR"),
|
||||||
|
"cellularConfirm" : MessageLookupByLibrary.simpleMessage("Alerta de dados móveis"),
|
||||||
|
"cellularConfirmDes" : MessageLookupByLibrary.simpleMessage("Tens a certeza que queres usar dados móveis para downloads?"),
|
||||||
|
"changeLayout" : MessageLookupByLibrary.simpleMessage("Mudar aparência"),
|
||||||
|
"changelog" : MessageLookupByLibrary.simpleMessage("Registo de mudanças"),
|
||||||
|
"chooseA" : MessageLookupByLibrary.simpleMessage("Escolher um"),
|
||||||
|
"clear" : MessageLookupByLibrary.simpleMessage("Limpar"),
|
||||||
|
"color" : MessageLookupByLibrary.simpleMessage("Cor"),
|
||||||
|
"confirm" : MessageLookupByLibrary.simpleMessage("CONFIRMAR"),
|
||||||
|
"darkMode" : MessageLookupByLibrary.simpleMessage("Modo escuro"),
|
||||||
|
"daysAgo" : m2,
|
||||||
|
"daysCount" : m3,
|
||||||
|
"delete" : MessageLookupByLibrary.simpleMessage("Eliminar"),
|
||||||
|
"developer" : MessageLookupByLibrary.simpleMessage("Desenvolvedor"),
|
||||||
|
"dismiss" : MessageLookupByLibrary.simpleMessage("Minimizar"),
|
||||||
|
"done" : MessageLookupByLibrary.simpleMessage("Feito"),
|
||||||
|
"download" : MessageLookupByLibrary.simpleMessage("Download"),
|
||||||
|
"downloadRemovedToast" : MessageLookupByLibrary.simpleMessage("Download removido"),
|
||||||
|
"downloaded" : MessageLookupByLibrary.simpleMessage("Descarregado"),
|
||||||
|
"editGroupName" : MessageLookupByLibrary.simpleMessage("Editar nome do grupo"),
|
||||||
|
"endOfEpisode" : MessageLookupByLibrary.simpleMessage("Fim do episódio"),
|
||||||
|
"episode" : m4,
|
||||||
|
"fastForward" : MessageLookupByLibrary.simpleMessage("Avanço"),
|
||||||
|
"fastRewind" : MessageLookupByLibrary.simpleMessage("Recuo rápido"),
|
||||||
|
"featureDiscoveryEditGroup" : MessageLookupByLibrary.simpleMessage("Prime para editar grupo"),
|
||||||
|
"featureDiscoveryEditGroupDes" : MessageLookupByLibrary.simpleMessage("Podes alterar o nome do grupo ou apagá-lo aqui, mas o grupo Home não pode ser editado ou eliminado"),
|
||||||
|
"featureDiscoveryEpisode" : MessageLookupByLibrary.simpleMessage("Vista de episódios"),
|
||||||
|
"featureDiscoveryEpisodeDes" : MessageLookupByLibrary.simpleMessage("Podes manter premido para reproduzir um episódio ou adicioná-lo a uma lista de reprodução."),
|
||||||
|
"featureDiscoveryEpisodeTitle" : MessageLookupByLibrary.simpleMessage("Mantém premido para reproduzir um episódio instantâneamente"),
|
||||||
|
"featureDiscoveryGroup" : MessageLookupByLibrary.simpleMessage("Prime para adicionar grupo"),
|
||||||
|
"featureDiscoveryGroupDes" : MessageLookupByLibrary.simpleMessage("O grupo por defeito para novos podcasts é Home. Podes criar novos grupos e mover os podcasts para estes, assim como adicionar podcasts a múltiplos grupos."),
|
||||||
|
"featureDiscoveryGroupPodcast" : MessageLookupByLibrary.simpleMessage("Mantém premido para reordenar podcasts"),
|
||||||
|
"featureDiscoveryGroupPodcastDes" : MessageLookupByLibrary.simpleMessage("Podes premir para ver mais opções, ou manter premido para reordenar podcasts em grupos."),
|
||||||
|
"featureDiscoveryOMPL" : MessageLookupByLibrary.simpleMessage("Premir para importar um OPML"),
|
||||||
|
"featureDiscoveryOMPLDes" : MessageLookupByLibrary.simpleMessage("Podes importar ficheiros OPML, abrir as definições ou atualizar todos os podcasts aqui."),
|
||||||
|
"featureDiscoveryPlaylist" : MessageLookupByLibrary.simpleMessage("Prime para abrir a lista de reprodução"),
|
||||||
|
"featureDiscoveryPlaylistDes" : MessageLookupByLibrary.simpleMessage("Podes adicionar episódios à lista de reprodução manualmente. Os episódios serão automaticamente removidos das listas de reprodução quando reproduzidos."),
|
||||||
|
"featureDiscoveryPodcast" : MessageLookupByLibrary.simpleMessage("Vista do podcast"),
|
||||||
|
"featureDiscoveryPodcastDes" : MessageLookupByLibrary.simpleMessage("Podes premir \"Ver Todos\" para adicionar grupos ou organizar pdcasts."),
|
||||||
|
"featureDiscoveryPodcastTitle" : MessageLookupByLibrary.simpleMessage("Deslizar verticalmente para alterar grupos"),
|
||||||
|
"featureDiscoverySearch" : MessageLookupByLibrary.simpleMessage("Prime para procurar podcasts"),
|
||||||
|
"featureDiscoverySearchDes" : MessageLookupByLibrary.simpleMessage("Podes procurar pelo título do podcast, palavra-chave ou ligação RSS para subscrever novos podcasts."),
|
||||||
|
"feedbackEmail" : MessageLookupByLibrary.simpleMessage("Escreve-me"),
|
||||||
|
"feedbackGithub" : MessageLookupByLibrary.simpleMessage("Submeter problema"),
|
||||||
|
"feedbackPlay" : MessageLookupByLibrary.simpleMessage("Avaliar na Play Store"),
|
||||||
|
"feedbackTelegram" : MessageLookupByLibrary.simpleMessage("Juntar um grupo"),
|
||||||
|
"filter" : MessageLookupByLibrary.simpleMessage("Filtro"),
|
||||||
|
"fontStyle" : MessageLookupByLibrary.simpleMessage("Estilo do tipo de letra"),
|
||||||
|
"fonts" : MessageLookupByLibrary.simpleMessage("Fontes"),
|
||||||
|
"from" : m5,
|
||||||
|
"goodNight" : MessageLookupByLibrary.simpleMessage("Boa Noite"),
|
||||||
|
"gpodderLoginDes" : MessageLookupByLibrary.simpleMessage("Congratulations! You have linked gpodder.net account successfully. Tsacdop will automatically sync subscriptions on your device with your gpodder.net account."),
|
||||||
|
"groupExisted" : MessageLookupByLibrary.simpleMessage("Grupo já existe"),
|
||||||
|
"groupFilter" : MessageLookupByLibrary.simpleMessage("Filtro de grupo"),
|
||||||
|
"groupRemoveConfirm" : MessageLookupByLibrary.simpleMessage("Tens a certeza que queres eliminar este grupo? Os podcasts serão removidos para o grupo \"Home\"."),
|
||||||
|
"groups" : m6,
|
||||||
|
"hideListenedSetting" : MessageLookupByLibrary.simpleMessage("Esconder ouvidos"),
|
||||||
|
"homeGroupsSeeAll" : MessageLookupByLibrary.simpleMessage("Ver Todos"),
|
||||||
|
"homeMenuPlaylist" : MessageLookupByLibrary.simpleMessage("Lista de Reprodução"),
|
||||||
|
"homeSubMenuSortBy" : MessageLookupByLibrary.simpleMessage("Ordenar por"),
|
||||||
|
"homeTabMenuFavotite" : MessageLookupByLibrary.simpleMessage("Favorito"),
|
||||||
|
"homeTabMenuRecent" : MessageLookupByLibrary.simpleMessage("Recentes"),
|
||||||
|
"homeToprightMenuAbout" : MessageLookupByLibrary.simpleMessage("Sobre"),
|
||||||
|
"homeToprightMenuImportOMPL" : MessageLookupByLibrary.simpleMessage("Importar OPML"),
|
||||||
|
"homeToprightMenuRefreshAll" : MessageLookupByLibrary.simpleMessage("Atualizar todos"),
|
||||||
|
"hostedOn" : m7,
|
||||||
|
"hoursAgo" : m8,
|
||||||
|
"hoursCount" : m9,
|
||||||
|
"import" : MessageLookupByLibrary.simpleMessage("Importar"),
|
||||||
|
"intergateWith" : m10,
|
||||||
|
"introFourthPage" : MessageLookupByLibrary.simpleMessage("Podes manter premido um episódio para uma ação rápida."),
|
||||||
|
"introSecondPage" : MessageLookupByLibrary.simpleMessage("Subscreve podcasts por pesquisa ou importa um ficheiro OPML."),
|
||||||
|
"introThirdPage" : MessageLookupByLibrary.simpleMessage("Podes criar um novo grupo para podcasts."),
|
||||||
|
"invalidName" : MessageLookupByLibrary.simpleMessage("Invalid username"),
|
||||||
|
"lastUpdate" : MessageLookupByLibrary.simpleMessage("Last update"),
|
||||||
|
"later" : MessageLookupByLibrary.simpleMessage("Mais tarde"),
|
||||||
|
"lightMode" : MessageLookupByLibrary.simpleMessage("Modo claro"),
|
||||||
|
"like" : MessageLookupByLibrary.simpleMessage("Gosto"),
|
||||||
|
"likeDate" : MessageLookupByLibrary.simpleMessage("Data do Gosto"),
|
||||||
|
"liked" : MessageLookupByLibrary.simpleMessage("Gostou"),
|
||||||
|
"listen" : MessageLookupByLibrary.simpleMessage("Ouvir"),
|
||||||
|
"listened" : MessageLookupByLibrary.simpleMessage("Ouvido"),
|
||||||
|
"loadMore" : MessageLookupByLibrary.simpleMessage("Carregar mais"),
|
||||||
|
"loggedInAs" : m11,
|
||||||
|
"login" : MessageLookupByLibrary.simpleMessage("Login"),
|
||||||
|
"loginFailed" : MessageLookupByLibrary.simpleMessage("Login failed"),
|
||||||
|
"logout" : MessageLookupByLibrary.simpleMessage("Logout"),
|
||||||
|
"mark" : MessageLookupByLibrary.simpleMessage("Marcar"),
|
||||||
|
"markConfirm" : MessageLookupByLibrary.simpleMessage("Confirmar marca"),
|
||||||
|
"markConfirmContent" : MessageLookupByLibrary.simpleMessage("Marcar todos os episódios como ouvidos?"),
|
||||||
|
"markListened" : MessageLookupByLibrary.simpleMessage("Marcar como ouvido"),
|
||||||
|
"markNotListened" : MessageLookupByLibrary.simpleMessage("Marcar não ouvidos"),
|
||||||
|
"menu" : MessageLookupByLibrary.simpleMessage("Menu"),
|
||||||
|
"menuAllPodcasts" : MessageLookupByLibrary.simpleMessage("Todos os podcasts"),
|
||||||
|
"menuMarkAllListened" : MessageLookupByLibrary.simpleMessage("Marcar todos como ouvidos"),
|
||||||
|
"menuViewRSS" : MessageLookupByLibrary.simpleMessage("Visitar Feed RSS"),
|
||||||
|
"menuVisitSite" : MessageLookupByLibrary.simpleMessage("Visitar website"),
|
||||||
|
"minsAgo" : m12,
|
||||||
|
"minsCount" : m13,
|
||||||
|
"network" : MessageLookupByLibrary.simpleMessage("Rede"),
|
||||||
|
"newGroup" : MessageLookupByLibrary.simpleMessage("Criar um novo grupo"),
|
||||||
|
"newestFirst" : MessageLookupByLibrary.simpleMessage("Mais recentes primeiro"),
|
||||||
|
"next" : MessageLookupByLibrary.simpleMessage("Seguinte"),
|
||||||
|
"noEpisodeDownload" : MessageLookupByLibrary.simpleMessage("Ainda não há episódios descarregados"),
|
||||||
|
"noEpisodeFavorite" : MessageLookupByLibrary.simpleMessage("Ainda não há episódios coletados"),
|
||||||
|
"noEpisodeRecent" : MessageLookupByLibrary.simpleMessage("Ainda não há episódios recebidos"),
|
||||||
|
"noPodcastGroup" : MessageLookupByLibrary.simpleMessage("Não há podcasts neste grupo"),
|
||||||
|
"noShownote" : MessageLookupByLibrary.simpleMessage("Não há notas disponíveis para este episódio"),
|
||||||
|
"notificaitonFatch" : m14,
|
||||||
|
"notificationNetworkError" : m15,
|
||||||
|
"notificationSetting" : MessageLookupByLibrary.simpleMessage("Painel de notificações"),
|
||||||
|
"notificationSubscribe" : m16,
|
||||||
|
"notificationSubscribeExisted" : m17,
|
||||||
|
"notificationSuccess" : m18,
|
||||||
|
"notificationUpdate" : m19,
|
||||||
|
"notificationUpdateError" : m20,
|
||||||
|
"oldestFirst" : MessageLookupByLibrary.simpleMessage("Mais antigos primeiro"),
|
||||||
|
"passwdRequired" : MessageLookupByLibrary.simpleMessage("Password required"),
|
||||||
|
"password" : MessageLookupByLibrary.simpleMessage("Password"),
|
||||||
|
"pause" : MessageLookupByLibrary.simpleMessage("Pausa"),
|
||||||
|
"play" : MessageLookupByLibrary.simpleMessage("Reproduzir"),
|
||||||
|
"playback" : MessageLookupByLibrary.simpleMessage("Controlo da reprodução"),
|
||||||
|
"player" : MessageLookupByLibrary.simpleMessage("Reprodutor"),
|
||||||
|
"playerHeightMed" : MessageLookupByLibrary.simpleMessage("Médio"),
|
||||||
|
"playerHeightShort" : MessageLookupByLibrary.simpleMessage("Baixo"),
|
||||||
|
"playerHeightTall" : MessageLookupByLibrary.simpleMessage("Alto"),
|
||||||
|
"playing" : MessageLookupByLibrary.simpleMessage("Em reprodução"),
|
||||||
|
"plugins" : MessageLookupByLibrary.simpleMessage("Plugins"),
|
||||||
|
"podcast" : m21,
|
||||||
|
"podcastSubscribed" : MessageLookupByLibrary.simpleMessage("Podcast subscrito"),
|
||||||
|
"popupMenuDownloadDes" : MessageLookupByLibrary.simpleMessage("Descarregar episódio"),
|
||||||
|
"popupMenuLaterDes" : MessageLookupByLibrary.simpleMessage("Adicionar episódio à lista de reprodução"),
|
||||||
|
"popupMenuLikeDes" : MessageLookupByLibrary.simpleMessage("Adicionar episódio aos favoritos"),
|
||||||
|
"popupMenuMarkDes" : MessageLookupByLibrary.simpleMessage("Marcar episódio como ouvido"),
|
||||||
|
"popupMenuPlayDes" : MessageLookupByLibrary.simpleMessage("Reproduzir episódio"),
|
||||||
|
"privacyPolicy" : MessageLookupByLibrary.simpleMessage("Política de Privacidade"),
|
||||||
|
"published" : m22,
|
||||||
|
"publishedDaily" : MessageLookupByLibrary.simpleMessage("Publicado diariamente"),
|
||||||
|
"publishedMonthly" : MessageLookupByLibrary.simpleMessage("Publicado mensalmente"),
|
||||||
|
"publishedWeekly" : MessageLookupByLibrary.simpleMessage("Publicado semanalmente"),
|
||||||
|
"publishedYearly" : MessageLookupByLibrary.simpleMessage("Publicado anualmente"),
|
||||||
|
"recoverSubscribe" : MessageLookupByLibrary.simpleMessage("Recuperar subscrição"),
|
||||||
|
"refreshArtwork" : MessageLookupByLibrary.simpleMessage("Atualizar capa"),
|
||||||
|
"remove" : MessageLookupByLibrary.simpleMessage("Remover"),
|
||||||
|
"removeConfirm" : MessageLookupByLibrary.simpleMessage("Confirmação de remoção"),
|
||||||
|
"removePodcastDes" : MessageLookupByLibrary.simpleMessage("Tens a certeza que pretendes cancelar a subscrição?"),
|
||||||
|
"removedAt" : m23,
|
||||||
|
"save" : MessageLookupByLibrary.simpleMessage("Guardar"),
|
||||||
|
"schedule" : MessageLookupByLibrary.simpleMessage("Horário"),
|
||||||
|
"search" : MessageLookupByLibrary.simpleMessage("Procurar"),
|
||||||
|
"searchEpisode" : MessageLookupByLibrary.simpleMessage("Procurar episódio"),
|
||||||
|
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("Ligação RSS inválida"),
|
||||||
|
"searchPodcast" : MessageLookupByLibrary.simpleMessage("Procurar podcasts"),
|
||||||
|
"secCount" : m24,
|
||||||
|
"secondsAgo" : m25,
|
||||||
|
"settingStorage" : MessageLookupByLibrary.simpleMessage("Armazenamento"),
|
||||||
|
"settings" : MessageLookupByLibrary.simpleMessage("Definições"),
|
||||||
|
"settingsAccentColor" : MessageLookupByLibrary.simpleMessage("Cor de realce"),
|
||||||
|
"settingsAccentColorDes" : MessageLookupByLibrary.simpleMessage("Incluir cor de sobreposição"),
|
||||||
|
"settingsAppIntro" : MessageLookupByLibrary.simpleMessage("Introdução da Aplicação"),
|
||||||
|
"settingsAppearance" : MessageLookupByLibrary.simpleMessage("Aparência"),
|
||||||
|
"settingsAppearanceDes" : MessageLookupByLibrary.simpleMessage("Cores e temas"),
|
||||||
|
"settingsAudioCache" : MessageLookupByLibrary.simpleMessage("Cache de áudio"),
|
||||||
|
"settingsAudioCacheDes" : MessageLookupByLibrary.simpleMessage("Tamanho máximo da cache de áudio"),
|
||||||
|
"settingsAutoDelete" : MessageLookupByLibrary.simpleMessage("Eliminar downloads automaticamente após"),
|
||||||
|
"settingsAutoDeleteDes" : MessageLookupByLibrary.simpleMessage("30 dias por defeito"),
|
||||||
|
"settingsAutoPlayDes" : MessageLookupByLibrary.simpleMessage("Reproduzir automaticamente o episódio seguinte"),
|
||||||
|
"settingsBackup" : MessageLookupByLibrary.simpleMessage("Cópia de segurança"),
|
||||||
|
"settingsBackupDes" : MessageLookupByLibrary.simpleMessage("Cópia de segurança dos dados da aplicação"),
|
||||||
|
"settingsBoostVolume" : MessageLookupByLibrary.simpleMessage("Nível de aumento de volume"),
|
||||||
|
"settingsBoostVolumeDes" : MessageLookupByLibrary.simpleMessage("Alterar nível de aumento de volume"),
|
||||||
|
"settingsDefaultGrid" : MessageLookupByLibrary.simpleMessage("Vista de grelha predefinida"),
|
||||||
|
"settingsDefaultGridDownload" : MessageLookupByLibrary.simpleMessage("Aba de downloads"),
|
||||||
|
"settingsDefaultGridFavorite" : MessageLookupByLibrary.simpleMessage("Aba de favoritos"),
|
||||||
|
"settingsDefaultGridPodcast" : MessageLookupByLibrary.simpleMessage("Página de podcasts"),
|
||||||
|
"settingsDefaultGridRecent" : MessageLookupByLibrary.simpleMessage("Aba de recentes"),
|
||||||
|
"settingsDiscovery" : MessageLookupByLibrary.simpleMessage("Reiniciar tutorial"),
|
||||||
|
"settingsEnableSyncing" : MessageLookupByLibrary.simpleMessage("Ativar sincronização"),
|
||||||
|
"settingsEnableSyncingDes" : MessageLookupByLibrary.simpleMessage("Atualizar todos os podcasts em segundo plano para obter os episódios mais recentes"),
|
||||||
|
"settingsExportDes" : MessageLookupByLibrary.simpleMessage("Exportar e importar definições da aplicação"),
|
||||||
|
"settingsFastForwardSec" : MessageLookupByLibrary.simpleMessage("Avançar segundos"),
|
||||||
|
"settingsFastForwardSecDes" : MessageLookupByLibrary.simpleMessage("Muda os segundos de avanço no reprodutor"),
|
||||||
|
"settingsFeedback" : MessageLookupByLibrary.simpleMessage("Feedback"),
|
||||||
|
"settingsFeedbackDes" : MessageLookupByLibrary.simpleMessage("Erros e sugestões"),
|
||||||
|
"settingsHistory" : MessageLookupByLibrary.simpleMessage("Histórico"),
|
||||||
|
"settingsHistoryDes" : MessageLookupByLibrary.simpleMessage("Dados de audição"),
|
||||||
|
"settingsInfo" : MessageLookupByLibrary.simpleMessage("Informações"),
|
||||||
|
"settingsInterface" : MessageLookupByLibrary.simpleMessage("Interface"),
|
||||||
|
"settingsLanguages" : MessageLookupByLibrary.simpleMessage("Idiomas"),
|
||||||
|
"settingsLanguagesDes" : MessageLookupByLibrary.simpleMessage("Mudar idioma"),
|
||||||
|
"settingsLayout" : MessageLookupByLibrary.simpleMessage("Esquema"),
|
||||||
|
"settingsLayoutDes" : MessageLookupByLibrary.simpleMessage("Esquema da aplicação"),
|
||||||
|
"settingsLibraries" : MessageLookupByLibrary.simpleMessage("Bibliotecas"),
|
||||||
|
"settingsLibrariesDes" : MessageLookupByLibrary.simpleMessage("Bibliotecas de código aberto usados nesta aplicação"),
|
||||||
|
"settingsManageDownload" : MessageLookupByLibrary.simpleMessage("Gerir downloads"),
|
||||||
|
"settingsManageDownloadDes" : MessageLookupByLibrary.simpleMessage("Gerir arquivos de aúdio descarregados"),
|
||||||
|
"settingsMenuAutoPlay" : MessageLookupByLibrary.simpleMessage("Reproduzir seguinte automaticamente"),
|
||||||
|
"settingsNetworkCellular" : MessageLookupByLibrary.simpleMessage("Perguntar antes de usar dados móveis"),
|
||||||
|
"settingsNetworkCellularAuto" : MessageLookupByLibrary.simpleMessage("Descarregar automaticamente usando os dados móveis"),
|
||||||
|
"settingsNetworkCellularAutoDes" : MessageLookupByLibrary.simpleMessage("Podes configurar o descarregamento automático na página de gestão de grupos"),
|
||||||
|
"settingsNetworkCellularDes" : MessageLookupByLibrary.simpleMessage("Perguntar a confirmar o uso de dados móveis ao descarregar episódios"),
|
||||||
|
"settingsPlayDes" : MessageLookupByLibrary.simpleMessage("Lista de reprodução e reprodutor"),
|
||||||
|
"settingsPlayerHeight" : MessageLookupByLibrary.simpleMessage("Altura do reprodutor"),
|
||||||
|
"settingsPlayerHeightDes" : MessageLookupByLibrary.simpleMessage("Mudar a altura do reprodutor a teu gosto"),
|
||||||
|
"settingsPopupMenu" : MessageLookupByLibrary.simpleMessage("Menu pop-up de episódios"),
|
||||||
|
"settingsPopupMenuDes" : MessageLookupByLibrary.simpleMessage("Muda o menu pop-up de episódios"),
|
||||||
|
"settingsPrefrence" : MessageLookupByLibrary.simpleMessage("Preferências"),
|
||||||
|
"settingsRealDark" : MessageLookupByLibrary.simpleMessage("Escuro AMOLED"),
|
||||||
|
"settingsRealDarkDes" : MessageLookupByLibrary.simpleMessage("Ativa caso o modo escuro não seja suficientemente escuro"),
|
||||||
|
"settingsRewindSec" : MessageLookupByLibrary.simpleMessage("Segundos de recuo"),
|
||||||
|
"settingsRewindSecDes" : MessageLookupByLibrary.simpleMessage("Muda os segundos de recuo no reprodutor"),
|
||||||
|
"settingsSTAuto" : MessageLookupByLibrary.simpleMessage("Ligar temporizador automaticamente"),
|
||||||
|
"settingsSTAutoDes" : MessageLookupByLibrary.simpleMessage("Ligar temporizador automaticamente num horário definido"),
|
||||||
|
"settingsSTDefaultTime" : MessageLookupByLibrary.simpleMessage("Tempo predefinido"),
|
||||||
|
"settingsSTDefautTimeDes" : MessageLookupByLibrary.simpleMessage("Tempo predefinido para temporizador"),
|
||||||
|
"settingsSTMode" : MessageLookupByLibrary.simpleMessage("Modo de temporizador automático"),
|
||||||
|
"settingsSpeeds" : MessageLookupByLibrary.simpleMessage("Velocidades"),
|
||||||
|
"settingsSpeedsDes" : MessageLookupByLibrary.simpleMessage("Customizar as velocidades disponíveis"),
|
||||||
|
"settingsStorageDes" : MessageLookupByLibrary.simpleMessage("Gerir cache e armazenamento de downloads"),
|
||||||
|
"settingsSyncing" : MessageLookupByLibrary.simpleMessage("Sincronização"),
|
||||||
|
"settingsSyncingDes" : MessageLookupByLibrary.simpleMessage("Atualizar podcasts em segundo plano"),
|
||||||
|
"settingsTapToOpenPopupMenu" : MessageLookupByLibrary.simpleMessage("Prime para abrir o menu pop-up"),
|
||||||
|
"settingsTapToOpenPopupMenuDes" : MessageLookupByLibrary.simpleMessage("Precisas manter premido para abrir a página do episódio"),
|
||||||
|
"settingsTheme" : MessageLookupByLibrary.simpleMessage("Tema"),
|
||||||
|
"settingsUpdateInterval" : MessageLookupByLibrary.simpleMessage("Intervalo de atualização"),
|
||||||
|
"settingsUpdateIntervalDes" : MessageLookupByLibrary.simpleMessage("24 horas predefinidas"),
|
||||||
|
"share" : MessageLookupByLibrary.simpleMessage("Partilhar"),
|
||||||
|
"showNotesFonts" : MessageLookupByLibrary.simpleMessage("Mostrar tipo de letra das notas"),
|
||||||
|
"size" : MessageLookupByLibrary.simpleMessage("Tamanho"),
|
||||||
|
"skipSecondsAtEnd" : MessageLookupByLibrary.simpleMessage("Saltar segundos no fim"),
|
||||||
|
"skipSecondsAtStart" : MessageLookupByLibrary.simpleMessage("Saltar segundos no início"),
|
||||||
|
"skipSilence" : MessageLookupByLibrary.simpleMessage("Saltar silêncio"),
|
||||||
|
"skipToNext" : MessageLookupByLibrary.simpleMessage("Saltar para o próximo"),
|
||||||
|
"sleepTimer" : MessageLookupByLibrary.simpleMessage("Temporizador"),
|
||||||
|
"status" : MessageLookupByLibrary.simpleMessage("Status"),
|
||||||
|
"stop" : MessageLookupByLibrary.simpleMessage("Parar"),
|
||||||
|
"subscribe" : MessageLookupByLibrary.simpleMessage("Subscrever"),
|
||||||
|
"subscribeExportDes" : MessageLookupByLibrary.simpleMessage("Exportar ficheiro OPML de todos os podcasts"),
|
||||||
|
"syncNow" : MessageLookupByLibrary.simpleMessage("Sync now"),
|
||||||
|
"systemDefault" : MessageLookupByLibrary.simpleMessage("Predefinido do sistema"),
|
||||||
|
"timeLastPlayed" : m26,
|
||||||
|
"timeLeft" : m27,
|
||||||
|
"to" : m28,
|
||||||
|
"toastAddPlaylist" : MessageLookupByLibrary.simpleMessage("Adicionado à lista de reprodução"),
|
||||||
|
"toastDiscovery" : MessageLookupByLibrary.simpleMessage("Característica \"Descobrir\" ligada, por favor reinicia a aplicação"),
|
||||||
|
"toastFileError" : MessageLookupByLibrary.simpleMessage("Erro no ficheiro, subscrição falhou"),
|
||||||
|
"toastFileNotValid" : MessageLookupByLibrary.simpleMessage("Ficheiro inválido"),
|
||||||
|
"toastHomeGroupNotSupport" : MessageLookupByLibrary.simpleMessage("Grupo Home não é suportado"),
|
||||||
|
"toastImportSettingsSuccess" : MessageLookupByLibrary.simpleMessage("Definições importadas com sucesso"),
|
||||||
|
"toastOneGroup" : MessageLookupByLibrary.simpleMessage("Seleciona pelo menos um grupo"),
|
||||||
|
"toastPodcastRecovering" : MessageLookupByLibrary.simpleMessage("A recuperar, espera um momento"),
|
||||||
|
"toastReadFile" : MessageLookupByLibrary.simpleMessage("Ficheiro lido com sucesso"),
|
||||||
|
"toastRecoverFailed" : MessageLookupByLibrary.simpleMessage("Recuperação do podcast falhou"),
|
||||||
|
"toastRemovePlaylist" : MessageLookupByLibrary.simpleMessage("Episódio removido da lista de reprodução"),
|
||||||
|
"toastSettingSaved" : MessageLookupByLibrary.simpleMessage("Definições guardadas"),
|
||||||
|
"toastTimeEqualEnd" : MessageLookupByLibrary.simpleMessage("Tempo marcado é igual ao tempo de fim"),
|
||||||
|
"toastTimeEqualStart" : MessageLookupByLibrary.simpleMessage("Tempo marcado é igual ao tempo de início"),
|
||||||
|
"translators" : MessageLookupByLibrary.simpleMessage("Tradutores"),
|
||||||
|
"understood" : MessageLookupByLibrary.simpleMessage("Compreendido"),
|
||||||
|
"undo" : MessageLookupByLibrary.simpleMessage("DESFAZER"),
|
||||||
|
"unlike" : MessageLookupByLibrary.simpleMessage("Não gosto"),
|
||||||
|
"unliked" : MessageLookupByLibrary.simpleMessage("Episódio removido dos favoritos"),
|
||||||
|
"updateDate" : MessageLookupByLibrary.simpleMessage("Atualizar data"),
|
||||||
|
"updateEpisodesCount" : m29,
|
||||||
|
"updateFailed" : MessageLookupByLibrary.simpleMessage("Atuallização falhou, erro de conexão"),
|
||||||
|
"username" : MessageLookupByLibrary.simpleMessage("Username"),
|
||||||
|
"usernameRequired" : MessageLookupByLibrary.simpleMessage("Username requeired"),
|
||||||
|
"version" : m30
|
||||||
|
};
|
||||||
|
}
|
@ -39,43 +39,47 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
|
|
||||||
static m9(count) => "${Intl.plural(count, zero: '0小时', other: '${count} 小时')}";
|
static m9(count) => "${Intl.plural(count, zero: '0小时', other: '${count} 小时')}";
|
||||||
|
|
||||||
static m10(count) => "${Intl.plural(count, zero: '刚刚', other: '${count}分钟前')}";
|
static m10(service) => "绑定 ${service}";
|
||||||
|
|
||||||
static m11(count) => "${Intl.plural(count, zero: '0分钟', other: '${count}分钟')}";
|
static m11(userName) => "使用${userName}登入";
|
||||||
|
|
||||||
static m12(title) => "获取数据 ${title}";
|
static m12(count) => "${Intl.plural(count, zero: '刚刚', other: '${count}分钟前')}";
|
||||||
|
|
||||||
static m13(title) => "订阅失败,网络错误 ${title}";
|
static m13(count) => "${Intl.plural(count, zero: '0分钟', other: '${count}分钟')}";
|
||||||
|
|
||||||
static m14(title) => "订阅 ${title}";
|
static m14(title) => "获取数据 ${title}";
|
||||||
|
|
||||||
static m15(title) => "订阅失败,播客已存在 ${title}";
|
static m15(title) => "订阅失败,网络错误 ${title}";
|
||||||
|
|
||||||
static m16(title) => "订阅成功 ${title}";
|
static m16(title) => "订阅 ${title}";
|
||||||
|
|
||||||
static m17(title) => "更新 ${title}";
|
static m17(title) => "订阅失败,播客已存在 ${title}";
|
||||||
|
|
||||||
static m18(title) => "更新失败 ${title}";
|
static m18(title) => "订阅成功 ${title}";
|
||||||
|
|
||||||
static m19(count) => "${Intl.plural(count, zero: '', other: '播客')}";
|
static m19(title) => "更新 ${title}";
|
||||||
|
|
||||||
static m20(date) => "${date}上线";
|
static m20(title) => "更新失败 ${title}";
|
||||||
|
|
||||||
static m21(date) => "${date}移除";
|
static m21(count) => "${Intl.plural(count, zero: '', other: '播客')}";
|
||||||
|
|
||||||
static m22(count) => "${Intl.plural(count, zero: '0 秒', other: '${count} 秒')}";
|
static m22(date) => "${date}上线";
|
||||||
|
|
||||||
static m23(count) => "${Intl.plural(count, zero: '刚刚', other: '${count}秒前')}";
|
static m23(date) => "${date}移除";
|
||||||
|
|
||||||
static m24(time) => "上次播放${time}";
|
static m24(count) => "${Intl.plural(count, zero: '0 秒', other: '${count} 秒')}";
|
||||||
|
|
||||||
static m25(time) => "剩余 ${time}";
|
static m25(count) => "${Intl.plural(count, zero: '刚刚', other: '${count}秒前')}";
|
||||||
|
|
||||||
static m26(time) => "到${time}";
|
static m26(time) => "上次播放${time}";
|
||||||
|
|
||||||
static m27(count) => "${Intl.plural(count, zero: '未有更新', other: '更新 ${count} 集节目')}";
|
static m27(time) => "剩余 ${time}";
|
||||||
|
|
||||||
static m28(version) => "版本:${version}";
|
static m28(time) => "到${time}";
|
||||||
|
|
||||||
|
static m29(count) => "${Intl.plural(count, zero: '未有更新', other: '更新 ${count} 集节目')}";
|
||||||
|
|
||||||
|
static m30(version) => "版本:${version}";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static _notInlinedMessages(_) => <String, Function> {
|
static _notInlinedMessages(_) => <String, Function> {
|
||||||
@ -140,6 +144,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"fonts" : MessageLookupByLibrary.simpleMessage("字体"),
|
"fonts" : MessageLookupByLibrary.simpleMessage("字体"),
|
||||||
"from" : m5,
|
"from" : m5,
|
||||||
"goodNight" : MessageLookupByLibrary.simpleMessage("晚安"),
|
"goodNight" : MessageLookupByLibrary.simpleMessage("晚安"),
|
||||||
|
"gpodderLoginDes" : MessageLookupByLibrary.simpleMessage("恭喜!您已经成功绑定 gpodder.net 账号,Tsacdop 将会自动同步您的订阅到 gpodder.net 账户。"),
|
||||||
"groupExisted" : MessageLookupByLibrary.simpleMessage("组名已使用"),
|
"groupExisted" : MessageLookupByLibrary.simpleMessage("组名已使用"),
|
||||||
"groupFilter" : MessageLookupByLibrary.simpleMessage("分组"),
|
"groupFilter" : MessageLookupByLibrary.simpleMessage("分组"),
|
||||||
"groupRemoveConfirm" : MessageLookupByLibrary.simpleMessage("您确认要移除该分组吗?播客将被移动到 Home 分组。"),
|
"groupRemoveConfirm" : MessageLookupByLibrary.simpleMessage("您确认要移除该分组吗?播客将被移动到 Home 分组。"),
|
||||||
@ -157,9 +162,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"hoursAgo" : m8,
|
"hoursAgo" : m8,
|
||||||
"hoursCount" : m9,
|
"hoursCount" : m9,
|
||||||
"import" : MessageLookupByLibrary.simpleMessage("导入"),
|
"import" : MessageLookupByLibrary.simpleMessage("导入"),
|
||||||
|
"intergateWith" : m10,
|
||||||
"introFourthPage" : MessageLookupByLibrary.simpleMessage("您可以长按节目打开快捷菜单。"),
|
"introFourthPage" : MessageLookupByLibrary.simpleMessage("您可以长按节目打开快捷菜单。"),
|
||||||
"introSecondPage" : MessageLookupByLibrary.simpleMessage("您可以通过搜索订阅播客,也可以直接导入OPML文件。"),
|
"introSecondPage" : MessageLookupByLibrary.simpleMessage("您可以通过搜索订阅播客,也可以直接导入OPML文件。"),
|
||||||
"introThirdPage" : MessageLookupByLibrary.simpleMessage("您可以创建分组,上下滑动切换分组。"),
|
"introThirdPage" : MessageLookupByLibrary.simpleMessage("您可以创建分组,上下滑动切换分组。"),
|
||||||
|
"invalidName" : MessageLookupByLibrary.simpleMessage("用户名错误"),
|
||||||
|
"lastUpdate" : MessageLookupByLibrary.simpleMessage("最近更新"),
|
||||||
"later" : MessageLookupByLibrary.simpleMessage("稍后"),
|
"later" : MessageLookupByLibrary.simpleMessage("稍后"),
|
||||||
"lightMode" : MessageLookupByLibrary.simpleMessage("明亮模式"),
|
"lightMode" : MessageLookupByLibrary.simpleMessage("明亮模式"),
|
||||||
"like" : MessageLookupByLibrary.simpleMessage("喜欢"),
|
"like" : MessageLookupByLibrary.simpleMessage("喜欢"),
|
||||||
@ -168,6 +176,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"listen" : MessageLookupByLibrary.simpleMessage("收听"),
|
"listen" : MessageLookupByLibrary.simpleMessage("收听"),
|
||||||
"listened" : MessageLookupByLibrary.simpleMessage("已收听"),
|
"listened" : MessageLookupByLibrary.simpleMessage("已收听"),
|
||||||
"loadMore" : MessageLookupByLibrary.simpleMessage("加载更多"),
|
"loadMore" : MessageLookupByLibrary.simpleMessage("加载更多"),
|
||||||
|
"loggedInAs" : m11,
|
||||||
|
"login" : MessageLookupByLibrary.simpleMessage("登入"),
|
||||||
|
"loginFailed" : MessageLookupByLibrary.simpleMessage("登入失败"),
|
||||||
|
"logout" : MessageLookupByLibrary.simpleMessage("注销"),
|
||||||
"mark" : MessageLookupByLibrary.simpleMessage("标记"),
|
"mark" : MessageLookupByLibrary.simpleMessage("标记"),
|
||||||
"markConfirm" : MessageLookupByLibrary.simpleMessage("确认标记"),
|
"markConfirm" : MessageLookupByLibrary.simpleMessage("确认标记"),
|
||||||
"markConfirmContent" : MessageLookupByLibrary.simpleMessage("是否确认标记全部节目为已收听?"),
|
"markConfirmContent" : MessageLookupByLibrary.simpleMessage("是否确认标记全部节目为已收听?"),
|
||||||
@ -178,8 +190,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"menuMarkAllListened" : MessageLookupByLibrary.simpleMessage("标记所有已收听"),
|
"menuMarkAllListened" : MessageLookupByLibrary.simpleMessage("标记所有已收听"),
|
||||||
"menuViewRSS" : MessageLookupByLibrary.simpleMessage("查看 RSS"),
|
"menuViewRSS" : MessageLookupByLibrary.simpleMessage("查看 RSS"),
|
||||||
"menuVisitSite" : MessageLookupByLibrary.simpleMessage("访问网站"),
|
"menuVisitSite" : MessageLookupByLibrary.simpleMessage("访问网站"),
|
||||||
"minsAgo" : m10,
|
"minsAgo" : m12,
|
||||||
"minsCount" : m11,
|
"minsCount" : m13,
|
||||||
"network" : MessageLookupByLibrary.simpleMessage("网络"),
|
"network" : MessageLookupByLibrary.simpleMessage("网络"),
|
||||||
"newGroup" : MessageLookupByLibrary.simpleMessage("创建分组"),
|
"newGroup" : MessageLookupByLibrary.simpleMessage("创建分组"),
|
||||||
"newestFirst" : MessageLookupByLibrary.simpleMessage("由新到旧"),
|
"newestFirst" : MessageLookupByLibrary.simpleMessage("由新到旧"),
|
||||||
@ -189,15 +201,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"noEpisodeRecent" : MessageLookupByLibrary.simpleMessage("暂无节目"),
|
"noEpisodeRecent" : MessageLookupByLibrary.simpleMessage("暂无节目"),
|
||||||
"noPodcastGroup" : MessageLookupByLibrary.simpleMessage("分组无播客"),
|
"noPodcastGroup" : MessageLookupByLibrary.simpleMessage("分组无播客"),
|
||||||
"noShownote" : MessageLookupByLibrary.simpleMessage("节目简介暂未收到。"),
|
"noShownote" : MessageLookupByLibrary.simpleMessage("节目简介暂未收到。"),
|
||||||
"notificaitonFatch" : m12,
|
"notificaitonFatch" : m14,
|
||||||
"notificationNetworkError" : m13,
|
"notificationNetworkError" : m15,
|
||||||
"notificationSetting" : MessageLookupByLibrary.simpleMessage("通知栏"),
|
"notificationSetting" : MessageLookupByLibrary.simpleMessage("通知栏"),
|
||||||
"notificationSubscribe" : m14,
|
"notificationSubscribe" : m16,
|
||||||
"notificationSubscribeExisted" : m15,
|
"notificationSubscribeExisted" : m17,
|
||||||
"notificationSuccess" : m16,
|
"notificationSuccess" : m18,
|
||||||
"notificationUpdate" : m17,
|
"notificationUpdate" : m19,
|
||||||
"notificationUpdateError" : m18,
|
"notificationUpdateError" : m20,
|
||||||
"oldestFirst" : MessageLookupByLibrary.simpleMessage("由旧到新"),
|
"oldestFirst" : MessageLookupByLibrary.simpleMessage("由旧到新"),
|
||||||
|
"passwdRequired" : MessageLookupByLibrary.simpleMessage("密码为空"),
|
||||||
|
"password" : MessageLookupByLibrary.simpleMessage("密码"),
|
||||||
"pause" : MessageLookupByLibrary.simpleMessage("暂停"),
|
"pause" : MessageLookupByLibrary.simpleMessage("暂停"),
|
||||||
"play" : MessageLookupByLibrary.simpleMessage("播放"),
|
"play" : MessageLookupByLibrary.simpleMessage("播放"),
|
||||||
"playback" : MessageLookupByLibrary.simpleMessage("播放控制"),
|
"playback" : MessageLookupByLibrary.simpleMessage("播放控制"),
|
||||||
@ -207,7 +221,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"playerHeightTall" : MessageLookupByLibrary.simpleMessage("高"),
|
"playerHeightTall" : MessageLookupByLibrary.simpleMessage("高"),
|
||||||
"playing" : MessageLookupByLibrary.simpleMessage("正在播放"),
|
"playing" : MessageLookupByLibrary.simpleMessage("正在播放"),
|
||||||
"plugins" : MessageLookupByLibrary.simpleMessage("插件"),
|
"plugins" : MessageLookupByLibrary.simpleMessage("插件"),
|
||||||
"podcast" : m19,
|
"podcast" : m21,
|
||||||
"podcastSubscribed" : MessageLookupByLibrary.simpleMessage("播客已订阅"),
|
"podcastSubscribed" : MessageLookupByLibrary.simpleMessage("播客已订阅"),
|
||||||
"popupMenuDownloadDes" : MessageLookupByLibrary.simpleMessage("下载节目"),
|
"popupMenuDownloadDes" : MessageLookupByLibrary.simpleMessage("下载节目"),
|
||||||
"popupMenuLaterDes" : MessageLookupByLibrary.simpleMessage("添加到播放列表"),
|
"popupMenuLaterDes" : MessageLookupByLibrary.simpleMessage("添加到播放列表"),
|
||||||
@ -215,7 +229,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"popupMenuMarkDes" : MessageLookupByLibrary.simpleMessage("设置为已收听"),
|
"popupMenuMarkDes" : MessageLookupByLibrary.simpleMessage("设置为已收听"),
|
||||||
"popupMenuPlayDes" : MessageLookupByLibrary.simpleMessage("播放节目"),
|
"popupMenuPlayDes" : MessageLookupByLibrary.simpleMessage("播放节目"),
|
||||||
"privacyPolicy" : MessageLookupByLibrary.simpleMessage("隐私条款"),
|
"privacyPolicy" : MessageLookupByLibrary.simpleMessage("隐私条款"),
|
||||||
"published" : m20,
|
"published" : m22,
|
||||||
"publishedDaily" : MessageLookupByLibrary.simpleMessage("每日更新"),
|
"publishedDaily" : MessageLookupByLibrary.simpleMessage("每日更新"),
|
||||||
"publishedMonthly" : MessageLookupByLibrary.simpleMessage("每月更新"),
|
"publishedMonthly" : MessageLookupByLibrary.simpleMessage("每月更新"),
|
||||||
"publishedWeekly" : MessageLookupByLibrary.simpleMessage("每周更新"),
|
"publishedWeekly" : MessageLookupByLibrary.simpleMessage("每周更新"),
|
||||||
@ -225,15 +239,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"remove" : MessageLookupByLibrary.simpleMessage("移除"),
|
"remove" : MessageLookupByLibrary.simpleMessage("移除"),
|
||||||
"removeConfirm" : MessageLookupByLibrary.simpleMessage("取消订阅"),
|
"removeConfirm" : MessageLookupByLibrary.simpleMessage("取消订阅"),
|
||||||
"removePodcastDes" : MessageLookupByLibrary.simpleMessage("您确认要取消订阅吗?"),
|
"removePodcastDes" : MessageLookupByLibrary.simpleMessage("您确认要取消订阅吗?"),
|
||||||
"removedAt" : m21,
|
"removedAt" : m23,
|
||||||
"save" : MessageLookupByLibrary.simpleMessage("保存"),
|
"save" : MessageLookupByLibrary.simpleMessage("保存"),
|
||||||
"schedule" : MessageLookupByLibrary.simpleMessage("定时"),
|
"schedule" : MessageLookupByLibrary.simpleMessage("定时"),
|
||||||
"search" : MessageLookupByLibrary.simpleMessage("搜索"),
|
"search" : MessageLookupByLibrary.simpleMessage("搜索"),
|
||||||
"searchEpisode" : MessageLookupByLibrary.simpleMessage("搜索节目"),
|
"searchEpisode" : MessageLookupByLibrary.simpleMessage("搜索节目"),
|
||||||
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("RSS 链接错误"),
|
"searchInvalidRss" : MessageLookupByLibrary.simpleMessage("RSS 链接错误"),
|
||||||
"searchPodcast" : MessageLookupByLibrary.simpleMessage("搜索播客"),
|
"searchPodcast" : MessageLookupByLibrary.simpleMessage("搜索播客"),
|
||||||
"secCount" : m22,
|
"secCount" : m24,
|
||||||
"secondsAgo" : m23,
|
"secondsAgo" : m25,
|
||||||
"settingStorage" : MessageLookupByLibrary.simpleMessage("储存空间"),
|
"settingStorage" : MessageLookupByLibrary.simpleMessage("储存空间"),
|
||||||
"settings" : MessageLookupByLibrary.simpleMessage("设置"),
|
"settings" : MessageLookupByLibrary.simpleMessage("设置"),
|
||||||
"settingsAccentColor" : MessageLookupByLibrary.simpleMessage("次要颜色"),
|
"settingsAccentColor" : MessageLookupByLibrary.simpleMessage("次要颜色"),
|
||||||
@ -313,13 +327,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"skipSilence" : MessageLookupByLibrary.simpleMessage("跳过无声"),
|
"skipSilence" : MessageLookupByLibrary.simpleMessage("跳过无声"),
|
||||||
"skipToNext" : MessageLookupByLibrary.simpleMessage("下一首"),
|
"skipToNext" : MessageLookupByLibrary.simpleMessage("下一首"),
|
||||||
"sleepTimer" : MessageLookupByLibrary.simpleMessage("睡眠模式"),
|
"sleepTimer" : MessageLookupByLibrary.simpleMessage("睡眠模式"),
|
||||||
|
"status" : MessageLookupByLibrary.simpleMessage("状态"),
|
||||||
"stop" : MessageLookupByLibrary.simpleMessage("停止"),
|
"stop" : MessageLookupByLibrary.simpleMessage("停止"),
|
||||||
"subscribe" : MessageLookupByLibrary.simpleMessage("订阅"),
|
"subscribe" : MessageLookupByLibrary.simpleMessage("订阅"),
|
||||||
"subscribeExportDes" : MessageLookupByLibrary.simpleMessage("导出 OPML 文件"),
|
"subscribeExportDes" : MessageLookupByLibrary.simpleMessage("导出 OPML 文件"),
|
||||||
|
"syncNow" : MessageLookupByLibrary.simpleMessage("立即同步"),
|
||||||
"systemDefault" : MessageLookupByLibrary.simpleMessage("系统默认"),
|
"systemDefault" : MessageLookupByLibrary.simpleMessage("系统默认"),
|
||||||
"timeLastPlayed" : m24,
|
"timeLastPlayed" : m26,
|
||||||
"timeLeft" : m25,
|
"timeLeft" : m27,
|
||||||
"to" : m26,
|
"to" : m28,
|
||||||
"toastAddPlaylist" : MessageLookupByLibrary.simpleMessage("添加到播放列表"),
|
"toastAddPlaylist" : MessageLookupByLibrary.simpleMessage("添加到播放列表"),
|
||||||
"toastDiscovery" : MessageLookupByLibrary.simpleMessage("重启应用后可查看"),
|
"toastDiscovery" : MessageLookupByLibrary.simpleMessage("重启应用后可查看"),
|
||||||
"toastFileError" : MessageLookupByLibrary.simpleMessage("文件错误,导入失败"),
|
"toastFileError" : MessageLookupByLibrary.simpleMessage("文件错误,导入失败"),
|
||||||
@ -340,8 +356,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"unlike" : MessageLookupByLibrary.simpleMessage("取消喜欢"),
|
"unlike" : MessageLookupByLibrary.simpleMessage("取消喜欢"),
|
||||||
"unliked" : MessageLookupByLibrary.simpleMessage("从收藏移除"),
|
"unliked" : MessageLookupByLibrary.simpleMessage("从收藏移除"),
|
||||||
"updateDate" : MessageLookupByLibrary.simpleMessage("更新日期"),
|
"updateDate" : MessageLookupByLibrary.simpleMessage("更新日期"),
|
||||||
"updateEpisodesCount" : m27,
|
"updateEpisodesCount" : m29,
|
||||||
"updateFailed" : MessageLookupByLibrary.simpleMessage("更新失败"),
|
"updateFailed" : MessageLookupByLibrary.simpleMessage("更新失败"),
|
||||||
"version" : m28
|
"username" : MessageLookupByLibrary.simpleMessage("用户名"),
|
||||||
|
"usernameRequired" : MessageLookupByLibrary.simpleMessage("用户名为空"),
|
||||||
|
"version" : m30
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import 'package:line_icons/line_icons.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../local_storage/key_value_storage.dart';
|
import '../local_storage/key_value_storage.dart';
|
||||||
import '../service/ompl_build.dart';
|
import '../service/opml_build.dart';
|
||||||
import '../settings/settting.dart';
|
import '../settings/settting.dart';
|
||||||
import '../state/podcast_group.dart';
|
import '../state/podcast_group.dart';
|
||||||
import '../state/refresh_podcast.dart';
|
import '../state/refresh_podcast.dart';
|
||||||
@ -58,7 +58,8 @@ class _PopupMenuState extends State<PopupMenu> {
|
|||||||
final s = context.s;
|
final s = context.s;
|
||||||
var file = File(path);
|
var file = File(path);
|
||||||
try {
|
try {
|
||||||
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOMPL(file);
|
final opml = file.readAsStringSync();
|
||||||
|
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOMPL(opml);
|
||||||
for (var entry in data.entries) {
|
for (var entry in data.entries) {
|
||||||
var title = entry.key;
|
var title = entry.key;
|
||||||
var list = entry.value.reversed;
|
var list = entry.value.reversed;
|
||||||
@ -83,14 +84,16 @@ class _PopupMenuState extends State<PopupMenu> {
|
|||||||
void _getFilePath() async {
|
void _getFilePath() async {
|
||||||
final s = context.s;
|
final s = context.s;
|
||||||
try {
|
try {
|
||||||
var filePath = await FilePicker.getFilePath(type: FileType.any);
|
var filePickResult =
|
||||||
if (filePath == '') {
|
await FilePicker.platform.pickFiles(type: FileType.any);
|
||||||
|
if (filePickResult == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.toastReadFile,
|
msg: s.toastReadFile,
|
||||||
gravity: ToastGravity.TOP,
|
gravity: ToastGravity.TOP,
|
||||||
);
|
);
|
||||||
|
final filePath = filePickResult.files.first.path;
|
||||||
_saveOmpl(filePath);
|
_saveOmpl(filePath);
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
developer.log(e.toString(), name: 'Get OMPL file');
|
developer.log(e.toString(), name: 'Get OMPL file');
|
||||||
|
@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../local_storage/key_value_storage.dart';
|
import '../local_storage/key_value_storage.dart';
|
||||||
import '../service/api_search.dart';
|
import '../service/search_api.dart';
|
||||||
import '../state/search_state.dart';
|
import '../state/search_state.dart';
|
||||||
import '../type/search_genre.dart';
|
import '../type/search_api/search_genre.dart';
|
||||||
import '../type/searchpodcast.dart';
|
import '../type/search_api/searchpodcast.dart';
|
||||||
import '../util/custom_widget.dart';
|
import '../util/custom_widget.dart';
|
||||||
import '../util/extension_helper.dart';
|
import '../util/extension_helper.dart';
|
||||||
import 'search_podcast.dart';
|
import 'search_podcast.dart';
|
||||||
@ -113,7 +113,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||||||
));
|
));
|
||||||
|
|
||||||
Future<List<OnlinePodcast>> _getTopPodcasts({int page}) async {
|
Future<List<OnlinePodcast>> _getTopPodcasts({int page}) async {
|
||||||
final searchEngine = SearchEngine();
|
final searchEngine = ListenNotesSearch();
|
||||||
var searchResult = await searchEngine.fetchBestPodcast(
|
var searchResult = await searchEngine.fetchBestPodcast(
|
||||||
genre: '',
|
genre: '',
|
||||||
page: page,
|
page: page,
|
||||||
@ -141,13 +141,12 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||||||
if (snapshot.hasData && snapshot.data.isNotEmpty) {
|
if (snapshot.hasData && snapshot.data.isNotEmpty) {
|
||||||
final history = snapshot.data;
|
final history = snapshot.data;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 50,
|
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
children: history
|
children: history
|
||||||
.map<Widget>((e) => Padding(
|
.map<Widget>((e) => Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
8, 8, 0, 8),
|
8, 2, 0, 0),
|
||||||
child: FlatButton.icon(
|
child: FlatButton.icon(
|
||||||
color: Colors
|
color: Colors
|
||||||
.accents[history.indexOf(e)]
|
.accents[history.indexOf(e)]
|
||||||
@ -159,7 +158,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
|
|||||||
onPressed: () => widget.onTap(e),
|
onPressed: () => widget.onTap(e),
|
||||||
label: Text(e),
|
label: Text(e),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.bookmark_border,
|
Icons.search,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -313,7 +312,7 @@ class __TopPodcastListState extends State<_TopPodcastList> {
|
|||||||
bool _loading;
|
bool _loading;
|
||||||
int _page;
|
int _page;
|
||||||
Future<List<OnlinePodcast>> _getTopPodcasts({Genre genre, int page}) async {
|
Future<List<OnlinePodcast>> _getTopPodcasts({Genre genre, int page}) async {
|
||||||
final searchEngine = SearchEngine();
|
final searchEngine = ListenNotesSearch();
|
||||||
var searchResult = await searchEngine.fetchBestPodcast(
|
var searchResult = await searchEngine.fetchBestPodcast(
|
||||||
genre: genre.id,
|
genre: genre.id,
|
||||||
page: page,
|
page: page,
|
||||||
|
@ -11,11 +11,11 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:webfeed/webfeed.dart';
|
import 'package:webfeed/webfeed.dart';
|
||||||
|
|
||||||
import '../local_storage/key_value_storage.dart';
|
import '../local_storage/key_value_storage.dart';
|
||||||
import '../service/api_search.dart';
|
import '../service/search_api.dart';
|
||||||
import '../state/podcast_group.dart';
|
import '../state/podcast_group.dart';
|
||||||
import '../state/search_state.dart';
|
import '../state/search_state.dart';
|
||||||
import '../type/searchepisodes.dart';
|
import '../type/search_api/searchepisodes.dart';
|
||||||
import '../type/searchpodcast.dart';
|
import '../type/search_api/searchpodcast.dart';
|
||||||
import '../util/extension_helper.dart';
|
import '../util/extension_helper.dart';
|
||||||
import 'pocast_discovery.dart';
|
import 'pocast_discovery.dart';
|
||||||
|
|
||||||
@ -359,7 +359,7 @@ class _SearchListState extends State<SearchList> {
|
|||||||
Future<List<OnlinePodcast>> _getList(
|
Future<List<OnlinePodcast>> _getList(
|
||||||
String searchText, int nextOffset) async {
|
String searchText, int nextOffset) async {
|
||||||
if (nextOffset == 0) _saveHistory(searchText);
|
if (nextOffset == 0) _saveHistory(searchText);
|
||||||
final searchEngine = SearchEngine();
|
final searchEngine = ListenNotesSearch();
|
||||||
var searchResult = await searchEngine.searchPodcasts(
|
var searchResult = await searchEngine.searchPodcasts(
|
||||||
searchText: searchText, nextOffset: nextOffset);
|
searchText: searchText, nextOffset: nextOffset);
|
||||||
_offset = searchResult.nextOffset;
|
_offset = searchResult.nextOffset;
|
||||||
@ -562,7 +562,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
|
|||||||
|
|
||||||
Future<List<OnlineEpisode>> _getEpisodes(
|
Future<List<OnlineEpisode>> _getEpisodes(
|
||||||
{String id, int nextEpisodeDate}) async {
|
{String id, int nextEpisodeDate}) async {
|
||||||
var searchEngine = SearchEngine();
|
var searchEngine = ListenNotesSearch();
|
||||||
var searchResult = await searchEngine.fetchEpisode(
|
var searchResult = await searchEngine.fetchEpisode(
|
||||||
id: id, nextEpisodeDate: nextEpisodeDate);
|
id: id, nextEpisodeDate: nextEpisodeDate);
|
||||||
_nextEpisdoeDate = searchResult.nextEpisodeDate;
|
_nextEpisdoeDate = searchResult.nextEpisodeDate;
|
||||||
|
@ -140,6 +140,8 @@
|
|||||||
},
|
},
|
||||||
"goodNight": "Good Night",
|
"goodNight": "Good Night",
|
||||||
"@goodNight": {},
|
"@goodNight": {},
|
||||||
|
"gpodderLoginDes": "Congratulations! You have linked gpodder.net account successfully. Tsacdop will automatically sync subscriptions on your device with your gpodder.net account.",
|
||||||
|
"@gpodderLoginDes": {},
|
||||||
"groupExisted": "Group already exists",
|
"groupExisted": "Group already exists",
|
||||||
"@groupExisted": {
|
"@groupExisted": {
|
||||||
"description": "Group name validate in add group dialog. User can't add group with same name."
|
"description": "Group name validate in add group dialog. User can't add group with same name."
|
||||||
@ -180,12 +182,25 @@
|
|||||||
"@hoursCount": {},
|
"@hoursCount": {},
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"@import": {},
|
"@import": {},
|
||||||
|
"intergateWith": "Integrate with {service}",
|
||||||
|
"@intergateWith": {
|
||||||
|
"description": "Integrate with",
|
||||||
|
"placeholders": {
|
||||||
|
"service": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
"introFourthPage": "You can long press on episode card for quick actions.",
|
"introFourthPage": "You can long press on episode card for quick actions.",
|
||||||
"@introFourthPage": {},
|
"@introFourthPage": {},
|
||||||
"introSecondPage": "Subscribe podcast via search or import OPML file.",
|
"introSecondPage": "Subscribe podcast via search or import OPML file.",
|
||||||
"@introSecondPage": {},
|
"@introSecondPage": {},
|
||||||
"introThirdPage": "You can create new group for podcasts.",
|
"introThirdPage": "You can create new group for podcasts.",
|
||||||
"@introThirdPage": {},
|
"@introThirdPage": {},
|
||||||
|
"invalidName": "Invalid username",
|
||||||
|
"@invalidName": {},
|
||||||
|
"lastUpdate": "Last update",
|
||||||
|
"@lastUpdate": {
|
||||||
|
"description": "gpodder.net update"
|
||||||
|
},
|
||||||
"later": "Later",
|
"later": "Later",
|
||||||
"@later": {},
|
"@later": {},
|
||||||
"lightMode": "Light mode",
|
"lightMode": "Light mode",
|
||||||
@ -204,6 +219,23 @@
|
|||||||
"@listened": {},
|
"@listened": {},
|
||||||
"loadMore": "Load more",
|
"loadMore": "Load more",
|
||||||
"@loadMore": {},
|
"@loadMore": {},
|
||||||
|
"loggedInAs": "Logged in as {userName}",
|
||||||
|
"@loggedInAs": {
|
||||||
|
"description": "gpodder.net",
|
||||||
|
"placeholders": {
|
||||||
|
"userName": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": "Login",
|
||||||
|
"@login": {
|
||||||
|
"description": "gpodder.net login"
|
||||||
|
},
|
||||||
|
"loginFailed": "Login failed",
|
||||||
|
"@loginFailed": {},
|
||||||
|
"logout": "Logout",
|
||||||
|
"@logout": {
|
||||||
|
"description": "gpodder.net logout"
|
||||||
|
},
|
||||||
"mark": "Mark",
|
"mark": "Mark",
|
||||||
"@mark": {
|
"@mark": {
|
||||||
"description": "In listen history page, if a episode is marked as listened."
|
"description": "In listen history page, if a episode is marked as listened."
|
||||||
@ -292,6 +324,10 @@
|
|||||||
},
|
},
|
||||||
"oldestFirst": "Oldest first",
|
"oldestFirst": "Oldest first",
|
||||||
"@oldestFirst": {},
|
"@oldestFirst": {},
|
||||||
|
"passwdRequired": "Password required",
|
||||||
|
"@passwdRequired": {},
|
||||||
|
"password": "Password",
|
||||||
|
"@password": {},
|
||||||
"pause": "Pause",
|
"pause": "Pause",
|
||||||
"@pause": {},
|
"@pause": {},
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
@ -547,12 +583,18 @@
|
|||||||
"@skipToNext": {},
|
"@skipToNext": {},
|
||||||
"sleepTimer": "Sleep timer",
|
"sleepTimer": "Sleep timer",
|
||||||
"@sleepTimer": {},
|
"@sleepTimer": {},
|
||||||
|
"status": "Status",
|
||||||
|
"@status": {
|
||||||
|
"description": "gpodder.net status"
|
||||||
|
},
|
||||||
"stop": "Stop",
|
"stop": "Stop",
|
||||||
"@stop": {},
|
"@stop": {},
|
||||||
"subscribe": "Subscribe",
|
"subscribe": "Subscribe",
|
||||||
"@subscribe": {},
|
"@subscribe": {},
|
||||||
"subscribeExportDes": "Export OPML file of all podcasts",
|
"subscribeExportDes": "Export OPML file of all podcasts",
|
||||||
"@subscribeExportDes": {},
|
"@subscribeExportDes": {},
|
||||||
|
"syncNow": "Sync now",
|
||||||
|
"@syncNow": {},
|
||||||
"systemDefault": "System default",
|
"systemDefault": "System default",
|
||||||
"@systemDefault": {},
|
"@systemDefault": {},
|
||||||
"timeLastPlayed": "Last time {time}",
|
"timeLastPlayed": "Last time {time}",
|
||||||
@ -628,6 +670,10 @@
|
|||||||
"@updateEpisodesCount": {},
|
"@updateEpisodesCount": {},
|
||||||
"updateFailed": "Update failed, network error",
|
"updateFailed": "Update failed, network error",
|
||||||
"@updateFailed": {},
|
"@updateFailed": {},
|
||||||
|
"username": "Username",
|
||||||
|
"@username": {},
|
||||||
|
"usernameRequired": "Username required",
|
||||||
|
"@usernameRequired": {},
|
||||||
"version": "Version: {version}",
|
"version": "Version: {version}",
|
||||||
"@version": {
|
"@version": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
@ -140,6 +140,8 @@
|
|||||||
},
|
},
|
||||||
"goodNight": "Buenas noches",
|
"goodNight": "Buenas noches",
|
||||||
"@goodNight": {},
|
"@goodNight": {},
|
||||||
|
"gpodderLoginDes": "Congratulations! You have linked gpodder.net account successfully. Tsacdop will automatically sync subscriptions on your device with your gpodder.net account.",
|
||||||
|
"@gpodderLoginDes": {},
|
||||||
"groupExisted": "El grupo ya existe",
|
"groupExisted": "El grupo ya existe",
|
||||||
"@groupExisted": {
|
"@groupExisted": {
|
||||||
"description": "Group name validate in add group dialog. User can't add group with same name."
|
"description": "Group name validate in add group dialog. User can't add group with same name."
|
||||||
@ -180,12 +182,25 @@
|
|||||||
"@hoursCount": {},
|
"@hoursCount": {},
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
"@import": {},
|
"@import": {},
|
||||||
|
"intergateWith": "Integrate with {service}",
|
||||||
|
"@intergateWith": {
|
||||||
|
"description": "Integrate with",
|
||||||
|
"placeholders": {
|
||||||
|
"service": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
"introFourthPage": "Puedes mantener presionado un episodio para realizar acciones rápidas",
|
"introFourthPage": "Puedes mantener presionado un episodio para realizar acciones rápidas",
|
||||||
"@introFourthPage": {},
|
"@introFourthPage": {},
|
||||||
"introSecondPage": "Suscribete a podcasts buscándolos, o importando un archivo OPML",
|
"introSecondPage": "Suscribete a podcasts buscándolos, o importando un archivo OPML",
|
||||||
"@introSecondPage": {},
|
"@introSecondPage": {},
|
||||||
"introThirdPage": "Puedes crear un nuevo grupo de podcasts",
|
"introThirdPage": "Puedes crear un nuevo grupo de podcasts",
|
||||||
"@introThirdPage": {},
|
"@introThirdPage": {},
|
||||||
|
"invalidName": "Invalid username",
|
||||||
|
"@invalidName": {},
|
||||||
|
"lastUpdate": "Last update",
|
||||||
|
"@lastUpdate": {
|
||||||
|
"description": "gpodder.net update"
|
||||||
|
},
|
||||||
"later": "Despues",
|
"later": "Despues",
|
||||||
"@later": {},
|
"@later": {},
|
||||||
"lightMode": "Modo claro",
|
"lightMode": "Modo claro",
|
||||||
@ -204,6 +219,23 @@
|
|||||||
"@listened": {},
|
"@listened": {},
|
||||||
"loadMore": "Cargar mas",
|
"loadMore": "Cargar mas",
|
||||||
"@loadMore": {},
|
"@loadMore": {},
|
||||||
|
"loggedInAs": "Logged in as {userName}",
|
||||||
|
"@loggedInAs": {
|
||||||
|
"description": "gpodder.net",
|
||||||
|
"placeholders": {
|
||||||
|
"userName": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": "Loign",
|
||||||
|
"@login": {
|
||||||
|
"description": "gpodder.net login"
|
||||||
|
},
|
||||||
|
"loginFailed": "Login failed",
|
||||||
|
"@loginFailed": {},
|
||||||
|
"logout": "Logout",
|
||||||
|
"@logout": {
|
||||||
|
"description": "gpodder.net logout"
|
||||||
|
},
|
||||||
"mark": "Completado",
|
"mark": "Completado",
|
||||||
"@mark": {
|
"@mark": {
|
||||||
"description": "In listen history page, if a episode is marked as listened."
|
"description": "In listen history page, if a episode is marked as listened."
|
||||||
@ -292,6 +324,10 @@
|
|||||||
},
|
},
|
||||||
"oldestFirst": "Mas antiguos primero",
|
"oldestFirst": "Mas antiguos primero",
|
||||||
"@oldestFirst": {},
|
"@oldestFirst": {},
|
||||||
|
"passwdRequired": "Password required",
|
||||||
|
"@passwdRequired": {},
|
||||||
|
"password": "Password",
|
||||||
|
"@password": {},
|
||||||
"pause": "Pause",
|
"pause": "Pause",
|
||||||
"@pause": {},
|
"@pause": {},
|
||||||
"play": "Reproducir",
|
"play": "Reproducir",
|
||||||
@ -547,12 +583,18 @@
|
|||||||
"@skipToNext": {},
|
"@skipToNext": {},
|
||||||
"sleepTimer": "Temporizador de sueño",
|
"sleepTimer": "Temporizador de sueño",
|
||||||
"@sleepTimer": {},
|
"@sleepTimer": {},
|
||||||
|
"status": "Status",
|
||||||
|
"@status": {
|
||||||
|
"description": "gpodder.net status"
|
||||||
|
},
|
||||||
"stop": "Stop",
|
"stop": "Stop",
|
||||||
"@stop": {},
|
"@stop": {},
|
||||||
"subscribe": "Suscribir",
|
"subscribe": "Suscribir",
|
||||||
"@subscribe": {},
|
"@subscribe": {},
|
||||||
"subscribeExportDes": "Exportar OPML de todos los podcasts",
|
"subscribeExportDes": "Exportar OPML de todos los podcasts",
|
||||||
"@subscribeExportDes": {},
|
"@subscribeExportDes": {},
|
||||||
|
"syncNow": "Sync now",
|
||||||
|
"@syncNow": {},
|
||||||
"systemDefault": "Acorde al sistema",
|
"systemDefault": "Acorde al sistema",
|
||||||
"@systemDefault": {},
|
"@systemDefault": {},
|
||||||
"timeLastPlayed": "Tiempo previo {time}",
|
"timeLastPlayed": "Tiempo previo {time}",
|
||||||
@ -628,6 +670,10 @@
|
|||||||
"@updateEpisodesCount": {},
|
"@updateEpisodesCount": {},
|
||||||
"updateFailed": "Actualización fallida, error de red",
|
"updateFailed": "Actualización fallida, error de red",
|
||||||
"@updateFailed": {},
|
"@updateFailed": {},
|
||||||
|
"username": "Username",
|
||||||
|
"@username": {},
|
||||||
|
"usernameRequired": "Username required",
|
||||||
|
"@usernameRequired": {},
|
||||||
"version": "Versión: {version}",
|
"version": "Versión: {version}",
|
||||||
"@version": {
|
"@version": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
@ -140,6 +140,8 @@
|
|||||||
},
|
},
|
||||||
"goodNight": "Bonne nuit",
|
"goodNight": "Bonne nuit",
|
||||||
"@goodNight": {},
|
"@goodNight": {},
|
||||||
|
"gpodderLoginDes": "Congratulations! You have linked gpodder.net account successfully. Tsacdop will automatically sync subscriptions on your device with your gpodder.net account.",
|
||||||
|
"@gpodderLoginDes": {},
|
||||||
"groupExisted": "Ce groupe existe déjà",
|
"groupExisted": "Ce groupe existe déjà",
|
||||||
"@groupExisted": {
|
"@groupExisted": {
|
||||||
"description": "Group name validate in add group dialog. User can't add group with same name."
|
"description": "Group name validate in add group dialog. User can't add group with same name."
|
||||||
@ -180,12 +182,25 @@
|
|||||||
"@hoursCount": {},
|
"@hoursCount": {},
|
||||||
"import": "Importer",
|
"import": "Importer",
|
||||||
"@import": {},
|
"@import": {},
|
||||||
|
"intergateWith": "Integrate with {service}",
|
||||||
|
"@intergateWith": {
|
||||||
|
"description": "Integrate with",
|
||||||
|
"placeholders": {
|
||||||
|
"service": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
"introFourthPage": "Un appui long sur un épisode lance les actions rapides.",
|
"introFourthPage": "Un appui long sur un épisode lance les actions rapides.",
|
||||||
"@introFourthPage": {},
|
"@introFourthPage": {},
|
||||||
"introSecondPage": "S'abonner aux podcasts via la section recherche ou un fichier OPML.",
|
"introSecondPage": "S'abonner aux podcasts via la section recherche ou un fichier OPML.",
|
||||||
"@introSecondPage": {},
|
"@introSecondPage": {},
|
||||||
"introThirdPage": "Vous pouvez créer des groupes de podcasts.",
|
"introThirdPage": "Vous pouvez créer des groupes de podcasts.",
|
||||||
"@introThirdPage": {},
|
"@introThirdPage": {},
|
||||||
|
"invalidName": "Invalid username",
|
||||||
|
"@invalidName": {},
|
||||||
|
"lastUpdate": "Last update",
|
||||||
|
"@lastUpdate": {
|
||||||
|
"description": "gpodder.net update"
|
||||||
|
},
|
||||||
"later": "Plus tard",
|
"later": "Plus tard",
|
||||||
"@later": {},
|
"@later": {},
|
||||||
"lightMode": "Mode clair",
|
"lightMode": "Mode clair",
|
||||||
@ -204,6 +219,23 @@
|
|||||||
"@listened": {},
|
"@listened": {},
|
||||||
"loadMore": "Voir plus",
|
"loadMore": "Voir plus",
|
||||||
"@loadMore": {},
|
"@loadMore": {},
|
||||||
|
"loggedInAs": "Logged in as {userName}",
|
||||||
|
"@loggedInAs": {
|
||||||
|
"description": "gpodder.net",
|
||||||
|
"placeholders": {
|
||||||
|
"userName": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": "Login",
|
||||||
|
"@login": {
|
||||||
|
"description": "gpodder.net login"
|
||||||
|
},
|
||||||
|
"loginFailed": "Login failed",
|
||||||
|
"@loginFailed": {},
|
||||||
|
"logout": "Logout",
|
||||||
|
"@logout": {
|
||||||
|
"description": "gpodder.net logout"
|
||||||
|
},
|
||||||
"mark": "✓",
|
"mark": "✓",
|
||||||
"@mark": {
|
"@mark": {
|
||||||
"description": "In listen history page, if a episode is marked as listened."
|
"description": "In listen history page, if a episode is marked as listened."
|
||||||
@ -292,6 +324,10 @@
|
|||||||
},
|
},
|
||||||
"oldestFirst": "Le plus ancien en premier",
|
"oldestFirst": "Le plus ancien en premier",
|
||||||
"@oldestFirst": {},
|
"@oldestFirst": {},
|
||||||
|
"passwdRequired": "Password required",
|
||||||
|
"@passwdRequired": {},
|
||||||
|
"password": "Password",
|
||||||
|
"@password": {},
|
||||||
"pause": "Pause",
|
"pause": "Pause",
|
||||||
"@pause": {},
|
"@pause": {},
|
||||||
"play": "Lecture",
|
"play": "Lecture",
|
||||||
@ -547,12 +583,18 @@
|
|||||||
"@skipToNext": {},
|
"@skipToNext": {},
|
||||||
"sleepTimer": "Minuterie",
|
"sleepTimer": "Minuterie",
|
||||||
"@sleepTimer": {},
|
"@sleepTimer": {},
|
||||||
|
"status": "Status",
|
||||||
|
"@status": {
|
||||||
|
"description": "gpodder.net status"
|
||||||
|
},
|
||||||
"stop": "Stop",
|
"stop": "Stop",
|
||||||
"@stop": {},
|
"@stop": {},
|
||||||
"subscribe": "S'abonner",
|
"subscribe": "S'abonner",
|
||||||
"@subscribe": {},
|
"@subscribe": {},
|
||||||
"subscribeExportDes": "Exporter le fichier OPML de tous les podcasts.",
|
"subscribeExportDes": "Exporter le fichier OPML de tous les podcasts.",
|
||||||
"@subscribeExportDes": {},
|
"@subscribeExportDes": {},
|
||||||
|
"syncNow": "Sync now",
|
||||||
|
"@syncNow": {},
|
||||||
"systemDefault": "Système par défaut",
|
"systemDefault": "Système par défaut",
|
||||||
"@systemDefault": {},
|
"@systemDefault": {},
|
||||||
"timeLastPlayed": "Dernière écoute à {time}",
|
"timeLastPlayed": "Dernière écoute à {time}",
|
||||||
@ -628,6 +670,10 @@
|
|||||||
"@updateEpisodesCount": {},
|
"@updateEpisodesCount": {},
|
||||||
"updateFailed": "Échec de la mise à jour, erreur réseau",
|
"updateFailed": "Échec de la mise à jour, erreur réseau",
|
||||||
"@updateFailed": {},
|
"@updateFailed": {},
|
||||||
|
"username": "Username",
|
||||||
|
"@username": {},
|
||||||
|
"usernameRequired": "Username required",
|
||||||
|
"@usernameRequired": {},
|
||||||
"version": "Version : {version}",
|
"version": "Version : {version}",
|
||||||
"@version": {
|
"@version": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
683
lib/l10n/intl_pt.arb
Normal file
683
lib/l10n/intl_pt.arb
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
{
|
||||||
|
"@@locale": "pt",
|
||||||
|
"add": "Adicionar",
|
||||||
|
"@add": {
|
||||||
|
"description": "Subscribe new podcast"
|
||||||
|
},
|
||||||
|
"addEpisodeGroup": "{count, plural, zero{} one{{count} episódio de {groupName} adicionado à lista} other{{count} episódios de {groupName} adicionados à lista}}",
|
||||||
|
"@addEpisodeGroup": {
|
||||||
|
"placeholders": {
|
||||||
|
"groupName": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"addNewEpisodeAll": "{count, plural, zero{} one{{count} episódio adicionado à lista} other{{count} episódios adicionados à lista}}",
|
||||||
|
"@addNewEpisodeAll": {},
|
||||||
|
"addNewEpisodeTooltip": "Adiciona novos episódios à lista de reprodução",
|
||||||
|
"@addNewEpisodeTooltip": {},
|
||||||
|
"addSomeGroups": "Adiciona alguns grupos",
|
||||||
|
"@addSomeGroups": {
|
||||||
|
"description": "Please add new groups"
|
||||||
|
},
|
||||||
|
"all": "Todos",
|
||||||
|
"@all": {},
|
||||||
|
"autoDownload": "Download automático",
|
||||||
|
"@autoDownload": {},
|
||||||
|
"back": "Atrás",
|
||||||
|
"@back": {},
|
||||||
|
"boostVolume": "Aumentar volume",
|
||||||
|
"@boostVolume": {
|
||||||
|
"description": "Boost volume in player widget."
|
||||||
|
},
|
||||||
|
"buffering": "A carregar",
|
||||||
|
"@buffering": {},
|
||||||
|
"cancel": "CANCELAR",
|
||||||
|
"@cancel": {},
|
||||||
|
"cellularConfirm": "Alerta de dados móveis",
|
||||||
|
"@cellularConfirm": {},
|
||||||
|
"cellularConfirmDes": "Tens a certeza que queres usar dados móveis para downloads?",
|
||||||
|
"@cellularConfirmDes": {},
|
||||||
|
"changeLayout": "Mudar aparência",
|
||||||
|
"@changeLayout": {},
|
||||||
|
"changelog": "Registo de mudanças",
|
||||||
|
"@changelog": {},
|
||||||
|
"chooseA": "Escolher um",
|
||||||
|
"@chooseA": {},
|
||||||
|
"clear": "Limpar",
|
||||||
|
"@clear": {},
|
||||||
|
"color": "Cor",
|
||||||
|
"@color": {},
|
||||||
|
"confirm": "CONFIRMAR",
|
||||||
|
"@confirm": {},
|
||||||
|
"darkMode": "Modo escuro",
|
||||||
|
"@darkMode": {},
|
||||||
|
"daysAgo": "{count, plural, zero{Hoje} one{Há {count} dia} other{Há {count} dias}}",
|
||||||
|
"@daysAgo": {},
|
||||||
|
"daysCount": "{count, plural, zero{Nunca} one{{count} dia} other{{count} dias}}",
|
||||||
|
"@daysCount": {},
|
||||||
|
"delete": "Eliminar",
|
||||||
|
"@delete": {},
|
||||||
|
"developer": "Desenvolvedor",
|
||||||
|
"@developer": {
|
||||||
|
"description": "Can also translate to About me"
|
||||||
|
},
|
||||||
|
"dismiss": "Minimizar",
|
||||||
|
"@dismiss": {},
|
||||||
|
"done": "Feito",
|
||||||
|
"@done": {},
|
||||||
|
"download": "Download",
|
||||||
|
"@download": {},
|
||||||
|
"downloaded": "Descarregado",
|
||||||
|
"@downloaded": {},
|
||||||
|
"downloadRemovedToast": "Download removido",
|
||||||
|
"@downloadRemovedToast": {},
|
||||||
|
"editGroupName": "Editar nome do grupo",
|
||||||
|
"@editGroupName": {},
|
||||||
|
"endOfEpisode": "Fim do episódio",
|
||||||
|
"@endOfEpisode": {},
|
||||||
|
"episode": "{count, plural, zero{} one{Episódio} other{Episódios}}",
|
||||||
|
"@episode": {},
|
||||||
|
"fastForward": "Avanço",
|
||||||
|
"@fastForward": {},
|
||||||
|
"fastRewind": "Recuo rápido",
|
||||||
|
"@fastRewind": {},
|
||||||
|
"featureDiscoveryEditGroup": "Prime para editar grupo",
|
||||||
|
"@featureDiscoveryEditGroup": {},
|
||||||
|
"featureDiscoveryEditGroupDes": "Podes alterar o nome do grupo ou apagá-lo aqui, mas o grupo Home não pode ser editado ou eliminado",
|
||||||
|
"@featureDiscoveryEditGroupDes": {},
|
||||||
|
"featureDiscoveryEpisode": "Vista de episódios",
|
||||||
|
"@featureDiscoveryEpisode": {},
|
||||||
|
"featureDiscoveryEpisodeDes": "Podes manter premido para reproduzir um episódio ou adicioná-lo a uma lista de reprodução.",
|
||||||
|
"@featureDiscoveryEpisodeDes": {},
|
||||||
|
"featureDiscoveryEpisodeTitle": "Mantém premido para reproduzir um episódio instantâneamente",
|
||||||
|
"@featureDiscoveryEpisodeTitle": {},
|
||||||
|
"featureDiscoveryGroup": "Prime para adicionar grupo",
|
||||||
|
"@featureDiscoveryGroup": {},
|
||||||
|
"featureDiscoveryGroupDes": "O grupo por defeito para novos podcasts é Home. Podes criar novos grupos e mover os podcasts para estes, assim como adicionar podcasts a múltiplos grupos.",
|
||||||
|
"@featureDiscoveryGroupDes": {},
|
||||||
|
"featureDiscoveryGroupPodcast": "Mantém premido para reordenar podcasts",
|
||||||
|
"@featureDiscoveryGroupPodcast": {},
|
||||||
|
"featureDiscoveryGroupPodcastDes": "Podes premir para ver mais opções, ou manter premido para reordenar podcasts em grupos.",
|
||||||
|
"@featureDiscoveryGroupPodcastDes": {},
|
||||||
|
"featureDiscoveryOMPL": "Premir para importar um OPML",
|
||||||
|
"@featureDiscoveryOMPL": {},
|
||||||
|
"featureDiscoveryOMPLDes": "Podes importar ficheiros OPML, abrir as definições ou atualizar todos os podcasts aqui.",
|
||||||
|
"@featureDiscoveryOMPLDes": {},
|
||||||
|
"featureDiscoveryPlaylist": "Prime para abrir a lista de reprodução",
|
||||||
|
"@featureDiscoveryPlaylist": {},
|
||||||
|
"featureDiscoveryPlaylistDes": "Podes adicionar episódios à lista de reprodução manualmente. Os episódios serão automaticamente removidos das listas de reprodução quando reproduzidos.",
|
||||||
|
"@featureDiscoveryPlaylistDes": {},
|
||||||
|
"featureDiscoveryPodcast": "Vista do podcast",
|
||||||
|
"@featureDiscoveryPodcast": {},
|
||||||
|
"featureDiscoveryPodcastDes": "Podes premir \"Ver Todos\" para adicionar grupos ou organizar pdcasts.",
|
||||||
|
"@featureDiscoveryPodcastDes": {},
|
||||||
|
"featureDiscoveryPodcastTitle": "Deslizar verticalmente para alterar grupos",
|
||||||
|
"@featureDiscoveryPodcastTitle": {},
|
||||||
|
"featureDiscoverySearch": "Prime para procurar podcasts",
|
||||||
|
"@featureDiscoverySearch": {},
|
||||||
|
"featureDiscoverySearchDes": "Podes procurar pelo título do podcast, palavra-chave ou ligação RSS para subscrever novos podcasts.",
|
||||||
|
"@featureDiscoverySearchDes": {},
|
||||||
|
"feedbackEmail": "Escreve-me",
|
||||||
|
"@feedbackEmail": {},
|
||||||
|
"feedbackGithub": "Submeter problema",
|
||||||
|
"@feedbackGithub": {},
|
||||||
|
"feedbackPlay": "Avaliar na Play Store",
|
||||||
|
"@feedbackPlay": {
|
||||||
|
"description": "Rate on Google Play Store.\nUser can tap to open play link."
|
||||||
|
},
|
||||||
|
"feedbackTelegram": "Juntar um grupo",
|
||||||
|
"@feedbackTelegram": {},
|
||||||
|
"filter": "Filtro",
|
||||||
|
"@filter": {},
|
||||||
|
"fonts": "Fontes",
|
||||||
|
"@fonts": {},
|
||||||
|
"fontStyle": "Estilo do tipo de letra",
|
||||||
|
"@fontStyle": {},
|
||||||
|
"from": "De {time}",
|
||||||
|
"@from": {
|
||||||
|
"placeholders": {
|
||||||
|
"time": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"goodNight": "Boa Noite",
|
||||||
|
"@goodNight": {},
|
||||||
|
"gpodderLoginDes": "Congratulations! You have linked gpodder.net account successfully. Tsacdop will automatically sync subscriptions on your device with your gpodder.net account.",
|
||||||
|
"@gpodderLoginDes": {},
|
||||||
|
"groupExisted": "Grupo já existe",
|
||||||
|
"@groupExisted": {
|
||||||
|
"description": "Group name validate in add group dialog. User can't add group with same name."
|
||||||
|
},
|
||||||
|
"groupFilter": "Filtro de grupo",
|
||||||
|
"@groupFilter": {},
|
||||||
|
"groupRemoveConfirm": "Tens a certeza que queres eliminar este grupo? Os podcasts serão removidos para o grupo \"Home\".",
|
||||||
|
"@groupRemoveConfirm": {},
|
||||||
|
"groups": "{count, plural, zero{Grupo} one{Grupo} other{Grupos}}",
|
||||||
|
"@groups": {},
|
||||||
|
"hideListenedSetting": "Esconder ouvidos",
|
||||||
|
"@hideListenedSetting": {},
|
||||||
|
"homeGroupsSeeAll": "Ver Todos",
|
||||||
|
"@homeGroupsSeeAll": {},
|
||||||
|
"homeMenuPlaylist": "Lista de Reprodução",
|
||||||
|
"@homeMenuPlaylist": {},
|
||||||
|
"homeSubMenuSortBy": "Ordenar por",
|
||||||
|
"@homeSubMenuSortBy": {},
|
||||||
|
"homeTabMenuFavotite": "Favorito",
|
||||||
|
"@homeTabMenuFavotite": {},
|
||||||
|
"homeTabMenuRecent": "Recentes",
|
||||||
|
"@homeTabMenuRecent": {},
|
||||||
|
"homeToprightMenuAbout": "Sobre",
|
||||||
|
"@homeToprightMenuAbout": {},
|
||||||
|
"homeToprightMenuImportOMPL": "Importar OPML",
|
||||||
|
"@homeToprightMenuImportOMPL": {},
|
||||||
|
"homeToprightMenuRefreshAll": "Atualizar todos",
|
||||||
|
"@homeToprightMenuRefreshAll": {},
|
||||||
|
"hostedOn": "Hospedado em {host}",
|
||||||
|
"@hostedOn": {
|
||||||
|
"placeholders": {
|
||||||
|
"host": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hoursAgo": "{count, plural, zero{} one{há {count} hora} other{há {count} horas}}",
|
||||||
|
"@hoursAgo": {},
|
||||||
|
"hoursCount": "{count, plural, zero{0 horas} one{{count} hora} other{{count} horas}}",
|
||||||
|
"@hoursCount": {},
|
||||||
|
"import": "Importar",
|
||||||
|
"@import": {},
|
||||||
|
"intergateWith": "Integrate with {service}",
|
||||||
|
"@intergateWith": {
|
||||||
|
"description": "Integrate with",
|
||||||
|
"placeholders": {
|
||||||
|
"service": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"introFourthPage": "Podes manter premido um episódio para uma ação rápida.",
|
||||||
|
"@introFourthPage": {},
|
||||||
|
"introSecondPage": "Subscreve podcasts por pesquisa ou importa um ficheiro OPML.",
|
||||||
|
"@introSecondPage": {},
|
||||||
|
"introThirdPage": "Podes criar um novo grupo para podcasts.",
|
||||||
|
"@introThirdPage": {},
|
||||||
|
"invalidName": "Invalid username",
|
||||||
|
"@invalidName": {},
|
||||||
|
"lastUpdate": "Last update",
|
||||||
|
"@lastUpdate": {
|
||||||
|
"description": "gpodder.net update"
|
||||||
|
},
|
||||||
|
"later": "Mais tarde",
|
||||||
|
"@later": {},
|
||||||
|
"lightMode": "Modo claro",
|
||||||
|
"@lightMode": {},
|
||||||
|
"like": "Gosto",
|
||||||
|
"@like": {},
|
||||||
|
"liked": "Gostou",
|
||||||
|
"@liked": {},
|
||||||
|
"likeDate": "Data do Gosto",
|
||||||
|
"@likeDate": {
|
||||||
|
"description": "Favorite tab, sort by like date."
|
||||||
|
},
|
||||||
|
"listen": "Ouvir",
|
||||||
|
"@listen": {},
|
||||||
|
"listened": "Ouvido",
|
||||||
|
"@listened": {},
|
||||||
|
"loadMore": "Carregar mais",
|
||||||
|
"@loadMore": {},
|
||||||
|
"loggedInAs": "Logged in as {userName}",
|
||||||
|
"@loggedInAs": {
|
||||||
|
"description": "gpodder.net",
|
||||||
|
"placeholders": {
|
||||||
|
"userName": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": "Login",
|
||||||
|
"@login": {
|
||||||
|
"description": "gpodder.net login"
|
||||||
|
},
|
||||||
|
"loginFailed": "Login failed",
|
||||||
|
"@loginFailed": {},
|
||||||
|
"logout": "Logout",
|
||||||
|
"@logout": {
|
||||||
|
"description": "gpodder.net logout"
|
||||||
|
},
|
||||||
|
"mark": "Marcar",
|
||||||
|
"@mark": {
|
||||||
|
"description": "In listen history page, if a episode is marked as listened."
|
||||||
|
},
|
||||||
|
"markConfirm": "Confirmar marca",
|
||||||
|
"@markConfirm": {},
|
||||||
|
"markConfirmContent": "Marcar todos os episódios como ouvidos?",
|
||||||
|
"@markConfirmContent": {},
|
||||||
|
"markListened": "Marcar como ouvido",
|
||||||
|
"@markListened": {},
|
||||||
|
"markNotListened": "Marcar não ouvidos",
|
||||||
|
"@markNotListened": {},
|
||||||
|
"menu": "Menu",
|
||||||
|
"@menu": {},
|
||||||
|
"menuAllPodcasts": "Todos os podcasts",
|
||||||
|
"@menuAllPodcasts": {},
|
||||||
|
"menuMarkAllListened": "Marcar todos como ouvidos",
|
||||||
|
"@menuMarkAllListened": {},
|
||||||
|
"menuViewRSS": "Visitar Feed RSS",
|
||||||
|
"@menuViewRSS": {},
|
||||||
|
"menuVisitSite": "Visitar website",
|
||||||
|
"@menuVisitSite": {},
|
||||||
|
"minsAgo": "{count, plural, zero{Agora} one{Há {count} minuto} other{Há {count} minutos}}",
|
||||||
|
"@minsAgo": {},
|
||||||
|
"minsCount": "{count, plural, zero{0 minutos} one{{count} minuto} other{{count} minutos}}",
|
||||||
|
"@minsCount": {},
|
||||||
|
"network": "Rede",
|
||||||
|
"@network": {},
|
||||||
|
"newestFirst": "Mais recentes primeiro",
|
||||||
|
"@newestFirst": {},
|
||||||
|
"newGroup": "Criar um novo grupo",
|
||||||
|
"@newGroup": {},
|
||||||
|
"next": "Seguinte",
|
||||||
|
"@next": {},
|
||||||
|
"noEpisodeDownload": "Ainda não há episódios descarregados",
|
||||||
|
"@noEpisodeDownload": {},
|
||||||
|
"noEpisodeFavorite": "Ainda não há episódios coletados",
|
||||||
|
"@noEpisodeFavorite": {},
|
||||||
|
"noEpisodeRecent": "Ainda não há episódios recebidos",
|
||||||
|
"@noEpisodeRecent": {},
|
||||||
|
"noPodcastGroup": "Não há podcasts neste grupo",
|
||||||
|
"@noPodcastGroup": {},
|
||||||
|
"noShownote": "Não há notas disponíveis para este episódio",
|
||||||
|
"@noShownote": {
|
||||||
|
"description": "Means this episode have no show notes."
|
||||||
|
},
|
||||||
|
"notificaitonFatch": "Obter dados {title}",
|
||||||
|
"@notificaitonFatch": {},
|
||||||
|
"notificationNetworkError": "A subscrição falhou, erro de rede {title}",
|
||||||
|
"@notificationNetworkError": {
|
||||||
|
"placeholders": {
|
||||||
|
"title": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notificationSetting": "Painel de notificações",
|
||||||
|
"@notificationSetting": {},
|
||||||
|
"notificationSubscribe": "Subscrever {title}",
|
||||||
|
"@notificationSubscribe": {
|
||||||
|
"placeholders": {
|
||||||
|
"title": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notificationSubscribeExisted": "Subscrição falhou, podcast já existe {title}",
|
||||||
|
"@notificationSubscribeExisted": {
|
||||||
|
"placeholders": {
|
||||||
|
"title": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notificationSuccess": "Subscrito com sucesso {title}",
|
||||||
|
"@notificationSuccess": {
|
||||||
|
"placeholders": {
|
||||||
|
"title": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notificationUpdate": "Atualizar {title}",
|
||||||
|
"@notificationUpdate": {
|
||||||
|
"placeholders": {
|
||||||
|
"title": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notificationUpdateError": "Erro de atualização {title}",
|
||||||
|
"@notificationUpdateError": {
|
||||||
|
"placeholders": {
|
||||||
|
"title": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oldestFirst": "Mais antigos primeiro",
|
||||||
|
"@oldestFirst": {},
|
||||||
|
"passwdRequired": "Password required",
|
||||||
|
"@passwdRequired": {},
|
||||||
|
"password": "Password",
|
||||||
|
"@password": {},
|
||||||
|
"pause": "Pausa",
|
||||||
|
"@pause": {},
|
||||||
|
"play": "Reproduzir",
|
||||||
|
"@play": {},
|
||||||
|
"playback": "Controlo da reprodução",
|
||||||
|
"@playback": {},
|
||||||
|
"player": "Reprodutor",
|
||||||
|
"@player": {},
|
||||||
|
"playerHeightMed": "Médio",
|
||||||
|
"@playerHeightMed": {},
|
||||||
|
"playerHeightShort": "Baixo",
|
||||||
|
"@playerHeightShort": {},
|
||||||
|
"playerHeightTall": "Alto",
|
||||||
|
"@playerHeightTall": {},
|
||||||
|
"playing": "Em reprodução",
|
||||||
|
"@playing": {},
|
||||||
|
"plugins": "Plugins",
|
||||||
|
"@plugins": {},
|
||||||
|
"podcast": "{count, plural, zero{} one{Podcast} other{Podcasts}}",
|
||||||
|
"@podcast": {},
|
||||||
|
"podcastSubscribed": "Podcast subscrito",
|
||||||
|
"@podcastSubscribed": {},
|
||||||
|
"popupMenuDownloadDes": "Descarregar episódio",
|
||||||
|
"@popupMenuDownloadDes": {},
|
||||||
|
"popupMenuLaterDes": "Adicionar episódio à lista de reprodução",
|
||||||
|
"@popupMenuLaterDes": {},
|
||||||
|
"popupMenuLikeDes": "Adicionar episódio aos favoritos",
|
||||||
|
"@popupMenuLikeDes": {},
|
||||||
|
"popupMenuMarkDes": "Marcar episódio como ouvido",
|
||||||
|
"@popupMenuMarkDes": {},
|
||||||
|
"popupMenuPlayDes": "Reproduzir episódio",
|
||||||
|
"@popupMenuPlayDes": {},
|
||||||
|
"privacyPolicy": "Política de Privacidade",
|
||||||
|
"@privacyPolicy": {},
|
||||||
|
"published": "Publicado em {date}",
|
||||||
|
"@published": {
|
||||||
|
"placeholders": {
|
||||||
|
"date": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publishedDaily": "Publicado diariamente",
|
||||||
|
"@publishedDaily": {},
|
||||||
|
"publishedMonthly": "Publicado mensalmente",
|
||||||
|
"@publishedMonthly": {},
|
||||||
|
"publishedWeekly": "Publicado semanalmente",
|
||||||
|
"@publishedWeekly": {
|
||||||
|
"description": "In search podcast detail page."
|
||||||
|
},
|
||||||
|
"publishedYearly": "Publicado anualmente",
|
||||||
|
"@publishedYearly": {},
|
||||||
|
"recoverSubscribe": "Recuperar subscrição",
|
||||||
|
"@recoverSubscribe": {
|
||||||
|
"description": "User can recover subscribe podcast after remove it in subscribe history page."
|
||||||
|
},
|
||||||
|
"refreshArtwork": "Atualizar capa",
|
||||||
|
"@refreshArtwork": {},
|
||||||
|
"remove": "Remover",
|
||||||
|
"@remove": {
|
||||||
|
"description": "Remove not \"removed\". \nRemove a podcast or a group."
|
||||||
|
},
|
||||||
|
"removeConfirm": "Confirmação de remoção",
|
||||||
|
"@removeConfirm": {
|
||||||
|
"description": "unsubscribe podcast dialog"
|
||||||
|
},
|
||||||
|
"removedAt": "Removido em {date}",
|
||||||
|
"@removedAt": {
|
||||||
|
"description": "For example :Removed at 2020.10.10",
|
||||||
|
"placeholders": {
|
||||||
|
"date": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"removePodcastDes": "Tens a certeza que pretendes cancelar a subscrição?",
|
||||||
|
"@removePodcastDes": {},
|
||||||
|
"save": "Guardar",
|
||||||
|
"@save": {},
|
||||||
|
"schedule": "Horário",
|
||||||
|
"@schedule": {},
|
||||||
|
"search": "Procurar",
|
||||||
|
"@search": {},
|
||||||
|
"searchEpisode": "Procurar episódio",
|
||||||
|
"@searchEpisode": {},
|
||||||
|
"searchInvalidRss": "Ligação RSS inválida",
|
||||||
|
"@searchInvalidRss": {},
|
||||||
|
"searchPodcast": "Procurar podcasts",
|
||||||
|
"@searchPodcast": {},
|
||||||
|
"secCount": "{count, plural, zero{0 segundos} one{{count} segundo} other{{count} segundos}}",
|
||||||
|
"@secCount": {},
|
||||||
|
"secondsAgo": "{count, plural, zero{Agora} one{Há {count} segundo} other{Há {count} segundos}}",
|
||||||
|
"@secondsAgo": {},
|
||||||
|
"settings": "Definições",
|
||||||
|
"@settings": {},
|
||||||
|
"settingsAccentColor": "Cor de realce",
|
||||||
|
"@settingsAccentColor": {},
|
||||||
|
"settingsAccentColorDes": "Incluir cor de sobreposição",
|
||||||
|
"@settingsAccentColorDes": {},
|
||||||
|
"settingsAppearance": "Aparência",
|
||||||
|
"@settingsAppearance": {},
|
||||||
|
"settingsAppearanceDes": "Cores e temas",
|
||||||
|
"@settingsAppearanceDes": {},
|
||||||
|
"settingsAppIntro": "Introdução da Aplicação",
|
||||||
|
"@settingsAppIntro": {},
|
||||||
|
"settingsAudioCache": "Cache de áudio",
|
||||||
|
"@settingsAudioCache": {},
|
||||||
|
"settingsAudioCacheDes": "Tamanho máximo da cache de áudio",
|
||||||
|
"@settingsAudioCacheDes": {},
|
||||||
|
"settingsAutoDelete": "Eliminar downloads automaticamente após",
|
||||||
|
"@settingsAutoDelete": {},
|
||||||
|
"settingsAutoDeleteDes": "30 dias por defeito",
|
||||||
|
"@settingsAutoDeleteDes": {},
|
||||||
|
"settingsAutoPlayDes": "Reproduzir automaticamente o episódio seguinte",
|
||||||
|
"@settingsAutoPlayDes": {},
|
||||||
|
"settingsBackup": "Cópia de segurança",
|
||||||
|
"@settingsBackup": {},
|
||||||
|
"settingsBackupDes": "Cópia de segurança dos dados da aplicação",
|
||||||
|
"@settingsBackupDes": {},
|
||||||
|
"settingsBoostVolume": "Nível de aumento de volume",
|
||||||
|
"@settingsBoostVolume": {},
|
||||||
|
"settingsBoostVolumeDes": "Alterar nível de aumento de volume",
|
||||||
|
"@settingsBoostVolumeDes": {},
|
||||||
|
"settingsDefaultGrid": "Vista de grelha predefinida",
|
||||||
|
"@settingsDefaultGrid": {},
|
||||||
|
"settingsDefaultGridDownload": "Aba de downloads",
|
||||||
|
"@settingsDefaultGridDownload": {},
|
||||||
|
"settingsDefaultGridFavorite": "Aba de favoritos",
|
||||||
|
"@settingsDefaultGridFavorite": {},
|
||||||
|
"settingsDefaultGridPodcast": "Página de podcasts",
|
||||||
|
"@settingsDefaultGridPodcast": {},
|
||||||
|
"settingsDefaultGridRecent": "Aba de recentes",
|
||||||
|
"@settingsDefaultGridRecent": {},
|
||||||
|
"settingsDiscovery": "Reiniciar tutorial",
|
||||||
|
"@settingsDiscovery": {
|
||||||
|
"description": "Reset feature discovery state. User tap it and restart app, will see features tutorial again."
|
||||||
|
},
|
||||||
|
"settingsEnableSyncing": "Ativar sincronização",
|
||||||
|
"@settingsEnableSyncing": {},
|
||||||
|
"settingsEnableSyncingDes": "Atualizar todos os podcasts em segundo plano para obter os episódios mais recentes",
|
||||||
|
"@settingsEnableSyncingDes": {},
|
||||||
|
"settingsExportDes": "Exportar e importar definições da aplicação",
|
||||||
|
"@settingsExportDes": {},
|
||||||
|
"settingsFastForwardSec": "Avançar segundos",
|
||||||
|
"@settingsFastForwardSec": {},
|
||||||
|
"settingsFastForwardSecDes": "Muda os segundos de avanço no reprodutor",
|
||||||
|
"@settingsFastForwardSecDes": {},
|
||||||
|
"settingsFeedback": "Feedback",
|
||||||
|
"@settingsFeedback": {},
|
||||||
|
"settingsFeedbackDes": "Erros e sugestões",
|
||||||
|
"@settingsFeedbackDes": {},
|
||||||
|
"settingsHistory": "Histórico",
|
||||||
|
"@settingsHistory": {},
|
||||||
|
"settingsHistoryDes": "Dados de audição",
|
||||||
|
"@settingsHistoryDes": {},
|
||||||
|
"settingsInfo": "Informações",
|
||||||
|
"@settingsInfo": {},
|
||||||
|
"settingsInterface": "Interface",
|
||||||
|
"@settingsInterface": {},
|
||||||
|
"settingsLanguages": "Idiomas",
|
||||||
|
"@settingsLanguages": {},
|
||||||
|
"settingsLanguagesDes": "Mudar idioma",
|
||||||
|
"@settingsLanguagesDes": {},
|
||||||
|
"settingsLayout": "Esquema",
|
||||||
|
"@settingsLayout": {},
|
||||||
|
"settingsLayoutDes": "Esquema da aplicação",
|
||||||
|
"@settingsLayoutDes": {},
|
||||||
|
"settingsLibraries": "Bibliotecas",
|
||||||
|
"@settingsLibraries": {},
|
||||||
|
"settingsLibrariesDes": "Bibliotecas de código aberto usados nesta aplicação",
|
||||||
|
"@settingsLibrariesDes": {},
|
||||||
|
"settingsManageDownload": "Gerir downloads",
|
||||||
|
"@settingsManageDownload": {},
|
||||||
|
"settingsManageDownloadDes": "Gerir arquivos de aúdio descarregados",
|
||||||
|
"@settingsManageDownloadDes": {},
|
||||||
|
"settingsMenuAutoPlay": "Reproduzir seguinte automaticamente",
|
||||||
|
"@settingsMenuAutoPlay": {},
|
||||||
|
"settingsNetworkCellular": "Perguntar antes de usar dados móveis",
|
||||||
|
"@settingsNetworkCellular": {},
|
||||||
|
"settingsNetworkCellularAuto": "Descarregar automaticamente usando os dados móveis",
|
||||||
|
"@settingsNetworkCellularAuto": {},
|
||||||
|
"settingsNetworkCellularAutoDes": "Podes configurar o descarregamento automático na página de gestão de grupos",
|
||||||
|
"@settingsNetworkCellularAutoDes": {},
|
||||||
|
"settingsNetworkCellularDes": "Perguntar a confirmar o uso de dados móveis ao descarregar episódios",
|
||||||
|
"@settingsNetworkCellularDes": {},
|
||||||
|
"settingsPlayDes": "Lista de reprodução e reprodutor",
|
||||||
|
"@settingsPlayDes": {},
|
||||||
|
"settingsPlayerHeight": "Altura do reprodutor",
|
||||||
|
"@settingsPlayerHeight": {},
|
||||||
|
"settingsPlayerHeightDes": "Mudar a altura do reprodutor a teu gosto",
|
||||||
|
"@settingsPlayerHeightDes": {},
|
||||||
|
"settingsPopupMenu": "Menu pop-up de episódios",
|
||||||
|
"@settingsPopupMenu": {},
|
||||||
|
"settingsPopupMenuDes": "Muda o menu pop-up de episódios",
|
||||||
|
"@settingsPopupMenuDes": {},
|
||||||
|
"settingsPrefrence": "Preferências",
|
||||||
|
"@settingsPrefrence": {},
|
||||||
|
"settingsRealDark": "Escuro AMOLED",
|
||||||
|
"@settingsRealDark": {},
|
||||||
|
"settingsRealDarkDes": "Ativa caso o modo escuro não seja suficientemente escuro",
|
||||||
|
"@settingsRealDarkDes": {},
|
||||||
|
"settingsRewindSec": "Segundos de recuo",
|
||||||
|
"@settingsRewindSec": {},
|
||||||
|
"settingsRewindSecDes": "Muda os segundos de recuo no reprodutor",
|
||||||
|
"@settingsRewindSecDes": {},
|
||||||
|
"settingsSpeeds": "Velocidades",
|
||||||
|
"@settingsSpeeds": {
|
||||||
|
"description": "Playback speeds setting."
|
||||||
|
},
|
||||||
|
"settingsSpeedsDes": "Customizar as velocidades disponíveis",
|
||||||
|
"@settingsSpeedsDes": {
|
||||||
|
"description": "Playback speed setting description"
|
||||||
|
},
|
||||||
|
"settingsSTAuto": "Ligar temporizador automaticamente",
|
||||||
|
"@settingsSTAuto": {},
|
||||||
|
"settingsSTAutoDes": "Ligar temporizador automaticamente num horário definido",
|
||||||
|
"@settingsSTAutoDes": {},
|
||||||
|
"settingsSTDefaultTime": "Tempo predefinido",
|
||||||
|
"@settingsSTDefaultTime": {},
|
||||||
|
"settingsSTDefautTimeDes": "Tempo predefinido para temporizador",
|
||||||
|
"@settingsSTDefautTimeDes": {},
|
||||||
|
"settingsSTMode": "Modo de temporizador automático",
|
||||||
|
"@settingsSTMode": {},
|
||||||
|
"settingsStorageDes": "Gerir cache e armazenamento de downloads",
|
||||||
|
"@settingsStorageDes": {},
|
||||||
|
"settingsSyncing": "Sincronização",
|
||||||
|
"@settingsSyncing": {},
|
||||||
|
"settingsSyncingDes": "Atualizar podcasts em segundo plano",
|
||||||
|
"@settingsSyncingDes": {},
|
||||||
|
"settingsTapToOpenPopupMenu": "Prime para abrir o menu pop-up",
|
||||||
|
"@settingsTapToOpenPopupMenu": {},
|
||||||
|
"settingsTapToOpenPopupMenuDes": "Precisas manter premido para abrir a página do episódio",
|
||||||
|
"@settingsTapToOpenPopupMenuDes": {},
|
||||||
|
"settingsTheme": "Tema",
|
||||||
|
"@settingsTheme": {},
|
||||||
|
"settingStorage": "Armazenamento",
|
||||||
|
"@settingStorage": {},
|
||||||
|
"settingsUpdateInterval": "Intervalo de atualização",
|
||||||
|
"@settingsUpdateInterval": {},
|
||||||
|
"settingsUpdateIntervalDes": "24 horas predefinidas",
|
||||||
|
"@settingsUpdateIntervalDes": {},
|
||||||
|
"share": "Partilhar",
|
||||||
|
"@share": {},
|
||||||
|
"showNotesFonts": "Mostrar tipo de letra das notas",
|
||||||
|
"@showNotesFonts": {},
|
||||||
|
"size": "Tamanho",
|
||||||
|
"@size": {},
|
||||||
|
"skipSecondsAtEnd": "Saltar segundos no fim",
|
||||||
|
"@skipSecondsAtEnd": {},
|
||||||
|
"skipSecondsAtStart": "Saltar segundos no início",
|
||||||
|
"@skipSecondsAtStart": {},
|
||||||
|
"skipSilence": "Saltar silêncio",
|
||||||
|
"@skipSilence": {
|
||||||
|
"description": "Feature skip silence"
|
||||||
|
},
|
||||||
|
"skipToNext": "Saltar para o próximo",
|
||||||
|
"@skipToNext": {},
|
||||||
|
"sleepTimer": "Temporizador",
|
||||||
|
"@sleepTimer": {},
|
||||||
|
"status": "Status",
|
||||||
|
"@status": {
|
||||||
|
"description": "gpodder.net status"
|
||||||
|
},
|
||||||
|
"stop": "Parar",
|
||||||
|
"@stop": {},
|
||||||
|
"subscribe": "Subscrever",
|
||||||
|
"@subscribe": {},
|
||||||
|
"subscribeExportDes": "Exportar ficheiro OPML de todos os podcasts",
|
||||||
|
"@subscribeExportDes": {},
|
||||||
|
"syncNow": "Sync now",
|
||||||
|
"@syncNow": {},
|
||||||
|
"systemDefault": "Predefinido do sistema",
|
||||||
|
"@systemDefault": {},
|
||||||
|
"timeLastPlayed": "Última vez {time}",
|
||||||
|
"@timeLastPlayed": {
|
||||||
|
"description": "Show last time stop position in player when a episode have been played.",
|
||||||
|
"placeholders": {
|
||||||
|
"time": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timeLeft": "{time} Restante",
|
||||||
|
"@timeLeft": {
|
||||||
|
"placeholders": {
|
||||||
|
"time": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"to": "Para {time}",
|
||||||
|
"@to": {
|
||||||
|
"placeholders": {
|
||||||
|
"time": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"toastAddPlaylist": "Adicionado à lista de reprodução",
|
||||||
|
"@toastAddPlaylist": {},
|
||||||
|
"toastDiscovery": "Característica \"Descobrir\" ligada, por favor reinicia a aplicação",
|
||||||
|
"@toastDiscovery": {
|
||||||
|
"description": "Toast displayed when user tap Discovery Features Again in settings page."
|
||||||
|
},
|
||||||
|
"toastFileError": "Erro no ficheiro, subscrição falhou",
|
||||||
|
"@toastFileError": {},
|
||||||
|
"toastFileNotValid": "Ficheiro inválido",
|
||||||
|
"@toastFileNotValid": {},
|
||||||
|
"toastHomeGroupNotSupport": "Grupo Home não é suportado",
|
||||||
|
"@toastHomeGroupNotSupport": {},
|
||||||
|
"toastImportSettingsSuccess": "Definições importadas com sucesso",
|
||||||
|
"@toastImportSettingsSuccess": {},
|
||||||
|
"toastOneGroup": "Seleciona pelo menos um grupo",
|
||||||
|
"@toastOneGroup": {},
|
||||||
|
"toastPodcastRecovering": "A recuperar, espera um momento",
|
||||||
|
"@toastPodcastRecovering": {
|
||||||
|
"description": "Resubscribe removed podcast"
|
||||||
|
},
|
||||||
|
"toastReadFile": "Ficheiro lido com sucesso",
|
||||||
|
"@toastReadFile": {},
|
||||||
|
"toastRecoverFailed": "Recuperação do podcast falhou",
|
||||||
|
"@toastRecoverFailed": {
|
||||||
|
"description": "Resubscribe removed podast"
|
||||||
|
},
|
||||||
|
"toastRemovePlaylist": "Episódio removido da lista de reprodução",
|
||||||
|
"@toastRemovePlaylist": {},
|
||||||
|
"toastSettingSaved": "Definições guardadas",
|
||||||
|
"@toastSettingSaved": {},
|
||||||
|
"toastTimeEqualEnd": "Tempo marcado é igual ao tempo de fim",
|
||||||
|
"@toastTimeEqualEnd": {
|
||||||
|
"description": "User can't choose the same time as schedule end time."
|
||||||
|
},
|
||||||
|
"toastTimeEqualStart": "Tempo marcado é igual ao tempo de início",
|
||||||
|
"@toastTimeEqualStart": {
|
||||||
|
"description": "User can't choose the same time as schedule start time."
|
||||||
|
},
|
||||||
|
"translators": "Tradutores",
|
||||||
|
"@translators": {},
|
||||||
|
"understood": "Compreendido",
|
||||||
|
"@understood": {},
|
||||||
|
"undo": "DESFAZER",
|
||||||
|
"@undo": {},
|
||||||
|
"unlike": "Não gosto",
|
||||||
|
"@unlike": {},
|
||||||
|
"unliked": "Episódio removido dos favoritos",
|
||||||
|
"@unliked": {},
|
||||||
|
"updateDate": "Atualizar data",
|
||||||
|
"@updateDate": {},
|
||||||
|
"updateEpisodesCount": "{count, plural, zero{Sem atualizações} one{{count} episódio atualizado} other{{count} episódios atualizados}}",
|
||||||
|
"@updateEpisodesCount": {},
|
||||||
|
"updateFailed": "Atuallização falhou, erro de conexão",
|
||||||
|
"@updateFailed": {},
|
||||||
|
"username": "Username",
|
||||||
|
"@username": {},
|
||||||
|
"usernameRequired": "Username requeired",
|
||||||
|
"@usernameRequired": {},
|
||||||
|
"version": "Versão: {version}",
|
||||||
|
"@version": {
|
||||||
|
"placeholders": {
|
||||||
|
"version": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -140,6 +140,8 @@
|
|||||||
},
|
},
|
||||||
"goodNight": "晚安",
|
"goodNight": "晚安",
|
||||||
"@goodNight": {},
|
"@goodNight": {},
|
||||||
|
"gpodderLoginDes": "恭喜!您已经成功绑定 gpodder.net 账号,Tsacdop 将会自动同步您的订阅到 gpodder.net 账户。",
|
||||||
|
"@gpodderLoginDes": {},
|
||||||
"groupExisted": "组名已使用",
|
"groupExisted": "组名已使用",
|
||||||
"@groupExisted": {
|
"@groupExisted": {
|
||||||
"description": "Group name validate in add group dialog. User can't add group with same name."
|
"description": "Group name validate in add group dialog. User can't add group with same name."
|
||||||
@ -180,12 +182,25 @@
|
|||||||
"@hoursCount": {},
|
"@hoursCount": {},
|
||||||
"import": "导入",
|
"import": "导入",
|
||||||
"@import": {},
|
"@import": {},
|
||||||
|
"intergateWith": "绑定 {service}",
|
||||||
|
"@intergateWith": {
|
||||||
|
"description": "Integrate with",
|
||||||
|
"placeholders": {
|
||||||
|
"service": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
"introFourthPage": "您可以长按节目打开快捷菜单。",
|
"introFourthPage": "您可以长按节目打开快捷菜单。",
|
||||||
"@introFourthPage": {},
|
"@introFourthPage": {},
|
||||||
"introSecondPage": "您可以通过搜索订阅播客,也可以直接导入OPML文件。",
|
"introSecondPage": "您可以通过搜索订阅播客,也可以直接导入OPML文件。",
|
||||||
"@introSecondPage": {},
|
"@introSecondPage": {},
|
||||||
"introThirdPage": "您可以创建分组,上下滑动切换分组。",
|
"introThirdPage": "您可以创建分组,上下滑动切换分组。",
|
||||||
"@introThirdPage": {},
|
"@introThirdPage": {},
|
||||||
|
"invalidName": "用户名错误",
|
||||||
|
"@invalidName": {},
|
||||||
|
"lastUpdate": "最近更新",
|
||||||
|
"@lastUpdate": {
|
||||||
|
"description": "gpodder.net update"
|
||||||
|
},
|
||||||
"later": "稍后",
|
"later": "稍后",
|
||||||
"@later": {},
|
"@later": {},
|
||||||
"lightMode": "明亮模式",
|
"lightMode": "明亮模式",
|
||||||
@ -204,6 +219,23 @@
|
|||||||
"@listened": {},
|
"@listened": {},
|
||||||
"loadMore": "加载更多",
|
"loadMore": "加载更多",
|
||||||
"@loadMore": {},
|
"@loadMore": {},
|
||||||
|
"loggedInAs": "使用{userName}登入",
|
||||||
|
"@loggedInAs": {
|
||||||
|
"description": "gpodder.net",
|
||||||
|
"placeholders": {
|
||||||
|
"userName": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": "登入",
|
||||||
|
"@login": {
|
||||||
|
"description": "gpodder.net login"
|
||||||
|
},
|
||||||
|
"loginFailed": "登入失败",
|
||||||
|
"@loginFailed": {},
|
||||||
|
"logout": "注销",
|
||||||
|
"@logout": {
|
||||||
|
"description": "gpodder.net logout"
|
||||||
|
},
|
||||||
"mark": "标记",
|
"mark": "标记",
|
||||||
"@mark": {
|
"@mark": {
|
||||||
"description": "In listen history page, if a episode is marked as listened."
|
"description": "In listen history page, if a episode is marked as listened."
|
||||||
@ -292,6 +324,10 @@
|
|||||||
},
|
},
|
||||||
"oldestFirst": "由旧到新",
|
"oldestFirst": "由旧到新",
|
||||||
"@oldestFirst": {},
|
"@oldestFirst": {},
|
||||||
|
"passwdRequired": "密码为空",
|
||||||
|
"@passwdRequired": {},
|
||||||
|
"password": "密码",
|
||||||
|
"@password": {},
|
||||||
"pause": "暂停",
|
"pause": "暂停",
|
||||||
"@pause": {},
|
"@pause": {},
|
||||||
"play": "播放",
|
"play": "播放",
|
||||||
@ -547,12 +583,18 @@
|
|||||||
"@skipToNext": {},
|
"@skipToNext": {},
|
||||||
"sleepTimer": "睡眠模式",
|
"sleepTimer": "睡眠模式",
|
||||||
"@sleepTimer": {},
|
"@sleepTimer": {},
|
||||||
|
"status": "状态",
|
||||||
|
"@status": {
|
||||||
|
"description": "gpodder.net status"
|
||||||
|
},
|
||||||
"stop": "停止",
|
"stop": "停止",
|
||||||
"@stop": {},
|
"@stop": {},
|
||||||
"subscribe": "订阅",
|
"subscribe": "订阅",
|
||||||
"@subscribe": {},
|
"@subscribe": {},
|
||||||
"subscribeExportDes": "导出 OPML 文件",
|
"subscribeExportDes": "导出 OPML 文件",
|
||||||
"@subscribeExportDes": {},
|
"@subscribeExportDes": {},
|
||||||
|
"syncNow": "立即同步",
|
||||||
|
"@syncNow": {},
|
||||||
"systemDefault": "系统默认",
|
"systemDefault": "系统默认",
|
||||||
"@systemDefault": {},
|
"@systemDefault": {},
|
||||||
"timeLastPlayed": "上次播放{time}",
|
"timeLastPlayed": "上次播放{time}",
|
||||||
@ -628,6 +670,10 @@
|
|||||||
"@updateEpisodesCount": {},
|
"@updateEpisodesCount": {},
|
||||||
"updateFailed": "更新失败",
|
"updateFailed": "更新失败",
|
||||||
"@updateFailed": {},
|
"@updateFailed": {},
|
||||||
|
"username": "用户名",
|
||||||
|
"@username": {},
|
||||||
|
"usernameRequired": "用户名为空",
|
||||||
|
"@usernameRequired": {},
|
||||||
"version": "版本:{version}",
|
"version": "版本:{version}",
|
||||||
"@version": {
|
"@version": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
@ -46,6 +46,13 @@ const String notificationLayoutKey = 'notificationLayoutKey';
|
|||||||
const String showNotesFontKey = 'showNotesFontKey';
|
const String showNotesFontKey = 'showNotesFontKey';
|
||||||
const String speedListKey = 'speedListKey';
|
const String speedListKey = 'speedListKey';
|
||||||
const String searchHistoryKey = 'searchHistoryKey';
|
const String searchHistoryKey = 'searchHistoryKey';
|
||||||
|
const String gpodderApiKey = 'gpodderApiKey';
|
||||||
|
const String gpodderAddKey = 'gpodderAddKey';
|
||||||
|
const String gpodderRemoveKey = 'gpodderRemoveKey';
|
||||||
|
const String gpodderSyncStatusKey = 'gpodderSyncStatusKey';
|
||||||
|
const String gpodderSyncDateTimeKey = 'gpodderSyncDateTimeKey';
|
||||||
|
const String gpodderRemoteAddKey = 'gpodderRemoteAddKey';
|
||||||
|
const String gpodderRemoteRemoveKey = 'gpodderRemoteRemoveKey';
|
||||||
|
|
||||||
class KeyValueStorage {
|
class KeyValueStorage {
|
||||||
final String key;
|
final String key;
|
||||||
@ -178,4 +185,13 @@ class KeyValueStorage {
|
|||||||
}
|
}
|
||||||
return prefs.getDouble(key);
|
return prefs.getDouble(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> addList(List<String> addList) async {
|
||||||
|
final list = await getStringList();
|
||||||
|
await saveStringList(list..addAll(addList));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearList() async {
|
||||||
|
await saveStringList([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ class DBHelper {
|
|||||||
return ['', ''];
|
return ['', ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future delPodcastLocal(String id) async {
|
Future<void> delPodcastLocal(String id) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
await dbClient.rawDelete('DELETE FROM PodcastLocal WHERE id =?', [id]);
|
await dbClient.rawDelete('DELETE FROM PodcastLocal WHERE id =?', [id]);
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
@ -255,10 +255,10 @@ class DBHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await dbClient.rawDelete('DELETE FROM Episodes WHERE feed_id=?', [id]);
|
await dbClient.rawDelete('DELETE FROM Episodes WHERE feed_id=?', [id]);
|
||||||
var _milliseconds = DateTime.now().millisecondsSinceEpoch;
|
var milliseconds = DateTime.now().millisecondsSinceEpoch;
|
||||||
await dbClient.rawUpdate(
|
await dbClient.rawUpdate(
|
||||||
"""UPDATE SubscribeHistory SET remove_date = ? , status = ? WHERE id = ?""",
|
"""UPDATE SubscribeHistory SET remove_date = ? , status = ? WHERE id = ?""",
|
||||||
[_milliseconds, 1, id]);
|
[milliseconds, 1, id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveHistory(PlayHistory history) async {
|
Future<void> saveHistory(PlayHistory history) async {
|
||||||
|
@ -466,7 +466,7 @@ class __PodcastCardState extends State<_PodcastCard>
|
|||||||
splashColor: Colors.red.withAlpha(70),
|
splashColor: Colors.red.withAlpha(70),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
groupList.removePodcast(
|
groupList.removePodcast(
|
||||||
widget.podcastLocal.id);
|
widget.podcastLocal);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -390,7 +390,7 @@ class _PodcastSettingState extends State<PodcastSetting> {
|
|||||||
FlatButton(
|
FlatButton(
|
||||||
splashColor: Colors.red.withAlpha(70),
|
splashColor: Colors.red.withAlpha(70),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await groupList.removePodcast(widget.podcastLocal.id);
|
await groupList.removePodcast(widget.podcastLocal);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child:
|
child:
|
||||||
|
@ -59,7 +59,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||||||
splashColor: context.accentColor.withAlpha(70),
|
splashColor: context.accentColor.withAlpha(70),
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_groupList.removePodcast(widget.podcastLocal.id);
|
_groupList.removePodcast(widget.podcastLocal);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
textColor: Colors.red,
|
textColor: Colors.red,
|
||||||
|
239
lib/service/gpodder_api.dart
Normal file
239
lib/service/gpodder_api.dart
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer' as developer;
|
||||||
|
|
||||||
|
import 'package:cookie_jar/cookie_jar.dart';
|
||||||
|
import 'package:device_info/device_info.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
import '../local_storage/key_value_storage.dart';
|
||||||
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
|
|
||||||
|
enum GpodderSyncStatus { none, success, fail, authError }
|
||||||
|
|
||||||
|
class Gpodder {
|
||||||
|
final _dio = Dio(BaseOptions(
|
||||||
|
connectTimeout: 30000,
|
||||||
|
receiveTimeout: 90000,
|
||||||
|
sendTimeout: 90000,
|
||||||
|
));
|
||||||
|
final _storage = KeyValueStorage(gpodderApiKey);
|
||||||
|
final _addStorage = KeyValueStorage(gpodderAddKey);
|
||||||
|
final _removeStorage = KeyValueStorage(gpodderRemoveKey);
|
||||||
|
final _remoteAddStorage = KeyValueStorage(gpodderRemoteAddKey);
|
||||||
|
final _remoteRemoveStorage = KeyValueStorage(gpodderRemoteRemoveKey);
|
||||||
|
final _dateTimeStorage = KeyValueStorage(gpodderSyncDateTimeKey);
|
||||||
|
final _statusStorage = KeyValueStorage(gpodderSyncStatusKey);
|
||||||
|
|
||||||
|
final _baseUrl = "https://gpodder.net";
|
||||||
|
|
||||||
|
Future<void> _initDio() async {
|
||||||
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
|
var cookieJar = PersistCookieJar(dir: "${dir.path}/.cookies/");
|
||||||
|
_dio.interceptors.add(CookieManager(cookieJar));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> login({String username, String password}) async {
|
||||||
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
|
var cookieJar = PersistCookieJar(dir: "${dir.path}/.cookies/");
|
||||||
|
cookieJar.delete(Uri.parse(_baseUrl));
|
||||||
|
_dio.interceptors.add(CookieManager(cookieJar));
|
||||||
|
final basicAuth =
|
||||||
|
'Basic ${base64Encode(utf8.encode('$username:$password'))}';
|
||||||
|
var status;
|
||||||
|
Response response;
|
||||||
|
try {
|
||||||
|
response = await _dio.post('$_baseUrl/api/2/auth/$username/login.json',
|
||||||
|
options:
|
||||||
|
Options(headers: <String, String>{'authorization': basicAuth}));
|
||||||
|
status = response.statusCode;
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(e.toString(), name: 'gpoderr login error');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> logout() async {
|
||||||
|
final loginInfo = await _storage.getStringList();
|
||||||
|
final username = loginInfo[0];
|
||||||
|
await _initDio();
|
||||||
|
var status;
|
||||||
|
try {
|
||||||
|
var response = await _dio.post(
|
||||||
|
'$_baseUrl/api/2/auth/$username/logout.json',
|
||||||
|
);
|
||||||
|
status = response.statusCode;
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(e.toString(), name: 'gpoderr logout error');
|
||||||
|
if (status == 400) {
|
||||||
|
await _initService();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (status == 200) {
|
||||||
|
await _initService();
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initService() async {
|
||||||
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
|
var cookieJar = PersistCookieJar(dir: "${dir.path}/.cookies/");
|
||||||
|
cookieJar.delete(Uri.parse(_baseUrl));
|
||||||
|
await _storage.clearList();
|
||||||
|
await _addStorage.clearList();
|
||||||
|
await _remoteAddStorage.clearList();
|
||||||
|
await _removeStorage.clearList();
|
||||||
|
await _remoteAddStorage.clearList();
|
||||||
|
await _statusStorage.saveInt(0);
|
||||||
|
await _dateTimeStorage.saveInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> checkLogin(String username) async {
|
||||||
|
await _initDio();
|
||||||
|
var response = await _dio.post(
|
||||||
|
'$_baseUrl/api/2/auth/$username/login.json',
|
||||||
|
);
|
||||||
|
final status = response.statusCode;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> updateDevice(String username) async {
|
||||||
|
await _initDio();
|
||||||
|
final deviceId = Uuid().v1();
|
||||||
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
|
var status = 0;
|
||||||
|
try {
|
||||||
|
var response = await _dio
|
||||||
|
.post("$_baseUrl/api/2/devices/$username/$deviceId.json", data: {
|
||||||
|
"caption": "Tsacdop on ${androidInfo.model}",
|
||||||
|
"type": "mobile"
|
||||||
|
});
|
||||||
|
status = response.statusCode;
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(e.toString(), name: 'gpodder update device error');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (status == 200) {
|
||||||
|
await _storage.saveStringList([username, deviceId]);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getAllPodcast() async {
|
||||||
|
final loginInfo = await _storage.getStringList();
|
||||||
|
final username = loginInfo[0];
|
||||||
|
Response response;
|
||||||
|
await _initDio();
|
||||||
|
try {
|
||||||
|
response = await _dio.get(
|
||||||
|
'$_baseUrl/subscriptions/$username.opml',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(e.toString(), name: 'gpodder update podcasts error');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> uploadSubscriptions() async {
|
||||||
|
final syncDataTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
await _dateTimeStorage.saveInt(syncDataTime);
|
||||||
|
final loginInfo = await _storage.getStringList();
|
||||||
|
final username = loginInfo[0];
|
||||||
|
final deviceId = loginInfo[1];
|
||||||
|
await _initDio();
|
||||||
|
final dbHelper = DBHelper();
|
||||||
|
final podcasts = await dbHelper.getPodcastLocalAll();
|
||||||
|
var subscriptions = '';
|
||||||
|
for (var podcast in podcasts) {
|
||||||
|
subscriptions += '${podcast.rssUrl}\n';
|
||||||
|
}
|
||||||
|
var status;
|
||||||
|
try {
|
||||||
|
final response = await _dio.put(
|
||||||
|
'$_baseUrl/subscriptions/$username/$deviceId.txt',
|
||||||
|
data: subscriptions);
|
||||||
|
status = response.statusCode;
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(e.toString(), name: 'gpodder update podcasts error');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> getChanges() async {
|
||||||
|
final loginInfo = await _storage.getStringList();
|
||||||
|
final username = loginInfo[0];
|
||||||
|
final deviceId = loginInfo[1];
|
||||||
|
final syncDataTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
await _dateTimeStorage.saveInt(syncDataTime);
|
||||||
|
final timeStamp = loginInfo.length == 3 ? int.parse(loginInfo[2]) : 0;
|
||||||
|
var status;
|
||||||
|
Response response;
|
||||||
|
await _initDio();
|
||||||
|
try {
|
||||||
|
response = await _dio.get(
|
||||||
|
"$_baseUrl/api/2/subscriptions/$username/$deviceId.json",
|
||||||
|
queryParameters: {'since': timeStamp});
|
||||||
|
status = response.statusCode;
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(e.toString(), name: 'gpodder update podcasts error');
|
||||||
|
if (status == 401) {
|
||||||
|
_statusStorage.saveInt(3);
|
||||||
|
} else {
|
||||||
|
_statusStorage.saveInt(2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (status == 200) {
|
||||||
|
Map changes = jsonDecode(response.toString());
|
||||||
|
final timeStamp = changes['timestamp'];
|
||||||
|
final addList = changes['add'].cast<String>();
|
||||||
|
final removeList = changes['remove'].cast<String>();
|
||||||
|
print(removeList);
|
||||||
|
await _storage.saveStringList([username, deviceId, timeStamp.toString()]);
|
||||||
|
await _remoteAddStorage.addList(addList);
|
||||||
|
await _remoteRemoveStorage.addList(removeList);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> updateChange() async {
|
||||||
|
final loginInfo = await _storage.getStringList();
|
||||||
|
final addList = await _addStorage.getStringList();
|
||||||
|
final removeList = await _removeStorage.getStringList();
|
||||||
|
final username = loginInfo[0];
|
||||||
|
final deviceId = loginInfo[1];
|
||||||
|
await _initDio();
|
||||||
|
var status;
|
||||||
|
Response response;
|
||||||
|
try {
|
||||||
|
response = await _dio.post(
|
||||||
|
'$_baseUrl/api/2/subscriptions/$username/$deviceId.json',
|
||||||
|
data: {'add': addList, 'remove': removeList});
|
||||||
|
status = response.statusCode;
|
||||||
|
} catch (e) {
|
||||||
|
if (status == 401) {
|
||||||
|
_statusStorage.saveInt(3);
|
||||||
|
} else {
|
||||||
|
_statusStorage.saveInt(2);
|
||||||
|
}
|
||||||
|
developer.log(e.toString(), name: 'gpodder update podcasts error');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (status == 200) {
|
||||||
|
await _addStorage.clearList();
|
||||||
|
await _removeStorage.clearList();
|
||||||
|
await _statusStorage.saveInt(1);
|
||||||
|
Map changes = jsonDecode(response.toString());
|
||||||
|
final timeStamp = changes['timestamp'] as int;
|
||||||
|
await _storage
|
||||||
|
.saveStringList([username, deviceId, (timeStamp + 1).toString()]);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:developer' as developer;
|
import 'dart:developer' as developer;
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:xml/xml.dart' as xml;
|
import 'package:xml/xml.dart' as xml;
|
||||||
import '../state/podcast_group.dart';
|
import '../state/podcast_group.dart';
|
||||||
@ -21,9 +20,9 @@ class OmplOutline {
|
|||||||
class PodcastsBackup {
|
class PodcastsBackup {
|
||||||
///Group list for backup.
|
///Group list for backup.
|
||||||
final List<PodcastGroup> groups;
|
final List<PodcastGroup> groups;
|
||||||
PodcastsBackup(this.groups) : assert(groups.length > 0);
|
PodcastsBackup(this.groups) : assert(groups.isNotEmpty);
|
||||||
|
|
||||||
omplBuilder() {
|
xml.XmlNode omplBuilder() {
|
||||||
var builder = xml.XmlBuilder();
|
var builder = xml.XmlBuilder();
|
||||||
builder.processing('xml', 'version="1.0" encoding="UTF-8"');
|
builder.processing('xml', 'version="1.0" encoding="UTF-8"');
|
||||||
builder.element('ompl', nest: () {
|
builder.element('ompl', nest: () {
|
||||||
@ -55,9 +54,9 @@ class PodcastsBackup {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
static parseOMPL(File file) {
|
static parseOMPL(String opml) {
|
||||||
var data = <String, List<OmplOutline>>{};
|
var data = <String, List<OmplOutline>>{};
|
||||||
var opml = file.readAsStringSync();
|
// var opml = file.readAsStringSync();
|
||||||
var content = xml.XmlDocument.parse(opml);
|
var content = xml.XmlDocument.parse(opml);
|
||||||
var title =
|
var title =
|
||||||
content.findAllElements('head').first.findElements('title').first.text;
|
content.findAllElements('head').first.findElements('title').first.text;
|
@ -3,11 +3,11 @@ import 'dart:convert';
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
import '../.env.dart';
|
import '../.env.dart';
|
||||||
import '../type/search_top_podcast.dart';
|
import '../type/search_api/search_top_podcast.dart';
|
||||||
import '../type/searchepisodes.dart';
|
import '../type/search_api/searchepisodes.dart';
|
||||||
import '../type/searchpodcast.dart';
|
import '../type/search_api/searchpodcast.dart';
|
||||||
|
|
||||||
class SearchEngine {
|
class ListenNotesSearch {
|
||||||
final apiKey = environment['apiKey'];
|
final apiKey = environment['apiKey'];
|
||||||
Future<SearchPodcast<dynamic>> searchPodcasts(
|
Future<SearchPodcast<dynamic>> searchPodcasts(
|
||||||
{String searchText, int nextOffset}) async {
|
{String searchText, int nextOffset}) async {
|
||||||
@ -51,3 +51,16 @@ class SearchEngine {
|
|||||||
return searchResult;
|
return searchResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ItunesSearch {
|
||||||
|
Future<SearchPodcast<dynamic>> searchPodcasts(
|
||||||
|
{String searchText, int limit}) async {
|
||||||
|
var url = "https://itunes.apple.com/search/search?q="
|
||||||
|
"${Uri.encodeComponent(searchText)}${"&media=podcast&entity=podcast&limit=$limit"}";
|
||||||
|
var response = await Dio()
|
||||||
|
.get(url, options: Options(headers: {'Accept': "application/json"}));
|
||||||
|
Map searchResultMap = jsonDecode(response.toString());
|
||||||
|
var searchResult = SearchPodcast.fromJson(searchResultMap);
|
||||||
|
return searchResult;
|
||||||
|
}
|
||||||
|
}
|
@ -3,17 +3,23 @@ import 'dart:developer' as developer;
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:tsacdop/util/custom_widget.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:wc_flutter_share/wc_flutter_share.dart';
|
import 'package:wc_flutter_share/wc_flutter_share.dart';
|
||||||
|
|
||||||
import '../service/ompl_build.dart';
|
import '../local_storage/key_value_storage.dart';
|
||||||
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
|
import '../service/gpodder_api.dart';
|
||||||
|
import '../service/opml_build.dart';
|
||||||
import '../state/podcast_group.dart';
|
import '../state/podcast_group.dart';
|
||||||
import '../state/setting_state.dart';
|
import '../state/setting_state.dart';
|
||||||
import '../type/settings_backup.dart';
|
import '../type/settings_backup.dart';
|
||||||
@ -25,17 +31,20 @@ class DataBackup extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DataBackupState extends State<DataBackup> {
|
class _DataBackupState extends State<DataBackup> {
|
||||||
|
final _gpodder = Gpodder();
|
||||||
|
var _syncing = false;
|
||||||
|
|
||||||
Future<File> _exportOmpl(BuildContext context) async {
|
Future<File> _exportOmpl(BuildContext context) async {
|
||||||
var groups = context.read<GroupList>().groups;
|
var groups = context.read<GroupList>().groups;
|
||||||
var ompl = PodcastsBackup(groups).omplBuilder();
|
var opml = PodcastsBackup(groups).omplBuilder();
|
||||||
var tempdir = await getTemporaryDirectory();
|
var tempdir = await getTemporaryDirectory();
|
||||||
var now = DateTime.now();
|
var now = DateTime.now();
|
||||||
var datePlus = now.year.toString() +
|
var datePlus = now.year.toString() +
|
||||||
now.month.toString() +
|
now.month.toString() +
|
||||||
now.day.toString() +
|
now.day.toString() +
|
||||||
now.second.toString();
|
now.second.toString();
|
||||||
var file = File(join(tempdir.path, 'tsacdop_ompl_$datePlus.xml'));
|
var file = File(path.join(tempdir.path, 'tsacdop_ompl_$datePlus.xml'));
|
||||||
await file.writeAsString(ompl.toString());
|
await file.writeAsString(opml.toXmlString());
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,12 +72,12 @@ class _DataBackupState extends State<DataBackup> {
|
|||||||
now.month.toString() +
|
now.month.toString() +
|
||||||
now.day.toString() +
|
now.day.toString() +
|
||||||
now.second.toString();
|
now.second.toString();
|
||||||
var file = File(join(tempdir.path, 'tsacdop_settings_$datePlus.json'));
|
var file = File(path.join(tempdir.path, 'tsacdop_settings_$datePlus.json'));
|
||||||
await file.writeAsString(jsonEncode(json));
|
await file.writeAsString(jsonEncode(json));
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _importSetting(String path, BuildContext context) async {
|
Future<void> _importSetting(String path, BuildContext context) async {
|
||||||
final s = context.s;
|
final s = context.s;
|
||||||
var settings = context.read<SettingState>();
|
var settings = context.read<SettingState>();
|
||||||
var file = File(path);
|
var file = File(path);
|
||||||
@ -89,23 +98,83 @@ class _DataBackupState extends State<DataBackup> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _syncStauts(int index) {
|
||||||
|
switch (index) {
|
||||||
|
case 1:
|
||||||
|
return Text('Success', style: TextStyle(color: Colors.green));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
return Text('Failed', style: TextStyle(color: Colors.red));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
return Text('Unauthorized', style: TextStyle(color: Colors.red));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Text('Unknown');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _getFilePath(BuildContext context) async {
|
void _getFilePath(BuildContext context) async {
|
||||||
final s = context.s;
|
final s = context.s;
|
||||||
try {
|
try {
|
||||||
var filePath = await FilePicker.getFilePath(type: FileType.any);
|
var filePickResult =
|
||||||
if (filePath == '') {
|
await FilePicker.platform.pickFiles(type: FileType.any);
|
||||||
|
if (filePickResult == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
msg: s.toastReadFile,
|
msg: s.toastReadFile,
|
||||||
gravity: ToastGravity.BOTTOM,
|
gravity: ToastGravity.BOTTOM,
|
||||||
);
|
);
|
||||||
|
final filePath = filePickResult.files.first.path;
|
||||||
_importSetting(filePath, context);
|
_importSetting(filePath, context);
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
developer.log(e.toString(), name: 'Get file path');
|
developer.log(e.toString(), name: 'Get file path');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _logout() async {
|
||||||
|
await _gpodder.logout();
|
||||||
|
final subscribeWorker = context.read<GroupList>();
|
||||||
|
subscribeWorker.cancelWork();
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Logout successfully',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<String>> _getLoginInfo() async {
|
||||||
|
final storage = KeyValueStorage(gpodderApiKey);
|
||||||
|
return await storage.getStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _syncNow() async {
|
||||||
|
setState(() {
|
||||||
|
_syncing = true;
|
||||||
|
});
|
||||||
|
final gpodder = Gpodder();
|
||||||
|
final status = await gpodder.getChanges();
|
||||||
|
|
||||||
|
if (status == 200) {
|
||||||
|
final groupList = context.read<GroupList>();
|
||||||
|
await gpodder.updateChange();
|
||||||
|
await groupList.gpodderSyncNow();
|
||||||
|
}
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_syncing = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Tuple2<int, int>> _getSyncStatus() async {
|
||||||
|
final syncDateTime = await KeyValueStorage(gpodderSyncDateTimeKey).getInt();
|
||||||
|
final statusIndex = await KeyValueStorage(gpodderSyncStatusKey).getInt();
|
||||||
|
return Tuple2(syncDateTime, statusIndex);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = context.s;
|
final s = context.s;
|
||||||
@ -129,13 +198,136 @@ class _DataBackupState extends State<DataBackup> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
),
|
),
|
||||||
|
FutureBuilder<List<String>>(
|
||||||
|
future: _getLoginInfo(),
|
||||||
|
initialData: [],
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final loginInfo = snapshot.data;
|
||||||
|
return Container(
|
||||||
|
height: 160,
|
||||||
|
width: double.infinity,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
Hero(
|
||||||
|
tag: 'gpodder.net',
|
||||||
|
child: CircleAvatar(
|
||||||
|
minRadius: 40,
|
||||||
|
backgroundColor: context.primaryColor,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
child: Image.asset('assets/gpodder.png')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_syncing)
|
||||||
|
Positioned(
|
||||||
|
left: context.width / 2 - 40,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 80,
|
||||||
|
width: 80,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_syncing)
|
||||||
|
Positioned(
|
||||||
|
bottom: 39,
|
||||||
|
left: context.width / 2 - 12,
|
||||||
|
child: _OpenEye()),
|
||||||
|
if (_syncing)
|
||||||
|
Positioned(
|
||||||
|
bottom: 39,
|
||||||
|
left: context.width / 2 + 3,
|
||||||
|
child: _OpenEye()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
loginInfo.isEmpty
|
||||||
|
? s.intergateWith('gpodder.net')
|
||||||
|
: s.loggedInAs(loginInfo.first),
|
||||||
|
style: TextStyle(color: Colors.purple[700])),
|
||||||
|
ButtonTheme(
|
||||||
|
height: 32,
|
||||||
|
child: OutlineButton(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
|
side: BorderSide(color: Colors.purple[700])),
|
||||||
|
highlightedBorderColor: Colors.purple[700],
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
LineIcons.user,
|
||||||
|
color: Colors.purple[700],
|
||||||
|
size: context.textTheme.headline6.fontSize,
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(loginInfo.isEmpty ? s.login : s.logout,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.purple[700])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
if (loginInfo.isEmpty) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _LoginGpodder(),
|
||||||
|
fullscreenDialog: true));
|
||||||
|
} else {
|
||||||
|
_logout();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
FutureBuilder<List<String>>(
|
||||||
|
future: _getLoginInfo(),
|
||||||
|
initialData: [],
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final loginInfo = snapshot.data;
|
||||||
|
if (loginInfo.isNotEmpty) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.only(left: 70.0, right: 20),
|
||||||
|
onTap: _syncNow,
|
||||||
|
title: Text(s.syncNow),
|
||||||
|
subtitle: FutureBuilder<Tuple2<int, int>>(
|
||||||
|
future: _getSyncStatus(),
|
||||||
|
initialData: Tuple2(0, 0),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final dateTime = snapshot.data.item1;
|
||||||
|
final status = snapshot.data.item2;
|
||||||
|
return Wrap(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${s.lastUpdate}: ${dateTime.toDate(context)}'),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text('${s.status}: '),
|
||||||
|
_syncStauts(status),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Center();
|
||||||
|
}),
|
||||||
|
Divider(height: 1),
|
||||||
Container(
|
Container(
|
||||||
height: 30.0,
|
height: 30.0,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
padding: EdgeInsets.fromLTRB(70, 0, 70, 0),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(s.subscribe,
|
child: Text(s.subscribe,
|
||||||
style: context.textTheme.bodyText1
|
style: context.textTheme.bodyText1
|
||||||
.copyWith(color: Theme.of(context).accentColor)),
|
.copyWith(color: context.accentColor)),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding:
|
||||||
@ -147,49 +339,55 @@ class _DataBackupState extends State<DataBackup> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
OutlineButton(
|
ButtonTheme(
|
||||||
shape: RoundedRectangleBorder(
|
height: 32,
|
||||||
borderRadius: BorderRadius.circular(100.0),
|
child: OutlineButton(
|
||||||
side: BorderSide(color: Colors.green[700])),
|
shape: RoundedRectangleBorder(
|
||||||
highlightedBorderColor: Colors.green[700],
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
child: Row(
|
side: BorderSide(color: Colors.green[700])),
|
||||||
children: [
|
highlightedBorderColor: Colors.green[700],
|
||||||
Icon(
|
child: Row(
|
||||||
LineIcons.save,
|
children: [
|
||||||
color: Colors.green[700],
|
Icon(
|
||||||
size: context.textTheme.headline6.fontSize,
|
LineIcons.save,
|
||||||
),
|
color: Colors.green[700],
|
||||||
SizedBox(width: 10),
|
size: context.textTheme.headline6.fontSize,
|
||||||
Text(s.save,
|
),
|
||||||
style: TextStyle(color: Colors.green[700])),
|
SizedBox(width: 10),
|
||||||
],
|
Text(s.save,
|
||||||
),
|
style: TextStyle(color: Colors.green[700])),
|
||||||
onPressed: () async {
|
],
|
||||||
var file = await _exportOmpl(context);
|
),
|
||||||
await _saveFile(file);
|
onPressed: () async {
|
||||||
}),
|
var file = await _exportOmpl(context);
|
||||||
|
await _saveFile(file);
|
||||||
|
}),
|
||||||
|
),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
OutlineButton(
|
ButtonTheme(
|
||||||
shape: RoundedRectangleBorder(
|
height: 32,
|
||||||
borderRadius: BorderRadius.circular(100.0),
|
child: OutlineButton(
|
||||||
side: BorderSide(color: Colors.blue[700])),
|
shape: RoundedRectangleBorder(
|
||||||
highlightedBorderColor: Colors.blue[700],
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
child: Row(
|
side: BorderSide(color: Colors.blue[700])),
|
||||||
children: [
|
highlightedBorderColor: Colors.blue[700],
|
||||||
Icon(
|
child: Row(
|
||||||
Icons.share,
|
children: [
|
||||||
size: context.textTheme.headline6.fontSize,
|
Icon(
|
||||||
color: Colors.blue[700],
|
Icons.share,
|
||||||
),
|
size: context.textTheme.headline6.fontSize,
|
||||||
SizedBox(width: 10),
|
color: Colors.blue[700],
|
||||||
Text(s.share,
|
),
|
||||||
style: TextStyle(color: Colors.blue[700])),
|
SizedBox(width: 10),
|
||||||
],
|
Text(s.share,
|
||||||
),
|
style: TextStyle(color: Colors.blue[700])),
|
||||||
onPressed: () async {
|
],
|
||||||
var file = await _exportOmpl(context);
|
),
|
||||||
await _shareFile(file);
|
onPressed: () async {
|
||||||
})
|
var file = await _exportOmpl(context);
|
||||||
|
await _shareFile(file);
|
||||||
|
}),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -210,81 +408,503 @@ class _DataBackupState extends State<DataBackup> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 70.0, right: 10),
|
padding: EdgeInsets.only(left: 70.0, right: 10),
|
||||||
child: Wrap(children: [
|
child: Wrap(children: [
|
||||||
OutlineButton(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(100.0),
|
|
||||||
side: BorderSide(color: Colors.green[700])),
|
|
||||||
highlightedBorderColor: Colors.green[700],
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
LineIcons.save,
|
|
||||||
color: Colors.green[700],
|
|
||||||
size: context.textTheme.headline6.fontSize,
|
|
||||||
),
|
|
||||||
SizedBox(width: 10),
|
|
||||||
Text(s.save,
|
|
||||||
style: TextStyle(color: Colors.green[700])),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
var file = await _exportSetting(context);
|
|
||||||
await _saveFile(file);
|
|
||||||
}),
|
|
||||||
SizedBox(width: 10),
|
|
||||||
OutlineButton(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(100.0),
|
|
||||||
side: BorderSide(color: Colors.blue[700])),
|
|
||||||
highlightedBorderColor: Colors.blue[700],
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.share,
|
|
||||||
size: context.textTheme.headline6.fontSize,
|
|
||||||
color: Colors.blue[700],
|
|
||||||
),
|
|
||||||
SizedBox(width: 10),
|
|
||||||
Text(s.share,
|
|
||||||
style: TextStyle(color: Colors.blue[700])),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
var file = await _exportSetting(context);
|
|
||||||
await _shareFile(file);
|
|
||||||
}),
|
|
||||||
SizedBox(width: 10),
|
|
||||||
ButtonTheme(
|
ButtonTheme(
|
||||||
height: 32,
|
height: 32,
|
||||||
child: OutlineButton(
|
child: OutlineButton(
|
||||||
highlightedBorderColor: Colors.red[700],
|
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(100.0),
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
side: BorderSide(color: Colors.red[700])),
|
side: BorderSide(color: Colors.green[700])),
|
||||||
|
highlightedBorderColor: Colors.green[700],
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
LineIcons.paperclip_solid,
|
LineIcons.save,
|
||||||
|
color: Colors.green[700],
|
||||||
size: context.textTheme.headline6.fontSize,
|
size: context.textTheme.headline6.fontSize,
|
||||||
color: Colors.red[700],
|
|
||||||
),
|
),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
Text(s.import,
|
Text(s.save,
|
||||||
style: TextStyle(color: Colors.red[700])),
|
style: TextStyle(color: Colors.green[700])),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
_getFilePath(context);
|
var file = await _exportSetting(context);
|
||||||
|
await _saveFile(file);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
ButtonTheme(
|
||||||
|
height: 32,
|
||||||
|
child: OutlineButton(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
|
side: BorderSide(color: Colors.blue[700])),
|
||||||
|
highlightedBorderColor: Colors.blue[700],
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.share,
|
||||||
|
size: context.textTheme.headline6.fontSize,
|
||||||
|
color: Colors.blue[700],
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(s.share,
|
||||||
|
style: TextStyle(color: Colors.blue[700])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
var file = await _exportSetting(context);
|
||||||
|
await _shareFile(file);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
ButtonTheme(
|
||||||
|
height: 32,
|
||||||
|
child: OutlineButton(
|
||||||
|
highlightedBorderColor: Colors.red[700],
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
|
side: BorderSide(color: Colors.red[700])),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
LineIcons.paperclip_solid,
|
||||||
|
size: context.textTheme.headline6.fontSize,
|
||||||
|
color: Colors.red[700],
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(s.import,
|
||||||
|
style: TextStyle(color: Colors.red[700])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
_getFilePath(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
Divider()
|
Divider(height: 1)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _OpenEye extends StatefulWidget {
|
||||||
|
_OpenEye({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
__OpenEyeState createState() => __OpenEyeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class __OpenEyeState extends State<_OpenEye>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
double _radius = 0.0;
|
||||||
|
Animation _animation;
|
||||||
|
AnimationController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller =
|
||||||
|
AnimationController(vsync: this, duration: Duration(seconds: 1));
|
||||||
|
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
|
||||||
|
..addListener(() {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_radius = _animation.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_controller.forward();
|
||||||
|
_controller.addStatusListener((status) async {
|
||||||
|
if (status == AnimationStatus.completed) {
|
||||||
|
await Future.delayed(Duration(milliseconds: 400));
|
||||||
|
_controller.reverse();
|
||||||
|
} else if (status == AnimationStatus.dismissed) {
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DotIndicator(radius: 8 * _radius + 0.5, color: Colors.white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LoginStatus { none, error, start, syncing, complete }
|
||||||
|
|
||||||
|
class _LoginGpodder extends StatefulWidget {
|
||||||
|
_LoginGpodder({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
__LoginGpodderState createState() => __LoginGpodderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class __LoginGpodderState extends State<_LoginGpodder> {
|
||||||
|
var _username = '';
|
||||||
|
var _password = '';
|
||||||
|
LoginStatus _loginStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_loginStatus = LoginStatus.none;
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
final GlobalKey<FormFieldState<String>> _passwordFieldKey =
|
||||||
|
GlobalKey<FormFieldState<String>>();
|
||||||
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
final _gpodder = Gpodder();
|
||||||
|
|
||||||
|
Future<void> _handleLogin() async {
|
||||||
|
setState(() => _loginStatus = LoginStatus.start);
|
||||||
|
final form = _formKey.currentState;
|
||||||
|
if (form.validate()) {
|
||||||
|
form.save();
|
||||||
|
final status =
|
||||||
|
await _gpodder.login(username: _username, password: _password);
|
||||||
|
if (status == 200) {
|
||||||
|
final updateDevice = await _gpodder.updateDevice(_username);
|
||||||
|
if (updateDevice == 200) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_loginStatus = LoginStatus.syncing;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
final uploadStatus = await _gpodder.uploadSubscriptions();
|
||||||
|
await _getSubscriptions(_gpodder);
|
||||||
|
if (uploadStatus == 200) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_loginStatus = LoginStatus.complete;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mounted) setState(() => _loginStatus = LoginStatus.error);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: context.s.loginFailed,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mounted) setState(() => _loginStatus = LoginStatus.error);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: context.s.loginFailed,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mounted) setState(() => _loginStatus = LoginStatus.none);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getSubscriptions(Gpodder gpodder) async {
|
||||||
|
var subscribeWorker = context.read<GroupList>();
|
||||||
|
var rssExp = RegExp(r'^(https?):\/\/(.*)');
|
||||||
|
final opml = await gpodder.getAllPodcast();
|
||||||
|
if (opml != '') {
|
||||||
|
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOMPL(opml);
|
||||||
|
for (var entry in data.entries) {
|
||||||
|
var list = entry.value.reversed;
|
||||||
|
for (var rss in list) {
|
||||||
|
var rssLink = rssExp.stringMatch(rss.xmlUrl);
|
||||||
|
if (rssLink != null) {
|
||||||
|
final dbHelper = DBHelper();
|
||||||
|
final exist = dbHelper.checkPodcast(rssLink);
|
||||||
|
if (exist == '') {
|
||||||
|
var item = SubscribeItem(rssLink, rss.text, group: 'Home');
|
||||||
|
await subscribeWorker.setSubscribeItem(item, syncGpodder: false);
|
||||||
|
await Future.delayed(Duration(milliseconds: 200));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await subscribeWorker.cancelWork();
|
||||||
|
subscribeWorker.setWorkManager(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _validateName(String value) {
|
||||||
|
if (value.isEmpty) {
|
||||||
|
return context.s.usernameRequired;
|
||||||
|
}
|
||||||
|
final nameExp = RegExp(r'^[A-Za-z ]+$');
|
||||||
|
if (!nameExp.hasMatch(value)) {
|
||||||
|
return context.s.invalidName;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _validatePassword(String value) {
|
||||||
|
final passwordField = _passwordFieldKey.currentState;
|
||||||
|
if (passwordField.value == null || passwordField.value.isEmpty) {
|
||||||
|
return context.s.passwdRequired;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _loginStatusButton() {
|
||||||
|
switch (_loginStatus) {
|
||||||
|
case LoginStatus.none:
|
||||||
|
return Text(
|
||||||
|
context.s.login,
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case LoginStatus.syncing:
|
||||||
|
return Text(
|
||||||
|
context.s.settingsSyncing,
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case LoginStatus.start:
|
||||||
|
return SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return Text(
|
||||||
|
context.s.login,
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final s = context.s;
|
||||||
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Brightness.dark,
|
||||||
|
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||||
|
systemNavigationBarIconBrightness:
|
||||||
|
Theme.of(context).accentColorBrightness,
|
||||||
|
),
|
||||||
|
child: Scaffold(
|
||||||
|
resizeToAvoidBottomInset: true,
|
||||||
|
body: SafeArea(
|
||||||
|
top: false,
|
||||||
|
child: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverAppBar(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
iconTheme: IconThemeData(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: context.accentColor,
|
||||||
|
expandedHeight: 200,
|
||||||
|
flexibleSpace: Container(
|
||||||
|
height: 200,
|
||||||
|
width: double.infinity,
|
||||||
|
color: context.accentColor,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Hero(
|
||||||
|
tag: 'gpodder.net',
|
||||||
|
child: CircleAvatar(
|
||||||
|
minRadius: 50,
|
||||||
|
backgroundColor:
|
||||||
|
context.primaryColor.withOpacity(0.3),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 80,
|
||||||
|
width: 80,
|
||||||
|
child: Image.asset('assets/gpodder.png')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(s.intergateWith('gpodder.net'),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_loginStatus == LoginStatus.complete
|
||||||
|
? SliverList(
|
||||||
|
delegate: SliverChildListDelegate([
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(40.0, 50, 40, 100),
|
||||||
|
child: Text(
|
||||||
|
s.gpodderLoginDes,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: OutlineButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
highlightedBorderColor: context.accentColor,
|
||||||
|
child: Text(s.back)),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
: Form(
|
||||||
|
key: _formKey,
|
||||||
|
autovalidate: false,
|
||||||
|
child: AutofillGroup(
|
||||||
|
child: SliverList(
|
||||||
|
delegate: SliverChildListDelegate(
|
||||||
|
[
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(40, 20, 40, 10),
|
||||||
|
child: TextFormField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelStyle:
|
||||||
|
TextStyle(color: context.accentColor),
|
||||||
|
focusColor: context.accentColor,
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: context.accentColor,
|
||||||
|
width: 2)),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: context.accentColor)),
|
||||||
|
labelText: s.username,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
autofocus: true,
|
||||||
|
validator: _validateName,
|
||||||
|
autofillHints: [AutofillHints.username],
|
||||||
|
onSaved: (value) {
|
||||||
|
setState(() => _username = value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(40, 10, 40, 20),
|
||||||
|
child: PasswordField(
|
||||||
|
fieldKey: _passwordFieldKey,
|
||||||
|
labelText: s.password,
|
||||||
|
validator: _validatePassword,
|
||||||
|
onSaved: (value) {
|
||||||
|
setState(() {
|
||||||
|
_password = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(40, 10, 40, 20),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
_handleLogin();
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
child: Container(
|
||||||
|
height: 40,
|
||||||
|
width: 150,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.accentColor,
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(5.0)),
|
||||||
|
child: Center(child: _loginStatusButton()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height:
|
||||||
|
MediaQuery.of(context).viewInsets.bottom,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PasswordField extends StatefulWidget {
|
||||||
|
const PasswordField({
|
||||||
|
this.fieldKey,
|
||||||
|
this.hintText,
|
||||||
|
this.labelText,
|
||||||
|
this.helperText,
|
||||||
|
this.onSaved,
|
||||||
|
this.validator,
|
||||||
|
this.onFieldSubmitted,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Key fieldKey;
|
||||||
|
final String hintText;
|
||||||
|
final String labelText;
|
||||||
|
final String helperText;
|
||||||
|
final FormFieldSetter<String> onSaved;
|
||||||
|
final FormFieldValidator<String> validator;
|
||||||
|
final ValueChanged<String> onFieldSubmitted;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PasswordFieldState createState() => _PasswordFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PasswordFieldState extends State<PasswordField> {
|
||||||
|
bool _obscureText = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextFormField(
|
||||||
|
key: widget.fieldKey,
|
||||||
|
obscureText: _obscureText,
|
||||||
|
autofillHints: [AutofillHints.password],
|
||||||
|
maxLength: 100,
|
||||||
|
onSaved: widget.onSaved,
|
||||||
|
validator: widget.validator,
|
||||||
|
onFieldSubmitted: widget.onFieldSubmitted,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintStyle: TextStyle(color: context.accentColor),
|
||||||
|
labelStyle: TextStyle(color: context.accentColor),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: context.accentColor)),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: context.accentColor, width: 2)),
|
||||||
|
hintText: widget.hintText,
|
||||||
|
labelText: widget.labelText,
|
||||||
|
helperText: widget.helperText,
|
||||||
|
suffixIcon: GestureDetector(
|
||||||
|
dragStartBehavior: DragStartBehavior.down,
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_obscureText = !_obscureText;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
_obscureText ? Icons.visibility : Icons.visibility_off,
|
||||||
|
color: context.accentColor,
|
||||||
|
semanticLabel: _obscureText ? 'Show' : 'Hide',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ import 'package:webfeed/webfeed.dart';
|
|||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
import '../state/podcast_group.dart';
|
import '../state/podcast_group.dart';
|
||||||
import '../type/play_histroy.dart';
|
import '../type/play_histroy.dart';
|
||||||
import '../type/searchpodcast.dart';
|
import '../type/search_api/searchpodcast.dart';
|
||||||
import '../type/sub_history.dart';
|
import '../type/sub_history.dart';
|
||||||
import '../util/extension_helper.dart';
|
import '../util/extension_helper.dart';
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
|
|||||||
Theme.of(context).accentColorBrightness,
|
Theme.of(context).accentColorBrightness,
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: context.primaryColor,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: NestedScrollView(
|
child: NestedScrollView(
|
||||||
headerSliverBuilder: (context, innerBoxScrolled) {
|
headerSliverBuilder: (context, innerBoxScrolled) {
|
||||||
|
@ -62,6 +62,7 @@ class _SettingsState extends State<Settings> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
@ -71,9 +72,7 @@ class _SettingsState extends State<Settings> {
|
|||||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(s.settingsPrefrence,
|
child: Text(s.settingsPrefrence,
|
||||||
style: Theme.of(context)
|
style: context.textTheme.bodyText1
|
||||||
.textTheme
|
|
||||||
.bodyText1
|
|
||||||
.copyWith(color: context.accentColor)),
|
.copyWith(color: context.accentColor)),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
@ -26,97 +26,73 @@ class SyncingSetting extends StatelessWidget {
|
|||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
scrollDirection: Axis.vertical,
|
child: Selector<SettingState, Tuple2<bool, int>>(
|
||||||
child: Column(
|
selector: (_, settings) =>
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
Tuple2(settings.autoUpdate, settings.updateInterval),
|
||||||
mainAxisSize: MainAxisSize.min,
|
builder: (_, data, __) => Column(
|
||||||
children: <Widget>[
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
Selector<SettingState, Tuple2<bool, int>>(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
selector: (_, settings) =>
|
mainAxisSize: MainAxisSize.min,
|
||||||
Tuple2(settings.autoUpdate, settings.updateInterval),
|
children: <Widget>[
|
||||||
builder: (_, data, __) => Column(
|
Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
padding: const EdgeInsets.fromLTRB(70, 20, 70, 10),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Text(s.settingsSyncing,
|
||||||
children: <Widget>[
|
style: context.textTheme.bodyText1
|
||||||
Padding(
|
.copyWith(color: context.accentColor)),
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: 30.0,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 70),
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(s.settingsSyncing,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyText1
|
|
||||||
.copyWith(color: Theme.of(context).accentColor)),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(5.0),
|
|
||||||
),
|
|
||||||
ListView(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
shrinkWrap: true,
|
|
||||||
scrollDirection: Axis.vertical,
|
|
||||||
children: <Widget>[
|
|
||||||
ListTile(
|
|
||||||
onTap: () {
|
|
||||||
if (settings.autoUpdate) {
|
|
||||||
settings.autoUpdate = false;
|
|
||||||
settings.cancelWork();
|
|
||||||
} else {
|
|
||||||
settings.autoUpdate = true;
|
|
||||||
settings.setWorkManager(data.item2);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
contentPadding: const EdgeInsets.only(
|
|
||||||
left: 70.0, right: 20, bottom: 10),
|
|
||||||
title: Text(s.settingsEnableSyncing),
|
|
||||||
subtitle: Text(s.settingsEnableSyncingDes),
|
|
||||||
trailing: Transform.scale(
|
|
||||||
scale: 0.9,
|
|
||||||
child: Switch(
|
|
||||||
value: data.item1,
|
|
||||||
onChanged: (boo) async {
|
|
||||||
settings.autoUpdate = boo;
|
|
||||||
if (boo) {
|
|
||||||
settings.setWorkManager(data.item2);
|
|
||||||
} else {
|
|
||||||
settings.cancelWork();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 70.0, right: 20),
|
|
||||||
title: Text(s.settingsUpdateInterval),
|
|
||||||
subtitle: Text(s.settingsUpdateIntervalDes),
|
|
||||||
trailing: MyDropdownButton(
|
|
||||||
hint: Text(s.hoursCount(data.item2)),
|
|
||||||
underline: Center(),
|
|
||||||
elevation: 1,
|
|
||||||
displayItemCount: 5,
|
|
||||||
value: data.item2,
|
|
||||||
onChanged: data.item1
|
|
||||||
? (value) async {
|
|
||||||
await settings.cancelWork();
|
|
||||||
settings.setWorkManager(value);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
items: <int>[1, 2, 4, 8, 24, 48]
|
|
||||||
.map<DropdownMenuItem<int>>((e) {
|
|
||||||
return DropdownMenuItem<int>(
|
|
||||||
value: e, child: Text(s.hoursCount(e)));
|
|
||||||
}).toList()),
|
|
||||||
),
|
|
||||||
Divider(height: 1),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
ListTile(
|
||||||
],
|
onTap: () {
|
||||||
|
if (settings.autoUpdate) {
|
||||||
|
settings.autoUpdate = false;
|
||||||
|
settings.cancelWork();
|
||||||
|
} else {
|
||||||
|
settings.autoUpdate = true;
|
||||||
|
settings.setWorkManager(data.item2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.only(left: 70.0, right: 20, bottom: 10),
|
||||||
|
title: Text(s.settingsEnableSyncing),
|
||||||
|
subtitle: Text(s.settingsEnableSyncingDes),
|
||||||
|
trailing: Transform.scale(
|
||||||
|
scale: 0.9,
|
||||||
|
child: Switch(
|
||||||
|
value: data.item1,
|
||||||
|
onChanged: (boo) async {
|
||||||
|
settings.autoUpdate = boo;
|
||||||
|
if (boo) {
|
||||||
|
settings.setWorkManager(data.item2);
|
||||||
|
} else {
|
||||||
|
settings.cancelWork();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.only(left: 70.0, right: 20),
|
||||||
|
title: Text(s.settingsUpdateInterval),
|
||||||
|
subtitle: Text(s.settingsUpdateIntervalDes),
|
||||||
|
trailing: MyDropdownButton(
|
||||||
|
hint: Text(s.hoursCount(data.item2)),
|
||||||
|
underline: Center(),
|
||||||
|
elevation: 1,
|
||||||
|
displayItemCount: 5,
|
||||||
|
value: data.item2,
|
||||||
|
onChanged: data.item1
|
||||||
|
? (value) async {
|
||||||
|
await settings.cancelWork();
|
||||||
|
settings.setWorkManager(value);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
items: <int>[1, 2, 4, 8, 24, 48]
|
||||||
|
.map<DropdownMenuItem<int>>((e) {
|
||||||
|
return DropdownMenuItem<int>(
|
||||||
|
value: e, child: Text(s.hoursCount(e)));
|
||||||
|
}).toList()),
|
||||||
|
),
|
||||||
|
Divider(height: 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -185,10 +185,8 @@ class ThemeSetting extends StatelessWidget {
|
|||||||
padding: EdgeInsets.symmetric(horizontal: 70),
|
padding: EdgeInsets.symmetric(horizontal: 70),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(s.fontStyle,
|
child: Text(s.fontStyle,
|
||||||
style: Theme.of(context)
|
style: context.textTheme.bodyText1
|
||||||
.textTheme
|
.copyWith(color: context.accentColor)),
|
||||||
.bodyText1
|
|
||||||
.copyWith(color: Theme.of(context).accentColor)),
|
|
||||||
),
|
),
|
||||||
Selector<SettingState, int>(
|
Selector<SettingState, int>(
|
||||||
selector: (_, setting) => setting.showNotesFontIndex,
|
selector: (_, setting) => setting.showNotesFontIndex,
|
||||||
|
@ -3,23 +3,39 @@ import 'dart:developer' as developer;
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:color_thief_flutter/color_thief_flutter.dart';
|
import 'package:color_thief_flutter/color_thief_flutter.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_isolate/flutter_isolate.dart';
|
import 'package:flutter_isolate/flutter_isolate.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:webfeed/webfeed.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:webfeed/webfeed.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
import '../local_storage/key_value_storage.dart';
|
import '../local_storage/key_value_storage.dart';
|
||||||
import '../local_storage/sqflite_localpodcast.dart';
|
import '../local_storage/sqflite_localpodcast.dart';
|
||||||
|
import '../service/gpodder_api.dart';
|
||||||
import '../type/fireside_data.dart';
|
import '../type/fireside_data.dart';
|
||||||
import '../type/podcastlocal.dart';
|
import '../type/podcastlocal.dart';
|
||||||
|
|
||||||
|
void callbackDispatcher() {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
Workmanager.executeTask((task, inputData) async {
|
||||||
|
final gpodder = Gpodder();
|
||||||
|
final status = await gpodder.getChanges();
|
||||||
|
if (status == 200) {
|
||||||
|
await gpodder.updateChange();
|
||||||
|
}
|
||||||
|
return Future.value(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class GroupEntity {
|
class GroupEntity {
|
||||||
final String name;
|
final String name;
|
||||||
final String id;
|
final String id;
|
||||||
@ -134,16 +150,21 @@ class SubscribeItem {
|
|||||||
|
|
||||||
///Podcast group, default Home.
|
///Podcast group, default Home.
|
||||||
String group;
|
String group;
|
||||||
|
|
||||||
|
///sync to gpodder
|
||||||
|
bool syncWithGpodder;
|
||||||
SubscribeItem(this.url, this.title,
|
SubscribeItem(this.url, this.title,
|
||||||
{this.subscribeState = SubscribeState.none,
|
{this.subscribeState = SubscribeState.none,
|
||||||
this.id = '',
|
this.id = '',
|
||||||
this.imgUrl = '',
|
this.imgUrl = '',
|
||||||
this.group = ''});
|
this.group = '',
|
||||||
|
this.syncWithGpodder = true});
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupList extends ChangeNotifier {
|
class GroupList extends ChangeNotifier {
|
||||||
/// List of all gourps.
|
/// List of all gourps.
|
||||||
final List<PodcastGroup> _groups = [];
|
final List<PodcastGroup> _groups = [];
|
||||||
|
|
||||||
List<PodcastGroup> get groups => _groups;
|
List<PodcastGroup> get groups => _groups;
|
||||||
|
|
||||||
final DBHelper _dbHelper = DBHelper();
|
final DBHelper _dbHelper = DBHelper();
|
||||||
@ -155,6 +176,7 @@ class GroupList extends ChangeNotifier {
|
|||||||
|
|
||||||
/// Default false, true during loading groups from storage.
|
/// Default false, true during loading groups from storage.
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
|
||||||
bool get isLoading => _isLoading;
|
bool get isLoading => _isLoading;
|
||||||
|
|
||||||
/// Svae ordered gourps info before saved.
|
/// Svae ordered gourps info before saved.
|
||||||
@ -177,8 +199,9 @@ class GroupList extends ChangeNotifier {
|
|||||||
|
|
||||||
/// Add subsribe item
|
/// Add subsribe item
|
||||||
SubscribeItem _subscribeItem;
|
SubscribeItem _subscribeItem;
|
||||||
setSubscribeItem(SubscribeItem item) async {
|
setSubscribeItem(SubscribeItem item, {bool syncGpodder = true}) async {
|
||||||
_subscribeItem = item;
|
_subscribeItem = item;
|
||||||
|
if (syncGpodder) _syncAdd(item.url);
|
||||||
await _start();
|
await _start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +210,13 @@ class GroupList extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _syncAdd(String rssUrl) async {
|
||||||
|
final check = await _checkGpodderLoggedin();
|
||||||
|
if (check) {
|
||||||
|
await _addStorage.addList([rssUrl]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future _start() async {
|
Future _start() async {
|
||||||
if (_created == false) {
|
if (_created == false) {
|
||||||
await _createIsolate();
|
await _createIsolate();
|
||||||
@ -197,7 +227,8 @@ class GroupList extends ChangeNotifier {
|
|||||||
_subscribeItem.url,
|
_subscribeItem.url,
|
||||||
_subscribeItem.title,
|
_subscribeItem.title,
|
||||||
_subscribeItem.imgUrl,
|
_subscribeItem.imgUrl,
|
||||||
_subscribeItem.group
|
_subscribeItem.group,
|
||||||
|
_subscribeItem.syncWithGpodder
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,7 +248,8 @@ class GroupList extends ChangeNotifier {
|
|||||||
_subscribeItem.url,
|
_subscribeItem.url,
|
||||||
_subscribeItem.title,
|
_subscribeItem.title,
|
||||||
_subscribeItem.imgUrl,
|
_subscribeItem.imgUrl,
|
||||||
_subscribeItem.group
|
_subscribeItem.group,
|
||||||
|
_subscribeItem.syncWithGpodder
|
||||||
]);
|
]);
|
||||||
} else if (message is List) {
|
} else if (message is List) {
|
||||||
_setCurrentSubscribeItem(SubscribeItem(
|
_setCurrentSubscribeItem(SubscribeItem(
|
||||||
@ -238,6 +270,65 @@ class GroupList extends ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Set gpodder sync
|
||||||
|
final _loginInfp = KeyValueStorage(gpodderApiKey);
|
||||||
|
final _addStorage = KeyValueStorage(gpodderAddKey);
|
||||||
|
final _removeStorage = KeyValueStorage(gpodderRemoveKey);
|
||||||
|
final _remoteAddStorage = KeyValueStorage(gpodderRemoteAddKey);
|
||||||
|
final _remoteRemoveStorage = KeyValueStorage(gpodderRemoteRemoveKey);
|
||||||
|
|
||||||
|
Future<bool> _checkGpodderLoggedin() async {
|
||||||
|
final loginInfo = await _loginInfp.getStringList();
|
||||||
|
return loginInfo.isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> gpodderSyncNow() async {
|
||||||
|
final addList = await _remoteAddStorage.getStringList();
|
||||||
|
final removeList = await _remoteRemoveStorage.getStringList();
|
||||||
|
|
||||||
|
if (removeList.isNotEmpty) {
|
||||||
|
for (var rssLink in removeList) {
|
||||||
|
final exist = await _dbHelper.checkPodcast(rssLink);
|
||||||
|
if (exist != '') {
|
||||||
|
await _unsubscribe(exist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await _remoteAddStorage.clearList();
|
||||||
|
}
|
||||||
|
if (addList.isNotEmpty) {
|
||||||
|
for (var rssLink in addList) {
|
||||||
|
final exist = await _dbHelper.checkPodcast(rssLink);
|
||||||
|
if (exist == '') {
|
||||||
|
var item = SubscribeItem(rssLink, rssLink, group: 'Home');
|
||||||
|
_subscribeItem = item;
|
||||||
|
await _start();
|
||||||
|
|
||||||
|
await Future.delayed(Duration(milliseconds: 200));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await _remoteRemoveStorage.clearList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWorkManager(int hour) {
|
||||||
|
Workmanager.initialize(
|
||||||
|
callbackDispatcher,
|
||||||
|
isInDebugMode: false,
|
||||||
|
);
|
||||||
|
Workmanager.registerPeriodicTask("2", "gpodder_sync",
|
||||||
|
frequency: Duration(hours: hour),
|
||||||
|
initialDelay: Duration(seconds: 10),
|
||||||
|
constraints: Constraints(
|
||||||
|
networkType: NetworkType.connected,
|
||||||
|
));
|
||||||
|
developer.log('work manager init done + (gpodder sync)');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future cancelWork() async {
|
||||||
|
await Workmanager.cancelByUniqueName('2');
|
||||||
|
developer.log('work job cancelled');
|
||||||
|
}
|
||||||
|
|
||||||
void addToOrderChanged(PodcastGroup group) {
|
void addToOrderChanged(PodcastGroup group) {
|
||||||
_orderChanged.add(group);
|
_orderChanged.add(group);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -261,6 +352,7 @@ class GroupList extends ChangeNotifier {
|
|||||||
@override
|
@override
|
||||||
void addListener(VoidCallback listener) {
|
void addListener(VoidCallback listener) {
|
||||||
loadGroups().then((value) => super.addListener(listener));
|
loadGroups().then((value) => super.addListener(listener));
|
||||||
|
gpodderSyncNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -414,7 +506,14 @@ class GroupList extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unsubscribe podcast
|
/// Unsubscribe podcast
|
||||||
Future<void> removePodcast(String id) async {
|
Future<void> _syncRemove(String rssUrl) async {
|
||||||
|
final check = await _checkGpodderLoggedin();
|
||||||
|
if (check) {
|
||||||
|
await _removeStorage.addList([rssUrl]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _unsubscribe(String id) async {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
for (var group in _groups) {
|
for (var group in _groups) {
|
||||||
@ -429,7 +528,15 @@ class GroupList extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
saveOrder(PodcastGroup group) async {
|
Future<void> removePodcast(
|
||||||
|
PodcastLocal podcast,
|
||||||
|
) async {
|
||||||
|
_syncRemove(podcast.rssUrl);
|
||||||
|
final id = podcast.id;
|
||||||
|
await _unsubscribe(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveOrder(PodcastGroup group) async {
|
||||||
group.podcastList = group.orderedPodcasts.map((e) => e.id).toList();
|
group.podcastList = group.orderedPodcasts.map((e) => e.id).toList();
|
||||||
await _saveGroup();
|
await _saveGroup();
|
||||||
await group.getPodcasts();
|
await group.getPodcasts();
|
||||||
@ -563,6 +670,13 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
|||||||
}
|
}
|
||||||
await dbHelper.savePodcastRss(p, uuid);
|
await dbHelper.savePodcastRss(p, uuid);
|
||||||
|
|
||||||
|
// if (item.syncWithGpodder) {
|
||||||
|
// final gpodder = Gpodder();
|
||||||
|
// await gpodder.updateChange({
|
||||||
|
// 'add': [item.url]
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
sendPort.send([item.title, item.url, 3, uuid]);
|
sendPort.send([item.title, item.url, 3, uuid]);
|
||||||
|
|
||||||
await Future.delayed(Duration(seconds: 2));
|
await Future.delayed(Duration(seconds: 2));
|
||||||
@ -600,9 +714,9 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
subReceivePort.distinct().listen((message) {
|
subReceivePort.distinct().listen((message) {
|
||||||
if (message is List<String>) {
|
if (message is List<dynamic>) {
|
||||||
items.add(SubscribeItem(message[0], message[1],
|
items.add(SubscribeItem(message[0], message[1],
|
||||||
imgUrl: message[2], group: message[3]));
|
imgUrl: message[2], group: message[3], syncWithGpodder: message[4]));
|
||||||
if (!_running) {
|
if (!_running) {
|
||||||
_subscribe(items.first);
|
_subscribe(items.first);
|
||||||
_running = true;
|
_running = true;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../type/searchpodcast.dart';
|
import '../type/search_api/searchpodcast.dart';
|
||||||
|
|
||||||
class SearchState extends ChangeNotifier {
|
class SearchState extends ChangeNotifier {
|
||||||
final List<OnlinePodcast> _subscribedList = [];
|
final List<OnlinePodcast> _subscribedList = [];
|
||||||
|
@ -161,7 +161,7 @@ class SettingState extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future cancelWork() async {
|
Future cancelWork() async {
|
||||||
await Workmanager.cancelAll();
|
await Workmanager.cancelByUniqueName('1');
|
||||||
developer.log('work job cancelled');
|
developer.log('work job cancelled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
67
lib/type/search_api/itunes_podcast.dart
Normal file
67
lib/type/search_api/itunes_podcast.dart
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'searchpodcast.dart';
|
||||||
|
|
||||||
|
part 'itunes_podcast.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ItunesSearchResult<P> {
|
||||||
|
@_ConvertP()
|
||||||
|
final List<P> results;
|
||||||
|
final int resultCount;
|
||||||
|
ItunesSearchResult({this.resultCount, this.results});
|
||||||
|
|
||||||
|
factory ItunesSearchResult.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ItunesSearchResultFromJson<P>(json);
|
||||||
|
Map<String, dynamic> toJson() => _$ItunesSearchResultToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConvertP<P> implements JsonConverter<P, Object> {
|
||||||
|
const _ConvertP();
|
||||||
|
@override
|
||||||
|
P fromJson(Object json) {
|
||||||
|
return ItunesPodcast.fromJson(json) as P;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object toJson(P object) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ItunesPodcast {
|
||||||
|
final String artistName;
|
||||||
|
final String collectionName;
|
||||||
|
final String feedUrl;
|
||||||
|
final String artworkUrl600;
|
||||||
|
final String releaseDate;
|
||||||
|
final int collectionId;
|
||||||
|
|
||||||
|
ItunesPodcast(
|
||||||
|
{this.artistName,
|
||||||
|
this.collectionName,
|
||||||
|
this.feedUrl,
|
||||||
|
this.artworkUrl600,
|
||||||
|
this.releaseDate,
|
||||||
|
this.collectionId});
|
||||||
|
|
||||||
|
factory ItunesPodcast.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ItunesPodcastFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$ItunesPodcastToJson(this);
|
||||||
|
|
||||||
|
int get latestPubDate => DateFormat('YYYY-MM-DDTHH:MM:SSZ', 'en_US')
|
||||||
|
.parse(releaseDate)
|
||||||
|
.millisecondsSinceEpoch;
|
||||||
|
OnlinePodcast get toOnlinePodcast => OnlinePodcast(
|
||||||
|
earliestPubDate: 0,
|
||||||
|
title: collectionName,
|
||||||
|
count: 0,
|
||||||
|
description: '',
|
||||||
|
image: artworkUrl600,
|
||||||
|
latestPubDate: latestPubDate,
|
||||||
|
rss: feedUrl,
|
||||||
|
publisher: artistName,
|
||||||
|
id: collectionId.toString());
|
||||||
|
}
|
41
lib/type/search_api/itunes_podcast.g.dart
Normal file
41
lib/type/search_api/itunes_podcast.g.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'itunes_podcast.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
ItunesSearchResult<P> _$ItunesSearchResultFromJson<P>(
|
||||||
|
Map<String, dynamic> json) {
|
||||||
|
return ItunesSearchResult<P>(
|
||||||
|
resultCount: json['resultCount'] as int,
|
||||||
|
results: (json['results'] as List)?.map(_ConvertP<P>().fromJson)?.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ItunesSearchResultToJson<P>(
|
||||||
|
ItunesSearchResult<P> instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'results': instance.results?.map(_ConvertP<P>().toJson)?.toList(),
|
||||||
|
'resultCount': instance.resultCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
ItunesPodcast _$ItunesPodcastFromJson(Map<String, dynamic> json) {
|
||||||
|
return ItunesPodcast(
|
||||||
|
artistName: json['artistName'] as String,
|
||||||
|
collectionName: json['collectionName'] as String,
|
||||||
|
feedUrl: json['feedUrl'] as String,
|
||||||
|
artworkUrl600: json['artworkUrl600'] as String,
|
||||||
|
releaseDate: json['releaseDate'] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _$ItunesPodcastToJson(ItunesPodcast instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'artistName': instance.artistName,
|
||||||
|
'collectionName': instance.collectionName,
|
||||||
|
'feedUrl': instance.feedUrl,
|
||||||
|
'artworkUrl600': instance.artworkUrl600,
|
||||||
|
'releaseDate': instance.releaseDate,
|
||||||
|
};
|
@ -924,9 +924,10 @@ class IconPainter extends StatelessWidget {
|
|||||||
|
|
||||||
/// A dot just a dot.
|
/// A dot just a dot.
|
||||||
class DotIndicator extends StatelessWidget {
|
class DotIndicator extends StatelessWidget {
|
||||||
DotIndicator({this.radius = 8, Key key})
|
DotIndicator({this.radius = 8, this.color, Key key})
|
||||||
: assert(radius > 0),
|
: assert(radius > 0),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
final Color color;
|
||||||
final double radius;
|
final double radius;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -934,8 +935,8 @@ class DotIndicator extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
width: radius,
|
width: radius,
|
||||||
height: radius,
|
height: radius,
|
||||||
decoration:
|
decoration: BoxDecoration(
|
||||||
BoxDecoration(shape: BoxShape.circle, color: context.accentColor));
|
shape: BoxShape.circle, color: color ?? context.accentColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ extension IntExtension on int {
|
|||||||
var date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
|
var date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
|
||||||
var difference = DateTime.now().toUtc().difference(date);
|
var difference = DateTime.now().toUtc().difference(date);
|
||||||
if (difference.inMinutes < 30) {
|
if (difference.inMinutes < 30) {
|
||||||
|
return s.minsAgo(difference.inMinutes);
|
||||||
|
} else if (difference.inMinutes < 60) {
|
||||||
return s.hoursAgo(0);
|
return s.hoursAgo(0);
|
||||||
} else if (difference.inHours < 24) {
|
} else if (difference.inHours < 24) {
|
||||||
return s.hoursAgo(difference.inHours);
|
return s.hoursAgo(difference.inHours);
|
||||||
|
@ -33,6 +33,7 @@ Future generalDialog(BuildContext context,
|
|||||||
|
|
||||||
Future generalSheet(BuildContext context, {Widget child, String title}) async =>
|
Future generalSheet(BuildContext context, {Widget child, String title}) async =>
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@ -41,21 +42,21 @@ Future generalSheet(BuildContext context, {Widget child, String title}) async =>
|
|||||||
elevation: 2,
|
elevation: 2,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
final statusHeight = MediaQuery.of(context).padding.top;
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: ConstrainedBox(
|
||||||
physics: NeverScrollableScrollPhysics(),
|
constraints:
|
||||||
|
BoxConstraints(maxHeight: context.height - statusHeight - 80),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Container(
|
||||||
padding: EdgeInsets.only(top: 10.0, bottom: 2.0),
|
height: 4,
|
||||||
child: Container(
|
width: 25,
|
||||||
height: 4,
|
margin: EdgeInsets.only(top: 10.0, bottom: 2.0),
|
||||||
width: 25,
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
borderRadius: BorderRadius.circular(2.0),
|
||||||
borderRadius: BorderRadius.circular(2.0),
|
color: context.primaryColorDark),
|
||||||
color: context.primaryColorDark),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
@ -69,7 +70,7 @@ Future generalSheet(BuildContext context, {Widget child, String title}) async =>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Divider(height: 1),
|
Divider(height: 1),
|
||||||
child,
|
Flexible(child: SingleChildScrollView(child: child)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
15
pubspec.yaml
15
pubspec.yaml
@ -13,16 +13,19 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
auto_animated: ^2.1.0
|
auto_animated: ^2.1.0
|
||||||
audio_session: ^0.0.7
|
audio_session: ^0.0.7
|
||||||
cached_network_image: ^2.3.1
|
cached_network_image: ^2.3.2+1
|
||||||
color_thief_flutter: ^1.0.2
|
color_thief_flutter: ^1.0.2
|
||||||
|
cookie_jar: ^1.0.0
|
||||||
cupertino_icons: ^1.0.0
|
cupertino_icons: ^1.0.0
|
||||||
connectivity: ^0.4.9
|
connectivity: ^0.4.9
|
||||||
|
device_info: ^0.4.2+7
|
||||||
dio: ^3.0.10
|
dio: ^3.0.10
|
||||||
|
dio_cookie_manager: ^1.0.0
|
||||||
extended_nested_scroll_view: ^1.0.1
|
extended_nested_scroll_view: ^1.0.1
|
||||||
effective_dart: ^1.2.4
|
effective_dart: ^1.2.4
|
||||||
equatable: ^1.2.3
|
equatable: ^1.2.5
|
||||||
feature_discovery: ^0.10.0
|
feature_discovery: ^0.10.0
|
||||||
file_picker: ^1.12.0
|
file_picker: ^2.0.0
|
||||||
flutter_html: ^0.11.1
|
flutter_html: ^0.11.1
|
||||||
flutter_downloader: ^1.5.0
|
flutter_downloader: ^1.5.0
|
||||||
fluttertoast: ^4.0.0
|
fluttertoast: ^4.0.0
|
||||||
@ -37,15 +40,15 @@ dependencies:
|
|||||||
intl: ^0.16.1
|
intl: ^0.16.1
|
||||||
json_serializable: ^3.4.1
|
json_serializable: ^3.4.1
|
||||||
json_annotation: ^3.0.1
|
json_annotation: ^3.0.1
|
||||||
path_provider: ^1.6.14
|
path_provider: ^1.6.16
|
||||||
permission_handler: ^5.0.1
|
permission_handler: ^5.0.1
|
||||||
provider: ^4.3.2
|
provider: ^4.3.2
|
||||||
rxdart: ^0.24.1
|
rxdart: ^0.24.1
|
||||||
sqflite: ^1.3.1
|
sqflite: ^1.3.1
|
||||||
shared_preferences: ^0.5.10
|
shared_preferences: ^0.5.10
|
||||||
tuple: ^1.0.3
|
tuple: ^1.0.3
|
||||||
url_launcher: ^5.5.3
|
url_launcher: ^5.6.0
|
||||||
uuid: ^2.2.0
|
uuid: ^2.2.2
|
||||||
xml: ^4.2.0
|
xml: ^4.2.0
|
||||||
workmanager: ^0.2.3
|
workmanager: ^0.2.3
|
||||||
wc_flutter_share: ^0.2.2
|
wc_flutter_share: ^0.2.2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user