AndroidQ(10.0) 手机锁屏炫酷充电动画————html方案

74 阅读2分钟

效果图

0YDu9g.gif

知识储备

1、WebView加载html,并通过JS传值

在网上随便搜索找到了这个炫酷的充电动画,可惜是css实现的,想在Android中使用那只能

通过 WebView 来加载了,要传递当前系统电量需要在Java和JS中传值,具体可参考下面的文章

Android如何使用HTML做界面——WebView控件

Android:你要的WebView与 JS 交互方式 都在这里了

2、SystemUI 中使用 WebView 控件排错

开始在普通工程中使用WebView是没有问题的,但移植到SystemUI中运行的时候出现错误

AndroidRuntime: Caused by: java.lang.UnsupportedOperationException: For security reasons, WebView is not

allowed in privileged processes

解决办法,添加 hookWebView() 方法 解决 WebView 报错 Binary XML file line #7 Error inflating class android.webkit.WebView

具体实现

1、将html文件拷贝至assets文件夹

lottie和html充电动画相关资源.zip

2、新建 layout_charge.xml 布局文件


<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent">
    
    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </WebView>

    <FrameLayout
        android:id="@+id/clickView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>

3、新建 ChargeActivity.java 文件


package com.android.systemui.power;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import com.android.systemui.R;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ChargeActivity extends Activity {


    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        hookWebView();
        setContentView(R.layout.layout_charge);
        
        mWebView = (WebView) findViewById(R.id.webView);
        FrameLayout mClickView = (FrameLayout) findViewById(R.id.clickView);
        mWebView.setVisibility(View.INVISIBLE);
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        webSettings.setDomStorageEnabled(true);
        mWebView.loadUrl("file:///android_asset/charging.html");
        mWebView.setWebViewClient(new WebViewClient(){
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                mWebView.setVisibility(View.VISIBLE);
                mHandler.sendEmptyMessage(MSG_BATTERY);
            }
        });

        mClickView.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                closeAnim();
            }
        });
        
        registerReceiver();
    }





    public static void hookWebView(){
        int sdkInt = Build.VERSION.SDK_INT;
        try {
            Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
            Field field = factoryClass.getDeclaredField("sProviderInstance");
            field.setAccessible(true);
            Object sProviderInstance = field.get(null);
            if (sProviderInstance != null) {
                Log.i("hook","sProviderInstance isn't null");
                return;
            }

            Method getProviderClassMethod;
            if (sdkInt > 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
            } else if (sdkInt == 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
            } else {
                Log.i("hook","Don't need to Hook WebView");
                return;
            }

            getProviderClassMethod.setAccessible(true);
            Class<?> factoryProviderClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
            Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
            Constructor<?> delegateConstructor = delegateClass.getDeclaredConstructor();
            delegateConstructor.setAccessible(true);
            if(sdkInt < 26){
                Constructor<?> providerConstructor = factoryProviderClass.getConstructor(delegateClass);
                if (providerConstructor != null) {
                    providerConstructor.setAccessible(true);
                    sProviderInstance = providerConstructor.newInstance(delegateConstructor.newInstance());
                }
            } else {
                Field chromiumMethodName = factoryClass.getDeclaredField("CHROMIUM_WEBVIEW_FACTORY_METHOD");
                chromiumMethodName.setAccessible(true);
                String chromiumMethodNameStr = (String)chromiumMethodName.get(null);
                if (chromiumMethodNameStr == null) {
                    chromiumMethodNameStr = "create";
                }

                Method staticFactory = factoryProviderClass.getMethod(chromiumMethodNameStr, delegateClass);
                if (staticFactory!=null){
                    sProviderInstance = staticFactory.invoke(null, delegateConstructor.newInstance());
                }
            }

            if (sProviderInstance != null){
                field.set("sProviderInstance", sProviderInstance);
                Log.i("hook","Hook success!");
            } else {
                Log.i("hook","Hook failed!");
            }
        } catch (Throwable e) {
            Log.w("hook",e);
            e.printStackTrace();
        }
    }

    private void closeAnim(){
        mHandler.removeMessages(MSG_BATTERY);
        finish();
    }



    private void registerReceiver() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
        registerReceiver(receiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
    }



    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (Intent.ACTION_POWER_DISCONNECTED.equals(action)){
                closeAnim();
            }
        }
    };



    private int MSG_BATTERY = 100;
    private Handler mHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == MSG_BATTERY){
                changeBatteryText();
                mHandler.sendEmptyMessageDelayed(MSG_BATTERY, 15000);
            }
        }
    };



    private int getBatteryLevel() {
        BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
    }



    private void changeBatteryText() {
        mWebView.post(new Runnable() {
            @Override
            public void run() {
                String mbettery = getBatteryLevel() + "%";
                Log.e("ccz", "mbettery=" + mbettery);
                mWebView.evaluateJavascript("javascript:callJSWithArgs('" + mbettery + "')", new ValueCallback<String>() {
                    @Override
                    public void onReceiveValue(String value) {
                        //Toast.makeText(getApplication(), " "+value, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
}

优化体验

因为本文章只是提供充电动画思路,细节考虑不到位,采用Activity方式加载layout,

切换时会有明显拉起动画,可以通过Window addView 方式将 layout 添加到SystemUI中,

有需求的可自己去实现

参考文章

android 高仿华为充电动画

css3安卓充电动画效果