diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f086a1149..6c63102f7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -113,9 +113,6 @@ dependencies { implementation("androidx.work:work-runtime:2.9.0") implementation("androidx.concurrent:concurrent-futures:1.1.0") - // Palette - implementation("androidx.palette:palette:1.0.0") - // Biometric Auth implementation("androidx.biometric:biometric:1.1.0") @@ -130,12 +127,6 @@ dependencies { // ColorPicker implementation("com.jaredrummler:colorpicker:1.1.0") //Color Picker Component for UI - // ViewPager2 - implementation("androidx.viewpager2:viewpager2:1.0.0") - - // Circle Indicator - implementation("me.relex:circleindicator:2.1.6") - // Lottie implementation("com.airbnb.android:lottie:6.4.0") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 37238a122..df08fd731 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,6 +91,11 @@ android:foregroundServiceType="location" android:permission="android.permission.BIND_JOB_SERVICE" /> + + rootAllowedPacks; + private final boolean rootGranted; + + private RootPoviderProxyIPC(Context context) + { + try { + Shell.setDefaultBuilder(Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER)); + } + catch (Throwable ignored){} + rootGranted = Shell.getShell().isRoot(); + + rootAllowedPacks = Arrays.asList(context.getResources().getStringArray(R.array.xposed_scope)); + } + + /** @noinspection RedundantThrows*/ + @Override + public String[] runCommand(String command) throws RemoteException { + try { + ensureEnvironment(); + + List result = Shell.cmd(command).exec().getOut(); + return result.toArray(new String[0]); + } + catch (Throwable t) + { + return new String[0]; + } + } + + private void ensureEnvironment() throws RemoteException { + if(!rootGranted) + { + throw new RemoteException("Root permission denied"); + } + + ensureSecurity(Binder.getCallingUid()); + } + + private void ensureSecurity(int uid) throws RemoteException { + for (String packageName : getPackageManager().getPackagesForUid(uid)) { + if(rootAllowedPacks.contains(packageName)) + return; + } + throw new RemoteException("You do know you're not supposed to use this service. So..."); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/it/dhd/oxygencustomizer/xposed/XPLauncher.java b/app/src/main/java/it/dhd/oxygencustomizer/xposed/XPLauncher.java index b7788635e..b23a9e62a 100644 --- a/app/src/main/java/it/dhd/oxygencustomizer/xposed/XPLauncher.java +++ b/app/src/main/java/it/dhd/oxygencustomizer/xposed/XPLauncher.java @@ -7,21 +7,32 @@ import static it.dhd.oxygencustomizer.BuildConfig.APPLICATION_ID; import static it.dhd.oxygencustomizer.xposed.XPrefs.Xprefs; import static it.dhd.oxygencustomizer.xposed.utils.BootLoopProtector.isBootLooped; +import static it.dhd.oxygencustomizer.xposed.utils.SystemUtils.sleep; import android.annotation.SuppressLint; import android.app.Instrumentation; +import android.app.Service; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Objects; +import java.util.Queue; import java.util.concurrent.CompletableFuture; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.callbacks.XC_LoadPackage; import it.dhd.oxygencustomizer.BuildConfig; +import it.dhd.oxygencustomizer.IRootProviderProxy; import it.dhd.oxygencustomizer.utils.Constants; import it.dhd.oxygencustomizer.xposed.utils.SystemUtils; -public class XPLauncher { +public class XPLauncher implements ServiceConnection { public static boolean isChildProcess = false; public static String processName = ""; @@ -29,6 +40,8 @@ public class XPLauncher { public static ArrayList runningMods = new ArrayList<>(); public Context mContext = null; + private static IRootProviderProxy rootProxyIPC; + private static final Queue proxyQueue = new LinkedList<>(); @SuppressLint("StaticFieldLeak") static XPLauncher instance; @@ -106,6 +119,7 @@ private void onXPrefsReady(XC_LoadPackage.LoadPackageParam lpparam) { } private void loadModpacks(XC_LoadPackage.LoadPackageParam lpparam) { + forceConnectRootService(); for (Class mod : ModPacks.getMods(lpparam.packageName)) { try { XposedMods instance = mod.getConstructor(Context.class).newInstance(mContext); @@ -141,4 +155,81 @@ private void waitForXprefsLoad(XC_LoadPackage.LoadPackageParam lpparam) { onXPrefsReady(lpparam); } + + private void forceConnectRootService() + { + new Thread(() -> { + while(SystemUtils.UserManager() == null + || !SystemUtils.UserManager().isUserUnlocked()) //device is still CE encrypted + { + sleep(2000); + } + sleep(5000); //wait for the unlocked account to settle down a bit + + while(rootProxyIPC == null) + { + connectRootService(); + sleep(5000); + } + }).start(); + } + + private void connectRootService() + { + try { + Intent intent = new Intent(); + intent.setComponent(new ComponentName(APPLICATION_ID, APPLICATION_ID + ".services.RootProviderProxy")); + mContext.bindService(intent, instance, Context.BIND_AUTO_CREATE | Context.BIND_ADJUST_WITH_ACTIVITY); + } + catch (Throwable t) + { + log(t); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + rootProxyIPC = IRootProviderProxy.Stub.asInterface(service); + synchronized (proxyQueue) + { + while(!proxyQueue.isEmpty()) + { + try + { + Objects.requireNonNull(proxyQueue.poll()).run(rootProxyIPC); + } + catch (Throwable ignored){} + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + rootProxyIPC = null; + + forceConnectRootService(); + } + + public static void enqueueProxyCommand(ProxyRunnable runnable) + { + if(rootProxyIPC != null) + { + try { + runnable.run(rootProxyIPC); + } catch (RemoteException ignored) {} + } + else + { + synchronized (proxyQueue) { + proxyQueue.add(runnable); + } + instance.forceConnectRootService(); + } + } + + public interface ProxyRunnable + { + void run(IRootProviderProxy proxy) throws RemoteException; + } + } diff --git a/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/FeatureEnabler.java b/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/FeatureEnabler.java index a9fa4eea2..2dcd8aaa7 100644 --- a/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/FeatureEnabler.java +++ b/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/FeatureEnabler.java @@ -26,6 +26,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricManager; +import android.os.RemoteException; import android.view.MotionEvent; import android.widget.ArrayAdapter; import android.widget.ListView; @@ -34,8 +35,10 @@ import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.callbacks.XC_LoadPackage; +import it.dhd.oxygencustomizer.IRootProviderProxy; import it.dhd.oxygencustomizer.R; import it.dhd.oxygencustomizer.utils.Constants; +import it.dhd.oxygencustomizer.xposed.XPLauncher; import it.dhd.oxygencustomizer.xposed.XposedMods; import it.dhd.oxygencustomizer.xposed.utils.ShellUtils; @@ -195,11 +198,11 @@ private void showDialog() throws Exception { listView.setScrollContainer(false); listView.setOnItemClickListener((parent, view, position, id) -> { switch (position) { - case 0 -> ShellUtils.execCommand("reboot recovery", true); - case 1 -> ShellUtils.execCommand("reboot bootloader", true); - case 2 -> ShellUtils.execCommand("reboot safemode", true); - case 3 -> ShellUtils.execCommand("killall zygote", true); - case 4 -> restartAllScope(new String[]{SYSTEM_UI}); + case 0 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("reboot recovery"));//ShellUtils.execCommand("reboot recovery", true); + case 1 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("reboot bootloader"));//ShellUtils.execCommand("reboot bootloader", true); + case 2 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("reboot safemode"));//ShellUtils.execCommand("reboot safemode", true); + case 3 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("killall zygote"));//ShellUtils.execCommand("killall zygote", true); + case 4 -> XPLauncher.enqueueProxyCommand(proxy -> proxy.runCommand("killall " + SYSTEM_UI));//restartAllScope(new String[]{SYSTEM_UI}); } }); diff --git a/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/statusbar/BatteryStyleManager.java b/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/statusbar/BatteryStyleManager.java index e235095c5..c0e2f42ce 100644 --- a/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/statusbar/BatteryStyleManager.java +++ b/app/src/main/java/it/dhd/oxygencustomizer/xposed/hooks/systemui/statusbar/BatteryStyleManager.java @@ -8,7 +8,10 @@ import static de.robv.android.xposed.XposedHelpers.findAndHookConstructor; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import static de.robv.android.xposed.XposedHelpers.findClass; +import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField; import static de.robv.android.xposed.XposedHelpers.getObjectField; +import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField; +import static de.robv.android.xposed.XposedHelpers.setObjectField; import static it.dhd.oxygencustomizer.utils.Constants.Preferences.BatteryPrefs.BATTERY_STYLE_CIRCLE; import static it.dhd.oxygencustomizer.utils.Constants.Preferences.BatteryPrefs.BATTERY_STYLE_CUSTOM_LANDSCAPE; import static it.dhd.oxygencustomizer.utils.Constants.Preferences.BatteryPrefs.BATTERY_STYLE_CUSTOM_RLANDSCAPE; @@ -78,6 +81,7 @@ import static it.dhd.oxygencustomizer.xposed.hooks.systemui.BatteryDataProvider.isPowerSaving; import static it.dhd.oxygencustomizer.xposed.hooks.systemui.OpUtils.getChargingColor; import static it.dhd.oxygencustomizer.xposed.utils.ViewHelper.dp2px; +import static it.dhd.oxygencustomizer.xposed.utils.ViewHelper.setMargins; import android.content.Context; import android.content.res.Resources; @@ -89,11 +93,13 @@ import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.res.ResourcesCompat; @@ -138,6 +144,7 @@ import it.dhd.oxygencustomizer.xposed.batterystyles.RLandscapeBatteryColorOS; import it.dhd.oxygencustomizer.xposed.batterystyles.RLandscapeBatteryStyleA; import it.dhd.oxygencustomizer.xposed.batterystyles.RLandscapeBatteryStyleB; +import it.dhd.oxygencustomizer.xposed.hooks.systemui.BatteryDataProvider; import it.dhd.oxygencustomizer.xposed.utils.ShellUtils; public class BatteryStyleManager extends XposedMods { @@ -149,6 +156,7 @@ public class BatteryStyleManager extends XposedMods { private static final ArrayList batteryViews = new ArrayList<>(); private static final int BatteryIconOpacity = 100; private static int BatteryStyle = 0; + public static int mBatteryStyle = 0; private static boolean mShowPercentInside = true; private static boolean mHidePercentage = false; private static boolean mHideBattery = false; @@ -173,15 +181,14 @@ public class BatteryStyleManager extends XposedMods { private int mCustomPowerSaveFillColor = Color.BLACK; private int mCustomFastChargingColor = Color.BLACK; private boolean mSwapPercentage = false; - private boolean mChargingIconSwitch = false; + private static boolean mChargingIconSwitch = false; private int mChargingIconStyle = 0; private int mChargingIconML = 1; private int mChargingIconMR = 0; private int mChargingIconWH = 14; - private boolean mIsChargingImpl = false; - private boolean mIsCharging = false; + private static boolean mIsPowerSaving = false; + private static boolean mIsCharging = false; private ImageView mStockChargingIcon = null, mBatteryIcon = null; - private ImageView mBatteryOOS13 = null; private Object BatteryControllerImpl = null; private boolean updating = false; private boolean customizePercSize = false; @@ -246,6 +253,29 @@ public void updatePrefs(String... Key) { customizePercSize = Xprefs.getBoolean(STOCK_CUSTOMIZE_PERCENTAGE_SIZE, false); mBatteryPercSize = Xprefs.getSliderInt(STOCK_PERCENTAGE_SIZE, 12); + if (mBatteryStyle != BatteryStyle && !oos13) { + mBatteryStyle = BatteryStyle; + for (View view : batteryViews) //destroy old drawables and make new ones :D + { + + ImageView batteryIcon = null, chargingIcon = null; + TextView batteryOutPercentage = null, batteryInPercentage = null; + if (view instanceof LinearLayout) { + try { + batteryIcon = view.findViewById(mContext.getResources().getIdentifier("battery_icon_view", "id", mContext.getPackageName())); + } catch (Throwable ignored) { + } + } + + if (CustomBatteryEnabled && batteryIcon != null) { + BatteryDrawable newDrawable = getNewBatteryDrawable(mContext); + batteryIcon.setImageDrawable(newDrawable); + setAdditionalInstanceField(view, "mBatteryDrawable", newDrawable); + } + } + } + + /* if (Key.length > 0 && (Key[0].equals(CUSTOMIZE_BATTERY_ICON) || Key[0].equals(CUSTOM_BATTERY_STYLE) || Key[0].equals(CUSTOM_BATTERY_HIDE_PERCENTAGE) || @@ -272,7 +302,9 @@ public void updatePrefs(String... Key) { Key[0].equals(STOCK_PERCENTAGE_SIZE) )) { notifyUpdate(); - } + }*/ + + if (!oos13) refreshAllBatteryIcons(); } @Override @@ -282,17 +314,61 @@ public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Th if (Build.VERSION.SDK_INT >= 34) { hookBattery(lpparam); // OOS 14 } else { - if (mBatteryOOS13 == null) { - mBatteryOOS13 = new ImageView(mContext); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); - mBatteryOOS13.setLayoutParams(lp); - } hookBattery13(lpparam); // OOS 13 } } private void hookBattery(XC_LoadPackage.LoadPackageParam lpparam) { + + BatteryDataProvider.registerInfoCallback(this::refreshAllBatteryIcons); + + final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(@NonNull View v) { + if (v instanceof LinearLayout batteryMeter) { + batteryViews.add(batteryMeter); + } + } + + @Override + public void onViewDetachedFromWindow(@NonNull View v) { + batteryViews.remove(v); + } + }; + + Class StatBatteryMeterView = findClass("com.oplus.systemui.statusbar.pipeline.battery.ui.view.StatBatteryMeterView", lpparam.classLoader); + hookAllConstructors(StatBatteryMeterView, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + ((View) param.thisObject).addOnAttachStateChangeListener(listener); + + if (!CustomBatteryEnabled) return; + + BatteryDrawable mBatteryDrawable = getNewBatteryDrawable(mContext); + setAdditionalInstanceField(param.thisObject, "mBatteryDrawable", mBatteryDrawable); + + } + }); + findAndHookMethod(StatBatteryMeterView, "onFinishInflate", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + //((View) param.thisObject).addOnAttachStateChangeListener(listener); + + if (!CustomBatteryEnabled) return; + + LinearLayout batteryMeterView = (LinearLayout) param.thisObject; + + ImageView mBatteryIconView = batteryMeterView.findViewById(mContext.getResources().getIdentifier("battery_icon_view", "id", mContext.getPackageName())); + + BatteryDrawable mBatteryDrawable = getNewBatteryDrawable(mContext); + setAdditionalInstanceField(param.thisObject, "mBatteryDrawable", mBatteryDrawable); + + mBatteryIconView.setImageDrawable(mBatteryDrawable); + } + }); + + Class BatteryIconColor = findClass("com.oplus.systemui.statusbar.pipeline.battery.ui.model.BatteryIconColor", lpparam.classLoader); findAndHookConstructor(BatteryIconColor, @@ -307,29 +383,11 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { this.foregroundColor = i2; this.backgroundColor = i3; */ + if (!CustomBatteryEnabled) return; singleToneColor = (int) param.args[0]; frameColor = (int) param.args[1]; backgroundColor = (int) param.args[2]; - if (mBatteryIcon != null) { - if (mBatteryIcon.getDrawable() instanceof BatteryDrawable mBatteryDrawable) { - mBatteryDrawable.setColors(frameColor, backgroundColor, singleToneColor); - mBatteryDrawable.customizeBatteryDrawable( - mBatteryLayoutReverse, - mScaledPerimeterAlpha, - mScaledFillAlpha, - mCustomBlendColor, - mRainbowFillColor, - mCustomFillColor, - mCustomFillGradColor, - mCustomBlendColor ? mCustomChargingColor : getChargingColor(mCustomChargingColor), - mCustomBlendColor ? mCustomFastChargingColor : getChargingColor(mCustomFastChargingColor), - mCustomPowerSaveColor, - mCustomPowerSaveFillColor, - mChargingIconSwitch - ); - mBatteryIcon.setImageDrawable(mBatteryDrawable); - } - } + updateIconsColor(); } }); @@ -345,6 +403,11 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { hookAllMethods(BatteryViewBinder, "bind$initView", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + //if (CustomBatteryEnabled) param.setResult(null); + } + @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { /* @@ -361,23 +424,27 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { 8 StatBatteryIcon statBatteryIcon) { */ + refreshAllBatteryIcons(); + // No need to call BatteryDataProvider or BroadcastReceiver // since battery view is updated automatically - mIsCharging = isCharging(); - boolean isCharging = false; - - batteryPercentInView = (TextView) param.args[0]; + /*batteryPercentInView = (TextView) param.args[0]; batteryPercentOutView = (TextView) param.args[1]; Object statBatteryIcon = param.args[8]; // Fix Charging if Battery Data Provider is not ready if (statBatteryIcon != null) { try { - isCharging = (boolean) callMethod(statBatteryIcon, "getCharging"); + mIsCharging = (boolean) callMethod(statBatteryIcon, "getCharging"); + } catch (Throwable t) { + mIsCharging = isCharging(); + } + try { + mIsPowerSaving = (boolean) callMethod(statBatteryIcon, "getSaveMode"); } catch (Throwable t) { - isCharging = mIsCharging; + mIsPowerSaving = isPowerSaving(); } } @@ -443,7 +510,7 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { if (mChargingIconSwitch) { mStockChargingIcon = (ImageView) param.args[7]; - if (isCharging) { + if (mIsCharging) { mStockChargingIcon.setVisibility(View.VISIBLE); if (mChargingIconSwitch) { mStockChargingIcon.setImageDrawable(getNewChargingIcon()); @@ -459,14 +526,142 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable { mStockChargingIcon.setVisibility(View.GONE); } } + updateAllIcons();*/ } }); } + private void refreshAllBatteryIcons() { + for (View view : batteryViews) { + updateBatteryViewValues(view); + } + } + + private void updateBatteryViewValues(View view) + { + TextView batteryOutPercentage = null; + try { + batteryOutPercentage = view.findViewById(mContext.getResources().getIdentifier("battery_percentage_view", "id", mContext.getPackageName())); + } catch (Throwable ignored) {} + if (batteryOutPercentage != null && batteryOutPercentage.getVisibility() == View.VISIBLE) { + batteryOutPercentage.setTextSize(TypedValue.COMPLEX_UNIT_SP, customizePercSize ? mBatteryPercSize : 12); + } + if (!CustomBatteryEnabled) return; + //setPercentViewColor(view, forcePercentageColor); + ImageView batteryIcon = null, chargingIcon = null; + TextView batteryInPercentage = null; + if (view instanceof LinearLayout) { + try { + batteryIcon = view.findViewById(mContext.getResources().getIdentifier("battery_icon_view", "id", mContext.getPackageName())); + } catch (Throwable ignored) {} + try { + chargingIcon = view.findViewById(mContext.getResources().getIdentifier("battery_dash_charge_view", "id", mContext.getPackageName())); + } catch (Throwable ignored) {} + try { + batteryInPercentage = view.findViewById(mContext.getResources().getIdentifier("battery_text", "id", mContext.getPackageName())); + batteryInPercentage.setVisibility(View.GONE); + } catch (Throwable ignored) {} + for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { + View child = ((ViewGroup) view).getChildAt(i); + if (child instanceof FrameLayout) { + child.setVisibility(View.GONE); + } else { + if (child != batteryIcon && child != batteryOutPercentage && child != batteryInPercentage) { + child.setVisibility(View.GONE); + } + } + } + } + if (CustomBatteryEnabled && batteryIcon != null) { + scaleBatteryMeterViews(batteryIcon); + updateBatteryRotation(batteryIcon); + updateFlipper(batteryIcon.getParent()); + mIsCharging = isCharging(); + mIsPowerSaving = isPowerSaving(); + batteryIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + if (batteryOutPercentage != null) { + if (mHidePercentage) { + batteryOutPercentage.setVisibility(View.GONE); + } else { + batteryOutPercentage.setVisibility(View.VISIBLE); + } + } + try { + BatteryDrawable drawable = (BatteryDrawable) getAdditionalInstanceField(view, "mBatteryDrawable"); + drawable.setChargingEnabled(mIsCharging, isFastCharging()); + drawable.setPowerSavingEnabled(isPowerSaving()); + drawable.setShowPercentEnabled(mShowPercentInside); + drawable.setAlpha(Math.round(BatteryIconOpacity * 2.55f)); + drawable.setColors(frameColor, backgroundColor, singleToneColor); + drawable.customizeBatteryDrawable( + mBatteryLayoutReverse, + mScaledPerimeterAlpha, + mScaledFillAlpha, + mCustomBlendColor, + mRainbowFillColor, + mCustomFillColor, + mCustomFillGradColor, + mCustomBlendColor ? mCustomChargingColor : getChargingColor(mCustomChargingColor), + mCustomBlendColor ? mCustomFastChargingColor : getChargingColor(mCustomFastChargingColor), + mCustomPowerSaveColor, + mCustomPowerSaveFillColor, + mChargingIconSwitch + ); + drawable.setBatteryLevel(getCurrentLevel()); + drawable.invalidateSelf(); + } catch (Throwable ignored) {} //it's probably the default battery. no action needed + } + + + if (mChargingIconSwitch && chargingIcon != null) { + if (mIsCharging) { + chargingIcon.setImageDrawable(getNewChargingIcon()); + chargingIcon.setVisibility(View.VISIBLE); + int size = dp2px(mContext, mChargingIconWH); + chargingIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size)); + setMargins(chargingIcon, mContext, mChargingIconML, 0, mChargingIconMR, 0); + } + chargingIcon.setVisibility(mIsCharging ? View.VISIBLE : View.GONE); + } + } + + private void updateIconsColor() { + if (batteryViews.isEmpty()) return; + for (View v : batteryViews) { + if (v instanceof ImageView) { + Drawable drawable = ((ImageView) v).getDrawable(); + if (drawable instanceof BatteryDrawable) { + ((BatteryDrawable) drawable).setColors(frameColor, backgroundColor, singleToneColor); + } + drawable.invalidateSelf(); + } else if (v instanceof LinearLayout) { + if (oos13) return; + BatteryDrawable mBatteryDrawable = (BatteryDrawable) getAdditionalInstanceField(v, "mBatteryDrawable"); + mBatteryDrawable.setColors(frameColor, backgroundColor, singleToneColor); + mBatteryDrawable.invalidateSelf(); + } + } + } + + private void updateAllIcons() { + if (batteryViews.isEmpty()) return; + for (View v : batteryViews) { + if (v instanceof ImageView) { + Drawable drawable = ((ImageView) v).getDrawable(); + if (drawable instanceof BatteryDrawable) { + ((BatteryDrawable) drawable).setPowerSavingEnabled(mIsPowerSaving); + ((BatteryDrawable) drawable).setChargingEnabled(mIsCharging, isFastCharging()); + drawable.invalidateSelf(); + } + } + } + } + private void hookBattery13(XC_LoadPackage.LoadPackageParam lpparam) { oos13 = true; Class TwoBatteryMeterDrawable = findClass("com.oplusos.systemui.statusbar.widget.TwoBatteryMeterDrawable", lpparam.classLoader); - findAndHookMethod(TwoBatteryMeterDrawable, "setColors", + Class StatBatteryMeterView = findClass("com.oplusos.systemui.statusbar.widget.StatBatteryMeterView", lpparam.classLoader); + findAndHookMethod(StatBatteryMeterView, "updateColors", int.class, int.class, int.class, new XC_MethodHook() { @@ -492,14 +687,32 @@ public void setColors(int i, int i2) { } */ - backgroundColor = (int) param.args[0]; - frameColor = (int) param.args[1]; - singleToneColor = (int) param.args[1]; - + backgroundColor = (int) param.args[1]; + frameColor = (int) param.args[0]; + singleToneColor = (int) param.args[2]; + updateIconsColor(); } }); + final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(@NonNull View v) { + if (v instanceof ImageView batteryIconView) { + batteryViews.add(batteryIconView); + } + } - Class StatBatteryMeterView = findClass("com.oplusos.systemui.statusbar.widget.StatBatteryMeterView", lpparam.classLoader); + @Override + public void onViewDetachedFromWindow(View v) { + batteryViews.remove(v); + } + }; + findAndHookMethod(StatBatteryMeterView, "initViews", Context.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + View v = (View) getObjectField(param.thisObject, "mBatteryIconView"); + v.addOnAttachStateChangeListener(listener); + } + }); findAndHookMethod(StatBatteryMeterView, "onBatteryLevelChanged", int.class, boolean.class, @@ -507,8 +720,6 @@ public void setColors(int i, int i2) { new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { - mBatteryIcon = (ImageView) getObjectField(param.thisObject, "mBatteryIconView"); - batteryPercentOutView = (TextView) getObjectField(param.thisObject, "batteryPercentText"); /* public void onBatteryLevelChanged(int i, boolean z, boolean z2) { @@ -519,9 +730,13 @@ public void onBatteryLevelChanged(int i, boolean z, boolean z2) { twoBatteryMeterDrawable.setBatteryLevel(i); } */ - - int batteryLevel = (int) param.args[0]; + //batteryLevel = (int) param.args[0]; + log(TAG + "onBatteryLevelChanged"); mIsCharging = (boolean) param.args[2]; + mBatteryIcon = (ImageView) getObjectField(param.thisObject, "mBatteryIconView"); + batteryPercentOutView = (TextView) getObjectField(param.thisObject, "batteryPercentText"); + ImageView batteryCharge = (ImageView) getObjectField(param.thisObject, "batteryCharge"); + int batteryLevel = (int) param.args[0];//getCurrentLevel(); if (CustomBatteryEnabled && mBatteryIcon != null) { if (mHidePercentage) @@ -552,25 +767,35 @@ public void onBatteryLevelChanged(int i, boolean z, boolean z2) { mCustomPowerSaveFillColor, mChargingIconSwitch ); - mBatteryIcon.setScaleType(ImageView.ScaleType.FIT_XY); - mBatteryIcon.setLayerType(View.LAYER_TYPE_NONE, null); + mBatteryIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + scaleBatteryMeterViews(mBatteryIcon); + updateBatteryRotation(mBatteryIcon); + updateFlipper(mBatteryIcon.getParent()); mBatteryIcon.setImageDrawable(mBatteryDrawable); } - scaleBatteryMeterViews(mBatteryIcon); - updateBatteryRotation(mBatteryIcon); - updateFlipper(mBatteryIcon.getParent()); } else { if (customizePercSize) { if (batteryPercentOutView != null && batteryPercentOutView.getVisibility() == View.VISIBLE) batteryPercentOutView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mBatteryPercSize); } } - } - }); - findAndHookMethod(StatBatteryMeterView, "initViews", Context.class, new XC_MethodHook() { - @Override - protected void afterHookedMethod(MethodHookParam param) throws Throwable { + + if (mChargingIconSwitch && batteryCharge != null) { + if (mIsCharging) { + batteryCharge.setVisibility(View.VISIBLE); + batteryCharge.setImageDrawable(getNewChargingIcon()); + int left = dp2px(mContext, mChargingIconML); + int right = dp2px(mContext, mChargingIconMR); + int size = dp2px(mContext, mChargingIconWH); + + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(size, size); + lp.setMargins(left, 0, right, mContext.getResources().getDimensionPixelSize(mContext.getResources().getIdentifier("battery_margin_bottom", "dimen", mContext.getPackageName()))); + batteryCharge.setLayoutParams(lp); + } else { + batteryCharge.setVisibility(View.GONE); + } + } } }); @@ -694,13 +919,14 @@ public static void scaleBatteryMeterViews(@Nullable ImageView mBatteryIconView) int batteryWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mBatteryScaleWidth, mBatteryIconView.getContext().getResources().getDisplayMetrics()); int batteryHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mBatteryScaleHeight, mBatteryIconView.getContext().getResources().getDisplayMetrics()); - LinearLayout.LayoutParams scaledLayoutParams; - if (!oos13) scaledLayoutParams = (LinearLayout.LayoutParams) mBatteryIconView.getLayoutParams(); - else scaledLayoutParams = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); - scaledLayoutParams.width = (int) (batteryWidth * iconScaleFactor); - scaledLayoutParams.height = (int) (batteryHeight * iconScaleFactor); - scaledLayoutParams.setMargins(mBatteryStockMarginLeft, 0, mBatteryStockMarginRight, context.getResources().getDimensionPixelOffset(context.getResources().getIdentifier("battery_margin_bottom", "dimen", context.getPackageName()))); - + LinearLayout.LayoutParams scaledLayoutParams = (LinearLayout.LayoutParams) mBatteryIconView.getLayoutParams(); + if (!oos13) { + scaledLayoutParams.width = (int) (batteryWidth * iconScaleFactor); + scaledLayoutParams.height = (int) (batteryHeight * iconScaleFactor); + } else { + scaledLayoutParams.width = batteryWidth; + scaledLayoutParams.height = batteryHeight; + } mBatteryIconView.setLayoutParams(scaledLayoutParams); mBatteryIconView.setVisibility(mHideBattery ? View.GONE : View.VISIBLE); mBatteryIconView.requestLayout();