?): Boolean {
+ return list == null || list.size == 0
+ }
+
+ @SuppressLint("ServiceCast")
+ fun copyToClipboard(context: Context, content: String?) {
+ // 从 API11 开始 android 推荐使用 android.content.ClipboardManager
+ // 为了兼容低版本我们这里使用旧版的 android.text.ClipboardManager,虽然提示 deprecated,但不影响使用。
+ val cm: ClipboardManager =
+ context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ // 将文本内容放到系统剪贴板里。
+ cm.setText(content)
+ Toast.makeText(context, "已复制到剪切板", Toast.LENGTH_SHORT).show()
+ }
+
+ fun Int.requireColor(context: Context): androidx.compose.ui.graphics.Color = Color(context.getColor(this))
+
+ /**
+ * 手机震动
+ *
+ * @param context
+ * @param isRepeat 是否重复震动
+ */
+ fun playVibrate(context: Context, isRepeat: Boolean) {
+
+ try {
+ val mVibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ val patern = longArrayOf(100,200,100)
+ var audioAttributes: AudioAttributes? = null
+ /**
+ * 适配android7.0以上版本的震动
+ * 说明:如果发现5.0或6.0版本在app退到后台之后也无法震动,那么只需要改下方的Build.VERSION_CODES.N版本号即可
+ */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ audioAttributes = AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ALARM) //key
+ .build()
+ mVibrator.vibrate(patern, if (isRepeat) 1 else -1, audioAttributes)
+ } else {
+ mVibrator.vibrate(patern, if (isRepeat) 1 else -1)
+ }
+ } catch (ex: Exception) {
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mozilla/xiu/browser/utils/ZipUtils.java b/app/src/main/java/org/mozilla/xiu/browser/utils/ZipUtils.java
new file mode 100644
index 0000000..d1b91c9
--- /dev/null
+++ b/app/src/main/java/org/mozilla/xiu/browser/utils/ZipUtils.java
@@ -0,0 +1,448 @@
+package org.mozilla.xiu.browser.utils;
+
+import android.os.Build;
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+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.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 2016/08/27
+ * desc : utils about zip
+ *
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public final class ZipUtils {
+
+ private static final int BUFFER_LEN = 8192;
+
+ private ZipUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ /**
+ * Zip the files.
+ *
+ * @param srcFiles The source of files.
+ * @param zipFilePath The path of ZIP file.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFiles(final Collection srcFiles,
+ final String zipFilePath)
+ throws IOException {
+ return zipFiles(srcFiles, zipFilePath, null);
+ }
+
+ /**
+ * Zip the files.
+ *
+ * @param srcFilePaths The paths of source files.
+ * @param zipFilePath The path of ZIP file.
+ * @param comment The comment.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFiles(final Collection srcFilePaths,
+ final String zipFilePath,
+ final String comment)
+ throws IOException {
+ if (srcFilePaths == null || zipFilePath == null) return false;
+ ZipOutputStream zos = null;
+ try {
+ zos = new ZipOutputStream(new FileOutputStream(zipFilePath));
+ for (String srcFile : srcFilePaths) {
+ if (!zipFile(getFileByPath(srcFile), "", zos, comment)) return false;
+ }
+ return true;
+ } finally {
+ if (zos != null) {
+ zos.finish();
+ zos.close();
+ }
+ }
+ }
+
+ /**
+ * Zip the files.
+ *
+ * @param srcFiles The source of files.
+ * @param zipFile The ZIP file.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFiles(final Collection srcFiles, final File zipFile)
+ throws IOException {
+ return zipFiles(srcFiles, zipFile, null);
+ }
+
+ /**
+ * Zip the files.
+ *
+ * @param srcFiles The source of files.
+ * @param zipFile The ZIP file.
+ * @param comment The comment.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFiles(final Collection srcFiles,
+ final File zipFile,
+ final String comment)
+ throws IOException {
+ if (srcFiles == null || zipFile == null) return false;
+ ZipOutputStream zos = null;
+ try {
+ zos = new ZipOutputStream(new FileOutputStream(zipFile));
+ for (File srcFile : srcFiles) {
+ if (!zipFile(srcFile, "", zos, comment)) return false;
+ }
+ return true;
+ } finally {
+ if (zos != null) {
+ zos.finish();
+ zos.close();
+ }
+ }
+ }
+
+ /**
+ * Zip the file.
+ *
+ * @param srcFilePath The path of source file.
+ * @param zipFilePath The path of ZIP file.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFile(final String srcFilePath,
+ final String zipFilePath)
+ throws IOException {
+ return zipFile(getFileByPath(srcFilePath), getFileByPath(zipFilePath), null);
+ }
+
+ /**
+ * Zip the file.
+ *
+ * @param srcFilePath The path of source file.
+ * @param zipFilePath The path of ZIP file.
+ * @param comment The comment.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFile(final String srcFilePath,
+ final String zipFilePath,
+ final String comment)
+ throws IOException {
+ return zipFile(getFileByPath(srcFilePath), getFileByPath(zipFilePath), comment);
+ }
+
+ /**
+ * Zip the file.
+ *
+ * @param srcFile The source of file.
+ * @param zipFile The ZIP file.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFile(final File srcFile,
+ final File zipFile)
+ throws IOException {
+ return zipFile(srcFile, zipFile, null);
+ }
+
+ /**
+ * Zip the file.
+ *
+ * @param srcFile The source of file.
+ * @param zipFile The ZIP file.
+ * @param comment The comment.
+ * @return {@code true}: success
{@code false}: fail
+ * @throws IOException if an I/O error has occurred
+ */
+ public static boolean zipFile(final File srcFile,
+ final File zipFile,
+ final String comment)
+ throws IOException {
+ if (srcFile == null || zipFile == null) return false;
+ try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
+ return zipFile(srcFile, "", zos, comment);
+ }
+ }
+
+ private static boolean zipFile(final File srcFile,
+ String rootPath,
+ final ZipOutputStream zos,
+ final String comment) throws IOException {
+ if (!srcFile.exists()) return true;
+ rootPath = rootPath + (isSpace(rootPath) ? "" : File.separator) + srcFile.getName();
+ if (srcFile.isDirectory()) {
+ File[] fileList = srcFile.listFiles();
+ if (fileList == null || fileList.length <= 0) {
+ String[] ss = rootPath.split(File.separator);
+ String entryPath = rootPath.substring(ss[0].length() + 1);
+ ZipEntry entry = new ZipEntry(entryPath + "/");
+ entry.setComment(comment);
+ zos.putNextEntry(entry);
+ zos.closeEntry();
+ } else {
+ for (File file : fileList) {
+ if (!zipFile(file, rootPath, zos, comment)) return false;
+ }
+ }
+ } else {
+ try (InputStream is = new BufferedInputStream(new FileInputStream(srcFile))) {
+ //去掉第一层目录,免得a.zip解压成a,再压缩变成了a/a/xxx多一层目录
+ String[] ss = rootPath.split(File.separator);
+ String entryPath = rootPath.substring(ss[0].length() + 1);
+ ZipEntry entry = new ZipEntry(entryPath);
+ entry.setComment(comment);
+ zos.putNextEntry(entry);
+ byte buffer[] = new byte[BUFFER_LEN];
+ int len;
+ while ((len = is.read(buffer, 0, BUFFER_LEN)) != -1) {
+ zos.write(buffer, 0, len);
+ }
+ zos.closeEntry();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Unzip the file.
+ *
+ * @param zipFilePath The path of ZIP file.
+ * @param destDirPath The path of destination directory.
+ * @return the unzipped files
+ * @throws IOException if unzip unsuccessfully
+ */
+ public static List unzipFile(final String zipFilePath, final String destDirPath) throws IOException {
+ return unzipFileByKeyword(zipFilePath, destDirPath, null);
+ }
+
+ /**
+ * Unzip the file.
+ *
+ * @param zipFile The ZIP file.
+ * @param destDir The destination directory.
+ * @return the unzipped files
+ * @throws IOException if unzip unsuccessfully
+ */
+ public static List unzipFile(final File zipFile,
+ final File destDir)
+ throws IOException {
+ return unzipFileByKeyword(zipFile, destDir, null);
+ }
+
+ /**
+ * Unzip the file by keyword.
+ *
+ * @param zipFilePath The path of ZIP file.
+ * @param destDirPath The path of destination directory.
+ * @param keyword The keyboard.
+ * @return the unzipped files
+ * @throws IOException if unzip unsuccessfully
+ */
+ public static List