效果图
知识储备
1、WebView加载html,并通过JS传值
在网上随便搜索找到了这个炫酷的充电动画,可惜是css实现的,想在Android中使用那只能
通过 WebView 来加载了,要传递当前系统电量需要在Java和JS中传值,具体可参考下面的文章
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文件夹
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中,
有需求的可自己去实现