AndroidSystemHelper.kt 8.34 KB
Newer Older
1
package com.ydl.ydlcommon.utils
konghaorui committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

import android.app.Activity
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.TypedArray
import android.graphics.Rect
import android.os.Build
import android.util.Log
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import java.lang.reflect.Field

/**
 * Created by haorui on 2019/6/11.
 * Des:Android系统相关帮助类
 */
class AndroidSystemHelper {
    companion object {

        //====================修复Android 8.0 崩溃,Only fullscreen activities can request orientation====================
        fun fixAndroidOrientationBug(activity: Activity) {
            if (!isAllowSetOrientation(activity)) {
                val result = resetOrientation(activity)
                Log.i(this.javaClass.simpleName, "onCreate resetOrientation when Oreo, result = " + result);
            }
        }

        /**
         * 反射调用ActivityInfo#isTranslucentOrFloating()
         * 判断当前Activity是否透明or悬浮
         */
        fun isTranslucentOrFloating(activity: Activity): Boolean {
            var isTranslucentOrFloating = false
            try {
                val styleableRes =
                    Class.forName("com.android.internal.R\$styleable").getField("Window").get(null) as IntArray
                val ta = activity.obtainStyledAttributes(styleableRes)
                val m = ActivityInfo::class.java.getMethod("isTranslucentOrFloating", TypedArray::class.java)
                m.isAccessible = true
                isTranslucentOrFloating = m.invoke(null, ta) as Boolean
                m.isAccessible = false
            } catch (e: Exception) {
                e.printStackTrace()
            }

            return isTranslucentOrFloating
        }

        /**
         * 重置屏幕方向
         */
        fun resetOrientation(activity: Activity): Boolean {
            try {
                //设置方向为改为SCREEN_ORIENTATION_UNSPECIFIED:未指定类型
                val field = Activity::class.java.getDeclaredField("mActivityInfo")
                field.isAccessible = true
                val o = field.get(activity) as ActivityInfo
                o.screenOrientation = -1
                field.isAccessible = false
                return true
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return false
        }

        /**
         * 是否允许设置屏幕方向
         */
        fun isAllowSetOrientation(activity: Activity): Boolean {
75
            return if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O && isTranslucentOrFloating(
konghaorui committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
                    activity
                )
            ) {
                Log.i(this.javaClass.simpleName, "avoid calling setRequestedOrientation when Oreo.")
                false;
            } else {
                true
            }
        }


        //====================解决 InputMethodManager 导致的内存泄漏问题====================
        fun fixInputMethodManagerLeak(activity: Activity) {
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
                return
            }

            val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

            val arr = arrayOf("mCurRootView", "mServedView", "mNextServedView")
            var f: Field?
            var obj_get: Any?
            for (i in arr.indices) {
                val param = arr[i]
                try {
                    f = imm.javaClass.getDeclaredField(param)
                    if (f!!.isAccessible === false) {
                        f!!.isAccessible = true
                    } // author: sodino mail:sodino@qq.com
                    obj_get = f!!.get(imm)
                    if (obj_get != null && obj_get is View) {
                        val v_get = obj_get as View?
                        if (v_get!!.context === activity) { // 被InputMethodManager持有引用的context是想要目标销毁的
                            f.set(imm, null) // 置空,破坏掉path to gc节点
                        } else {
                            // 不是想要目标销毁的,即为又进了另一层界面了,不要处理,避免影响原逻辑,也就不用继续for循环了
                            break
                        }
                    }
                } catch (t: Throwable) {
                    t.printStackTrace()
                }
            }
        }

        //====================解决 修改系统字体大小后 H5页面大小比例不正常现象====================
        fun fixResourcesScale(res: Resources): Resources {
            val config = Configuration()
            config.setToDefaults()
            res.updateConfiguration(config, res.displayMetrics)
            return res
        }

        fun fixAndroidBug5497Workaround(activity: Activity){
130 131 132
            AndroidBug5497Workaround.assistActivity(
                activity
            )
konghaorui committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
        }
    }

    //================== 解决全屏模式下,“adjustResize”失效问题(Android系统Bug)======================
    /**
     * Des: 解决全屏模式下,“adjustResize”失效问题(Android系统Bug)
     * 链接地址:https://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible/19494006#19494006
     */
    class AndroidBug5497Workaround private constructor(private val activity: Activity) {
        private val mChildOfContent: View
        private var usableHeightPrevious: Int = 0
        private val frameLayoutParams: FrameLayout.LayoutParams
        private var contentHeight: Int = 0
        private var isfirst = true
        private val statusBarHeight: Int

        init {
            //获取状态栏的高度
            val resourceId = activity.resources.getIdentifier("status_bar_height", "dimen", "android")
            statusBarHeight = activity.resources.getDimensionPixelSize(resourceId)
            val content = activity.findViewById<View>(android.R.id.content) as FrameLayout
            mChildOfContent = content.getChildAt(0)

            //界面出现变动都会调用这个监听事件
            mChildOfContent.viewTreeObserver.addOnGlobalLayoutListener {
                if (isfirst) {
                    contentHeight = mChildOfContent.height//兼容华为等机型
                    isfirst = false
                }
                possiblyResizeChildOfContent()
            }

            frameLayoutParams = mChildOfContent.layoutParams as FrameLayout.LayoutParams
        }

        //重新调整跟布局的高度
        private fun possiblyResizeChildOfContent() {

            val usableHeightNow = computeUsableHeight()

            //当前可见高度和上一次可见高度不一致 布局变动
            if (usableHeightNow != usableHeightPrevious) {
                //int usableHeightSansKeyboard2 = mChildOfContent.getHeight();//兼容华为等机型
                val usableHeightSansKeyboard = mChildOfContent.rootView.height
                val heightDifference = usableHeightSansKeyboard - usableHeightNow
                if (heightDifference > usableHeightSansKeyboard / 4) {
                    // keyboard probably just became visible
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                        //frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
                        frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight
                    } else {
                        frameLayoutParams.height = usableHeightSansKeyboard - heightDifference
                    }
                } else {
                    frameLayoutParams.height = contentHeight
                }

                mChildOfContent.requestLayout()
                usableHeightPrevious = usableHeightNow
            }
        }

        /**     * 计算mChildOfContent可见高度     ** @return      */
        private fun computeUsableHeight(): Int {
            val r = Rect()
            mChildOfContent.getWindowVisibleDisplayFrame(r)
            return r.bottom - r.top
        }

        companion object {
            fun assistActivity(activity: Activity) {
                AndroidBug5497Workaround(activity)
            }
        }
    }


}