From bd8dd70969289e78da6a0bcb8ca34d745ce0dd69 Mon Sep 17 00:00:00 2001 From: tehcneko <7764726+tehcneko@users.noreply.github.com> Date: Fri, 12 Feb 2021 22:11:52 +0800 Subject: [PATCH] [app] Remove light hack --- .../manager/adapters/ScopeAdapter.java | 6 +- .../manager/ui/activity/AppListActivity.java | 5 +- .../manager/ui/activity/BaseActivity.java | 5 +- .../manager/ui/activity/LogsActivity.java | 5 +- .../manager/ui/activity/MainActivity.java | 24 +- .../manager/ui/activity/RepoItemActivity.java | 4 +- .../manager/ui/activity/SettingsActivity.java | 5 +- .../ui/fragment/CompileDialogFragment.java | 4 +- .../ui/fragment/StatusDialogBuilder.java | 5 +- .../lsposed/manager/util/light/Hack.java | 1203 ----------------- .../lsposed/manager/util/light/Light.java | 47 - app/src/main/res/layout/activity_main.xml | 4 + 12 files changed, 34 insertions(+), 1283 deletions(-) delete mode 100644 app/src/main/java/io/github/lsposed/manager/util/light/Hack.java delete mode 100644 app/src/main/java/io/github/lsposed/manager/util/light/Light.java diff --git a/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java index 751c47a9..7e7cacac 100644 --- a/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java +++ b/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java @@ -31,12 +31,12 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import com.google.android.material.checkbox.MaterialCheckBox; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import java.util.ArrayList; @@ -201,7 +201,7 @@ public class ScopeAdapter extends RecyclerView.Adapter int itemId = item.getItemId(); if (itemId == R.id.use_recommended) { if (!checkedList.isEmpty()) { - new MaterialAlertDialogBuilder(activity) + new AlertDialog.Builder(activity) .setTitle(R.string.use_recommended) .setMessage(R.string.use_recommended_message) .setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended()) @@ -468,7 +468,7 @@ public class ScopeAdapter extends RecyclerView.Adapter public boolean onBackPressed() { if (masterSwitch.isChecked() && checkedList.isEmpty()) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity); + AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.use_recommended); builder.setMessage(hasRecommended() ? R.string.no_scope_selected_has_recommended : R.string.no_scope_selected); if (hasRecommended()) { diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java index 61759ac4..84c8eb1a 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/AppListActivity.java @@ -17,7 +17,6 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import io.github.lsposed.manager.BuildConfig; @@ -84,7 +83,7 @@ public class AppListActivity extends BaseActivity { } catch (Exception e) { e.printStackTrace(); } - AlertDialog alertDialog = new MaterialAlertDialogBuilder(this) + AlertDialog alertDialog = new AlertDialog.Builder(this) .setCancelable(false) .setMessage(R.string.settings_backuping) .show(); @@ -110,7 +109,7 @@ public class AppListActivity extends BaseActivity { } catch (Exception e) { e.printStackTrace(); } - AlertDialog alertDialog = new MaterialAlertDialogBuilder(this) + AlertDialog alertDialog = new AlertDialog.Builder(this) .setCancelable(false) .setMessage(R.string.settings_restoring) .show(); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/BaseActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/BaseActivity.java index 99735ee0..8ee0cf75 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/BaseActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/BaseActivity.java @@ -13,10 +13,9 @@ import android.view.Window; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StyleRes; +import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - import java.util.Objects; import io.github.lsposed.manager.App; @@ -101,7 +100,7 @@ public class BaseActivity extends MaterialActivity { Version managerVersion = new Version(BuildConfig.VERSION_NAME); Version coreVersion = new Version(coreVersionStr); if (!managerVersion.equals(coreVersion)) { - new MaterialAlertDialogBuilder(this) + new AlertDialog.Builder(this) .setMessage(R.string.outdated_manager) .setPositiveButton(R.string.ok, (dialog, id) -> { NavUtil.startURL(this, getString(R.string.about_source)); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java index 55d82312..0db4094c 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java @@ -22,7 +22,6 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.FileProvider; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.tabs.TabLayout; @@ -79,7 +78,7 @@ public class LogsActivity extends BaseActivity { } if (!preferences.getBoolean("hide_logcat_warning", false)) { DialogInstallWarningBinding binding = DialogInstallWarningBinding.inflate(getLayoutInflater()); - new MaterialAlertDialogBuilder(this) + new AlertDialog.Builder(this) .setTitle(R.string.install_warning_title) .setMessage(R.string.not_logcat) .setView(binding.getRoot()) @@ -251,7 +250,7 @@ public class LogsActivity extends BaseActivity { @Override protected void onPreExecute() { - mProgressDialog = new MaterialAlertDialogBuilder(LogsActivity.this).create(); + mProgressDialog = new AlertDialog.Builder(LogsActivity.this).create(); mProgressDialog.setMessage(getString(R.string.loading)); mProgressDialog.setCancelable(false); handler.postDelayed(mRunnable, 300); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java index 31fa6f29..96316a6a 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java @@ -2,6 +2,7 @@ package io.github.lsposed.manager.ui.activity; import android.annotation.SuppressLint; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.view.View; @@ -19,7 +20,6 @@ import io.github.lsposed.manager.ui.fragment.StatusDialogBuilder; import io.github.lsposed.manager.util.GlideHelper; import io.github.lsposed.manager.util.ModuleUtil; import io.github.lsposed.manager.util.NavUtil; -import io.github.lsposed.manager.util.light.Light; import name.mikanoshi.customiuizer.holidays.HolidayHelper; import name.mikanoshi.customiuizer.utils.Helpers; @@ -52,28 +52,35 @@ public class MainActivity extends BaseActivity { .load(GlideHelper.wrapApplicationInfoForIconLoader(getApplicationInfo())) .into(binding.appIcon); String installedXposedVersion = Constants.getXposedVersion(); + int cardBackgroundColor; if (installedXposedVersion != null) { binding.statusTitle.setText(String.format(Locale.US, "%s %s", getString(R.string.Activated), Constants.getXposedVariant())); if (!Constants.isPermissive()) { if (Helpers.currentHoliday == Helpers.Holidays.LUNARNEWYEAR) { - binding.status.setCardBackgroundColor(0xfff05654); + cardBackgroundColor = 0xfff05654; + } else { - binding.status.setCardBackgroundColor(ContextCompat.getColor(this, R.color.colorNormal)); + cardBackgroundColor = ContextCompat.getColor(this, R.color.colorNormal); } binding.statusIcon.setImageResource(R.drawable.ic_check_circle); binding.statusSummary.setText(String.format(Locale.US, "%s (%d)", installedXposedVersion, Constants.getXposedVersionCode())); } else { - binding.status.setCardBackgroundColor(ContextCompat.getColor(this, R.color.colorError)); + cardBackgroundColor = ContextCompat.getColor(this, R.color.colorError); binding.statusIcon.setImageResource(R.drawable.ic_warning); binding.statusSummary.setText(R.string.selinux_permissive_summary); } } else { + cardBackgroundColor = ContextCompat.getColor(this, R.color.colorInstall); binding.statusTitle.setText(R.string.Install); binding.statusSummary.setText(R.string.InstallDetail); - binding.status.setCardBackgroundColor(ContextCompat.getColor(this, R.color.colorInstall)); binding.statusIcon.setImageResource(R.drawable.ic_error); Snackbar.make(binding.snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_LONG).show(); } + binding.status.setCardBackgroundColor(cardBackgroundColor); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + binding.status.setOutlineSpotShadowColor(cardBackgroundColor); + binding.status.setOutlineAmbientShadowColor(cardBackgroundColor); + } } private class StartActivityListener implements View.OnClickListener { @@ -99,12 +106,7 @@ public class MainActivity extends BaseActivity { @Override protected void onResume() { - getWindow().getDecorView().post(() -> { - if (Light.setLightSourceAlpha(getWindow().getDecorView(), 0.01f, 0.029f)) { - binding.status.setElevation(24); - binding.modules.setElevation(12); - } - }); + super.onResume(); binding.modulesSummary.setText(String.format(getString(R.string.ModulesDetail), ModuleUtil.getInstance().getEnabledModules().size())); HolidayHelper.onResume(); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/RepoItemActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/RepoItemActivity.java index 8b550ed0..bb84be5d 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/RepoItemActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/RepoItemActivity.java @@ -35,10 +35,10 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.widget.ViewPager2; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.tabs.TabLayoutMediator; import java.util.ArrayList; @@ -254,7 +254,7 @@ public class RepoItemActivity extends BaseActivity { holder.viewAssets.setOnClickListener(v -> { ArrayList names = new ArrayList<>(); assets.forEach(releaseAsset -> names.add(releaseAsset.getName())); - new MaterialAlertDialogBuilder(RepoItemActivity.this) + new AlertDialog.Builder(RepoItemActivity.this) .setItems(names.toArray(new String[0]), (dialog, which) -> NavUtil.startURL(RepoItemActivity.this, assets.get(which).getDownloadUrl())) .show(); }); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java index ff5a18ad..411caa92 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java @@ -21,7 +21,6 @@ import androidx.preference.Preference; import androidx.preference.SwitchPreference; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import com.takisoft.preferencex.PreferenceFragmentCompat; import com.takisoft.preferencex.SimpleMenuPreference; @@ -133,7 +132,7 @@ public class SettingsActivity extends BaseActivity { } catch (Exception e) { e.printStackTrace(); } - AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireActivity()) + AlertDialog alertDialog = new AlertDialog.Builder(requireActivity()) .setCancelable(false) .setMessage(R.string.settings_backuping) .show(); @@ -160,7 +159,7 @@ public class SettingsActivity extends BaseActivity { } catch (Exception e) { e.printStackTrace(); } - AlertDialog alertDialog = new MaterialAlertDialogBuilder(requireActivity()) + AlertDialog alertDialog = new AlertDialog.Builder(requireActivity()) .setCancelable(false) .setMessage(R.string.settings_restoring) .show(); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/fragment/CompileDialogFragment.java b/app/src/main/java/io/github/lsposed/manager/ui/fragment/CompileDialogFragment.java index 2915cffc..b0646fc4 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/fragment/CompileDialogFragment.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/fragment/CompileDialogFragment.java @@ -11,11 +11,11 @@ import android.view.LayoutInflater; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDialogFragment; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.Lifecycle; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import java.io.BufferedReader; @@ -58,7 +58,7 @@ public class CompileDialogFragment extends AppCompatDialogFragment { FragmentCompileDialogBinding binding = FragmentCompileDialogBinding.inflate(LayoutInflater.from(requireActivity()), null, false); final PackageManager pm = requireContext().getPackageManager(); - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity()) + AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()) .setIcon(appInfo.loadIcon(pm)) .setTitle(appInfo.loadLabel(pm)) .setView(binding.getRoot()); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java b/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java index c825dd71..435497d1 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java @@ -1,6 +1,7 @@ package io.github.lsposed.manager.ui.fragment; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.content.Context; import android.os.Build; import android.view.LayoutInflater; @@ -9,8 +10,6 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.core.text.HtmlCompat; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - import java.io.BufferedReader; import java.io.File; import java.io.FileReader; @@ -24,7 +23,7 @@ import io.github.lsposed.manager.R; import io.github.lsposed.manager.databinding.StatusInstallerBinding; @SuppressLint("StaticFieldLeak") -public class StatusDialogBuilder extends MaterialAlertDialogBuilder { +public class StatusDialogBuilder extends AlertDialog.Builder { private static final String CPU_ABI; private static final String CPU_ABI2; diff --git a/app/src/main/java/io/github/lsposed/manager/util/light/Hack.java b/app/src/main/java/io/github/lsposed/manager/util/light/Hack.java deleted file mode 100644 index 71eda722..00000000 --- a/app/src/main/java/io/github/lsposed/manager/util/light/Hack.java +++ /dev/null @@ -1,1203 +0,0 @@ -package io.github.lsposed.manager.util.light; - -import android.util.Log; - -import androidx.annotation.CheckResult; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.IOException; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.Comparator; - -/** - * Java reflection helper optimized for hacking non-public APIs. - * The core design philosophy behind is compile-time consistency enforcement. - *

- * It's suggested to declare all hacks in a centralized point, typically as static fields in a class. - * Then call it during application initialization, thus they are verified all together in an early stage. - * If any assertion failed, a fall-back strategy is suggested. - * - *

https://gist.github.com/oasisfeng/75d3774ca5441372f049818de4d52605 - * - * @author Oasis - * @see Demo - */ -@SuppressWarnings({"Convert2Lambda", "WeakerAccess", "unused"}) -class Hack { - - public static Class ANY_TYPE = $.class; - private static final HackedClass FALLBACK = new HackedClass<>(ANY_TYPE); - private static AssertionFailureHandler sFailureHandler; - - private Hack() { - } - - public static HackedClass into(final @NonNull Class clazz) { - return new HackedClass<>(clazz); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public static HackedClass into(final String class_name) { - try { - return new HackedClass(Class.forName(class_name)); - } catch (final ClassNotFoundException e) { - fail(new AssertionException(e)); - return new HackedClass(ANY_TYPE); // Use AnyType as a lazy trick to make fallback working and avoid null. - } - } - - @SuppressWarnings("unchecked") - public static HackedClass onlyIf(final boolean condition, final Hacking> hacking) { - if (condition) return hacking.hack(); - return (HackedClass) FALLBACK; - } - - public static ConditionalHack onlyIf(final boolean condition) { - return condition ? new ConditionalHack() { - @Override - public HackedClass into(@NonNull final Class clazz) { - return Hack.into(clazz); - } - - @Override - public HackedClass into(final String class_name) { - return Hack.into(class_name); - } - } : new ConditionalHack() { - @SuppressWarnings("unchecked") - @Override - public HackedClass into(@NonNull final Class clazz) { - return (HackedClass) FALLBACK; - } - - @SuppressWarnings("unchecked") - @Override - public HackedClass into(final String class_name) { - return (HackedClass) FALLBACK; - } - }; - } - - private static void fail(final AssertionException e) { - if (sFailureHandler != null) sFailureHandler.onAssertionFailure(e); - } - - /** - * Specify a handler to deal with assertion failure, and decide whether the failure should be thrown. - */ - public static AssertionFailureHandler setAssertionFailureHandler(final AssertionFailureHandler handler) { - final AssertionFailureHandler old = sFailureHandler; - sFailureHandler = handler; - return old; - } - - /** - * Use {@link Hack#setAssertionFailureHandler(AssertionFailureHandler) } to set the global handler - */ - public interface AssertionFailureHandler { - void onAssertionFailure(AssertionException failure); - } - - public interface HackedField { - T get(C instance); - - void set(C instance, T value); - - HackedTargetField on(C target); - - Class getType(); - - boolean isAbsent(); - } - - public interface HackedTargetField { - T get(); - - void set(T value); - - Class getType(); - - boolean isAbsent(); - } - - public interface HackedInvokable { - @CheckResult - HackedInvokable throwing(Class type); - - @CheckResult - HackedInvokable throwing(Class type1, Class type2); - - @CheckResult - HackedInvokable throwing(Class type1, Class type2, Class type3); - - @Nullable - HackedMethod0 withoutParams(); - - @Nullable - HackedMethod1 withParam(Class type); - - @Nullable - HackedMethod2 withParams(Class type1, Class type2); - - @Nullable - HackedMethod3 withParams(Class type1, Class type2, Class type3); - - @Nullable - HackedMethod4 withParams(Class type1, Class type2, Class type3, Class type4); - - @Nullable - HackedMethod5 withParams(Class type1, final Class type2, final Class type3, final Class type4, final Class type5); - - @Nullable - HackedMethodN withParams(Class... types); - } - - public interface NonNullHackedInvokable extends HackedInvokable { - @CheckResult - NonNullHackedInvokable throwing(Class type); - - @CheckResult - NonNullHackedInvokable throwing(Class type1, Class type2); - - @CheckResult - NonNullHackedInvokable throwing(Class type1, Class type2, Class type3); - - @NonNull - HackedMethod0 withoutParams(); - - @NonNull - HackedMethod1 withParam(Class type); - - @NonNull - HackedMethod2 withParams(Class type1, Class type2); - - @NonNull - HackedMethod3 withParams(Class type1, Class type2, Class type3); - - @NonNull - HackedMethod4 withParams(Class type1, Class type2, Class type3, Class type4); - - @NonNull - HackedMethod5 withParams(Class type1, final Class type2, final Class type3, final Class type4, final Class type5); - - @NonNull - HackedMethodN withParams(Class... types); - } - - public interface HackedMethod extends HackedInvokable { - /** - * Optional - */ - @CheckResult - HackedMethod returning(Class type); - - /** - * Fallback to the given value if this field is unavailable at runtime. (Optional) - */ - @CheckResult - NonNullHackedMethod fallbackReturning(R return_value); - - @CheckResult - HackedMethod throwing(Class type); - - @CheckResult - HackedMethod throwing(Class type1, Class type2); - - @CheckResult - HackedMethod throwing(Class type1, Class type2, Class type3); - - @CheckResult - HackedMethod throwing(Class... types); - } - - @SuppressWarnings("NullableProblems") // Force to NonNull - public interface NonNullHackedMethod extends HackedMethod, NonNullHackedInvokable { - /** - * Optional - */ - @CheckResult - HackedMethod returning(Class type); - - @CheckResult - NonNullHackedMethod throwing(Class type); - - @CheckResult - NonNullHackedMethod throwing(Class type1, Class type2); - - @CheckResult - NonNullHackedMethod throwing(Class type1, Class type2, Class type3); - } - - interface Invokable { - Object invoke(C target, Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException; - - Class getReturnType(); - } - - public interface Hacking { - T hack(); - } - - public interface ConditionalHack { - /** - * WARNING: Never use this method if the target class may not exist when the condition is not met, use {@link #onlyIf(boolean, Hacking)} instead. - */ - HackedClass into(final @NonNull Class clazz); - - HackedClass into(final String class_name); - } - - private static class $ { - } - - public static class AssertionException extends Throwable { - - private static final long serialVersionUID = 1L; - private Class mClass; - private Field mHackedField; - private Method mHackedMethod; - private String mHackedFieldName; - private String mHackedMethodName; - private Class[] mParamTypes; - - AssertionException(final String e) { - super(e); - } - - AssertionException(final Exception e) { - super(e); - } - - @Override - public String toString() { - return getCause() != null ? getClass().getName() + ": " + getCause() : super.toString(); - } - - public String getDebugInfo() { - final StringBuilder info = new StringBuilder(getCause() != null ? getCause().toString() : super.toString()); - final Throwable cause = getCause(); - if (cause instanceof NoSuchMethodException) { - info.append(" Potential candidates:"); - final int initial_length = info.length(); - final String name = getHackedMethodName(); - if (name != null) { - for (final Method method : getHackedClass().getDeclaredMethods()) - if (method.getName().equals(name)) // Exact name match - info.append(' ').append(method); - if (info.length() == initial_length) - for (final Method method : getHackedClass().getDeclaredMethods()) - if (method.getName().startsWith(name)) // Name prefix match - info.append(' ').append(method); - if (info.length() == initial_length) - for (final Method method : getHackedClass().getDeclaredMethods()) - if (!method.getName().startsWith("-")) // Dump all but generated methods - info.append(' ').append(method); - } else - for (final Constructor constructor : getHackedClass().getDeclaredConstructors()) - info.append(' ').append(constructor); - } else if (cause instanceof NoSuchFieldException) { - info.append(" Potential candidates:"); - final int initial_length = info.length(); - final String name = getHackedFieldName(); - final Field[] fields = getHackedClass().getDeclaredFields(); - for (final Field field : fields) - if (field.getName().equals(name)) // Exact name match - info.append(' ').append(field); - if (info.length() == initial_length) for (final Field field : fields) - if (field.getName().startsWith(name)) // Name prefix match - info.append(' ').append(field); - if (info.length() == initial_length) for (final Field field : fields) - if (!field.getName().startsWith("$")) // Dump all but generated fields - info.append(' ').append(field); - } - return info.toString(); - } - - public Class getHackedClass() { - return mClass; - } - - AssertionException setHackedClass(final Class hacked_class) { - mClass = hacked_class; - return this; - } - - public Method getHackedMethod() { - return mHackedMethod; - } - - AssertionException setHackedMethod(final Method method) { - mHackedMethod = method; - return this; - } - - public String getHackedMethodName() { - return mHackedMethodName; - } - - AssertionException setHackedMethodName(final String method) { - mHackedMethodName = method; - return this; - } - - public Class[] getParamTypes() { - return mParamTypes; - } - - AssertionException setParamTypes(final Class[] param_types) { - mParamTypes = param_types; - return this; - } - - public Field getHackedField() { - return mHackedField; - } - - AssertionException setHackedField(final Field field) { - mHackedField = field; - return this; - } - - public String getHackedFieldName() { - return mHackedFieldName; - } - - AssertionException setHackedFieldName(final String field) { - mHackedFieldName = field; - return this; - } - } - - public static class FieldToHack { - - protected final Class mClass; - protected final String mName; - protected final int mModifiers; - - /** - * @param modifiers the modifiers this field must have - */ - protected FieldToHack(final Class clazz, final String name, final int modifiers) { - mClass = clazz; - mName = name; - mModifiers = modifiers; - } - - protected @Nullable - Field findField(final @Nullable Class type) { - if (mClass == ANY_TYPE) - return null; // AnyType as a internal indicator for class not found. - Field field = null; - try { - field = mClass.getDeclaredField(mName); - if (Modifier.isStatic(mModifiers) != Modifier.isStatic(field.getModifiers())) { - fail(new AssertionException(field + (Modifier.isStatic(mModifiers) ? " is not static" : " is static")).setHackedFieldName(mName)); - field = null; - } else if (mModifiers > 0 && (field.getModifiers() & mModifiers) != mModifiers) { - fail(new AssertionException(field + " does not match modifiers: " + mModifiers).setHackedFieldName(mName)); - field = null; - } else if (!field.isAccessible()) field.setAccessible(true); - } catch (final NoSuchFieldException e) { - final AssertionException hae = new AssertionException(e); - hae.setHackedClass(mClass); - hae.setHackedFieldName(mName); - fail(hae); - } - - if (type != null && field != null && !type.isAssignableFrom(field.getType())) - fail(new AssertionException(new ClassCastException(field + " is not of type " + type)).setHackedField(field)); - return field; - } - } - - public static class MemberFieldToHack extends FieldToHack { - - /** - * @param modifiers the modifiers this field must have - */ - private MemberFieldToHack(final Class clazz, final String name, final int modifiers) { - super(clazz, name, modifiers); - } - - /** - * Assert the field type. - */ - public @Nullable - HackedField ofType(final Class type) { - return ofType(type, false, null); - } - - public @Nullable - HackedField ofType(final String type_name) { - try { //noinspection unchecked - return ofType((Class) Class.forName(type_name, false, mClass.getClassLoader())); - } catch (final ClassNotFoundException e) { - fail(new AssertionException(e)); - return null; - } - } - - public @NonNull - HackedField fallbackTo(final byte value) { //noinspection ConstantConditions - return ofType(byte.class, true, value); - } - - public @NonNull - HackedField fallbackTo(final char value) { //noinspection ConstantConditions - return ofType(char.class, true, value); - } - - public @NonNull - HackedField fallbackTo(final short value) { //noinspection ConstantConditions - return ofType(short.class, true, value); - } - - public @NonNull - HackedField fallbackTo(final int value) { //noinspection ConstantConditions - return ofType(int.class, true, value); - } - - public @NonNull - HackedField fallbackTo(final long value) { //noinspection ConstantConditions - return ofType(long.class, true, value); - } - - public @NonNull - HackedField fallbackTo(final boolean value) { //noinspection ConstantConditions - return ofType(boolean.class, true, value); - } - - public @NonNull - HackedField fallbackTo(final float value) { //noinspection ConstantConditions - return ofType(float.class, true, value); - } - - public @NonNull - HackedField fallbackTo(final double value) { //noinspection ConstantConditions - return ofType(double.class, true, value); - } - - /** - * Fallback to the given value if this field is unavailable at runtime - */ - public @NonNull - HackedField fallbackTo(final T value) { - @SuppressWarnings("unchecked") final Class type = value == null ? null : (Class) value.getClass(); - //noinspection ConstantConditions - return ofType(type, true, value); - } - - private HackedField ofType(final Class type, final boolean fallback, final T fallback_value) { - final Field field = findField(type); - return field != null ? new HackedFieldImpl(field) : fallback ? new FallbackField(type, fallback_value) : null; - } - } - - public static class StaticFieldToHack extends FieldToHack { - - /** - * @param modifiers the modifiers this field must have - */ - private StaticFieldToHack(final Class clazz, final String name, final int modifiers) { - super(clazz, name, modifiers); - } - - /** - * Assert the field type. - */ - public @Nullable - HackedTargetField ofType(final Class type) { - return ofType(type, false, null); - } - - public @Nullable - HackedTargetField ofType(final String type_name) { - try { //noinspection unchecked - return ofType((Class) Class.forName(type_name, false, mClass.getClassLoader())); - } catch (final ClassNotFoundException e) { - fail(new AssertionException(e)); - return null; - } - } - - /** - * Fallback to the given value if this field is unavailable at runtime - */ - public @NonNull - HackedTargetField fallbackTo(final T value) { - @SuppressWarnings("unchecked") final Class type = value == null ? null : (Class) value.getClass(); - //noinspection ConstantConditions - return ofType(type, true, value); - } - - private HackedTargetField ofType(final Class type, final boolean fallback, final T fallback_value) { - final Field field = findField(type); - return field != null ? new HackedFieldImpl(field).onTarget(null) : fallback ? new FallbackField(type, fallback_value) : null; - } - } - - private static class HackedFieldImpl implements HackedField { - - private final @NonNull - Field mField; - - HackedFieldImpl(final @NonNull Field field) { - mField = field; - } - - @Override - public HackedTargetFieldImpl on(final C target) { - if (target == null) throw new IllegalArgumentException("target is null"); - return onTarget(target); - } - - private HackedTargetFieldImpl onTarget(final @Nullable C target) { - return new HackedTargetFieldImpl<>(mField, target); - } - - /** - * Get current value of this field - */ - @Override - public T get(final C instance) { - try { - @SuppressWarnings("unchecked") final T value = (T) mField.get(instance); - return value; - } catch (final IllegalAccessException e) { - return null; - } // Should never happen - } - - /** - * Set value of this field - * - *

No type enforced here since most type mismatch can be easily tested and exposed early.

- */ - @Override - public void set(final C instance, final T value) { - try { - mField.set(instance, value); - } catch (final IllegalAccessException ignored) { - } // Should never happen - } - - @Override - @SuppressWarnings("unchecked") - public @Nullable - Class getType() { - return (Class) mField.getType(); - } - - @Override - public boolean isAbsent() { - return false; - } - - public @Nullable - Field getField() { - return mField; - } - } - - private static class FallbackField implements HackedField, HackedTargetField { - - private final Class mType; - private final T mValue; - - private FallbackField(final Class type, final T value) { - mType = type; - mValue = value; - } - - @Override - public T get(final C instance) { - return mValue; - } - - @Override - public void set(final C instance, final T value) { - } - - @Override - public T get() { - return mValue; - } - - @Override - public void set(final T value) { - } - - @Override - public HackedTargetField on(final C target) { - return this; - } - - @Override - public Class getType() { - return mType; - } - - @Override - public boolean isAbsent() { - return true; - } - } - - public static class HackedTargetFieldImpl implements HackedTargetField { - - private final Field mField; - private final Object mInstance; // Instance type is already checked - private @Nullable - T mFallbackValue; - - HackedTargetFieldImpl(final Field field, final @Nullable Object instance) { - mField = field; - mInstance = instance; - } - - @Override - public T get() { - if (mField == null) return mFallbackValue; - try { - @SuppressWarnings("unchecked") final T value = (T) mField.get(mInstance); - return value; - } catch (final IllegalAccessException e) { - return null; - } // Should never happen - } - - @Override - public void set(final T value) { - if (mField != null) try { - mField.set(mInstance, value); - } catch (final IllegalAccessException ignored) { - } // Should never happen - } - - @Override - @SuppressWarnings("unchecked") - public @Nullable - Class getType() { - return (Class) mField.getType(); - } - - @Override - public boolean isAbsent() { - return mField == null; - } - } - - public static class CheckedHackedMethod { - - private final Invokable mInvokable; - - CheckedHackedMethod(final Invokable invokable) { - mInvokable = invokable; - } - - @SuppressWarnings("unchecked") - public Class getReturnType() { - return (Class) mInvokable.getReturnType(); - } - - protected HackInvocation invoke(final Object... args) { - return new HackInvocation<>(mInvokable, args); - } - - /** - * Whether this hack is absent, thus will be fallen-back when invoked - */ - public boolean isAbsent() { - return mInvokable instanceof FallbackInvokable; - } - } - - public static class HackedMethod0 extends CheckedHackedMethod { - HackedMethod0(final Invokable invokable) { - super(invokable); - } - - public @CheckResult - HackInvocation invoke() { - return super.invoke(); - } - } - - public static class HackedMethod1 extends CheckedHackedMethod { - HackedMethod1(final Invokable invokable) { - super(invokable); - } - - public @CheckResult - HackInvocation invoke(final A1 arg) { - return super.invoke(arg); - } - } - - public static class HackedMethod2 extends CheckedHackedMethod { - HackedMethod2(final Invokable invokable) { - super(invokable); - } - - public @CheckResult - HackInvocation invoke(final A1 arg1, final A2 arg2) { - return super.invoke(arg1, arg2); - } - } - - public static class HackedMethod3 extends CheckedHackedMethod { - HackedMethod3(final Invokable invokable) { - super(invokable); - } - - public @CheckResult - HackInvocation invoke(final A1 arg1, final A2 arg2, final A3 arg3) { - return super.invoke(arg1, arg2, arg3); - } - } - - public static class HackedMethod4 extends CheckedHackedMethod { - HackedMethod4(final Invokable invokable) { - super(invokable); - } - - public @CheckResult - HackInvocation invoke(final A1 arg1, final A2 arg2, final A3 arg3, final A4 arg4) { - return super.invoke(arg1, arg2, arg3, arg4); - } - } - - public static class HackedMethod5 extends CheckedHackedMethod { - HackedMethod5(final Invokable invokable) { - super(invokable); - } - - public @CheckResult - HackInvocation invoke(final A1 arg1, final A2 arg2, final A3 arg3, final A4 arg4, final A5 arg5) { - return super.invoke(arg1, arg2, arg3, arg4, arg5); - } - } - - public static class HackedMethodN extends CheckedHackedMethod { - HackedMethodN(final Invokable invokable) { - super(invokable); - } - - public @CheckResult - HackInvocation invoke(final Object... args) { - return super.invoke(args); - } - } - - public static class HackInvocation { - - private final Invokable invokable; - private final Object[] args; - - HackInvocation(final Invokable invokable, final Object... args) { - this.invokable = invokable; - this.args = args; - } - - public R on(final @NonNull C target) throws T1, T2, T3 { - return onTarget(target); - } - - public R statically() throws T1, T2, T3 { - return onTarget(null); - } - - private R onTarget(final C target) throws T1 { //noinspection TryWithIdenticalCatches - try { - @SuppressWarnings("unchecked") final R result = (R) invokable.invoke(target, args); - return result; - } catch (final IllegalAccessException e) { - throw new RuntimeException(e); // Should never happen - } catch (final InstantiationException e) { - throw new RuntimeException(e); - } catch (final InvocationTargetException e) { - final Throwable ex = e.getTargetException(); - //noinspection unchecked - throw (T1) ex; - } - } - } - - private static class HackedMethodImpl implements NonNullHackedMethod { - - private static final Comparator CLASS_COMPARATOR = new Comparator() { - @Override - public int compare(final Class lhs, final Class rhs) { - return lhs.toString().compareTo(rhs.toString()); - } - - @Override - public boolean equals(final Object object) { - return this == object; - } - }; - private final Class mClass; - private final @Nullable - String mName; // Null for constructor - private final int mModifiers; - private Class mReturnType; - private Class[] mThrowTypes; - private R mFallbackReturnValue; - private boolean mHasFallback; - - HackedMethodImpl(final Class clazz, @Nullable final String name, final int modifiers) { - //noinspection unchecked, to be compatible with HackedClass.staticMethod() - mClass = (Class) clazz; - mName = name; - mModifiers = modifiers; - } - - @Override - public HackedMethod returning(final Class type) { - mReturnType = type; - @SuppressWarnings("unchecked") final HackedMethod casted = (HackedMethod) this; - return casted; - } - - @Override - public NonNullHackedMethod fallbackReturning(final R value) { - mFallbackReturnValue = value; - mHasFallback = true; - return this; - } - - @Override - public NonNullHackedMethod throwing(final Class type) { - mThrowTypes = new Class[]{type}; - @SuppressWarnings("unchecked") final NonNullHackedMethod casted = (NonNullHackedMethod) this; - return casted; - } - - @Override - public NonNullHackedMethod - throwing(final Class type1, final Class type2) { - mThrowTypes = new Class[]{type1, type2}; - Arrays.sort(mThrowTypes, CLASS_COMPARATOR); - @SuppressWarnings("unchecked") final NonNullHackedMethod cast = (NonNullHackedMethod) this; - return cast; - } - - @Override - public NonNullHackedMethod - throwing(final Class type1, final Class type2, final Class type3) { - mThrowTypes = new Class[]{type1, type2, type3}; - Arrays.sort(mThrowTypes, CLASS_COMPARATOR); - @SuppressWarnings("unchecked") final NonNullHackedMethod cast = (NonNullHackedMethod) this; - return cast; - } - - @Override - public HackedMethod throwing(final Class... types) { - mThrowTypes = types; - Arrays.sort(mThrowTypes, CLASS_COMPARATOR); - @SuppressWarnings("unchecked") final HackedMethod cast = (HackedMethod) this; - return cast; - } - - @NonNull - @SuppressWarnings("ConstantConditions") - @Override - public HackedMethod0 withoutParams() { - final Invokable invokable = findInvokable(); - return invokable == null ? null : new HackedMethod0(invokable); - } - - @NonNull - @SuppressWarnings("ConstantConditions") - @Override - public HackedMethod1 withParam(final Class type) { - final Invokable invokable = findInvokable(type); - return invokable == null ? null : new HackedMethod1(invokable); - } - - @NonNull - @SuppressWarnings("ConstantConditions") - @Override - public HackedMethod2 withParams(final Class type1, final Class type2) { - final Invokable invokable = findInvokable(type1, type2); - return invokable == null ? null : new HackedMethod2(invokable); - } - - @NonNull - @SuppressWarnings("ConstantConditions") - @Override - public HackedMethod3 withParams(final Class type1, final Class type2, final Class type3) { - final Invokable invokable = findInvokable(type1, type2, type3); - return invokable == null ? null : new HackedMethod3(invokable); - } - - @NonNull - @SuppressWarnings("ConstantConditions") - @Override - public HackedMethod4 withParams(final Class type1, final Class type2, final Class type3, final Class type4) { - final Invokable invokable = findInvokable(type1, type2, type3, type4); - return invokable == null ? null : new HackedMethod4(invokable); - } - - @NonNull - @SuppressWarnings("ConstantConditions") - @Override - public HackedMethod5 withParams(final Class type1, final Class type2, final Class type3, final Class type4, final Class type5) { - final Invokable invokable = findInvokable(type1, type2, type3, type4, type5); - return invokable == null ? null : new HackedMethod5(invokable); - } - - @NonNull - @SuppressWarnings("ConstantConditions") - @Override - public HackedMethodN withParams(final Class... types) { - final Invokable invokable = findInvokable(types); - return invokable == null ? null : new HackedMethodN(invokable); - } - - private @Nullable - Invokable findInvokable(final Class... param_types) { - if (mClass == ANY_TYPE) // AnyType as a internal indicator for class not found. - return mHasFallback ? new FallbackInvokable(mFallbackReturnValue) : null; - - final int modifiers; - Invokable invokable; - final AccessibleObject accessible; - final Class[] ex_types; - try { - if (mName != null) { - final Method candidate = mClass.getDeclaredMethod(mName, param_types); - Method method = candidate; - ex_types = candidate.getExceptionTypes(); - modifiers = method.getModifiers(); - if (Modifier.isStatic(mModifiers) != Modifier.isStatic(candidate.getModifiers())) { - fail(new AssertionException(candidate + (Modifier.isStatic(mModifiers) ? " is not static" : "is static")).setHackedMethod(method)); - method = null; - } - if (mReturnType != null && mReturnType != ANY_TYPE && !candidate.getReturnType().equals(mReturnType)) { - fail(new AssertionException("Return type mismatch: " + candidate)); - method = null; - } - if (method != null) { - invokable = new InvokableMethod<>(method); - accessible = method; - } else { - invokable = null; - accessible = null; - } - } else { - final Constructor ctor = mClass.getDeclaredConstructor(param_types); - modifiers = ctor.getModifiers(); - invokable = new InvokableConstructor<>(ctor); - accessible = ctor; - ex_types = ctor.getExceptionTypes(); - } - } catch (final NoSuchMethodException e) { - fail(new AssertionException(e).setHackedClass(mClass).setHackedMethodName(mName).setParamTypes(param_types)); - return mHasFallback ? new FallbackInvokable(mFallbackReturnValue) : null; - } - - if (mModifiers > 0 && (modifiers & mModifiers) != mModifiers) - fail(new AssertionException(invokable + " does not match modifiers: " + mModifiers).setHackedMethodName(mName)); - - if (mThrowTypes == null && ex_types.length > 0 || mThrowTypes != null && ex_types.length == 0) { - fail(new AssertionException("Checked exception(s) not match: " + invokable)); - if (ex_types.length > 0) - invokable = null; // No need to fall-back if expected checked exceptions are absent. - } else if (mThrowTypes != null) { - Arrays.sort(ex_types, CLASS_COMPARATOR); - if (!Arrays.equals(ex_types, mThrowTypes)) { // TODO: Check derived relation of exceptions - fail(new AssertionException("Checked exception(s) not match: " + invokable)); - invokable = null; - } - } - - if (invokable == null) { - if (!mHasFallback) return null; - return new FallbackInvokable<>(mFallbackReturnValue); - } - - if (!accessible.isAccessible()) accessible.setAccessible(true); - return invokable; - } - } - - private static class InvokableMethod implements Invokable { - - private final Method method; - - InvokableMethod(final Method method) { - this.method = method; - } - - public Object invoke(final C target, final Object[] args) throws IllegalAccessException, - IllegalArgumentException, InvocationTargetException { - return method.invoke(target, args); - } - - @Override - public Class getReturnType() { - return method.getReturnType(); - } - - @Override - public String toString() { - return method.toString(); - } - } - - private static class InvokableConstructor implements Invokable { - - private final Constructor constructor; - - InvokableConstructor(final Constructor method) { - this.constructor = method; - } - - public Object invoke(final C target, final Object[] args) throws InstantiationException, - IllegalAccessException, IllegalArgumentException, InvocationTargetException { - return constructor.newInstance(args); - } - - @Override - public Class getReturnType() { - return constructor.getDeclaringClass(); - } - - @Override - public String toString() { - return constructor.toString(); - } - } - - private static class FallbackInvokable implements Invokable { - - private final @Nullable - Object mValue; - - FallbackInvokable(final @Nullable Object value) { - mValue = value; - } - - @Override - public Object invoke(final C target, final Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException { - return mValue; - } - - @Override - public Class getReturnType() { - return mValue == null ? Object.class : mValue.getClass(); - } - } - - public static class HackedClass { - - private final Class mClass; - - HackedClass(final Class clazz) { - mClass = clazz; - } - - public @CheckResult - MemberFieldToHack field(final @NonNull String name) { - return new MemberFieldToHack<>(mClass, name, 0); - } - - public @CheckResult - StaticFieldToHack staticField(final @NonNull String name) { - return new StaticFieldToHack<>(mClass, name, Modifier.STATIC); - } - - public @CheckResult - NonNullHackedMethod method(final String name) { - return new HackedMethodImpl<>(mClass, name, 0); - } - - public @CheckResult - NonNullHackedMethod staticMethod(final String name) { - return new HackedMethodImpl<>(mClass, name, Modifier.STATIC); - } - - public @CheckResult - NonNullHackedInvokable constructor() { - final HackedMethodImpl constructor = new HackedMethodImpl<>(mClass, null, 0); - constructor.fallbackReturning(null); // Always fallback to null. - return constructor; - } - } - - /** - * This is a simple demo for the common usage of {@link Hack} - */ - @SuppressWarnings("unused") - private static class Demo { - - static String sField; - boolean mField; - - Demo(final int flags) { - } - - static void demo() { - final Demo demo = Hacks.Demo_ctor.invoke(0).statically(); - try { - Hacks.Demo_methodThrows.invoke().on(demo); - } catch (final InterruptedException | IOException e) { // The checked exceptions declared by throwing() in hack definition. - e.printStackTrace(); - } - Hacks.Demo_staticMethod.invoke(1, "xx").statically(); - } - - static boolean staticMethod(final int a, final String c) { - return false; - } - - private void methodThrows() throws InterruptedException, IOException { - } - - @SuppressWarnings({"FieldCanBeLocal", "UnnecessarilyQualifiedStaticUsage"}) - static class Hacks { - - static HackedMethod1 Demo_ctor; - static HackedMethod0 Demo_methodThrows; - static HackedMethod2 Demo_staticMethod; - static @Nullable - HackedField Demo_mField; // Optional hack may be null if assertion failed - static @Nullable - HackedTargetField Demo_sField; - - static { - Hack.setAssertionFailureHandler(new AssertionFailureHandler() { - @Override - public void onAssertionFailure(final AssertionException failure) { - Log.w("Demo", "Partially incompatible: " + failure.getDebugInfo()); - // Report the incompatibility silently. - //... - } - }); - Demo_ctor = Hack.into(Demo.class).constructor().withParam(int.class); - // Method without fallback (will be null if absent) - Demo_methodThrows = Hack.into(Demo.class).method("methodThrows").returning(Void.class).fallbackReturning(null) - .throwing(InterruptedException.class, IOException.class).withoutParams(); - // Method with fallback (will never be null) - Demo_staticMethod = Hack.into(Demo.class).staticMethod("methodWith2Params").returning(boolean.class) - .fallbackReturning(false).withParams(int.class, String.class); - Demo_mField = Hack.into(Demo.class).field("mField").fallbackTo(false); - Demo_sField = Hack.into(Demo.class).staticField("sField").ofType(String.class); - } - } - } - - /** - * Placeholder for unchecked exception - */ - public class Unchecked extends RuntimeException { - } -} diff --git a/app/src/main/java/io/github/lsposed/manager/util/light/Light.java b/app/src/main/java/io/github/lsposed/manager/util/light/Light.java deleted file mode 100644 index c7343a88..00000000 --- a/app/src/main/java/io/github/lsposed/manager/util/light/Light.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.lsposed.manager.util.light; - -import android.annotation.SuppressLint; -import android.graphics.HardwareRenderer; -import android.os.Build; -import android.view.View; - - -@SuppressWarnings({"unchecked", "ConstantConditions"}) -@SuppressLint("PrivateApi") -public class Light { - - public static boolean setLightSourceAlpha(View view, float ambientShadowAlpha, float spotShadowAlpha) { - try { - @SuppressWarnings("rawtypes") Class threadedRendererClass = Class.forName("android.view.ThreadedRenderer"); - - Object threadedRenderer = Hack.into(View.class) - .method("getThreadedRenderer") - .returning(threadedRendererClass) - .withoutParams() - .invoke() - .on(view); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - ((HardwareRenderer) threadedRenderer).setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha); - } else { - long mNativeProxy = (long) Hack.into(threadedRendererClass) - .field("mNativeProxy").ofType(long.class).get(threadedRenderer); - - float mLightRadius = (float) Hack.into(threadedRendererClass) - .field("mLightRadius") - .ofType(float.class) - .get(threadedRenderer); - - Hack.into(threadedRendererClass) - .staticMethod("nSetup") - .withParams(long.class, float.class, int.class, int.class) - .invoke(mNativeProxy, mLightRadius, - (int) (255 * ambientShadowAlpha + 0.5f), (int) (255 * spotShadowAlpha + 0.5f)) - .statically(); - } - return true; - } catch (Throwable tr) { - tr.printStackTrace(); - return false; - } - } -} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5dbd012e..f0bd4e0e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -132,6 +132,8 @@ android:clickable="true" android:focusable="true" android:foreground="?attr/selectableItemBackground" + android:outlineAmbientShadowColor="#3A000000" + android:outlineSpotShadowColor="#3A000000" app:cardCornerRadius="8dp" app:cardElevation="4dp" app:cardPreventCornerOverlap="false"> @@ -183,6 +185,8 @@ android:clickable="true" android:focusable="true" android:foreground="?attr/selectableItemBackground" + android:outlineAmbientShadowColor="#3A000000" + android:outlineSpotShadowColor="#3A000000" app:cardCornerRadius="8dp" app:cardElevation="4dp" app:cardPreventCornerOverlap="false">