diff --git a/app/build.gradle b/app/build.gradle index f3777b469..a8775b1c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,9 +9,6 @@ androidExtensions { android { compileSdkVersion 28 - dataBinding { - enabled = true - } defaultConfig { applicationId "io.legado.app" minSdkVersion 21 @@ -85,6 +82,6 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.8.0' testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3bef8cc80..9d4cfaca1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/AppTheme" + android:theme="@style/CAppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> @@ -32,7 +32,7 @@ + android:theme="@style/CAppTheme.NoActionBar"> diff --git a/app/src/main/assets/disclaimer.md b/app/src/main/assets/disclaimer.md new file mode 100644 index 000000000..0f630a9e2 --- /dev/null +++ b/app/src/main/assets/disclaimer.md @@ -0,0 +1,16 @@ +# 免责声明(Disclaimer) + +* 阅读是一款提供网络文学搜索的工具,为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。 +* 当您搜索一本书的时,阅读会将该书的书名以关键词的形式提交到各个第三方网络文学网站。 +各第三方网站返回的内容与阅读无关,阅读对其概不负责,亦不承担任何法律责任。 +任何通过使用阅读而链接到的第三方网页均系他人制作或提供,您可能从第三方网页上获得其他服务,阅读对其合法性概不负责,亦不承担任何法律责任。 +第三方搜索引擎结果根据您提交的书名自动搜索获得并提供试读,不代表阅读赞成或被搜索链接到的第三方网页上的内容或立场。 +您应该对使用搜索引擎的结果自行承担风险。 +* 阅读不做任何形式的保证:不保证第三方搜索引擎的搜索结果满足您的要求,不保证搜索服务不中断,不保证搜索结果的安全性、正确性、及时性、合法性。 +因网络状况、通讯线路、第三方网站等任何原因而导致您不能正常使用阅读,阅读不承担任何法律责任。 +阅读尊重并保护所有使用阅读用户的个人隐私权,您注册的用户名、电子邮件地址等个人资料,非经您亲自许可或根据相关法律、法规的强制性规定,阅读不会主动地泄露给第三方。 +* 阅读致力于最大程度地减少网络文学阅读者在自行搜寻过程中的无意义的时间浪费,通过专业搜索展示不同网站中网络文学的最新章节。 +阅读在为广大小说爱好者提供方便、快捷舒适的试读体验的同时,也使优秀网络文学得以迅速、更广泛的传播,从而达到了在一定程度促进网络文学充分繁荣发展之目的。 +阅读鼓励广大小说爱好者通过阅读发现优秀网络小说及其提供商,并建议阅读正版图书。 +任何单位或个人认为通过阅读搜索链接到的第三方网页内容可能涉嫌侵犯其信息网络传播权,应该及时向阅读提出书面权力通知,并提供身份证明、权属证明及详细侵权情况证明。 +阅读在收到上述法律文件后,将会依法尽快断开相关链接内容。 \ No newline at end of file diff --git a/app/src/main/assets/number.ttf b/app/src/main/assets/number.ttf new file mode 100644 index 000000000..f2804dbe9 Binary files /dev/null and b/app/src/main/assets/number.ttf differ diff --git a/app/src/main/assets/txtChapterRule.json b/app/src/main/assets/txtChapterRule.json new file mode 100644 index 000000000..b035fccb5 --- /dev/null +++ b/app/src/main/assets/txtChapterRule.json @@ -0,0 +1,38 @@ +[ + { + "enable": true, + "name": "默认正则1", + "rule": "^(.{0,8})(第)([0-9零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10})([章节卷集部篇回场])(.{0,30})$", + "serialNumber": 0 + }, + { + "enable": true, + "name": "默认正则2", + "rule": "^([0-9]{1,5})([\\,\\.,-])(.{1,20})$", + "serialNumber": 1 + }, + { + "enable": true, + "name": "默认正则3", + "rule": "^(\\s{0,4})([\\(【《]?(卷)?)([0-9零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10})([\\.:: \f\t])(.{0,30})$", + "serialNumber": 2 + }, + { + "enable": true, + "name": "默认正则4", + "rule": "^(\\s{0,4})([\\((【《])(.{0,30})([\\))】》])(\\s{0,2})$", + "serialNumber": 3 + }, + { + "enable": true, + "name": "默认正则5", + "rule": "^(\\s{0,4})(正文)(.{0,20})$", + "serialNumber": 4 + }, + { + "enable": true, + "name": "默认正则6", + "rule": "^(.{0,4})(Chapter|chapter)(\\s{0,4})([0-9]{1,4})(.{0,30})$", + "serialNumber": 5 + } +] \ No newline at end of file diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md new file mode 100644 index 000000000..8038b0c77 --- /dev/null +++ b/app/src/main/assets/updateLog.md @@ -0,0 +1,5 @@ +## 本软件为开源软件,没有上架Google Play,请不要在任何地方购买! +### 关注公众号[开源阅读软件]可获取书源,点击广告可支持作者! +### 捐赠里点击红包搜索码可开启高级功能! +## 更新日志 + diff --git a/app/src/main/assets/web/bookshelf.css b/app/src/main/assets/web/bookshelf.css new file mode 100644 index 000000000..f6e26caa9 --- /dev/null +++ b/app/src/main/assets/web/bookshelf.css @@ -0,0 +1,176 @@ +html, body { + height: 100%; + margin: 0; +} + +.hide { + display: none; +} + +.top, .showchapter, .hidebooks { + width: 60px; + height: 50px; + position: absolute; + right: 30px; + bottom: 30px; + color: black; + font-size: 28px; + background-color: #ddd; + opacity: 0.85; +} + +.top { + bottom: 150px; +} + +.showchapter { + bottom: 90px; + bottom: 90px; +} + +.address { + width: 270px; +} + +.nav { + border-bottom: solid 1px #ccc; +} + +input, button { + width: 110px; + line-height: 34px; + background-color: #eee; + color: #555; + border: none; + margin: 10px 5px; + font-weight: 500; + border-radius: 2px; + outline: none; + cursor: pointer; +} + +input { + padding: 0 10px; + cursor: text; +} + + input:hover, button:hover { + border-color: #aaa; + background-color: #efefef; + color: #222; + outline: solid 1px #ccc; + } + +.allcontent { + height: calc(100% - 60px); +} + +.allscreen { + height: 100% +} + +.books > div { + display: inline-block; + margin: 10px; + vertical-align: top; + border: solid 1px #ddd; +} + +.read > .books { + width: 420px; + float: left; + height: 100%; + overflow: auto; + border-right: solid 1px #ccc; +} + + .read > .books > div { + margin-right: 0; + border-right: none; + } + + +.more { + overflow-y: auto; + height: 100%; + display: none; +} + +.read .more { + display: block; +} + +.books > div > img { + width: 120px; + height: 180px; + float: left; + margin-right: 10px; + cursor: pointer; +} + +.info { + padding: 10px 20px 0 20px; + width: 600px; + margin: 0 auto; +} + + .info > img { + width: 600px; + height: 900px; + } + + .info p { + line-height: 1.5; + text-align: justify; + margin: 0; + } + +.books tr:nth-child(n+2) td { + border-top: solid 1px #999; +} + +.books td:nth-child(1) { + vertical-align: top; + width: 50px; +} + +.books td:nth-child(2) { + vertical-align: top; + width: 200px; +} + +.clear { + clear: both; +} + +.chapter { + margin: 10px; + max-height: 500px; + overflow-y: auto; + border-top: solid 1px #333; + border-bottom: solid 1px #333; +} + + .chapter button { + width: 230px; + text-align: left; + text-indent: 14px; + margin: 10px 4px; + } + + +.content { + padding: 20px; + text-align: justify; + min-height: 1000px; + padding-bottom: 200px; +} + + .content h2 { + font-family: "Microsoft YaHei",微软雅黑,"MicrosoftJhengHei",华文细黑,STHeiti,MingLiu; + font-weight: 500; + text-align: center; + line-height: 100px; + font-size: 40px; + margin: 0; + } diff --git a/app/src/main/assets/web/bookshelf.html b/app/src/main/assets/web/bookshelf.html new file mode 100644 index 000000000..485622373 --- /dev/null +++ b/app/src/main/assets/web/bookshelf.html @@ -0,0 +1,32 @@ + + + + + 阅读书架 + + + + + + + + +
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/app/src/main/assets/web/bookshelf.js b/app/src/main/assets/web/bookshelf.js new file mode 100644 index 000000000..22c8ff87e --- /dev/null +++ b/app/src/main/assets/web/bookshelf.js @@ -0,0 +1,161 @@ +var $ = document.querySelector.bind(document) + , $$ = document.querySelectorAll.bind(document) + , $c = document.createElement.bind(document) + , randomImg = "http://acg.bakayun.cn/randbg.php?t=dfzh" + , randomImg2 = "http://img.xjh.me/random_img.php" + , books + ; + +var formatTime = value => { + return new Date(value).toLocaleString('zh-CN', { + hour12: false, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" + }).replace(/\//g, "-"); +}; + +var apiMap = { + getBookshelf: "/getBookshelf", + getChapterList: "/getChapterList", + getBookContent: "/getBookContent", + saveBook: "/saveBook" +}; + +var apiAddress = (apiName, url) => { + let address = $('#address').value || window.location.host; + if (!(/^http|^\/\//).test(address)) { + address = "//" + address; + } + if (!(/:\d{4,}/).test(address.split("//")[1].split("/")[0])) { + address += ":1122"; + } + localStorage.setItem('address', address); + return address + apiMap[apiName] + (url ? "?url=" + encodeURIComponent(url) : ""); +}; + +var init = () => { + $('#allcontent').classList.remove("read"); + $('#books').innerHTML = ""; + fetch(apiAddress("getBookshelf"), { mode: "cors" }) + .then(res => res.json()) + .then(data => { + if (!data.isSuccess) { + alert(getBookshelf.errorMsg); + return; + } + books = data.data.sort((book1, book2) => book1.serialNumber - book2.serialNumber); + books.forEach(book => { + let bookDiv = $c("div"); + let img = $c("img"); + img.src = book.bookInfoBean.coverUrl || randomImg; + img.setAttribute("data-series-num", book.serialNumber); + bookDiv.appendChild(img); + bookDiv.innerHTML += ` + + + + + +
书名:${book.bookInfoBean.name}
作者:${book.bookInfoBean.author}
阅读:${book.durChapterName}
${formatTime(book.finalDate)}
更新:${book.lastChapterName}
${formatTime(book.finalRefreshData)}
来源:${book.bookInfoBean.origin}
`; + $('#books').appendChild(bookDiv); + }); + $$('#books img').forEach(bookImg => + bookImg.addEventListener("click", () => { + $('#allcontent').classList.add("read"); + var book = books[bookImg.getAttribute("data-series-num")]; + $("#info").innerHTML = ` +

  来源:${book.bookInfoBean.origin}

+

  书名:${book.bookInfoBean.name}

+

  作者:${book.bookInfoBean.author}

+

阅读章节:${book.durChapterName}

+

阅读时间:${formatTime(book.finalDate)}

+

最新章节:${book.lastChapterName}

+

检查时间:${formatTime(book.finalRefreshData)}

+

  简介:${book.bookInfoBean.introduce.trim().replace(/\n/g, "
")}

`; + window.location.hash = ""; + window.location.hash = "#info"; + $("#content").innerHTML = "章节列表加载中..."; + $("#chapter").innerHTML = ""; + fetch(apiAddress("getChapterList", book.noteUrl), { mode: "cors" }) + .then(res => res.json()) + .then(data => { + if (!data.isSuccess) { + alert(data.errorMsg); + $("#content").innerHTML = "章节列表加载失败!"; + return; + } + + data.data.forEach(chapter => { + let ch = $c("button"); + ch.setAttribute("data-url", chapter.durChapterUrl); + ch.setAttribute("title", chapter.durChapterName); + ch.innerHTML = chapter.durChapterName.length > 15 ? chapter.durChapterName.substring(0, 14) + "..." : chapter.durChapterName; + $("#chapter").appendChild(ch); + }); + $('#chapter').scrollTop = 0; + $("#content").innerHTML = "章节列表加载完成!"; + }); + + })); + }); +}; + +$("#back").addEventListener("click", () => { + if (window.location.hash === "#content") { + window.location.hash = "#chapter"; + } else if (window.location.hash === "#chapter") { + window.location.hash = "#info"; + } else { + $('#allcontent').classList.remove("read"); + } +}); + +$("#refresh").addEventListener("click", init); + +$('#hidebooks').addEventListener("click", () => { + $("#books").classList.toggle("hide"); + $(".nav").classList.toggle("hide"); + $("#allcontent").classList.toggle("allscreen"); +}); + +$('#top').addEventListener("click", () => { + window.location.hash = ""; + window.location.hash = "#info"; +}); + +$('#showchapter').addEventListener("click", () => { + window.location.hash = ""; + window.location.hash = "#chapter"; +}); + +$('#chapter').addEventListener("click", (e) => { + if (e.target.tagName === "BUTTON") { + var url = e.target.getAttribute("data-url"); + var name = e.target.getAttribute("title"); + if (!url) { + alert("未取得章节地址"); + } + $("#content").innerHTML = "

" + name + " 加载中...

"; + fetch(apiAddress("getBookContent", url), { mode: "cors" }) + .then(res => res.json()) + .then(data => { + if (!data.isSuccess) { + alert(data.errorMsg); + $("#content").innerHTML = "

" + name + " 加载失败!

"; + return; + } + var content = data.data.trim().split("\n\n"); + if (content.length === 2) { + $("#content").innerHTML = `

${content[0]}

  (全文 ${content[1].length} 字)

  ` + content[1].trim().replace(/\n/g, "

"); + } else { + $("#content").innerHTML = `

${name || e.target.innerHTML}

  (全文 ${data.data.length} 字)

  ` + data.data.trim().replace(/\n/g, "

"); + } + window.location.hash = ""; + window.location.hash = "#content"; + }); + } +}); + +$('#address').setAttribute("placeholder", "阅读APP地址或IP:" + window.location.host); +if (!$('#address').value && typeof localStorage && localStorage.getItem('address')) { + $('#address').value = localStorage.getItem('address'); +} +init(); diff --git a/app/src/main/assets/web/favicon.ico b/app/src/main/assets/web/favicon.ico new file mode 100644 index 000000000..c3d5e6bc0 Binary files /dev/null and b/app/src/main/assets/web/favicon.ico differ diff --git a/app/src/main/assets/web/index.css b/app/src/main/assets/web/index.css new file mode 100644 index 000000000..a3e76ba06 --- /dev/null +++ b/app/src/main/assets/web/index.css @@ -0,0 +1,148 @@ +body { + margin: 0; +} +.editor { + display: flex; + align-items: stretch; +} +.setbox, +.menu, +.outbox { + flex: 1; + display: flex; + flex-flow: column; + max-height: 100vh; + overflow-y: auto; +} +.menu { + justify-content: center; + max-width: 90px; + margin: 0 5px; +} +.menu .button { + width: 90px; + height: 30px; + min-height: 30px; + margin: 5px 0px; + cursor: pointer; +} +@keyframes stroker { + 0% { + stroke-dashoffset: 0 + } + 100% { + stroke-dashoffset: -240 + } +} +.button rect { + width: 100%; + height: 100%; + fill: transparent; + stroke: #666; + stroke-width: 2px; +} +.button rect.busy { + stroke: #fD1850; + stroke-dasharray: 30 90; + animation: stroker 1s linear infinite; +} +.button text { + text-anchor: middle; + dominant-baseline: middle; +} +.setbox { + min-width: 40em; +} +.rules, +.tabbox { + flex: 1; + display: flex; + flex-flow: column; +} +.rules>* { + display: flex; + margin: 2px 0; +} +.rules textarea { + flex: 1; + margin-left: 5px; +} +.rules>*, +.rules>*>div, +.rules textarea { + min-height: 1em; +} +textarea { + word-break: break-all; +} +.tabtitle { + display: flex; + z-index: 1; + justify-content: flex-end; +} +.tabtitle>div { + cursor: pointer; + padding: 1px 10px 0 10px; + border-bottom: 3px solid transparent; + font-weight: bold; +} +.tabtitle>.this { + color: #4f9da6; + border-bottom-color: #4EBBE4; +} +.tabbody { + flex: 1; + display: flex; + margin-top: -1px; + border: 1px solid #A9A9A9; + height: 0; +} +.tabbody>* { + flex: 1; + flex-flow: column; + display: none; +} +.tabbody>.this { + display: flex; +} +.tabbody>*>.titlebar{ + display: flex; +} +.tabbody>*>.titlebar>*{ + flex: 1; + margin: 1px 1px 1px 1px; +} +.tabbody>*>.context { + flex: 1; + flex-flow: column; + border: 0; + padding: 5px; + overflow-y: auto; +} +.tabbody>*>.inputbox{ + border: 0; + border-bottom: #A9A9A9 solid 1px; + height: 15px; + text-align:center; +} +.link>* { + display: flex; + margin: 5px; + border-bottom: 1px solid; + text-decoration: none; +} +#RuleList>label>* { + background: #eee; + padding-left: 3px; + margin: 2px 0; + cursor: pointer; +} +#RuleList input[type=radio] { + display: none; +} +#RuleList input[type="radio"]:checked+* { + background: #15cda8; +} +.isError { + color: #FF0000; +} \ No newline at end of file diff --git a/app/src/main/assets/web/index.html b/app/src/main/assets/web/index.html new file mode 100644 index 000000000..2c47adb7f --- /dev/null +++ b/app/src/main/assets/web/index.html @@ -0,0 +1,306 @@ + + + + + + 书源编辑器v3.8 + + + + +
+
+
+
书源基础信息
+
+
书源名称:
+ +
+
+
书源分组:
+ +
+
+
书源域名:
+ +
+
+
登录网页:
+ +
+
书籍发现规则
+
+
发现菜单:
+ +
+
+
结果列表:
+ +
+
+
书籍名称:
+ +
+
+
书籍作者:
+ +
+
+
书籍分类:
+ +
+
+
最新章节:
+ +
+
+
简介内容:
+ +
+
+
封面链接:
+ +
+
+
详情链接:
+ +
+
书籍搜索规则
+
+
搜索网址:
+ +
+
+
结果验证:
+ +
+
+
结果列表:
+ +
+
+
书籍名称:
+ +
+
+
书籍作者:
+ +
+
+
书籍分类:
+ +
+
+
最新章节:
+ +
+
+
简介内容:
+ +
+
+
封面链接:
+ +
+
+
详情链接:
+ +
+
书籍详情规则
+
+
页面处理:
+ +
+
+
书籍名称:
+ +
+
+
书籍作者:
+ +
+
+
书籍分类:
+ +
+
+
最新章节:
+ +
+
+
简介内容:
+ +
+
+
封面链接:
+ +
+
+
目录链接:
+ +
+
目录列表规则
+
+
目录翻页:
+ +
+
+
目录列表:
+ +
+
+
章节名称:
+ +
+
+
章节链接:
+ +
+
正文阅读规则
+
+
章节正文:
+ +
+
+
正文翻页:
+ +
+
其它规则
+
+
浏览标识:
+ +
+
+
排序编号:
+ +
+
+
搜索权重:
+ +
+
+
是否启用:
+ +
+
+
+ +
+
+
+
编辑书源
+
调试书源
+
书源列表
+
帮助信息
+
+
+
+ +
+
+ + +
+
+
+ + + + +
+
+
+
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/app/src/main/assets/web/index.js b/app/src/main/assets/web/index.js new file mode 100644 index 000000000..9463800ce --- /dev/null +++ b/app/src/main/assets/web/index.js @@ -0,0 +1,362 @@ +// 简化js原生选择器 +function $(selector) { return document.querySelector(selector); } +function $$(selector) { return document.querySelectorAll(selector); } +// 读写Hash值(val未赋值时为读取) +function hashParam(key, val) { + let hashstr = decodeURIComponent(window.location.hash); + let regKey = new RegExp(`${key}=([^&]*)`); + let getVal = regKey.test(hashstr) ? hashstr.match(regKey)[1] : null; + if (val == undefined) return getVal; + if (hashstr == '' || hashstr == '#') { + window.location.hash = `#${key}=${val}`; + } + else { + if (getVal) window.location.hash = hashstr.replace(getVal, val); + else { + window.location.hash = hashstr.indexOf(key) > -1 ? hashstr.replace(regKey, `${key}=${val}`) : `${hashstr}&${key}=${val}`; + } + } +} +// 创建书源规则容器对象 +const RuleJSON = (() => { + let ruleJson = {}; + $$('.rules textarea').forEach(item => ruleJson[item.id] = ''); +// for (let item of $$('.rules textarea')) ruleJson[item.id] = ''; + ruleJson.serialNumber = 0; + ruleJson.weight = 0; + ruleJson.enable = true; + return ruleJson; +})(); +// 选项卡Tab切换事件处理 +function showTab(tabName) { + $$('.tabtitle>*').forEach(node => { node.className = node.className.replace(' this', ''); }); + $$('.tabbody>*').forEach(node => { node.className = node.className.replace(' this', ''); }); + $(`.tabbody>.${$(`.tabtitle>*[name=${tabName}]`).className}`).className += ' this'; + $(`.tabtitle>*[name=${tabName}]`).className += ' this'; + hashParam('tab', tabName); +} +// 书源列表列表标签构造函数 +function newRule(rule) { + return ``; +} +// 缓存规则列表 +var RuleSources = []; +if (localStorage.getItem('RuleSources')) { + RuleSources = JSON.parse(localStorage.getItem('RuleSources')); + RuleSources.forEach(item => $('#RuleList').innerHTML += newRule(item)); +} +// 页面加载完成事件 +window.onload = () => { + $$('.tabtitle>*').forEach(item => { + item.addEventListener('click', () => { + showTab(item.innerHTML); + }); + }); + if (hashParam('tab')) showTab(hashParam('tab')); +} +// 获取数据 +function HttpGet(url) { + return fetch(hashParam('domain') ? hashParam('domain') + url : url) + .then(res => res.json()).catch(err => console.error('Error:', err)); +} +// 提交数据 +function HttpPost(url, data) { + return fetch(hashParam('domain') ? hashParam('domain') + url : url, { + body: JSON.stringify(data), + method: 'POST', + mode: "cors", + headers: new Headers({ + 'Content-Type': 'application/json;charset=utf-8' + }) + }).then(res => res.json()).catch(err => console.error('Error:', err)); +} +// 将书源表单转化为书源对象 +function rule2json() { + Object.keys(RuleJSON).forEach((key) => RuleJSON[key] = $('#' + key).value); + RuleJSON.serialNumber = RuleJSON.serialNumber == '' ? 0 : parseInt(RuleJSON.serialNumber); + RuleJSON.weight = RuleJSON.weight == '' ? 0 : parseInt(RuleJSON.weight); + RuleJSON.enable = RuleJSON.enable == '' || RuleJSON.enable.toLocaleLowerCase().replace(/^\s*|\s*$/g, '') == 'true'; + return RuleJSON; +} +// 将书源对象填充到书源表单 +function json2rule(RuleEditor) { + Object.keys(RuleJSON).forEach((key) => $("#" + key).value = RuleEditor[key] ? RuleEditor[key] : ''); +} +// 记录操作过程 +var course = { "old": [], "now": {}, "new": [] }; +if (localStorage.getItem('course')) { + course = JSON.parse(localStorage.getItem('course')); + json2rule(course.now); +} +else { + course.now = rule2json(); + window.localStorage.setItem('course', JSON.stringify(course)); +} +function todo() { + course.old.push(Object.assign({}, course.now)); + course.now = rule2json(); + course.new = []; + if (course.old.length > 50) course.old.shift(); // 限制历史记录堆栈大小 + localStorage.setItem('course', JSON.stringify(course)); +} +function undo() { + course = JSON.parse(localStorage.getItem('course')); + if (course.old.length > 0) { + course.new.push(course.now); + course.now = course.old.pop(); + localStorage.setItem('course', JSON.stringify(course)); + json2rule(course.now); + } +} +function redo() { + course = JSON.parse(localStorage.getItem('course')); + if (course.new.length > 0) { + course.old.push(course.now); + course.now = course.new.pop(); + localStorage.setItem('course', JSON.stringify(course)); + json2rule(course.now); + } +} +function setRule(editRule) { + let checkRule = RuleSources.find(x => x.bookSourceUrl == editRule.bookSourceUrl); + if ($(`input[id="${editRule.bookSourceUrl}"]`)) { + Object.keys(checkRule).forEach(key => { checkRule[key] = editRule[key]; }); + $(`input[id="${editRule.bookSourceUrl}"]+*`).innerHTML = `${editRule.bookSourceName}
${editRule.bookSourceUrl}`; + } else { + RuleSources.push(editRule); + $('#RuleList').innerHTML += newRule(editRule); + } +} +$$('input').forEach((item) => { item.addEventListener('change', () => { todo() }) }); +$$('textarea').forEach((item) => { item.addEventListener('change', () => { todo() }) }); +// 处理按钮点击事件 +$('.menu').addEventListener('click', e => { + let thisNode = e.target; + thisNode = thisNode.parentNode.nodeName == 'svg' ? thisNode.parentNode.querySelector('rect') : + thisNode.nodeName == 'svg' ? thisNode.querySelector('rect') : null; + if (!thisNode) return; + if (thisNode.getAttribute('class') == 'busy') return; + thisNode.setAttribute('class', 'busy'); + switch (thisNode.id) { + case 'push': + $$('#RuleList>label>div').forEach(item => { item.className = ''; }); + (async () => { + await HttpPost(`/saveSources`, RuleSources).then(json => { + if (json.isSuccess) { + let okData = json.data; + if (Array.isArray(okData)) { + let failMsg = ``; + if (RuleSources.length > okData.length) { + RuleSources.forEach(item => { + if (okData.find(x => x.bookSourceUrl == item.bookSourceUrl)) { } + else { $(`#RuleList #${item.bookSourceUrl}+*`).className += 'isError'; } + }); + failMsg = '\n推送失败的书源将用红色字体标注!'; + } + alert(`批量推送书源到「阅读APP」\n共计: ${RuleSources.length} 条\n成功: ${okData.length} 条\n失败: ${RuleSources.length - okData.length} 条${failMsg}`); + } + else { + alert(`批量推送书源到「阅读APP」成功!\n共计: ${RuleSources.length} 条`); + } + } + else { + alert(`批量推送书源失败!\nErrorMsg: ${json.errorMsg}`); + } + }).catch(err => { alert(`批量推送书源失败,无法连接到「阅读APP」!\n${err}`); }); + thisNode.setAttribute('class', ''); + })(); + return; + case 'pull': + showTab('书源列表'); + (async () => { + await HttpGet(`/getSources`).then(json => { + if (json.isSuccess) { + $('#RuleList').innerHTML = '' + localStorage.setItem('RuleSources', JSON.stringify(RuleSources = json.data)); + RuleSources.forEach(item => { + $('#RuleList').innerHTML += newRule(item); + }); + alert(`成功拉取 ${RuleSources.length} 条书源`); + } + else { + alert(`批量拉取书源失败!\nErrorMsg: ${json.errorMsg}`); + } + }).catch(err => { alert(`批量拉取书源失败,无法连接到「阅读APP」!\n${err}`); }); + thisNode.setAttribute('class', ''); + })(); + return; + case 'editor': + if ($('#RuleJsonString').value == '') break; + try { + json2rule(JSON.parse($('#RuleJsonString').value)); + todo(); + } catch (error) { + console.log(error); + alert(error); + } + break; + case 'conver': + showTab('编辑书源'); + $('#RuleJsonString').value = JSON.stringify(rule2json(), null, 4); + break; + case 'initial': + $$('.rules textarea').forEach(item => { item.value = '' }); + todo(); + break; + case 'undo': + undo() + break; + case 'redo': + redo() + break; + case 'debug': + showTab('调试书源'); + let wsOrigin = (hashParam('domain') || location.origin).replace(/^.*?:/, 'ws:').replace(/\d+$/, (port) => (parseInt(port) + 1)); + let DebugInfos = $('#DebugConsole'); + function DebugPrint(msg) { DebugInfos.value += `\n${msg}`; DebugInfos.scrollTop = DebugInfos.scrollHeight; } + let saveRule = [rule2json()]; + HttpPost(`/saveSources`, saveRule).then(sResult => { + if (sResult.isSuccess) { + let sKey = DebugKey.value ? DebugKey.value : '我的'; + $('#DebugConsole').value = `书源《${saveRule[0].bookSourceName}》保存成功!使用搜索关键字“${sKey}”开始调试...`; + let ws = new WebSocket(`${wsOrigin}/sourceDebug`); + ws.onopen = () => { + ws.send(`{"tag":"${saveRule[0].bookSourceUrl}", "key":"${sKey}"}`); + }; + ws.onmessage = (msg) => { + DebugPrint(msg.data == 'finish' ? `\n[${Date().split(' ')[4]}] 调试任务已完成!` : msg.data); + if (msg.data == 'finish') setRule(saveRule[0]); + }; + ws.onerror = (err) => { + throw `${err.data}`; + } + ws.onclose = () => { + thisNode.setAttribute('class', ''); + DebugPrint(`[${Date().split(' ')[4]}] 调试服务已关闭!`); + } + } else throw `${sResult.errorMsg}`; + }).catch(err => { + DebugPrint(`调试过程意外中止,以下是详细错误信息:\n${err}`); + thisNode.setAttribute('class', ''); + }); + return; + case 'accept': + (async () => { + let saveRule = [rule2json()]; + await HttpPost(`/saveSources`, saveRule).then(json => { + alert(json.isSuccess ? `书源《${saveRule[0].bookSourceName}》已成功保存到「阅读APP」` : `书源《${saveRule[0].bookSourceName}》保存失败!\nErrorMsg: ${json.errorMsg}`); + setRule(saveRule[0]); + }).catch(err => { alert(`保存书源失败,无法连接到「阅读APP」!\n${err}`); }); + thisNode.setAttribute('class', ''); + })(); + return; + default: + } + setTimeout(() => { thisNode.setAttribute('class', ''); }, 500); +}); +$('#DebugKey').addEventListener('keydown', e => { + if (e.keyCode == 13) { + let clickEvent = document.createEvent('MouseEvents'); + clickEvent.initEvent("click", true, false); + $('#debug').dispatchEvent(clickEvent); + } +}); + +// 列表规则更改事件 +$('#RuleList').addEventListener('click', e => { + let editRule = null; + if (e.target && e.target.getAttribute('name') == 'rule') { + editRule = rule2json(); + json2rule(RuleSources.find(x => x.bookSourceUrl == e.target.id)); + } else return; + if (editRule.bookSourceUrl == '') return; + if (editRule.bookSourceName == '') editRule.bookSourceName = editRule.bookSourceUrl.replace(/.*?\/\/|\/.*/g, ''); + setRule(editRule); + localStorage.setItem('RuleSources', JSON.stringify(RuleSources)); +}); +// 处理列表按钮事件 +$('.tab3>.titlebar').addEventListener('click', e => { + let thisNode = e.target; + if (thisNode.nodeName != 'BUTTON') return; + switch (thisNode.id) { + case 'Import': + let fileImport = document.createElement('input'); + fileImport.type = 'file'; + fileImport.accept = '.json'; + fileImport.addEventListener('change', () => { + let file = fileImport.files[0]; + let reader = new FileReader(); + reader.onloadend = function (evt) { + if (evt.target.readyState == FileReader.DONE) { + let fileText = evt.target.result; + try { + let fileJson = JSON.parse(fileText); + let newSources = []; + newSources.push(...fileJson); + if (window.confirm(`如何处理导入的书源?\n"确定": 覆盖当前列表(不会删除APP源)\n"取消": 插入列表尾部(自动忽略重复源)`)) { + localStorage.setItem('RuleSources', JSON.stringify(RuleSources = newSources)); + $('#RuleList').innerHTML = '' + RuleSources.forEach(item => { + $('#RuleList').innerHTML += newRule(item); + }); + } + else { + newSources = newSources.filter(item => !JSON.stringify(RuleSources).includes(item.bookSourceUrl)); + RuleSources.push(...newSources); + localStorage.setItem('RuleSources', JSON.stringify(RuleSources)); + newSources.forEach(item => { + $('#RuleList').innerHTML += newRule(item); + }); + } + alert(`成功导入 ${newSources.length} 条书源`); + } + catch (err) { + alert(`导入书源文件失败!\n${err}`); + } + } + }; + reader.readAsText(file); + }, false); + fileImport.click(); + break; + case 'Export': + let fileExport = document.createElement('a'); + fileExport.download = `Rules${Date().replace(/.*?\s(\d+)\s(\d+)\s(\d+:\d+:\d+).*/, '$2$1$3').replace(/:/g, '')}.json`; + let myBlob = new Blob([JSON.stringify(RuleSources, null, 4)], { type: "application/json" }); + fileExport.href = window.URL.createObjectURL(myBlob); + fileExport.click(); + break; + case 'Delete': + let selectRule = $('#RuleList input:checked'); + if (!selectRule) { + alert(`没有书源被选中!`); + return; + } + if (confirm(`确定要删除选定书源吗?\n(同时删除APP内书源)`)) { + let selectRuleUrl = selectRule.id; + let deleteSources = RuleSources.filter(item => item.bookSourceUrl == selectRuleUrl); // 提取待删除的书源 + let laveSources = RuleSources.filter(item => !(item.bookSourceUrl == selectRuleUrl)); // 提取待留下的书源 + HttpPost(`/deleteSources`, deleteSources).then(json => { + if (json.isSuccess) { + let selectNode = document.getElementById(selectRuleUrl).parentNode; + selectNode.parentNode.removeChild(selectNode); + localStorage.setItem('RuleSources', JSON.stringify(RuleSources = laveSources)); + if ($('#bookSourceUrl').value == selectRuleUrl) { + $$('.rules textarea').forEach(item => { item.value = '' }); + todo(); + } + console.log(deleteSources); + console.log(`以上书源已删除!`) + } + }).catch(err => { alert(`删除书源失败,无法连接到「阅读APP」!\n${err}`); }); + } + break; + case 'ClrAll': + if (confirm(`确定要清空当前书源列表吗?\n(不会删除APP内书源)`)) { + localStorage.setItem('RuleSources', JSON.stringify(RuleSources = [])); + $('#RuleList').innerHTML = '' + } + break; + default: + } +}); \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index 6ef0e877d..868748389 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -1,7 +1,21 @@ package io.legado.app import android.app.Application +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatDelegate +import io.legado.app.constant.AppConst.channelIdDownload +import io.legado.app.constant.AppConst.channelIdReadAloud +import io.legado.app.constant.AppConst.channelIdWeb import io.legado.app.data.AppDatabase +import io.legado.app.lib.theme.ThemeStore +import io.legado.app.utils.getPrefBoolean +import io.legado.app.utils.getPrefInt +import java.util.* class App : Application() { @@ -15,9 +29,94 @@ class App : Application() { private set } + private var versionCode = 0 + override fun onCreate() { super.onCreate() INSTANCE = this db = AppDatabase.createDatabase(INSTANCE) + versionCode = try { + packageManager.getPackageInfo(packageName, 0).versionCode + } catch (e: PackageManager.NameNotFoundException) { + 0 + } + initNightTheme() + if (!ThemeStore.isConfigured(this, versionCode)) { + upThemeStore() + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createChannelId() + } + } + + fun initNightTheme() { + if (getPrefBoolean("isNightTheme", false)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } + } + + /** + * 更新主题 + */ + fun upThemeStore() { + if (getPrefBoolean("isNightTheme", false)) { + ThemeStore.editTheme(this) + .primaryColor(getPrefInt("colorPrimaryNight", resources.getColor(R.color.md_grey_800))) + .accentColor(getPrefInt("colorAccentNight", resources.getColor(R.color.md_pink_800))) + .backgroundColor(getPrefInt("colorBackgroundNight", resources.getColor(R.color.md_grey_800))) + .apply() + } else { + ThemeStore.editTheme(this) + .primaryColor(getPrefInt("colorPrimary", resources.getColor(R.color.md_grey_100))) + .accentColor(getPrefInt("colorAccent", resources.getColor(R.color.md_pink_600))) + .backgroundColor(getPrefInt("colorBackground", resources.getColor(R.color.md_grey_100))) + .apply() + } + } + + /** + * 创建通知ID + */ + + @RequiresApi(Build.VERSION_CODES.O) + private fun createChannelId() { + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + //用唯一的ID创建渠道对象 + val downloadChannel = NotificationChannel( + channelIdDownload, + getString(R.string.download_offline), + NotificationManager.IMPORTANCE_LOW + ) + //初始化channel + downloadChannel.enableLights(false) + downloadChannel.enableVibration(false) + downloadChannel.setSound(null, null) + + //用唯一的ID创建渠道对象 + val readAloudChannel = NotificationChannel( + channelIdReadAloud, + getString(R.string.read_aloud), + NotificationManager.IMPORTANCE_LOW + ) + //初始化channel + readAloudChannel.enableLights(false) + readAloudChannel.enableVibration(false) + readAloudChannel.setSound(null, null) + + //用唯一的ID创建渠道对象 + val webChannel = NotificationChannel( + channelIdWeb, + getString(R.string.web_service), + NotificationManager.IMPORTANCE_LOW + ) + //初始化channel + webChannel.enableLights(false) + webChannel.enableVibration(false) + webChannel.setSound(null, null) + + //向notification manager 提交channel + notificationManager.createNotificationChannels(Arrays.asList(downloadChannel, readAloudChannel, webChannel)) } } diff --git a/app/src/main/java/io/legado/app/base/BaseActivity.kt b/app/src/main/java/io/legado/app/base/BaseActivity.kt index 8e9072433..2ee755f42 100644 --- a/app/src/main/java/io/legado/app/base/BaseActivity.kt +++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt @@ -1,25 +1,28 @@ package io.legado.app.base +import android.annotation.SuppressLint +import android.graphics.PorterDuff import android.os.Bundle +import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding import androidx.lifecycle.ViewModel -import com.google.android.material.bottomnavigation.BottomNavigationView +import io.legado.app.R +import io.legado.app.lib.theme.ColorUtil +import io.legado.app.lib.theme.MaterialValueHelper +import io.legado.app.lib.theme.ThemeStore +import java.util.* -abstract class BaseActivity : AppCompatActivity() { - - protected lateinit var dataBinding: BD - private set +abstract class BaseActivity : AppCompatActivity() { protected abstract val viewModel: VM protected abstract val layoutID: Int override fun onCreate(savedInstanceState: Bundle?) { + initTheme() super.onCreate(savedInstanceState) - dataBinding = DataBindingUtil.setContentView(this, layoutID) + setContentView(layoutID) onViewModelCreated(viewModel, savedInstanceState) } @@ -27,6 +30,52 @@ abstract class BaseActivity : AppCompatAc } + /** + * 设置MENU图标颜色 + */ + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val primaryTextColor = MaterialValueHelper + .getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.primaryColor(this))) + for (i in 0 until menu.size()) { + val drawable = menu.getItem(i).icon + if (drawable != null) { + drawable.mutate() + drawable.setColorFilter(primaryTextColor, PorterDuff.Mode.SRC_ATOP) + } + } + return super.onCreateOptionsMenu(menu) + } + + @SuppressLint("PrivateApi") + override fun onMenuOpened(featureId: Int, menu: Menu?): Boolean { + if (menu != null) { + //展开菜单显示图标 + if (menu.javaClass.simpleName.equals("MenuBuilder", ignoreCase = true)) { + try { + var method = menu.javaClass.getDeclaredMethod("setOptionalIconsVisible", java.lang.Boolean.TYPE) + method.isAccessible = true + method.invoke(menu, true) + method = menu.javaClass.getDeclaredMethod("getNonActionItems") + val menuItems = method.invoke(menu) as ArrayList + if (menuItems.isNotEmpty()) { + for (menuItem in menuItems) { + val drawable = menuItem.icon + if (drawable != null) { + drawable.mutate() + drawable.setColorFilter( + resources.getColor(R.color.tv_text_default), + PorterDuff.Mode.SRC_ATOP + ) + } + } + } + } catch (ignored: Exception) { + } + } + } + return super.onMenuOpened(featureId, menu) + } + override fun onOptionsItemSelected(item: MenuItem?): Boolean { item?.let { if (it.itemId == android.R.id.home) { @@ -40,4 +89,12 @@ abstract class BaseActivity : AppCompatAc open fun onCompatOptionsItemSelected(item: MenuItem): Boolean { return true } + + protected fun initTheme() { + if (ColorUtil.isColorLight(ThemeStore.primaryColor(this))) { + setTheme(R.style.CAppTheme) + } else { + setTheme(R.style.CAppThemeBarDark) + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.java b/app/src/main/java/io/legado/app/lib/theme/ATH.java deleted file mode 100644 index d7ee194d1..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/ATH.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.legado.app.lib.theme; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Context; -import android.content.res.ColorStateList; -import android.os.Build; -import android.view.View; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public final class ATH { - - @SuppressLint("CommitPrefEdits") - public static boolean didThemeValuesChange(@NonNull Context context, long since) { - return ThemeStore.isConfigured(context) && ThemeStore.prefs(context).getLong(ThemeStore.VALUES_CHANGED, -1) > since; - } - - public static void setStatusbarColorAuto(Activity activity) { - setStatusbarColor(activity, ThemeStore.statusBarColor(activity)); - } - - public static void setStatusbarColor(Activity activity, int color) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - activity.getWindow().setStatusBarColor(color); - setLightStatusbarAuto(activity, color); - } - } - - public static void setLightStatusbarAuto(Activity activity, int bgColor) { - setLightStatusbar(activity, ColorUtil.isColorLight(bgColor)); - } - - public static void setLightStatusbar(Activity activity, boolean enabled) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - final View decorView = activity.getWindow().getDecorView(); - final int systemUiVisibility = decorView.getSystemUiVisibility(); - if (enabled) { - decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - } else { - decorView.setSystemUiVisibility(systemUiVisibility & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - } - } - } - - public static void setLightNavigationbar(Activity activity, boolean enabled) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - final View decorView = activity.getWindow().getDecorView(); - int systemUiVisibility = decorView.getSystemUiVisibility(); - if (enabled) { - systemUiVisibility |= SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - } else { - systemUiVisibility &= ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - } - decorView.setSystemUiVisibility(systemUiVisibility); - } - } - - public static void setLightNavigationbarAuto(Activity activity, int bgColor) { - setLightNavigationbar(activity, ColorUtil.isColorLight(bgColor)); - } - - public static void setNavigationbarColorAuto(Activity activity) { - setNavigationbarColor(activity, ThemeStore.navigationBarColor(activity)); - } - - public static void setNavigationbarColor(Activity activity, int color) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - activity.getWindow().setNavigationBarColor(color); - setLightNavigationbarAuto(activity, color); - } - } - - public static void setTaskDescriptionColorAuto(@NonNull Activity activity) { - setTaskDescriptionColor(activity, ThemeStore.primaryColor(activity)); - } - - public static void setTaskDescriptionColor(@NonNull Activity activity, @ColorInt int color) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Task description requires fully opaque color - color = ColorUtil.stripAlpha(color); - // Sets color of entry in the system recents page - activity.setTaskDescription(new ActivityManager.TaskDescription((String) activity.getTitle(), null, color)); - } - } - - public static void setTint(@NonNull View view, @ColorInt int color) { - TintHelper.setTintAuto(view, color, false); - } - - public static void setBackgroundTint(@NonNull View view, @ColorInt int color) { - TintHelper.setTintAuto(view, color, true); - } - - public static void setAlertDialogTint(@NonNull AlertDialog dialog) { - ColorStateList colorStateList = Selector.colorBuild() - .setDefaultColor(ThemeStore.accentColor(dialog.getContext())) - .setPressedColor(ColorUtil.darkenColor(ThemeStore.accentColor(dialog.getContext()))) - .create(); - if (dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEGATIVE) != null) { - dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEGATIVE).setTextColor(colorStateList); - } - if (dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE) != null) { - dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setTextColor(colorStateList); - } - } - - private ATH() { - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt new file mode 100644 index 000000000..e0a55c52b --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt @@ -0,0 +1,116 @@ +package io.legado.app.lib.theme + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.ActivityManager +import android.content.Context +import android.os.Build +import android.view.View +import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR +import androidx.annotation.ColorInt +import androidx.appcompat.app.AlertDialog + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +object ATH { + + @SuppressLint("CommitPrefEdits") + fun didThemeValuesChange(context: Context, since: Long): Boolean { + return ThemeStore.isConfigured(context) && ThemeStore.prefs(context).getLong( + ThemeStore.VALUES_CHANGED, + -1 + ) > since + } + + fun setStatusbarColorAuto(activity: Activity) { + setStatusbarColor(activity, ThemeStore.statusBarColor(activity)) + } + + fun setStatusbarColor(activity: Activity, color: Int) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.window.statusBarColor = color + setLightStatusbarAuto(activity, color) + } + } + + fun setLightStatusbarAuto(activity: Activity, bgColor: Int) { + setLightStatusbar(activity, ColorUtil.isColorLight(bgColor)) + } + + fun setLightStatusbar(activity: Activity, enabled: Boolean) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val decorView = activity.window.decorView + val systemUiVisibility = decorView.systemUiVisibility + if (enabled) { + decorView.systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + } else { + decorView.systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() + } + } + } + + fun setLightNavigationbar(activity: Activity, enabled: Boolean) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val decorView = activity.window.decorView + var systemUiVisibility = decorView.systemUiVisibility + if (enabled) { + systemUiVisibility = systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + } else { + systemUiVisibility = systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv() + } + decorView.systemUiVisibility = systemUiVisibility + } + } + + fun setLightNavigationbarAuto(activity: Activity, bgColor: Int) { + setLightNavigationbar(activity, ColorUtil.isColorLight(bgColor)) + } + + fun setNavigationbarColorAuto(activity: Activity) { + setNavigationbarColor(activity, ThemeStore.navigationBarColor(activity)) + } + + fun setNavigationbarColor(activity: Activity, color: Int) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.window.navigationBarColor = color + setLightNavigationbarAuto(activity, color) + } + } + + fun setTaskDescriptionColorAuto(activity: Activity) { + setTaskDescriptionColor(activity, ThemeStore.primaryColor(activity)) + } + + fun setTaskDescriptionColor(activity: Activity, @ColorInt color: Int) { + val color1: Int + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // Task description requires fully opaque color + color1 = ColorUtil.stripAlpha(color) + // Sets color of entry in the system recents page + activity.setTaskDescription(ActivityManager.TaskDescription(activity.title as String, null, color1)) + } + } + + fun setTint(view: View, @ColorInt color: Int) { + TintHelper.setTintAuto(view, color, false) + } + + fun setBackgroundTint(view: View, @ColorInt color: Int) { + TintHelper.setTintAuto(view, color, true) + } + + fun setAlertDialogTint(dialog: AlertDialog): AlertDialog { + val colorStateList = Selector.colorBuild() + .setDefaultColor(ThemeStore.accentColor(dialog.context)) + .setPressedColor(ColorUtil.darkenColor(ThemeStore.accentColor(dialog.context))) + .create() + if (dialog.getButton(AlertDialog.BUTTON_NEGATIVE) != null) { + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(colorStateList) + } + if (dialog.getButton(AlertDialog.BUTTON_POSITIVE) != null) { + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(colorStateList) + } + return dialog + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ATHUtil.java b/app/src/main/java/io/legado/app/lib/theme/ATHUtil.java deleted file mode 100644 index eb84da3e5..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/ATHUtil.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.legado.app.lib.theme; - -import android.content.Context; -import android.content.res.TypedArray; -import androidx.annotation.AttrRes; - -/** - * @author Aidan Follestad (afollestad) - */ -public final class ATHUtil { - - public static boolean isWindowBackgroundDark(Context context) { - return !ColorUtil.isColorLight(ATHUtil.resolveColor(context, android.R.attr.windowBackground)); - } - - public static int resolveColor(Context context, @AttrRes int attr) { - return resolveColor(context, attr, 0); - } - - public static int resolveColor(Context context, @AttrRes int attr, int fallback) { - TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr}); - try { - return a.getColor(0, fallback); - } finally { - a.recycle(); - } - } - - private ATHUtil() { - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ATHUtil.kt b/app/src/main/java/io/legado/app/lib/theme/ATHUtil.kt new file mode 100644 index 000000000..6cc19d511 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/ATHUtil.kt @@ -0,0 +1,24 @@ +package io.legado.app.lib.theme + +import android.content.Context +import androidx.annotation.AttrRes + +/** + * @author Aidan Follestad (afollestad) + */ +object ATHUtil { + + fun isWindowBackgroundDark(context: Context): Boolean { + return !ColorUtil.isColorLight(ATHUtil.resolveColor(context, android.R.attr.windowBackground)) + } + + @JvmOverloads + fun resolveColor(context: Context, @AttrRes attr: Int, fallback: Int = 0): Int { + val a = context.theme.obtainStyledAttributes(intArrayOf(attr)) + try { + return a.getColor(0, fallback) + } finally { + a.recycle() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ColorUtil.java b/app/src/main/java/io/legado/app/lib/theme/ColorUtil.java deleted file mode 100644 index 4fc255d07..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/ColorUtil.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.legado.app.lib.theme; - -import android.graphics.Color; -import androidx.annotation.ColorInt; -import androidx.annotation.FloatRange; - -@SuppressWarnings({"unused", "WeakerAccess"}) -public class ColorUtil { - - public static String intToString(int intColor) { - return String.format("#%06X", 0xFFFFFF & intColor); - } - - - public static int stripAlpha(@ColorInt int color) { - return 0xff000000 | color; - } - - @ColorInt - public static int shiftColor(@ColorInt int color, @FloatRange(from = 0.0f, to = 2.0f) float by) { - if (by == 1f) return color; - int alpha = Color.alpha(color); - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[2] *= by; // value component - return (alpha << 24) + (0x00ffffff & Color.HSVToColor(hsv)); - } - - @ColorInt - public static int darkenColor(@ColorInt int color) { - return shiftColor(color, 0.9f); - } - - @ColorInt - public static int lightenColor(@ColorInt int color) { - return shiftColor(color, 1.1f); - } - - public static boolean isColorLight(@ColorInt int color) { - final double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255; - return darkness < 0.4; - } - - @ColorInt - public static int invertColor(@ColorInt int color) { - final int r = 255 - Color.red(color); - final int g = 255 - Color.green(color); - final int b = 255 - Color.blue(color); - return Color.argb(Color.alpha(color), r, g, b); - } - - @ColorInt - public static int adjustAlpha(@ColorInt int color, @FloatRange(from = 0.0, to = 1.0) float factor) { - int alpha = Math.round(Color.alpha(color) * factor); - int red = Color.red(color); - int green = Color.green(color); - int blue = Color.blue(color); - return Color.argb(alpha, red, green, blue); - } - - @ColorInt - public static int withAlpha(@ColorInt int baseColor, @FloatRange(from = 0.0, to = 1.0) float alpha) { - int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24; - int rgb = 0x00ffffff & baseColor; - return a + rgb; - } - - /** - * Taken from CollapsingToolbarLayout's CollapsingTextHelper class. - */ - public static int blendColors(int color1, int color2, @FloatRange(from = 0.0, to = 1.0) float ratio) { - final float inverseRatio = 1f - ratio; - float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio); - float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio); - float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio); - float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio); - return Color.argb((int) a, (int) r, (int) g, (int) b); - } - -} diff --git a/app/src/main/java/io/legado/app/lib/theme/ColorUtil.kt b/app/src/main/java/io/legado/app/lib/theme/ColorUtil.kt new file mode 100644 index 000000000..fa9bce4bd --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/ColorUtil.kt @@ -0,0 +1,79 @@ +package io.legado.app.lib.theme + +import android.graphics.Color +import androidx.annotation.ColorInt +import androidx.annotation.FloatRange + +object ColorUtil { + + fun intToString(intColor: Int): String { + return String.format("#%06X", 0xFFFFFF and intColor) + } + + + fun stripAlpha(@ColorInt color: Int): Int { + return -0x1000000 or color + } + + @ColorInt + fun shiftColor(@ColorInt color: Int, @FloatRange(from = 0.0, to = 2.0) by: Float): Int { + if (by == 1f) return color + val alpha = Color.alpha(color) + val hsv = FloatArray(3) + Color.colorToHSV(color, hsv) + hsv[2] *= by // value component + return (alpha shl 24) + (0x00ffffff and Color.HSVToColor(hsv)) + } + + @ColorInt + fun darkenColor(@ColorInt color: Int): Int { + return shiftColor(color, 0.9f) + } + + @ColorInt + fun lightenColor(@ColorInt color: Int): Int { + return shiftColor(color, 1.1f) + } + + fun isColorLight(@ColorInt color: Int): Boolean { + val darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255 + return darkness < 0.4 + } + + @ColorInt + fun invertColor(@ColorInt color: Int): Int { + val r = 255 - Color.red(color) + val g = 255 - Color.green(color) + val b = 255 - Color.blue(color) + return Color.argb(Color.alpha(color), r, g, b) + } + + @ColorInt + fun adjustAlpha(@ColorInt color: Int, @FloatRange(from = 0.0, to = 1.0) factor: Float): Int { + val alpha = Math.round(Color.alpha(color) * factor) + val red = Color.red(color) + val green = Color.green(color) + val blue = Color.blue(color) + return Color.argb(alpha, red, green, blue) + } + + @ColorInt + fun withAlpha(@ColorInt baseColor: Int, @FloatRange(from = 0.0, to = 1.0) alpha: Float): Int { + val a = Math.min(255, Math.max(0, (alpha * 255).toInt())) shl 24 + val rgb = 0x00ffffff and baseColor + return a + rgb + } + + /** + * Taken from CollapsingToolbarLayout's CollapsingTextHelper class. + */ + fun blendColors(color1: Int, color2: Int, @FloatRange(from = 0.0, to = 1.0) ratio: Float): Int { + val inverseRatio = 1f - ratio + val a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio + val r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio + val g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio + val b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio + return Color.argb(a.toInt(), r.toInt(), g.toInt(), b.toInt()) + } + +} diff --git a/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java b/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java deleted file mode 100644 index 2254798f5..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.legado.app.lib.theme; - -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.TransitionDrawable; -import androidx.annotation.ColorInt; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public final class DrawableUtil { - - public static TransitionDrawable createTransitionDrawable(@ColorInt int startColor, @ColorInt int endColor) { - return createTransitionDrawable(new ColorDrawable(startColor), new ColorDrawable(endColor)); - } - - public static TransitionDrawable createTransitionDrawable(Drawable start, Drawable end) { - final Drawable[] drawables = new Drawable[2]; - - drawables[0] = start; - drawables[1] = end; - - return new TransitionDrawable(drawables); - } - - private DrawableUtil() { - } -} diff --git a/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.kt b/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.kt new file mode 100644 index 000000000..454d9ba71 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.kt @@ -0,0 +1,25 @@ +package io.legado.app.lib.theme + +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.TransitionDrawable +import androidx.annotation.ColorInt + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +object DrawableUtil { + + fun createTransitionDrawable(@ColorInt startColor: Int, @ColorInt endColor: Int): TransitionDrawable { + return createTransitionDrawable(ColorDrawable(startColor), ColorDrawable(endColor)) + } + + fun createTransitionDrawable(start: Drawable, end: Drawable): TransitionDrawable { + val drawables = arrayOfNulls(2) + + drawables[0] = start + drawables[1] = end + + return TransitionDrawable(drawables) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java deleted file mode 100644 index 51f9031b4..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.legado.app.lib.theme; - -import android.annotation.SuppressLint; -import android.content.Context; -import androidx.annotation.ColorInt; -import androidx.core.content.ContextCompat; -import io.legado.app.R; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public final class MaterialValueHelper { - - @SuppressLint("PrivateResource") - @ColorInt - public static int getPrimaryTextColor(final Context context, boolean dark) { - if (dark) { - return ContextCompat.getColor(context, R.color.primary_text_default_material_light); - } - return ContextCompat.getColor(context, R.color.primary_text_default_material_dark); - } - - @SuppressLint("PrivateResource") - @ColorInt - public static int getSecondaryTextColor(final Context context, boolean dark) { - if (dark) { - return ContextCompat.getColor(context, R.color.secondary_text_default_material_light); - } - return ContextCompat.getColor(context, R.color.secondary_text_default_material_dark); - } - - @SuppressLint("PrivateResource") - @ColorInt - public static int getPrimaryDisabledTextColor(final Context context, boolean dark) { - if (dark) { - return ContextCompat.getColor(context, R.color.primary_text_disabled_material_light); - } - return ContextCompat.getColor(context, R.color.primary_text_disabled_material_dark); - } - - @SuppressLint("PrivateResource") - @ColorInt - public static int getSecondaryDisabledTextColor(final Context context, boolean dark) { - if (dark) { - return ContextCompat.getColor(context, R.color.secondary_text_disabled_material_light); - } - return ContextCompat.getColor(context, R.color.secondary_text_disabled_material_dark); - } - - private MaterialValueHelper() { - } -} diff --git a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt new file mode 100644 index 000000000..410de43dc --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt @@ -0,0 +1,45 @@ +package io.legado.app.lib.theme + +import android.annotation.SuppressLint +import android.content.Context +import androidx.annotation.ColorInt +import androidx.core.content.ContextCompat +import io.legado.app.R + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +object MaterialValueHelper { + + @SuppressLint("PrivateResource") + @ColorInt + fun getPrimaryTextColor(context: Context, dark: Boolean): Int { + return if (dark) { + ContextCompat.getColor(context, R.color.primary_text_default_material_light) + } else ContextCompat.getColor(context, R.color.primary_text_default_material_dark) + } + + @SuppressLint("PrivateResource") + @ColorInt + fun getSecondaryTextColor(context: Context, dark: Boolean): Int { + return if (dark) { + ContextCompat.getColor(context, R.color.secondary_text_default_material_light) + } else ContextCompat.getColor(context, R.color.secondary_text_default_material_dark) + } + + @SuppressLint("PrivateResource") + @ColorInt + fun getPrimaryDisabledTextColor(context: Context, dark: Boolean): Int { + return if (dark) { + ContextCompat.getColor(context, R.color.primary_text_disabled_material_light) + } else ContextCompat.getColor(context, R.color.primary_text_disabled_material_dark) + } + + @SuppressLint("PrivateResource") + @ColorInt + fun getSecondaryDisabledTextColor(context: Context, dark: Boolean): Int { + return if (dark) { + ContextCompat.getColor(context, R.color.secondary_text_disabled_material_light) + } else ContextCompat.getColor(context, R.color.secondary_text_disabled_material_dark) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java b/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java deleted file mode 100644 index 0707cf132..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.legado.app.lib.theme; - -import android.content.res.ColorStateList; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import com.google.android.material.internal.NavigationMenuView; -import com.google.android.material.navigation.NavigationView; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public final class NavigationViewUtil { - - public static void setItemIconColors(@NonNull NavigationView navigationView, @ColorInt int normalColor, @ColorInt int selectedColor) { - final ColorStateList iconSl = new ColorStateList( - new int[][]{ - new int[]{-android.R.attr.state_checked}, - new int[]{android.R.attr.state_checked} - }, - new int[]{ - normalColor, - selectedColor - }); - navigationView.setItemIconTintList(iconSl); - } - - public static void setItemTextColors(@NonNull NavigationView navigationView, @ColorInt int normalColor, @ColorInt int selectedColor) { - final ColorStateList textSl = new ColorStateList( - new int[][]{ - new int[]{-android.R.attr.state_checked}, - new int[]{android.R.attr.state_checked} - }, - new int[]{ - normalColor, - selectedColor - }); - navigationView.setItemTextColor(textSl); - } - - /** - * 去掉navigationView的滚动条 - * @param navigationView NavigationView - */ - public static void disableScrollbar(NavigationView navigationView) { - if (navigationView != null) { - NavigationMenuView navigationMenuView = (NavigationMenuView) navigationView.getChildAt(0); - if (navigationMenuView != null) { - navigationMenuView.setVerticalScrollBarEnabled(false); - } - } - } - - private NavigationViewUtil() { - } -} diff --git a/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.kt b/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.kt new file mode 100644 index 000000000..43d17ff4b --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.kt @@ -0,0 +1,38 @@ +package io.legado.app.lib.theme + +import android.content.res.ColorStateList +import androidx.annotation.ColorInt +import com.google.android.material.internal.NavigationMenuView +import com.google.android.material.navigation.NavigationView + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +object NavigationViewUtil { + + fun setItemIconColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) { + val iconSl = ColorStateList( + arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)), + intArrayOf(normalColor, selectedColor) + ) + navigationView.itemIconTintList = iconSl + } + + fun setItemTextColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) { + val textSl = ColorStateList( + arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)), + intArrayOf(normalColor, selectedColor) + ) + navigationView.itemTextColor = textSl + } + + /** + * 去掉navigationView的滚动条 + * @param navigationView NavigationView + */ + fun disableScrollbar(navigationView: NavigationView?) { + navigationView ?: return + val navigationMenuView = navigationView.getChildAt(0) as? NavigationMenuView + navigationMenuView?.isVerticalScrollBarEnabled = false + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/Selector.java b/app/src/main/java/io/legado/app/lib/theme/Selector.java deleted file mode 100644 index df4e44b59..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/Selector.java +++ /dev/null @@ -1,430 +0,0 @@ -package io.legado.app.lib.theme; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.StateListDrawable; -import androidx.annotation.ColorInt; -import androidx.annotation.Dimension; -import androidx.annotation.DrawableRes; -import androidx.annotation.IntDef; -import androidx.core.content.ContextCompat; - -public class Selector { - public static ShapeSelector shapeBuild() { - return new ShapeSelector(); - } - - public static ColorSelector colorBuild() { - return new ColorSelector(); - } - - public static DrawableSelector drawableBuild() { - return new DrawableSelector(); - } - - /** - * 形状ShapeSelector - * - * @author hjy - * created at 2017/12/11 22:26 - */ - public static final class ShapeSelector { - @IntDef({GradientDrawable.RECTANGLE, GradientDrawable.OVAL, - GradientDrawable.LINE, GradientDrawable.RING}) - private @interface Shape { - } - - private int mShape; //the shape of background - private int mDefaultBgColor; //default background color - private int mDisabledBgColor; //state_enabled = false - private int mPressedBgColor; //state_pressed = true - private int mSelectedBgColor; //state_selected = true - private int mFocusedBgColor; //state_focused = true - private int mCheckedBgColor; //state_checked = true - private int mStrokeWidth; //stroke width in pixel - private int mDefaultStrokeColor; //default stroke color - private int mDisabledStrokeColor; //state_enabled = false - private int mPressedStrokeColor; //state_pressed = true - private int mSelectedStrokeColor; //state_selected = true - private int mFocusedStrokeColor; //state_focused = true - private int mCheckedStrokeColor; //state_checked = true - private int mCornerRadius; //corner radius - - private boolean hasSetDisabledBgColor = false; - private boolean hasSetPressedBgColor = false; - private boolean hasSetSelectedBgColor = false; - private boolean hasSetFocusedBgColor = false; - private boolean hasSetCheckedBgColor = false; - - private boolean hasSetDisabledStrokeColor = false; - private boolean hasSetPressedStrokeColor = false; - private boolean hasSetSelectedStrokeColor = false; - private boolean hasSetFocusedStrokeColor = false; - private boolean hasSetCheckedStrokeColor = false; - - public ShapeSelector() { - //initialize default values - mShape = GradientDrawable.RECTANGLE; - mDefaultBgColor = Color.TRANSPARENT; - mDisabledBgColor = Color.TRANSPARENT; - mPressedBgColor = Color.TRANSPARENT; - mSelectedBgColor = Color.TRANSPARENT; - mFocusedBgColor = Color.TRANSPARENT; - mStrokeWidth = 0; - mDefaultStrokeColor = Color.TRANSPARENT; - mDisabledStrokeColor = Color.TRANSPARENT; - mPressedStrokeColor = Color.TRANSPARENT; - mSelectedStrokeColor = Color.TRANSPARENT; - mFocusedStrokeColor = Color.TRANSPARENT; - mCornerRadius = 0; - } - - public ShapeSelector setShape(@Shape int shape) { - mShape = shape; - return this; - } - - public ShapeSelector setDefaultBgColor(@ColorInt int color) { - mDefaultBgColor = color; - if (!hasSetDisabledBgColor) - mDisabledBgColor = color; - if (!hasSetPressedBgColor) - mPressedBgColor = color; - if (!hasSetSelectedBgColor) - mSelectedBgColor = color; - if (!hasSetFocusedBgColor) - mFocusedBgColor = color; - return this; - } - - public ShapeSelector setDisabledBgColor(@ColorInt int color) { - mDisabledBgColor = color; - hasSetDisabledBgColor = true; - return this; - } - - public ShapeSelector setPressedBgColor(@ColorInt int color) { - mPressedBgColor = color; - hasSetPressedBgColor = true; - return this; - } - - public ShapeSelector setSelectedBgColor(@ColorInt int color) { - mSelectedBgColor = color; - hasSetSelectedBgColor = true; - return this; - } - - public ShapeSelector setFocusedBgColor(@ColorInt int color) { - mFocusedBgColor = color; - hasSetPressedBgColor = true; - return this; - } - - public ShapeSelector setCheckedBgColor(@ColorInt int color) { - mCheckedBgColor = color; - hasSetCheckedBgColor = true; - return this; - } - - public ShapeSelector setStrokeWidth(@Dimension int width) { - mStrokeWidth = width; - return this; - } - - public ShapeSelector setDefaultStrokeColor(@ColorInt int color) { - mDefaultStrokeColor = color; - if (!hasSetDisabledStrokeColor) - mDisabledStrokeColor = color; - if (!hasSetPressedStrokeColor) - mPressedStrokeColor = color; - if (!hasSetSelectedStrokeColor) - mSelectedStrokeColor = color; - if (!hasSetFocusedStrokeColor) - mFocusedStrokeColor = color; - return this; - } - - public ShapeSelector setDisabledStrokeColor(@ColorInt int color) { - mDisabledStrokeColor = color; - hasSetDisabledStrokeColor = true; - return this; - } - - public ShapeSelector setPressedStrokeColor(@ColorInt int color) { - mPressedStrokeColor = color; - hasSetPressedStrokeColor = true; - return this; - } - - public ShapeSelector setSelectedStrokeColor(@ColorInt int color) { - mSelectedStrokeColor = color; - hasSetSelectedStrokeColor = true; - return this; - } - - public ShapeSelector setCheckedStrokeColor(@ColorInt int color) { - mCheckedStrokeColor = color; - hasSetCheckedStrokeColor = true; - return this; - } - - public ShapeSelector setFocusedStrokeColor(@ColorInt int color) { - mFocusedStrokeColor = color; - hasSetFocusedStrokeColor = true; - return this; - } - - public ShapeSelector setCornerRadius(@Dimension int radius) { - mCornerRadius = radius; - return this; - } - - public StateListDrawable create() { - StateListDrawable selector = new StateListDrawable(); - - //enabled = false - if (hasSetDisabledBgColor || hasSetDisabledStrokeColor) { - GradientDrawable disabledShape = getItemShape(mShape, mCornerRadius, - mDisabledBgColor, mStrokeWidth, mDisabledStrokeColor); - selector.addState(new int[]{-android.R.attr.state_enabled}, disabledShape); - } - - //pressed = true - if (hasSetPressedBgColor || hasSetPressedStrokeColor) { - GradientDrawable pressedShape = getItemShape(mShape, mCornerRadius, - mPressedBgColor, mStrokeWidth, mPressedStrokeColor); - selector.addState(new int[]{android.R.attr.state_pressed}, pressedShape); - } - - //selected = true - if (hasSetSelectedBgColor || hasSetSelectedStrokeColor) { - GradientDrawable selectedShape = getItemShape(mShape, mCornerRadius, - mSelectedBgColor, mStrokeWidth, mSelectedStrokeColor); - selector.addState(new int[]{android.R.attr.state_selected}, selectedShape); - } - - //focused = true - if (hasSetFocusedBgColor || hasSetFocusedStrokeColor) { - GradientDrawable focusedShape = getItemShape(mShape, mCornerRadius, - mFocusedBgColor, mStrokeWidth, mFocusedStrokeColor); - selector.addState(new int[]{android.R.attr.state_focused}, focusedShape); - } - - //checked = true - if (hasSetCheckedBgColor || hasSetCheckedStrokeColor) { - GradientDrawable checkedShape = getItemShape(mShape, mCornerRadius, - mCheckedBgColor, mStrokeWidth, mCheckedStrokeColor); - selector.addState(new int[]{android.R.attr.state_checked}, checkedShape); - } - - //default - GradientDrawable defaultShape = getItemShape(mShape, mCornerRadius, - mDefaultBgColor, mStrokeWidth, mDefaultStrokeColor); - selector.addState(new int[]{}, defaultShape); - - return selector; - } - - private GradientDrawable getItemShape(int shape, int cornerRadius, - int solidColor, int strokeWidth, int strokeColor) { - GradientDrawable drawable = new GradientDrawable(); - drawable.setShape(shape); - drawable.setStroke(strokeWidth, strokeColor); - drawable.setCornerRadius(cornerRadius); - drawable.setColor(solidColor); - return drawable; - } - } - - /** - * 资源DrawableSelector - * - * @author hjy - * created at 2017/12/11 22:34 - */ - public static final class DrawableSelector { - - private Drawable mDefaultDrawable; - private Drawable mDisabledDrawable; - private Drawable mPressedDrawable; - private Drawable mSelectedDrawable; - private Drawable mFocusedDrawable; - - private boolean hasSetDisabledDrawable = false; - private boolean hasSetPressedDrawable = false; - private boolean hasSetSelectedDrawable = false; - private boolean hasSetFocusedDrawable = false; - - private DrawableSelector() { - mDefaultDrawable = new ColorDrawable(Color.TRANSPARENT); - } - - public DrawableSelector setDefaultDrawable(Drawable drawable) { - mDefaultDrawable = drawable; - if (!hasSetDisabledDrawable) - mDisabledDrawable = drawable; - if (!hasSetPressedDrawable) - mPressedDrawable = drawable; - if (!hasSetSelectedDrawable) - mSelectedDrawable = drawable; - if (!hasSetFocusedDrawable) - mFocusedDrawable = drawable; - return this; - } - - public DrawableSelector setDisabledDrawable(Drawable drawable) { - mDisabledDrawable = drawable; - hasSetDisabledDrawable = true; - return this; - } - - public DrawableSelector setPressedDrawable(Drawable drawable) { - mPressedDrawable = drawable; - hasSetPressedDrawable = true; - return this; - } - - public DrawableSelector setSelectedDrawable(Drawable drawable) { - mSelectedDrawable = drawable; - hasSetSelectedDrawable = true; - return this; - } - - public DrawableSelector setFocusedDrawable(Drawable drawable) { - mFocusedDrawable = drawable; - hasSetFocusedDrawable = true; - return this; - } - - public StateListDrawable create() { - StateListDrawable selector = new StateListDrawable(); - if (hasSetDisabledDrawable) - selector.addState(new int[]{-android.R.attr.state_enabled}, mDisabledDrawable); - if (hasSetPressedDrawable) - selector.addState(new int[]{android.R.attr.state_pressed}, mPressedDrawable); - if (hasSetSelectedDrawable) - selector.addState(new int[]{android.R.attr.state_selected}, mSelectedDrawable); - if (hasSetFocusedDrawable) - selector.addState(new int[]{android.R.attr.state_focused}, mFocusedDrawable); - selector.addState(new int[]{}, mDefaultDrawable); - return selector; - } - - public DrawableSelector setDefaultDrawable(Context context, @DrawableRes int drawableRes) { - return setDefaultDrawable(ContextCompat.getDrawable(context, drawableRes)); - } - - public DrawableSelector setDisabledDrawable(Context context, @DrawableRes int drawableRes) { - return setDisabledDrawable(ContextCompat.getDrawable(context, drawableRes)); - } - - public DrawableSelector setPressedDrawable(Context context, @DrawableRes int drawableRes) { - return setPressedDrawable(ContextCompat.getDrawable(context, drawableRes)); - } - - public DrawableSelector setSelectedDrawable(Context context, @DrawableRes int drawableRes) { - return setSelectedDrawable(ContextCompat.getDrawable(context, drawableRes)); - } - - public DrawableSelector setFocusedDrawable(Context context, @DrawableRes int drawableRes) { - return setFocusedDrawable(ContextCompat.getDrawable(context, drawableRes)); - } - } - - /** - * 颜色ColorSelector - * - * @author hjy - * created at 2017/12/11 22:26 - */ - public static final class ColorSelector { - - private int mDefaultColor; - private int mDisabledColor; - private int mPressedColor; - private int mSelectedColor; - private int mFocusedColor; - private int mCheckedColor; - - private boolean hasSetDisabledColor = false; - private boolean hasSetPressedColor = false; - private boolean hasSetSelectedColor = false; - private boolean hasSetFocusedColor = false; - private boolean hasSetCheckedColor = false; - - private ColorSelector() { - mDefaultColor = Color.BLACK; - mDisabledColor = Color.GRAY; - mPressedColor = Color.BLACK; - mSelectedColor = Color.BLACK; - mFocusedColor = Color.BLACK; - } - - public ColorSelector setDefaultColor(@ColorInt int color) { - mDefaultColor = color; - if (!hasSetDisabledColor) - mDisabledColor = color; - if (!hasSetPressedColor) - mPressedColor = color; - if (!hasSetSelectedColor) - mSelectedColor = color; - if (!hasSetFocusedColor) - mFocusedColor = color; - return this; - } - - public ColorSelector setDisabledColor(@ColorInt int color) { - mDisabledColor = color; - hasSetDisabledColor = true; - return this; - } - - public ColorSelector setPressedColor(@ColorInt int color) { - mPressedColor = color; - hasSetPressedColor = true; - return this; - } - - public ColorSelector setSelectedColor(@ColorInt int color) { - mSelectedColor = color; - hasSetSelectedColor = true; - return this; - } - - public ColorSelector setFocusedColor(@ColorInt int color) { - mFocusedColor = color; - hasSetFocusedColor = true; - return this; - } - - public ColorSelector setCheckedColor(@ColorInt int color) { - mCheckedColor = color; - hasSetCheckedColor = true; - return this; - } - - public ColorStateList create() { - int[] colors = new int[]{ - hasSetDisabledColor ? mDisabledColor : mDefaultColor, - hasSetPressedColor ? mPressedColor : mDefaultColor, - hasSetSelectedColor ? mSelectedColor : mDefaultColor, - hasSetFocusedColor ? mFocusedColor : mDefaultColor, - hasSetCheckedColor ? mCheckedColor : mDefaultColor, - mDefaultColor - }; - int[][] states = new int[6][]; - states[0] = new int[]{-android.R.attr.state_enabled}; - states[1] = new int[]{android.R.attr.state_pressed}; - states[2] = new int[]{android.R.attr.state_selected}; - states[3] = new int[]{android.R.attr.state_focused}; - states[4] = new int[]{android.R.attr.state_checked}; - states[5] = new int[]{}; - return new ColorStateList(states, colors); - } - } -} diff --git a/app/src/main/java/io/legado/app/lib/theme/Selector.kt b/app/src/main/java/io/legado/app/lib/theme/Selector.kt new file mode 100644 index 000000000..89a71198b --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/Selector.kt @@ -0,0 +1,443 @@ +package io.legado.app.lib.theme + +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.StateListDrawable +import androidx.annotation.ColorInt +import androidx.annotation.Dimension +import androidx.annotation.DrawableRes +import androidx.annotation.IntDef +import androidx.core.content.ContextCompat + +object Selector { + fun shapeBuild(): ShapeSelector { + return ShapeSelector() + } + + fun colorBuild(): ColorSelector { + return ColorSelector() + } + + fun drawableBuild(): DrawableSelector { + return DrawableSelector() + } + + /** + * 形状ShapeSelector + * + * @author hjy + * created at 2017/12/11 22:26 + */ + class ShapeSelector { + + private var mShape: Int = 0 //the shape of background + private var mDefaultBgColor: Int = 0 //default background color + private var mDisabledBgColor: Int = 0 //state_enabled = false + private var mPressedBgColor: Int = 0 //state_pressed = true + private var mSelectedBgColor: Int = 0 //state_selected = true + private var mFocusedBgColor: Int = 0 //state_focused = true + private var mCheckedBgColor: Int = 0 //state_checked = true + private var mStrokeWidth: Int = 0 //stroke width in pixel + private var mDefaultStrokeColor: Int = 0 //default stroke color + private var mDisabledStrokeColor: Int = 0 //state_enabled = false + private var mPressedStrokeColor: Int = 0 //state_pressed = true + private var mSelectedStrokeColor: Int = 0 //state_selected = true + private var mFocusedStrokeColor: Int = 0 //state_focused = true + private var mCheckedStrokeColor: Int = 0 //state_checked = true + private var mCornerRadius: Int = 0 //corner radius + + private var hasSetDisabledBgColor = false + private var hasSetPressedBgColor = false + private var hasSetSelectedBgColor = false + private val hasSetFocusedBgColor = false + private var hasSetCheckedBgColor = false + + private var hasSetDisabledStrokeColor = false + private var hasSetPressedStrokeColor = false + private var hasSetSelectedStrokeColor = false + private var hasSetFocusedStrokeColor = false + private var hasSetCheckedStrokeColor = false + + @IntDef(GradientDrawable.RECTANGLE, GradientDrawable.OVAL, GradientDrawable.LINE, GradientDrawable.RING) + private annotation class Shape + + init { + //initialize default values + mShape = GradientDrawable.RECTANGLE + mDefaultBgColor = Color.TRANSPARENT + mDisabledBgColor = Color.TRANSPARENT + mPressedBgColor = Color.TRANSPARENT + mSelectedBgColor = Color.TRANSPARENT + mFocusedBgColor = Color.TRANSPARENT + mStrokeWidth = 0 + mDefaultStrokeColor = Color.TRANSPARENT + mDisabledStrokeColor = Color.TRANSPARENT + mPressedStrokeColor = Color.TRANSPARENT + mSelectedStrokeColor = Color.TRANSPARENT + mFocusedStrokeColor = Color.TRANSPARENT + mCornerRadius = 0 + } + + fun setShape(@Shape shape: Int): ShapeSelector { + mShape = shape + return this + } + + fun setDefaultBgColor(@ColorInt color: Int): ShapeSelector { + mDefaultBgColor = color + if (!hasSetDisabledBgColor) + mDisabledBgColor = color + if (!hasSetPressedBgColor) + mPressedBgColor = color + if (!hasSetSelectedBgColor) + mSelectedBgColor = color + if (!hasSetFocusedBgColor) + mFocusedBgColor = color + return this + } + + fun setDisabledBgColor(@ColorInt color: Int): ShapeSelector { + mDisabledBgColor = color + hasSetDisabledBgColor = true + return this + } + + fun setPressedBgColor(@ColorInt color: Int): ShapeSelector { + mPressedBgColor = color + hasSetPressedBgColor = true + return this + } + + fun setSelectedBgColor(@ColorInt color: Int): ShapeSelector { + mSelectedBgColor = color + hasSetSelectedBgColor = true + return this + } + + fun setFocusedBgColor(@ColorInt color: Int): ShapeSelector { + mFocusedBgColor = color + hasSetPressedBgColor = true + return this + } + + fun setCheckedBgColor(@ColorInt color: Int): ShapeSelector { + mCheckedBgColor = color + hasSetCheckedBgColor = true + return this + } + + fun setStrokeWidth(@Dimension width: Int): ShapeSelector { + mStrokeWidth = width + return this + } + + fun setDefaultStrokeColor(@ColorInt color: Int): ShapeSelector { + mDefaultStrokeColor = color + if (!hasSetDisabledStrokeColor) + mDisabledStrokeColor = color + if (!hasSetPressedStrokeColor) + mPressedStrokeColor = color + if (!hasSetSelectedStrokeColor) + mSelectedStrokeColor = color + if (!hasSetFocusedStrokeColor) + mFocusedStrokeColor = color + return this + } + + fun setDisabledStrokeColor(@ColorInt color: Int): ShapeSelector { + mDisabledStrokeColor = color + hasSetDisabledStrokeColor = true + return this + } + + fun setPressedStrokeColor(@ColorInt color: Int): ShapeSelector { + mPressedStrokeColor = color + hasSetPressedStrokeColor = true + return this + } + + fun setSelectedStrokeColor(@ColorInt color: Int): ShapeSelector { + mSelectedStrokeColor = color + hasSetSelectedStrokeColor = true + return this + } + + fun setCheckedStrokeColor(@ColorInt color: Int): ShapeSelector { + mCheckedStrokeColor = color + hasSetCheckedStrokeColor = true + return this + } + + fun setFocusedStrokeColor(@ColorInt color: Int): ShapeSelector { + mFocusedStrokeColor = color + hasSetFocusedStrokeColor = true + return this + } + + fun setCornerRadius(@Dimension radius: Int): ShapeSelector { + mCornerRadius = radius + return this + } + + fun create(): StateListDrawable { + val selector = StateListDrawable() + + //enabled = false + if (hasSetDisabledBgColor || hasSetDisabledStrokeColor) { + val disabledShape = getItemShape( + mShape, mCornerRadius, + mDisabledBgColor, mStrokeWidth, mDisabledStrokeColor + ) + selector.addState(intArrayOf(-android.R.attr.state_enabled), disabledShape) + } + + //pressed = true + if (hasSetPressedBgColor || hasSetPressedStrokeColor) { + val pressedShape = getItemShape( + mShape, mCornerRadius, + mPressedBgColor, mStrokeWidth, mPressedStrokeColor + ) + selector.addState(intArrayOf(android.R.attr.state_pressed), pressedShape) + } + + //selected = true + if (hasSetSelectedBgColor || hasSetSelectedStrokeColor) { + val selectedShape = getItemShape( + mShape, mCornerRadius, + mSelectedBgColor, mStrokeWidth, mSelectedStrokeColor + ) + selector.addState(intArrayOf(android.R.attr.state_selected), selectedShape) + } + + //focused = true + if (hasSetFocusedBgColor || hasSetFocusedStrokeColor) { + val focusedShape = getItemShape( + mShape, mCornerRadius, + mFocusedBgColor, mStrokeWidth, mFocusedStrokeColor + ) + selector.addState(intArrayOf(android.R.attr.state_focused), focusedShape) + } + + //checked = true + if (hasSetCheckedBgColor || hasSetCheckedStrokeColor) { + val checkedShape = getItemShape( + mShape, mCornerRadius, + mCheckedBgColor, mStrokeWidth, mCheckedStrokeColor + ) + selector.addState(intArrayOf(android.R.attr.state_checked), checkedShape) + } + + //default + val defaultShape = getItemShape( + mShape, mCornerRadius, + mDefaultBgColor, mStrokeWidth, mDefaultStrokeColor + ) + selector.addState(intArrayOf(), defaultShape) + + return selector + } + + private fun getItemShape( + shape: Int, cornerRadius: Int, + solidColor: Int, strokeWidth: Int, strokeColor: Int + ): GradientDrawable { + val drawable = GradientDrawable() + drawable.shape = shape + drawable.setStroke(strokeWidth, strokeColor) + drawable.cornerRadius = cornerRadius.toFloat() + drawable.setColor(solidColor) + return drawable + } + } + + /** + * 资源DrawableSelector + * + * @author hjy + * created at 2017/12/11 22:34 + */ + class DrawableSelector constructor() { + + private var mDefaultDrawable: Drawable? = null + private var mDisabledDrawable: Drawable? = null + private var mPressedDrawable: Drawable? = null + private var mSelectedDrawable: Drawable? = null + private var mFocusedDrawable: Drawable? = null + + private var hasSetDisabledDrawable = false + private var hasSetPressedDrawable = false + private var hasSetSelectedDrawable = false + private var hasSetFocusedDrawable = false + + init { + mDefaultDrawable = ColorDrawable(Color.TRANSPARENT) + } + + fun setDefaultDrawable(drawable: Drawable?): DrawableSelector { + mDefaultDrawable = drawable + if (!hasSetDisabledDrawable) + mDisabledDrawable = drawable + if (!hasSetPressedDrawable) + mPressedDrawable = drawable + if (!hasSetSelectedDrawable) + mSelectedDrawable = drawable + if (!hasSetFocusedDrawable) + mFocusedDrawable = drawable + return this + } + + fun setDisabledDrawable(drawable: Drawable?): DrawableSelector { + mDisabledDrawable = drawable + hasSetDisabledDrawable = true + return this + } + + fun setPressedDrawable(drawable: Drawable?): DrawableSelector { + mPressedDrawable = drawable + hasSetPressedDrawable = true + return this + } + + fun setSelectedDrawable(drawable: Drawable?): DrawableSelector { + mSelectedDrawable = drawable + hasSetSelectedDrawable = true + return this + } + + fun setFocusedDrawable(drawable: Drawable?): DrawableSelector { + mFocusedDrawable = drawable + hasSetFocusedDrawable = true + return this + } + + fun create(): StateListDrawable { + val selector = StateListDrawable() + if (hasSetDisabledDrawable) + selector.addState(intArrayOf(-android.R.attr.state_enabled), mDisabledDrawable) + if (hasSetPressedDrawable) + selector.addState(intArrayOf(android.R.attr.state_pressed), mPressedDrawable) + if (hasSetSelectedDrawable) + selector.addState(intArrayOf(android.R.attr.state_selected), mSelectedDrawable) + if (hasSetFocusedDrawable) + selector.addState(intArrayOf(android.R.attr.state_focused), mFocusedDrawable) + selector.addState(intArrayOf(), mDefaultDrawable) + return selector + } + + fun setDefaultDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector { + return setDefaultDrawable(ContextCompat.getDrawable(context, drawableRes)) + } + + fun setDisabledDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector { + return setDisabledDrawable(ContextCompat.getDrawable(context, drawableRes)) + } + + fun setPressedDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector { + return setPressedDrawable(ContextCompat.getDrawable(context, drawableRes)) + } + + fun setSelectedDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector { + return setSelectedDrawable(ContextCompat.getDrawable(context, drawableRes)) + } + + fun setFocusedDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector { + return setFocusedDrawable(ContextCompat.getDrawable(context, drawableRes)) + } + } + + /** + * 颜色ColorSelector + * + * @author hjy + * created at 2017/12/11 22:26 + */ + class ColorSelector constructor() { + + private var mDefaultColor: Int = 0 + private var mDisabledColor: Int = 0 + private var mPressedColor: Int = 0 + private var mSelectedColor: Int = 0 + private var mFocusedColor: Int = 0 + private var mCheckedColor: Int = 0 + + private var hasSetDisabledColor = false + private var hasSetPressedColor = false + private var hasSetSelectedColor = false + private var hasSetFocusedColor = false + private var hasSetCheckedColor = false + + init { + mDefaultColor = Color.BLACK + mDisabledColor = Color.GRAY + mPressedColor = Color.BLACK + mSelectedColor = Color.BLACK + mFocusedColor = Color.BLACK + } + + fun setDefaultColor(@ColorInt color: Int): ColorSelector { + mDefaultColor = color + if (!hasSetDisabledColor) + mDisabledColor = color + if (!hasSetPressedColor) + mPressedColor = color + if (!hasSetSelectedColor) + mSelectedColor = color + if (!hasSetFocusedColor) + mFocusedColor = color + return this + } + + fun setDisabledColor(@ColorInt color: Int): ColorSelector { + mDisabledColor = color + hasSetDisabledColor = true + return this + } + + fun setPressedColor(@ColorInt color: Int): ColorSelector { + mPressedColor = color + hasSetPressedColor = true + return this + } + + fun setSelectedColor(@ColorInt color: Int): ColorSelector { + mSelectedColor = color + hasSetSelectedColor = true + return this + } + + fun setFocusedColor(@ColorInt color: Int): ColorSelector { + mFocusedColor = color + hasSetFocusedColor = true + return this + } + + fun setCheckedColor(@ColorInt color: Int): ColorSelector { + mCheckedColor = color + hasSetCheckedColor = true + return this + } + + fun create(): ColorStateList { + val colors = intArrayOf( + if (hasSetDisabledColor) mDisabledColor else mDefaultColor, + if (hasSetPressedColor) mPressedColor else mDefaultColor, + if (hasSetSelectedColor) mSelectedColor else mDefaultColor, + if (hasSetFocusedColor) mFocusedColor else mDefaultColor, + if (hasSetCheckedColor) mCheckedColor else mDefaultColor, + mDefaultColor + ) + val states = arrayOfNulls(6) + states[0] = intArrayOf(-android.R.attr.state_enabled) + states[1] = intArrayOf(android.R.attr.state_pressed) + states[2] = intArrayOf(android.R.attr.state_selected) + states[3] = intArrayOf(android.R.attr.state_focused) + states[4] = intArrayOf(android.R.attr.state_checked) + states[5] = intArrayOf() + return ColorStateList(states, colors) + } + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.java b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.java deleted file mode 100644 index 52e4544ea..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.java +++ /dev/null @@ -1,318 +0,0 @@ -package io.legado.app.lib.theme; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Color; -import androidx.annotation.*; -import androidx.core.content.ContextCompat; -import io.legado.app.R; - -/** - * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) - */ -public final class ThemeStore implements ThemeStorePrefKeys, ThemeStoreInterface { - - private final Context mContext; - private final SharedPreferences.Editor mEditor; - - public static ThemeStore editTheme(@NonNull Context context) { - return new ThemeStore(context); - } - - @SuppressLint("CommitPrefEdits") - private ThemeStore(@NonNull Context context) { - mContext = context; - mEditor = prefs(context).edit(); - } - - - @Override - public ThemeStore primaryColor(@ColorInt int color) { - mEditor.putInt(KEY_PRIMARY_COLOR, color); - if (autoGeneratePrimaryDark(mContext)) - primaryColorDark(ColorUtil.darkenColor(color)); - return this; - } - - @Override - public ThemeStore primaryColorRes(@ColorRes int colorRes) { - return primaryColor(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore primaryColorAttr(@AttrRes int colorAttr) { - return primaryColor(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore primaryColorDark(@ColorInt int color) { - mEditor.putInt(KEY_PRIMARY_COLOR_DARK, color); - return this; - } - - @Override - public ThemeStore primaryColorDarkRes(@ColorRes int colorRes) { - return primaryColorDark(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore primaryColorDarkAttr(@AttrRes int colorAttr) { - return primaryColorDark(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore accentColor(@ColorInt int color) { - mEditor.putInt(KEY_ACCENT_COLOR, color); - return this; - } - - @Override - public ThemeStore accentColorRes(@ColorRes int colorRes) { - return accentColor(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore accentColorAttr(@AttrRes int colorAttr) { - return accentColor(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore statusBarColor(@ColorInt int color) { - mEditor.putInt(KEY_STATUS_BAR_COLOR, color); - return this; - } - - @Override - public ThemeStore statusBarColorRes(@ColorRes int colorRes) { - return statusBarColor(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore statusBarColorAttr(@AttrRes int colorAttr) { - return statusBarColor(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore navigationBarColor(@ColorInt int color) { - mEditor.putInt(KEY_NAVIGATION_BAR_COLOR, color); - return this; - } - - @Override - public ThemeStore navigationBarColorRes(@ColorRes int colorRes) { - return navigationBarColor(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore navigationBarColorAttr(@AttrRes int colorAttr) { - return navigationBarColor(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore textColorPrimary(@ColorInt int color) { - mEditor.putInt(KEY_TEXT_COLOR_PRIMARY, color); - return this; - } - - @Override - public ThemeStore textColorPrimaryRes(@ColorRes int colorRes) { - return textColorPrimary(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore textColorPrimaryAttr(@AttrRes int colorAttr) { - return textColorPrimary(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore textColorPrimaryInverse(@ColorInt int color) { - mEditor.putInt(KEY_TEXT_COLOR_PRIMARY_INVERSE, color); - return this; - } - - @Override - public ThemeStore textColorPrimaryInverseRes(@ColorRes int colorRes) { - return textColorPrimaryInverse(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore textColorPrimaryInverseAttr(@AttrRes int colorAttr) { - return textColorPrimaryInverse(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore textColorSecondary(@ColorInt int color) { - mEditor.putInt(KEY_TEXT_COLOR_SECONDARY, color); - return this; - } - - @Override - public ThemeStore textColorSecondaryRes(@ColorRes int colorRes) { - return textColorSecondary(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore textColorSecondaryAttr(@AttrRes int colorAttr) { - return textColorSecondary(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore textColorSecondaryInverse(@ColorInt int color) { - mEditor.putInt(KEY_TEXT_COLOR_SECONDARY_INVERSE, color); - return this; - } - - @Override - public ThemeStore textColorSecondaryInverseRes(@ColorRes int colorRes) { - return textColorSecondaryInverse(ContextCompat.getColor(mContext, colorRes)); - } - - @Override - public ThemeStore textColorSecondaryInverseAttr(@AttrRes int colorAttr) { - return textColorSecondaryInverse(ATHUtil.resolveColor(mContext, colorAttr)); - } - - @Override - public ThemeStore backgroundColor(int color) { - mEditor.putInt(KEY_BACKGROUND_COLOR, color); - return this; - } - - @Override - public ThemeStore coloredStatusBar(boolean colored) { - mEditor.putBoolean(KEY_APPLY_PRIMARYDARK_STATUSBAR, colored); - return this; - } - - @Override - public ThemeStore coloredNavigationBar(boolean applyToNavBar) { - mEditor.putBoolean(KEY_APPLY_PRIMARY_NAVBAR, applyToNavBar); - return this; - } - - @Override - public ThemeStore autoGeneratePrimaryDark(boolean autoGenerate) { - mEditor.putBoolean(KEY_AUTO_GENERATE_PRIMARYDARK, autoGenerate); - return this; - } - - // Commit method - - @SuppressWarnings("unchecked") - @Override - public void apply() { - mEditor.putLong(VALUES_CHANGED, System.currentTimeMillis()) - .putBoolean(IS_CONFIGURED_KEY, true) - .apply(); - } - - // Static getters - - @CheckResult - @NonNull - protected static SharedPreferences prefs(@NonNull Context context) { - return context.getSharedPreferences(CONFIG_PREFS_KEY_DEFAULT, Context.MODE_PRIVATE); - } - - public static void markChanged(@NonNull Context context) { - new ThemeStore(context).apply(); - } - - @CheckResult - @ColorInt - public static int primaryColor(@NonNull Context context) { - return prefs(context).getInt(KEY_PRIMARY_COLOR, ATHUtil.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64"))); - } - - @CheckResult - @ColorInt - public static int primaryColorDark(@NonNull Context context) { - return prefs(context).getInt(KEY_PRIMARY_COLOR_DARK, ATHUtil.resolveColor(context, R.attr.colorPrimaryDark, Color.parseColor("#37474F"))); - } - - @CheckResult - @ColorInt - public static int accentColor(@NonNull Context context) { - return prefs(context).getInt(KEY_ACCENT_COLOR, ATHUtil.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238"))); - } - - @CheckResult - @ColorInt - public static int statusBarColor(@NonNull Context context) { - if (!coloredStatusBar(context)) { - return Color.BLACK; - } - return prefs(context).getInt(KEY_STATUS_BAR_COLOR, primaryColorDark(context)); - } - - @CheckResult - @ColorInt - public static int navigationBarColor(@NonNull Context context) { - if (!coloredNavigationBar(context)) { - return Color.BLACK; - } - return prefs(context).getInt(KEY_NAVIGATION_BAR_COLOR, primaryColor(context)); - } - - @CheckResult - @ColorInt - public static int textColorPrimary(@NonNull Context context) { - return prefs(context).getInt(KEY_TEXT_COLOR_PRIMARY, ATHUtil.resolveColor(context, android.R.attr.textColorPrimary)); - } - - @CheckResult - @ColorInt - public static int textColorPrimaryInverse(@NonNull Context context) { - return prefs(context).getInt(KEY_TEXT_COLOR_PRIMARY_INVERSE, ATHUtil.resolveColor(context, android.R.attr.textColorPrimaryInverse)); - } - - @CheckResult - @ColorInt - public static int textColorSecondary(@NonNull Context context) { - return prefs(context).getInt(KEY_TEXT_COLOR_SECONDARY, ATHUtil.resolveColor(context, android.R.attr.textColorSecondary)); - } - - @CheckResult - @ColorInt - public static int textColorSecondaryInverse(@NonNull Context context) { - return prefs(context).getInt(KEY_TEXT_COLOR_SECONDARY_INVERSE, ATHUtil.resolveColor(context, android.R.attr.textColorSecondaryInverse)); - } - - @CheckResult - @ColorInt - public static int backgroundColor(@NonNull Context context) { - return prefs(context).getInt(KEY_BACKGROUND_COLOR, ATHUtil.resolveColor(context, android.R.attr.colorBackground)); - } - - @CheckResult - public static boolean coloredStatusBar(@NonNull Context context) { - return prefs(context).getBoolean(KEY_APPLY_PRIMARYDARK_STATUSBAR, true); - } - - @CheckResult - public static boolean coloredNavigationBar(@NonNull Context context) { - return prefs(context).getBoolean(KEY_APPLY_PRIMARY_NAVBAR, false); - } - - @CheckResult - public static boolean autoGeneratePrimaryDark(@NonNull Context context) { - return prefs(context).getBoolean(KEY_AUTO_GENERATE_PRIMARYDARK, true); - } - - @CheckResult - public static boolean isConfigured(Context context) { - return prefs(context).getBoolean(IS_CONFIGURED_KEY, false); - } - - @SuppressLint("CommitPrefEdits") - public static boolean isConfigured(Context context, @IntRange(from = 0, to = Integer.MAX_VALUE) int version) { - final SharedPreferences prefs = prefs(context); - final int lastVersion = prefs.getInt(IS_CONFIGURED_VERSION_KEY, -1); - if (version > lastVersion) { - prefs.edit().putInt(IS_CONFIGURED_VERSION_KEY, version).apply(); - return false; - } - return true; - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt new file mode 100644 index 000000000..a55237ee5 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt @@ -0,0 +1,309 @@ +package io.legado.app.lib.theme + +import android.annotation.SuppressLint +import android.content.Context +import android.content.SharedPreferences +import android.graphics.Color +import androidx.annotation.AttrRes +import androidx.annotation.CheckResult +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import io.legado.app.R + +/** + * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) + */ +class ThemeStore @SuppressLint("CommitPrefEdits") +private constructor(private val mContext: Context) : ThemeStorePrefKeys, ThemeStoreInterface { + private val mEditor: SharedPreferences.Editor + + init { + mEditor = prefs(mContext).edit() + } + + + override fun primaryColor(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR, color) + if (autoGeneratePrimaryDark(mContext)) + primaryColorDark(ColorUtil.darkenColor(color)) + return this + } + + override fun primaryColorRes(@ColorRes colorRes: Int): ThemeStore { + return primaryColor(ContextCompat.getColor(mContext, colorRes)) + } + + override fun primaryColorAttr(@AttrRes colorAttr: Int): ThemeStore { + return primaryColor(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun primaryColorDark(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR_DARK, color) + return this + } + + override fun primaryColorDarkRes(@ColorRes colorRes: Int): ThemeStore { + return primaryColorDark(ContextCompat.getColor(mContext, colorRes)) + } + + override fun primaryColorDarkAttr(@AttrRes colorAttr: Int): ThemeStore { + return primaryColorDark(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun accentColor(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_ACCENT_COLOR, color) + return this + } + + override fun accentColorRes(@ColorRes colorRes: Int): ThemeStore { + return accentColor(ContextCompat.getColor(mContext, colorRes)) + } + + override fun accentColorAttr(@AttrRes colorAttr: Int): ThemeStore { + return accentColor(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun statusBarColor(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR, color) + return this + } + + override fun statusBarColorRes(@ColorRes colorRes: Int): ThemeStore { + return statusBarColor(ContextCompat.getColor(mContext, colorRes)) + } + + override fun statusBarColorAttr(@AttrRes colorAttr: Int): ThemeStore { + return statusBarColor(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun navigationBarColor(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_NAVIGATION_BAR_COLOR, color) + return this + } + + override fun navigationBarColorRes(@ColorRes colorRes: Int): ThemeStore { + return navigationBarColor(ContextCompat.getColor(mContext, colorRes)) + } + + override fun navigationBarColorAttr(@AttrRes colorAttr: Int): ThemeStore { + return navigationBarColor(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun textColorPrimary(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY, color) + return this + } + + override fun textColorPrimaryRes(@ColorRes colorRes: Int): ThemeStore { + return textColorPrimary(ContextCompat.getColor(mContext, colorRes)) + } + + override fun textColorPrimaryAttr(@AttrRes colorAttr: Int): ThemeStore { + return textColorPrimary(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun textColorPrimaryInverse(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY_INVERSE, color) + return this + } + + override fun textColorPrimaryInverseRes(@ColorRes colorRes: Int): ThemeStore { + return textColorPrimaryInverse(ContextCompat.getColor(mContext, colorRes)) + } + + override fun textColorPrimaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore { + return textColorPrimaryInverse(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun textColorSecondary(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY, color) + return this + } + + override fun textColorSecondaryRes(@ColorRes colorRes: Int): ThemeStore { + return textColorSecondary(ContextCompat.getColor(mContext, colorRes)) + } + + override fun textColorSecondaryAttr(@AttrRes colorAttr: Int): ThemeStore { + return textColorSecondary(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun textColorSecondaryInverse(@ColorInt color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY_INVERSE, color) + return this + } + + override fun textColorSecondaryInverseRes(@ColorRes colorRes: Int): ThemeStore { + return textColorSecondaryInverse(ContextCompat.getColor(mContext, colorRes)) + } + + override fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore { + return textColorSecondaryInverse(ATHUtil.resolveColor(mContext, colorAttr)) + } + + override fun backgroundColor(color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_BACKGROUND_COLOR, color) + return this + } + + override fun coloredStatusBar(colored: Boolean): ThemeStore { + mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, colored) + return this + } + + override fun coloredNavigationBar(applyToNavBar: Boolean): ThemeStore { + mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARY_NAVBAR, applyToNavBar) + return this + } + + override fun autoGeneratePrimaryDark(autoGenerate: Boolean): ThemeStore { + mEditor.putBoolean(ThemeStorePrefKeys.KEY_AUTO_GENERATE_PRIMARYDARK, autoGenerate) + return this + } + + // Commit method + + override fun apply() { + mEditor.putLong(ThemeStorePrefKeys.VALUES_CHANGED, System.currentTimeMillis()) + .putBoolean(ThemeStorePrefKeys.IS_CONFIGURED_KEY, true) + .apply() + } + + companion object { + + fun editTheme(context: Context): ThemeStore { + return ThemeStore(context) + } + + // Static getters + + @CheckResult + protected fun prefs(context: Context): SharedPreferences { + return context.getSharedPreferences(ThemeStorePrefKeys.CONFIG_PREFS_KEY_DEFAULT, Context.MODE_PRIVATE) + } + + fun markChanged(context: Context) { + ThemeStore(context).apply() + } + + @CheckResult + @ColorInt + fun primaryColor(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_PRIMARY_COLOR, + ATHUtil.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64")) + ) + } + + @CheckResult + @ColorInt + fun primaryColorDark(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_PRIMARY_COLOR_DARK, + ATHUtil.resolveColor(context, R.attr.colorPrimaryDark, Color.parseColor("#37474F")) + ) + } + + @CheckResult + @ColorInt + fun accentColor(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_ACCENT_COLOR, + ATHUtil.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238")) + ) + } + + @CheckResult + @ColorInt + fun statusBarColor(context: Context): Int { + return if (!coloredStatusBar(context)) { + Color.BLACK + } else prefs(context).getInt(ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR, primaryColorDark(context)) + } + + @CheckResult + @ColorInt + fun navigationBarColor(context: Context): Int { + return if (!coloredNavigationBar(context)) { + Color.BLACK + } else prefs(context).getInt(ThemeStorePrefKeys.KEY_NAVIGATION_BAR_COLOR, primaryColor(context)) + } + + @CheckResult + @ColorInt + fun textColorPrimary(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY, + ATHUtil.resolveColor(context, android.R.attr.textColorPrimary) + ) + } + + @CheckResult + @ColorInt + fun textColorPrimaryInverse(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY_INVERSE, + ATHUtil.resolveColor(context, android.R.attr.textColorPrimaryInverse) + ) + } + + @CheckResult + @ColorInt + fun textColorSecondary(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY, + ATHUtil.resolveColor(context, android.R.attr.textColorSecondary) + ) + } + + @CheckResult + @ColorInt + fun textColorSecondaryInverse(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY_INVERSE, + ATHUtil.resolveColor(context, android.R.attr.textColorSecondaryInverse) + ) + } + + @CheckResult + @ColorInt + fun backgroundColor(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_BACKGROUND_COLOR, + ATHUtil.resolveColor(context, android.R.attr.colorBackground) + ) + } + + @CheckResult + fun coloredStatusBar(context: Context): Boolean { + return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, true) + } + + @CheckResult + fun coloredNavigationBar(context: Context): Boolean { + return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARY_NAVBAR, false) + } + + @CheckResult + fun autoGeneratePrimaryDark(context: Context): Boolean { + return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_AUTO_GENERATE_PRIMARYDARK, true) + } + + @CheckResult + fun isConfigured(context: Context): Boolean { + return prefs(context).getBoolean(ThemeStorePrefKeys.IS_CONFIGURED_KEY, false) + } + + @SuppressLint("CommitPrefEdits") + fun isConfigured(context: Context, version: Int): Boolean { + val prefs = prefs(context) + val lastVersion = prefs.getInt(ThemeStorePrefKeys.IS_CONFIGURED_VERSION_KEY, -1) + if (version > lastVersion) { + prefs.edit().putInt(ThemeStorePrefKeys.IS_CONFIGURED_VERSION_KEY, version).apply() + return false + } + return true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java deleted file mode 100644 index 4c3214396..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.legado.app.lib.theme; - - -import androidx.annotation.AttrRes; -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; - -/** - * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) - */ -interface ThemeStoreInterface { - - // Primary colors - - ThemeStore primaryColor(@ColorInt int color); - - ThemeStore primaryColorRes(@ColorRes int colorRes); - - ThemeStore primaryColorAttr(@AttrRes int colorAttr); - - ThemeStore autoGeneratePrimaryDark(boolean autoGenerate); - - ThemeStore primaryColorDark(@ColorInt int color); - - ThemeStore primaryColorDarkRes(@ColorRes int colorRes); - - ThemeStore primaryColorDarkAttr(@AttrRes int colorAttr); - - // Accent colors - - ThemeStore accentColor(@ColorInt int color); - - ThemeStore accentColorRes(@ColorRes int colorRes); - - ThemeStore accentColorAttr(@AttrRes int colorAttr); - - // Status bar color - - ThemeStore statusBarColor(@ColorInt int color); - - ThemeStore statusBarColorRes(@ColorRes int colorRes); - - ThemeStore statusBarColorAttr(@AttrRes int colorAttr); - - // Navigation bar color - - ThemeStore navigationBarColor(@ColorInt int color); - - ThemeStore navigationBarColorRes(@ColorRes int colorRes); - - ThemeStore navigationBarColorAttr(@AttrRes int colorAttr); - - // Primary text color - - ThemeStore textColorPrimary(@ColorInt int color); - - ThemeStore textColorPrimaryRes(@ColorRes int colorRes); - - ThemeStore textColorPrimaryAttr(@AttrRes int colorAttr); - - ThemeStore textColorPrimaryInverse(@ColorInt int color); - - ThemeStore textColorPrimaryInverseRes(@ColorRes int colorRes); - - ThemeStore textColorPrimaryInverseAttr(@AttrRes int colorAttr); - - // Secondary text color - - ThemeStore textColorSecondary(@ColorInt int color); - - ThemeStore textColorSecondaryRes(@ColorRes int colorRes); - - ThemeStore textColorSecondaryAttr(@AttrRes int colorAttr); - - ThemeStore textColorSecondaryInverse(@ColorInt int color); - - ThemeStore textColorSecondaryInverseRes(@ColorRes int colorRes); - - ThemeStore textColorSecondaryInverseAttr(@AttrRes int colorAttr); - - ThemeStore backgroundColor(@ColorInt int color); - - // Toggle configurations - - ThemeStore coloredStatusBar(boolean colored); - - ThemeStore coloredNavigationBar(boolean applyToNavBar); - - // Commit/apply - - void apply(); -} diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt new file mode 100644 index 000000000..55584b633 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt @@ -0,0 +1,92 @@ +package io.legado.app.lib.theme + + +import androidx.annotation.AttrRes +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes + +/** + * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) + */ +internal interface ThemeStoreInterface { + + // Primary colors + + fun primaryColor(@ColorInt color: Int): ThemeStore + + fun primaryColorRes(@ColorRes colorRes: Int): ThemeStore + + fun primaryColorAttr(@AttrRes colorAttr: Int): ThemeStore + + fun autoGeneratePrimaryDark(autoGenerate: Boolean): ThemeStore + + fun primaryColorDark(@ColorInt color: Int): ThemeStore + + fun primaryColorDarkRes(@ColorRes colorRes: Int): ThemeStore + + fun primaryColorDarkAttr(@AttrRes colorAttr: Int): ThemeStore + + // Accent colors + + fun accentColor(@ColorInt color: Int): ThemeStore + + fun accentColorRes(@ColorRes colorRes: Int): ThemeStore + + fun accentColorAttr(@AttrRes colorAttr: Int): ThemeStore + + // Status bar color + + fun statusBarColor(@ColorInt color: Int): ThemeStore + + fun statusBarColorRes(@ColorRes colorRes: Int): ThemeStore + + fun statusBarColorAttr(@AttrRes colorAttr: Int): ThemeStore + + // Navigation bar color + + fun navigationBarColor(@ColorInt color: Int): ThemeStore + + fun navigationBarColorRes(@ColorRes colorRes: Int): ThemeStore + + fun navigationBarColorAttr(@AttrRes colorAttr: Int): ThemeStore + + // Primary text color + + fun textColorPrimary(@ColorInt color: Int): ThemeStore + + fun textColorPrimaryRes(@ColorRes colorRes: Int): ThemeStore + + fun textColorPrimaryAttr(@AttrRes colorAttr: Int): ThemeStore + + fun textColorPrimaryInverse(@ColorInt color: Int): ThemeStore + + fun textColorPrimaryInverseRes(@ColorRes colorRes: Int): ThemeStore + + fun textColorPrimaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore + + // Secondary text color + + fun textColorSecondary(@ColorInt color: Int): ThemeStore + + fun textColorSecondaryRes(@ColorRes colorRes: Int): ThemeStore + + fun textColorSecondaryAttr(@AttrRes colorAttr: Int): ThemeStore + + fun textColorSecondaryInverse(@ColorInt color: Int): ThemeStore + + fun textColorSecondaryInverseRes(@ColorRes colorRes: Int): ThemeStore + + fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore + + fun backgroundColor(@ColorInt color: Int): ThemeStore + + // Toggle configurations + + fun coloredStatusBar(colored: Boolean): ThemeStore + + fun coloredNavigationBar(applyToNavBar: Boolean): ThemeStore + + // Commit/apply + + fun apply() +} diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java deleted file mode 100644 index dc09388cc..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.legado.app.lib.theme; - -/** - * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) - */ -interface ThemeStorePrefKeys { - - String CONFIG_PREFS_KEY_DEFAULT = "app_themes"; - String IS_CONFIGURED_KEY = "is_configured"; - String IS_CONFIGURED_VERSION_KEY = "is_configured_version"; - String VALUES_CHANGED = "values_changed"; - - String KEY_PRIMARY_COLOR = "primary_color"; - String KEY_PRIMARY_COLOR_DARK = "primary_color_dark"; - String KEY_ACCENT_COLOR = "accent_color"; - String KEY_STATUS_BAR_COLOR = "status_bar_color"; - String KEY_NAVIGATION_BAR_COLOR = "navigation_bar_color"; - - String KEY_TEXT_COLOR_PRIMARY = "text_color_primary"; - String KEY_TEXT_COLOR_PRIMARY_INVERSE = "text_color_primary_inverse"; - String KEY_TEXT_COLOR_SECONDARY = "text_color_secondary"; - String KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse"; - - String KEY_BACKGROUND_COLOR = "backgroundColor"; - - String KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar"; - String KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"; - String KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark"; -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt new file mode 100644 index 000000000..abe73d828 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt @@ -0,0 +1,31 @@ +package io.legado.app.lib.theme + +/** + * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) + */ +internal interface ThemeStorePrefKeys { + companion object { + + val CONFIG_PREFS_KEY_DEFAULT = "app_themes" + val IS_CONFIGURED_KEY = "is_configured" + val IS_CONFIGURED_VERSION_KEY = "is_configured_version" + val VALUES_CHANGED = "values_changed" + + val KEY_PRIMARY_COLOR = "primary_color" + val KEY_PRIMARY_COLOR_DARK = "primary_color_dark" + val KEY_ACCENT_COLOR = "accent_color" + val KEY_STATUS_BAR_COLOR = "status_bar_color" + val KEY_NAVIGATION_BAR_COLOR = "navigation_bar_color" + + val KEY_TEXT_COLOR_PRIMARY = "text_color_primary" + val KEY_TEXT_COLOR_PRIMARY_INVERSE = "text_color_primary_inverse" + val KEY_TEXT_COLOR_SECONDARY = "text_color_secondary" + val KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse" + + val KEY_BACKGROUND_COLOR = "backgroundColor" + + val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar" + val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar" + val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark" + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/TintHelper.java b/app/src/main/java/io/legado/app/lib/theme/TintHelper.java deleted file mode 100644 index 439190147..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/TintHelper.java +++ /dev/null @@ -1,384 +0,0 @@ -package io.legado.app.lib.theme; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.RippleDrawable; -import android.os.Build; -import android.view.View; -import android.widget.*; -import androidx.annotation.CheckResult; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatEditText; -import androidx.appcompat.widget.SearchView; -import androidx.appcompat.widget.SwitchCompat; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import io.legado.app.R; - -import java.lang.reflect.Field; - -/** - * @author afollestad, plusCubed - */ -public final class TintHelper { - - @SuppressLint("PrivateResource") - @ColorInt - private static int getDefaultRippleColor(@NonNull Context context, boolean useDarkRipple) { - // Light ripple is actually translucent black, and vice versa - return ContextCompat.getColor(context, useDarkRipple ? - R.color.ripple_material_light : R.color.ripple_material_dark); - } - - @NonNull - private static ColorStateList getDisabledColorStateList(@ColorInt int normal, @ColorInt int disabled) { - return new ColorStateList(new int[][]{ - new int[]{-android.R.attr.state_enabled}, - new int[]{android.R.attr.state_enabled} - }, new int[]{ - disabled, - normal - }); - } - - @SuppressWarnings("deprecation") - public static void setTintSelector(@NonNull View view, @ColorInt final int color, final boolean darker, final boolean useDarkTheme) { - final boolean isColorLight = ColorUtil.isColorLight(color); - final int disabled = ContextCompat.getColor(view.getContext(), useDarkTheme ? R.color.ate_button_disabled_dark : R.color.ate_button_disabled_light); - final int pressed = ColorUtil.shiftColor(color, darker ? 0.9f : 1.1f); - final int activated = ColorUtil.shiftColor(color, darker ? 1.1f : 0.9f); - final int rippleColor = getDefaultRippleColor(view.getContext(), isColorLight); - final int textColor = ContextCompat.getColor(view.getContext(), isColorLight ? R.color.ate_primary_text_light : R.color.ate_primary_text_dark); - - final ColorStateList sl; - if (view instanceof Button) { - sl = getDisabledColorStateList(color, disabled); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && - view.getBackground() instanceof RippleDrawable) { - RippleDrawable rd = (RippleDrawable) view.getBackground(); - rd.setColor(ColorStateList.valueOf(rippleColor)); - } - - // Disabled text color state for buttons, may get overridden later by ATE tags - final Button button = (Button) view; - button.setTextColor(getDisabledColorStateList(textColor, ContextCompat.getColor(view.getContext(), useDarkTheme ? R.color.ate_button_text_disabled_dark : R.color.ate_button_text_disabled_light))); - } else if (view instanceof FloatingActionButton) { - // FloatingActionButton doesn't support disabled state? - sl = new ColorStateList(new int[][]{ - new int[]{-android.R.attr.state_pressed}, - new int[]{android.R.attr.state_pressed} - }, new int[]{ - color, - pressed - }); - - final FloatingActionButton fab = (FloatingActionButton) view; - fab.setRippleColor(rippleColor); - fab.setBackgroundTintList(sl); - if (fab.getDrawable() != null) - fab.setImageDrawable(createTintedDrawable(fab.getDrawable(), textColor)); - return; - } else { - sl = new ColorStateList( - new int[][]{ - new int[]{-android.R.attr.state_enabled}, - new int[]{android.R.attr.state_enabled}, - new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed}, - new int[]{android.R.attr.state_enabled, android.R.attr.state_activated}, - new int[]{android.R.attr.state_enabled, android.R.attr.state_checked} - }, - new int[]{ - disabled, - color, - pressed, - activated, - activated - } - ); - } - - Drawable drawable = view.getBackground(); - if (drawable != null) { - drawable = createTintedDrawable(drawable, sl); - ViewUtil.setBackgroundCompat(view, drawable); - } - - if (view instanceof TextView && !(view instanceof Button)) { - final TextView tv = (TextView) view; - tv.setTextColor(getDisabledColorStateList(textColor, ContextCompat.getColor(view.getContext(), isColorLight ? R.color.ate_text_disabled_light : R.color.ate_text_disabled_dark))); - } - } - - public static void setTintAuto(final @NonNull View view, final @ColorInt int color, - boolean background) { - setTintAuto(view, color, background, ATHUtil.isWindowBackgroundDark(view.getContext())); - } - - @SuppressWarnings("deprecation") - public static void setTintAuto(final @NonNull View view, final @ColorInt int color, - boolean background, final boolean isDark) { - if (!background) { - if (view instanceof RadioButton) - setTint((RadioButton) view, color, isDark); - else if (view instanceof SeekBar) - setTint((SeekBar) view, color, isDark); - else if (view instanceof ProgressBar) - setTint((ProgressBar) view, color); - else if (view instanceof AppCompatEditText) - setTint((AppCompatEditText) view, color, isDark); - else if (view instanceof CheckBox) - setTint((CheckBox) view, color, isDark); - else if (view instanceof ImageView) - setTint((ImageView) view, color); - else if (view instanceof Switch) - setTint((Switch) view, color, isDark); - else if (view instanceof SwitchCompat) - setTint((SwitchCompat) view, color, isDark); - else if (view instanceof SearchView) { - int iconIdS[] = new int[]{androidx.appcompat.R.id.search_button, androidx.appcompat.R.id.search_close_btn,}; - for (int iconId : iconIdS) { - ImageView icon = view.findViewById(iconId); - if (icon != null) { - setTint(icon, color); - } - } - - } else { - background = true; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && - !background && view.getBackground() instanceof RippleDrawable) { - // Ripples for the above views (e.g. when you tap and hold a switch or checkbox) - RippleDrawable rd = (RippleDrawable) view.getBackground(); - @SuppressLint("PrivateResource") final int unchecked = ContextCompat.getColor(view.getContext(), - isDark ? R.color.ripple_material_dark : R.color.ripple_material_light); - final int checked = ColorUtil.adjustAlpha(color, 0.4f); - final ColorStateList sl = new ColorStateList( - new int[][]{ - new int[]{-android.R.attr.state_activated, -android.R.attr.state_checked}, - new int[]{android.R.attr.state_activated}, - new int[]{android.R.attr.state_checked} - }, - new int[]{ - unchecked, - checked, - checked - } - ); - rd.setColor(sl); - } - } - if (background) { - // Need to tint the background of a view - if (view instanceof FloatingActionButton || view instanceof Button) { - setTintSelector(view, color, false, isDark); - } else if (view.getBackground() != null) { - Drawable drawable = view.getBackground(); - if (drawable != null) { - drawable = createTintedDrawable(drawable, color); - ViewUtil.setBackgroundCompat(view, drawable); - } - } - } - } - - public static void setTint(@NonNull RadioButton radioButton, @ColorInt int color, boolean useDarker) { - ColorStateList sl = new ColorStateList(new int[][]{ - new int[]{-android.R.attr.state_enabled}, - new int[]{android.R.attr.state_enabled, -android.R.attr.state_checked}, - new int[]{android.R.attr.state_enabled, android.R.attr.state_checked} - }, new int[]{ - // Rdio button includes own alpha for disabled state - ColorUtil.stripAlpha(ContextCompat.getColor(radioButton.getContext(), useDarker ? R.color.ate_control_disabled_dark : R.color.ate_control_disabled_light)), - ContextCompat.getColor(radioButton.getContext(), useDarker ? R.color.ate_control_normal_dark : R.color.ate_control_normal_light), - color - }); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - radioButton.setButtonTintList(sl); - } else { - Drawable d = createTintedDrawable(ContextCompat.getDrawable(radioButton.getContext(), R.drawable.abc_btn_radio_material), sl); - radioButton.setButtonDrawable(d); - } - } - - public static void setTint(@NonNull SeekBar seekBar, @ColorInt int color, boolean useDarker) { - final ColorStateList s1 = getDisabledColorStateList(color, - ContextCompat.getColor(seekBar.getContext(), useDarker ? R.color.ate_control_disabled_dark : R.color.ate_control_disabled_light)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - seekBar.setThumbTintList(s1); - seekBar.setProgressTintList(s1); - } else { - Drawable progressDrawable = createTintedDrawable(seekBar.getProgressDrawable(), s1); - seekBar.setProgressDrawable(progressDrawable); - Drawable thumbDrawable = createTintedDrawable(seekBar.getThumb(), s1); - seekBar.setThumb(thumbDrawable); - } - } - - public static void setTint(@NonNull ProgressBar progressBar, @ColorInt int color) { - setTint(progressBar, color, false); - } - - public static void setTint(@NonNull ProgressBar progressBar, @ColorInt int color, boolean skipIndeterminate) { - ColorStateList sl = ColorStateList.valueOf(color); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - progressBar.setProgressTintList(sl); - progressBar.setSecondaryProgressTintList(sl); - if (!skipIndeterminate) - progressBar.setIndeterminateTintList(sl); - } else { - PorterDuff.Mode mode = PorterDuff.Mode.SRC_IN; - if (!skipIndeterminate && progressBar.getIndeterminateDrawable() != null) - progressBar.getIndeterminateDrawable().setColorFilter(color, mode); - if (progressBar.getProgressDrawable() != null) - progressBar.getProgressDrawable().setColorFilter(color, mode); - } - } - - - @SuppressLint("RestrictedApi") - public static void setTint(@NonNull AppCompatEditText editText, @ColorInt int color, boolean useDarker) { - final ColorStateList editTextColorStateList = new ColorStateList(new int[][]{ - new int[]{-android.R.attr.state_enabled}, - new int[]{android.R.attr.state_enabled, -android.R.attr.state_pressed, -android.R.attr.state_focused}, - new int[]{} - }, new int[]{ - ContextCompat.getColor(editText.getContext(), useDarker ? R.color.ate_text_disabled_dark : R.color.ate_text_disabled_light), - ContextCompat.getColor(editText.getContext(), useDarker ? R.color.ate_control_normal_dark : R.color.ate_control_normal_light), - color - }); - editText.setSupportBackgroundTintList(editTextColorStateList); - setCursorTint(editText, color); - } - - public static void setTint(@NonNull CheckBox box, @ColorInt int color, boolean useDarker) { - ColorStateList sl = new ColorStateList(new int[][]{ - new int[]{-android.R.attr.state_enabled}, - new int[]{android.R.attr.state_enabled, -android.R.attr.state_checked}, - new int[]{android.R.attr.state_enabled, android.R.attr.state_checked} - }, new int[]{ - ContextCompat.getColor(box.getContext(), useDarker ? R.color.ate_control_disabled_dark : R.color.ate_control_disabled_light), - ContextCompat.getColor(box.getContext(), useDarker ? R.color.ate_control_normal_dark : R.color.ate_control_normal_light), - color - }); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - box.setButtonTintList(sl); - } else { - Drawable drawable = createTintedDrawable(ContextCompat.getDrawable(box.getContext(), R.drawable.abc_btn_check_material), sl); - box.setButtonDrawable(drawable); - } - } - - public static void setTint(@NonNull ImageView image, @ColorInt int color) { - image.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); - } - - private static Drawable modifySwitchDrawable(@NonNull Context context, @NonNull Drawable from, @ColorInt int tint, boolean thumb, boolean compatSwitch, boolean useDarker) { - if (useDarker) { - tint = ColorUtil.shiftColor(tint, 1.1f); - } - tint = ColorUtil.adjustAlpha(tint, (compatSwitch && !thumb) ? 0.5f : 1.0f); - int disabled; - int normal; - if (thumb) { - disabled = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_thumb_disabled_dark : R.color.ate_switch_thumb_disabled_light); - normal = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_thumb_normal_dark : R.color.ate_switch_thumb_normal_light); - } else { - disabled = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_track_disabled_dark : R.color.ate_switch_track_disabled_light); - normal = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_track_normal_dark : R.color.ate_switch_track_normal_light); - } - - // Stock switch includes its own alpha - if (!compatSwitch) { - normal = ColorUtil.stripAlpha(normal); - } - - final ColorStateList sl = new ColorStateList( - new int[][]{ - new int[]{-android.R.attr.state_enabled}, - new int[]{android.R.attr.state_enabled, -android.R.attr.state_activated, -android.R.attr.state_checked}, - new int[]{android.R.attr.state_enabled, android.R.attr.state_activated}, - new int[]{android.R.attr.state_enabled, android.R.attr.state_checked} - }, - new int[]{ - disabled, - normal, - tint, - tint - } - ); - return createTintedDrawable(from, sl); - } - - public static void setTint(@NonNull Switch switchView, @ColorInt int color, boolean useDarker) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) return; - if (switchView.getTrackDrawable() != null) { - switchView.setTrackDrawable(modifySwitchDrawable(switchView.getContext(), - switchView.getTrackDrawable(), color, false, false, useDarker)); - } - if (switchView.getThumbDrawable() != null) { - switchView.setThumbDrawable(modifySwitchDrawable(switchView.getContext(), - switchView.getThumbDrawable(), color, true, false, useDarker)); - } - } - - public static void setTint(@NonNull SwitchCompat switchView, @ColorInt int color, boolean useDarker) { - if (switchView.getTrackDrawable() != null) { - switchView.setTrackDrawable(modifySwitchDrawable(switchView.getContext(), - switchView.getTrackDrawable(), color, false, true, useDarker)); - } - if (switchView.getThumbDrawable() != null) { - switchView.setThumbDrawable(modifySwitchDrawable(switchView.getContext(), - switchView.getThumbDrawable(), color, true, true, useDarker)); - } - } - - // This returns a NEW Drawable because of the mutate() call. The mutate() call is necessary because Drawables with the same resource have shared states otherwise. - @CheckResult - @Nullable - public static Drawable createTintedDrawable(@Nullable Drawable drawable, @ColorInt int color) { - if (drawable == null) return null; - drawable = DrawableCompat.wrap(drawable.mutate()); - DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_IN); - DrawableCompat.setTint(drawable, color); - return drawable; - } - - // This returns a NEW Drawable because of the mutate() call. The mutate() call is necessary because Drawables with the same resource have shared states otherwise. - @CheckResult - @Nullable - public static Drawable createTintedDrawable(@Nullable Drawable drawable, @NonNull ColorStateList sl) { - if (drawable == null) return null; - drawable = DrawableCompat.wrap(drawable.mutate()); - DrawableCompat.setTintList(drawable, sl); - return drawable; - } - - public static void setCursorTint(@NonNull EditText editText, @ColorInt int color) { - try { - Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); - fCursorDrawableRes.setAccessible(true); - int mCursorDrawableRes = fCursorDrawableRes.getInt(editText); - Field fEditor = TextView.class.getDeclaredField("mEditor"); - fEditor.setAccessible(true); - Object editor = fEditor.get(editText); - Class clazz = editor.getClass(); - Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable"); - fCursorDrawable.setAccessible(true); - Drawable[] drawables = new Drawable[2]; - drawables[0] = ContextCompat.getDrawable(editText.getContext(), mCursorDrawableRes); - drawables[0] = createTintedDrawable(drawables[0], color); - drawables[1] = ContextCompat.getDrawable(editText.getContext(), mCursorDrawableRes); - drawables[1] = createTintedDrawable(drawables[1], color); - fCursorDrawable.set(editor, drawables); - } catch (Exception ignored) { - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/TintHelper.kt b/app/src/main/java/io/legado/app/lib/theme/TintHelper.kt new file mode 100644 index 000000000..de2bf4164 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/TintHelper.kt @@ -0,0 +1,455 @@ +package io.legado.app.lib.theme + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.PorterDuff +import android.graphics.drawable.Drawable +import android.graphics.drawable.RippleDrawable +import android.os.Build +import android.view.View +import android.widget.* +import androidx.annotation.CheckResult +import androidx.annotation.ColorInt +import androidx.appcompat.widget.AppCompatEditText +import androidx.appcompat.widget.SearchView +import androidx.appcompat.widget.SwitchCompat +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.DrawableCompat +import com.google.android.material.floatingactionbutton.FloatingActionButton +import io.legado.app.R + +/** + * @author afollestad, plusCubed + */ +object TintHelper { + + @SuppressLint("PrivateResource") + @ColorInt + private fun getDefaultRippleColor(context: Context, useDarkRipple: Boolean): Int { + // Light ripple is actually translucent black, and vice versa + return ContextCompat.getColor( + context, if (useDarkRipple) + R.color.ripple_material_light + else + R.color.ripple_material_dark + ) + } + + private fun getDisabledColorStateList(@ColorInt normal: Int, @ColorInt disabled: Int): ColorStateList { + return ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_enabled), + intArrayOf(android.R.attr.state_enabled) + ), intArrayOf(disabled, normal) + ) + } + + fun setTintSelector(view: View, @ColorInt color: Int, darker: Boolean, useDarkTheme: Boolean) { + val isColorLight = ColorUtil.isColorLight(color) + val disabled = ContextCompat.getColor( + view.context, + if (useDarkTheme) R.color.ate_button_disabled_dark else R.color.ate_button_disabled_light + ) + val pressed = ColorUtil.shiftColor(color, if (darker) 0.9f else 1.1f) + val activated = ColorUtil.shiftColor(color, if (darker) 1.1f else 0.9f) + val rippleColor = getDefaultRippleColor(view.context, isColorLight) + val textColor = ContextCompat.getColor( + view.context, + if (isColorLight) R.color.ate_primary_text_light else R.color.ate_primary_text_dark + ) + + val sl: ColorStateList + if (view is Button) { + sl = getDisabledColorStateList(color, disabled) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && view.getBackground() is RippleDrawable) { + val rd = view.getBackground() as RippleDrawable + rd.setColor(ColorStateList.valueOf(rippleColor)) + } + + // Disabled text color state for buttons, may get overridden later by ATE tags + view.setTextColor( + getDisabledColorStateList( + textColor, + ContextCompat.getColor( + view.getContext(), + if (useDarkTheme) R.color.ate_button_text_disabled_dark else R.color.ate_button_text_disabled_light + ) + ) + ) + } else if (view is FloatingActionButton) { + // FloatingActionButton doesn't support disabled state? + sl = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_pressed), + intArrayOf(android.R.attr.state_pressed) + ), intArrayOf(color, pressed) + ) + + view.rippleColor = rippleColor + view.backgroundTintList = sl + if (view.drawable != null) + view.setImageDrawable(createTintedDrawable(view.drawable, textColor)) + return + } else { + sl = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_enabled), + intArrayOf(android.R.attr.state_enabled), + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_pressed), + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_activated), + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked) + ), + intArrayOf(disabled, color, pressed, activated, activated) + ) + } + + var drawable: Drawable? = view.background + if (drawable != null) { + drawable = createTintedDrawable(drawable, sl) + ViewUtil.setBackgroundCompat(view, drawable) + } + + if (view is TextView && view !is Button) { + view.setTextColor( + getDisabledColorStateList( + textColor, + ContextCompat.getColor( + view.getContext(), + if (isColorLight) R.color.ate_text_disabled_light else R.color.ate_text_disabled_dark + ) + ) + ) + } + } + + fun setTintAuto( + view: View, @ColorInt color: Int, + background: Boolean + ) { + setTintAuto(view, color, background, ATHUtil.isWindowBackgroundDark(view.context)) + } + + fun setTintAuto( + view: View, @ColorInt color: Int, + background: Boolean, isDark: Boolean + ) { + var background = background + if (!background) { + if (view is RadioButton) + setTint(view, color, isDark) + else if (view is SeekBar) + setTint(view, color, isDark) + else if (view is ProgressBar) + setTint(view, color) + else if (view is AppCompatEditText) + setTint(view, color, isDark) + else if (view is CheckBox) + setTint(view, color, isDark) + else if (view is ImageView) + setTint(view, color) + else if (view is Switch) + setTint(view, color, isDark) + else if (view is SwitchCompat) + setTint(view, color, isDark) + else if (view is SearchView) { + val iconIdS = + intArrayOf(androidx.appcompat.R.id.search_button, androidx.appcompat.R.id.search_close_btn) + for (iconId in iconIdS) { + val icon = view.findViewById(iconId) + if (icon != null) { + setTint(icon, color) + } + } + + } else { + background = true + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + !background && view.background is RippleDrawable + ) { + // Ripples for the above views (e.g. when you tap and hold a switch or checkbox) + val rd = view.background as RippleDrawable + @SuppressLint("PrivateResource") val unchecked = ContextCompat.getColor( + view.context, + if (isDark) R.color.ripple_material_dark else R.color.ripple_material_light + ) + val checked = ColorUtil.adjustAlpha(color, 0.4f) + val sl = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_activated, -android.R.attr.state_checked), + intArrayOf(android.R.attr.state_activated), + intArrayOf(android.R.attr.state_checked) + ), + intArrayOf(unchecked, checked, checked) + ) + rd.setColor(sl) + } + } + if (background) { + // Need to tint the background of a view + if (view is FloatingActionButton || view is Button) { + setTintSelector(view, color, false, isDark) + } else if (view.background != null) { + var drawable: Drawable? = view.background + if (drawable != null) { + drawable = createTintedDrawable(drawable, color) + ViewUtil.setBackgroundCompat(view, drawable) + } + } + } + } + + fun setTint(radioButton: RadioButton, @ColorInt color: Int, useDarker: Boolean) { + val sl = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_enabled), + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked) + ), intArrayOf( + // Rdio button includes own alpha for disabled state + ColorUtil.stripAlpha( + ContextCompat.getColor( + radioButton.context, + if (useDarker) R.color.ate_control_disabled_dark else R.color.ate_control_disabled_light + ) + ), + ContextCompat.getColor( + radioButton.context, + if (useDarker) R.color.ate_control_normal_dark else R.color.ate_control_normal_light + ), + color + ) + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + radioButton.buttonTintList = sl + } else { + val d = createTintedDrawable( + ContextCompat.getDrawable(radioButton.context, R.drawable.abc_btn_radio_material), + sl + ) + radioButton.buttonDrawable = d + } + } + + fun setTint(seekBar: SeekBar, @ColorInt color: Int, useDarker: Boolean) { + val s1 = getDisabledColorStateList( + color, + ContextCompat.getColor( + seekBar.context, + if (useDarker) R.color.ate_control_disabled_dark else R.color.ate_control_disabled_light + ) + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + seekBar.thumbTintList = s1 + seekBar.progressTintList = s1 + } else { + val progressDrawable = createTintedDrawable(seekBar.progressDrawable, s1) + seekBar.progressDrawable = progressDrawable + val thumbDrawable = createTintedDrawable(seekBar.thumb, s1) + seekBar.thumb = thumbDrawable + } + } + + @JvmOverloads + fun setTint(progressBar: ProgressBar, @ColorInt color: Int, skipIndeterminate: Boolean = false) { + val sl = ColorStateList.valueOf(color) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + progressBar.progressTintList = sl + progressBar.secondaryProgressTintList = sl + if (!skipIndeterminate) + progressBar.indeterminateTintList = sl + } else { + val mode = PorterDuff.Mode.SRC_IN + if (!skipIndeterminate && progressBar.indeterminateDrawable != null) + progressBar.indeterminateDrawable.setColorFilter(color, mode) + if (progressBar.progressDrawable != null) + progressBar.progressDrawable.setColorFilter(color, mode) + } + } + + + @SuppressLint("RestrictedApi") + fun setTint(editText: AppCompatEditText, @ColorInt color: Int, useDarker: Boolean) { + val editTextColorStateList = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_enabled), + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_pressed, -android.R.attr.state_focused), + intArrayOf() + ), + intArrayOf( + ContextCompat.getColor( + editText.context, + if (useDarker) R.color.ate_text_disabled_dark else R.color.ate_text_disabled_light + ), + ContextCompat.getColor( + editText.context, + if (useDarker) R.color.ate_control_normal_dark else R.color.ate_control_normal_light + ), + color + ) + ) + editText.supportBackgroundTintList = editTextColorStateList + setCursorTint(editText, color) + } + + fun setTint(box: CheckBox, @ColorInt color: Int, useDarker: Boolean) { + val sl = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_enabled), + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked) + ), + intArrayOf( + ContextCompat.getColor( + box.context, + if (useDarker) R.color.ate_control_disabled_dark else R.color.ate_control_disabled_light + ), + ContextCompat.getColor( + box.context, + if (useDarker) R.color.ate_control_normal_dark else R.color.ate_control_normal_light + ), + color + ) + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + box.buttonTintList = sl + } else { + val drawable = + createTintedDrawable(ContextCompat.getDrawable(box.context, R.drawable.abc_btn_check_material), sl) + box.buttonDrawable = drawable + } + } + + fun setTint(image: ImageView, @ColorInt color: Int) { + image.setColorFilter(color, PorterDuff.Mode.SRC_ATOP) + } + + private fun modifySwitchDrawable( + context: Context, + from: Drawable, @ColorInt tint: Int, + thumb: Boolean, + compatSwitch: Boolean, + useDarker: Boolean + ): Drawable? { + var tint = tint + if (useDarker) { + tint = ColorUtil.shiftColor(tint, 1.1f) + } + tint = ColorUtil.adjustAlpha(tint, if (compatSwitch && !thumb) 0.5f else 1.0f) + val disabled: Int + var normal: Int + if (thumb) { + disabled = ContextCompat.getColor( + context, + if (useDarker) R.color.ate_switch_thumb_disabled_dark else R.color.ate_switch_thumb_disabled_light + ) + normal = ContextCompat.getColor( + context, + if (useDarker) R.color.ate_switch_thumb_normal_dark else R.color.ate_switch_thumb_normal_light + ) + } else { + disabled = ContextCompat.getColor( + context, + if (useDarker) R.color.ate_switch_track_disabled_dark else R.color.ate_switch_track_disabled_light + ) + normal = ContextCompat.getColor( + context, + if (useDarker) R.color.ate_switch_track_normal_dark else R.color.ate_switch_track_normal_light + ) + } + + // Stock switch includes its own alpha + if (!compatSwitch) { + normal = ColorUtil.stripAlpha(normal) + } + + val sl = ColorStateList( + arrayOf( + intArrayOf(-android.R.attr.state_enabled), + intArrayOf( + android.R.attr.state_enabled, + -android.R.attr.state_activated, + -android.R.attr.state_checked + ), + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_activated), + intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked) + ), + intArrayOf(disabled, normal, tint, tint) + ) + return createTintedDrawable(from, sl) + } + + fun setTint(switchView: Switch, @ColorInt color: Int, useDarker: Boolean) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) return + if (switchView.trackDrawable != null) { + switchView.trackDrawable = modifySwitchDrawable( + switchView.context, + switchView.trackDrawable, color, false, false, useDarker + ) + } + if (switchView.thumbDrawable != null) { + switchView.thumbDrawable = modifySwitchDrawable( + switchView.context, + switchView.thumbDrawable, color, true, false, useDarker + ) + } + } + + fun setTint(switchView: SwitchCompat, @ColorInt color: Int, useDarker: Boolean) { + if (switchView.trackDrawable != null) { + switchView.trackDrawable = modifySwitchDrawable( + switchView.context, + switchView.trackDrawable, color, false, true, useDarker + ) + } + if (switchView.thumbDrawable != null) { + switchView.thumbDrawable = modifySwitchDrawable( + switchView.context, + switchView.thumbDrawable, color, true, true, useDarker + ) + } + } + + // This returns a NEW Drawable because of the mutate() call. The mutate() call is necessary because Drawables with the same resource have shared states otherwise. + @CheckResult + fun createTintedDrawable(drawable: Drawable?, @ColorInt color: Int): Drawable? { + var drawable: Drawable? = drawable ?: return null + drawable = DrawableCompat.wrap(drawable!!.mutate()) + DrawableCompat.setTintMode(drawable!!, PorterDuff.Mode.SRC_IN) + DrawableCompat.setTint(drawable, color) + return drawable + } + + // This returns a NEW Drawable because of the mutate() call. The mutate() call is necessary because Drawables with the same resource have shared states otherwise. + @CheckResult + fun createTintedDrawable(drawable: Drawable?, sl: ColorStateList): Drawable? { + var drawable: Drawable? = drawable ?: return null + drawable = DrawableCompat.wrap(drawable!!.mutate()) + DrawableCompat.setTintList(drawable!!, sl) + return drawable + } + + fun setCursorTint(editText: EditText, @ColorInt color: Int) { + try { + val fCursorDrawableRes = TextView::class.java.getDeclaredField("mCursorDrawableRes") + fCursorDrawableRes.isAccessible = true + val mCursorDrawableRes = fCursorDrawableRes.getInt(editText) + val fEditor = TextView::class.java.getDeclaredField("mEditor") + fEditor.isAccessible = true + val editor = fEditor.get(editText) + val clazz = editor.javaClass + val fCursorDrawable = clazz.getDeclaredField("mCursorDrawable") + fCursorDrawable.isAccessible = true + val drawables = arrayOfNulls(2) + drawables[0] = ContextCompat.getDrawable(editText.context, mCursorDrawableRes) + drawables[0] = createTintedDrawable(drawables[0], color) + drawables[1] = ContextCompat.getDrawable(editText.context, mCursorDrawableRes) + drawables[1] = createTintedDrawable(drawables[1], color) + fCursorDrawable.set(editor, drawables) + } catch (ignored: Exception) { + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/ViewUtil.java b/app/src/main/java/io/legado/app/lib/theme/ViewUtil.java deleted file mode 100644 index 7fd0f65e3..000000000 --- a/app/src/main/java/io/legado/app/lib/theme/ViewUtil.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.legado.app.lib.theme; - -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.TransitionDrawable; -import android.view.View; -import android.view.ViewTreeObserver; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public final class ViewUtil { - - @SuppressWarnings("deprecation") - public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener) { - v.getViewTreeObserver().removeOnGlobalLayoutListener(listener); - } - - @SuppressWarnings("deprecation") - public static void setBackgroundCompat(@NonNull View view, @Nullable Drawable drawable) { - view.setBackground(drawable); - } - - public static TransitionDrawable setBackgroundTransition(@NonNull View view, @NonNull Drawable newDrawable) { - TransitionDrawable transition = DrawableUtil.createTransitionDrawable(view.getBackground(), newDrawable); - setBackgroundCompat(view, transition); - return transition; - } - - public static TransitionDrawable setBackgroundColorTransition(@NonNull View view, @ColorInt int newColor) { - final Drawable oldColor = view.getBackground(); - - Drawable start = oldColor != null ? oldColor : new ColorDrawable(view.getSolidColor()); - Drawable end = new ColorDrawable(newColor); - - TransitionDrawable transition = DrawableUtil.createTransitionDrawable(start, end); - - setBackgroundCompat(view, transition); - - return transition; - } - - private ViewUtil() { - } -} diff --git a/app/src/main/java/io/legado/app/lib/theme/ViewUtil.kt b/app/src/main/java/io/legado/app/lib/theme/ViewUtil.kt new file mode 100644 index 000000000..7b2e69451 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/ViewUtil.kt @@ -0,0 +1,41 @@ +package io.legado.app.lib.theme + +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.TransitionDrawable +import android.view.View +import android.view.ViewTreeObserver +import androidx.annotation.ColorInt + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +object ViewUtil { + + fun removeOnGlobalLayoutListener(v: View, listener: ViewTreeObserver.OnGlobalLayoutListener) { + v.viewTreeObserver.removeOnGlobalLayoutListener(listener) + } + + fun setBackgroundCompat(view: View, drawable: Drawable?) { + view.background = drawable + } + + fun setBackgroundTransition(view: View, newDrawable: Drawable): TransitionDrawable { + val transition = DrawableUtil.createTransitionDrawable(view.background, newDrawable) + setBackgroundCompat(view, transition) + return transition + } + + fun setBackgroundColorTransition(view: View, @ColorInt newColor: Int): TransitionDrawable { + val oldColor = view.background + + val start = oldColor ?: ColorDrawable(view.solidColor) + val end = ColorDrawable(newColor) + + val transition = DrawableUtil.createTransitionDrawable(start, end) + + setBackgroundCompat(view, transition) + + return transition + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/prefs/ATEPreferenceCategory.kt b/app/src/main/java/io/legado/app/lib/theme/prefs/ATEPreferenceCategory.kt new file mode 100644 index 000000000..2029744ed --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/prefs/ATEPreferenceCategory.kt @@ -0,0 +1,36 @@ +package io.legado.app.lib.theme.prefs + +import android.content.Context +import android.os.Build +import android.preference.PreferenceCategory +import android.util.AttributeSet +import android.view.View +import android.widget.TextView +import androidx.annotation.RequiresApi +import io.legado.app.lib.theme.ThemeStore + + +class ATEPreferenceCategory : PreferenceCategory { + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super( + context, + attrs, + defStyleAttr, + defStyleRes + ) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context) : super(context) + + override fun onBindView(view: View) { + super.onBindView(view) + if (view is TextView) { + view.setTextColor(ThemeStore.accentColor(view.getContext()))//设置title文本的颜色 + } + } + +} diff --git a/app/src/main/java/io/legado/app/lib/theme/prefs/ATESwitchPreference.kt b/app/src/main/java/io/legado/app/lib/theme/prefs/ATESwitchPreference.kt new file mode 100644 index 000000000..8d3ca5f56 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/prefs/ATESwitchPreference.kt @@ -0,0 +1,52 @@ +package io.legado.app.lib.theme.prefs + +import android.content.Context +import android.os.Build +import android.preference.SwitchPreference +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.Switch +import androidx.annotation.RequiresApi +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore +import java.util.* + +class ATESwitchPreference : SwitchPreference { + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super( + context, + attrs, + defStyleAttr, + defStyleRes + ) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context) : super(context) + + override fun onBindView(view: View) { + super.onBindView(view) + + if (view is ViewGroup) { + val queue = LinkedList() + queue.add(view) + while (!queue.isEmpty()) { + val current = queue.removeFirst() + for (i in 0 until current.childCount) { + if (current.getChildAt(i) is Switch) { + ATH.setTint(current.getChildAt(i), ThemeStore.accentColor(view.getContext())) + return + } else if (current.getChildAt(i) is ViewGroup) { + queue.addLast(current.getChildAt(i) as ViewGroup) + } + } + } + } + + } + +} diff --git a/app/src/main/java/io/legado/app/lib/theme/prefs/IconListPreference.kt b/app/src/main/java/io/legado/app/lib/theme/prefs/IconListPreference.kt new file mode 100644 index 000000000..f1389a989 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/prefs/IconListPreference.kt @@ -0,0 +1,95 @@ +package io.legado.app.lib.theme.prefs + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.AlertDialog.Builder +import android.content.Context +import android.graphics.drawable.Drawable +import android.preference.ListPreference +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.CheckedTextView +import android.widget.ImageView +import android.widget.ListAdapter +import io.legado.app.R +import java.util.* + + +class IconListPreference(context: Context, attrs: AttributeSet) : ListPreference(context, attrs) { + + private val mEntryDrawables = ArrayList() + + init { + + val a = context.theme.obtainStyledAttributes(attrs, R.styleable.IconListPreference, 0, 0) + + val drawables: Array + + try { + drawables = a.getTextArray(R.styleable.IconListPreference_icons) + } finally { + a.recycle() + } + + for (drawable in drawables) { + val resId = context.resources.getIdentifier(drawable.toString(), "mipmap", context.packageName) + + val d = context.resources.getDrawable(resId) + + mEntryDrawables.add(d) + } + + widgetLayoutResource = R.layout.view_icon + } + + protected fun createListAdapter(): ListAdapter { + val selectedValue = value + val selectedIndex = findIndexOfValue(selectedValue) + return AppArrayAdapter(context, R.layout.item_icon_preference, entries, mEntryDrawables, selectedIndex) + } + + override fun onBindView(view: View) { + super.onBindView(view) + + val selectedValue = value + val selectedIndex = findIndexOfValue(selectedValue) + + val drawable = mEntryDrawables[selectedIndex] + + (view.findViewById(R.id.preview) as ImageView).setImageDrawable(drawable) + } + + override fun onPrepareDialogBuilder(builder: Builder) { + builder.setAdapter(createListAdapter(), this) + super.onPrepareDialogBuilder(builder) + } + + inner class AppArrayAdapter( + context: Context, textViewResourceId: Int, + objects: Array, imageDrawables: List, + selectedIndex: Int + ) : ArrayAdapter(context, textViewResourceId, objects) { + private var mImageDrawables: List? = null + private var mSelectedIndex = 0 + + init { + mSelectedIndex = selectedIndex + mImageDrawables = imageDrawables + } + + @SuppressLint("ViewHolder") + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val inflater = (context as Activity).layoutInflater + val view = inflater.inflate(R.layout.item_icon_preference, parent, false) + val textView = view.findViewById(R.id.label) as CheckedTextView + textView.text = getItem(position) + textView.isChecked = position == mSelectedIndex + + val imageView = view.findViewById(R.id.icon) as ImageView + imageView.setImageDrawable(mImageDrawables!![position]) + return view + } + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEAccentBgTextView.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEAccentBgTextView.kt new file mode 100644 index 000000000..ae66905dd --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEAccentBgTextView.kt @@ -0,0 +1,33 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView +import io.legado.app.lib.theme.ColorUtil +import io.legado.app.lib.theme.Selector +import io.legado.app.lib.theme.ThemeStore +import io.legado.app.utils.dp + +class ATEAccentBgTextView : AppCompatTextView { + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + background = Selector.shapeBuild() + .setCornerRadius(3.dp) + .setDefaultBgColor(ThemeStore.accentColor(context)) + .setPressedBgColor(ColorUtil.darkenColor(ThemeStore.accentColor(context))) + .create() + setTextColor(Color.WHITE) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEAccentStrokeTextView.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEAccentStrokeTextView.kt new file mode 100644 index 000000000..def0146f3 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEAccentStrokeTextView.kt @@ -0,0 +1,39 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView +import io.legado.app.R +import io.legado.app.lib.theme.Selector +import io.legado.app.lib.theme.ThemeStore +import io.legado.app.utils.dp + +class ATEAccentStrokeTextView : AppCompatTextView { + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + background = Selector.shapeBuild() + .setCornerRadius(3.dp) + .setStrokeWidth(1.dp) + .setDisabledStrokeColor(context.resources.getColor(R.color.md_grey_500)) + .setDefaultStrokeColor(ThemeStore.accentColor(context)) + .setPressedBgColor(context.resources.getColor(R.color.transparent30)) + .create() + setTextColor( + Selector.colorBuild() + .setDefaultColor(ThemeStore.accentColor(context)) + .setDisabledColor(context.resources.getColor(R.color.md_grey_500)) + .create() + ) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEAutoCompleteTextView.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEAutoCompleteTextView.kt new file mode 100644 index 000000000..4a4688fbb --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEAutoCompleteTextView.kt @@ -0,0 +1,33 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatAutoCompleteTextView +import io.legado.app.lib.theme.Selector +import io.legado.app.lib.theme.ThemeStore + + +class ATEAutoCompleteTextView : AppCompatAutoCompleteTextView { + + constructor(context: Context) : super(context) { + init(context) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context) + } + + private fun init(context: Context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + backgroundTintList = Selector.colorBuild() + .setFocusedColor(ThemeStore.accentColor(context)) + .setDefaultColor(ThemeStore.textColorPrimary(context)) + .create() + } + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATECheckBox.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATECheckBox.kt new file mode 100644 index 000000000..15e3b5f14 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATECheckBox.kt @@ -0,0 +1,29 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatCheckBox +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATECheckBox : AppCompatCheckBox { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + ATH.setTint(this, ThemeStore.accentColor(context)) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEEditText.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEEditText.kt new file mode 100644 index 000000000..c70ea4eda --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEEditText.kt @@ -0,0 +1,29 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatEditText +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATEEditText : AppCompatEditText { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + ATH.setTint(this, ThemeStore.accentColor(context)) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEPrimaryTextView.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEPrimaryTextView.kt new file mode 100644 index 000000000..b39385688 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEPrimaryTextView.kt @@ -0,0 +1,28 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATEPrimaryTextView : AppCompatTextView { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + setTextColor(ThemeStore.textColorPrimary(context)) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEProgressBar.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEProgressBar.kt new file mode 100644 index 000000000..3a1809f16 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEProgressBar.kt @@ -0,0 +1,41 @@ +package io.legado.app.lib.theme.view + +import android.annotation.TargetApi +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import android.widget.ProgressBar +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATEProgressBar : ProgressBar { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super( + context, + attrs, + defStyleAttr, + defStyleRes + ) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + ATH.setTint(this, ThemeStore.accentColor(context)) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATERadioButton.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATERadioButton.kt new file mode 100644 index 000000000..583d5e054 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATERadioButton.kt @@ -0,0 +1,29 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatRadioButton +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATERadioButton : AppCompatRadioButton { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + ATH.setTint(this, ThemeStore.accentColor(context)) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATERadioNoButton.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATERadioNoButton.kt new file mode 100644 index 000000000..138f05992 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATERadioNoButton.kt @@ -0,0 +1,37 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatRadioButton +import io.legado.app.lib.theme.Selector +import io.legado.app.lib.theme.ThemeStore +import io.legado.app.utils.dp + +/** + * @author Aidan Follestad (afollestad) + */ +class ATERadioNoButton : AppCompatRadioButton { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + background = Selector.shapeBuild() + .setCornerRadius(3.dp) + .setStrokeWidth(3.dp) + .setCheckedBgColor(ThemeStore.accentColor(context)) + .setCheckedStrokeColor(ThemeStore.accentColor(context)) + .setDefaultStrokeColor(Color.WHITE) + .create() + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATESecondaryTextView.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATESecondaryTextView.kt new file mode 100644 index 000000000..dec5d938e --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATESecondaryTextView.kt @@ -0,0 +1,28 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATESecondaryTextView : AppCompatTextView { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + setTextColor(ThemeStore.textColorSecondary(context)) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATESeekBar.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATESeekBar.kt new file mode 100644 index 000000000..4a49b34f3 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATESeekBar.kt @@ -0,0 +1,29 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatSeekBar +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATESeekBar : AppCompatSeekBar { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + ATH.setTint(this, ThemeStore.accentColor(context)) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEStockSwitch.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEStockSwitch.kt new file mode 100644 index 000000000..a22f3ee47 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEStockSwitch.kt @@ -0,0 +1,34 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.Switch +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATEStockSwitch : Switch { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + ATH.setTint(this, ThemeStore.accentColor(context)) + } + + override fun isShown(): Boolean { + return parent != null && visibility == View.VISIBLE + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATEStrokeTextView.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATEStrokeTextView.kt new file mode 100644 index 000000000..ee6697224 --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATEStrokeTextView.kt @@ -0,0 +1,41 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView +import io.legado.app.R +import io.legado.app.lib.theme.Selector +import io.legado.app.lib.theme.ThemeStore +import io.legado.app.utils.dp + +class ATEStrokeTextView : AppCompatTextView { + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + background = Selector.shapeBuild() + .setCornerRadius(1.dp) + .setStrokeWidth(1.dp) + .setDisabledStrokeColor(context.resources.getColor(R.color.md_grey_500)) + .setDefaultStrokeColor(ThemeStore.textColorSecondary(context)) + .setSelectedStrokeColor(ThemeStore.accentColor(context)) + .setPressedBgColor(context.resources.getColor(R.color.transparent30)) + .create() + setTextColor( + Selector.colorBuild() + .setDefaultColor(ThemeStore.textColorSecondary(context)) + .setSelectedColor(ThemeStore.accentColor(context)) + .setDisabledColor(context.resources.getColor(R.color.md_grey_500)) + .create() + ) + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATESwitch.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATESwitch.kt new file mode 100644 index 000000000..6a718559e --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATESwitch.kt @@ -0,0 +1,34 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.Switch +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore + +/** + * @author Aidan Follestad (afollestad) + */ +class ATESwitch : Switch { + + constructor(context: Context) : super(context) { + init(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun init(context: Context, attrs: AttributeSet?) { + ATH.setTint(this, ThemeStore.accentColor(context)) + } + + override fun isShown(): Boolean { + return parent != null && visibility == View.VISIBLE + } +} diff --git a/app/src/main/java/io/legado/app/lib/theme/view/ATETextInputLayout.kt b/app/src/main/java/io/legado/app/lib/theme/view/ATETextInputLayout.kt new file mode 100644 index 000000000..e09b03e2c --- /dev/null +++ b/app/src/main/java/io/legado/app/lib/theme/view/ATETextInputLayout.kt @@ -0,0 +1,31 @@ +package io.legado.app.lib.theme.view + +import android.content.Context +import android.graphics.Canvas +import android.util.AttributeSet +import com.google.android.material.textfield.TextInputLayout +import io.legado.app.lib.theme.Selector +import io.legado.app.lib.theme.ThemeStore + +class ATETextInputLayout : TextInputLayout { + constructor(context: Context) : super(context) { + init(context) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init(context) + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context) + } + + private fun init(context: Context) { + defaultHintTextColor = Selector.colorBuild().setDefaultColor(ThemeStore.accentColor(context)).create() + } + + override fun draw(canvas: Canvas) { + + super.draw(canvas) + } +} diff --git a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt index 4e47cc604..150e8a027 100644 --- a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt +++ b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt @@ -1,13 +1,19 @@ package io.legado.app.ui.about import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.AndroidViewModel import io.legado.app.R +import io.legado.app.base.BaseActivity +import io.legado.app.utils.getViewModel -class AboutActivity : AppCompatActivity() { +class AboutActivity : BaseActivity() { + override val viewModel: AndroidViewModel + get() = getViewModel(AndroidViewModel::class.java) + override val layoutID: Int + get() = R.layout.activity_about - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_about) + override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { + super.onViewModelCreated(viewModel, savedInstanceState) } + } diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 9e20de414..246533e56 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -21,7 +21,7 @@ import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.app_bar_main.* import org.jetbrains.anko.startActivity -class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { +class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener { override val viewModel: MainViewModel get() = getViewModel(MainViewModel::class.java) diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index 5d0ebadc6..2c282e458 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -2,34 +2,37 @@ package io.legado.app.ui.replacerule import android.os.Bundle import android.util.Log -import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.paging.LivePagedListBuilder import androidx.paging.PagedList import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import io.legado.app.App import io.legado.app.R +import io.legado.app.base.BaseActivity +import io.legado.app.constant.AppConst.APP_TAG import io.legado.app.data.entities.ReplaceRule +import io.legado.app.utils.getViewModel import kotlinx.android.synthetic.main.activity_replace_rule.* import org.jetbrains.anko.doAsync import org.jetbrains.anko.toast -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.ItemTouchHelper -import io.legado.app.constant.AppConst.APP_TAG -import kotlinx.android.synthetic.main.item_relace_rule.* -class ReplaceRuleActivity : AppCompatActivity() { +class ReplaceRuleActivity : BaseActivity() { + override val viewModel: ReplaceRuleViewModel + get() = getViewModel(ReplaceRuleViewModel::class.java) + override val layoutID: Int + get() = R.layout.activity_replace_rule private lateinit var adapter: ReplaceRuleAdapter private var rulesLiveData: LiveData>? = null private var allEnabled = false - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_replace_rule) + override fun onViewModelCreated(viewModel: ReplaceRuleViewModel, savedInstanceState: Bundle?) { + super.onViewModelCreated(viewModel, savedInstanceState) initRecyclerView() initDataObservers() initSwipeToDelete() diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.java b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.java new file mode 100644 index 000000000..fac35d61d --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.java @@ -0,0 +1,11 @@ +package io.legado.app.ui.replacerule; + +import android.app.Application; +import io.legado.app.base.BaseViewModel; +import org.jetbrains.annotations.NotNull; + +public class ReplaceRuleViewModel extends BaseViewModel { + public ReplaceRuleViewModel(@NotNull Application application) { + super(application); + } +} diff --git a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt index 95f4c3be9..d11694683 100644 --- a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt @@ -3,11 +3,10 @@ package io.legado.app.ui.search import android.os.Bundle import io.legado.app.R import io.legado.app.base.BaseActivity -import io.legado.app.data.entities.rule.Rule import io.legado.app.utils.getViewModel import org.jetbrains.anko.startActivity -class SearchActivity : BaseActivity() { +class SearchActivity : BaseActivity() { override val viewModel: SearchViewModel get() = getViewModel(SearchViewModel::class.java) diff --git a/app/src/main/java/io/legado/app/utils/AlertDialogExtensions.kt b/app/src/main/java/io/legado/app/utils/AlertDialogExtensions.kt new file mode 100644 index 000000000..300309ca7 --- /dev/null +++ b/app/src/main/java/io/legado/app/utils/AlertDialogExtensions.kt @@ -0,0 +1,7 @@ +package io.legado.app.utils + +import androidx.appcompat.app.AlertDialog +import io.legado.app.lib.theme.ATH + +val AlertDialog.upTint: AlertDialog + get() = ATH.setAlertDialogTint(this) \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/utils/FloatExtensions.kt b/app/src/main/java/io/legado/app/utils/FloatExtensions.kt new file mode 100644 index 000000000..af9576c00 --- /dev/null +++ b/app/src/main/java/io/legado/app/utils/FloatExtensions.kt @@ -0,0 +1,16 @@ +package io.legado.app.utils + +import android.content.res.Resources + + +val Float.dp: Float // [xxhdpi](360 -> 1080) + get() = android.util.TypedValue.applyDimension( + android.util.TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics + ) + + +val Float.sp: Float // [xxhdpi](360 -> 1080) + get() = android.util.TypedValue.applyDimension( + android.util.TypedValue.COMPLEX_UNIT_SP, this, Resources.getSystem().displayMetrics + ) + diff --git a/app/src/main/java/io/legado/app/utils/IntExtensions.kt b/app/src/main/java/io/legado/app/utils/IntExtensions.kt new file mode 100644 index 000000000..11946c14a --- /dev/null +++ b/app/src/main/java/io/legado/app/utils/IntExtensions.kt @@ -0,0 +1,13 @@ +package io.legado.app.utils + +import android.content.res.Resources + +val Int.dp: Int + get() = android.util.TypedValue.applyDimension( + android.util.TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics + ).toInt() + +val Int.sp: Int + get() = android.util.TypedValue.applyDimension( + android.util.TypedValue.COMPLEX_UNIT_SP, this.toFloat(), Resources.getSystem().displayMetrics + ).toInt() \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/bg_ib_pre.xml b/app/src/main/res/drawable-v21/bg_ib_pre.xml new file mode 100644 index 000000000..9fdfe8c17 --- /dev/null +++ b/app/src/main/res/drawable-v21/bg_ib_pre.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/bg_ib_pre_round.xml b/app/src/main/res/drawable-v21/bg_ib_pre_round.xml new file mode 100644 index 000000000..c43b5c490 --- /dev/null +++ b/app/src/main/res/drawable-v21/bg_ib_pre_round.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_chapter_item_divider.xml b/app/src/main/res/drawable/bg_chapter_item_divider.xml new file mode 100644 index 000000000..423a6bb03 --- /dev/null +++ b/app/src/main/res/drawable/bg_chapter_item_divider.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_edit.xml b/app/src/main/res/drawable/bg_edit.xml new file mode 100644 index 000000000..8a3c4e59e --- /dev/null +++ b/app/src/main/res/drawable/bg_edit.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_ib_pre.xml b/app/src/main/res/drawable/bg_ib_pre.xml new file mode 100644 index 000000000..d44a8b2f0 --- /dev/null +++ b/app/src/main/res/drawable/bg_ib_pre.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_shadow_bottom.png b/app/src/main/res/drawable/bg_shadow_bottom.png new file mode 100644 index 000000000..ac0124b67 Binary files /dev/null and b/app/src/main/res/drawable/bg_shadow_bottom.png differ diff --git a/app/src/main/res/drawable/bg_shadow_bottom_night.png b/app/src/main/res/drawable/bg_shadow_bottom_night.png new file mode 100644 index 000000000..c3036b036 Binary files /dev/null and b/app/src/main/res/drawable/bg_shadow_bottom_night.png differ diff --git a/app/src/main/res/drawable/bg_shadow_top.png b/app/src/main/res/drawable/bg_shadow_top.png new file mode 100644 index 000000000..cb8fee03d Binary files /dev/null and b/app/src/main/res/drawable/bg_shadow_top.png differ diff --git a/app/src/main/res/drawable/bg_shadow_top_night.png b/app/src/main/res/drawable/bg_shadow_top_night.png new file mode 100644 index 000000000..7984c35db Binary files /dev/null and b/app/src/main/res/drawable/bg_shadow_top_night.png differ diff --git a/app/src/main/res/drawable/bg_textfield_search.xml b/app/src/main/res/drawable/bg_textfield_search.xml new file mode 100644 index 000000000..9db913814 --- /dev/null +++ b/app/src/main/res/drawable/bg_textfield_search.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/fastscroll_bubble.xml b/app/src/main/res/drawable/fastscroll_bubble.xml new file mode 100644 index 000000000..566919858 --- /dev/null +++ b/app/src/main/res/drawable/fastscroll_bubble.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/fastscroll_handle.xml b/app/src/main/res/drawable/fastscroll_handle.xml new file mode 100644 index 000000000..7cc91b9ea --- /dev/null +++ b/app/src/main/res/drawable/fastscroll_handle.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/fastscroll_track.xml b/app/src/main/res/drawable/fastscroll_track.xml new file mode 100644 index 000000000..d60893c04 --- /dev/null +++ b/app/src/main/res/drawable/fastscroll_track.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_about.xml b/app/src/main/res/drawable/ic_about.xml new file mode 100644 index 000000000..1c6a7f643 --- /dev/null +++ b/app/src/main/res/drawable/ic_about.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 000000000..67a246c89 --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add_online.xml b/app/src/main/res/drawable/ic_add_online.xml new file mode 100644 index 000000000..1b1387f70 --- /dev/null +++ b/app/src/main/res/drawable/ic_add_online.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrange.xml b/app/src/main/res/drawable/ic_arrange.xml new file mode 100644 index 000000000..130079315 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrange.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_back.xml b/app/src/main/res/drawable/ic_arrow_back.xml new file mode 100644 index 000000000..e1d42df18 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_back.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_drop_down.xml b/app/src/main/res/drawable/ic_arrow_drop_down.xml new file mode 100644 index 000000000..f7def8a08 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_drop_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_drop_up.xml b/app/src/main/res/drawable/ic_arrow_drop_up.xml new file mode 100644 index 000000000..a6d5895e2 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_drop_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_author.xml b/app/src/main/res/drawable/ic_author.xml new file mode 100644 index 000000000..d14ffaf39 --- /dev/null +++ b/app/src/main/res/drawable/ic_author.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_auto_page.xml b/app/src/main/res/drawable/ic_auto_page.xml new file mode 100644 index 000000000..f720abd23 --- /dev/null +++ b/app/src/main/res/drawable/ic_auto_page.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_auto_page_stop.xml b/app/src/main/res/drawable/ic_auto_page_stop.xml new file mode 100644 index 000000000..bebcd8680 --- /dev/null +++ b/app/src/main/res/drawable/ic_auto_page_stop.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_back_last.xml b/app/src/main/res/drawable/ic_back_last.xml new file mode 100644 index 000000000..243d0386e --- /dev/null +++ b/app/src/main/res/drawable/ic_back_last.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_backup.xml b/app/src/main/res/drawable/ic_backup.xml new file mode 100644 index 000000000..8b17ca37c --- /dev/null +++ b/app/src/main/res/drawable/ic_backup.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_book_has.xml b/app/src/main/res/drawable/ic_book_has.xml new file mode 100644 index 000000000..0b7941616 --- /dev/null +++ b/app/src/main/res/drawable/ic_book_has.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_book_last.xml b/app/src/main/res/drawable/ic_book_last.xml new file mode 100644 index 000000000..3ee254c91 --- /dev/null +++ b/app/src/main/res/drawable/ic_book_last.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_book_source_manage.xml b/app/src/main/res/drawable/ic_book_source_manage.xml new file mode 100644 index 000000000..ac9acfc6c --- /dev/null +++ b/app/src/main/res/drawable/ic_book_source_manage.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_bookmark.xml b/app/src/main/res/drawable/ic_bookmark.xml new file mode 100644 index 000000000..0eccdac3f --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_brightness.xml b/app/src/main/res/drawable/ic_brightness.xml new file mode 100644 index 000000000..095df912f --- /dev/null +++ b/app/src/main/res/drawable/ic_brightness.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_cancel.xml b/app/src/main/res/drawable/ic_cancel.xml new file mode 100644 index 000000000..8b2b5cbb4 --- /dev/null +++ b/app/src/main/res/drawable/ic_cancel.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_chapter_list.xml b/app/src/main/res/drawable/ic_chapter_list.xml new file mode 100644 index 000000000..c667fa24e --- /dev/null +++ b/app/src/main/res/drawable/ic_chapter_list.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 000000000..a4f01346c --- /dev/null +++ b/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_check_source.xml b/app/src/main/res/drawable/ic_check_source.xml new file mode 100644 index 000000000..7384b9986 --- /dev/null +++ b/app/src/main/res/drawable/ic_check_source.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_copy.xml b/app/src/main/res/drawable/ic_copy.xml new file mode 100644 index 000000000..54c020707 --- /dev/null +++ b/app/src/main/res/drawable/ic_copy.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_daytime.xml b/app/src/main/res/drawable/ic_daytime.xml new file mode 100644 index 000000000..363491941 --- /dev/null +++ b/app/src/main/res/drawable/ic_daytime.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_disclaimer.xml b/app/src/main/res/drawable/ic_disclaimer.xml new file mode 100644 index 000000000..fd6ad6dd2 --- /dev/null +++ b/app/src/main/res/drawable/ic_disclaimer.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_donate.xml b/app/src/main/res/drawable/ic_donate.xml new file mode 100644 index 000000000..b86f39cb5 --- /dev/null +++ b/app/src/main/res/drawable/ic_donate.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_download.xml b/app/src/main/res/drawable/ic_download.xml new file mode 100644 index 000000000..0e2c939be --- /dev/null +++ b/app/src/main/res/drawable/ic_download.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_download_line.xml b/app/src/main/res/drawable/ic_download_line.xml new file mode 100644 index 000000000..854a3ec99 --- /dev/null +++ b/app/src/main/res/drawable/ic_download_line.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_exchange.xml b/app/src/main/res/drawable/ic_exchange.xml new file mode 100644 index 000000000..2eb01190b --- /dev/null +++ b/app/src/main/res/drawable/ic_exchange.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_expand_less_24dp.xml b/app/src/main/res/drawable/ic_expand_less_24dp.xml new file mode 100644 index 000000000..4ac9d39c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_expand_less_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_expand_more_24dp.xml b/app/src/main/res/drawable/ic_expand_more_24dp.xml new file mode 100644 index 000000000..82e7ee5de --- /dev/null +++ b/app/src/main/res/drawable/ic_expand_more_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_faq.xml b/app/src/main/res/drawable/ic_faq.xml new file mode 100644 index 000000000..00835615d --- /dev/null +++ b/app/src/main/res/drawable/ic_faq.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_find_replace.xml b/app/src/main/res/drawable/ic_find_replace.xml new file mode 100644 index 000000000..3e9c524b1 --- /dev/null +++ b/app/src/main/res/drawable/ic_find_replace.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_folder.xml b/app/src/main/res/drawable/ic_folder.xml new file mode 100644 index 000000000..7f16bd7c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_folder.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_format_line_spacing.xml b/app/src/main/res/drawable/ic_format_line_spacing.xml new file mode 100644 index 000000000..b1586f195 --- /dev/null +++ b/app/src/main/res/drawable/ic_format_line_spacing.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_groups.xml b/app/src/main/res/drawable/ic_groups.xml new file mode 100644 index 000000000..8a46a8295 --- /dev/null +++ b/app/src/main/res/drawable/ic_groups.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_history.xml b/app/src/main/res/drawable/ic_history.xml new file mode 100644 index 000000000..7a17e5efd --- /dev/null +++ b/app/src/main/res/drawable/ic_history.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_import.xml b/app/src/main/res/drawable/ic_import.xml new file mode 100644 index 000000000..f2ebb9ced --- /dev/null +++ b/app/src/main/res/drawable/ic_import.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_interface_setting.xml b/app/src/main/res/drawable/ic_interface_setting.xml new file mode 100644 index 000000000..12377d124 --- /dev/null +++ b/app/src/main/res/drawable/ic_interface_setting.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_last_read.xml b/app/src/main/res/drawable/ic_last_read.xml new file mode 100644 index 000000000..b74e62b0b --- /dev/null +++ b/app/src/main/res/drawable/ic_last_read.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/drawable/ic_launch.xml b/app/src/main/res/drawable/ic_launch.xml new file mode 100644 index 000000000..fd066a65a --- /dev/null +++ b/app/src/main/res/drawable/ic_launch.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_list.xml b/app/src/main/res/drawable/ic_list.xml new file mode 100644 index 000000000..447de0d25 --- /dev/null +++ b/app/src/main/res/drawable/ic_list.xml @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_mail.xml b/app/src/main/res/drawable/ic_mail.xml new file mode 100644 index 000000000..1a141c9ab --- /dev/null +++ b/app/src/main/res/drawable/ic_mail.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_more_vert.xml b/app/src/main/res/drawable/ic_more_vert.xml new file mode 100644 index 000000000..e708a4db1 --- /dev/null +++ b/app/src/main/res/drawable/ic_more_vert.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_network_check.xml b/app/src/main/res/drawable/ic_network_check.xml new file mode 100644 index 000000000..c6ef1b857 --- /dev/null +++ b/app/src/main/res/drawable/ic_network_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_pause_24dp.xml b/app/src/main/res/drawable/ic_pause_24dp.xml new file mode 100644 index 000000000..a4eba8213 --- /dev/null +++ b/app/src/main/res/drawable/ic_pause_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_pause_outline_24dp.xml b/app/src/main/res/drawable/ic_pause_outline_24dp.xml new file mode 100644 index 000000000..556cddae7 --- /dev/null +++ b/app/src/main/res/drawable/ic_pause_outline_24dp.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_play_24dp.xml b/app/src/main/res/drawable/ic_play_24dp.xml new file mode 100644 index 000000000..cf5976472 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_play_outline_24dp.xml b/app/src/main/res/drawable/ic_play_outline_24dp.xml new file mode 100644 index 000000000..b41d03091 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_outline_24dp.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_qq_group.xml b/app/src/main/res/drawable/ic_qq_group.xml new file mode 100644 index 000000000..e97350048 --- /dev/null +++ b/app/src/main/res/drawable/ic_qq_group.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_read_aloud.xml b/app/src/main/res/drawable/ic_read_aloud.xml new file mode 100644 index 000000000..f0ab5860a --- /dev/null +++ b/app/src/main/res/drawable/ic_read_aloud.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_refresh_black_24dp.xml b/app/src/main/res/drawable/ic_refresh_black_24dp.xml new file mode 100644 index 000000000..6f22d5f31 --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh_black_24dp.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_refresh_white_24dp.xml b/app/src/main/res/drawable/ic_refresh_white_24dp.xml new file mode 100644 index 000000000..5c2f53873 --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_restore.xml b/app/src/main/res/drawable/ic_restore.xml new file mode 100644 index 000000000..12c95ce76 --- /dev/null +++ b/app/src/main/res/drawable/ic_restore.xml @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_save.xml b/app/src/main/res/drawable/ic_save.xml new file mode 100644 index 000000000..27c5b36ce --- /dev/null +++ b/app/src/main/res/drawable/ic_save.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_scan.xml b/app/src/main/res/drawable/ic_scan.xml new file mode 100644 index 000000000..ed5b5f201 --- /dev/null +++ b/app/src/main/res/drawable/ic_scan.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_scoring.xml b/app/src/main/res/drawable/ic_scoring.xml new file mode 100644 index 000000000..b716b3ef8 --- /dev/null +++ b/app/src/main/res/drawable/ic_scoring.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 000000000..c7370b2d3 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/drawable/ic_select_all.xml b/app/src/main/res/drawable/ic_select_all.xml new file mode 100644 index 000000000..6e73c7002 --- /dev/null +++ b/app/src/main/res/drawable/ic_select_all.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 000000000..fe8892929 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 000000000..0ed7a1688 --- /dev/null +++ b/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_skip_next.xml b/app/src/main/res/drawable/ic_skip_next.xml new file mode 100644 index 000000000..4ad2389e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_next.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_skip_previous.xml b/app/src/main/res/drawable/ic_skip_previous.xml new file mode 100644 index 000000000..1f558a617 --- /dev/null +++ b/app/src/main/res/drawable/ic_skip_previous.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_stop_black_24dp.xml b/app/src/main/res/drawable/ic_stop_black_24dp.xml new file mode 100644 index 000000000..27eec317a --- /dev/null +++ b/app/src/main/res/drawable/ic_stop_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_swap_outline_24dp.xml b/app/src/main/res/drawable/ic_swap_outline_24dp.xml new file mode 100644 index 000000000..52e3355c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_swap_outline_24dp.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_theme.xml b/app/src/main/res/drawable/ic_theme.xml new file mode 100644 index 000000000..7af5b372e --- /dev/null +++ b/app/src/main/res/drawable/ic_theme.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_time_add_24dp.xml b/app/src/main/res/drawable/ic_time_add_24dp.xml new file mode 100644 index 000000000..9114c9848 --- /dev/null +++ b/app/src/main/res/drawable/ic_time_add_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_timer_black_24dp.xml b/app/src/main/res/drawable/ic_timer_black_24dp.xml new file mode 100644 index 000000000..a7338a31f --- /dev/null +++ b/app/src/main/res/drawable/ic_timer_black_24dp.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_toc.xml b/app/src/main/res/drawable/ic_toc.xml new file mode 100644 index 000000000..e21bcf95c --- /dev/null +++ b/app/src/main/res/drawable/ic_toc.xml @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_top_source.xml b/app/src/main/res/drawable/ic_top_source.xml new file mode 100644 index 000000000..2c973959a --- /dev/null +++ b/app/src/main/res/drawable/ic_top_source.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_translate.xml b/app/src/main/res/drawable/ic_translate.xml new file mode 100644 index 000000000..0d5f42df1 --- /dev/null +++ b/app/src/main/res/drawable/ic_translate.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_tune.xml b/app/src/main/res/drawable/ic_tune.xml new file mode 100644 index 000000000..b9bd12b85 --- /dev/null +++ b/app/src/main/res/drawable/ic_tune.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_update.xml b/app/src/main/res/drawable/ic_update.xml new file mode 100644 index 000000000..0d0e1fe81 --- /dev/null +++ b/app/src/main/res/drawable/ic_update.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_version.xml b/app/src/main/res/drawable/ic_version.xml new file mode 100644 index 000000000..0eccdac3f --- /dev/null +++ b/app/src/main/res/drawable/ic_version.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_view_quilt.xml b/app/src/main/res/drawable/ic_view_quilt.xml new file mode 100644 index 000000000..30f5e324c --- /dev/null +++ b/app/src/main/res/drawable/ic_view_quilt.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_volume_up.xml b/app/src/main/res/drawable/ic_volume_up.xml new file mode 100644 index 000000000..e1e30c7c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_web_outline.xml b/app/src/main/res/drawable/ic_web_outline.xml new file mode 100644 index 000000000..7bcbe7ca6 --- /dev/null +++ b/app/src/main/res/drawable/ic_web_outline.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_web_service_noti.xml b/app/src/main/res/drawable/ic_web_service_noti.xml new file mode 100644 index 000000000..edb48575d --- /dev/null +++ b/app/src/main/res/drawable/ic_web_service_noti.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_web_service_phone.xml b/app/src/main/res/drawable/ic_web_service_phone.xml new file mode 100644 index 000000000..4c8c83a40 --- /dev/null +++ b/app/src/main/res/drawable/ic_web_service_phone.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_read_book.png b/app/src/main/res/drawable/icon_read_book.png new file mode 100644 index 000000000..016b8cf16 Binary files /dev/null and b/app/src/main/res/drawable/icon_read_book.png differ diff --git a/app/src/main/res/drawable/image_welcome.xml b/app/src/main/res/drawable/image_welcome.xml new file mode 100644 index 000000000..4f5b613b8 --- /dev/null +++ b/app/src/main/res/drawable/image_welcome.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/image_yue_du.webp b/app/src/main/res/drawable/image_yue_du.webp new file mode 100644 index 000000000..2d3652cdb Binary files /dev/null and b/app/src/main/res/drawable/image_yue_du.webp differ diff --git a/app/src/main/res/drawable/img_cover_default.jpg b/app/src/main/res/drawable/img_cover_default.jpg new file mode 100644 index 000000000..6504a4e7e Binary files /dev/null and b/app/src/main/res/drawable/img_cover_default.jpg differ diff --git a/app/src/main/res/drawable/img_cover_gs.jpg b/app/src/main/res/drawable/img_cover_gs.jpg new file mode 100644 index 000000000..e4b21cd73 Binary files /dev/null and b/app/src/main/res/drawable/img_cover_gs.jpg differ diff --git a/app/src/main/res/drawable/selector_common_bg.xml b/app/src/main/res/drawable/selector_common_bg.xml new file mode 100644 index 000000000..2dc834803 --- /dev/null +++ b/app/src/main/res/drawable/selector_common_bg.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_fillet_btn_bg.xml b/app/src/main/res/drawable/selector_fillet_btn_bg.xml new file mode 100644 index 000000000..817bb3499 --- /dev/null +++ b/app/src/main/res/drawable/selector_fillet_btn_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_tv_black.xml b/app/src/main/res/drawable/selector_tv_black.xml new file mode 100644 index 000000000..eab98a451 --- /dev/null +++ b/app/src/main/res/drawable/selector_tv_black.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_card_view.xml b/app/src/main/res/drawable/shape_card_view.xml new file mode 100644 index 000000000..20d4d7656 --- /dev/null +++ b/app/src/main/res/drawable/shape_card_view.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_fillet_btn.xml b/app/src/main/res/drawable/shape_fillet_btn.xml new file mode 100644 index 000000000..5a29bf37e --- /dev/null +++ b/app/src/main/res/drawable/shape_fillet_btn.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_fillet_btn_press.xml b/app/src/main/res/drawable/shape_fillet_btn_press.xml new file mode 100644 index 000000000..417341636 --- /dev/null +++ b/app/src/main/res/drawable/shape_fillet_btn_press.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_pop_checkaddshelf_bg.xml b/app/src/main/res/drawable/shape_pop_checkaddshelf_bg.xml new file mode 100644 index 000000000..951803c38 --- /dev/null +++ b/app/src/main/res/drawable/shape_pop_checkaddshelf_bg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_radius_1dp.xml b/app/src/main/res/drawable/shape_radius_1dp.xml new file mode 100644 index 000000000..ffcc8c82c --- /dev/null +++ b/app/src/main/res/drawable/shape_radius_1dp.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_space_divider.xml b/app/src/main/res/drawable/shape_space_divider.xml new file mode 100644 index 000000000..9a699b89d --- /dev/null +++ b/app/src/main/res/drawable/shape_space_divider.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_text_cursor.xml b/app/src/main/res/drawable/shape_text_cursor.xml new file mode 100644 index 000000000..721c358c6 --- /dev/null +++ b/app/src/main/res/drawable/shape_text_cursor.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 92789a7fb..8e1943029 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,29 +1,24 @@ - - - - - + + + + - - - - + app:headerLayout="@layout/nav_header_main" + app:menu="@menu/activity_main_drawer"/> - - + diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 4b23ca3d4..33b3b9ddb 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -1,39 +1,32 @@ - - - - + + + - + android:layout_height="0dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/title_bar"> - - - - - - + android:layout_height="match_parent"/> - - + - + diff --git a/app/src/main/res/layout/item_icon_preference.xml b/app/src/main/res/layout/item_icon_preference.xml new file mode 100644 index 000000000..a2063a71f --- /dev/null +++ b/app/src/main/res/layout/item_icon_preference.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_icon.xml b/app/src/main/res/layout/view_icon.xml new file mode 100644 index 000000000..d0247c6ef --- /dev/null +++ b/app/src/main/res/layout/view_icon.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index e54680451..16b48ec19 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + - + + + + + + + + + + - + + + + + + + + +