AiUtil.java
// SPDX-License-Identifier: Apache-2.0
package com.stkouyu.util;
import android.content.Context;
import com.shengzhiai.yugu.stcompat.internal.Codec;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/** File, asset and hashing helpers of the Shengtong SDK. Native resource helpers are kept for compatibility. */
public class AiUtil {
/** SHA-1 of the UTF-8 text as lower-case hex, null for null. */
public static String sha1(String text) {
if (text == null) {
return null;
}
return Codec.hex(Codec.digest("SHA-1", text.getBytes(Codec.UTF_8)));
}
/** MD5 of a stream as lower-case hex (the stream is read to the end, not closed), null on error. */
public static String md5(Context context, InputStream in) {
if (in == null) {
return null;
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) > 0) {
md.update(buf, 0, n);
}
return Codec.hex(md.digest());
} catch (IOException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
}
}
/** Number of words: runs of Latin letters, digits and apostrophes, plus each CJK character. */
public static long getWordCount(String text) {
if (text == null) {
return 0;
}
long count = 0;
boolean inWord = false;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (isHanzi(c)) {
count++;
inWord = false;
} else if (Character.isLetterOrDigit(c) || c == '\'') {
if (!inWord) {
count++;
inWord = true;
}
} else {
inWord = false;
}
}
return count;
}
/** Number of CJK unified ideographs. */
public static long getHanziCount(String text) {
if (text == null) {
return 0;
}
long count = 0;
for (int i = 0; i < text.length(); i++) {
if (isHanzi(text.charAt(i))) {
count++;
}
}
return count;
}
private static boolean isHanzi(char c) {
return (c >= '一' && c <= '鿿') || (c >= '㐀' && c <= '䶿') || (c >= '豈' && c <= '');
}
/** {@code getExternalFilesDir(null)}, or the internal files directory when external storage is unavailable. */
public static File externalFilesDir(Context context) {
if (context == null) {
return new File(System.getProperty("java.io.tmpdir", "."));
}
File d = null;
try {
d = context.getExternalFilesDir(null);
} catch (RuntimeException ignored) {
d = null;
}
if (d == null || (!d.isDirectory() && !d.mkdirs() && !d.isDirectory())) {
d = context.getFilesDir();
}
return d;
}
/** UTF-8 content of a file, null when it cannot be read. */
public static String readFile(File file) {
if (file == null || !file.isFile()) {
return null;
}
try {
return readStream(new FileInputStream(file));
} catch (IOException e) {
return null;
}
}
/** UTF-8 content of an asset, null when it cannot be read. */
public static String readFileFromAssets(Context context, String name) {
if (context == null || name == null) {
return null;
}
try {
return readStream(context.getAssets().open(name));
} catch (IOException e) {
return null;
}
}
private static String readStream(InputStream in) throws IOException {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) > 0) {
bos.write(buf, 0, n);
}
return new String(bos.toByteArray(), Codec.UTF_8);
} finally {
in.close();
}
}
public static void writeToFile(String path, String content) {
if (path != null) {
writeToFile(new File(path), content);
}
}
/** Writes UTF-8 text, creating parent directories; errors are ignored. */
public static void writeToFile(File file, String content) {
if (file == null) {
return;
}
try {
OutputStream out = open(file);
try {
out.write((content == null ? "" : content).getBytes(Codec.UTF_8));
} finally {
out.close();
}
} catch (IOException ignored) {
// best effort, like the Shengtong helper
}
}
/** Copies a stream into a file (the stream is closed); errors are ignored. */
public static void writeToFile(File file, InputStream in) {
if (file == null || in == null) {
return;
}
try {
copy(in, file);
} catch (IOException ignored) {
// best effort
}
}
/** Extracts a zip asset into {@code <externalFilesDir>/<name without .zip>}; null on error. */
public static File unzipFileCN(Context context, String assetName) {
return unzipFile(context, assetName);
}
/** Extracts a zip asset into {@code <externalFilesDir>/<name without .zip>}; null on error. */
public static File unzipFile(Context context, String assetName) {
if (context == null || assetName == null) {
return null;
}
String dirName = assetName.endsWith(".zip") ? assetName.substring(0, assetName.length() - 4) : assetName + ".d";
File dir = new File(externalFilesDir(context), dirName);
try {
String root = dir.getCanonicalPath() + File.separator;
ZipInputStream zin = new ZipInputStream(context.getAssets().open(assetName));
try {
ZipEntry e;
while ((e = zin.getNextEntry()) != null) {
File out = new File(dir, e.getName());
if (!out.getCanonicalPath().startsWith(root)) {
throw new IOException("zip entry outside target: " + e.getName());
}
if (e.isDirectory()) {
//noinspection ResultOfMethodCallIgnored
out.mkdirs();
} else {
OutputStream os = open(out);
try {
byte[] buf = new byte[8192];
int n;
while ((n = zin.read(buf)) > 0) {
os.write(buf, 0, n);
}
} finally {
os.close();
}
}
}
} finally {
zin.close();
}
return dir;
} catch (IOException e) {
return null;
}
}
/** Copies an asset into {@code <externalFilesDir>/<name>}; null on error. */
public static File copyDb2SD(Context context, String assetName) {
if (context == null || assetName == null) {
return null;
}
File out = new File(externalFilesDir(context), assetName);
try {
copy(context.getAssets().open(assetName), out);
return out;
} catch (IOException e) {
return null;
}
}
public static File getFilesDir(Context context) {
return context == null ? null : context.getFilesDir();
}
/** Copies an asset into {@code <externalFilesDir>/<name>}. Not needed in cloud mode. */
public static void copyNativeResToSD(Context context, String assetName) throws IOException {
if (context == null || assetName == null) {
throw new IOException("context and asset name are required");
}
copy(context.getAssets().open(assetName), new File(externalFilesDir(context), assetName));
}
private static OutputStream open(File file) throws IOException {
File dir = file.getAbsoluteFile().getParentFile();
if (dir != null && !dir.isDirectory() && !dir.mkdirs() && !dir.isDirectory()) {
throw new IOException("cannot create " + dir);
}
return new FileOutputStream(file);
}
private static void copy(InputStream in, File file) throws IOException {
try {
OutputStream out = open(file);
try {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) > 0) {
out.write(buf, 0, n);
}
} finally {
out.close();
}
} finally {
in.close();
}
}
}