mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
Compare commits
8 Commits
27fcbd3f45
...
3205f4a90f
Author | SHA1 | Date | |
---|---|---|---|
|
3205f4a90f | ||
|
988a1b5385 | ||
|
94fb7564f1 | ||
|
11eb304fae | ||
|
33e9286a8e | ||
|
e57ad3a09e | ||
|
f83a2bc357 | ||
|
cb06d149f7 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
{"arm64-v8a":"bbf2c50d1ebf0763d451b08e290a3244","armeabi-v7a":"ecb872f7b1b5342f4d7c36262bec0600","x86":"4da4832b89e2412d808c5b1ecdc24e3d","x86_64":"62080f051db02ed0e939affb39ce67fb","version":"123.0.6312.80"}
|
||||
{"x86":"cb929a66b3c21a5b27b49f6ff79b5441","armeabi-v7a":"4b5b7c7be1d4cab7f848d982081062f6","x86_64":"698858d140e577226364d7310ea015fb","arm64-v8a":"d03030209bb386fcf6842b98e93a9942","version":"124.0.6367.82"}
|
@ -13,6 +13,7 @@
|
||||
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
||||
|
||||
**2024/02/27**
|
||||
* 更新cronet: 124.0.6367.82
|
||||
* 更新cronet: 123.0.6312.80
|
||||
* 更新cronet: 123.0.6312.40
|
||||
|
||||
|
@ -178,7 +178,8 @@ let options = {
|
||||
if(b64){
|
||||
var f1 = java.queryBase64TTF(b64[1]);
|
||||
var f2 = java.queryTTF("https://alanskycn.gitee.io/teachme/assets/font/Source Han Sans CN Regular.ttf");
|
||||
return java.replaceFont(result, f1, f2);
|
||||
// return java.replaceFont(result, f1, f2);
|
||||
return java.replaceFont(result, f1, f2, true); // 过滤掉f1中不存在的字形
|
||||
}
|
||||
return result;
|
||||
})()
|
||||
|
@ -799,23 +799,34 @@ interface JsExtensions : JsEncodeUtils {
|
||||
fun replaceFont(
|
||||
text: String,
|
||||
errorQueryTTF: QueryTTF?,
|
||||
correctQueryTTF: QueryTTF?
|
||||
correctQueryTTF: QueryTTF?,
|
||||
filter: Boolean
|
||||
): String {
|
||||
if (errorQueryTTF == null || correctQueryTTF == null) return text
|
||||
val contentArray = text.toStringArray() //这里不能用toCharArray,因为有些文字占多个字节
|
||||
contentArray.forEachIndexed { index, s ->
|
||||
val oldCode = s.codePointAt(0)
|
||||
if (errorQueryTTF.inLimit(oldCode)) {
|
||||
val glyf = errorQueryTTF.getGlyfByCode(oldCode)
|
||||
val code = correctQueryTTF.getCodeByGlyf(glyf)
|
||||
if (code != 0) {
|
||||
contentArray[index] = code.toChar().toString()
|
||||
}
|
||||
val glyf = errorQueryTTF.getGlyfByCode(oldCode)
|
||||
val code = correctQueryTTF.getCodeByGlyf(glyf)
|
||||
if (code != 0) {
|
||||
contentArray[index] = code.toChar().toString()
|
||||
}
|
||||
if (glyf == "" && filter) {
|
||||
// 删除轮廓数据为空的字符
|
||||
contentArray[index] = ""
|
||||
}
|
||||
}
|
||||
return contentArray.joinToString("")
|
||||
}
|
||||
|
||||
fun replaceFont(
|
||||
text: String,
|
||||
errorQueryTTF: QueryTTF?,
|
||||
correctQueryTTF: QueryTTF?
|
||||
): String {
|
||||
return replaceFont(text, errorQueryTTF, correctQueryTTF, false)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 章节数转数字
|
||||
|
@ -11,7 +11,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "StatementWithEmptyBody", "unused"})
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
public class QueryTTF {
|
||||
private static class Header {
|
||||
public int majorVersion;
|
||||
@ -222,7 +222,7 @@ public class QueryTTF {
|
||||
private final MaxpLayout maxp = new MaxpLayout();
|
||||
private final List<Integer> loca = new LinkedList<>();
|
||||
private final CmapLayout Cmap = new CmapLayout();
|
||||
private final List<GlyfLayout> glyf = new LinkedList<>();
|
||||
private final List<String> glyf = new LinkedList<>();
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Pair<Integer, Integer>[] pps = new Pair[]{
|
||||
Pair.of(3, 10),
|
||||
@ -233,10 +233,9 @@ public class QueryTTF {
|
||||
Pair.of(0, 1)
|
||||
};
|
||||
|
||||
public final Map<Integer, String> codeToGlyph = new HashMap<>();
|
||||
public final Map<String, Integer> glyphToCode = new HashMap<>();
|
||||
private int limitMix = Integer.MAX_VALUE;
|
||||
private int limitMax = 0;
|
||||
public final Map<Integer, String> unicodeToGlyph = new HashMap<>();
|
||||
public final Map<String, Integer> glyphToUnicode = new HashMap<>();
|
||||
public final Map<Integer, Integer> unicodeToGlyphIndex = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@ -406,83 +405,42 @@ public class QueryTTF {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 解析表 glyf (字体轮廓数据表)
|
||||
// 读取表 glyf (字体轮廓数据表)
|
||||
for (Directory Temp : directorys) {
|
||||
if (Temp.tag.equals("glyf")) {
|
||||
fontReader.index = Temp.offset;
|
||||
for (int i = 0; i < maxp.numGlyphs; ++i) {
|
||||
int glyfCount = maxp.numGlyphs;
|
||||
for (int i = 0; i < glyfCount; ) {
|
||||
fontReader.index = Temp.offset + loca.get(i);
|
||||
++i;
|
||||
int glyfNextIndex = i < glyfCount ? loca.get(i) : Temp.length;
|
||||
|
||||
byte[] glyph;
|
||||
short numberOfContours = fontReader.ReadInt16();
|
||||
if (numberOfContours > 0) {
|
||||
GlyfLayout g = new GlyfLayout();
|
||||
g.numberOfContours = numberOfContours;
|
||||
g.xMin = fontReader.ReadInt16();
|
||||
g.yMin = fontReader.ReadInt16();
|
||||
g.xMax = fontReader.ReadInt16();
|
||||
g.yMax = fontReader.ReadInt16();
|
||||
g.endPtsOfContours = fontReader.GetUInt16Array(numberOfContours);
|
||||
g.instructionLength = fontReader.ReadUInt16();
|
||||
g.instructions = fontReader.GetBytes(g.instructionLength);
|
||||
int flagLength = g.endPtsOfContours[g.endPtsOfContours.length - 1] + 1;
|
||||
// 获取轮廓点描述标志
|
||||
g.flags = new byte[flagLength];
|
||||
for (int n = 0; n < flagLength; ++n) {
|
||||
g.flags[n] = fontReader.GetByte();
|
||||
if ((g.flags[n] & 0x08) != 0x00) {
|
||||
for (int m = fontReader.ReadUInt8(); m > 0; --m) {
|
||||
g.flags[++n] = g.flags[n - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取轮廓点描述x轴相对值
|
||||
g.xCoordinates = new short[flagLength];
|
||||
for (int n = 0; n < flagLength; ++n) {
|
||||
short same = (short) ((g.flags[n] & 0x10) != 0 ? 1 : -1);
|
||||
if ((g.flags[n] & 0x02) != 0) {
|
||||
g.xCoordinates[n] = (short) (same * fontReader.ReadUInt8());
|
||||
} else {
|
||||
g.xCoordinates[n] = same == 1 ? (short) 0 : fontReader.ReadInt16();
|
||||
}
|
||||
}
|
||||
// 获取轮廓点描述y轴相对值
|
||||
g.yCoordinates = new short[flagLength];
|
||||
for (int n = 0; n < flagLength; ++n) {
|
||||
short same = (short) ((g.flags[n] & 0x20) != 0 ? 1 : -1);
|
||||
if ((g.flags[n] & 0x04) != 0) {
|
||||
g.yCoordinates[n] = (short) (same * fontReader.ReadUInt8());
|
||||
} else {
|
||||
g.yCoordinates[n] = same == 1 ? (short) 0 : fontReader.ReadInt16();
|
||||
}
|
||||
}
|
||||
/* 相对坐标转绝对坐标
|
||||
for (int n = 1; n < flagLength; ++n) {
|
||||
xCoordinates[n] += xCoordinates[n - 1];
|
||||
yCoordinates[n] += yCoordinates[n - 1];
|
||||
}*/
|
||||
glyf.add(g);
|
||||
short g_xMin = fontReader.ReadInt16();
|
||||
short g_yMin = fontReader.ReadInt16();
|
||||
short g_xMax = fontReader.ReadInt16();
|
||||
short g_yMax = fontReader.ReadInt16();
|
||||
int[] endPtsOfContours = fontReader.GetUInt16Array(numberOfContours);
|
||||
glyph = fontReader.GetBytes(glyfNextIndex - (fontReader.index - Temp.offset));
|
||||
} else {
|
||||
// 复合字体暂未使用
|
||||
glyph = fontReader.GetBytes(glyfNextIndex - (fontReader.index - 2));
|
||||
}
|
||||
glyf.add(getHexFromBytes(glyph));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 建立Unicode&Glyph双向表
|
||||
for (int key = 0; key < 130000; ++key) {
|
||||
if (key == 0xFF) key = 0x3400;
|
||||
int gid = getGlyfIndex(key);
|
||||
if (gid == 0 || gid >= glyf.size()) continue;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 字型数据转String,方便存HashMap
|
||||
for (short b : glyf.get(gid).xCoordinates) sb.append(b);
|
||||
for (short b : glyf.get(gid).yCoordinates) sb.append(b);
|
||||
String val = sb.toString();
|
||||
if (limitMix > key) limitMix = key;
|
||||
if (limitMax < key) limitMax = key;
|
||||
codeToGlyph.put(key, val);
|
||||
if (glyphToCode.containsKey(val)) continue;
|
||||
glyphToCode.put(val, key);
|
||||
// if (key == 0xFF) key = 0x3400;
|
||||
int gid = queryGlyfIndex(key);
|
||||
if (gid >= glyf.size()) continue;
|
||||
unicodeToGlyphIndex.put(key, gid);
|
||||
var val = glyf.get(gid);
|
||||
unicodeToGlyph.put(key, val);
|
||||
if (glyphToUnicode.containsKey(val)) continue;
|
||||
glyphToUnicode.put(val, key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,11 +467,11 @@ public class QueryTTF {
|
||||
/**
|
||||
* 使用Unicode值查找轮廓索引
|
||||
*
|
||||
* @param code 传入Unicode十进制值
|
||||
* @param unicode 传入Unicode值
|
||||
* @return 返回十进制轮廓索引
|
||||
*/
|
||||
private int getGlyfIndex(int code) {
|
||||
if (code == 0) return 0;
|
||||
public int queryGlyfIndex(int unicode) {
|
||||
if (unicode == 0) return 0;
|
||||
int fmtKey = 0;
|
||||
for (Pair<Integer, Integer> item : pps) {
|
||||
for (CmapRecord record : Cmap.records) {
|
||||
@ -531,34 +489,33 @@ public class QueryTTF {
|
||||
assert table != null;
|
||||
int fmt = table.format;
|
||||
if (fmt == 0) {
|
||||
if (code < table.glyphIdArray.length) glyfID = table.glyphIdArray[code] & 0xFF;
|
||||
if (unicode < table.glyphIdArray.length) glyfID = table.glyphIdArray[unicode] & 0xFF;
|
||||
} else if (fmt == 4) {
|
||||
CmapFormat4 tab = (CmapFormat4) table;
|
||||
if (code > tab.endCode[tab.endCode.length - 1]) return 0;
|
||||
if (unicode > tab.endCode[tab.endCode.length - 1]) return 0;
|
||||
// 二分法查找数值索引
|
||||
int start = 0, middle, end = tab.endCode.length - 1;
|
||||
while (start + 1 < end) {
|
||||
middle = (start + end) / 2;
|
||||
if (tab.endCode[middle] <= code) start = middle;
|
||||
if (tab.endCode[middle] <= unicode) start = middle;
|
||||
else end = middle;
|
||||
}
|
||||
if (tab.endCode[start] < code) ++start;
|
||||
if (code < tab.startCode[start]) return 0;
|
||||
if (tab.endCode[start] < unicode) ++start;
|
||||
if (unicode < tab.startCode[start]) return 0;
|
||||
if (tab.idRangeOffset[start] != 0) {
|
||||
glyfID = tab.glyphIdArray[code - tab.startCode[start] + (tab.idRangeOffset[start] >> 1) - (tab.idRangeOffset.length - start)];
|
||||
} else glyfID = code + tab.idDelta[start];
|
||||
glyfID = tab.glyphIdArray[unicode - tab.startCode[start] + (tab.idRangeOffset[start] >> 1) - (tab.idRangeOffset.length - start)];
|
||||
} else glyfID = unicode + tab.idDelta[start];
|
||||
glyfID &= 0xFFFF;
|
||||
} else if (fmt == 6) {
|
||||
CmapFormat6 tab = (CmapFormat6) table;
|
||||
int index = code - tab.firstCode;
|
||||
if (index < 0 || index >= tab.glyphIdArray.length) glyfID = 0;
|
||||
else glyfID = tab.glyphIdArray[index];
|
||||
int index = unicode - tab.firstCode;
|
||||
if (0 <= index && index < tab.glyphIdArray.length) glyfID = tab.glyphIdArray[index];
|
||||
} else if (fmt == 12) {
|
||||
CmapFormat12 tab = (CmapFormat12) table;
|
||||
if (code > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0;
|
||||
if (unicode > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0;
|
||||
for (int i = 0; i < tab.numGroups; i++) {
|
||||
if (tab.groups.get(i).getLeft() <= code && code <= tab.groups.get(i).getMiddle()) {
|
||||
glyfID = tab.groups.get(i).getRight() + code - tab.groups.get(i).getLeft();
|
||||
if (tab.groups.get(i).getLeft() <= unicode && unicode <= tab.groups.get(i).getMiddle()) {
|
||||
glyfID = tab.groups.get(i).getRight() + unicode - tab.groups.get(i).getLeft();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -567,33 +524,54 @@ public class QueryTTF {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Unicode值是否在字体范围内
|
||||
* 使用Unicode值获取轮廓索引
|
||||
*
|
||||
* @param code 传入Unicode十进制值
|
||||
* @return 返回bool查询结果
|
||||
* @param unicode 传入Unicode值
|
||||
* @return 返回十进制轮廓索引
|
||||
*/
|
||||
public boolean inLimit(int code) {
|
||||
return (limitMix <= code) && (code <= limitMax);
|
||||
public int getGlyfIndex(int unicode) {
|
||||
var glyfIndex = unicodeToGlyphIndex.get(unicode);
|
||||
if (glyfIndex == null) return 0;
|
||||
return glyfIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Unicode值获取轮廓数据
|
||||
*
|
||||
* @param key 传入Unicode十进制值
|
||||
* @param unicode 传入Unicode值
|
||||
* @return 返回轮廓数组的String值
|
||||
*/
|
||||
public String getGlyfByCode(int key) {
|
||||
return codeToGlyph.getOrDefault(key, "");
|
||||
public String getGlyfByCode(int unicode) {
|
||||
return unicodeToGlyph.getOrDefault(unicode, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用轮廓数据获取Unicode值
|
||||
*
|
||||
* @param val 传入轮廓数组的String值
|
||||
* @param glyph 传入轮廓数组的String值
|
||||
* @return 返回Unicode十进制值
|
||||
*/
|
||||
public int getCodeByGlyf(String val) {
|
||||
public int getCodeByGlyf(String glyph) {
|
||||
//noinspection ConstantConditions
|
||||
return glyphToCode.getOrDefault(val, 0);
|
||||
return glyphToUnicode.getOrDefault(glyph, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字体轮廓数据转Hex字符串
|
||||
*
|
||||
* @param glyph 字体轮廓数据
|
||||
* @return 返回轮廓数组的String值
|
||||
*/
|
||||
public String getHexFromBytes(byte[] glyph) {
|
||||
if (glyph == null) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : glyph) {
|
||||
String hex = Integer.toHexString(b);
|
||||
if (hex.length() == 1) {
|
||||
sb.append("0");//当16进制为个位数时,在前面补0
|
||||
}
|
||||
sb.append(hex);//将16进制加入字符串
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.BaseService
|
||||
import io.legado.app.constant.AppConst
|
||||
@ -15,7 +16,16 @@ import io.legado.app.constant.IntentAction
|
||||
import io.legado.app.constant.NotificationId
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.receiver.NetworkChangedListener
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.NetworkUtils
|
||||
import io.legado.app.utils.getPrefBoolean
|
||||
import io.legado.app.utils.getPrefInt
|
||||
import io.legado.app.utils.postEvent
|
||||
import io.legado.app.utils.printOnDebug
|
||||
import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.servicePendingIntent
|
||||
import io.legado.app.utils.startService
|
||||
import io.legado.app.utils.stopService
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.web.HttpServer
|
||||
import io.legado.app.web.WebSocketServer
|
||||
import splitties.init.appCtx
|
||||
@ -33,6 +43,11 @@ class WebService : BaseService() {
|
||||
context.startService<WebService>()
|
||||
}
|
||||
|
||||
fun startForeground(context: Context) {
|
||||
val intent = Intent(context, WebService::class.java)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
|
||||
fun stop(context: Context) {
|
||||
context.stopService<WebService>()
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
package io.legado.app.service
|
||||
|
||||
import android.app.Dialog
|
||||
import android.app.ForegroundServiceStartNotAllowedException
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.service.quicksettings.Tile
|
||||
import android.service.quicksettings.TileService
|
||||
import android.view.WindowManager.BadTokenException
|
||||
import androidx.annotation.RequiresApi
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.IntentAction
|
||||
import io.legado.app.utils.printOnDebug
|
||||
|
||||
@ -22,6 +26,7 @@ class WebTileService : TileService() {
|
||||
state = Tile.STATE_ACTIVE
|
||||
updateTile()
|
||||
}
|
||||
|
||||
IntentAction.stop -> qsTile?.run {
|
||||
state = Tile.STATE_INACTIVE
|
||||
updateTile()
|
||||
@ -49,7 +54,24 @@ class WebTileService : TileService() {
|
||||
if (WebService.isRun) {
|
||||
WebService.stop(this)
|
||||
} else {
|
||||
WebService.start(this)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
val dialog = Dialog(this, R.style.AppTheme_Transparent)
|
||||
dialog.setOnShowListener {
|
||||
try {
|
||||
WebService.startForeground(this)
|
||||
} catch (e: ForegroundServiceStartNotAllowedException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
try {
|
||||
showDialog(dialog)
|
||||
} catch (e: BadTokenException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else {
|
||||
WebService.start(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,15 @@
|
||||
package io.legado.app.ui.book.read.page
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Picture
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import androidx.core.graphics.withClip
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.lib.theme.ThemeStore
|
||||
import io.legado.app.ui.book.read.page.entities.PageDirection
|
||||
import io.legado.app.utils.screenshot
|
||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||
import io.legado.app.utils.canvasrecorder.recordIfNeeded
|
||||
|
||||
/**
|
||||
* 自动翻页
|
||||
@ -24,13 +21,10 @@ class AutoPager(private val readView: ReadView) {
|
||||
private var scrollOffsetRemain = 0.0
|
||||
private var scrollOffset = 0
|
||||
private var lastTimeMillis = 0L
|
||||
private var bitmap: Bitmap? = null
|
||||
private var picture: Picture? = null
|
||||
private var pictureIsDirty = true
|
||||
private val atLeastApi23 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
private val rect = Rect()
|
||||
private var canvasRecorder = CanvasRecorderFactory.create()
|
||||
private val paint by lazy { Paint() }
|
||||
|
||||
|
||||
fun start() {
|
||||
isRunning = true
|
||||
paint.color = ThemeStore.accentColor
|
||||
@ -48,7 +42,7 @@ class AutoPager(private val readView: ReadView) {
|
||||
readView.curPage.upSelectAble(AppConfig.textSelectAble)
|
||||
readView.invalidate()
|
||||
reset()
|
||||
picture = null
|
||||
canvasRecorder.recycle()
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
@ -71,9 +65,12 @@ class AutoPager(private val readView: ReadView) {
|
||||
progress = 0
|
||||
scrollOffsetRemain = 0.0
|
||||
scrollOffset = 0
|
||||
bitmap?.recycle()
|
||||
bitmap = null
|
||||
pictureIsDirty = true
|
||||
canvasRecorder.invalidate()
|
||||
}
|
||||
|
||||
fun upRecorder() {
|
||||
canvasRecorder.recycle()
|
||||
canvasRecorder = CanvasRecorderFactory.create()
|
||||
}
|
||||
|
||||
fun onDraw(canvas: Canvas) {
|
||||
@ -86,24 +83,12 @@ class AutoPager(private val readView: ReadView) {
|
||||
} else {
|
||||
val bottom = progress
|
||||
val width = readView.width
|
||||
if (atLeastApi23) {
|
||||
if (picture == null) {
|
||||
picture = Picture()
|
||||
}
|
||||
if (pictureIsDirty) {
|
||||
pictureIsDirty = false
|
||||
readView.nextPage.screenshot(picture!!)
|
||||
}
|
||||
canvas.withClip(0, 0, width, bottom) {
|
||||
drawPicture(picture!!)
|
||||
}
|
||||
} else {
|
||||
if (bitmap == null) {
|
||||
bitmap = readView.nextPage.screenshot()
|
||||
}
|
||||
rect.set(0, 0, width, bottom)
|
||||
canvas.drawBitmap(bitmap!!, rect, rect, null)
|
||||
|
||||
canvasRecorder.recordIfNeeded(readView.nextPage)
|
||||
canvas.withClip(0, 0, width, bottom) {
|
||||
canvasRecorder.draw(this)
|
||||
}
|
||||
|
||||
canvas.drawRect(
|
||||
0f,
|
||||
bottom.toFloat() - 1,
|
||||
|
@ -514,6 +514,7 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
(pageDelegate as? ScrollPageDelegate)?.noAnim = AppConfig.noAnimScrollPage
|
||||
if (upRecorder) {
|
||||
(pageDelegate as? HorizontalPageDelegate)?.upRecorder()
|
||||
autoPager.upRecorder()
|
||||
}
|
||||
pageDelegate?.setViewSize(width, height)
|
||||
if (isScroll) {
|
||||
|
@ -6,7 +6,6 @@ import android.view.View
|
||||
import androidx.core.view.isGone
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
@ -29,6 +28,7 @@ import io.legado.app.ui.book.info.BookInfoActivity
|
||||
import io.legado.app.ui.book.read.ReadBookActivity
|
||||
import io.legado.app.ui.main.MainViewModel
|
||||
import io.legado.app.utils.cnCompare
|
||||
import io.legado.app.utils.flowWithLifecycleFirst
|
||||
import io.legado.app.utils.observeEvent
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.startActivity
|
||||
@ -175,7 +175,7 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
|
||||
|
||||
else -> list.sortedByDescending { it.durChapterTime }
|
||||
}
|
||||
}.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch {
|
||||
}.flowWithLifecycleFirst(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch {
|
||||
AppLog.put("书架更新出错", it)
|
||||
}.conflate().flowOn(Dispatchers.Default).collect { list ->
|
||||
binding.tvEmptyMsg.isGone = list.isNotEmpty()
|
||||
|
@ -1,13 +1,17 @@
|
||||
package io.legado.app.utils
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
@ -185,3 +189,18 @@ inline fun <T> Flow<T>.onEachAsyncIndexed(
|
||||
}.onEach { semaphore.release() }
|
||||
}.buffer(0)
|
||||
}
|
||||
|
||||
fun <T> Flow<T>.flowWithLifecycleFirst(
|
||||
lifecycle: Lifecycle,
|
||||
minActiveState: Lifecycle.State = Lifecycle.State.STARTED
|
||||
): Flow<T> = callbackFlow {
|
||||
if (!lifecycle.currentState.isAtLeast(minActiveState)) {
|
||||
send(first())
|
||||
}
|
||||
lifecycle.repeatOnLifecycle(minActiveState) {
|
||||
this@flowWithLifecycleFirst.collect {
|
||||
send(it)
|
||||
}
|
||||
}
|
||||
close()
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.legado.app.utils.canvasrecorder
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.view.View
|
||||
import androidx.core.graphics.withSave
|
||||
|
||||
inline fun CanvasRecorder.recordIfNeeded(
|
||||
@ -13,6 +14,14 @@ inline fun CanvasRecorder.recordIfNeeded(
|
||||
return true
|
||||
}
|
||||
|
||||
fun CanvasRecorder.recordIfNeeded(view: View): Boolean {
|
||||
if (!needRecord()) return false
|
||||
record(view.width, view.height) {
|
||||
view.draw(this)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun CanvasRecorder.record(width: Int, height: Int, block: Canvas.() -> Unit) {
|
||||
try {
|
||||
val canvas = beginRecording(width, height)
|
||||
|
@ -42,7 +42,7 @@ android.defaults.buildfeatures.shaders=false
|
||||
# and none from the library's dependencies, thereby reducing the size of the R class for that library.
|
||||
android.nonTransitiveRClass=true
|
||||
# https://chromiumdash.appspot.com/releases?platform=Android
|
||||
CronetVersion=123.0.6312.80
|
||||
CronetMainVersion=123.0.0.0
|
||||
CronetVersion=124.0.6367.82
|
||||
CronetMainVersion=124.0.0.0
|
||||
android.injected.testOnly=false
|
||||
android.nonFinalResIds=true
|
Loading…
Reference in New Issue
Block a user