diff --git a/app/build.gradle b/app/build.gradle
index 6d576b873..f19373953 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -152,7 +152,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
//room
- def room_version = '2.2.6'
+ def room_version = '2.3.0'
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
testImplementation "androidx.room:room-testing:$room_version"
@@ -178,10 +178,7 @@ dependencies {
implementation 'com.github.gedoor:rhino-android:1.5'
//网络
- //noinspection GradleDependency
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
- implementation 'com.ljx.rxhttp:rxhttp:2.5.7'
- kapt 'com.ljx.rxhttp:rxhttp-compiler:2.5.7'
//Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
@@ -191,7 +188,7 @@ dependencies {
implementation 'org.nanohttpd:nanohttpd-websocket:2.3.1'
//二维码
- implementation 'com.king.zxing:zxing-lite:2.0.2'
+ implementation 'com.king.zxing:zxing-lite:2.0.3'
//颜色选择
implementation 'com.jaredrummler:colorpicker:1.1.0'
diff --git a/app/src/main/assets/defaultData/httpTTS.json b/app/src/main/assets/defaultData/httpTTS.json
index de70e5196..08d3386cd 100644
--- a/app/src/main/assets/defaultData/httpTTS.json
+++ b/app/src/main/assets/defaultData/httpTTS.json
@@ -1,147 +1,237 @@
[
+ {
+ "id": -100,
+ "name": "0",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4127&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=11&vol=5&aue=6&pit=3&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -99,
+ "name": "zaixianai.cn",
+ "url": "\nlet url='https://www.zaixianai.cn/voiceCompose';\n\nlet ua=\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36\";\n\nlet doc=java.get(url,{\"User-Agent\":ua});\nlet cookie=String(doc.header(\"set-cookie\")).match(/laravel_session=[^\\n]+/)[0];\nlet token=String(doc.body()).match(/token=\"([^\"]+)/)[1];\n\nurl='https://www.zaixianai.cn/Api_getVoice,'+JSON.stringify({\n\"method\": \"POST\",\n\"body\": \"content=\" + java.encodeURI(speakText) + \"&volume=50&speech_rate=0&voice=Aixia&_token=\"+token,\n\"headers\": {\n\"User-Agent\": ua,\n\"cookie\": cookie\n}\n});\n\nlet res=java.ajax(url);\n\n'https://www.zaixianai.cn/voice/'+JSON.parse(res).data.file_name+','+JSON.stringify({\n\"headers\": {\n\"User-Agent\": ua,\n\"accept\": \"*/*\",\n\"referer\": \"https://www.zaixianai.cn/voiceCompose\",\n\"cookie\": cookie,\n\"accept-encoding\": \"identity;q=1, *;q=0\"\n}\n})\n"
+ },
+ {
+ "id": -98,
+ "name": "台湾女声",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4007&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
{
"id": -1,
"name": "度丫丫",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -2,
"name": "度博文①",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=106&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=106&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -3,
"name": "度博文②",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4106&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4106&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -4,
"name": "度博文③",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5106&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5106&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -97,
+ "name": "度小乔",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=1117&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=3&_res_tag_=audio\"\n}"
},
{
"id": -5,
"name": "度小娇",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -6,
"name": "度小宇",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=1&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=2&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -7,
"name": "度小童",
"url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=110&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
},
+ {
+ "id": -96,
+ "name": "度小童",
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=110&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -95,
+ "name": "度小粤",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=0&cuid=baidu_speech_demo&idx=1&cod=2&lan=cte&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
{
"id": -8,
"name": "度小美",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=0&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=0&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -94,
+ "name": "度小芳",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4125&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -9,
"name": "度小萌",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=111&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=111&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -93,
+ "name": "度小贤",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4115&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=5&_res_tag_=audio\"}"
+ },
+ {
+ "id": -92,
+ "name": "度小雯",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5100&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -10,
"name": "度小鹿①",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4118&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4118&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
- "id": -11,
+ "id": -12,
"name": "度小鹿②",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5118&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4119&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
- "id": -12,
+ "id": -11,
"name": "度小鹿③",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4119&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5118&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -91,
+ "name": "度灵儿",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5105&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -13,
"name": "度米朵①",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=103&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=103&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -14,
"name": "度米朵②",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4103&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4103&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -15,
"name": "度逍遥-基础",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=3&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=3&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -16,
"name": "度逍遥-精品①",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4003&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4003&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -17,
"name": "度逍遥-精品②",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5003&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5003&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -18,
"name": "情感女声",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4105&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4105&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -19,
"name": "情感男声",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4115&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4115&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -20,
"name": "标准女声",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4100&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4100&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -90,
+ "name": "标准女声-基础",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=100&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -21,
"name": "标准男声",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4121&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4121&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160=&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
- "id": -22,
- "name": "治愈女声",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5120&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "id": -89,
+ "name": "温柔女声",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4126&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
- "id": -23,
- "name": "治愈男声",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5121&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "id": -88,
+ "name": "甜美女声①",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=1200&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=&rate=32=5&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -24,
- "name": "甜美女声①",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4117&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "name": "甜美女声②",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4117&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -25,
- "name": "甜美女声②",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5117&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "name": "甜美女声③",
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5117&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -22,
+ "name": "电台女声",
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5120&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=&rate=32=5&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -23,
+ "name": "电台男声",
+ "url": "http://tsn.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5121&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -26,
"name": "百度主持",
"url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=9&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
},
+ {
+ "id": -87,
+ "name": "百度主持",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4127&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=11&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -86,
+ "name": "百度解说①",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4123&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=12&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -85,
+ "name": "百度解说②",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4128&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=12&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -84,
+ "name": "百度解说③",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4129&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=12&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
{
"id": -27,
"name": "百度评书①",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=6&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=6&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -28,
"name": "百度评书②",
- "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4114&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=301&vol=5&pit=5&_res_tag_=audio\"\n}"
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=4114&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
+ },
+ {
+ "id": -83,
+ "name": "萝莉少女音",
+ "url": "http://tts.baidu.com/text2audio,{\n \"method\": \"POST\",\n \"body\": \"tex={{java.encodeURI(java.encodeURI(speakText))}}&spd={{(speakSpeed + 5) / 10 + 4}}&per=5201&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=160&vol=5&aue=6&pit=5&_res_tag_=audio\"\n}"
},
{
"id": -29,
"name": "阿里云语音",
"url": "/*播音人Aiting可改其他https://cdn.jsdelivr.net/gh/Celeter/build/.github/scripts/speaker.json,详见https://ai.aliyun.com/nls/tts*/;eval(''+java.ajax('https://cdn.jsdelivr.net/gh/Celeter/build/.github/scripts/ttsDemo.js'));ttsDemo(speakText,speakSpeed,'Aiting')"
}
-]
+]
\ No newline at end of file
diff --git a/app/src/main/assets/defaultData/readConfig.json b/app/src/main/assets/defaultData/readConfig.json
index 968141a0c..cd62a05be 100644
--- a/app/src/main/assets/defaultData/readConfig.json
+++ b/app/src/main/assets/defaultData/readConfig.json
@@ -1,5 +1,56 @@
[
{
+ "bgStr": "#ffc0edc6",
+ "bgStrEInk": "#FFFFFF",
+ "bgStrNight": "#000000",
+ "bgType": 0,
+ "bgTypeEInk": 0,
+ "bgTypeNight": 0,
+ "darkStatusIcon": true,
+ "darkStatusIconEInk": true,
+ "darkStatusIconNight": false,
+ "footerMode": 0,
+ "footerPaddingBottom": 10,
+ "footerPaddingLeft": 13,
+ "footerPaddingRight": 17,
+ "footerPaddingTop": 0,
+ "headerMode": 0,
+ "headerPaddingBottom": 0,
+ "headerPaddingLeft": 19,
+ "headerPaddingRight": 16,
+ "headerPaddingTop": 10,
+ "letterSpacing": 0,
+ "lineSpacingExtra": 10,
+ "name": "微信读书",
+ "paddingBottom": 4,
+ "paddingLeft": 22,
+ "paddingRight": 22,
+ "paddingTop": 5,
+ "pageAnim": 3,
+ "pageAnimEInk": 3,
+ "paragraphIndent": " ",
+ "paragraphSpacing": 6,
+ "showFooterLine": true,
+ "showHeaderLine": true,
+ "textBold": 0,
+ "textColor": "#ff0b0b0b",
+ "textColorEInk": "#000000",
+ "textColorNight": "#ADADAD",
+ "textSize": 24,
+ "tipColor": -10461088,
+ "tipFooterLeft": 7,
+ "tipFooterMiddle": 0,
+ "tipFooterRight": 6,
+ "tipHeaderLeft": 1,
+ "tipHeaderMiddle": 0,
+ "tipHeaderRight": 2,
+ "titleBottomSpacing": 0,
+ "titleMode": 0,
+ "titleSize": 4,
+ "titleTopSpacing": 0
+ },
+ {
+ "name": "预设1",
"bgStr": "#FFFFFF",
"bgStrNight": "#000000",
"textColor": "#000000",
@@ -10,6 +61,7 @@
"darkStatusIconNight": false
},
{
+ "name": "预设2",
"bgStr": "#DDC090",
"bgStrNight": "#3C3F43",
"textColor": "#3E3422",
@@ -20,6 +72,7 @@
"darkStatusIconNight": false
},
{
+ "name": "预设3",
"bgStr": "#C2D8AA",
"bgStrNight": "#3C3F43",
"textColor": "#596C44",
@@ -30,6 +83,7 @@
"darkStatusIconNight": false
},
{
+ "name": "预设4",
"bgStr": "#DBB8E2",
"bgStrNight": "#3C3F43",
"textColor": "#68516C",
@@ -40,6 +94,7 @@
"darkStatusIconNight": false
},
{
+ "name": "预设5",
"bgStr": "#ABCEE0",
"bgStrNight": "#3C3F43",
"textColor": "#3D4C54",
diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md
index 8b4c3a008..11a6dcfcd 100644
--- a/app/src/main/assets/updateLog.md
+++ b/app/src/main/assets/updateLog.md
@@ -3,12 +3,22 @@
* 关注合作公众号 **[小说拾遗]** 获取好看的小说。
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
-**2021/04/18**
-* epub插图,epublib优化 by ag2s20150909
+**2021/05/04**
+* 修复bug
+* url参数添加重置次数,retry
+* 修改默认tts, 手动导入
+* 升级android studio
+
+**2021/04/30**
+* epub插图,epublib优化,图片解码优化,epub读取导出优化。by ag2s20150909
+* 添加高刷设置
+* 其它一些优化
+* pro版本被play商店下架了,先把pro设置图片背景的功能开放到所有版本,使用pro版本的可以使用备份恢复功能切换最新版本
**2021/04/16**
* 去掉google统计,解决华为手机使用崩溃的bug
* 添加规则订阅时判断重复提醒
+* 添加恢复预设布局的功能, 添加一个微信读书布局作为预设布局
**2021/04/13**
* 修复导入布局bug
diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt
index 6345cf1b1..5791d7007 100644
--- a/app/src/main/java/io/legado/app/App.kt
+++ b/app/src/main/java/io/legado/app/App.kt
@@ -7,7 +7,6 @@ import android.content.res.Configuration
import android.os.Build
import androidx.multidex.MultiDexApplication
import com.jeremyliao.liveeventbus.LiveEventBus
-import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.channelIdDownload
import io.legado.app.constant.AppConst.channelIdReadAloud
import io.legado.app.constant.AppConst.channelIdWeb
@@ -15,10 +14,8 @@ import io.legado.app.help.ActivityHelp
import io.legado.app.help.AppConfig
import io.legado.app.help.CrashHandler
import io.legado.app.help.ThemeConfig.applyDayNight
-import io.legado.app.help.http.HttpHelper
import io.legado.app.utils.LanguageUtils
import io.legado.app.utils.defaultSharedPreferences
-import rxhttp.wrapper.param.RxHttp
class App : MultiDexApplication() {
@@ -26,10 +23,6 @@ class App : MultiDexApplication() {
super.onCreate()
CrashHandler(this)
LanguageUtils.setConfiguration(this)
- RxHttp.init(HttpHelper.client, BuildConfig.DEBUG)
- RxHttp.setOnParamAssembly {
- it.addHeader(AppConst.UA_NAME, AppConfig.userAgent)
- }
createNotificationChannels()
applyDayNight(this)
LiveEventBus.config()
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 ef9c23953..4d2707444 100644
--- a/app/src/main/java/io/legado/app/base/BaseActivity.kt
+++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt
@@ -14,6 +14,7 @@ import androidx.viewbinding.ViewBinding
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.AppConst
+import io.legado.app.constant.PreferKey
import io.legado.app.constant.Theme
import io.legado.app.help.AppConfig
import io.legado.app.help.ThemeConfig
@@ -66,6 +67,26 @@ abstract class BaseActivity(
}
override fun onCreate(savedInstanceState: Bundle?) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
+ getPrefBoolean(PreferKey.highBrush)
+ ) {
+ /**
+ * 添加高刷新率支持
+ */
+ // 获取系统window支持的模式
+ @Suppress("DEPRECATION")
+ val modes = window.windowManager.defaultDisplay.supportedModes
+ // 对获取的模式,基于刷新率的大小进行排序,从小到大排序
+ modes.sortBy {
+ it.refreshRate
+ }
+ window.let {
+ val lp = it.attributes
+ // 取出最大的那一个刷新率,直接设置给window
+ lp.preferredDisplayModeId = modes.last().modeId
+ it.attributes = lp
+ }
+ }
window.decorView.disableAutoFill()
initTheme()
super.onCreate(savedInstanceState)
@@ -152,11 +173,9 @@ abstract class BaseActivity(
ATH.applyBackgroundTint(window.decorView)
}
}
- if (AppConfig.isGooglePlay) {
- ThemeConfig.getBgImage(this)?.let {
- kotlin.runCatching {
- window.decorView.background = it
- }
+ ThemeConfig.getBgImage(this)?.let {
+ kotlin.runCatching {
+ window.decorView.background = it
}
}
}
diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt
index 0d1e5ef7e..651830256 100644
--- a/app/src/main/java/io/legado/app/constant/PreferKey.kt
+++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt
@@ -73,6 +73,7 @@ object PreferKey {
const val exportUseReplace = "exportUseReplace"
const val useZhLayout = "useZhLayout"
const val fullScreenGesturesSupport = "fullScreenGesturesSupport"
+ const val highBrush = "highBrush"
const val cPrimary = "colorPrimary"
const val cAccent = "colorAccent"
diff --git a/app/src/main/java/io/legado/app/data/AppDatabase.kt b/app/src/main/java/io/legado/app/data/AppDatabase.kt
index 70c05de42..8add07077 100644
--- a/app/src/main/java/io/legado/app/data/AppDatabase.kt
+++ b/app/src/main/java/io/legado/app/data/AppDatabase.kt
@@ -10,6 +10,7 @@ import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.androidId
import io.legado.app.data.dao.*
import io.legado.app.data.entities.*
+import io.legado.app.help.AppConfig
import splitties.init.appCtx
import java.util.*
@@ -88,6 +89,13 @@ abstract class AppDatabase : RoomDatabase() {
"""insert into book_groups(groupId, groupName, 'order', show) select ${AppConst.bookGroupNoneId}, '未分组', -7, 1
where not exists (select * from book_groups where groupId = ${AppConst.bookGroupNoneId})"""
)
+ if (AppConfig.isGooglePlay) {
+ db.execSQL(
+ """
+ delete from rssSources where sourceUrl = 'https://github.com/gedoor/legado/releases'
+ """
+ )
+ }
}
}
diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt
index 5093c05c1..2ee782122 100644
--- a/app/src/main/java/io/legado/app/data/entities/Book.kt
+++ b/app/src/main/java/io/legado/app/data/entities/Book.kt
@@ -107,6 +107,8 @@ data class Book(
fun getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0)
+ fun getDisplayTag() = if (customTag.isNullOrBlank()) kind else customTag
+
fun getDisplayCover() = if (customCoverUrl.isNullOrEmpty()) coverUrl else customCoverUrl
fun getDisplayIntro() = if (customIntro.isNullOrEmpty()) intro else customIntro
diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt
index 64d8d0893..851660fdf 100644
--- a/app/src/main/java/io/legado/app/help/JsExtensions.kt
+++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt
@@ -4,9 +4,7 @@ import android.net.Uri
import android.util.Base64
import androidx.annotation.Keep
import io.legado.app.constant.AppConst.dateFormat
-import io.legado.app.help.http.CookieStore
-import io.legado.app.help.http.SSLHelper
-import io.legado.app.help.http.StrResponse
+import io.legado.app.help.http.*
import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.analyzeRule.QueryTTF
@@ -16,8 +14,6 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.jsoup.Connection
import org.jsoup.Jsoup
-import rxhttp.wrapper.param.RxHttp
-import rxhttp.wrapper.param.toByteArray
import splitties.init.appCtx
import java.io.File
import java.net.URLEncoder
@@ -292,7 +288,7 @@ interface JsExtensions {
str.isAbsUrl() -> runBlocking {
var x = CacheManager.getByteArray(key)
if (x == null) {
- x = RxHttp.get(str).toByteArray().await()
+ x = okHttpClient.newCall { url(str) }.bytes()
x.let {
CacheManager.put(key, it)
}
diff --git a/app/src/main/java/io/legado/app/help/http/HttpHelper.kt b/app/src/main/java/io/legado/app/help/http/HttpHelper.kt
index 6a9f7f8b9..cdf6a03bc 100644
--- a/app/src/main/java/io/legado/app/help/http/HttpHelper.kt
+++ b/app/src/main/java/io/legado/app/help/http/HttpHelper.kt
@@ -11,82 +11,28 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
-@Suppress("unused")
-object HttpHelper {
-
- private val proxyClientCache: ConcurrentHashMap by lazy {
- ConcurrentHashMap()
- }
-
- val client: OkHttpClient by lazy {
-
- val specs = arrayListOf(
- ConnectionSpec.MODERN_TLS,
- ConnectionSpec.COMPATIBLE_TLS,
- ConnectionSpec.CLEARTEXT
- )
-
- val builder = OkHttpClient.Builder()
- .connectTimeout(15, TimeUnit.SECONDS)
- .writeTimeout(15, TimeUnit.SECONDS)
- .readTimeout(15, TimeUnit.SECONDS)
- .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager)
- .retryOnConnectionFailure(true)
- .hostnameVerifier(SSLHelper.unsafeHostnameVerifier)
- .connectionSpecs(specs)
- .followRedirects(true)
- .followSslRedirects(true)
- .addInterceptor(getHeaderInterceptor())
-
- builder.build()
- }
+private val proxyClientCache: ConcurrentHashMap by lazy {
+ ConcurrentHashMap()
+}
- /**
- * 缓存代理okHttp
- */
- fun getProxyClient(proxy: String? = null): OkHttpClient {
- if (proxy.isNullOrBlank()) {
- return client
- }
- proxyClientCache[proxy]?.let {
- return it
- }
- val r = Regex("(http|socks4|socks5)://(.*):(\\d{2,5})(@.*@.*)?")
- val ms = r.findAll(proxy)
- val group = ms.first()
- var username = "" //代理服务器验证用户名
- var password = "" //代理服务器验证密码
- val type = if (group.groupValues[1] == "http") "http" else "socks"
- val host = group.groupValues[2]
- val port = group.groupValues[3].toInt()
- if (group.groupValues[4] != "") {
- username = group.groupValues[4].split("@")[1]
- password = group.groupValues[4].split("@")[2]
- }
- if (type != "direct" && host != "") {
- val builder = client.newBuilder()
- if (type == "http") {
- builder.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(host, port)))
- } else {
- builder.proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress(host, port)))
- }
- if (username != "" && password != "") {
- builder.proxyAuthenticator { _, response -> //设置代理服务器账号密码
- val credential: String = Credentials.basic(username, password)
- response.request.newBuilder()
- .header("Proxy-Authorization", credential)
- .build()
- }
- }
- val proxyClient = builder.build()
- proxyClientCache[proxy] = proxyClient
- return proxyClient
- }
- return client
- }
+val okHttpClient: OkHttpClient by lazy {
+ val specs = arrayListOf(
+ ConnectionSpec.MODERN_TLS,
+ ConnectionSpec.COMPATIBLE_TLS,
+ ConnectionSpec.CLEARTEXT
+ )
- private fun getHeaderInterceptor(): Interceptor {
- return Interceptor { chain ->
+ val builder = OkHttpClient.Builder()
+ .connectTimeout(15, TimeUnit.SECONDS)
+ .writeTimeout(15, TimeUnit.SECONDS)
+ .readTimeout(15, TimeUnit.SECONDS)
+ .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager)
+ .retryOnConnectionFailure(true)
+ .hostnameVerifier(SSLHelper.unsafeHostnameVerifier)
+ .connectionSpecs(specs)
+ .followRedirects(true)
+ .followSslRedirects(true)
+ .addInterceptor(Interceptor { chain ->
val request = chain.request()
.newBuilder()
.addHeader("Keep-Alive", "300")
@@ -94,28 +40,72 @@ object HttpHelper {
.addHeader("Cache-Control", "no-cache")
.build()
chain.proceed(request)
+ })
+
+ builder.build()
+}
+
+/**
+ * 缓存代理okHttp
+ */
+fun getProxyClient(proxy: String? = null): OkHttpClient {
+ if (proxy.isNullOrBlank()) {
+ return okHttpClient
+ }
+ proxyClientCache[proxy]?.let {
+ return it
+ }
+ val r = Regex("(http|socks4|socks5)://(.*):(\\d{2,5})(@.*@.*)?")
+ val ms = r.findAll(proxy)
+ val group = ms.first()
+ var username = "" //代理服务器验证用户名
+ var password = "" //代理服务器验证密码
+ val type = if (group.groupValues[1] == "http") "http" else "socks"
+ val host = group.groupValues[2]
+ val port = group.groupValues[3].toInt()
+ if (group.groupValues[4] != "") {
+ username = group.groupValues[4].split("@")[1]
+ password = group.groupValues[4].split("@")[2]
+ }
+ if (type != "direct" && host != "") {
+ val builder = okHttpClient.newBuilder()
+ if (type == "http") {
+ builder.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(host, port)))
+ } else {
+ builder.proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress(host, port)))
+ }
+ if (username != "" && password != "") {
+ builder.proxyAuthenticator { _, response -> //设置代理服务器账号密码
+ val credential: String = Credentials.basic(username, password)
+ response.request.newBuilder()
+ .header("Proxy-Authorization", credential)
+ .build()
+ }
}
+ val proxyClient = builder.build()
+ proxyClientCache[proxy] = proxyClient
+ return proxyClient
}
+ return okHttpClient
+}
- suspend fun ajax(params: AjaxWebView.AjaxParams): StrResponse =
- suspendCancellableCoroutine { block ->
- val webView = AjaxWebView()
- block.invokeOnCancellation {
- webView.destroyWebView()
- }
- webView.callback = object : AjaxWebView.Callback() {
- override fun onResult(response: StrResponse) {
+suspend fun getWebViewSrc(params: AjaxWebView.AjaxParams): StrResponse =
+ suspendCancellableCoroutine { block ->
+ val webView = AjaxWebView()
+ block.invokeOnCancellation {
+ webView.destroyWebView()
+ }
+ webView.callback = object : AjaxWebView.Callback() {
+ override fun onResult(response: StrResponse) {
- if (!block.isCompleted)
- block.resume(response)
- }
+ if (!block.isCompleted)
+ block.resume(response)
+ }
- override fun onError(error: Throwable) {
- if (!block.isCompleted)
- block.cancel(error)
- }
+ override fun onError(error: Throwable) {
+ if (!block.isCompleted)
+ block.cancel(error)
}
- webView.load(params)
}
-
-}
+ webView.load(params)
+ }
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt b/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt
new file mode 100644
index 000000000..4dec4bf3e
--- /dev/null
+++ b/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt
@@ -0,0 +1,122 @@
+package io.legado.app.help.http
+
+import io.legado.app.constant.AppConst
+import io.legado.app.help.AppConfig
+import io.legado.app.utils.EncodingDetect
+import io.legado.app.utils.UTF8BOMFighter
+import kotlinx.coroutines.suspendCancellableCoroutine
+import okhttp3.*
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.RequestBody.Companion.toRequestBody
+import java.io.IOException
+import java.nio.charset.Charset
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+
+suspend fun OkHttpClient.newCall(
+ retry: Int = 0,
+ builder: Request.Builder.() -> Unit
+): ResponseBody {
+ val requestBuilder = Request.Builder()
+ requestBuilder.header(AppConst.UA_NAME, AppConfig.userAgent)
+ requestBuilder.apply(builder)
+ var response: Response? = null
+ for (i in 0..retry) {
+ response = this.newCall(requestBuilder.build()).await()
+ if (response.isSuccessful) {
+ return response.body!!
+ }
+ }
+ throw IOException(response!!.message)
+}
+
+suspend fun OkHttpClient.newCallStrResponse(
+ retry: Int = 0,
+ builder: Request.Builder.() -> Unit
+): StrResponse {
+ val requestBuilder = Request.Builder()
+ requestBuilder.header(AppConst.UA_NAME, AppConfig.userAgent)
+ requestBuilder.apply(builder)
+ var response: Response? = null
+ for (i in 0..retry) {
+ response = this.newCall(requestBuilder.build()).await()
+ if (response.isSuccessful) {
+ return StrResponse(response, response.body!!.text())
+ }
+ }
+ throw IOException(response!!.message)
+}
+
+suspend fun Call.await(): Response = suspendCancellableCoroutine { block ->
+
+ block.invokeOnCancellation {
+ cancel()
+ }
+
+ enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ block.resumeWithException(e)
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ block.resume(response)
+ }
+ })
+
+}
+
+fun ResponseBody.text(encode: String? = null): String {
+ val responseBytes = UTF8BOMFighter.removeUTF8BOM(bytes())
+ var charsetName: String? = encode
+
+ charsetName?.let {
+ return String(responseBytes, Charset.forName(charsetName))
+ }
+
+ //根据http头判断
+ contentType()?.charset()?.let {
+ return String(responseBytes, it)
+ }
+
+ //根据内容判断
+ charsetName = EncodingDetect.getHtmlEncode(responseBytes)
+ return String(responseBytes, Charset.forName(charsetName))
+}
+
+fun Request.Builder.addHeaders(headers: Map) {
+ headers.forEach {
+ addHeader(it.key, it.value)
+ }
+}
+
+fun Request.Builder.get(url: String, queryMap: Map, encoded: Boolean = false) {
+ val httpBuilder = url.toHttpUrl().newBuilder()
+ queryMap.forEach {
+ if (encoded) {
+ httpBuilder.addEncodedQueryParameter(it.key, it.value)
+ } else {
+ httpBuilder.addQueryParameter(it.key, it.value)
+ }
+ }
+ url(httpBuilder.build())
+}
+
+fun Request.Builder.postForm(form: Map, encoded: Boolean = false) {
+ val formBody = FormBody.Builder()
+ form.forEach {
+ if (encoded) {
+ formBody.addEncoded(it.key, it.value)
+ } else {
+ formBody.add(it.key, it.value)
+ }
+ }
+ post(formBody.build())
+}
+
+fun Request.Builder.postJson(json: String?) {
+ json?.let {
+ val requestBody = json.toRequestBody("application/json; charset=UTF-8".toMediaType())
+ post(requestBody)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/http/RetryInterceptor.kt b/app/src/main/java/io/legado/app/help/http/RetryInterceptor.kt
new file mode 100644
index 000000000..00c4a0881
--- /dev/null
+++ b/app/src/main/java/io/legado/app/help/http/RetryInterceptor.kt
@@ -0,0 +1,12 @@
+package io.legado.app.help.http
+
+import okhttp3.Interceptor
+import okhttp3.Response
+
+class RetryInterceptor : Interceptor {
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ TODO("Not yet implemented")
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/http/parser/ByteParser.kt b/app/src/main/java/io/legado/app/help/http/parser/ByteParser.kt
deleted file mode 100644
index b2812d41e..000000000
--- a/app/src/main/java/io/legado/app/help/http/parser/ByteParser.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.legado.app.help.http.parser
-
-import okhttp3.Response
-import rxhttp.wrapper.annotation.Parser
-
-@Parser(name = "ByteArray")
-class ByteParser : rxhttp.wrapper.parse.Parser {
-
- override fun onParse(response: Response): ByteArray {
- return response.body!!.bytes()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/http/parser/InputStreamParser.kt b/app/src/main/java/io/legado/app/help/http/parser/InputStreamParser.kt
deleted file mode 100644
index 9ca0ffe4d..000000000
--- a/app/src/main/java/io/legado/app/help/http/parser/InputStreamParser.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.legado.app.help.http.parser
-
-import okhttp3.Response
-import rxhttp.wrapper.annotation.Parser
-import java.io.InputStream
-
-@Parser(name = "InputStream")
-class InputStreamParser : rxhttp.wrapper.parse.Parser {
-
- override fun onParse(response: Response): InputStream {
- return response.body!!.byteStream()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/http/parser/StrResponseParser.kt b/app/src/main/java/io/legado/app/help/http/parser/StrResponseParser.kt
deleted file mode 100644
index 9868b9dc8..000000000
--- a/app/src/main/java/io/legado/app/help/http/parser/StrResponseParser.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package io.legado.app.help.http.parser
-
-import io.legado.app.help.http.StrResponse
-import io.legado.app.utils.EncodingDetect
-import io.legado.app.utils.UTF8BOMFighter
-import okhttp3.Response
-import rxhttp.wrapper.annotation.Parser
-import rxhttp.wrapper.exception.HttpStatusCodeException
-import java.nio.charset.Charset
-
-@Parser(name = "StrResponse")
-class StrResponseParser(private val encode: String? = null) :
- rxhttp.wrapper.parse.Parser {
-
- override fun onParse(response: Response): StrResponse {
- val body = getString(response)
- return StrResponse(response, body)
- }
-
- private fun getString(response: Response): String {
-
- val responseBody = response.body ?: throw HttpStatusCodeException(response, "内容为空")
- val responseBytes = UTF8BOMFighter.removeUTF8BOM(responseBody.bytes())
- var charsetName: String? = encode
-
- charsetName?.let {
- return String(responseBytes, Charset.forName(charsetName))
- }
-
- //根据http头判断
- responseBody.contentType()?.charset()?.let {
- return String(responseBytes, it)
- }
-
- //根据内容判断
- charsetName = EncodingDetect.getHtmlEncode(responseBytes)
- return String(responseBytes, Charset.forName(charsetName))
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/http/parser/TextParser.kt b/app/src/main/java/io/legado/app/help/http/parser/TextParser.kt
deleted file mode 100644
index 031ad9d33..000000000
--- a/app/src/main/java/io/legado/app/help/http/parser/TextParser.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package io.legado.app.help.http.parser
-
-import io.legado.app.utils.EncodingDetect
-import io.legado.app.utils.UTF8BOMFighter
-import okhttp3.Response
-import rxhttp.wrapper.annotation.Parser
-import rxhttp.wrapper.exception.HttpStatusCodeException
-import java.nio.charset.Charset
-
-@Parser(name = "Text")
-class TextParser(private val encode: String? = null) : rxhttp.wrapper.parse.Parser {
-
- override fun onParse(response: Response): String {
-
- val responseBody = response.body ?: throw HttpStatusCodeException(response, "内容为空")
- val responseBytes = UTF8BOMFighter.removeUTF8BOM(responseBody.bytes())
- var charsetName: String? = encode
-
- charsetName?.let {
- return String(responseBytes, Charset.forName(charsetName))
- }
-
- //根据http头判断
- responseBody.contentType()?.charset()?.let {
- return String(responseBytes, it)
- }
-
- //根据内容判断
- charsetName = EncodingDetect.getHtmlEncode(responseBytes)
- return String(responseBytes, Charset.forName(charsetName))
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/lib/permission/PermissionsCompat.kt b/app/src/main/java/io/legado/app/lib/permission/PermissionsCompat.kt
index a2d604c99..f127c77d6 100644
--- a/app/src/main/java/io/legado/app/lib/permission/PermissionsCompat.kt
+++ b/app/src/main/java/io/legado/app/lib/permission/PermissionsCompat.kt
@@ -29,11 +29,6 @@ class PermissionsCompat private constructor() {
return this
}
- fun requestCode(requestCode: Int): Builder {
- request.setRequestCode(requestCode)
- return this
- }
-
fun onGranted(callback: () -> Unit): Builder {
request.setOnGrantedCallback(object : OnPermissionsGrantedCallback {
override fun onPermissionsGranted() {
diff --git a/app/src/main/java/io/legado/app/lib/permission/Request.kt b/app/src/main/java/io/legado/app/lib/permission/Request.kt
index 2de60d059..a5f3acacd 100644
--- a/app/src/main/java/io/legado/app/lib/permission/Request.kt
+++ b/app/src/main/java/io/legado/app/lib/permission/Request.kt
@@ -46,10 +46,6 @@ internal class Request : OnRequestPermissionsResultCallback {
this.permissions?.addAll(listOf(*permissions))
}
- fun setRequestCode(requestCode: Int) {
- this.requestCode = requestCode
- }
-
fun setOnGrantedCallback(callback: OnPermissionsGrantedCallback) {
grantedCallback = callback
}
@@ -81,13 +77,10 @@ internal class Request : OnRequestPermissionsResultCallback {
if (rationaleResId != 0) source?.context?.getText(rationaleResId) else rationale
if (rationale != null) {
showSettingDialog(rationale) {
- onPermissionsDenied(
- requestCode,
- deniedPermissions
- )
+ onPermissionsDenied(deniedPermissions)
}
} else {
- onPermissionsDenied(requestCode, deniedPermissions)
+ onPermissionsDenied(deniedPermissions)
}
}
} else {
@@ -160,7 +153,7 @@ internal class Request : OnRequestPermissionsResultCallback {
RequestPlugins.sResultCallback?.onPermissionsGranted()
}
- private fun onPermissionsDenied(requestCode: Int, deniedPermissions: Array) {
+ private fun onPermissionsDenied(deniedPermissions: Array) {
try {
deniedCallback?.onPermissionsDenied(deniedPermissions)
} catch (ignore: Exception) {
@@ -178,9 +171,9 @@ internal class Request : OnRequestPermissionsResultCallback {
val rationale =
if (rationaleResId != 0) source?.context?.getText(rationaleResId) else rationale
if (rationale != null) {
- showSettingDialog(rationale) { onPermissionsDenied(requestCode, deniedPermissions) }
+ showSettingDialog(rationale) { onPermissionsDenied(deniedPermissions) }
} else {
- onPermissionsDenied(requestCode, deniedPermissions)
+ onPermissionsDenied(deniedPermissions)
}
} else {
onPermissionsGranted()
@@ -192,7 +185,7 @@ internal class Request : OnRequestPermissionsResultCallback {
if (deniedPermissions == null) {
onPermissionsGranted()
} else {
- onPermissionsDenied(this.requestCode, deniedPermissions)
+ onPermissionsDenied(deniedPermissions)
}
}
diff --git a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
index 1570bc793..36713320f 100644
--- a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
+++ b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
@@ -1,14 +1,13 @@
package io.legado.app.lib.webdav
-import io.legado.app.help.http.HttpHelper
-import io.legado.app.utils.await
+import io.legado.app.help.http.newCall
+import io.legado.app.help.http.okHttpClient
+import io.legado.app.help.http.text
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.Jsoup
-import rxhttp.wrapper.param.RxHttp
-import rxhttp.wrapper.param.toInputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
@@ -23,16 +22,16 @@ class WebDav(urlStr: String) {
// 指定返回哪些属性
private const val DIR =
"""
-
-
-
-
-
-
-
- %s
-
- """
+
+
+
+
+
+
+
+ %s
+
+ """
}
private val url: URL = URL(urlStr)
@@ -59,16 +58,9 @@ class WebDav(urlStr: String) {
* @return 远程文件是否存在
*/
suspend fun indexFileInfo(): Boolean {
- propFindResponse(ArrayList())?.let { response ->
- if (!response.isSuccessful) {
- this.exists = false
- return false
- }
- response.body?.let {
- @Suppress("BlockingMethodInNonBlockingContext")
- if (it.string().isNotEmpty()) {
- return true
- }
+ propFindResponse(ArrayList())?.let {
+ if (it.text().isNotEmpty()) {
+ return true
}
}
return false
@@ -81,19 +73,13 @@ class WebDav(urlStr: String) {
* @return 文件列表
*/
suspend fun listFiles(propsList: ArrayList = ArrayList()): List {
- propFindResponse(propsList)?.let { response ->
- if (response.isSuccessful) {
- response.body?.let { body ->
- @Suppress("BlockingMethodInNonBlockingContext")
- return parseDir(body.string())
- }
- }
+ propFindResponse(propsList)?.text()?.let { body ->
+ return parseDir(body)
}
return ArrayList()
}
- @Throws(IOException::class)
- private suspend fun propFindResponse(propsList: ArrayList, depth: Int = 1): Response? {
+ private suspend fun propFindResponse(propsList: ArrayList): ResponseBody? {
val requestProps = StringBuilder()
for (p in propsList) {
requestProps.append("\n")
@@ -103,19 +89,21 @@ class WebDav(urlStr: String) {
} else {
String.format(DIR, requestProps.toString() + "\n")
}
- httpUrl?.let { url ->
- // 添加RequestBody对象,可以只返回的属性。如果设为null,则会返回全部属性
- // 注意:尽量手动指定需要返回的属性。若返回全部属性,可能后由于Prop.java里没有该属性名,而崩溃。
- val requestBody = requestPropsStr.toRequestBody("text/plain".toMediaType())
- val request = Request.Builder()
- .url(url)
- .method("PROPFIND", requestBody)
-
- HttpAuth.auth?.let {
- request.header("Authorization", Credentials.basic(it.user, it.pass))
- }
- request.header("Depth", if (depth < 0) "infinity" else depth.toString())
- return HttpHelper.client.newCall(request.build()).await()
+ val url = httpUrl
+ val auth = HttpAuth.auth
+ if (url != null && auth != null) {
+ return kotlin.runCatching {
+ okHttpClient.newCall {
+ url(url)
+ addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
+ // 添加RequestBody对象,可以只返回的属性。如果设为null,则会返回全部属性
+ // 注意:尽量手动指定需要返回的属性。若返回全部属性,可能后由于Prop.java里没有该属性名,而崩溃。
+ val requestBody = requestPropsStr.toRequestBody("text/plain".toMediaType())
+ method("PROPFIND", requestBody)
+ }
+ }.onFailure {
+ it.printStackTrace()
+ }.getOrNull()
}
return null
}
@@ -160,11 +148,15 @@ class WebDav(urlStr: String) {
* @return 是否创建成功
*/
suspend fun makeAsDir(): Boolean {
- httpUrl?.let { url ->
- val request = Request.Builder()
- .url(url)
- .method("MKCOL", null)
- return execRequest(request)
+ val url = httpUrl
+ val auth = HttpAuth.auth
+ if (url != null && auth != null) {
+ okHttpClient.newCall {
+ url(url)
+ method("MKCOL", null)
+ addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
+ }
+ return true
}
return false
}
@@ -198,11 +190,15 @@ class WebDav(urlStr: String) {
if (!file.exists()) return false
// 务必注意RequestBody不要嵌套,不然上传时内容可能会被追加多余的文件信息
val fileBody = file.asRequestBody(contentType?.toMediaType())
- httpUrl?.let {
- val request = Request.Builder()
- .url(it)
- .put(fileBody)
- return execRequest(request)
+ val url = httpUrl
+ val auth = HttpAuth.auth
+ if (url != null && auth != null) {
+ okHttpClient.newCall {
+ url(url)
+ put(fileBody)
+ addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
+ }
+ return true
}
return false
}
@@ -210,37 +206,28 @@ class WebDav(urlStr: String) {
suspend fun upload(byteArray: ByteArray, contentType: String? = null): Boolean {
// 务必注意RequestBody不要嵌套,不然上传时内容可能会被追加多余的文件信息
val fileBody = byteArray.toRequestBody(contentType?.toMediaType())
- httpUrl?.let {
- val request = Request.Builder()
- .url(it)
- .put(fileBody)
- return execRequest(request)
+ val url = httpUrl
+ val auth = HttpAuth.auth
+ if (url != null && auth != null) {
+ okHttpClient.newCall {
+ url(url)
+ put(fileBody)
+ addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
+ }
+ return true
}
return false
}
- /**
- * 执行请求,获取响应结果
- * @param requestBuilder 因为还需要追加验证信息,所以此处传递Request.Builder的对象,而不是Request的对象
- * @return 请求执行的结果
- */
- @Throws(IOException::class)
- private suspend fun execRequest(requestBuilder: Request.Builder): Boolean {
- HttpAuth.auth?.let {
- requestBuilder.header("Authorization", Credentials.basic(it.user, it.pass))
- }
- val response = HttpHelper.client.newCall(requestBuilder.build()).await()
- return response.isSuccessful
- }
-
@Throws(IOException::class)
private suspend fun getInputStream(): InputStream? {
val url = httpUrl
val auth = HttpAuth.auth
if (url != null && auth != null) {
- return RxHttp.get(url)
- .addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
- .toInputStream().await()
+ return okHttpClient.newCall {
+ url(url)
+ addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
+ }.byteStream()
}
return null
}
diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
index 318c1dcd3..73e90e782 100644
--- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
+++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
@@ -16,9 +16,6 @@ import io.legado.app.help.CacheManager
import io.legado.app.help.JsExtensions
import io.legado.app.help.http.*
import io.legado.app.utils.*
-import rxhttp.wrapper.param.RxHttp
-import rxhttp.wrapper.param.toByteArray
-import rxhttp.wrapper.param.toStrResponse
import java.net.URLEncoder
import java.util.*
import java.util.regex.Pattern
@@ -58,6 +55,7 @@ class AnalyzeUrl(
private var charset: String? = null
private var method = RequestMethod.GET
private var proxy: String? = null
+ private var retry: Int = 0
init {
baseUrl = baseUrl.split(splitUrlRegex, 1)[0]
@@ -200,6 +198,7 @@ class AnalyzeUrl(
option.js?.let {
evalJS(it)
}
+ retry = option.retry
}
}
headerMap[UA_NAME] ?: let {
@@ -305,62 +304,43 @@ class AnalyzeUrl(
params.sourceRegex = sourceRegex
params.postData = body?.toByteArray()
params.tag = tag
- return HttpHelper.ajax(params)
+ return getWebViewSrc(params)
}
- return when (method) {
- RequestMethod.POST -> {
- if (fieldMap.isNotEmpty() || body.isNullOrBlank()) {
- RxHttp.postForm(url)
- .setAssemblyEnabled(false)
- .setOkClient(HttpHelper.getProxyClient(proxy))
- .addAllEncoded(fieldMap)
- .addAllHeader(headerMap)
- .toStrResponse().await()
- } else {
- RxHttp.postJson(url)
- .setAssemblyEnabled(false)
- .setOkClient(HttpHelper.getProxyClient(proxy))
- .addAll(body)
- .addAllHeader(headerMap)
- .toStrResponse().await()
+ return getProxyClient(proxy).newCallStrResponse(retry) {
+ removeHeader(UA_NAME)
+ addHeaders(headerMap)
+ when (method) {
+ RequestMethod.POST -> {
+ url(url)
+ if (fieldMap.isNotEmpty() || body.isNullOrBlank()) {
+ postForm(fieldMap, true)
+ } else {
+ postJson(body)
+ }
}
+ else -> get(url, fieldMap, true)
}
- else -> RxHttp.get(url)
- .setAssemblyEnabled(false)
- .setOkClient(HttpHelper.getProxyClient(proxy))
- .addAllEncoded(fieldMap)
- .addAllHeader(headerMap)
- .toStrResponse().await()
}
}
suspend fun getByteArray(tag: String? = null): ByteArray {
setCookie(tag)
- return when (method) {
- RequestMethod.POST -> {
- if (fieldMap.isNotEmpty() || body.isNullOrBlank()) {
- RxHttp.postForm(url)
- .setAssemblyEnabled(false)
- .setOkClient(HttpHelper.getProxyClient(proxy))
- .addAllEncoded(fieldMap)
- .addAllHeader(headerMap)
- .toByteArray().await()
- } else {
- RxHttp.postJson(url)
- .setAssemblyEnabled(false)
- .setOkClient(HttpHelper.getProxyClient(proxy))
- .addAll(body)
- .addAllHeader(headerMap)
- .toByteArray().await()
+ @Suppress("BlockingMethodInNonBlockingContext")
+ return getProxyClient(proxy).newCall(retry) {
+ removeHeader(UA_NAME)
+ addHeaders(headerMap)
+ when (method) {
+ RequestMethod.POST -> {
+ url(url)
+ if (fieldMap.isNotEmpty() || body.isNullOrBlank()) {
+ postForm(fieldMap, true)
+ } else {
+ postJson(body)
+ }
}
+ else -> get(url, fieldMap, true)
}
- else -> RxHttp.get(url)
- .setAssemblyEnabled(false)
- .setOkClient(HttpHelper.getProxyClient(proxy))
- .addAllEncoded(fieldMap)
- .addAllHeader(headerMap)
- .toByteArray().await()
- }
+ }.bytes()
}
private fun setCookie(tag: String?) {
@@ -393,7 +373,8 @@ class AnalyzeUrl(
val headers: Any?,
val body: Any?,
val type: String?,
- val js: String?
+ val js: String?,
+ val retry: Int = 0
)
}
diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt
index 8eef8d50b..e6984a548 100644
--- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt
+++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt
@@ -13,6 +13,9 @@ import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.HtmlFormatter
import io.legado.app.utils.NetworkUtils
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.async
+import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import splitties.init.appCtx
@@ -45,11 +48,11 @@ object BookContent {
val analyzeRule = AnalyzeRule(book).setContent(body, baseUrl)
analyzeRule.setRedirectUrl(baseUrl)
analyzeRule.nextChapterUrl = mNextChapterUrl
+ scope.ensureActive()
var contentData = analyzeContent(
book, baseUrl, redirectUrl, body, contentRule, bookChapter, bookSource, mNextChapterUrl
)
content.append(contentData.content).append("\n")
-
if (contentData.nextUrl.size == 1) {
var nextUrl = contentData.nextUrl[0]
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) {
@@ -58,6 +61,7 @@ object BookContent {
== NetworkUtils.getAbsoluteURL(baseUrl, mNextChapterUrl)
) break
nextUrlList.add(nextUrl)
+ scope.ensureActive()
val res = AnalyzeUrl(
ruleUrl = nextUrl,
book = book,
@@ -75,29 +79,27 @@ object BookContent {
}
Debug.log(bookSource.bookSourceUrl, "◇本章总页数:${nextUrlList.size}")
} else if (contentData.nextUrl.size > 1) {
- val contentDataList = arrayListOf>()
- for (item in contentData.nextUrl) {
- if (!nextUrlList.contains(item))
- contentDataList.add(ContentData(nextUrl = item))
- }
- for (item in contentDataList) {
- withContext(scope.coroutineContext) {
- val res = AnalyzeUrl(
- ruleUrl = item.nextUrl,
- book = book,
- headerMapF = bookSource.getHeaderMap()
- ).getStrResponse(bookSource.bookSourceUrl)
- res.body?.let { nextBody ->
- contentData = analyzeContent(
- book, item.nextUrl, res.url, nextBody, contentRule,
- bookChapter, bookSource, mNextChapterUrl, false
+ Debug.log(bookSource.bookSourceUrl, "◇并发解析目录,总页数:${contentData.nextUrl.size}")
+ withContext(IO) {
+ val asyncArray = Array(contentData.nextUrl.size) {
+ async(IO) {
+ val urlStr = contentData.nextUrl[it]
+ val analyzeUrl = AnalyzeUrl(
+ ruleUrl = urlStr,
+ book = book,
+ headerMapF = bookSource.getHeaderMap()
)
- item.content = contentData.content
+ val res = analyzeUrl.getStrResponse(bookSource.bookSourceUrl)
+ analyzeContent(
+ book, urlStr, res.url, res.body!!, contentRule,
+ bookChapter, bookSource, mNextChapterUrl, false
+ ).content
}
}
- }
- for (item in contentDataList) {
- content.append(item.content).append("\n")
+ asyncArray.forEach { coroutine ->
+ scope.ensureActive()
+ content.append(coroutine.await()).append("\n")
+ }
}
}
content.deleteCharAt(content.length - 1)
diff --git a/app/src/main/java/io/legado/app/ui/README.md b/app/src/main/java/io/legado/app/ui/README.md
index bc6549bec..05d4be931 100644
--- a/app/src/main/java/io/legado/app/ui/README.md
+++ b/app/src/main/java/io/legado/app/ui/README.md
@@ -10,7 +10,7 @@
* book\source 书源界面
* book\changeCover 封面换源界面
* book\changeSource 换源界面
-* book\chapterList 目录界面
+* book\toc 目录界面
* book\download 下载界面
* book\explore 发现界面
* book\local 书籍导入界面
diff --git a/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt b/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt
index 67aec440f..8401830c6 100644
--- a/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt
@@ -1,14 +1,10 @@
package io.legado.app.ui.association
-import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.Theme
import io.legado.app.databinding.ActivityTranslucenceBinding
-import io.legado.app.ui.main.MainActivity
-import io.legado.app.utils.startActivity
-
import io.legado.app.utils.toastOnUi
class FileAssociationActivity :
@@ -39,14 +35,4 @@ class FileAssociationActivity :
}
}
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- finish()
- //返回后直接跳转到主页面
- gotoMainActivity()
- }
-
- private fun gotoMainActivity() {
- startActivity()
- }
}
diff --git a/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt
index 7a08cacb1..a67c7e7f8 100644
--- a/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt
@@ -9,13 +9,14 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig
import io.legado.app.help.SourceHelp
+import io.legado.app.help.http.newCall
+import io.legado.app.help.http.okHttpClient
+import io.legado.app.help.http.text
import io.legado.app.help.storage.OldRule
import io.legado.app.help.storage.Restore
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.isJsonArray
import io.legado.app.utils.isJsonObject
-import rxhttp.wrapper.param.RxHttp
-import rxhttp.wrapper.param.toText
class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
var groupName: String? = null
@@ -111,7 +112,9 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
}
private suspend fun importSourceUrl(url: String) {
- RxHttp.get(url).toText("utf-8").await().let { body ->
+ okHttpClient.newCall {
+ url(url)
+ }.text("utf-8").let { body ->
val items: List