diff --git a/android/build.gradle b/android/build.gradle index 11c1586f1..fcdf50f7b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -63,7 +63,6 @@ android { implementation deps.soloader implementation deps.jsr305 implementation deps.supportAppCompat - implementation deps.stetho testImplementation deps.mockito testImplementation deps.robolectric diff --git a/android/plugins/fresco/build.gradle b/android/plugins/fresco/build.gradle index 3edead239..ab768d263 100644 --- a/android/plugins/fresco/build.gradle +++ b/android/plugins/fresco/build.gradle @@ -21,8 +21,12 @@ android { implementation project(':android') implementation deps.fresco implementation deps.frescoFlipper - implementation deps.frescoStetho compileOnly deps.jsr305 + + // Exclude the actual stetho dep as we only want some of the fresco APIs here + implementation(deps.frescoStetho) { + exclude group: 'com.facebook.stetho' + } } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ActivityDescriptor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ActivityDescriptor.java index 1d44cb0aa..eeba69925 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ActivityDescriptor.java +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ActivityDescriptor.java @@ -14,9 +14,9 @@ import com.facebook.flipper.core.FlipperObject; import com.facebook.flipper.plugins.inspector.Named; import com.facebook.flipper.plugins.inspector.NodeDescriptor; import com.facebook.flipper.plugins.inspector.Touch; -import com.facebook.stetho.common.android.FragmentActivityAccessor; -import com.facebook.stetho.common.android.FragmentCompat; -import com.facebook.stetho.common.android.FragmentManagerAccessor; +import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentActivityAccessor; +import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentCompat; +import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentManagerAccessor; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/FragmentDescriptor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/FragmentDescriptor.java index 6b8be822a..22deb9304 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/FragmentDescriptor.java +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/FragmentDescriptor.java @@ -15,7 +15,7 @@ import com.facebook.flipper.core.FlipperObject; import com.facebook.flipper.plugins.inspector.Named; import com.facebook.flipper.plugins.inspector.NodeDescriptor; import com.facebook.flipper.plugins.inspector.Touch; -import com.facebook.stetho.common.android.ResourcesUtil; +import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.ResourcesUtil; import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/SupportFragmentDescriptor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/SupportFragmentDescriptor.java index 5b474a3cd..d1e86f1cf 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/SupportFragmentDescriptor.java +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/SupportFragmentDescriptor.java @@ -15,7 +15,7 @@ import com.facebook.flipper.core.FlipperObject; import com.facebook.flipper.plugins.inspector.Named; import com.facebook.flipper.plugins.inspector.NodeDescriptor; import com.facebook.flipper.plugins.inspector.Touch; -import com.facebook.stetho.common.android.ResourcesUtil; +import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.ResourcesUtil; import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewDescriptor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewDescriptor.java index ef8d8fcd0..af58717ff 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewDescriptor.java +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewDescriptor.java @@ -39,7 +39,7 @@ import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityEva import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityRoleUtil; import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityUtil; import com.facebook.flipper.plugins.inspector.descriptors.utils.EnumMapping; -import com.facebook.stetho.common.android.ResourcesUtil; +import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.ResourcesUtil; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewGroupDescriptor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewGroupDescriptor.java index 4aacf3cd8..40b490389 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewGroupDescriptor.java +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/ViewGroupDescriptor.java @@ -24,7 +24,7 @@ import com.facebook.flipper.plugins.inspector.InspectorValue; import com.facebook.flipper.plugins.inspector.Named; import com.facebook.flipper.plugins.inspector.NodeDescriptor; import com.facebook.flipper.plugins.inspector.Touch; -import com.facebook.stetho.common.android.FragmentCompatUtil; +import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentCompatUtil; import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/DialogFragmentAccessor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/DialogFragmentAccessor.java new file mode 100644 index 000000000..007907a6d --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/DialogFragmentAccessor.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.app.Dialog; + +public interface DialogFragmentAccessor + extends FragmentAccessor { + Dialog getDialog(DIALOG_FRAGMENT dialogFragment); +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentAccessor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentAccessor.java new file mode 100644 index 000000000..21fac8cc2 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentAccessor.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.content.res.Resources; +import android.view.View; +import javax.annotation.Nullable; + +public interface FragmentAccessor { + int NO_ID = 0; + + @Nullable + FRAGMENT_MANAGER getFragmentManager(FRAGMENT fragment); + + Resources getResources(FRAGMENT fragment); + + int getId(FRAGMENT fragment); + + @Nullable + String getTag(FRAGMENT fragment); + + @Nullable + View getView(FRAGMENT fragment); + + @Nullable + FRAGMENT_MANAGER getChildFragmentManager(FRAGMENT fragment); +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentActivityAccessor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentActivityAccessor.java new file mode 100644 index 000000000..0654518d0 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentActivityAccessor.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.app.Activity; +import javax.annotation.Nullable; + +public interface FragmentActivityAccessor { + @Nullable + FRAGMENT_MANAGER getFragmentManager(FRAGMENT_ACTIVITY activity); +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompat.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompat.java new file mode 100644 index 000000000..61f26f405 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompat.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.app.Activity; +import android.os.Build; +import java.lang.reflect.Field; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; + +/** + * Compatibility abstraction which allows us to generalize access to both the support library's + * fragments and the built-in framework version. Note: both versions can be live at the same time in + * a single application and even on a single object instance. + * + *

Type safety is enforced via generics internal to the implementation but treated as opaque from + * the outside. + * + * @param + * @param + * @param + * @param + */ +@NotThreadSafe +public abstract class FragmentCompat< + FRAGMENT, DIALOG_FRAGMENT, FRAGMENT_MANAGER, FRAGMENT_ACTIVITY extends Activity> { + private static FragmentCompat sFrameworkInstance; + private static FragmentCompat sSupportInstance; + + private static final boolean sHasSupportFragment; + + static { + sHasSupportFragment = + ReflectionUtil.tryGetClassForName("android.support.v4.app.Fragment") != null; + } + + @Nullable + public static FragmentCompat getFrameworkInstance() { + if (sFrameworkInstance == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + sFrameworkInstance = new FragmentCompatFramework(); + } + return sFrameworkInstance; + } + + @Nullable + public static FragmentCompat getSupportLibInstance() { + if (sSupportInstance == null && sHasSupportFragment) { + sSupportInstance = new FragmentCompatSupportLib(); + } + return sSupportInstance; + } + + FragmentCompat() {} + + public abstract Class getFragmentClass(); + + public abstract Class getDialogFragmentClass(); + + public abstract Class getFragmentActivityClass(); + + public abstract FragmentAccessor forFragment(); + + public abstract DialogFragmentAccessor + forDialogFragment(); + + public abstract FragmentManagerAccessor forFragmentManager(); + + public abstract FragmentActivityAccessor + forFragmentActivity(); + + static class FragmentManagerAccessorViaReflection + implements FragmentManagerAccessor { + @Nullable private Field mFieldMAdded; + + @SuppressWarnings("unchecked") + @Nullable + @Override + public List getAddedFragments(FRAGMENT_MANAGER fragmentManager) { + // This field is actually sitting on FragmentManagerImpl, which derives from FragmentManager. + if (mFieldMAdded == null) { + Field fieldMAdded = + ReflectionUtil.tryGetDeclaredField(fragmentManager.getClass(), "mAdded"); + + if (fieldMAdded != null) { + fieldMAdded.setAccessible(true); + mFieldMAdded = fieldMAdded; + } + } + + return (mFieldMAdded != null) + ? (List) ReflectionUtil.getFieldValue(mFieldMAdded, fragmentManager) + : null; + } + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatFramework.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatFramework.java new file mode 100644 index 000000000..0620cf275 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatFramework.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.app.FragmentManager; +import android.content.res.Resources; +import android.os.Build; +import android.view.View; +import javax.annotation.Nullable; + +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +final class FragmentCompatFramework + extends FragmentCompat { + private static final FragmentAccessorFrameworkHoneycomb sFragmentAccessor; + private static final DialogFragmentAccessorFramework sDialogFragmentAccessor; + private static final FragmentManagerAccessorViaReflection + sFragmentManagerAccessor = new FragmentManagerAccessorViaReflection<>(); + private static final FragmentActivityAccessorFramework sFragmentActivityAccessor = + new FragmentActivityAccessorFramework(); + + static { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + sFragmentAccessor = new FragmentAccessorFrameworkJellyBean(); + } else { + sFragmentAccessor = new FragmentAccessorFrameworkHoneycomb(); + } + + sDialogFragmentAccessor = new DialogFragmentAccessorFramework(sFragmentAccessor); + } + + @Override + public Class getFragmentClass() { + return Fragment.class; + } + + @Override + public Class getDialogFragmentClass() { + return DialogFragment.class; + } + + @Override + public Class getFragmentActivityClass() { + return Activity.class; + } + + @Override + public FragmentAccessorFrameworkHoneycomb forFragment() { + return sFragmentAccessor; + } + + @Override + public DialogFragmentAccessorFramework forDialogFragment() { + return sDialogFragmentAccessor; + } + + @Override + public FragmentManagerAccessorViaReflection forFragmentManager() { + return sFragmentManagerAccessor; + } + + @Override + public FragmentActivityAccessorFramework forFragmentActivity() { + return sFragmentActivityAccessor; + } + + private static class FragmentAccessorFrameworkHoneycomb + implements FragmentAccessor { + @Nullable + @Override + public FragmentManager getFragmentManager(Fragment fragment) { + return fragment.getFragmentManager(); + } + + @Override + public Resources getResources(Fragment fragment) { + return fragment.getResources(); + } + + @Override + public int getId(Fragment fragment) { + return fragment.getId(); + } + + @Nullable + @Override + public String getTag(Fragment fragment) { + return fragment.getTag(); + } + + @Nullable + @Override + public View getView(Fragment fragment) { + return fragment.getView(); + } + + @Nullable + @Override + public FragmentManager getChildFragmentManager(Fragment fragment) { + return null; + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private static class FragmentAccessorFrameworkJellyBean + extends FragmentAccessorFrameworkHoneycomb { + @Nullable + @Override + public FragmentManager getChildFragmentManager(Fragment fragment) { + return fragment.getChildFragmentManager(); + } + } + + private static class DialogFragmentAccessorFramework + implements DialogFragmentAccessor { + private final FragmentAccessor mFragmentAccessor; + + public DialogFragmentAccessorFramework( + FragmentAccessor fragmentAccessor) { + mFragmentAccessor = fragmentAccessor; + } + + @Override + public Dialog getDialog(DialogFragment dialogFragment) { + return dialogFragment.getDialog(); + } + + @Nullable + @Override + public FragmentManager getFragmentManager(Fragment fragment) { + return mFragmentAccessor.getFragmentManager(fragment); + } + + @Override + public Resources getResources(Fragment fragment) { + return mFragmentAccessor.getResources(fragment); + } + + @Override + public int getId(Fragment fragment) { + return mFragmentAccessor.getId(fragment); + } + + @Nullable + @Override + public String getTag(Fragment fragment) { + return mFragmentAccessor.getTag(fragment); + } + + @Nullable + @Override + public View getView(Fragment fragment) { + return mFragmentAccessor.getView(fragment); + } + + @Nullable + @Override + public FragmentManager getChildFragmentManager(Fragment fragment) { + return mFragmentAccessor.getChildFragmentManager(fragment); + } + } + + private static class FragmentActivityAccessorFramework + implements FragmentActivityAccessor { + @Nullable + @Override + public FragmentManager getFragmentManager(Activity activity) { + return activity.getFragmentManager(); + } + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatSupportLib.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatSupportLib.java new file mode 100644 index 000000000..f7196bfcf --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatSupportLib.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.app.Dialog; +import android.content.res.Resources; +import android.view.View; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import javax.annotation.Nullable; + +final class FragmentCompatSupportLib + extends FragmentCompat { + private static final FragmentAccessorSupportLib sFragmentAccessor = + new FragmentAccessorSupportLib(); + private static final DialogFragmentAccessorSupportLib sDialogFragmentAccessor = + new DialogFragmentAccessorSupportLib(); + private static final FragmentManagerAccessorViaReflection + sFragmentManagerAccessor = new FragmentManagerAccessorViaReflection<>(); + private static final FragmentActivityAccessorSupportLib sFragmentActivityAccessor = + new FragmentActivityAccessorSupportLib(); + + @Override + public Class getFragmentClass() { + return Fragment.class; + } + + @Override + public Class getDialogFragmentClass() { + return DialogFragment.class; + } + + @Override + public Class getFragmentActivityClass() { + return FragmentActivity.class; + } + + @Override + public FragmentAccessorSupportLib forFragment() { + return sFragmentAccessor; + } + + @Override + public DialogFragmentAccessorSupportLib forDialogFragment() { + return sDialogFragmentAccessor; + } + + @Override + public FragmentManagerAccessor forFragmentManager() { + return sFragmentManagerAccessor; + } + + @Override + public FragmentActivityAccessorSupportLib forFragmentActivity() { + return sFragmentActivityAccessor; + } + + private static class FragmentAccessorSupportLib + implements FragmentAccessor { + @Nullable + @Override + public FragmentManager getFragmentManager(Fragment fragment) { + return fragment.getFragmentManager(); + } + + @Override + public Resources getResources(Fragment fragment) { + return fragment.getResources(); + } + + @Override + public int getId(Fragment fragment) { + return fragment.getId(); + } + + @Nullable + @Override + public String getTag(Fragment fragment) { + return fragment.getTag(); + } + + @Nullable + @Override + public View getView(Fragment fragment) { + return fragment.getView(); + } + + @Nullable + @Override + public FragmentManager getChildFragmentManager(Fragment fragment) { + return fragment.getChildFragmentManager(); + } + } + + private static class DialogFragmentAccessorSupportLib extends FragmentAccessorSupportLib + implements DialogFragmentAccessor { + @Override + public Dialog getDialog(DialogFragment dialogFragment) { + return dialogFragment.getDialog(); + } + } + + private static class FragmentActivityAccessorSupportLib + implements FragmentActivityAccessor { + @Nullable + @Override + public FragmentManager getFragmentManager(FragmentActivity activity) { + return activity.getSupportFragmentManager(); + } + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatUtil.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatUtil.java new file mode 100644 index 000000000..7049a5c06 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentCompatUtil.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.app.Activity; +import android.view.View; +import java.util.List; +import javax.annotation.Nullable; + +public final class FragmentCompatUtil { + private FragmentCompatUtil() {} + + public static boolean isDialogFragment(Object fragment) { + FragmentCompat supportLib = FragmentCompat.getSupportLibInstance(); + if (supportLib != null && supportLib.getDialogFragmentClass().isInstance(fragment)) { + return true; + } + + FragmentCompat framework = FragmentCompat.getFrameworkInstance(); + if (framework != null && framework.getDialogFragmentClass().isInstance(fragment)) { + return true; + } + + return false; + } + + @Nullable + public static Object findFragmentForView(View view) { + Activity activity = ViewUtil.tryGetActivity(view); + if (activity == null) { + return null; + } + + return findFragmentForViewInActivity(activity, view); + } + + @Nullable + private static Object findFragmentForViewInActivity(Activity activity, View view) { + FragmentCompat supportLib = FragmentCompat.getSupportLibInstance(); + + // Try the support library version if it is present and the activity is FragmentActivity. + if (supportLib != null && supportLib.getFragmentActivityClass().isInstance(activity)) { + Object fragment = findFragmentForViewInActivity(supportLib, activity, view); + if (fragment != null) { + return fragment; + } + } + + // Try the actual Android runtime version if we are on a sufficiently high API level for it to + // exist. Note that technically we can have both the support library and the framework + // version in the same object instance due to FragmentActivity extending Activity (which has + // fragment support in the system). + FragmentCompat framework = FragmentCompat.getFrameworkInstance(); + if (framework != null) { + Object fragment = findFragmentForViewInActivity(framework, activity, view); + if (fragment != null) { + return fragment; + } + } + + return null; + } + + private static Object findFragmentForViewInActivity( + FragmentCompat compat, Activity activity, View view) { + Object fragmentManager = compat.forFragmentActivity().getFragmentManager(activity); + if (fragmentManager != null) { + return findFragmentForViewInFragmentManager(compat, fragmentManager, view); + } else { + return null; + } + } + + @Nullable + private static Object findFragmentForViewInFragmentManager( + FragmentCompat compat, Object fragmentManager, View view) { + List fragments = compat.forFragmentManager().getAddedFragments(fragmentManager); + + if (fragments != null) { + for (int i = 0, N = fragments.size(); i < N; ++i) { + Object fragment = fragments.get(i); + Object result = findFragmentForViewInFragment(compat, fragment, view); + if (result != null) { + return result; + } + } + } + + return null; + } + + @Nullable + private static Object findFragmentForViewInFragment( + FragmentCompat compat, Object fragment, View view) { + FragmentAccessor accessor = compat.forFragment(); + + if (accessor.getView(fragment) == view) { + return fragment; + } + + Object childFragmentManager = accessor.getChildFragmentManager(fragment); + if (childFragmentManager != null) { + return findFragmentForViewInFragmentManager(compat, childFragmentManager, view); + } + + return null; + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentManagerAccessor.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentManagerAccessor.java new file mode 100644 index 000000000..60c0cc85e --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/FragmentManagerAccessor.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import java.util.List; +import javax.annotation.Nullable; + +public interface FragmentManagerAccessor { + @Nullable + List getAddedFragments(FRAGMENT_MANAGER fragmentManager); +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ReflectionUtil.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ReflectionUtil.java new file mode 100644 index 000000000..076f510cc --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ReflectionUtil.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.util.Log; +import java.lang.reflect.Field; +import javax.annotation.Nullable; + +public final class ReflectionUtil { + private ReflectionUtil() {} + + @Nullable + public static Class tryGetClassForName(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + return null; + } + } + + @Nullable + public static Field tryGetDeclaredField(Class theClass, String fieldName) { + try { + return theClass.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + Log.e( + ReflectionUtil.class.getCanonicalName(), + String.format("Could not retrieve %s field from %s", fieldName, theClass), + e); + + return null; + } + } + + @Nullable + public static Object getFieldValue(Field field, Object target) { + try { + return field.get(target); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ResourcesUtil.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ResourcesUtil.java new file mode 100644 index 000000000..2bfaaef33 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ResourcesUtil.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.content.res.Resources; +import android.util.Log; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** Copied from Stetho. */ +public class ResourcesUtil { + private ResourcesUtil() {} + + @Nonnull + public static String getIdStringQuietly(Object idContext, @Nullable Resources r, int resourceId) { + try { + return getIdString(r, resourceId); + } catch (Resources.NotFoundException e) { + String idString = getFallbackIdString(resourceId); + Log.w( + ResourcesUtil.class.getCanonicalName(), + "Unknown identifier encountered on " + idContext + ": " + idString); + return idString; + } + } + + public static String getIdString(@Nullable Resources r, int resourceId) + throws Resources.NotFoundException { + if (r == null) { + return getFallbackIdString(resourceId); + } + + String prefix; + String prefixSeparator; + switch (getResourcePackageId(resourceId)) { + case 0x7f: + prefix = ""; + prefixSeparator = ""; + break; + default: + prefix = r.getResourcePackageName(resourceId); + prefixSeparator = ":"; + break; + } + + String typeName = r.getResourceTypeName(resourceId); + String entryName = r.getResourceEntryName(resourceId); + + StringBuilder sb = + new StringBuilder( + 1 + + prefix.length() + + prefixSeparator.length() + + typeName.length() + + 1 + + entryName.length()); + sb.append("@"); + sb.append(prefix); + sb.append(prefixSeparator); + sb.append(typeName); + sb.append("/"); + sb.append(entryName); + + return sb.toString(); + } + + private static String getFallbackIdString(int resourceId) { + return "#" + Integer.toHexString(resourceId); + } + + private static int getResourcePackageId(int id) { + return (id >>> 24) & 0xff; + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ViewUtil.java b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ViewUtil.java new file mode 100644 index 000000000..f40e19b77 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/stethocopies/ViewUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies; + +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; +import android.view.View; +import android.view.ViewParent; +import javax.annotation.Nullable; + +final class ViewUtil { + private ViewUtil() {} + + @Nullable + static Activity tryGetActivity(View view) { + if (view == null) { + return null; + } + + Context context = view.getContext(); + + Activity activityFromContext = tryGetActivity(context); + if (activityFromContext != null) { + return activityFromContext; + } + + ViewParent parent = view.getParent(); + if (parent instanceof View) { + View parentView = (View) parent; + return tryGetActivity(parentView); + } + + return null; + } + + @Nullable + private static Activity tryGetActivity(Context context) { + while (context != null) { + if (context instanceof Activity) { + return (Activity) context; + } else if (context instanceof ContextWrapper) { + context = ((ContextWrapper) context).getBaseContext(); + } else { + return null; + } + } + + return null; + } +} diff --git a/build.gradle b/build.gradle index f3c92131f..2ff77d82f 100644 --- a/build.gradle +++ b/build.gradle @@ -86,7 +86,6 @@ ext.deps = [ junit : 'junit:junit:4.12', hamcrest : 'org.hamcrest:hamcrest-library:1.3', mockito : 'org.mockito:mockito-core:1.9.5', - stetho : 'com.facebook.stetho:stetho:1.5.1', okhttp3 : 'com.squareup.okhttp3:okhttp:3.14.1', leakcanary : 'com.squareup.leakcanary:leakcanary-android:1.6.3', testCore : 'androidx.test:core:1.1.0',