Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
0dcb0810ec
21 files changed
+749
-12
No files matched your search
@@ -281,6 +281,10 @@
|
||||
<activity
|
||||
android:name=".ui.book.local.ImportBookActivity"
|
||||
android:launchMode="singleTop" />
|
||||
<!-- 添加远程 -->
|
||||
<activity
|
||||
android:name=".ui.book.remote.RemoteBookActivity"
|
||||
android:launchMode="singleTop" />
|
||||
<!-- 发现界面 -->
|
||||
<activity
|
||||
android:name=".ui.book.explore.ExploreShowActivity"
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
|
||||
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
||||
|
||||
**2022/05/18**
|
||||
|
||||
* 修复更改本地文件后每次打开都刷新目录的bug
|
||||
|
||||
**2022/05/16**
|
||||
|
||||
* 添加firebase性能监测
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<!DOCTYPE html><html lang="en" style="padding: 0;height:100%"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"><link rel="icon" href="../favicon.ico" type="image/x-icon"><link rel="shortcut icon" href="../favicon.ico" type="image/x-icon"><title>Legado Bookshelf</title><link href="css/about.65a00131.css" rel="prefetch"><link href="css/detail.12a39aa7.css" rel="prefetch"><link href="js/about.2456ab2e.js" rel="prefetch"><link href="js/detail.8e2caf8f.js" rel="prefetch"><link href="css/app.e4c919b7.css" rel="preload" as="style"><link href="css/chunk-vendors.bd1373b6.css" rel="preload" as="style"><link href="js/app.a68b7203.js" rel="preload" as="script"><link href="js/chunk-vendors.f36dda93.js" rel="preload" as="script"><link href="css/chunk-vendors.bd1373b6.css" rel="stylesheet"><link href="css/app.e4c919b7.css" rel="stylesheet"></head><style>body::-webkit-scrollbar {
|
||||
<!DOCTYPE html><html lang="zh-CN" style="padding: 0;height:100%"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"><link rel="icon" href="../favicon.ico" type="image/x-icon"><link rel="shortcut icon" href="../favicon.ico" type="image/x-icon"><title>Legado Bookshelf</title><link href="css/about.65a00131.css" rel="prefetch"><link href="css/detail.12a39aa7.css" rel="prefetch"><link href="js/about.cee6f6d7.js" rel="prefetch"><link href="js/detail.bb04bd6b.js" rel="prefetch"><link href="css/app.e4c919b7.css" rel="preload" as="style"><link href="css/chunk-vendors.bd1373b6.css" rel="preload" as="style"><link href="js/app.aa604b87.js" rel="preload" as="script"><link href="js/chunk-vendors.ca94fdd0.js" rel="preload" as="script"><link href="css/chunk-vendors.bd1373b6.css" rel="stylesheet"><link href="css/app.e4c919b7.css" rel="stylesheet"></head><style>body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}</style><body style="margin: 0;height:100%"><noscript><strong>We're sorry but yd-web-tool doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.f36dda93.js"></script><script src="js/app.a68b7203.js"></script></body></html>
|
||||
}</style><body style="margin: 0;height:100%"><noscript><strong>We're sorry but yd-web-tool doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.ca94fdd0.js"></script><script src="js/app.aa604b87.js"></script></body></html>
|
||||
File renamed without changes.
+1
-1
@@ -1 +1 @@
|
||||
(function(e){function t(t){for(var r,o,u=t[0],c=t[1],f=t[2],s=0,l=[];s<u.length;s++)o=u[s],Object.prototype.hasOwnProperty.call(a,o)&&a[o]&&l.push(a[o][0]),a[o]=0;for(r in c)Object.prototype.hasOwnProperty.call(c,r)&&(e[r]=c[r]);d&&d(t);while(l.length)l.shift()();return i.push.apply(i,f||[]),n()}function n(){for(var e,t=0;t<i.length;t++){for(var n=i[t],r=!0,o=1;o<n.length;o++){var u=n[o];0!==a[u]&&(r=!1)}r&&(i.splice(t--,1),e=c(c.s=n[0]))}return e}var r={},o={app:0},a={app:0},i=[];function u(e){return c.p+"js/"+({about:"about",detail:"detail"}[e]||e)+"."+{about:"2456ab2e",detail:"8e2caf8f"}[e]+".js"}function c(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,c),n.l=!0,n.exports}c.e=function(e){var t=[],n={about:1,detail:1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise((function(t,n){for(var r="css/"+({about:"about",detail:"detail"}[e]||e)+"."+{about:"65a00131",detail:"12a39aa7"}[e]+".css",a=c.p+r,i=document.getElementsByTagName("link"),u=0;u<i.length;u++){var f=i[u],s=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(s===r||s===a))return t()}var l=document.getElementsByTagName("style");for(u=0;u<l.length;u++){f=l[u],s=f.getAttribute("data-href");if(s===r||s===a)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var r=t&&t.target&&t.target.src||a,i=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");i.code="CSS_CHUNK_LOAD_FAILED",i.request=r,delete o[e],d.parentNode.removeChild(d),n(i)},d.href=a;var p=document.getElementsByTagName("head")[0];p.appendChild(d)})).then((function(){o[e]=0})));var r=a[e];if(0!==r)if(r)t.push(r[2]);else{var i=new Promise((function(t,n){r=a[e]=[t,n]}));t.push(r[2]=i);var f,s=document.createElement("script");s.charset="utf-8",s.timeout=120,c.nc&&s.setAttribute("nonce",c.nc),s.src=u(e);var l=new Error;f=function(t){s.onerror=s.onload=null,clearTimeout(d);var n=a[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;l.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",l.name="ChunkLoadError",l.type=r,l.request=o,n[1](l)}a[e]=void 0}};var d=setTimeout((function(){f({type:"timeout",target:s})}),12e4);s.onerror=s.onload=f,document.head.appendChild(s)}return Promise.all(t)},c.m=e,c.c=r,c.d=function(e,t,n){c.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},c.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,t){if(1&t&&(e=c(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(c.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)c.d(n,r,function(t){return e[t]}.bind(null,r));return n},c.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return c.d(t,"a",t),t},c.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},c.p="",c.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],s=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var d=s;i.push([0,"chunk-vendors"]),n()})({0:function(e,t,n){e.exports=n("56d7")},"034f":function(e,t,n){"use strict";n("85ec")},3562:function(e,t,n){e.exports=n.p+"img/loading.550df755.gif"},"56d7":function(e,t,n){"use strict";n.r(t);n("e260"),n("e6cf"),n("cca6"),n("a79d");var r=n("2b0e"),o=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"app"}},[n("router-view")],1)},a=[],i={name:"app",components:{},beforeCreate:function(){var e=this;this.$store.commit("setMiniInterface",window.innerWidth<750),window.onresize=function(){e.$store.commit("setMiniInterface",window.innerWidth<750)}}},u=i,c=(n("034f"),n("2877")),f=Object(c["a"])(u,o,a,!1,null,null,null),s=f.exports,l=(n("d3b7"),n("3ca3"),n("ddb0"),n("8c4f"));r["default"].use(l["a"]);var d=l["a"].prototype.push;l["a"].prototype.push=function(e){return d.call(this,e).catch((function(e){return e}))};var p=[{path:"/",name:"index",component:function(){return n.e("about").then(n.bind(null,"d504"))}},{path:"/chapter",name:"Chapter",component:function(){return n.e("detail").then(n.bind(null,"537b"))}}],h=new l["a"]({base:"",routes:p}),g=h,b=(n("0fb7"),n("450d"),n("f529")),m=n.n(b),v=(n("9e1f"),n("6ed5")),y=n.n(v),w=(n("be4f"),n("896a")),C=n.n(w),S=(n("cbb5"),n("8bbc")),O=n.n(S),j=(n("10cb"),n("f3ad")),k=n.n(j),x=(n("06f1"),n("6ac9")),P=n.n(x),T=(n("5466"),n("ecdf")),_=n.n(T),E=(n("38a0"),n("ad41")),L=n.n(E),$=(n("b84d"),n("c216")),A=n.n($),I=(n("8f24"),n("76b9")),M=n.n(I),B=(n("e3ea"),n("7bc3")),N=n.n(B),R=(n("1951"),n("eedf")),V=n.n(R);r["default"].use(V.a),r["default"].use(N.a),r["default"].use(M.a),r["default"].use(A.a),r["default"].use(L.a),r["default"].use(_.a),r["default"].use(P.a),r["default"].use(k.a),r["default"].use(O.a),r["defaultLine truncated
|
||||
(function(e){function t(t){for(var r,o,u=t[0],c=t[1],f=t[2],s=0,l=[];s<u.length;s++)o=u[s],Object.prototype.hasOwnProperty.call(a,o)&&a[o]&&l.push(a[o][0]),a[o]=0;for(r in c)Object.prototype.hasOwnProperty.call(c,r)&&(e[r]=c[r]);d&&d(t);while(l.length)l.shift()();return i.push.apply(i,f||[]),n()}function n(){for(var e,t=0;t<i.length;t++){for(var n=i[t],r=!0,o=1;o<n.length;o++){var u=n[o];0!==a[u]&&(r=!1)}r&&(i.splice(t--,1),e=c(c.s=n[0]))}return e}var r={},o={app:0},a={app:0},i=[];function u(e){return c.p+"js/"+({about:"about",detail:"detail"}[e]||e)+"."+{about:"cee6f6d7",detail:"bb04bd6b"}[e]+".js"}function c(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,c),n.l=!0,n.exports}c.e=function(e){var t=[],n={about:1,detail:1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise((function(t,n){for(var r="css/"+({about:"about",detail:"detail"}[e]||e)+"."+{about:"65a00131",detail:"12a39aa7"}[e]+".css",a=c.p+r,i=document.getElementsByTagName("link"),u=0;u<i.length;u++){var f=i[u],s=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(s===r||s===a))return t()}var l=document.getElementsByTagName("style");for(u=0;u<l.length;u++){f=l[u],s=f.getAttribute("data-href");if(s===r||s===a)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var r=t&&t.target&&t.target.src||a,i=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");i.code="CSS_CHUNK_LOAD_FAILED",i.request=r,delete o[e],d.parentNode.removeChild(d),n(i)},d.href=a;var p=document.getElementsByTagName("head")[0];p.appendChild(d)})).then((function(){o[e]=0})));var r=a[e];if(0!==r)if(r)t.push(r[2]);else{var i=new Promise((function(t,n){r=a[e]=[t,n]}));t.push(r[2]=i);var f,s=document.createElement("script");s.charset="utf-8",s.timeout=120,c.nc&&s.setAttribute("nonce",c.nc),s.src=u(e);var l=new Error;f=function(t){s.onerror=s.onload=null,clearTimeout(d);var n=a[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;l.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",l.name="ChunkLoadError",l.type=r,l.request=o,n[1](l)}a[e]=void 0}};var d=setTimeout((function(){f({type:"timeout",target:s})}),12e4);s.onerror=s.onload=f,document.head.appendChild(s)}return Promise.all(t)},c.m=e,c.c=r,c.d=function(e,t,n){c.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},c.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,t){if(1&t&&(e=c(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(c.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)c.d(n,r,function(t){return e[t]}.bind(null,r));return n},c.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return c.d(t,"a",t),t},c.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},c.p="",c.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],s=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var d=s;i.push([0,"chunk-vendors"]),n()})({0:function(e,t,n){e.exports=n("56d7")},"034f":function(e,t,n){"use strict";n("85ec")},3562:function(e,t,n){e.exports=n.p+"img/loading.550df755.gif"},"56d7":function(e,t,n){"use strict";n.r(t);n("e260"),n("e6cf"),n("cca6"),n("a79d");var r=n("2b0e"),o=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"app"}},[n("router-view")],1)},a=[],i={name:"app",components:{},beforeCreate:function(){var e=this;this.$store.commit("setMiniInterface",window.innerWidth<750),window.onresize=function(){e.$store.commit("setMiniInterface",window.innerWidth<750)}}},u=i,c=(n("034f"),n("2877")),f=Object(c["a"])(u,o,a,!1,null,null,null),s=f.exports,l=(n("d3b7"),n("3ca3"),n("ddb0"),n("8c4f"));r["default"].use(l["a"]);var d=l["a"].prototype.push;l["a"].prototype.push=function(e){return d.call(this,e).catch((function(e){return e}))};var p=[{path:"/",name:"index",component:function(){return n.e("about").then(n.bind(null,"d504"))}},{path:"/chapter",name:"Chapter",component:function(){return n.e("detail").then(n.bind(null,"537b"))}}],b=new l["a"]({base:"",routes:p}),h=b,g=(n("0fb7"),n("450d"),n("f529")),m=n.n(g),v=(n("9e1f"),n("6ed5")),y=n.n(v),w=(n("be4f"),n("896a")),C=n.n(w),S=(n("cbb5"),n("8bbc")),O=n.n(S),j=(n("10cb"),n("f3ad")),k=n.n(j),x=(n("06f1"),n("6ac9")),P=n.n(x),T=(n("5466"),n("ecdf")),_=n.n(T),E=(n("38a0"),n("ad41")),L=n.n(E),$=(n("b84d"),n("c216")),A=n.n($),I=(n("8f24"),n("76b9")),M=n.n(I),B=(n("e3ea"),n("7bc3")),N=n.n(B),R=(n("1951"),n("eedf")),V=n.n(R);r["default"].use(V.a),r["default"].use(N.a),r["default"].use(M.a),r["default"].use(A.a),r["default"].use(L.a),r["default"].use(_.a),r["default"].use(P.a),r["default"].use(k.a),r["default"].use(O.a),r["defaultLine truncated
|
||||
+3
-3
@@ -27,10 +27,10 @@ function n(e){var t=Number(e.version.split(".")[0]);if(t>=2)e.mixin({beforeCreat
|
||||
*/
|
||||
function i(e,t){if(!o.canUseDOM||t&&!("addEventListener"in document))return!1;var n="on"+e,i=n in document;if(!i){var a=document.createElement("div");a.setAttribute(n,"return;"),i="function"===typeof a[n]}return!i&&r&&"wheel"===e&&(i=document.implementation.hasFeature("Events.wheel","3.0")),i}o.canUseDOM&&(r=document.implementation&&document.implementation.hasFeature&&!0!==document.implementation.hasFeature("","")),e.exports=i},"7bc3":function(e,t,n){e.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist/",n(n.s=122)}({0:function(e,t,n){"use strict";function r(e,t,n,r,o,i,a,s){var l,u="function"===typeof e?e.options:e;if(t&&(u.render=t,u.staticRenderFns=n,u._compiled=!0),r&&(u.functional=!0),i&&(u._scopeId="data-v-"+i),a?(l=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"===typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),o&&o.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(a)},u._ssrRegister=l):o&&(l=s?function(){o.call(this,this.$root.$options.shadowRoot)}:o),l)if(u.functional){u._injectStyles=l;var c=u.render;u.render=function(e,t){return l.call(t),c(e,t)}}else{var f=u.beforeCreate;u.beforeCreate=f?[].concat(f,l):[l]}return{exports:e,options:u}}n.d(t,"a",(function(){return r}))},122:function(e,t,n){"use strict";n.r(t);var r=function(e,t){var n=t._c;return n("div",t._g(t._b({class:[t.data.staticClass,"el-divider","el-divider--"+t.props.direction]},"div",t.data.attrs,!1),t.listeners),[t.slots().default&&"vertical"!==t.props.direction?n("div",{class:["el-divider__text","is-"+t.props.contentPosition]},[t._t("default")],2):t._e()])},o=[];r._withStripped=!0;var i={name:"ElDivider",props:{direction:{type:String,default:"horizontal",validator:function(e){return-1!==["horizontal","vertical"].indexOf(e)}},contentPosition:{type:String,default:"center",validator:function(e){return-1!==["left","center","right"].indexOf(e)}}}},a=i,s=n(0),l=Object(s["a"])(a,r,o,!0,null,null,null);l.options.__file="packages/divider/src/main.vue";var u=l.exports;u.install=function(e){e.component(u.name,u)};t["default"]=u}})},"7c73":function(e,t,n){var r,o=n("825a"),i=n("37e8"),a=n("7839"),s=n("d012"),l=n("1be4"),u=n("cc12"),c=n("f772"),f=">",d="<",p="prototype",h="script",v=c("IE_PROTO"),m=function(){},y=function(e){return d+h+f+e+d+"/"+h+f},g=function(e){e.write(y("")),e.close();var t=e.parentWindow.Object;return e=null,t},b=function(){var e,t=u("iframe"),n="java"+h+":";return t.style.display="none",l.appendChild(t),t.src=String(n),e=t.contentWindow.document,e.open(),e.write(y("document.F=Object")),e.close(),e.F},_=function(){try{r=new ActiveXObject("htmlfile")}catch(t){}_="undefined"!=typeof document?document.domain&&r?g(r):b():g(r);var e=a.length;while(e--)delete _[p][a[e]];return _()};s[v]=!0,e.exports=Object.create||function(e,t){var n;return null!==e?(m[p]=o(e),n=new m,m[p]=null,n[v]=e):n=_(),void 0===t?n:i.f(n,t)}},"7dd0":function(e,t,n){"use strict";var r=n("23e7"),o=n("c65b"),i=n("c430"),a=n("5e77"),s=n("1626"),l=n("9ed3"),u=n("e163"),c=n("d2bb"),f=n("d44e"),d=n("9112"),p=n("cb2d"),h=n("b622"),v=n("3f8c"),m=n("ae93"),y=a.PROPER,g=a.CONFIGURABLE,b=m.IteratorPrototype,_=m.BUGGY_SAFARI_ITERATORS,x=h("iterator"),w="keys",C="values",S="entries",O=function(){return this};e.exports=function(e,t,n,a,h,m,E){l(n,t,a);var k,j,$,A=function(e){if(e===h&&R)return R;if(!_&&e in L)return L[e];switch(e){case w:return function(){return new n(this,e)};case C:return function(){return new n(this,e)};case S:return function(){return new n(this,e)}}return function(){return new n(this)}},T=t+" Iterator",P=!1,L=e.prototype,M=L[x]||L["@@iterator"]||h&&L[h],R=!_&&M||A(h),N="Array"==t&&L.entries||M;if(N&&(k=u(N.call(new e)),k!==Object.prototype&&k.next&&(i||u(k)===b||(c?c(k,b):s(k[x])||p(k,x,O)),f(k,T,!0,!0),i&&(v[T]=O))),y&&h==C&&M&&M.name!==C&&(!i&&g?d(L,"name",C):(P=!0,R=function(){return o(M,this)})),h)if(j={values:A(C),keys:m?R:A(w),entries:A(S)},E)for($ in j)(_||P||!($ in L))&&p(L,$,j[$]);else r({target:t,proto:!0,forced:_||P},j);return i&&!E||L[x]===R||p(L,x,R,{name:h}),v[t]=R,j}},"7f4d":fuLine truncated
|
||||
/*!
|
||||
* vue-router v3.5.3
|
||||
* (c) 2021 Evan You
|
||||
* vue-router v3.5.4
|
||||
* (c) 2022 Evan You
|
||||
* @license MIT
|
||||
*/function r(e,t){for(var n in t)e[n]=t[n];return e}var o=/[!'()*]/g,i=function(e){return"%"+e.charCodeAt(0).toString(16)},a=/%2C/g,s=function(e){return encodeURIComponent(e).replace(o,i).replace(a,",")};function l(e){try{return decodeURIComponent(e)}catch(t){0}return e}function u(e,t,n){void 0===t&&(t={});var r,o=n||f;try{r=o(e||"")}catch(s){r={}}for(var i in t){var a=t[i];r[i]=Array.isArray(a)?a.map(c):c(a)}return r}var c=function(e){return null==e||"object"===typeof e?e:String(e)};function f(e){var t={};return e=e.trim().replace(/^(\?|#|&)/,""),e?(e.split("&").forEach((function(e){var n=e.replace(/\+/g," ").split("="),r=l(n.shift()),o=n.length>0?l(n.join("=")):null;void 0===t[r]?t[r]=o:Array.isArray(t[r])?t[r].push(o):t[r]=[t[r],o]})),t):t}function d(e){var t=e?Object.keys(e).map((function(t){var n=e[t];if(void 0===n)return"";if(null===n)return s(t);if(Array.isArray(n)){var r=[];return n.forEach((function(e){void 0!==e&&(null===e?r.push(s(t)):r.push(s(t)+"="+s(e)))})),r.join("&")}return s(t)+"="+s(n)})).filter((function(e){return e.length>0})).join("&"):null;return t?"?"+t:""}var p=/\/?$/;function h(e,t,n,r){var o=r&&r.options.stringifyQuery,i=t.query||{};try{i=v(i)}catch(s){}var a={name:t.name||e&&e.name,meta:e&&e.meta||{},path:t.path||"/",hash:t.hash||"",query:i,params:t.params||{},fullPath:g(t,o),matched:e?y(e):[]};return n&&(a.redirectedFrom=g(n,o)),Object.freeze(a)}function v(e){if(Array.isArray(e))return e.map(v);if(e&&"object"===typeof e){var t={};for(var n in e)t[n]=v(e[n]);return t}return e}var m=h(null,{path:"/"});function y(e){var t=[];while(e)t.unshift(e),e=e.parent;return t}function g(e,t){var n=e.path,r=e.query;void 0===r&&(r={});var o=e.hash;void 0===o&&(o="");var i=t||d;return(n||"/")+i(r)+o}function b(e,t,n){return t===m?e===t:!!t&&(e.path&&t.path?e.path.replace(p,"")===t.path.replace(p,"")&&(n||e.hash===t.hash&&_(e.query,t.query)):!(!e.name||!t.name)&&(e.name===t.name&&(n||e.hash===t.hash&&_(e.query,t.query)&&_(e.params,t.params))))}function _(e,t){if(void 0===e&&(e={}),void 0===t&&(t={}),!e||!t)return e===t;var n=Object.keys(e).sort(),r=Object.keys(t).sort();return n.length===r.length&&n.every((function(n,o){var i=e[n],a=r[o];if(a!==n)return!1;var s=t[n];return null==i||null==s?i===s:"object"===typeof i&&"object"===typeof s?_(i,s):String(i)===String(s)}))}function x(e,t){return 0===e.path.replace(p,"/").indexOf(t.path.replace(p,"/"))&&(!t.hash||e.hash===t.hash)&&w(e.query,t.query)}function w(e,t){for(var n in t)if(!(n in e))return!1;return!0}function C(e){for(var t=0;t<e.matched.length;t++){var n=e.matched[t];for(var r in n.instances){var o=n.instances[r],i=n.enteredCbs[r];if(o&&i){delete n.enteredCbs[r];for(var a=0;a<i.length;a++)o._isBeingDestroyed||i[a](o)}}}}var S={name:"RouterView",functional:!0,props:{name:{type:String,default:"default"}},render:function(e,t){var n=t.props,o=t.children,i=t.parent,a=t.data;a.routerView=!0;var s=i.$createElement,l=n.name,u=i.$route,c=i._routerViewCache||(i._routerViewCache={}),f=0,d=!1;while(i&&i._routerRoot!==i){var p=i.$vnode?i.$vnode.data:{};p.routerView&&f++,p.keepAlive&&i._directInactive&&i._inactive&&(d=!0),i=i.$parent}if(a.routerViewDepth=f,d){var h=c[l],v=h&&h.component;return v?(h.configProps&&O(v,a,h.route,h.configProps),s(v,a,o)):s()}var m=u.matched[f],y=m&&m.components[l];if(!m||!y)return c[l]=null,s();c[l]={component:y},a.registerRouteInstance=function(e,t){var n=m.instances[l];(t&&n!==e||!t&&n===e)&&(m.instances[l]=t)},(a.hook||(a.hook={})).prepatch=function(e,t){m.instances[l]=t.componentInstance},a.hook.init=function(e){e.data.keepAlive&&e.componentInstance&&e.componentInstance!==m.instances[l]&&(m.instances[l]=e.componentInstance),C(u)};var g=m.props&&m.props[l];return g&&(r(c[l],{route:u,configProps:g}),O(y,a,u,g)),s(y,a,o)}};function O(e,t,n,o){var i=t.props=E(n,o);if(i){i=t.props=r({},i);var a=t.attrs=t.attrs||{};for(var s in i)e.props&&s in e.props||(a[s]=i[s],delete i[s])}}function E(e,t){switch(typeof t){case"undefined":return;case"object":return t;case"function":return t(e);case"boolean":return t?e.params:void 0;default:0}}function k(e,t,n){var r=e.charAt(0);if("/"===r)return e;if("?"===r||"#"===r)return t+e;var o=t.split("/");n&&o[o.length-1]||o.pop();for(var i=e.replace(/^\//,"").split("/"),a=0;a<i.length;a++){var s=i[a];".."===s?o.pop():"."!==s&&o.push(s)}return""!==o[0]&&o.unshift(""),o.join("/")}function j(e){var t="",n="",r=e.indexOf("#");r>=0&&(t=e.slice(r),e=e.slice(0,r));var o=e.indexOf("?");return o>=0&&(n=e.slice(o+1),e=e.slice(0,o)),{path:e,query:n,hash:t}}function $(e){return e.replace(/\/+/g,"/")}var A=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)},T=Y,P=I,L=F,M=B,R=X,N=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function I(e,t){var n,r=[],o=0,i=0,a="",s=t&&t.delimiter||"/";while(null!=(n=N.exec(e))){var l=n[0],u=n[1],c=n.index;if(a+=e.slice(i,c),i=c+l.length,u)a+=u[1];elsLine truncated
|
||||
*/function r(e,t){for(var n in t)e[n]=t[n];return e}var o=/[!'()*]/g,i=function(e){return"%"+e.charCodeAt(0).toString(16)},a=/%2C/g,s=function(e){return encodeURIComponent(e).replace(o,i).replace(a,",")};function l(e){try{return decodeURIComponent(e)}catch(t){0}return e}function u(e,t,n){void 0===t&&(t={});var r,o=n||f;try{r=o(e||"")}catch(s){r={}}for(var i in t){var a=t[i];r[i]=Array.isArray(a)?a.map(c):c(a)}return r}var c=function(e){return null==e||"object"===typeof e?e:String(e)};function f(e){var t={};return e=e.trim().replace(/^(\?|#|&)/,""),e?(e.split("&").forEach((function(e){var n=e.replace(/\+/g," ").split("="),r=l(n.shift()),o=n.length>0?l(n.join("=")):null;void 0===t[r]?t[r]=o:Array.isArray(t[r])?t[r].push(o):t[r]=[t[r],o]})),t):t}function d(e){var t=e?Object.keys(e).map((function(t){var n=e[t];if(void 0===n)return"";if(null===n)return s(t);if(Array.isArray(n)){var r=[];return n.forEach((function(e){void 0!==e&&(null===e?r.push(s(t)):r.push(s(t)+"="+s(e)))})),r.join("&")}return s(t)+"="+s(n)})).filter((function(e){return e.length>0})).join("&"):null;return t?"?"+t:""}var p=/\/?$/;function h(e,t,n,r){var o=r&&r.options.stringifyQuery,i=t.query||{};try{i=v(i)}catch(s){}var a={name:t.name||e&&e.name,meta:e&&e.meta||{},path:t.path||"/",hash:t.hash||"",query:i,params:t.params||{},fullPath:g(t,o),matched:e?y(e):[]};return n&&(a.redirectedFrom=g(n,o)),Object.freeze(a)}function v(e){if(Array.isArray(e))return e.map(v);if(e&&"object"===typeof e){var t={};for(var n in e)t[n]=v(e[n]);return t}return e}var m=h(null,{path:"/"});function y(e){var t=[];while(e)t.unshift(e),e=e.parent;return t}function g(e,t){var n=e.path,r=e.query;void 0===r&&(r={});var o=e.hash;void 0===o&&(o="");var i=t||d;return(n||"/")+i(r)+o}function b(e,t,n){return t===m?e===t:!!t&&(e.path&&t.path?e.path.replace(p,"")===t.path.replace(p,"")&&(n||e.hash===t.hash&&_(e.query,t.query)):!(!e.name||!t.name)&&(e.name===t.name&&(n||e.hash===t.hash&&_(e.query,t.query)&&_(e.params,t.params))))}function _(e,t){if(void 0===e&&(e={}),void 0===t&&(t={}),!e||!t)return e===t;var n=Object.keys(e).sort(),r=Object.keys(t).sort();return n.length===r.length&&n.every((function(n,o){var i=e[n],a=r[o];if(a!==n)return!1;var s=t[n];return null==i||null==s?i===s:"object"===typeof i&&"object"===typeof s?_(i,s):String(i)===String(s)}))}function x(e,t){return 0===e.path.replace(p,"/").indexOf(t.path.replace(p,"/"))&&(!t.hash||e.hash===t.hash)&&w(e.query,t.query)}function w(e,t){for(var n in t)if(!(n in e))return!1;return!0}function C(e){for(var t=0;t<e.matched.length;t++){var n=e.matched[t];for(var r in n.instances){var o=n.instances[r],i=n.enteredCbs[r];if(o&&i){delete n.enteredCbs[r];for(var a=0;a<i.length;a++)o._isBeingDestroyed||i[a](o)}}}}var S={name:"RouterView",functional:!0,props:{name:{type:String,default:"default"}},render:function(e,t){var n=t.props,o=t.children,i=t.parent,a=t.data;a.routerView=!0;var s=i.$createElement,l=n.name,u=i.$route,c=i._routerViewCache||(i._routerViewCache={}),f=0,d=!1;while(i&&i._routerRoot!==i){var p=i.$vnode?i.$vnode.data:{};p.routerView&&f++,p.keepAlive&&i._directInactive&&i._inactive&&(d=!0),i=i.$parent}if(a.routerViewDepth=f,d){var h=c[l],v=h&&h.component;return v?(h.configProps&&O(v,a,h.route,h.configProps),s(v,a,o)):s()}var m=u.matched[f],y=m&&m.components[l];if(!m||!y)return c[l]=null,s();c[l]={component:y},a.registerRouteInstance=function(e,t){var n=m.instances[l];(t&&n!==e||!t&&n===e)&&(m.instances[l]=t)},(a.hook||(a.hook={})).prepatch=function(e,t){m.instances[l]=t.componentInstance},a.hook.init=function(e){e.data.keepAlive&&e.componentInstance&&e.componentInstance!==m.instances[l]&&(m.instances[l]=e.componentInstance),C(u)};var g=m.props&&m.props[l];return g&&(r(c[l],{route:u,configProps:g}),O(y,a,u,g)),s(y,a,o)}};function O(e,t,n,o){var i=t.props=E(n,o);if(i){i=t.props=r({},i);var a=t.attrs=t.attrs||{};for(var s in i)e.props&&s in e.props||(a[s]=i[s],delete i[s])}}function E(e,t){switch(typeof t){case"undefined":return;case"object":return t;case"function":return t(e);case"boolean":return t?e.params:void 0;default:0}}function k(e,t,n){var r=e.charAt(0);if("/"===r)return e;if("?"===r||"#"===r)return t+e;var o=t.split("/");n&&o[o.length-1]||o.pop();for(var i=e.replace(/^\//,"").split("/"),a=0;a<i.length;a++){var s=i[a];".."===s?o.pop():"."!==s&&o.push(s)}return""!==o[0]&&o.unshift(""),o.join("/")}function j(e){var t="",n="",r=e.indexOf("#");r>=0&&(t=e.slice(r),e=e.slice(0,r));var o=e.indexOf("?");return o>=0&&(n=e.slice(o+1),e=e.slice(0,o)),{path:e,query:n,hash:t}}function $(e){return e.replace(/\/(?:\s*\/)+/g,"/")}var A=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)},T=Y,P=I,L=F,M=B,R=X,N=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function I(e,t){var n,r=[],o=0,i=0,a="",s=t&&t.delimiter||"/";while(null!=(n=N.exec(e))){var l=n[0],u=n[1],c=n.index;if(a+=e.slice(i,c),i=c+l.length,u)a+Line truncated
|
||||
/*!
|
||||
* Vue-Lazyload.js v1.3.3
|
||||
* (c) 2019 Awe <hilongjw@gmail.com>
|
||||
File renamed without changes.
@@ -19,14 +19,16 @@ import java.io.InputStream
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import java.net.URLEncoder
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
open class WebDav(urlStr: String, val authorization: Authorization) {
|
||||
companion object {
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ZZZ")
|
||||
@SuppressLint("DateTimeFormatter")
|
||||
private val dateTimeFormatter = DateTimeFormatter.RFC_1123_DATE_TIME
|
||||
|
||||
// 指定返回哪些属性
|
||||
@Language("xml")
|
||||
@@ -133,11 +135,11 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
|
||||
.firstOrNull()?.text()?.toLong() ?: 0
|
||||
}.getOrDefault(0)
|
||||
val lastModify: Long = kotlin.runCatching {
|
||||
element.getElementsByTag("d:getcontentlength")
|
||||
element.getElementsByTag("d:getlastmodified")
|
||||
.firstOrNull()?.text()?.let {
|
||||
dateFormat.parse(it)
|
||||
LocalDateTime.parse(it, dateTimeFormatter).toInstant(ZoneOffset.of("+8")).toEpochMilli()
|
||||
}
|
||||
}.getOrNull()?.time ?: 0
|
||||
}.getOrNull() ?: 0
|
||||
webDavFile = WebDavFile(
|
||||
baseUrl + fileName,
|
||||
authorization,
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.legado.app.ui.book.info
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
@@ -33,6 +34,7 @@ import io.legado.app.ui.book.changesource.ChangeBookSourceDialog
|
||||
import io.legado.app.ui.book.group.GroupSelectDialog
|
||||
import io.legado.app.ui.book.info.edit.BookInfoEditActivity
|
||||
import io.legado.app.ui.book.read.ReadBookActivity
|
||||
import io.legado.app.ui.book.remote.manager.RemoteBookWebDav
|
||||
import io.legado.app.ui.book.search.SearchActivity
|
||||
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
||||
import io.legado.app.ui.book.toc.TocActivityResult
|
||||
@@ -129,6 +131,8 @@ class BookInfoActivity :
|
||||
viewModel.bookSource != null
|
||||
menu.findItem(R.id.menu_split_long_chapter)?.isVisible =
|
||||
viewModel.bookData.value?.isLocalTxt() ?: false
|
||||
menu.findItem(R.id.menu_upload)?.isVisible =
|
||||
viewModel.bookData.value?.isLocalBook() ?: false
|
||||
return super.onMenuOpened(featureId, menu)
|
||||
}
|
||||
|
||||
@@ -198,6 +202,17 @@ class BookInfoActivity :
|
||||
item.isChecked = !item.isChecked
|
||||
if (!item.isChecked) longToastOnUi(R.string.need_more_time_load_content)
|
||||
}
|
||||
|
||||
R.id.menu_upload -> {
|
||||
launch {
|
||||
val uri = Uri.parse(viewModel.bookData.value?.bookUrl.toString())
|
||||
if (RemoteBookWebDav.upload(uri))
|
||||
toastOnUi(getString(R.string.upload_book_success))
|
||||
else
|
||||
toastOnUi(getString(R.string.upload_book_fail))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
if (book.isLocalBook()) {
|
||||
execute {
|
||||
LocalBook.getChapterList(book).let {
|
||||
book.latestChapterTime = System.currentTimeMillis()
|
||||
appDb.bookChapterDao.delByBook(book.bookUrl)
|
||||
appDb.bookChapterDao.insert(*it.toTypedArray())
|
||||
appDb.bookDao.update(book)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package io.legado.app.ui.book.remote
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.VMBaseActivity
|
||||
|
||||
|
||||
import io.legado.app.databinding.ActivityRemoteBookBinding
|
||||
import io.legado.app.utils.toastOnUi
|
||||
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* 展示远程书籍
|
||||
* @author qianfanguojin
|
||||
* @time 2022/05/12
|
||||
*/
|
||||
class RemoteBookActivity : VMBaseActivity<ActivityRemoteBookBinding,RemoteBookViewModel>(),
|
||||
RemoteBookAdapter.CallBack {
|
||||
override val binding by viewBinding(ActivityRemoteBookBinding::inflate)
|
||||
override val viewModel by viewModels<RemoteBookViewModel>()
|
||||
private val adapter by lazy { RemoteBookAdapter(this, this) }
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
initView()
|
||||
// initEvent()
|
||||
initData()
|
||||
// toastOnUi("远程书籍")
|
||||
onFinally()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private fun initView() {
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
binding.recyclerView.adapter = adapter
|
||||
}
|
||||
private fun initData() {
|
||||
binding.refreshProgressBar.isAutoLoading = true
|
||||
viewModel.loadRemoteBookList()
|
||||
launch {
|
||||
viewModel.dataFlow.conflate().collect { remoteBooks ->
|
||||
adapter.setItems(remoteBooks)
|
||||
}
|
||||
binding.refreshProgressBar.isAutoLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFinally() {
|
||||
|
||||
}
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun addToBookshelf(remoteBook: RemoteBook) {
|
||||
viewModel.addToBookshelf(remoteBook){
|
||||
toastOnUi(getString(R.string.download_book_fail))
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.legado.app.ui.book.remote
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import cn.hutool.core.date.LocalDateTimeUtil
|
||||
import io.legado.app.base.adapter.ItemViewHolder
|
||||
import io.legado.app.base.adapter.RecyclerAdapter
|
||||
import io.legado.app.databinding.ItemRemoteBookBinding
|
||||
import io.legado.app.utils.ConvertUtils
|
||||
|
||||
|
||||
/**
|
||||
* 适配器
|
||||
* @author qianfanguojin
|
||||
*/
|
||||
class RemoteBookAdapter (context: Context, val callBack: CallBack) :
|
||||
RecyclerAdapter<RemoteBook, ItemRemoteBookBinding>(context){
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemRemoteBookBinding {
|
||||
return ItemRemoteBookBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
|
||||
override fun onCurrentListChanged() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定RecycleView 中每一个项的视图和数据
|
||||
*/
|
||||
override fun convert(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemRemoteBookBinding,
|
||||
item: RemoteBook,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
binding.run {
|
||||
//Todo:需要判断书籍是否已经加入书架,来改变“下载”按钮的文本,暂时还没有比较好的方案
|
||||
tvName.text = item.filename.substringBeforeLast(".")
|
||||
tvContentType.text = item.contentType
|
||||
tvSize.text = ConvertUtils.formatFileSize(item.size)
|
||||
tvDate.text = LocalDateTimeUtil.format(LocalDateTimeUtil.of(item.lastModify), "yyyy-MM-dd")
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerListener(holder: ItemViewHolder, binding: ItemRemoteBookBinding) {
|
||||
binding.btnDownload.setOnClickListener {
|
||||
getItem(holder.layoutPosition)?.let {
|
||||
callBack.addToBookshelf(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
interface CallBack {
|
||||
fun addToBookshelf(remoteBook: RemoteBook)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.legado.app.ui.book.remote
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
|
||||
|
||||
abstract class RemoteBookManager {
|
||||
protected val remoteBookFolder : String = "books"
|
||||
protected val contentTypeList: ArrayList<String> = arrayListOf("epub","txt")
|
||||
abstract suspend fun initRemoteContext()
|
||||
abstract suspend fun getRemoteBookList(): MutableList<RemoteBook>
|
||||
abstract suspend fun upload(localBookUri: Uri): Boolean
|
||||
abstract suspend fun delete(remoteBookUrl: String): Boolean
|
||||
|
||||
/**
|
||||
* @return String:下载到本地的路径
|
||||
*/
|
||||
abstract suspend fun getRemoteBook(remoteBook: RemoteBook): String?
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package io.legado.app.ui.book.remote
|
||||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.model.localBook.LocalBook
|
||||
import io.legado.app.ui.book.remote.manager.RemoteBookWebDav
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import java.util.*
|
||||
|
||||
class RemoteBookViewModel(application: Application): BaseViewModel(application){
|
||||
private val remoteBookFolderName = "book_remote"
|
||||
private var dataCallback : DataCallback? = null
|
||||
var isRemoteBookLiveData = MutableLiveData<Boolean>()
|
||||
var dataFlowStart: (() -> Unit)? = null
|
||||
|
||||
|
||||
val dataFlow = callbackFlow<List<RemoteBook>> {
|
||||
|
||||
val list = Collections.synchronizedList(ArrayList<RemoteBook>())
|
||||
|
||||
dataCallback = object : DataCallback {
|
||||
|
||||
override fun setItems(remoteFiles: List<RemoteBook>) {
|
||||
list.clear()
|
||||
list.addAll(remoteFiles)
|
||||
trySend(list)
|
||||
}
|
||||
|
||||
override fun addItems(remoteFiles: List<RemoteBook>) {
|
||||
list.addAll(remoteFiles)
|
||||
trySend(list)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
list.clear()
|
||||
trySend(emptyList())
|
||||
}
|
||||
}
|
||||
// withContext(Dispatchers.Main) {
|
||||
// dataFlowStart?.invoke()
|
||||
// }
|
||||
|
||||
awaitClose {
|
||||
dataCallback = null
|
||||
}
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun loadRemoteBookList() {
|
||||
execute {
|
||||
dataCallback?.clear()
|
||||
val bookList = RemoteBookWebDav.getRemoteBookList()
|
||||
dataCallback?.setItems(bookList)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun addToBookshelf(uriList: HashSet<String>, finally: () -> Unit) {
|
||||
execute {
|
||||
uriList.forEach {
|
||||
LocalBook.importFile(Uri.parse(it))
|
||||
}
|
||||
}.onFinally {
|
||||
finally.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加书籍到本地书架
|
||||
*/
|
||||
fun addToBookshelf(remoteBook: RemoteBook, finally: () -> Unit) {
|
||||
execute {
|
||||
val downloadBookPath = RemoteBookWebDav.getRemoteBook(remoteBook)
|
||||
downloadBookPath?.let {
|
||||
LocalBook.importFile(Uri.parse(it))
|
||||
}
|
||||
}.onFinally {
|
||||
finally.invoke()
|
||||
}
|
||||
}
|
||||
interface DataCallback {
|
||||
|
||||
fun setItems(remoteFiles: List<RemoteBook>)
|
||||
|
||||
fun addItems(remoteFiles: List<RemoteBook>)
|
||||
|
||||
fun clear()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
data class RemoteBook(
|
||||
val filename: String,
|
||||
val urlName: String,
|
||||
val size: Long,
|
||||
val contentType: String,
|
||||
val lastModify: Long
|
||||
)
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
package io.legado.app.ui.book.remote.manager
|
||||
|
||||
|
||||
import android.net.Uri
|
||||
import io.legado.app.constant.PreferKey
|
||||
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.config.AppConfig
|
||||
|
||||
import io.legado.app.lib.webdav.Authorization
|
||||
import io.legado.app.lib.webdav.WebDav
|
||||
import io.legado.app.lib.webdav.WebDavException
|
||||
import io.legado.app.lib.webdav.WebDavFile
|
||||
import io.legado.app.ui.book.info.BookInfoActivity
|
||||
|
||||
import io.legado.app.ui.book.remote.RemoteBook
|
||||
import io.legado.app.ui.book.remote.RemoteBookManager
|
||||
import io.legado.app.utils.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import splitties.init.appCtx
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
|
||||
object RemoteBookWebDav : RemoteBookManager() {
|
||||
private const val defaultWebDavUrl = "https://dav.jianguoyun.com/dav/"
|
||||
private var authorization: Authorization? = null
|
||||
private val remoteBookUrl get() = "${rootWebDavUrl}${remoteBookFolder}"
|
||||
private val localSaveFolder get() = "${appCtx.externalFiles.absolutePath}${File.separator}${remoteBookFolder}"
|
||||
init {
|
||||
runBlocking {
|
||||
initRemoteContext()
|
||||
}
|
||||
}
|
||||
|
||||
private val rootWebDavUrl: String
|
||||
get() {
|
||||
val configUrl = appCtx.getPrefString(PreferKey.webDavUrl)?.trim()
|
||||
var url = if (configUrl.isNullOrEmpty()) defaultWebDavUrl else configUrl
|
||||
if (!url.endsWith("/")) url = "${url}/"
|
||||
AppConfig.webDavDir?.trim()?.let {
|
||||
if (it.isNotEmpty()) {
|
||||
url = "${url}${it}/"
|
||||
}
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
override suspend fun initRemoteContext() {
|
||||
kotlin.runCatching {
|
||||
authorization = null
|
||||
val account = appCtx.getPrefString(PreferKey.webDavAccount)
|
||||
val password = appCtx.getPrefString(PreferKey.webDavPassword)
|
||||
if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
|
||||
val mAuthorization = Authorization(account, password)
|
||||
WebDav(rootWebDavUrl, mAuthorization).makeAsDir()
|
||||
WebDav(remoteBookUrl, mAuthorization).makeAsDir()
|
||||
authorization = mAuthorization
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
override suspend fun getRemoteBookList(): MutableList<RemoteBook> {
|
||||
val remoteBooks = mutableListOf<RemoteBook>()
|
||||
authorization?.let {
|
||||
//读取文件列表
|
||||
var remoteWebDavFileList : List<WebDavFile>? = null
|
||||
kotlin.runCatching {
|
||||
remoteWebDavFileList = WebDav(remoteBookUrl, it).listFiles()
|
||||
}
|
||||
//逆序文件排序
|
||||
remoteWebDavFileList = remoteWebDavFileList!!.reversed()
|
||||
//转化远程文件信息到本地对象
|
||||
remoteWebDavFileList!!.forEach { webDavFile ->
|
||||
val webDavFileName = webDavFile.displayName
|
||||
val webDavUrlName = "${remoteBookUrl}${File.separator}${webDavFile.displayName}"
|
||||
|
||||
// 转码
|
||||
//val trueFileName = String(webDavFileName.toByteArray(Charset.forName("GBK")), Charset.forName("UTF-8"))
|
||||
//val trueUrlName = String(webDavUrlName.toByteArray(Charset.forName("GBK")), Charset.forName("UTF-8"))
|
||||
|
||||
//分割后缀
|
||||
val fileExtension = webDavFileName.substringAfterLast(".")
|
||||
|
||||
//扩展名符合阅读的格式则认为是书籍
|
||||
if (contentTypeList.contains(fileExtension)) {
|
||||
remoteBooks.add(RemoteBook(webDavFileName,webDavUrlName,webDavFile.size,fileExtension,webDavFile.lastModify))
|
||||
}
|
||||
}
|
||||
} ?: throw NoStackTraceException("webDav没有配置")
|
||||
return remoteBooks
|
||||
}
|
||||
|
||||
override suspend fun getRemoteBook(remoteBook: RemoteBook): String? {
|
||||
val saveFilePath= "${localSaveFolder}${File.separator}${remoteBook.filename}"
|
||||
kotlin.runCatching {
|
||||
authorization?.let {
|
||||
FileUtils.createFolderIfNotExist(localSaveFolder).run{
|
||||
val webdav = WebDav(
|
||||
remoteBook.urlName,
|
||||
it
|
||||
)
|
||||
webdav.downloadTo(saveFilePath, true)
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
return null
|
||||
}
|
||||
return saveFilePath
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传本地导入的书籍到远程
|
||||
*/
|
||||
override suspend fun upload(localBookUri: Uri): Boolean {
|
||||
if (!NetworkUtils.isAvailable()) return false
|
||||
|
||||
val localBookName = localBookUri.path?.substringAfterLast(File.separator)
|
||||
val putUrl = "${remoteBookUrl}${File.separator}${localBookName}"
|
||||
kotlin.runCatching {
|
||||
authorization?.let {
|
||||
if (localBookUri.isContentScheme()){
|
||||
WebDav(putUrl, it).upload(byteArray = localBookUri.readBytes(appCtx),contentType = "application/octet-stream")
|
||||
}else{
|
||||
WebDav(putUrl, it).upload(localBookUri.path!!)
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun delete(remoteBookUrl: String): Boolean {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
// suspend fun showRestoreDialog(context: Context) {
|
||||
// val names = withContext(Dispatchers.IO) { getBackupNames() }
|
||||
// if (names.isNotEmpty()) {
|
||||
// withContext(Dispatchers.Main) {
|
||||
// context.selector(
|
||||
// title = context.getString(R.string.select_restore_file),
|
||||
// items = names
|
||||
// ) { _, index ->
|
||||
// if (index in 0 until names.size) {
|
||||
// Coroutine.async {
|
||||
// restoreWebDav(names[index])
|
||||
// }.onError {
|
||||
// appCtx.toastOnUi("WebDav恢复出错\n${it.localizedMessage}")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// throw NoStackTraceException("Web dav no back up file")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Throws(WebDavException::class)
|
||||
// suspend fun restoreWebDav(name: String) {
|
||||
// authorization?.let {
|
||||
// val webDav = WebDav(rootWebDavUrl + name, it)
|
||||
// webDav.downloadTo(zipFilePath, true)
|
||||
// @Suppress("BlockingMethodInNonBlockingContext")
|
||||
// ZipUtils.unzipFile(zipFilePath, Backup.backupPath)
|
||||
// Restore.restoreDatabase()
|
||||
// Restore.restoreConfig()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// suspend fun hasBackUp(): Boolean {
|
||||
// authorization?.let {
|
||||
// val url = "${rootWebDavUrl}${backupFileName}"
|
||||
// return WebDav(url, it).exists()
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// suspend fun lastBackUp(): Result<WebDavFile?> {
|
||||
// return kotlin.runCatching {
|
||||
// authorization?.let {
|
||||
// var lastBackupFile: WebDavFile? = null
|
||||
// WebDav(rootWebDavUrl, it).listFiles().reversed().forEach { webDavFile ->
|
||||
// if (webDavFile.displayName.startsWith("backup")) {
|
||||
// if (lastBackupFile == null
|
||||
// || webDavFile.lastModify > lastBackupFile!!.lastModify
|
||||
// ) {
|
||||
// lastBackupFile = webDavFile
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// lastBackupFile
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Throws(Exception::class)
|
||||
// suspend fun backUpWebDav(path: String) {
|
||||
// if (!NetworkUtils.isAvailable()) return
|
||||
// authorization?.let {
|
||||
// val paths = arrayListOf(*Backup.backupFileNames)
|
||||
// for (i in 0 until paths.size) {
|
||||
// paths[i] = path + File.separator + paths[i]
|
||||
// }
|
||||
// FileUtils.delete(zipFilePath)
|
||||
// if (ZipUtils.zipFiles(paths, zipFilePath)) {
|
||||
// val putUrl = "${rootWebDavUrl}${backupFileName}"
|
||||
// WebDav(putUrl, it).upload(zipFilePath)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// suspend fun exportWebDav(byteArray: ByteArray, fileName: String) {
|
||||
// if (!NetworkUtils.isAvailable()) return
|
||||
// try {
|
||||
// authorization?.let {
|
||||
// // 如果导出的本地文件存在,开始上传
|
||||
// val putUrl = exportsWebDavUrl + fileName
|
||||
// WebDav(putUrl, it).upload(byteArray, "text/plain")
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// val msg = "WebDav导出\n${e.localizedMessage}"
|
||||
// AppLog.put(msg)
|
||||
// appCtx.toastOnUi(msg)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun uploadBookProgress(book: Book) {
|
||||
// val authorization = authorization ?: return
|
||||
// if (!syncBookProgress) return
|
||||
// if (!NetworkUtils.isAvailable()) return
|
||||
// Coroutine.async {
|
||||
// val bookProgress = BookProgress(book)
|
||||
// val json = GSON.toJson(bookProgress)
|
||||
// val url = getProgressUrl(book)
|
||||
// WebDav(url, authorization).upload(json.toByteArray(), "application/json")
|
||||
// }.onError {
|
||||
// AppLog.put("上传进度失败\n${it.localizedMessage}")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun getProgressUrl(book: Book): String {
|
||||
// return bookProgressUrl + book.name + "_" + book.author + ".json"
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取书籍进度
|
||||
// */
|
||||
// suspend fun getBookProgress(book: Book): BookProgress? {
|
||||
// authorization?.let {
|
||||
// val url = getProgressUrl(book)
|
||||
// kotlin.runCatching {
|
||||
// WebDav(url, it).download().let { byteArray ->
|
||||
// val json = String(byteArray)
|
||||
// if (json.isJson()) {
|
||||
// return GSON.fromJsonObject<BookProgress>(json).getOrNull()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return null
|
||||
// }
|
||||
//
|
||||
// suspend fun downloadAllBookProgress() {
|
||||
// authorization ?: return
|
||||
// if (!NetworkUtils.isAvailable()) return
|
||||
// appDb.bookDao.all.forEach { book ->
|
||||
// getBookProgress(book)?.let { bookProgress ->
|
||||
// if (bookProgress.durChapterIndex > book.durChapterIndex
|
||||
// || (bookProgress.durChapterIndex == book.durChapterIndex
|
||||
// && bookProgress.durChapterPos > book.durChapterPos)
|
||||
// ) {
|
||||
// book.durChapterIndex = bookProgress.durChapterIndex
|
||||
// book.durChapterPos = bookProgress.durChapterPos
|
||||
// book.durChapterTitle = bookProgress.durChapterTitle
|
||||
// book.durChapterTime = bookProgress.durChapterTime
|
||||
// appDb.bookDao.update(book)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.legado.app.ui.main.bookshelf
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.fragment.app.activityViewModels
|
||||
@@ -23,6 +24,7 @@ import io.legado.app.ui.book.cache.CacheActivity
|
||||
import io.legado.app.ui.book.group.GroupManageDialog
|
||||
import io.legado.app.ui.book.local.ImportBookActivity
|
||||
import io.legado.app.ui.book.manage.BookshelfManageActivity
|
||||
import io.legado.app.ui.book.remote.RemoteBookActivity
|
||||
import io.legado.app.ui.book.search.SearchActivity
|
||||
import io.legado.app.ui.document.HandleFileContract
|
||||
import io.legado.app.ui.main.MainViewModel
|
||||
@@ -74,6 +76,8 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
|
||||
override fun onCompatOptionsItemSelected(item: MenuItem) {
|
||||
super.onCompatOptionsItemSelected(item)
|
||||
when (item.itemId) {
|
||||
// 查看远程书籍
|
||||
R.id.menu_remote -> startActivity<RemoteBookActivity>()
|
||||
R.id.menu_search -> startActivity<SearchActivity>()
|
||||
R.id.menu_update_toc -> activityViewModel.upToc(books)
|
||||
R.id.menu_bookshelf_layout -> configBookshelf()
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<io.legado.app.ui.widget.TitleBar
|
||||
android:id="@+id/titleBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:title="远程书籍" />
|
||||
|
||||
<!-- <io.legado.app.ui.widget.anima.RefreshProgressBar-->
|
||||
<!-- android:id="@+id/refresh_progress_bar"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="2dp"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/lay_top" />-->
|
||||
<io.legado.app.ui.widget.anima.RefreshProgressBar
|
||||
android:id="@+id/refresh_progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/titleBar" />
|
||||
|
||||
<io.legado.app.ui.widget.dynamiclayout.DynamicFrameLayout
|
||||
android:id="@+id/content_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/refresh_progress_bar">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</io.legado.app.ui.widget.dynamiclayout.DynamicFrameLayout>
|
||||
|
||||
<!-- <io.legado.app.ui.widget.SelectActionBar-->
|
||||
<!-- android:id="@+id/select_action_bar"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/content_view"-->
|
||||
<!-- />-->
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<io.legado.app.ui.widget.image.CoverImageView
|
||||
android:id="@+id/iv_cover"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="110dp"
|
||||
android:layout_margin="8dp"
|
||||
android:contentDescription="@string/img_cover"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/image_cover_default"
|
||||
android:transitionName="img_cover"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/primaryText"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toEndOf="@+id/iv_cover"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="top"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/tv_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_name">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
tools:text="128kb"
|
||||
android:textColor="@color/tv_text_summary"
|
||||
android:textSize="13sp" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:text="@string/separator"
|
||||
android:textColor="@color/tv_text_summary"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_date"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
tools:text="2022-12-7"
|
||||
android:textColor="@color/tv_text_summary"
|
||||
android:textSize="13sp" />
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<io.legado.app.ui.widget.text.AccentBgTextView
|
||||
android:id="@+id/tv_content_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:paddingStart="5dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:text="TXT"
|
||||
android:maxLines="1"
|
||||
android:maxWidth="50dp"
|
||||
app:radius="2dp"
|
||||
tools:ignore="HardcodedText,RtlHardcoded"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/tv_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/ll_info"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="3dp"
|
||||
>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_download"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="加入书架"
|
||||
android:text="@string/nb_file_add_shelf">
|
||||
|
||||
</Button>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -14,6 +14,11 @@
|
||||
android:title="@string/share"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_upload"
|
||||
android:title="@string/upload_to_remote"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_refresh"
|
||||
android:title="@string/refresh"
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
android:title="@string/search"
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_remote"
|
||||
android:icon="@drawable/ic_add"
|
||||
android:title="@string/add_remote_book"
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_update_toc"
|
||||
android:icon="@drawable/ic_refresh_black_24dp"
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
<string name="tip_perm_request_storage">Legado needs storage access to find and read books. please go "App Settings" to allow "Storage permission".</string>
|
||||
|
||||
<!--Other-->
|
||||
<string name="upload_book_success" translatable="false">Upload Success</string>
|
||||
<string name="upload_book_fail" translatable="false">Upload Fail</string>
|
||||
<string name="download_book_success" translatable="false">Download Success</string>
|
||||
<string name="download_book_fail" translatable="false">Download Fail</string>
|
||||
<string name="separator" translatable="false">丨</string>
|
||||
<string name="upload_to_remote" translatable="false">Upload</string>
|
||||
<string name="add_remote_book" translatable="false">Add Remote</string>
|
||||
<string name="menu_backup">Home</string>
|
||||
<string name="menu_restore">Restore</string>
|
||||
<string name="menu_import_old">Import Legado data</string>
|
||||
|
||||
Reference in new issue
Block a user