提交 fdf8b3a4 编写于 作者: B Blankj

see 11/01 log

上级 1c36d1ed
......@@ -8,8 +8,6 @@ import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import com.blankj.utilcode.util.ScreenUtils;
/**
* <pre>
* author: Blankj
......
......@@ -400,7 +400,6 @@ public final class LogUtils {
Log.println(type, tag, msg);
return;
}
StringBuilder sb = new StringBuilder();
String[] lines = msg.split(LINE_SEP);
for (String line : lines) {
Log.println(type, tag, LEFT_BORDER + line);
......@@ -577,16 +576,6 @@ public final class LogUtils {
return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
}
private static boolean isSpace(final String s) {
if (s == null) return true;
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
private static void input2File(final String input, final String filePath) {
EXECUTOR.execute(new Runnable() {
@Override
......
@file:JvmName("LogUtils")
package com.blankj.utilcode.util
import android.content.ClipData
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.support.annotation.IntDef
import android.support.annotation.IntRange
import android.support.annotation.RequiresApi
import android.support.v4.util.SimpleArrayMap
import android.util.Log
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.*
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.net.UnknownHostException
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executors
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource
const val V = Log.VERBOSE
const val D = Log.DEBUG
const val I = Log.INFO
const val W = Log.WARN
const val E = Log.ERROR
const val A = Log.ASSERT
@IntDef(V, D, I, W, E, A)
@Retention(AnnotationRetention.SOURCE)
annotation class TYPE
private val T = charArrayOf('V', 'D', 'I', 'W', 'E', 'A')
private val FILE = 0x10
private val JSON = 0x20
private val XML = 0x30
private const val TOP_CORNER = "┌"
private const val MIDDLE_CORNER = "├"
private const val LEFT_BORDER = "│ "
private const val BOTTOM_CORNER = "└"
private const val SIDE_DIVIDER = "────────────────────────────────────────────────────────"
private const val MIDDLE_DIVIDER = "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
private const val TOP_BORDER = TOP_CORNER + SIDE_DIVIDER + SIDE_DIVIDER
private const val MIDDLE_BORDER = MIDDLE_CORNER + MIDDLE_DIVIDER + MIDDLE_DIVIDER
private const val BOTTOM_BORDER = BOTTOM_CORNER + SIDE_DIVIDER + SIDE_DIVIDER
private const val MAX_LEN = 3000
private const val NOTHING = "log nothing"
private const val NULL = "null"
private const val ARGS = "args"
private const val PLACEHOLDER = " "
private val FILE_SEP = System.getProperty("file.separator")
private val LINE_SEP = System.getProperty("line.separator")
private val FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ", Locale.getDefault())
private val CONFIG = Config()
private val EXECUTOR = Executors.newSingleThreadExecutor()
private val I_FORMATTER_MAP = SimpleArrayMap<Class<*>, IFormatter<*>>()
fun getConfig(): Config {
return CONFIG
}
fun v(vararg contents: Any) {
log(V, CONFIG.mGlobalTag, *contents)
}
fun vTag(tag: String, vararg contents: Any) {
log(V, tag, *contents)
}
fun d(vararg contents: Any) {
log(D, CONFIG.mGlobalTag, *contents)
}
fun dTag(tag: String, vararg contents: Any) {
log(D, tag, *contents)
}
fun i(vararg contents: Any) {
log(I, CONFIG.mGlobalTag, *contents)
}
fun iTag(tag: String, vararg contents: Any) {
log(I, tag, *contents)
}
fun w(vararg contents: Any) {
log(W, CONFIG.mGlobalTag, *contents)
}
fun wTag(tag: String, vararg contents: Any) {
log(W, tag, *contents)
}
fun e(vararg contents: Any) {
log(E, CONFIG.mGlobalTag, *contents)
}
fun eTag(tag: String, vararg contents: Any) {
log(E, tag, *contents)
}
fun a(vararg contents: Any) {
log(A, CONFIG.mGlobalTag, *contents)
}
fun aTag(tag: String, vararg contents: Any) {
log(A, tag, *contents)
}
fun file(content: Any) {
log(FILE or D, CONFIG.mGlobalTag, content)
}
fun file(@TYPE type: Int, content: Any) {
log(FILE or type, CONFIG.mGlobalTag, content)
}
fun file(tag: String, content: Any) {
log(FILE or D, tag, content)
}
fun file(@TYPE type: Int, tag: String, content: Any) {
log(FILE or type, tag, content)
}
fun json(content: String) {
log(JSON or D, CONFIG.mGlobalTag, content)
}
fun json(@TYPE type: Int, content: String) {
log(JSON or type, CONFIG.mGlobalTag, content)
}
fun json(tag: String, content: String) {
log(JSON or D, tag, content)
}
fun json(@TYPE type: Int, tag: String, content: String) {
log(JSON or type, tag, content)
}
fun xml(content: String) {
log(XML or D, CONFIG.mGlobalTag, content)
}
fun xml(@TYPE type: Int, content: String) {
log(XML or type, CONFIG.mGlobalTag, content)
}
fun xml(tag: String, content: String) {
log(XML or D, tag, content)
}
fun xml(@TYPE type: Int, tag: String, content: String) {
log(XML or type, tag, content)
}
fun log(type: Int, tag: String?, vararg contents: Any) {
if (!CONFIG.mLogSwitch || !CONFIG.mLog2ConsoleSwitch && !CONFIG.mLog2FileSwitch) return
val type_low = type and 0x0f
val type_high = type and 0xf0
if (type_low < CONFIG.mConsoleFilter && type_low < CONFIG.mFileFilter) return
val tagHead = processTagAndHead(tag)
val body = processBody(type_high, *contents)
if (CONFIG.mLog2ConsoleSwitch && type_low >= CONFIG.mConsoleFilter && type_high != FILE) {
print2Console(type_low, tagHead.tag, tagHead.consoleHead, body)
}
if ((CONFIG.mLog2FileSwitch || type_high == FILE) && type_low >= CONFIG.mFileFilter) {
print2File(type_low, tagHead.tag, tagHead.fileHead + body)
}
}
private fun processTagAndHead(tag: String?): TagHead {
var tag = tag
if (!CONFIG.mTagIsSpace && !CONFIG.mLogHeadSwitch) {
tag = CONFIG.mGlobalTag
} else {
val stackTrace = Throwable().stackTrace
val stackIndex = 3 + CONFIG.mStackOffset
if (stackIndex >= stackTrace.size) {
val targetElement = stackTrace[3]
val fileName = getFileName(targetElement)
if (CONFIG.mTagIsSpace && isSpace(tag)) {
val index = fileName.indexOf('.')// Use proguard may not find '.'.
tag = if (index == -1) fileName else fileName.substring(0, index)
}
return TagHead(tag, null, ": ")
}
var targetElement = stackTrace[stackIndex]
val fileName = getFileName(targetElement)
if (CONFIG.mTagIsSpace && isSpace(tag)) {
val index = fileName.indexOf('.')// Use proguard may not find '.'.
tag = if (index == -1) fileName else fileName.substring(0, index)
}
if (CONFIG.mLogHeadSwitch) {
val tName = Thread.currentThread().name
val head = Formatter()
.format("%s, %s.%s(%s:%d)",
tName,
targetElement.className,
targetElement.methodName,
fileName,
targetElement.lineNumber)
.toString()
val fileHead = " [$head]: "
if (CONFIG.mStackDeep <= 1) {
return TagHead(tag, arrayOf(head), fileHead)
} else {
val consoleHead = arrayOfNulls<String>(Math.min(
CONFIG.mStackDeep,
stackTrace.size - stackIndex
))
consoleHead[0] = head
val spaceLen = tName.length + 2
val space = Formatter().format("%" + spaceLen + "s", "").toString()
var i = 1
val len = consoleHead.size
while (i < len) {
targetElement = stackTrace[i + stackIndex]
consoleHead[i] = Formatter()
.format("%s%s.%s(%s:%d)",
space,
targetElement.className,
targetElement.methodName,
getFileName(targetElement),
targetElement.lineNumber)
.toString()
++i
}
return TagHead(tag, consoleHead, fileHead)
}
}
}
return TagHead(tag, null, ": ")
}
private fun getFileName(targetElement: StackTraceElement): String {
val fileName = targetElement.fileName
if (fileName != null) return fileName
// If name of file is null, should add
// "-keepattributes SourceFile,LineNumberTable" in proguard file.
var className = targetElement.className
val classNameInfo = className.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (classNameInfo.size > 0) {
className = classNameInfo[classNameInfo.size - 1]
}
val index = className.indexOf('$')
if (index != -1) {
className = className.substring(0, index)
}
return "$className.java"
}
private fun processBody(type: Int, vararg contents: Any): String {
var body = NULL
if (contents != null) {
if (contents.size == 1) {
body = formatObject(type, contents[0])
} else {
val sb = StringBuilder()
var i = 0
val len = contents.size
while (i < len) {
val content = contents[i]
sb.append(ARGS)
.append("[")
.append(i)
.append("]")
.append(" = ")
.append(formatObject(content))
.append(LINE_SEP)
++i
}
body = sb.toString()
}
}
return if (body.length == 0) NOTHING else body
}
private fun formatObject(type: Int, obj: Any?): String {
if (obj == null) return NULL
if (type == JSON) return LogFormatter.formatJson(obj.toString())
return if (type == XML) LogFormatter.formatXml(obj.toString()) else formatObject(obj)
}
private fun formatObject(obj: Any?): String {
if (obj == null) return NULL
if (!I_FORMATTER_MAP.isEmpty) {
val iFormatter = I_FORMATTER_MAP.get(getClassFromObject(obj))
if (iFormatter != null) {
return iFormatter.format(obj)
}
}
if (obj.javaClass.isArray) return LogFormatter.array2String(obj)
if (obj is Throwable) return LogFormatter.throwable2String(obj as Throwable?)
if (obj is Bundle) return LogFormatter.bundle2String((obj as Bundle?)!!)
return if (obj is Intent) LogFormatter.intent2String((obj as Intent?)!!) else obj.toString()
}
private fun print2Console(type: Int,
tag: String,
head: Array<String>,
msg: String) {
if (CONFIG.mSingleTagSwitch) {
printSingleTagMsg(type, tag, processSingleTagMsg(type, tag, head, msg))
} else {
printBorder(type, tag, true)
printHead(type, tag, head)
printMsg(type, tag, msg)
printBorder(type, tag, false)
}
}
private fun printBorder(type: Int, tag: String, isTop: Boolean) {
if (CONFIG.mLogBorderSwitch) {
Log.println(type, tag, if (isTop) TOP_BORDER else BOTTOM_BORDER)
}
}
private fun printHead(type: Int, tag: String, head: Array<String>?) {
if (head != null) {
for (aHead in head) {
Log.println(type, tag, if (CONFIG.mLogBorderSwitch) LEFT_BORDER + aHead else aHead)
}
if (CONFIG.mLogBorderSwitch) Log.println(type, tag, MIDDLE_BORDER)
}
}
private fun printMsg(type: Int, tag: String, msg: String) {
val len = msg.length
val countOfSub = len / MAX_LEN
if (countOfSub > 0) {
var index = 0
for (i in 0 until countOfSub) {
printSubMsg(type, tag, msg.substring(index, index + MAX_LEN))
index += MAX_LEN
}
if (index != len) {
printSubMsg(type, tag, msg.substring(index, len))
}
} else {
printSubMsg(type, tag, msg)
}
}
private fun printSubMsg(type: Int, tag: String, msg: String) {
if (!CONFIG.mLogBorderSwitch) {
Log.println(type, tag, msg)
return
}
val sb = StringBuilder()
val lines = msg.split(LINE_SEP.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (line in lines) {
Log.println(type, tag, LEFT_BORDER + line)
}
}
private fun processSingleTagMsg(type: Int,
tag: String,
head: Array<String>?,
msg: String): String {
val sb = StringBuilder()
sb.append(PLACEHOLDER).append(LINE_SEP)
if (CONFIG.mLogBorderSwitch) {
sb.append(TOP_BORDER).append(LINE_SEP)
if (head != null) {
for (aHead in head) {
sb.append(LEFT_BORDER).append(aHead).append(LINE_SEP)
}
sb.append(MIDDLE_BORDER).append(LINE_SEP)
}
for (line in msg.split(LINE_SEP.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
sb.append(LEFT_BORDER).append(line).append(LINE_SEP)
}
sb.append(BOTTOM_BORDER)
} else {
if (head != null) {
for (aHead in head) {
sb.append(aHead).append(LINE_SEP)
}
}
sb.append(msg)
}
return sb.toString()
}
private fun printSingleTagMsg(type: Int, tag: String, msg: String) {
val len = msg.length
val countOfSub = len / MAX_LEN
if (countOfSub > 0) {
if (CONFIG.mLogBorderSwitch) {
Log.println(type, tag, msg.substring(0, MAX_LEN) + LINE_SEP + BOTTOM_BORDER)
var index = MAX_LEN
for (i in 1 until countOfSub) {
Log.println(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
+ LEFT_BORDER + msg.substring(index, index + MAX_LEN)
+ LINE_SEP + BOTTOM_BORDER)
index += MAX_LEN
}
if (index != len) {
Log.println(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
+ LEFT_BORDER + msg.substring(index, len))
}
} else {
Log.println(type, tag, msg.substring(0, MAX_LEN))
var index = MAX_LEN
for (i in 1 until countOfSub) {
Log.println(type, tag,
PLACEHOLDER + LINE_SEP + msg.substring(index, index + MAX_LEN))
index += MAX_LEN
}
if (index != len) {
Log.println(type, tag, PLACEHOLDER + LINE_SEP + msg.substring(index, len))
}
}
} else {
Log.println(type, tag, msg)
}
}
private fun print2File(type: Int, tag: String, msg: String) {
val now = Date(System.currentTimeMillis())
val format = FORMAT.format(now)
val date = format.substring(0, 10)
val time = format.substring(11)
val fullPath = ((if (CONFIG.mDir == null) CONFIG.mDefaultDir else CONFIG.mDir)
+ CONFIG.mFilePrefix + "-" + date + ".txt")
if (!createOrExistsFile(fullPath)) {
Log.e("LogUtils", "create $fullPath failed!")
return
}
val sb = StringBuilder()
sb.append(time)
.append(T[type - V])
.append("/")
.append(tag)
.append(msg)
.append(LINE_SEP)
val content = sb.toString()
input2File(content, fullPath)
}
private fun createOrExistsFile(filePath: String): Boolean {
val file = File(filePath)
if (file.exists()) return file.isFile()
if (!createOrExistsDir(file.getParentFile())) return false
try {
deleteDueLogs(filePath)
val isCreate = file.createNewFile()
if (isCreate) {
printDeviceInfo(filePath)
}
return isCreate
} catch (e: IOException) {
e.printStackTrace()
return false
}
}
private fun deleteDueLogs(filePath: String) {
val file = File(filePath)
val parentFile = file.getParentFile()
val files = parentFile.listFiles(object : FilenameFilter() {
fun accept(dir: File, name: String): Boolean {
return name.matches(("^" + CONFIG.mFilePrefix + "-[0-9]{4}-[0-9]{2}-[0-9]{2}.txt$").toRegex())
}
})
if (files.size <= 0) return
val length = filePath.length
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
try {
val curDay = filePath.substring(length - 14, length - 4)
val dueMillis = sdf.parse(curDay).getTime() - CONFIG.mSaveDays * 86400000L
for (aFile in files) {
val name = aFile.getName()
val l = name.length
val logDay = name.substring(l - 14, l - 4)
if (sdf.parse(logDay).getTime() <= dueMillis) {
EXECUTOR.execute(Runnable {
val delete = aFile.delete()
if (!delete) {
Log.e("LogUtils", "delete $aFile failed!")
}
})
}
}
} catch (e: ParseException) {
e.printStackTrace()
}
}
private fun printDeviceInfo(filePath: String) {
var versionName = ""
var versionCode = 0
try {
val pi = Utils.getApp()
.getPackageManager()
.getPackageInfo(Utils.getApp().getPackageName(), 0)
if (pi != null) {
versionName = pi!!.versionName
versionCode = pi!!.versionCode
}
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
val time = filePath.substring(filePath.length - 14, filePath.length - 4)
val head = "************* Log Head ****************" +
"\nDate of Log : " + time +
"\nDevice Manufacturer: " + Build.MANUFACTURER +
"\nDevice Model : " + Build.MODEL +
"\nAndroid Version : " + Build.VERSION.RELEASE +
"\nAndroid SDK : " + Build.VERSION.SDK_INT +
"\nApp VersionName : " + versionName +
"\nApp VersionCode : " + versionCode +
"\n************* Log Head ****************\n\n"
input2File(head, filePath)
}
private fun createOrExistsDir(file: File?): Boolean {
return file != null && if (file!!.exists()) file!!.isDirectory() else file!!.mkdirs()
}
private fun input2File(input: String, filePath: String) {
EXECUTOR.execute(Runnable {
var bw: BufferedWriter? = null
try {
bw = BufferedWriter(FileWriter(filePath, true))
bw!!.write(input)
} catch (e: IOException) {
e.printStackTrace()
Log.e("LogUtils", "log to $filePath failed!")
} finally {
try {
if (bw != null) {
bw!!.close()
}
} catch (e: IOException) {
e.printStackTrace()
}
}
})
}
class Config internal constructor() {
var mDefaultDir: String? = null// The default storage directory of log.
var mDir: String? = null // The storage directory of log.
var mFilePrefix = "util"// The file prefix of log.
var mLogSwitch = true // The switch of log.
var mLog2ConsoleSwitch = true // The logcat's switch of log.
var mGlobalTag: String? = null // The global tag of log.
var mTagIsSpace = true // The global tag is space.
var mLogHeadSwitch = true // The head's switch of log.
var mLog2FileSwitch = false // The file's switch of log.
var mLogBorderSwitch = true // The border's switch of log.
var mSingleTagSwitch = true // The single tag of log.
var mConsoleFilter = V // The console's filter of log.
var mFileFilter = V // The file's filter of log.
var mStackDeep = 1 // The stack's deep of log.
var mStackOffset = 0 // The stack's offset of log.
var mSaveDays = -1 // The save days of log.
init {
if (mDefaultDir != null) return
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && Utils.getApp().getExternalCacheDir() != null)
mDefaultDir = Utils.getApp().getExternalCacheDir() + FILE_SEP + "log" + FILE_SEP
else {
mDefaultDir = Utils.getApp().getCacheDir() + FILE_SEP + "log" + FILE_SEP
}
}
fun setLogSwitch(logSwitch: Boolean): Config {
mLogSwitch = logSwitch
return this
}
fun setConsoleSwitch(consoleSwitch: Boolean): Config {
mLog2ConsoleSwitch = consoleSwitch
return this
}
fun setGlobalTag(tag: String): Config {
if (isSpace(tag)) {
mGlobalTag = ""
mTagIsSpace = true
} else {
mGlobalTag = tag
mTagIsSpace = false
}
return this
}
fun setLogHeadSwitch(logHeadSwitch: Boolean): Config {
mLogHeadSwitch = logHeadSwitch
return this
}
fun setLog2FileSwitch(log2FileSwitch: Boolean): Config {
mLog2FileSwitch = log2FileSwitch
return this
}
fun setDir(dir: String): Config {
if (isSpace(dir)) {
mDir = null
} else {
mDir = if (dir.endsWith(FILE_SEP)) dir else dir + FILE_SEP
}
return this
}
fun setDir(dir: File?): Config {
mDir = if (dir == null) null else dir!!.getAbsolutePath() + FILE_SEP
return this
}
fun setFilePrefix(filePrefix: String): Config {
if (isSpace(filePrefix)) {
mFilePrefix = "util"
} else {
mFilePrefix = filePrefix
}
return this
}
fun setBorderSwitch(borderSwitch: Boolean): Config {
mLogBorderSwitch = borderSwitch
return this
}
fun setSingleTagSwitch(singleTagSwitch: Boolean): Config {
mSingleTagSwitch = singleTagSwitch
return this
}
fun setConsoleFilter(@TYPE consoleFilter: Int): Config {
mConsoleFilter = consoleFilter
return this
}
fun setFileFilter(@TYPE fileFilter: Int): Config {
mFileFilter = fileFilter
return this
}
fun setStackDeep(@IntRange(from = 1) stackDeep: Int): Config {
mStackDeep = stackDeep
return this
}
fun setStackOffset(@IntRange(from = 0) stackOffset: Int): Config {
mStackOffset = stackOffset
return this
}
fun setSaveDays(@IntRange(from = 1) saveDays: Int): Config {
mSaveDays = saveDays
return this
}
fun <T> addFormatter(iFormatter: IFormatter<T>?): Config {
if (iFormatter != null) {
I_FORMATTER_MAP.put(getTypeClassFromParadigm(iFormatter), iFormatter)
}
return this
}
override fun toString(): String {
return ("switch: " + mLogSwitch
+ LINE_SEP + "console: " + mLog2ConsoleSwitch
+ LINE_SEP + "tag: " + (if (mTagIsSpace) "null" else mGlobalTag)
+ LINE_SEP + "head: " + mLogHeadSwitch
+ LINE_SEP + "file: " + mLog2FileSwitch
+ LINE_SEP + "dir: " + (if (mDir == null) mDefaultDir else mDir)
+ LINE_SEP + "filePrefix: " + mFilePrefix
+ LINE_SEP + "border: " + mLogBorderSwitch
+ LINE_SEP + "singleTag: " + mSingleTagSwitch
+ LINE_SEP + "consoleFilter: " + T[mConsoleFilter - V]
+ LINE_SEP + "fileFilter: " + T[mFileFilter - V]
+ LINE_SEP + "stackDeep: " + mStackDeep
+ LINE_SEP + "stackOffset: " + mStackOffset
+ LINE_SEP + "saveDays: " + mSaveDays
+ LINE_SEP + "formatter: " + I_FORMATTER_MAP)
}
}
abstract class IFormatter<T> {
abstract fun format(t: T): String
}
private class TagHead internal constructor(internal var tag: String,
internal var consoleHead: Array<String>,
internal var fileHead: String)
private object LogFormatter {
internal fun formatJson(json: String): String {
var json = json
try {
if (json.startsWith("{")) {
json = JSONObject(json).toString(4)
} else if (json.startsWith("[")) {
json = JSONArray(json).toString(4)
}
} catch (e: JSONException) {
e.printStackTrace()
}
return json
}
internal fun formatXml(xml: String): String {
var xml = xml
try {
val xmlInput = StreamSource(StringReader(xml))
val xmlOutput = StreamResult(StringWriter())
val transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4")
transformer.transform(xmlInput, xmlOutput)
xml = xmlOutput.getWriter().toString().replaceFirst(">", ">$LINE_SEP")
} catch (e: Exception) {
e.printStackTrace()
}
return xml
}
internal fun array2String(obj: Any): String {
if (obj is Array<*>) {
return Arrays.deepToString(obj)
} else if (obj is BooleanArray) {
return Arrays.toString(obj)
} else if (obj is ByteArray) {
return Arrays.toString(obj)
} else if (obj is CharArray) {
return Arrays.toString(obj)
} else if (obj is DoubleArray) {
return Arrays.toString(obj)
} else if (obj is FloatArray) {
return Arrays.toString(obj)
} else if (obj is IntArray) {
return Arrays.toString(obj)
} else if (obj is LongArray) {
return Arrays.toString(obj)
} else if (obj is ShortArray) {
return Arrays.toString(obj)
}
throw IllegalArgumentException("Array has incompatible type: " + obj.javaClass)
}
internal fun throwable2String(e: Throwable): String {
var t: Throwable? = e
while (t != null) {
if (t is UnknownHostException) {
return ""
}
t = t.cause
}
val sw = StringWriter()
val pw = PrintWriter(sw)
e.printStackTrace(pw)
var cause: Throwable? = e.cause
while (cause != null) {
cause.printStackTrace(pw)
cause = cause.cause
}
pw.flush()
return sw.toString()
}
internal fun bundle2String(bundle: Bundle): String {
val iterator = bundle.keySet().iterator()
if (!iterator.hasNext()) {
return "Bundle {}"
}
val sb = StringBuilder(128)
sb.append("Bundle { ")
while (true) {
val key = iterator.next()
val value = bundle.get(key)
sb.append(key).append('=')
if (value != null && value is Bundle) {
sb.append(if (value === bundle) "(this Bundle)" else bundle2String(value))
} else {
sb.append(formatObject(value))
}
if (!iterator.hasNext()) return sb.append(" }").toString()
sb.append(',').append(' ')
}
}
internal fun intent2String(intent: Intent): String {
val sb = StringBuilder(128)
sb.append("Intent { ")
var first = true
val mAction = intent.action
if (mAction != null) {
sb.append("act=").append(mAction)
first = false
}
val mCategories = intent.categories
if (mCategories != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("cat=[")
var firstCategory = true
for (c in mCategories) {
if (!firstCategory) {
sb.append(',')
}
sb.append(c)
firstCategory = false
}
sb.append("]")
}
val mData = intent.data
if (mData != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("dat=").append(mData)
}
val mType = intent.type
if (mType != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("typ=").append(mType)
}
val mFlags = intent.flags
if (mFlags != 0) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("flg=0x").append(Integer.toHexString(mFlags))
}
val mPackage = intent.getPackage()
if (mPackage != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("pkg=").append(mPackage)
}
val mComponent = intent.component
if (mComponent != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("cmp=").append(mComponent.flattenToShortString())
}
val mSourceBounds = intent.sourceBounds
if (mSourceBounds != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("bnds=").append(mSourceBounds.toShortString())
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val mClipData = intent.clipData
if (mClipData != null) {
if (!first) {
sb.append(' ')
}
first = false
clipData2String(mClipData, sb)
}
}
val mExtras = intent.extras
if (mExtras != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("extras={")
sb.append(bundle2String(mExtras))
sb.append('}')
}
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
val mSelector = intent.selector
if (mSelector != null) {
if (!first) {
sb.append(' ')
}
first = false
sb.append("sel={")
sb.append(if (mSelector === intent) "(this Intent)" else intent2String(mSelector))
sb.append("}")
}
}
sb.append(" }")
return sb.toString()
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private fun clipData2String(clipData: ClipData, sb: StringBuilder) {
val item = clipData.getItemAt(0)
if (item == null) {
sb.append("ClipData.Item {}")
return
}
sb.append("ClipData.Item { ")
val mHtmlText = item.htmlText
if (mHtmlText != null) {
sb.append("H:")
sb.append(mHtmlText)
sb.append("}")
return
}
val mText = item.text
if (mText != null) {
sb.append("T:")
sb.append(mText)
sb.append("}")
return
}
val uri = item.uri
if (uri != null) {
sb.append("U:").append(uri)
sb.append("}")
return
}
val intent = item.intent
if (intent != null) {
sb.append("I:")
sb.append(intent2String(intent))
sb.append("}")
return
}
sb.append("NULL")
sb.append("}")
}
}
fun <T> getTypeClassFromParadigm(formatter: IFormatter<T>): Class<*>? {
val genericInterfaces = formatter.javaClass.genericInterfaces
var type: Type
if (genericInterfaces.size == 1) {
type = genericInterfaces[0]
} else {
type = formatter.javaClass.genericSuperclass
}
type = (type as ParameterizedType).getActualTypeArguments()[0]
while (type is ParameterizedType) {
type = (type as ParameterizedType).getRawType()
}
var className = type.toString()
if (className.startsWith("class ")) {
className = className.substring(6)
} else if (className.startsWith("interface ")) {
className = className.substring(10)
}
try {
return Class.forName(className)
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
return null
}
private fun getClassFromObject(obj: Any): Class<*> {
val objClass = obj.javaClass
if (objClass.isAnonymousClass || objClass.isSynthetic) {
val genericInterfaces = objClass.genericInterfaces
var className: String
if (genericInterfaces.size == 1) {// interface
var type = genericInterfaces[0]
while (type is ParameterizedType) {
type = (type as ParameterizedType).getRawType()
}
className = type.toString()
} else {// abstract class or lambda
var type = objClass.genericSuperclass
while (type is ParameterizedType) {
type = (type as ParameterizedType).getRawType()
}
className = type.toString()
}
if (className.startsWith("class ")) {
className = className.substring(6)
} else if (className.startsWith("interface ")) {
className = className.substring(10)
}
try {
return Class.forName(className)
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
}
return objClass
}
\ No newline at end of file
@file:JvmName("StringUtils")
package com.blankj.utilcode.util
/**
* Return whether the string is null or 0-length.
*
* @param s The string.
* @return `true`: yes<br></br> `false`: no
*/
fun isEmpty(s: CharSequence?): Boolean {
return s == null || s.isEmpty()
}
object StringUtils {
/**
* Return whether the string is null or 0-length.
*
* @param s The string.
* @return `true`: yes<br></br> `false`: no
*/
@JvmStatic
fun isEmpty(s: CharSequence?): Boolean {
return s == null || s.isEmpty()
}
/**
* Return whether the string is null or whitespace.
*
* @param s The string.
* @return `true`: yes<br></br> `false`: no
*/
fun isTrimEmpty(s: String?): Boolean {
return s == null || s.trim { it <= ' ' }.isEmpty()
}
/**
* Return whether the string is null or whitespace.
*
* @param s The string.
* @return `true`: yes<br></br> `false`: no
*/
@JvmStatic
fun isTrimEmpty(s: String?): Boolean {
return s == null || s.trim { it <= ' ' }.isEmpty()
}
/**
* Return whether the string is null or white space.
*
* @param s The string.
* @return `true`: yes<br></br> `false`: no
*/
fun isSpace(s: String?): Boolean {
if (s == null) return true
var i = 0
val len = s.length
while (i < len) {
if (!Character.isWhitespace(s[i])) {
return false
/**
* Return whether the string is null or white space.
*
* @param s The string.
* @return `true`: yes<br></br> `false`: no
*/
@JvmStatic
fun isSpace(s: String?): Boolean {
if (s == null) return true
var i = 0
val len = s.length
while (i < len) {
if (!Character.isWhitespace(s[i])) {
return false
}
++i
}
++i
return true
}
return true
}
/**
* Return whether string1 is equals to string2.
*
* @param s1 The first string.
* @param s2 The second string.
* @return `true`: yes<br></br>`false`: no
*/
fun equals(s1: CharSequence?, s2: CharSequence?): Boolean {
if (s1 === s2) return true
if (s1 != null && s2 != null) {
val length: Int = s1.length
if (length == s2.length) {
if (s1 is String && s2 is String) {
return s1 == s2
} else {
for (i in 0 until length) {
if (s1[i] != s2[i]) return false
/**
* Return whether string1 is equals to string2.
*
* @param s1 The first string.
* @param s2 The second string.
* @return `true`: yes<br></br>`false`: no
*/
@JvmStatic
fun equals(s1: CharSequence?, s2: CharSequence?): Boolean {
if (s1 === s2) return true
if (s1 != null && s2 != null) {
val length: Int = s1.length
if (length == s2.length) {
if (s1 is String && s2 is String) {
return s1 == s2
} else {
for (i in 0 until length) {
if (s1[i] != s2[i]) return false
}
return true
}
return true
}
}
return false
}
return false
}
/**
* Return whether string1 is equals to string2, ignoring case considerations..
*
* @param s1 The first string.
* @param s2 The second string.
* @return `true`: yes<br></br>`false`: no
*/
fun equalsIgnoreCase(s1: String?, s2: String?): Boolean {
return s1.equals(s2, ignoreCase = true)
}
/**
* Return whether string1 is equals to string2, ignoring case considerations..
*
* @param s1 The first string.
* @param s2 The second string.
* @return `true`: yes<br></br>`false`: no
*/
@JvmStatic
fun equalsIgnoreCase(s1: String?, s2: String?): Boolean {
return s1.equals(s2, ignoreCase = true)
}
/**
* Return `""` if string equals null.
*
* @param s The string.
* @return `""` if string equals null
*/
fun null2Length0(s: String?): String {
return s ?: ""
}
/**
* Return `""` if string equals null.
*
* @param s The string.
* @return `""` if string equals null
*/
@JvmStatic
fun null2Length0(s: String?): String {
return s ?: ""
}
/**
* Return the length of string.
*
* @param s The string.
* @return the length of string
*/
fun length(s: CharSequence?): Int {
return s?.length ?: 0
}
/**
* Return the length of string.
*
* @param s The string.
* @return the length of string
*/
@JvmStatic
fun length(s: CharSequence?): Int {
return s?.length ?: 0
}
/**
* Set the first letter of string upper.
*
* @param s The string.
* @return the string with first letter upper.
*/
fun upperFirstLetter(s: String?): String {
if (s == null || s.isEmpty()) return ""
return if (!Character.isLowerCase(s[0])) s else (s[0].toInt() - 32).toChar().toString() + s.substring(1)
}
/**
* Set the first letter of string upper.
*
* @param s The string.
* @return the string with first letter upper.
*/
@JvmStatic
fun upperFirstLetter(s: String?): String {
if (s == null || s.isEmpty()) return ""
return if (!Character.isLowerCase(s[0])) s else (s[0].toInt() - 32).toChar().toString() + s.substring(1)
}
/**
* Set the first letter of string lower.
*
* @param s The string.
* @return the string with first letter lower.
*/
fun lowerFirstLetter(s: String?): String {
if (s == null || s.isEmpty()) return ""
return if (!Character.isUpperCase(s[0])) s else (s[0].toInt() + 32).toChar().toString() + s.substring(1)
}
/**
* Set the first letter of string lower.
*
* @param s The string.
* @return the string with first letter lower.
*/
@JvmStatic
fun lowerFirstLetter(s: String?): String {
if (s == null || s.isEmpty()) return ""
return if (!Character.isUpperCase(s[0])) s else (s[0].toInt() + 32).toChar().toString() + s.substring(1)
}
/**
* Reverse the string.
*
* @param s The string.
* @return the reverse string.
*/
fun reverse(s: String?): String {
if (s == null) return ""
val len = s.length
if (len <= 1) return s
val mid = len shr 1
val chars = s.toCharArray()
var c: Char
for (i in 0 until mid) {
c = chars[i]
chars[i] = chars[len - i - 1]
chars[len - i - 1] = c
/**
* Reverse the string.
*
* @param s The string.
* @return the reverse string.
*/
@JvmStatic
fun reverse(s: String?): String {
if (s == null) return ""
val len = s.length
if (len <= 1) return s
val mid = len shr 1
val chars = s.toCharArray()
var c: Char
for (i in 0 until mid) {
c = chars[i]
chars[i] = chars[len - i - 1]
chars[len - i - 1] = c
}
return String(chars)
}
return String(chars)
}
/**
* Convert string to DBC.
*
* @param s The string.
* @return the DBC string
*/
fun toDBC(s: String?): String {
if (s == null || s.isEmpty()) return ""
val chars = s.toCharArray()
var i = 0
val len = chars.size
while (i < len) {
when {
chars[i].toInt() == 12288 -> chars[i] = ' '
chars[i].toInt() in 65281..65374 -> chars[i] = (chars[i].toInt() - 65248).toChar()
else -> chars[i] = chars[i]
/**
* Convert string to DBC.
*
* @param s The string.
* @return the DBC string
*/
@JvmStatic
fun toDBC(s: String?): String {
if (s == null || s.isEmpty()) return ""
val chars = s.toCharArray()
var i = 0
val len = chars.size
while (i < len) {
when {
chars[i].toInt() == 12288 -> chars[i] = ' '
chars[i].toInt() in 65281..65374 -> chars[i] = (chars[i].toInt() - 65248).toChar()
else -> chars[i] = chars[i]
}
i++
}
i++
return String(chars)
}
return String(chars)
}
/**
* Convert string to SBC.
*
* @param s The string.
* @return the SBC string
*/
fun toSBC(s: String?): String {
if (s == null || s.isEmpty()) return ""
val chars = s.toCharArray()
var i = 0
val len = chars.size
while (i < len) {
when {
chars[i] == ' ' -> chars[i] = 12288.toChar()
chars[i].toInt() in 33..126 -> chars[i] = (chars[i].toInt() + 65248).toChar()
else -> chars[i] = chars[i]
/**
* Convert string to SBC.
*
* @param s The string.
* @return the SBC string
*/
@JvmStatic
fun toSBC(s: String?): String {
if (s == null || s.isEmpty()) return ""
val chars = s.toCharArray()
var i = 0
val len = chars.size
while (i < len) {
when {
chars[i] == ' ' -> chars[i] = 12288.toChar()
chars[i].toInt() in 33..126 -> chars[i] = (chars[i].toInt() + 65248).toChar()
else -> chars[i] = chars[i]
}
i++
}
i++
return String(chars)
}
return String(chars)
}
\ No newline at end of file
package com.blankj.utilcode.util;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import android.util.DisplayMetrics;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* <pre>
* author:
* ___ ___ ___ ___
* _____ / /\ /__/\ /__/| / /\
* / /::\ / /::\ \ \:\ | |:| / /:/
* / /:/\:\ ___ ___ / /:/\:\ \ \:\ | |:| /__/::\
* / /:/~/::\ /__/\ / /\ / /:/~/::\ _____\__\:\ __| |:| \__\/\:\
* /__/:/ /:/\:| \ \:\ / /:/ /__/:/ /:/\:\ /__/::::::::\ /__/\_|:|____ \ \:\
* \ \:\/:/~/:/ \ \:\ /:/ \ \:\/:/__\/ \ \:\~~\~~\/ \ \:\/:::::/ \__\:\
* \ \::/ /:/ \ \:\/:/ \ \::/ \ \:\ ~~~ \ \::/~~~~ / /:/
* \ \:\/:/ \ \::/ \ \:\ \ \:\ \ \:\ /__/:/
* \ \::/ \__\/ \ \:\ \ \:\ \ \:\ \__\/
* \__\/ \__\/ \__\/ \__\/
* blog : http://blankj.com
* time : 16/12/08
* desc : utils about initialization
* </pre>
*/
public final class Utils {
@SuppressLint("StaticFieldLeak")
private static Application sApplication;
private static final ActivityLifecycleImpl ACTIVITY_LIFECYCLE = new ActivityLifecycleImpl();
private Utils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* Init utils.
* <p>Init it in the class of Application.</p>
*
* @param context context
*/
public static void init(final Context context) {
if (context == null) {
init(getApplicationByReflect());
return;
}
init((Application) context.getApplicationContext());
}
/**
* Init utils.
* <p>Init it in the class of Application.</p>
*
* @param app application
*/
public static void init(final Application app) {
if (sApplication == null) {
if (app == null) {
sApplication = getApplicationByReflect();
} else {
sApplication = app;
}
sApplication.registerActivityLifecycleCallbacks(ACTIVITY_LIFECYCLE);
}
}
/**
* Return the context of Application object.
*
* @return the context of Application object
*/
public static Application getApp() {
if (sApplication != null) return sApplication;
Application app = getApplicationByReflect();
init(app);
return app;
}
private static Application getApplicationByReflect() {
try {
@SuppressLint("PrivateApi")
Class<?> activityThread = Class.forName("android.app.ActivityThread");
Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
Object app = activityThread.getMethod("getApplication").invoke(thread);
if (app == null) {
throw new NullPointerException("u should init first");
}
return (Application) app;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
throw new NullPointerException("u should init first");
}
static ActivityLifecycleImpl getActivityLifecycle() {
return ACTIVITY_LIFECYCLE;
}
static LinkedList<Activity> getActivityList() {
return ACTIVITY_LIFECYCLE.mActivityList;
}
static Context getTopActivityOrApp() {
if (isAppForeground()) {
Activity topActivity = ACTIVITY_LIFECYCLE.getTopActivity();
return topActivity == null ? Utils.getApp() : topActivity;
} else {
return Utils.getApp();
}
}
static boolean isAppForeground() {
ActivityManager am =
(ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);
//noinspection ConstantConditions
List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();
if (info == null || info.size() == 0) return false;
for (ActivityManager.RunningAppProcessInfo aInfo : info) {
if (aInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return aInfo.processName.equals(Utils.getApp().getPackageName());
}
}
return false;
}
static final AdaptScreenArgs ADAPT_SCREEN_ARGS = new AdaptScreenArgs();
static void restoreAdaptScreen() {
final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics();
final Activity activity = ACTIVITY_LIFECYCLE.getTopActivity();
if (activity != null) {
final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();
if (ADAPT_SCREEN_ARGS.isVerticalSlide) {
activityDm.density = activityDm.widthPixels / (float) ADAPT_SCREEN_ARGS.sizeInPx;
} else {
activityDm.density = activityDm.heightPixels / (float) ADAPT_SCREEN_ARGS.sizeInPx;
}
activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density);
activityDm.densityDpi = (int) (160 * activityDm.density);
appDm.density = activityDm.density;
appDm.scaledDensity = activityDm.scaledDensity;
appDm.densityDpi = activityDm.densityDpi;
} else {
if (ADAPT_SCREEN_ARGS.isVerticalSlide) {
appDm.density = appDm.widthPixels / (float) ADAPT_SCREEN_ARGS.sizeInPx;
} else {
appDm.density = appDm.heightPixels / (float) ADAPT_SCREEN_ARGS.sizeInPx;
}
appDm.scaledDensity = appDm.density * (systemDm.scaledDensity / systemDm.density);
appDm.densityDpi = (int) (160 * appDm.density);
}
}
static void cancelAdaptScreen() {
final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics();
final Activity activity = ACTIVITY_LIFECYCLE.getTopActivity();
if (activity != null) {
final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();
activityDm.density = systemDm.density;
activityDm.scaledDensity = systemDm.scaledDensity;
activityDm.densityDpi = systemDm.densityDpi;
}
appDm.density = systemDm.density;
appDm.scaledDensity = systemDm.scaledDensity;
appDm.densityDpi = systemDm.densityDpi;
}
static boolean isAdaptScreen() {
final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics();
final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics();
return systemDm.density != appDm.density;
}
static class AdaptScreenArgs {
int sizeInPx;
boolean isVerticalSlide;
}
static class ActivityLifecycleImpl implements ActivityLifecycleCallbacks {
final LinkedList<Activity> mActivityList = new LinkedList<>();
final HashMap<Object, OnAppStatusChangedListener> mStatusListenerMap = new HashMap<>();
private int mForegroundCount = 0;
private int mConfigCount = 0;
void addListener(final Object object, final OnAppStatusChangedListener listener) {
mStatusListenerMap.put(object, listener);
}
void removeListener(final Object object) {
mStatusListenerMap.remove(object);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
setTopActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
setTopActivity(activity);
if (mForegroundCount <= 0) {
postStatus(true);
}
if (mConfigCount < 0) {
++mConfigCount;
} else {
++mForegroundCount;
}
}
@Override
public void onActivityResumed(Activity activity) {
setTopActivity(activity);
}
@Override
public void onActivityPaused(Activity activity) {/**/}
@Override
public void onActivityStopped(Activity activity) {
if (activity.isChangingConfigurations()) {
--mConfigCount;
} else {
--mForegroundCount;
if (mForegroundCount <= 0) {
postStatus(false);
}
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {/**/}
@Override
public void onActivityDestroyed(Activity activity) {
mActivityList.remove(activity);
}
private void postStatus(final boolean isForeground) {
if (mStatusListenerMap.isEmpty()) return;
for (OnAppStatusChangedListener onAppStatusChangedListener : mStatusListenerMap.values()) {
if (onAppStatusChangedListener == null) return;
if (isForeground) {
onAppStatusChangedListener.onForeground();
} else {
onAppStatusChangedListener.onBackground();
}
}
}
private void setTopActivity(final Activity activity) {
if (activity.getClass() == PermissionUtils.PermissionActivity.class) return;
if (mActivityList.contains(activity)) {
if (!mActivityList.getLast().equals(activity)) {
mActivityList.remove(activity);
mActivityList.addLast(activity);
}
} else {
mActivityList.addLast(activity);
}
}
Activity getTopActivity() {
if (!mActivityList.isEmpty()) {
final Activity topActivity = mActivityList.getLast();
if (topActivity != null) {
return topActivity;
}
}
Activity topActivityByReflect = getTopActivityByReflect();
if (topActivityByReflect != null) {
setTopActivity(topActivityByReflect);
}
return topActivityByReflect;
}
private Activity getTopActivityByReflect() {
try {
@SuppressLint("PrivateApi")
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
Field activitiesField = activityThreadClass.getDeclaredField("mActivityList");
activitiesField.setAccessible(true);
Map activities = (Map) activitiesField.get(activityThread);
if (activities == null) return null;
for (Object activityRecord : activities.values()) {
Class activityRecordClass = activityRecord.getClass();
Field pausedField = activityRecordClass.getDeclaredField("paused");
pausedField.setAccessible(true);
if (!pausedField.getBoolean(activityRecord)) {
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
return (Activity) activityField.get(activityRecord);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return null;
}
}
public static final class FileProvider4UtilCode extends FileProvider {
@Override
public boolean onCreate() {
Utils.init(getContext());
return true;
}
}
///////////////////////////////////////////////////////////////////////////
// interface
///////////////////////////////////////////////////////////////////////////
public interface OnAppStatusChangedListener {
void onForeground();
void onBackground();
}
}
@file:JvmName("Utils")
package com.blankj.utilcode.util
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.Application
import android.app.Application.ActivityLifecycleCallbacks
import android.content.Context
import android.content.res.Resources
import android.os.Bundle
import android.support.v4.content.FileProvider
import java.lang.reflect.InvocationTargetException
import java.util.*
@SuppressLint("StaticFieldLeak")
private var sApplication: Application? = null
private val ACTIVITY_LIFECYCLE = ActivityLifecycleImpl()
/**
* Init utils.
*
* Init it in the class of Application.
*
* @param context context
*/
fun init(context: Context?) {
if (context == null) {
init(getApplicationByReflect())
return
}
init(context.applicationContext as Application)
}
/**
* Init utils.
*
* Init it in the class of Application.
*
* @param app application
*/
fun init(app: Application?) {
if (sApplication == null) {
sApplication = app ?: getApplicationByReflect()
sApplication!!.registerActivityLifecycleCallbacks(ACTIVITY_LIFECYCLE)
}
}
/**
* Return the context of Application object.
*
* @return the context of Application object
*/
fun getApp(): Application {
if (sApplication != null) return sApplication as Application
val app = getApplicationByReflect()
init(app)
return app
}
private fun getApplicationByReflect(): Application {
try {
@SuppressLint("PrivateApi")
val activityThread = Class.forName("android.app.ActivityThread")
val thread = activityThread.getMethod("currentActivityThread").invoke(null)
val app = activityThread.getMethod("getApplication").invoke(thread)
?: throw NullPointerException("u should init first")
return app as Application
} catch (e: NoSuchMethodException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
} catch (e: InvocationTargetException) {
e.printStackTrace()
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
throw NullPointerException("u should init first")
}
internal fun getActivityLifecycle(): ActivityLifecycleImpl {
return ACTIVITY_LIFECYCLE
}
internal fun getActivityList(): LinkedList<Activity> {
return ACTIVITY_LIFECYCLE.mActivityList
}
fun getTopActivityOrApp(): Context {
return if (isAppForeground()) {
val topActivity = ACTIVITY_LIFECYCLE.topActivity
topActivity ?: getApp()
} else {
getApp()
}
}
internal fun isAppForeground(): Boolean {
val am = getApp().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val info = am.runningAppProcesses
if (info == null || info.size == 0) return false
for (aInfo in info) {
if (aInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return aInfo.processName == getApp().packageName
}
}
return false
}
internal val ADAPT_SCREEN_ARGS = AdaptScreenArgs()
internal fun restoreAdaptScreen() {
val systemDm = Resources.getSystem().displayMetrics
val appDm = getApp().resources.displayMetrics
val activity = ACTIVITY_LIFECYCLE.topActivity
if (activity != null) {
val activityDm = activity.resources.displayMetrics
if (ADAPT_SCREEN_ARGS.isVerticalSlide) {
activityDm.density = activityDm.widthPixels / ADAPT_SCREEN_ARGS.sizeInPx.toFloat()
} else {
activityDm.density = activityDm.heightPixels / ADAPT_SCREEN_ARGS.sizeInPx.toFloat()
}
activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density)
activityDm.densityDpi = (160 * activityDm.density).toInt()
appDm.density = activityDm.density
appDm.scaledDensity = activityDm.scaledDensity
appDm.densityDpi = activityDm.densityDpi
} else {
if (ADAPT_SCREEN_ARGS.isVerticalSlide) {
appDm.density = appDm.widthPixels / ADAPT_SCREEN_ARGS.sizeInPx.toFloat()
} else {
appDm.density = appDm.heightPixels / ADAPT_SCREEN_ARGS.sizeInPx.toFloat()
}
appDm.scaledDensity = appDm.density * (systemDm.scaledDensity / systemDm.density)
appDm.densityDpi = (160 * appDm.density).toInt()
}
}
internal fun cancelAdaptScreen() {
val systemDm = Resources.getSystem().displayMetrics
val appDm = getApp().resources.displayMetrics
val activity = ACTIVITY_LIFECYCLE.topActivity
if (activity != null) {
val activityDm = activity.resources.displayMetrics
activityDm.density = systemDm.density
activityDm.scaledDensity = systemDm.scaledDensity
activityDm.densityDpi = systemDm.densityDpi
}
appDm.density = systemDm.density
appDm.scaledDensity = systemDm.scaledDensity
appDm.densityDpi = systemDm.densityDpi
}
internal fun isAdaptScreen(): Boolean {
val systemDm = Resources.getSystem().displayMetrics
val appDm = getApp().resources.displayMetrics
return systemDm.density != appDm.density
}
internal class AdaptScreenArgs {
var sizeInPx: Int = 0
var isVerticalSlide: Boolean = false
}
internal class ActivityLifecycleImpl : ActivityLifecycleCallbacks {
val mActivityList: LinkedList<Activity> = LinkedList()
private val mStatusListenerMap: HashMap<Any, OnAppStatusChangedListener> = HashMap()
private var mForegroundCount = 0
private var mConfigCount = 0
var topActivity: Activity?
get() {
if (!mActivityList.isEmpty()) {
val topActivity = mActivityList.last
if (topActivity != null) {
return topActivity
}
}
val topActivityByReflect = topActivityByReflect
if (topActivityByReflect != null) {
topActivity = topActivityByReflect
}
return topActivityByReflect
}
private set(activity) {
if (activity?.javaClass == PermissionUtils.PermissionActivity::class.java) return
if (mActivityList.contains(activity)) {
if (!mActivityList.last.equals(activity)) {
mActivityList.remove(activity)
mActivityList.addLast(activity)
}
} else {
mActivityList.addLast(activity)
}
}
private val topActivityByReflect: Activity?
get() {
try {
@SuppressLint("PrivateApi")
val activityThreadClass = Class.forName("android.app.ActivityThread")
val activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null)
val activitiesField = activityThreadClass.getDeclaredField("mActivityList")
activitiesField.isAccessible = true
val activities = activitiesField.get(activityThread) as Map<*, *>
for (activityRecord in activities.values) {
if (activityRecord == null) continue
val activityRecordClass = activityRecord.javaClass
val pausedField = activityRecordClass.getDeclaredField("paused")
pausedField.isAccessible = true
if (!pausedField.getBoolean(activityRecord)) {
val activityField = activityRecordClass.getDeclaredField("activity")
activityField.isAccessible = true
return activityField.get(activityRecord) as Activity
}
}
} catch (e: ClassNotFoundException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
} catch (e: InvocationTargetException) {
e.printStackTrace()
} catch (e: NoSuchMethodException) {
e.printStackTrace()
} catch (e: NoSuchFieldException) {
e.printStackTrace()
}
return null
}
fun addListener(obj: Any, listener: OnAppStatusChangedListener) {
mStatusListenerMap[obj] = listener
}
fun removeListener(obj: Any) {
mStatusListenerMap.remove(obj)
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle) {
topActivity = activity
}
override fun onActivityStarted(activity: Activity) {
topActivity = activity
if (mForegroundCount <= 0) {
postStatus(true)
}
if (mConfigCount < 0) {
++mConfigCount
} else {
++mForegroundCount
}
}
override fun onActivityResumed(activity: Activity) {
topActivity = activity
}
override fun onActivityPaused(activity: Activity) {/**/
}
override fun onActivityStopped(activity: Activity) {
if (activity.isChangingConfigurations) {
--mConfigCount
} else {
--mForegroundCount
if (mForegroundCount <= 0) {
postStatus(false)
}
}
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {/**/
}
override fun onActivityDestroyed(activity: Activity) {
mActivityList.remove(activity)
}
private fun postStatus(isForeground: Boolean) {
if (mStatusListenerMap.isEmpty()) return
for (onAppStatusChangedListener in mStatusListenerMap.values) {
if (isForeground) {
onAppStatusChangedListener.onForeground()
} else {
onAppStatusChangedListener.onBackground()
}
}
}
}
class FileProvider4UtilCode : FileProvider() {
override fun onCreate(): Boolean {
init(context)
return true
}
}
///////////////////////////////////////////////////////////////////////////
// interface
///////////////////////////////////////////////////////////////////////////
interface OnAppStatusChangedListener {
fun onForeground()
fun onBackground()
}
\ No newline at end of file
package com.blankj.utilcode.util;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
......@@ -291,12 +293,22 @@ public final class ZipUtils {
if (isSpace(keyword)) {
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.contains("../")) {
Log.e("ZipUtils", "it's dangerous!");
return files;
}
if (!unzipChildFile(destDir, files, zip, entry)) return files;
}
} else {
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
if (entry.getName().contains(keyword)) {
String entryName = entry.getName();
if (entryName.contains("../")) {
Log.e("ZipUtils", "it's dangerous!");
return files;
}
if (entryName.contains(keyword)) {
if (!unzipChildFile(destDir, files, zip, entry)) return files;
}
}
......
package com.blankj.utilcode.util;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
......@@ -291,12 +293,22 @@ public final class ZipUtils {
if (isSpace(keyword)) {
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.contains("../")) {
Log.e("ZipUtils", "it's dangerous!");
return files;
}
if (!unzipChildFile(destDir, files, zip, entry)) return files;
}
} else {
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
if (entry.getName().contains(keyword)) {
String entryName = entry.getName();
if (entryName.contains("../")) {
Log.e("ZipUtils", "it's dangerous!");
return files;
}
if (entryName.contains(keyword)) {
if (!unzipChildFile(destDir, files, zip, entry)) return files;
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册