mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
Compare commits
13 Commits
aea8eb3767
...
ae6f93dd38
Author | SHA1 | Date | |
---|---|---|---|
|
ae6f93dd38 | ||
|
fd3d96fa74 | ||
|
ad1992d493 | ||
|
fa3fc01080 | ||
|
1e5ea697ad | ||
|
6cc3d3a085 | ||
|
5685d442ff | ||
|
29a6b49d67 | ||
|
025dbcc3d4 | ||
|
10d2764c61 | ||
|
5b86fb8dd8 | ||
|
59d4bb6c8e | ||
|
85575111da |
16
.idea/.gitignore
vendored
16
.idea/.gitignore
vendored
@ -1,16 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Custom ignored
|
||||
/caches/
|
||||
/dictionaries/
|
||||
/modules/
|
||||
/libraries/
|
||||
/*.xml
|
||||
# GitHub Copilot persisted chat sessions
|
||||
/copilot/chatSessions
|
@ -1 +0,0 @@
|
||||
legado
|
@ -1,123 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
@ -1,20 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ConstantConditions" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="SUGGEST_NULLABLE_ANNOTATIONS" value="false" />
|
||||
<option name="DONT_REPORT_TRUE_ASSERT_STATEMENTS" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="name" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="NonAsciiCharacters" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
@ -1,7 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
@ -768,15 +768,7 @@ interface JsExtensions : JsEncodeUtils {
|
||||
var qTTF = CacheManager.getQueryTTF(key)
|
||||
if (qTTF != null) return qTTF
|
||||
val font: ByteArray? = when {
|
||||
str.isAbsUrl() -> {
|
||||
var x = CacheManager.getByteArray(key)
|
||||
if (x == null) {
|
||||
x = AnalyzeUrl(str, source = getSource()).getByteArray()
|
||||
CacheManager.put(key, x)
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
str.isAbsUrl() -> AnalyzeUrl(str, source = getSource()).getByteArray()
|
||||
str.isContentScheme() -> Uri.parse(str).readBytes(appCtx)
|
||||
str.startsWith("/storage") -> File(str).readBytes()
|
||||
else -> base64DecodeToByteArray(str)
|
||||
|
@ -11,7 +11,6 @@ import io.legado.app.constant.PageAnim
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.help.DefaultData
|
||||
import io.legado.app.help.coroutine.Coroutine
|
||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||
import io.legado.app.utils.BitmapUtils
|
||||
import io.legado.app.utils.FileUtils
|
||||
import io.legado.app.utils.GSON
|
||||
|
@ -1,15 +1,19 @@
|
||||
package io.legado.app.model.analyzeRule;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import android.util.Log;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
public class QueryTTF {
|
||||
@ -129,11 +133,11 @@ public class QueryTTF {
|
||||
public int length;
|
||||
public int language;
|
||||
public int numGroups;
|
||||
public List<Triple<Integer, Integer, Integer>> groups;
|
||||
public List<int[]> groups;
|
||||
}
|
||||
|
||||
private static class GlyfLayout {
|
||||
public short numberOfContours; // 非负值为简单字型,负值为符合字型
|
||||
public short numberOfContours; // 非负值为简单字型,负值为复合字型
|
||||
public short xMin;
|
||||
public short yMin;
|
||||
public short xMax;
|
||||
@ -147,103 +151,335 @@ public class QueryTTF {
|
||||
}
|
||||
|
||||
private static class ByteArrayReader {
|
||||
public int index;
|
||||
public byte[] buffer;
|
||||
private final byte[] buffer;
|
||||
public final ByteBuffer byteBuffer;
|
||||
|
||||
public ByteArrayReader(byte[] buffer, int index) {
|
||||
this.buffer = buffer;
|
||||
this.index = index;
|
||||
this.byteBuffer = ByteBuffer.wrap(buffer);
|
||||
this.byteBuffer.order(ByteOrder.BIG_ENDIAN); // 设置为大端模式
|
||||
this.byteBuffer.position(index); // 设置起始索引
|
||||
}
|
||||
|
||||
public long ReadUIntX(long len) {
|
||||
long result = 0;
|
||||
for (long i = 0; i < len; ++i) {
|
||||
result <<= 8;
|
||||
result |= buffer[index++] & 0xFF;
|
||||
}
|
||||
return result;
|
||||
public void position(int index) {
|
||||
byteBuffer.position(index); // 设置起始索引
|
||||
}
|
||||
|
||||
public int position() {
|
||||
return byteBuffer.position();
|
||||
}
|
||||
|
||||
|
||||
public long ReadUInt64() {
|
||||
return ReadUIntX(8);
|
||||
return byteBuffer.getLong();
|
||||
}
|
||||
|
||||
public int ReadUInt32() {
|
||||
return (int) ReadUIntX(4);
|
||||
return byteBuffer.getInt();
|
||||
}
|
||||
|
||||
public int ReadInt32() {
|
||||
return byteBuffer.getInt();
|
||||
}
|
||||
|
||||
public int ReadUInt16() {
|
||||
return (int) ReadUIntX(2);
|
||||
return byteBuffer.getShort() & 0xFFFF;
|
||||
}
|
||||
|
||||
public short ReadInt16() {
|
||||
return (short) ReadUIntX(2);
|
||||
return byteBuffer.getShort();
|
||||
}
|
||||
|
||||
public short ReadUInt8() {
|
||||
return (short) ReadUIntX(1);
|
||||
return (short) (byteBuffer.get() & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
public String ReadStrings(int len, Charset charset) {
|
||||
byte[] result = len > 0 ? new byte[len] : null;
|
||||
for (int i = 0; i < len; ++i) result[i] = buffer[index++];
|
||||
return new String(result, charset);
|
||||
public byte ReadInt8() {
|
||||
return byteBuffer.get();
|
||||
}
|
||||
|
||||
public byte GetByte() {
|
||||
return buffer[index++];
|
||||
}
|
||||
|
||||
public byte[] GetBytes(int len) {
|
||||
byte[] result = len > 0 ? new byte[len] : null;
|
||||
for (int i = 0; i < len; ++i) result[i] = buffer[index++];
|
||||
public byte[] ReadByteArray(int len) {
|
||||
if (len < 0) throw new IllegalArgumentException("Length must not be negative");
|
||||
byte[] result = new byte[len];
|
||||
byteBuffer.get(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int[] GetUInt16Array(int len) {
|
||||
int[] result = len > 0 ? new int[len] : null;
|
||||
for (int i = 0; i < len; ++i) result[i] = ReadUInt16();
|
||||
public short[] ReadInt16Array(int len) {
|
||||
if (len < 0) throw new IllegalArgumentException("Length must not be negative");
|
||||
var result = new short[len];
|
||||
for (int i = 0; i < len; ++i) result[i] = byteBuffer.getShort();
|
||||
return result;
|
||||
}
|
||||
|
||||
public short[] GetInt16Array(int len) {
|
||||
short[] result = len > 0 ? new short[len] : null;
|
||||
for (int i = 0; i < len; ++i) result[i] = ReadInt16();
|
||||
public int[] ReadUInt16Array(int len) {
|
||||
if (len < 0) throw new IllegalArgumentException("Length must not be negative");
|
||||
var result = new int[len];
|
||||
for (int i = 0; i < len; ++i) result[i] = byteBuffer.getShort() & 0xFFFF;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private final ByteArrayReader fontReader;
|
||||
private final Header fileHeader = new Header();
|
||||
private final List<Directory> directorys = new LinkedList<>();
|
||||
private final Map<String, Directory> directorys = new HashMap<>();
|
||||
private final NameLayout name = new NameLayout();
|
||||
private final HeadLayout head = new HeadLayout();
|
||||
private final MaxpLayout maxp = new MaxpLayout();
|
||||
private final List<Integer> loca = new LinkedList<>();
|
||||
private final CmapLayout Cmap = new CmapLayout();
|
||||
private final List<String> glyf = new LinkedList<>();
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Pair<Integer, Integer>[] pps = new Pair[]{
|
||||
Pair.of(3, 10),
|
||||
Pair.of(0, 4),
|
||||
Pair.of(3, 1),
|
||||
Pair.of(1, 0),
|
||||
Pair.of(0, 3),
|
||||
Pair.of(0, 1)
|
||||
private final ConcurrentHashMap<Integer, String> glyf = new ConcurrentHashMap<>();
|
||||
private final int[][] pps = new int[][]{
|
||||
{3, 10},
|
||||
{0, 4},
|
||||
{3, 1},
|
||||
{1, 0},
|
||||
{0, 3},
|
||||
{0, 1}
|
||||
};
|
||||
|
||||
public final Map<Integer, String> unicodeToGlyph = new HashMap<>();
|
||||
public final Map<String, Integer> glyphToUnicode = new HashMap<>();
|
||||
public final Map<Integer, Integer> unicodeToGlyphIndex = new HashMap<>();
|
||||
|
||||
private void readNameTable(byte[] buffer) {
|
||||
var dataTable = Objects.requireNonNull(directorys.get("name"));
|
||||
var reader = new ByteArrayReader(buffer, dataTable.offset);
|
||||
name.format = reader.ReadUInt16();
|
||||
name.count = reader.ReadUInt16();
|
||||
name.stringOffset = reader.ReadUInt16();
|
||||
for (int i = 0; i < name.count; ++i) {
|
||||
NameRecord record = new NameRecord();
|
||||
record.platformID = reader.ReadUInt16();
|
||||
record.encodingID = reader.ReadUInt16();
|
||||
record.languageID = reader.ReadUInt16();
|
||||
record.nameID = reader.ReadUInt16();
|
||||
record.length = reader.ReadUInt16();
|
||||
record.offset = reader.ReadUInt16();
|
||||
name.records.add(record);
|
||||
}
|
||||
}
|
||||
|
||||
private void readHeadTable(byte[] buffer) {
|
||||
var dataTable = Objects.requireNonNull(directorys.get("head"));
|
||||
var reader = new ByteArrayReader(buffer, dataTable.offset);
|
||||
head.majorVersion = reader.ReadUInt16();
|
||||
head.minorVersion = reader.ReadUInt16();
|
||||
head.fontRevision = reader.ReadUInt32();
|
||||
head.checkSumAdjustment = reader.ReadUInt32();
|
||||
head.magicNumber = reader.ReadUInt32();
|
||||
head.flags = reader.ReadUInt16();
|
||||
head.unitsPerEm = reader.ReadUInt16();
|
||||
head.created = reader.ReadUInt64();
|
||||
head.modified = reader.ReadUInt64();
|
||||
head.xMin = reader.ReadInt16();
|
||||
head.yMin = reader.ReadInt16();
|
||||
head.xMax = reader.ReadInt16();
|
||||
head.yMax = reader.ReadInt16();
|
||||
head.macStyle = reader.ReadUInt16();
|
||||
head.lowestRecPPEM = reader.ReadUInt16();
|
||||
head.fontDirectionHint = reader.ReadInt16();
|
||||
head.indexToLocFormat = reader.ReadInt16();
|
||||
head.glyphDataFormat = reader.ReadInt16();
|
||||
}
|
||||
|
||||
private void readCmapTable(byte[] buffer) {
|
||||
var dataTable = Objects.requireNonNull(directorys.get("cmap"));
|
||||
var reader = new ByteArrayReader(buffer, dataTable.offset);
|
||||
Cmap.version = reader.ReadUInt16();
|
||||
Cmap.numTables = reader.ReadUInt16();
|
||||
for (int i = 0; i < Cmap.numTables; ++i) {
|
||||
CmapRecord record = new CmapRecord();
|
||||
record.platformID = reader.ReadUInt16();
|
||||
record.encodingID = reader.ReadUInt16();
|
||||
record.offset = reader.ReadUInt32();
|
||||
Cmap.records.add(record);
|
||||
}
|
||||
for (int i = 0; i < Cmap.numTables; ++i) {
|
||||
int fmtOffset = Cmap.records.get(i).offset;
|
||||
reader.position(dataTable.offset + fmtOffset);
|
||||
int EndIndex = reader.position();
|
||||
|
||||
int format = reader.ReadUInt16();
|
||||
if (Cmap.tables.containsKey(fmtOffset)) continue;
|
||||
if (format == 0) {
|
||||
CmapFormat f = new CmapFormat();
|
||||
f.format = format;
|
||||
f.length = reader.ReadUInt16();
|
||||
f.language = reader.ReadUInt16();
|
||||
f.glyphIdArray = reader.ReadByteArray(f.length - 6);
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
} else if (format == 4) {
|
||||
CmapFormat4 f = new CmapFormat4();
|
||||
f.format = format;
|
||||
f.length = reader.ReadUInt16();
|
||||
f.language = reader.ReadUInt16();
|
||||
f.segCountX2 = reader.ReadUInt16();
|
||||
int segCount = f.segCountX2 >> 1;
|
||||
f.searchRange = reader.ReadUInt16();
|
||||
f.entrySelector = reader.ReadUInt16();
|
||||
f.rangeShift = reader.ReadUInt16();
|
||||
f.endCode = reader.ReadUInt16Array(segCount);
|
||||
f.reservedPad = reader.ReadUInt16();
|
||||
f.startCode = reader.ReadUInt16Array(segCount);
|
||||
f.idDelta = reader.ReadInt16Array(segCount);
|
||||
f.idRangeOffset = reader.ReadUInt16Array(segCount);
|
||||
f.glyphIdArray = reader.ReadUInt16Array((EndIndex + f.length - reader.position()) >> 1);
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
} else if (format == 6) {
|
||||
CmapFormat6 f = new CmapFormat6();
|
||||
f.format = format;
|
||||
f.length = reader.ReadUInt16();
|
||||
f.language = reader.ReadUInt16();
|
||||
f.firstCode = reader.ReadUInt16();
|
||||
f.entryCount = reader.ReadUInt16();
|
||||
f.glyphIdArray = reader.ReadUInt16Array(f.entryCount);
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
} else if (format == 12) {
|
||||
CmapFormat12 f = new CmapFormat12();
|
||||
f.format = format;
|
||||
f.reserved = reader.ReadUInt16();
|
||||
f.length = reader.ReadUInt32();
|
||||
f.language = reader.ReadUInt32();
|
||||
f.numGroups = reader.ReadUInt32();
|
||||
f.groups = new LinkedList<>();
|
||||
for (int n = 0; n < f.numGroups; ++n) {
|
||||
f.groups.add(new int[]{reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32()});
|
||||
}
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readLocaTable(byte[] buffer) {
|
||||
var dataTable = Objects.requireNonNull(directorys.get("loca"));
|
||||
var reader = new ByteArrayReader(buffer, dataTable.offset);
|
||||
var locaTableSize = dataTable.length;
|
||||
if (head.indexToLocFormat == 0) {
|
||||
for (long i = 0; i < locaTableSize; i += 2) loca.add(reader.ReadUInt16());
|
||||
} else {
|
||||
for (long i = 0; i < locaTableSize; i += 4) loca.add(reader.ReadUInt32());
|
||||
}
|
||||
}
|
||||
|
||||
private void readMaxpTable(byte[] buffer) {
|
||||
var dataTable = Objects.requireNonNull(directorys.get("maxp"));
|
||||
var reader = new ByteArrayReader(buffer, dataTable.offset);
|
||||
maxp.majorVersion = reader.ReadUInt16();
|
||||
maxp.minorVersion = reader.ReadUInt16();
|
||||
maxp.numGlyphs = reader.ReadUInt16();
|
||||
maxp.maxPoints = reader.ReadUInt16();
|
||||
maxp.maxContours = reader.ReadUInt16();
|
||||
maxp.maxCompositePoints = reader.ReadUInt16();
|
||||
maxp.maxCompositeContours = reader.ReadUInt16();
|
||||
maxp.maxZones = reader.ReadUInt16();
|
||||
maxp.maxTwilightPoints = reader.ReadUInt16();
|
||||
maxp.maxStorage = reader.ReadUInt16();
|
||||
maxp.maxFunctionDefs = reader.ReadUInt16();
|
||||
maxp.maxInstructionDefs = reader.ReadUInt16();
|
||||
maxp.maxStackElements = reader.ReadUInt16();
|
||||
maxp.maxSizeOfInstructions = reader.ReadUInt16();
|
||||
maxp.maxComponentElements = reader.ReadUInt16();
|
||||
maxp.maxComponentDepth = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
private void readGlyfTable(byte[] buffer) {
|
||||
var dataTable = Objects.requireNonNull(directorys.get("glyf"));
|
||||
int glyfCount = maxp.numGlyphs;
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
for (int i = 0; i < glyfCount; i++) {
|
||||
final int index = i;
|
||||
final var reader = new ByteArrayReader(buffer, dataTable.offset + loca.get(index));
|
||||
executor.submit(() -> {
|
||||
int glyfNextIndex = index + 1 < glyfCount ? (dataTable.offset + loca.get(index + 1)) : (dataTable.offset + dataTable.length);
|
||||
byte[] glyph;
|
||||
short numberOfContours = reader.ReadInt16();
|
||||
if (numberOfContours > 0) {
|
||||
short g_xMin = reader.ReadInt16();
|
||||
short g_yMin = reader.ReadInt16();
|
||||
short g_xMax = reader.ReadInt16();
|
||||
short g_yMax = reader.ReadInt16();
|
||||
int[] endPtsOfContours = reader.ReadUInt16Array(numberOfContours);
|
||||
int instructionLength = reader.ReadUInt16();
|
||||
var instructions = reader.ReadByteArray(instructionLength);
|
||||
int flagLength = endPtsOfContours[endPtsOfContours.length - 1] + 1;
|
||||
// 获取轮廓点描述标志
|
||||
var flags = new byte[flagLength];
|
||||
for (int n = 0; n < flagLength; ++n) {
|
||||
flags[n] = reader.ReadInt8();
|
||||
if ((flags[n] & 0x08) != 0x00) {
|
||||
for (int m = reader.ReadUInt8(); m > 0; --m) {
|
||||
flags[++n] = flags[n - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取轮廓点描述x,y相对值
|
||||
ByteBuffer xyCoordinatesBuffer = ByteBuffer.allocate(flagLength * 4);
|
||||
// 获取轮廓点描述x轴相对值
|
||||
for (int n = 0; n < flagLength; ++n) {
|
||||
short same = (short) ((flags[n] & 0x10) != 0 ? 1 : -1);
|
||||
if ((flags[n] & 0x02) != 0) {
|
||||
xyCoordinatesBuffer.putShort((short) (same * reader.ReadUInt8()));
|
||||
} else {
|
||||
xyCoordinatesBuffer.putShort(same == 1 ? (short) 0 : reader.ReadInt16());
|
||||
}
|
||||
}
|
||||
// 获取轮廓点描述y轴相对值
|
||||
for (int n = 0; n < flagLength; ++n) {
|
||||
short same = (short) ((flags[n] & 0x20) != 0 ? 1 : -1);
|
||||
if ((flags[n] & 0x04) != 0) {
|
||||
xyCoordinatesBuffer.putShort((short) (same * reader.ReadUInt8()));
|
||||
} else {
|
||||
xyCoordinatesBuffer.putShort(same == 1 ? (short) 0 : reader.ReadInt16());
|
||||
}
|
||||
}
|
||||
// 保存轮廓点描述x,y相对值ByteArray
|
||||
glyph = xyCoordinatesBuffer.array();
|
||||
} else {
|
||||
// 复合字体未做详细处理
|
||||
glyph = reader.ReadByteArray(glyfNextIndex - (reader.position() - 2));
|
||||
}
|
||||
glyf.put(index, getHexFromBytes(glyph));
|
||||
});
|
||||
}
|
||||
executor.shutdown();
|
||||
try {
|
||||
boolean b = executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e("queryTTF", "glyf表解析出错: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeHashTable() {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
for (int i = 0; i < 130000; ++i) {
|
||||
final int key = i;
|
||||
executor.submit(() -> {
|
||||
Integer gid = queryGlyfIndex(key);
|
||||
if (gid < glyf.size()) {
|
||||
unicodeToGlyphIndex.put(key, gid);
|
||||
var val = glyf.get(gid);
|
||||
unicodeToGlyph.put(key, val);
|
||||
if (!glyphToUnicode.containsKey(val)) glyphToUnicode.put(val, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
executor.shutdown();
|
||||
try {
|
||||
boolean b = executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e("queryTTF", "建立Unicode&Glyph映射表出错: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param buffer 传入TTF字体二进制数组
|
||||
*/
|
||||
public QueryTTF(byte[] buffer) {
|
||||
fontReader = new ByteArrayReader(buffer, 0);
|
||||
var fontReader = new ByteArrayReader(buffer, 0);
|
||||
Log.i("queryTTF", "读文件头");
|
||||
// 获取文件头
|
||||
fileHeader.majorVersion = fontReader.ReadUInt16();
|
||||
fileHeader.minorVersion = fontReader.ReadUInt16();
|
||||
@ -254,215 +490,45 @@ public class QueryTTF {
|
||||
// 获取目录
|
||||
for (int i = 0; i < fileHeader.numOfTables; ++i) {
|
||||
Directory d = new Directory();
|
||||
d.tag = fontReader.ReadStrings(4, StandardCharsets.US_ASCII);
|
||||
d.tag = new String(fontReader.ReadByteArray(4), StandardCharsets.US_ASCII);
|
||||
d.checkSum = fontReader.ReadUInt32();
|
||||
d.offset = fontReader.ReadUInt32();
|
||||
d.length = fontReader.ReadUInt32();
|
||||
directorys.add(d);
|
||||
}
|
||||
// 解析表 name (字体信息,包含版权、名称、作者等...)
|
||||
for (Directory Temp : directorys) {
|
||||
if (Temp.tag.equals("name")) {
|
||||
fontReader.index = Temp.offset;
|
||||
name.format = fontReader.ReadUInt16();
|
||||
name.count = fontReader.ReadUInt16();
|
||||
name.stringOffset = fontReader.ReadUInt16();
|
||||
for (int i = 0; i < name.count; ++i) {
|
||||
NameRecord record = new NameRecord();
|
||||
record.platformID = fontReader.ReadUInt16();
|
||||
record.encodingID = fontReader.ReadUInt16();
|
||||
record.languageID = fontReader.ReadUInt16();
|
||||
record.nameID = fontReader.ReadUInt16();
|
||||
record.length = fontReader.ReadUInt16();
|
||||
record.offset = fontReader.ReadUInt16();
|
||||
name.records.add(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 解析表 head (获取 head.indexToLocFormat)
|
||||
for (Directory Temp : directorys) {
|
||||
if (Temp.tag.equals("head")) {
|
||||
fontReader.index = Temp.offset;
|
||||
head.majorVersion = fontReader.ReadUInt16();
|
||||
head.minorVersion = fontReader.ReadUInt16();
|
||||
head.fontRevision = fontReader.ReadUInt32();
|
||||
head.checkSumAdjustment = fontReader.ReadUInt32();
|
||||
head.magicNumber = fontReader.ReadUInt32();
|
||||
head.flags = fontReader.ReadUInt16();
|
||||
head.unitsPerEm = fontReader.ReadUInt16();
|
||||
head.created = fontReader.ReadUInt64();
|
||||
head.modified = fontReader.ReadUInt64();
|
||||
head.xMin = fontReader.ReadInt16();
|
||||
head.yMin = fontReader.ReadInt16();
|
||||
head.xMax = fontReader.ReadInt16();
|
||||
head.yMax = fontReader.ReadInt16();
|
||||
head.macStyle = fontReader.ReadUInt16();
|
||||
head.lowestRecPPEM = fontReader.ReadUInt16();
|
||||
head.fontDirectionHint = fontReader.ReadInt16();
|
||||
head.indexToLocFormat = fontReader.ReadInt16();
|
||||
head.glyphDataFormat = fontReader.ReadInt16();
|
||||
}
|
||||
}
|
||||
// 解析表 maxp (获取 maxp.numGlyphs)
|
||||
for (Directory Temp : directorys) {
|
||||
if (Temp.tag.equals("maxp")) {
|
||||
fontReader.index = Temp.offset;
|
||||
maxp.majorVersion = fontReader.ReadUInt16();
|
||||
maxp.minorVersion = fontReader.ReadUInt16();
|
||||
maxp.numGlyphs = fontReader.ReadUInt16();
|
||||
maxp.maxPoints = fontReader.ReadUInt16();
|
||||
maxp.maxContours = fontReader.ReadUInt16();
|
||||
maxp.maxCompositePoints = fontReader.ReadUInt16();
|
||||
maxp.maxCompositeContours = fontReader.ReadUInt16();
|
||||
maxp.maxZones = fontReader.ReadUInt16();
|
||||
maxp.maxTwilightPoints = fontReader.ReadUInt16();
|
||||
maxp.maxStorage = fontReader.ReadUInt16();
|
||||
maxp.maxFunctionDefs = fontReader.ReadUInt16();
|
||||
maxp.maxInstructionDefs = fontReader.ReadUInt16();
|
||||
maxp.maxStackElements = fontReader.ReadUInt16();
|
||||
maxp.maxSizeOfInstructions = fontReader.ReadUInt16();
|
||||
maxp.maxComponentElements = fontReader.ReadUInt16();
|
||||
maxp.maxComponentDepth = fontReader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
// 解析表 loca (轮廓数据偏移地址表)
|
||||
for (Directory Temp : directorys) {
|
||||
if (Temp.tag.equals("loca")) {
|
||||
fontReader.index = Temp.offset;
|
||||
int offset = head.indexToLocFormat == 0 ? 2 : 4;
|
||||
for (long i = 0; i < Temp.length; i += offset) {
|
||||
loca.add(offset == 2 ? fontReader.ReadUInt16() << 1 : fontReader.ReadUInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 解析表 cmap (Unicode编码轮廓索引对照表)
|
||||
for (Directory Temp : directorys) {
|
||||
if (Temp.tag.equals("cmap")) {
|
||||
fontReader.index = Temp.offset;
|
||||
Cmap.version = fontReader.ReadUInt16();
|
||||
Cmap.numTables = fontReader.ReadUInt16();
|
||||
|
||||
for (int i = 0; i < Cmap.numTables; ++i) {
|
||||
CmapRecord record = new CmapRecord();
|
||||
record.platformID = fontReader.ReadUInt16();
|
||||
record.encodingID = fontReader.ReadUInt16();
|
||||
record.offset = fontReader.ReadUInt32();
|
||||
Cmap.records.add(record);
|
||||
}
|
||||
for (int i = 0; i < Cmap.numTables; ++i) {
|
||||
int fmtOffset = Cmap.records.get(i).offset;
|
||||
fontReader.index = Temp.offset + fmtOffset;
|
||||
int EndIndex = fontReader.index;
|
||||
|
||||
int format = fontReader.ReadUInt16();
|
||||
if (Cmap.tables.containsKey(fmtOffset)) continue;
|
||||
if (format == 0) {
|
||||
CmapFormat f = new CmapFormat();
|
||||
f.format = format;
|
||||
f.length = fontReader.ReadUInt16();
|
||||
f.language = fontReader.ReadUInt16();
|
||||
f.glyphIdArray = fontReader.GetBytes(f.length - 6);
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
} else if (format == 4) {
|
||||
CmapFormat4 f = new CmapFormat4();
|
||||
f.format = format;
|
||||
f.length = fontReader.ReadUInt16();
|
||||
f.language = fontReader.ReadUInt16();
|
||||
f.segCountX2 = fontReader.ReadUInt16();
|
||||
int segCount = f.segCountX2 >> 1;
|
||||
f.searchRange = fontReader.ReadUInt16();
|
||||
f.entrySelector = fontReader.ReadUInt16();
|
||||
f.rangeShift = fontReader.ReadUInt16();
|
||||
f.endCode = fontReader.GetUInt16Array(segCount);
|
||||
f.reservedPad = fontReader.ReadUInt16();
|
||||
f.startCode = fontReader.GetUInt16Array(segCount);
|
||||
f.idDelta = fontReader.GetInt16Array(segCount);
|
||||
f.idRangeOffset = fontReader.GetUInt16Array(segCount);
|
||||
f.glyphIdArray = fontReader.GetUInt16Array((EndIndex + f.length - fontReader.index) >> 1);
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
} else if (format == 6) {
|
||||
CmapFormat6 f = new CmapFormat6();
|
||||
f.format = format;
|
||||
f.length = fontReader.ReadUInt16();
|
||||
f.language = fontReader.ReadUInt16();
|
||||
f.firstCode = fontReader.ReadUInt16();
|
||||
f.entryCount = fontReader.ReadUInt16();
|
||||
f.glyphIdArray = fontReader.GetUInt16Array(f.entryCount);
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
} else if (format == 12) {
|
||||
CmapFormat12 f = new CmapFormat12();
|
||||
f.format = format;
|
||||
f.reserved = fontReader.ReadUInt16();
|
||||
f.length = fontReader.ReadUInt32();
|
||||
f.language = fontReader.ReadUInt32();
|
||||
f.numGroups = fontReader.ReadUInt32();
|
||||
f.groups = new ArrayList<>(f.numGroups);
|
||||
for (int n = 0; n < f.numGroups; ++n) {
|
||||
f.groups.add(Triple.of(fontReader.ReadUInt32(), fontReader.ReadUInt32(), fontReader.ReadUInt32()));
|
||||
}
|
||||
Cmap.tables.put(fmtOffset, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 读取表 glyf (字体轮廓数据表)
|
||||
for (Directory Temp : directorys) {
|
||||
if (Temp.tag.equals("glyf")) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
directorys.put(d.tag, d);
|
||||
}
|
||||
|
||||
// 建立Unicode&Glyph双向表
|
||||
for (int key = 0; key < 130000; ++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);
|
||||
}
|
||||
Log.i("queryTTF", "解析表 name"); // 字体信息,包含版权、名称、作者等...
|
||||
readNameTable(buffer);
|
||||
Log.i("queryTTF", "解析表 head"); // 获取 head.indexToLocFormat
|
||||
readHeadTable(buffer);
|
||||
Log.i("queryTTF", "解析表 cmap"); // Unicode编码->轮廓索引 对照表
|
||||
readCmapTable(buffer);
|
||||
Log.i("queryTTF", "解析表 loca"); // 轮廓数据偏移地址表
|
||||
readLocaTable(buffer);
|
||||
Log.i("queryTTF", "解析表 maxp"); // 获取 maxp.numGlyphs 字体轮廓数量
|
||||
readMaxpTable(buffer);
|
||||
Log.i("queryTTF", "解析表 glyf"); // 字体轮廓数据表,需要解析loca,maxp表后计算
|
||||
readGlyfTable(buffer);
|
||||
Log.i("queryTTF", "建立Unicode&Glyph映射表");
|
||||
makeHashTable();
|
||||
Log.i("queryTTF", "字体处理完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字体信息 (1=字体名称)
|
||||
*
|
||||
* @param nameId 传入十进制字体信息索引
|
||||
* @return 返回查询结果字符串
|
||||
*/
|
||||
public String getNameById(int nameId) {
|
||||
for (Directory Temp : directorys) {
|
||||
if (!Temp.tag.equals("name")) continue;
|
||||
fontReader.index = Temp.offset;
|
||||
break;
|
||||
}
|
||||
for (NameRecord record : name.records) {
|
||||
if (record.nameID != nameId) continue;
|
||||
fontReader.index += name.stringOffset + record.offset;
|
||||
return fontReader.ReadStrings(record.length, record.platformID == 1 ? StandardCharsets.UTF_8 : StandardCharsets.UTF_16BE);
|
||||
}
|
||||
return "error";
|
||||
}
|
||||
// /**
|
||||
// * 获取字体信息 (1=字体名称)
|
||||
// *
|
||||
// * @param nameId 传入十进制字体信息索引
|
||||
// * @return 返回查询结果字符串
|
||||
// */
|
||||
// public String getNameById(int nameId) {
|
||||
// fontReader.index = Objects.requireNonNull(directorys.get("name")).offset;
|
||||
// for (NameRecord record : name.records) {
|
||||
// if (record.nameID != nameId) continue;
|
||||
// fontReader.index += name.stringOffset + record.offset;
|
||||
// return fontReader.ReadStrings(record.length, record.platformID == 1 ? StandardCharsets.UTF_8 : StandardCharsets.UTF_16BE);
|
||||
// }
|
||||
// return "error";
|
||||
// }
|
||||
|
||||
/**
|
||||
* 使用Unicode值查找轮廓索引
|
||||
@ -472,10 +538,11 @@ public class QueryTTF {
|
||||
*/
|
||||
public int queryGlyfIndex(int unicode) {
|
||||
if (unicode == 0) return 0;
|
||||
|
||||
int fmtKey = 0;
|
||||
for (Pair<Integer, Integer> item : pps) {
|
||||
for (var item : pps) {
|
||||
for (CmapRecord record : Cmap.records) {
|
||||
if ((item.getLeft() == record.platformID) && (item.getRight() == record.encodingID)) {
|
||||
if ((item[0] == record.platformID) && (item[1] == record.encodingID)) {
|
||||
fmtKey = record.offset;
|
||||
break;
|
||||
}
|
||||
@ -512,10 +579,10 @@ public class QueryTTF {
|
||||
if (0 <= index && index < tab.glyphIdArray.length) glyfID = tab.glyphIdArray[index];
|
||||
} else if (fmt == 12) {
|
||||
CmapFormat12 tab = (CmapFormat12) table;
|
||||
if (unicode > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0;
|
||||
if (unicode > tab.groups.get(tab.numGroups - 1)[1]) return 0;
|
||||
for (int i = 0; i < tab.numGroups; i++) {
|
||||
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();
|
||||
if (tab.groups.get(i)[0] <= unicode && unicode <= tab.groups.get(i)[1]) {
|
||||
glyfID = tab.groups.get(i)[2] + unicode - tab.groups.get(i)[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -564,14 +631,8 @@ public class QueryTTF {
|
||||
*/
|
||||
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();
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : glyph) hexString.append(String.format("%02X", b));
|
||||
return hexString.toString();
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ import io.legado.app.ui.replace.ReplaceRuleActivity
|
||||
import io.legado.app.ui.replace.edit.ReplaceEditActivity
|
||||
import io.legado.app.ui.widget.PopupAction
|
||||
import io.legado.app.ui.widget.dialog.PhotoDialog
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.utils.ACache
|
||||
import io.legado.app.utils.Debounce
|
||||
import io.legado.app.utils.LogUtils
|
||||
@ -114,6 +113,7 @@ import io.legado.app.utils.observeEvent
|
||||
import io.legado.app.utils.observeEventSticky
|
||||
import io.legado.app.utils.postEvent
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.startActivity
|
||||
import io.legado.app.utils.sysScreenOffTime
|
||||
import io.legado.app.utils.throttle
|
||||
@ -553,7 +553,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
|
||||
R.id.menu_effective_replaces -> showDialogFragment<EffectiveReplacesDialog>()
|
||||
|
||||
R.id.menu_help -> showReadMenuHelp()
|
||||
R.id.menu_help -> showHelp()
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
}
|
||||
@ -1022,11 +1022,6 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun showReadMenuHelp() {
|
||||
val text = String(assets.open("web/help/md/readMenuHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示朗读菜单
|
||||
*/
|
||||
@ -1255,6 +1250,10 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun showHelp() {
|
||||
showHelp("readMenuHelp")
|
||||
}
|
||||
|
||||
/**
|
||||
* 长按图片
|
||||
*/
|
||||
|
@ -150,7 +150,7 @@ class ReadMenu @JvmOverloads constructor(
|
||||
}
|
||||
callBack.upSystemUiVisibility()
|
||||
if (!LocalConfig.readMenuHelpVersionIsLast) {
|
||||
callBack.showReadMenuHelp()
|
||||
callBack.showHelp()
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,7 +580,7 @@ class ReadMenu @JvmOverloads constructor(
|
||||
fun showReadAloudDialog()
|
||||
fun upSystemUiVisibility()
|
||||
fun onClickReadAloud()
|
||||
fun showReadMenuHelp()
|
||||
fun showHelp()
|
||||
fun showLogin()
|
||||
fun payAction()
|
||||
fun disableSource()
|
||||
|
@ -17,8 +17,14 @@ import io.legado.app.ui.login.SourceLoginActivity
|
||||
import io.legado.app.ui.widget.code.addJsPattern
|
||||
import io.legado.app.ui.widget.code.addJsonPattern
|
||||
import io.legado.app.ui.widget.code.addLegadoPattern
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.GSON
|
||||
import io.legado.app.utils.applyTint
|
||||
import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setLayout
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.startActivity
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
|
||||
class HttpTtsEditDialog() : BaseDialogFragment(R.layout.dialog_http_tts_edit, true),
|
||||
@ -111,7 +117,7 @@ class HttpTtsEditDialog() : BaseDialogFragment(R.layout.dialog_http_tts_edit, tr
|
||||
initView(it)
|
||||
}
|
||||
R.id.menu_log -> showDialogFragment<AppLogDialog>()
|
||||
R.id.menu_help -> help()
|
||||
R.id.menu_help -> showHelp("httpTTSHelp")
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -130,11 +136,4 @@ class HttpTtsEditDialog() : BaseDialogFragment(R.layout.dialog_http_tts_edit, tr
|
||||
)
|
||||
}
|
||||
|
||||
private fun help() {
|
||||
val helpStr = String(
|
||||
requireContext().assets.open("web/help/md/httpTTSHelp.md").readBytes()
|
||||
)
|
||||
showDialogFragment(TextDialog(getString(R.string.help), helpStr, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.utils.launch
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import kotlinx.coroutines.launch
|
||||
@ -184,14 +185,9 @@ class BookSourceDebugActivity : VMBaseActivity<ActivitySourceDebugBinding, BookS
|
||||
R.id.menu_book_src -> showDialogFragment(TextDialog("html", viewModel.bookSrc))
|
||||
R.id.menu_toc_src -> showDialogFragment(TextDialog("html", viewModel.tocSrc))
|
||||
R.id.menu_content_src -> showDialogFragment(TextDialog("html", viewModel.contentSrc))
|
||||
R.id.menu_help -> showHelp()
|
||||
R.id.menu_help -> showHelp("debugHelp")
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun showHelp() {
|
||||
val text = String(assets.open("web/help/md/debugHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
}
|
@ -43,7 +43,6 @@ import io.legado.app.ui.config.CheckSourceConfig
|
||||
import io.legado.app.ui.file.HandleFileContract
|
||||
import io.legado.app.ui.qrcode.QrCodeResult
|
||||
import io.legado.app.ui.widget.SelectActionBar
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
|
||||
import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
@ -59,6 +58,7 @@ import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.share
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.startActivity
|
||||
import io.legado.app.utils.toastOnUi
|
||||
@ -134,7 +134,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
initSelectActionBar()
|
||||
resumeCheckSource()
|
||||
if (!LocalConfig.bookSourcesHelpVersionIsLast) {
|
||||
showHelp()
|
||||
showHelp("SourceMBookHelp")
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,7 +248,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
searchView.setQuery(getString(R.string.disabled_explore), true)
|
||||
}
|
||||
|
||||
R.id.menu_help -> showHelp()
|
||||
R.id.menu_help -> showHelp("SourceMBookHelp")
|
||||
}
|
||||
if (item.groupId == R.id.source_group) {
|
||||
searchView.setQuery("group:${item.title}", true)
|
||||
@ -370,11 +370,6 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHelp() {
|
||||
val text = String(assets.open("web/help/md/SourceMBookHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
private fun initLiveDataGroup() {
|
||||
lifecycleScope.launch {
|
||||
appDb.bookSourceDao.flowGroups().conflate().collect {
|
||||
|
@ -22,7 +22,6 @@ import io.legado.app.ui.association.ImportTxtTocRuleDialog
|
||||
import io.legado.app.ui.file.HandleFileContract
|
||||
import io.legado.app.ui.qrcode.QrCodeResult
|
||||
import io.legado.app.ui.widget.SelectActionBar
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
|
||||
import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
@ -34,6 +33,7 @@ import io.legado.app.utils.readText
|
||||
import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
@ -140,7 +140,7 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
|
||||
R.id.menu_import_onLine -> showImportDialog()
|
||||
R.id.menu_import_qr -> qrCodeResult.launch()
|
||||
R.id.menu_import_default -> viewModel.importDefault()
|
||||
R.id.menu_help -> showTxtTocRuleHelp()
|
||||
R.id.menu_help -> showHelp("txtTocRuleHelp")
|
||||
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
@ -244,11 +244,6 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
|
||||
}
|
||||
}
|
||||
|
||||
private fun showTxtTocRuleHelp() {
|
||||
val text = String(assets.open("web/help/md/txtTocRuleHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.menu_enable_selection -> viewModel.enableSelection(*adapter.selection.toTypedArray())
|
||||
|
@ -88,7 +88,7 @@ class TxtTocRuleAdapter(context: Context, private val callBack: CallBack) :
|
||||
bundle.keySet().map {
|
||||
when (it) {
|
||||
"selected" -> cbSource.isChecked = selected.contains(item)
|
||||
"upNmae" -> cbSource.text = item.name
|
||||
"upName" -> cbSource.text = item.name
|
||||
"upExample" -> titleExample.text = item.example
|
||||
"enabled" -> swtEnabled.isChecked = item.enable
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import io.legado.app.lib.theme.primaryColor
|
||||
import io.legado.app.ui.association.ImportTxtTocRuleDialog
|
||||
import io.legado.app.ui.file.HandleFileContract
|
||||
import io.legado.app.ui.qrcode.QrCodeResult
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
import io.legado.app.utils.ACache
|
||||
@ -38,6 +37,7 @@ import io.legado.app.utils.launch
|
||||
import io.legado.app.utils.readText
|
||||
import io.legado.app.utils.setLayout
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
@ -152,7 +152,7 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
R.id.menu_import_onLine -> showImportDialog()
|
||||
R.id.menu_import_qr -> qrCodeResult.launch()
|
||||
R.id.menu_import_default -> viewModel.importDefault()
|
||||
R.id.menu_help -> showTxtTocRuleHelp()
|
||||
R.id.menu_help -> showHelp("txtTocRuleHelp")
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -197,11 +197,6 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
}
|
||||
}
|
||||
|
||||
private fun showTxtTocRuleHelp() {
|
||||
val text = String(requireContext().assets.open("web/help/md/txtTocRuleHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
inner class TocRegexAdapter(context: Context) :
|
||||
RecyclerAdapter<TxtTocRule, ItemTocRegexBinding>(context),
|
||||
ItemTouchCallback.Callback {
|
||||
@ -264,7 +259,7 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
|
||||
} else {
|
||||
bundle.keySet().map {
|
||||
when (it) {
|
||||
"upNmae" -> rbRegexName.text = item.name
|
||||
"upName" -> rbRegexName.text = item.name
|
||||
"upExample" -> titleExample.text = item.example
|
||||
"enabled" -> swtEnabled.isChecked = item.enable
|
||||
"upSelect" -> rbRegexName.isChecked = item.name == selectedName
|
||||
|
@ -35,9 +35,17 @@ import io.legado.app.lib.prefs.fragment.PreferenceFragment
|
||||
import io.legado.app.lib.theme.primaryColor
|
||||
import io.legado.app.ui.about.AppLogDialog
|
||||
import io.legado.app.ui.file.HandleFileContract
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.ui.widget.dialog.WaitDialog
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.applyTint
|
||||
import io.legado.app.utils.checkWrite
|
||||
import io.legado.app.utils.getPrefString
|
||||
import io.legado.app.utils.isContentScheme
|
||||
import io.legado.app.utils.launch
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.toEditable
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.Job
|
||||
@ -150,7 +158,7 @@ class BackupConfigFragment : PreferenceFragment(),
|
||||
listView.setEdgeEffectColor(primaryColor)
|
||||
activity?.addMenuProvider(this, viewLifecycleOwner)
|
||||
if (!LocalConfig.backupHelpVersionIsLast) {
|
||||
showHelp()
|
||||
showHelp("webDavHelp")
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +170,7 @@ class BackupConfigFragment : PreferenceFragment(),
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
when (menuItem.itemId) {
|
||||
R.id.menu_help -> {
|
||||
showHelp()
|
||||
showHelp("webDavHelp")
|
||||
return true
|
||||
}
|
||||
|
||||
@ -171,11 +179,6 @@ class BackupConfigFragment : PreferenceFragment(),
|
||||
return false
|
||||
}
|
||||
|
||||
private fun showHelp() {
|
||||
val text = String(requireContext().assets.open("web/help/md/webDavHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
preferenceManager.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
|
||||
|
@ -23,7 +23,6 @@ import io.legado.app.ui.association.ImportDictRuleDialog
|
||||
import io.legado.app.ui.file.HandleFileContract
|
||||
import io.legado.app.ui.qrcode.QrCodeResult
|
||||
import io.legado.app.ui.widget.SelectActionBar
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
|
||||
import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
@ -35,6 +34,7 @@ import io.legado.app.utils.readText
|
||||
import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
@ -143,7 +143,7 @@ class DictRuleActivity : VMBaseActivity<ActivityDictRuleBinding, DictRuleViewMod
|
||||
R.id.menu_import_onLine -> showImportDialog()
|
||||
R.id.menu_import_qr -> qrCodeResult.launch()
|
||||
R.id.menu_import_default -> viewModel.importDefault()
|
||||
R.id.menu_help -> showDictRuleHelp()
|
||||
R.id.menu_help -> showHelp("dictRuleHelp")
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
}
|
||||
@ -235,9 +235,4 @@ class DictRuleActivity : VMBaseActivity<ActivityDictRuleBinding, DictRuleViewMod
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDictRuleHelp() {
|
||||
val text = String(assets.open("web/help/md/dictRuleHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +148,11 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
|
||||
3 -> list.sortedBy {
|
||||
it.order
|
||||
}
|
||||
|
||||
4 -> list.sortedByDescending {
|
||||
max(it.latestChapterTime, it.durChapterTime)
|
||||
}
|
||||
|
||||
else -> list.sortedByDescending {
|
||||
it.durChapterTime
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ import io.legado.app.ui.dict.rule.DictRuleActivity
|
||||
import io.legado.app.ui.file.FileManageActivity
|
||||
import io.legado.app.ui.main.MainFragmentInterface
|
||||
import io.legado.app.ui.replace.ReplaceRuleActivity
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.utils.LogUtils
|
||||
import io.legado.app.utils.getPrefBoolean
|
||||
import io.legado.app.utils.observeEventSticky
|
||||
@ -37,7 +36,7 @@ import io.legado.app.utils.openUrl
|
||||
import io.legado.app.utils.putPrefBoolean
|
||||
import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.startActivity
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
|
||||
@ -68,10 +67,7 @@ class MyFragment() : BaseFragment(R.layout.fragment_my_config), MainFragmentInte
|
||||
|
||||
override fun onCompatOptionsItemSelected(item: MenuItem) {
|
||||
when (item.itemId) {
|
||||
R.id.menu_help -> {
|
||||
val text = String(requireContext().assets.open("web/help/md/appHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
R.id.menu_help -> showHelp("appHelp")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@ import io.legado.app.ui.file.HandleFileContract
|
||||
import io.legado.app.ui.qrcode.QrCodeResult
|
||||
import io.legado.app.ui.replace.edit.ReplaceEditActivity
|
||||
import io.legado.app.ui.widget.SelectActionBar
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
|
||||
import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
@ -47,6 +46,7 @@ import io.legado.app.utils.readText
|
||||
import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
@ -255,7 +255,7 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
|
||||
allowExtensions = arrayOf("txt", "json")
|
||||
}
|
||||
R.id.menu_import_qr -> qrCodeResult.launch()
|
||||
R.id.menu_help -> showHelp()
|
||||
R.id.menu_help -> showHelp("replaceRuleHelp")
|
||||
R.id.menu_group_null -> {
|
||||
searchView.setQuery(getString(R.string.no_group), true)
|
||||
}
|
||||
@ -324,11 +324,6 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHelp() {
|
||||
val text = String(assets.open("web/help/md/replaceRuleHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
observeReplaceRuleData(newText)
|
||||
return false
|
||||
|
@ -28,7 +28,6 @@ import io.legado.app.ui.file.HandleFileContract
|
||||
import io.legado.app.ui.qrcode.QrCodeResult
|
||||
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
|
||||
import io.legado.app.ui.widget.SelectActionBar
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
|
||||
import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
@ -44,6 +43,7 @@ import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
import io.legado.app.utils.share
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.startActivity
|
||||
import io.legado.app.utils.toastOnUi
|
||||
@ -163,7 +163,7 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
|
||||
R.id.menu_group_null -> {
|
||||
searchView.setQuery(getString(R.string.no_group), true)
|
||||
}
|
||||
R.id.menu_help -> showHelp()
|
||||
R.id.menu_help -> showHelp("SourceMRssHelp")
|
||||
else -> if (item.groupId == R.id.source_group) {
|
||||
searchView.setQuery("group:${item.title}", true)
|
||||
}
|
||||
@ -354,11 +354,6 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHelp() {
|
||||
val text = String(assets.open("web/help/md/SourceMRssHelp.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
|
||||
}
|
||||
|
||||
override fun upCountView() {
|
||||
binding.selectActionBar.upCountView(
|
||||
adapter.selection.size,
|
||||
|
@ -203,8 +203,10 @@ val Activity.navigationBarGravity: Int
|
||||
return gravity ?: Gravity.BOTTOM
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示目录help下的帮助文档
|
||||
*/
|
||||
fun AppCompatActivity.showHelp(fileName: String) {
|
||||
//显示目录help下的帮助文档
|
||||
val mdText = String(assets.open("web/help/md/${fileName}.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), mdText, TextDialog.Mode.MD))
|
||||
}
|
@ -32,14 +32,16 @@ object ChineseUtils {
|
||||
fun fixT2sDict() {
|
||||
val dict = DictionaryContainer.getInstance().getDictionary(TransType.TRADITIONAL_TO_SIMPLE)
|
||||
dict.run {
|
||||
remove("劈", "脊", "槃")
|
||||
remove("支援", "沈默", "類比", "模擬", "划槳", "列根", "先進", "雪梨", "雪糕")
|
||||
remove("零錢", "零钱", "離線", "碟片", "模組", "桌球", "案頭", "機車", "電漿")
|
||||
remove("鳳梨", "魔戒", "載入")
|
||||
remove("路易斯", "非同步", "出租车", "周杰倫", "马铃薯", "馬鈴薯", "機械人", "電單車")
|
||||
remove("電扶梯", "音效卡", "飆車族", "點陣圖", "個入球", "顆進球")
|
||||
remove("魔獸紀元", "高空彈跳", "铁达尼号")
|
||||
remove("魔鬼終結者", "純文字檔案")
|
||||
remove(
|
||||
"劈", "脊", "槃",
|
||||
"支援", "沈默", "類比", "模擬", "划槳", "列根", "先進", "雪梨", "雪糕",
|
||||
"零錢", "零钱", "離線", "碟片", "模組", "桌球", "案頭", "機車", "電漿",
|
||||
"鳳梨", "魔戒", "載入",
|
||||
"路易斯", "非同步", "出租车", "周杰倫", "马铃薯", "馬鈴薯", "機械人", "電單車",
|
||||
"電扶梯", "音效卡", "飆車族", "點陣圖", "個入球", "顆進球",
|
||||
"魔獸紀元", "高空彈跳", "铁达尼号",
|
||||
"魔鬼終結者", "純文字檔案"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@ import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.edit
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.widget.dialog.TextDialog
|
||||
|
||||
inline fun <reified T : DialogFragment> Fragment.showDialogFragment(
|
||||
arguments: Bundle.() -> Unit = {}
|
||||
@ -76,4 +78,9 @@ inline fun <reified T : Activity> Fragment.startActivity(
|
||||
configIntent: Intent.() -> Unit = {}
|
||||
) {
|
||||
startActivity(Intent(requireContext(), T::class.java).apply(configIntent))
|
||||
}
|
||||
|
||||
fun Fragment.showHelp(fileName: String) {
|
||||
val mdText = String(requireContext().assets.open("web/help/md/${fileName}.md").readBytes())
|
||||
showDialogFragment(TextDialog(getString(R.string.help), mdText, TextDialog.Mode.MD))
|
||||
}
|
Loading…
Reference in New Issue
Block a user