效果图
知识储备
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中,
有需求的可自己去实现