Skip to content

Commit

Permalink
refactor: replace getInstanceFromClassName
Browse files Browse the repository at this point in the history
Reflection should be avoided in production code
  • Loading branch information
BrayanDSO committed Dec 5, 2024
1 parent 569f6f5 commit d0f2190
Show file tree
Hide file tree
Showing 8 changed files with 21 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import com.ichi2.anki.android.input.ShortcutGroupProvider
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialogFactory
import com.ichi2.utils.ExtendedFragmentFactory
import com.ichi2.utils.getInstanceFromClassName
import com.ichi2.utils.FragmentFactoryUtils
import timber.log.Timber
import kotlin.reflect.KClass
import kotlin.reflect.jvm.jvmName
Expand Down Expand Up @@ -72,7 +72,7 @@ open class SingleFragmentActivity : AnkiActivity(), CustomStudyDialog.CustomStud

Timber.d("Creating fragment %s", fragmentClassName)

val fragment = getInstanceFromClassName<Fragment>(fragmentClassName).apply {
val fragment = FragmentFactoryUtils.instantiate<Fragment>(this, fragmentClassName).apply {
arguments = intent.getBundleExtra(FRAGMENT_ARGS_EXTRA)
}
supportFragmentManager.commit {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import com.ichi2.anki.snackbar.SnackbarBuilder
import com.ichi2.compat.CompatHelper.Companion.getSerializableCompat
import com.ichi2.compat.CompatHelper.Companion.getSerializableExtraCompat
import com.ichi2.themes.setTransparentStatusBar
import com.ichi2.utils.getInstanceFromClassName
import com.ichi2.utils.FragmentFactoryUtils
import timber.log.Timber
import java.io.Serializable
import kotlin.reflect.KClass
Expand Down Expand Up @@ -87,7 +87,7 @@ class MultimediaActivity : AnkiActivity(), BaseSnackbarBuilderProvider {
"'$MULTIMEDIA_FRAGMENT_NAME_EXTRA' extra should be provided"
}

val fragment = getInstanceFromClassName<Fragment>(fragmentClassName).apply {
val fragment = FragmentFactoryUtils.instantiate<Fragment>(this, fragmentClassName).apply {
arguments = bundleOf(
MULTIMEDIA_ARGS_EXTRA to intent.multimediaArgsExtra,
EXTRA_MEDIA_OPTIONS to intent.mediaOptionsExtra
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import com.google.android.material.appbar.MaterialToolbar
import com.ichi2.anki.R
import com.ichi2.anki.SingleFragmentActivity
import com.ichi2.anki.utils.isWindowCompact
import com.ichi2.utils.getInstanceFromClassName
import com.ichi2.utils.FragmentFactoryUtils
import timber.log.Timber
import kotlin.reflect.KClass
import kotlin.reflect.jvm.jvmName
Expand Down Expand Up @@ -140,11 +140,7 @@ class PreferencesFragment :
val initialFragment = if (fragmentClassName == null) {
if (resources.isWindowCompact()) HeaderFragment() else GeneralSettingsFragment()
} else {
try {
getInstanceFromClassName<Fragment>(fragmentClassName)
} catch (e: Exception) {
throw RuntimeException("Failed to load $fragmentClassName", e)
}
FragmentFactoryUtils.instantiate<Fragment>(requireActivity(), fragmentClassName)
}
childFragmentManager.commit {
// In big screens, show the headers fragment at the lateral navigation container
Expand Down
11 changes: 9 additions & 2 deletions AnkiDroid/src/main/java/com/ichi2/utils/FragmentFactoryUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ object FragmentFactoryUtils {
/**
* A convenience util method that instantiate a fragment using the passed activity [FragmentFactory]
*/
inline fun <reified F : Fragment?> instantiate(activity: FragmentActivity, cls: Class<F>): F {
inline fun <reified F : Fragment> instantiate(activity: FragmentActivity, className: String): F {
val factory = activity.supportFragmentManager.fragmentFactory
return factory.instantiate(activity.classLoader, cls.name) as F
return factory.instantiate(activity.classLoader, className) as F
}

/**
* A convenience util method that instantiate a fragment using the passed activity [FragmentFactory]
*/
inline fun <reified F : Fragment> instantiate(activity: FragmentActivity, cls: Class<F>): F {
return instantiate(activity, cls.name)
}
}
20 changes: 0 additions & 20 deletions AnkiDroid/src/main/java/com/ichi2/utils/ReflectionUtils.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import androidx.annotation.XmlRes
import androidx.fragment.app.Fragment
import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.R
import com.ichi2.utils.getInstanceFromClassName
import com.ichi2.testutils.getInstanceFromClassName
import org.xmlpull.v1.XmlPullParser

object PreferenceTestUtils {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import com.ichi2.anki.preferences.HeaderFragment.Companion.getHeaderKeyForFragme
import com.ichi2.anki.preferences.PreferenceTestUtils.getAttrFromXml
import com.ichi2.libanki.exception.ConfirmModSchemaException
import com.ichi2.preferences.HeaderPreference
import com.ichi2.testutils.getInstanceFromClassName
import com.ichi2.testutils.getJavaMethodAsAccessible
import com.ichi2.utils.getInstanceFromClassName
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ fun getJavaMethodAsAccessible(clazz: Class<*>, methodName: String, vararg parame
isAccessible = true
}
}

inline fun <reified T> getInstanceFromClassName(javaClassName: String): T {
return Class.forName(javaClassName).getDeclaredConstructor().newInstance() as T
}

0 comments on commit d0f2190

Please sign in to comment.