mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/AlertFragment.java (#50630)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50630 Changelog: [Internal] - Convert AlertFragment to Kotlin Reviewed By: mlord93 Differential Revision: D72729380 fbshipit-source-id: f7d8ee4cd5938c453b865aea60b6f0533bd8fe0e
This commit is contained in:
committed by
Facebook GitHub Bot
parent
7101adf611
commit
c51c4b1922
@@ -2890,15 +2890,19 @@ public abstract interface class com/facebook/react/modules/debug/interfaces/Deve
|
||||
public abstract fun setStartSamplingProfilerOnInit (Z)V
|
||||
}
|
||||
|
||||
public class com/facebook/react/modules/dialog/AlertFragment : androidx/fragment/app/DialogFragment, android/content/DialogInterface$OnClickListener {
|
||||
public final class com/facebook/react/modules/dialog/AlertFragment : androidx/fragment/app/DialogFragment, android/content/DialogInterface$OnClickListener {
|
||||
public static final field Companion Lcom/facebook/react/modules/dialog/AlertFragment$Companion;
|
||||
public fun <init> ()V
|
||||
public fun <init> (Lcom/facebook/react/modules/dialog/DialogModule$AlertFragmentListener;Landroid/os/Bundle;)V
|
||||
public static fun createDialog (Landroid/content/Context;Landroid/os/Bundle;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/Dialog;
|
||||
public static final fun createDialog (Landroid/content/Context;Landroid/os/Bundle;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/Dialog;
|
||||
public fun onClick (Landroid/content/DialogInterface;I)V
|
||||
public fun onCreateDialog (Landroid/os/Bundle;)Landroid/app/Dialog;
|
||||
public fun onDismiss (Landroid/content/DialogInterface;)V
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/dialog/AlertFragment$Companion {
|
||||
public final fun createDialog (Landroid/content/Context;Landroid/os/Bundle;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/Dialog;
|
||||
}
|
||||
|
||||
public final class com/facebook/react/modules/dialog/DialogModule : com/facebook/fbreact/specs/NativeDialogManagerAndroidSpec, com/facebook/react/bridge/LifecycleEventListener {
|
||||
public static final field Companion Lcom/facebook/react/modules/dialog/DialogModule$Companion;
|
||||
public static final field NAME Ljava/lang/String;
|
||||
|
||||
-204
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and 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.react.modules.dialog;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.view.AccessibilityDelegateCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.R;
|
||||
|
||||
/** A fragment used to display the dialog. */
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class AlertFragment extends DialogFragment implements DialogInterface.OnClickListener {
|
||||
|
||||
/* package */ static final String ARG_TITLE = "title";
|
||||
/* package */ static final String ARG_MESSAGE = "message";
|
||||
/* package */ static final String ARG_BUTTON_POSITIVE = "button_positive";
|
||||
/* package */ static final String ARG_BUTTON_NEGATIVE = "button_negative";
|
||||
/* package */ static final String ARG_BUTTON_NEUTRAL = "button_neutral";
|
||||
/* package */ static final String ARG_ITEMS = "items";
|
||||
|
||||
private final @Nullable DialogModule.AlertFragmentListener mListener;
|
||||
|
||||
public AlertFragment() {
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@SuppressLint("ValidFragment")
|
||||
public AlertFragment(@Nullable DialogModule.AlertFragmentListener listener, Bundle arguments) {
|
||||
mListener = listener;
|
||||
setArguments(arguments);
|
||||
}
|
||||
|
||||
public static Dialog createDialog(
|
||||
Context activityContext, Bundle arguments, DialogInterface.OnClickListener fragment) {
|
||||
if (isAppCompatTheme(activityContext)) {
|
||||
return createAppCompatDialog(activityContext, arguments, fragment);
|
||||
} else {
|
||||
return createAppDialog(activityContext, arguments, fragment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current activity is a descendant of an AppCompat theme. This check is required to
|
||||
* safely display an AppCompat dialog. If the current activity is not a descendant of an AppCompat
|
||||
* theme and we attempt to render an AppCompat dialog, this will cause a crash.
|
||||
*
|
||||
* @returns true if the current activity is a descendant of an AppCompat theme.
|
||||
*/
|
||||
private static boolean isAppCompatTheme(Context activityContext) {
|
||||
TypedArray attributes =
|
||||
activityContext.obtainStyledAttributes(androidx.appcompat.R.styleable.AppCompatTheme);
|
||||
boolean isAppCompat =
|
||||
attributes.hasValue(androidx.appcompat.R.styleable.AppCompatTheme_windowActionBar);
|
||||
attributes.recycle();
|
||||
return isAppCompat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom dialog title View that has the role of "Heading" and focusable for
|
||||
* accessibility purposes.
|
||||
*
|
||||
* @returns accessible TextView title
|
||||
*/
|
||||
private static View getAccessibleTitle(Context activityContext, String titleText) {
|
||||
LayoutInflater inflater = LayoutInflater.from(activityContext);
|
||||
|
||||
// This layout matches the sizing and styling of AlertDialog's title_template (minus the icon)
|
||||
// since the whole thing gets tossed out when setting a custom title
|
||||
View titleContainer = inflater.inflate(R.layout.alert_title_layout, null);
|
||||
|
||||
TextView accessibleTitle =
|
||||
Assertions.assertNotNull(titleContainer.findViewById(R.id.alert_title));
|
||||
accessibleTitle.setText(titleText);
|
||||
accessibleTitle.setFocusable(true);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
accessibleTitle.setAccessibilityHeading(true);
|
||||
} else {
|
||||
ViewCompat.setAccessibilityDelegate(
|
||||
accessibleTitle,
|
||||
new AccessibilityDelegateCompat() {
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(
|
||||
View view, AccessibilityNodeInfoCompat info) {
|
||||
super.onInitializeAccessibilityNodeInfo(accessibleTitle, info);
|
||||
info.setHeading(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return titleContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dialog compatible only with AppCompat activities. This function should be kept in
|
||||
* sync with {@link createAppDialog}.
|
||||
*/
|
||||
private static Dialog createAppCompatDialog(
|
||||
Context activityContext, Bundle arguments, DialogInterface.OnClickListener fragment) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activityContext);
|
||||
|
||||
if (arguments.containsKey(ARG_TITLE)) {
|
||||
String title = Assertions.assertNotNull(arguments.getString(ARG_TITLE));
|
||||
View accessibleTitle = getAccessibleTitle(activityContext, title);
|
||||
builder.setCustomTitle(accessibleTitle);
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
|
||||
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment);
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEGATIVE)) {
|
||||
builder.setNegativeButton(arguments.getString(ARG_BUTTON_NEGATIVE), fragment);
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEUTRAL)) {
|
||||
builder.setNeutralButton(arguments.getString(ARG_BUTTON_NEUTRAL), fragment);
|
||||
}
|
||||
// if both message and items are set, Android will only show the message
|
||||
// and ignore the items argument entirely
|
||||
if (arguments.containsKey(ARG_MESSAGE)) {
|
||||
builder.setMessage(arguments.getString(ARG_MESSAGE));
|
||||
}
|
||||
if (arguments.containsKey(ARG_ITEMS)) {
|
||||
builder.setItems(arguments.getCharSequenceArray(ARG_ITEMS), fragment);
|
||||
}
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dialog compatible with non-AppCompat activities. This function should be kept in sync
|
||||
* with {@link createAppCompatDialog}.
|
||||
*
|
||||
* @deprecated non-AppCompat dialogs are deprecated and will be removed in a future version.
|
||||
*/
|
||||
@Deprecated(since = "0.75.0", forRemoval = true)
|
||||
private static Dialog createAppDialog(
|
||||
Context activityContext, Bundle arguments, DialogInterface.OnClickListener fragment) {
|
||||
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(activityContext);
|
||||
|
||||
if (arguments.containsKey(ARG_TITLE)) {
|
||||
String title = Assertions.assertNotNull(arguments.getString(ARG_TITLE));
|
||||
View accessibleTitle = getAccessibleTitle(activityContext, title);
|
||||
builder.setCustomTitle(accessibleTitle);
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
|
||||
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment);
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEGATIVE)) {
|
||||
builder.setNegativeButton(arguments.getString(ARG_BUTTON_NEGATIVE), fragment);
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEUTRAL)) {
|
||||
builder.setNeutralButton(arguments.getString(ARG_BUTTON_NEUTRAL), fragment);
|
||||
}
|
||||
// if both message and items are set, Android will only show the message
|
||||
// and ignore the items argument entirely
|
||||
if (arguments.containsKey(ARG_MESSAGE)) {
|
||||
builder.setMessage(arguments.getString(ARG_MESSAGE));
|
||||
}
|
||||
if (arguments.containsKey(ARG_ITEMS)) {
|
||||
builder.setItems(arguments.getCharSequenceArray(ARG_ITEMS), fragment);
|
||||
}
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return createDialog(requireActivity(), requireArguments(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (mListener != null) {
|
||||
mListener.onClick(dialog, which);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
if (mListener != null) {
|
||||
mListener.onDismiss(dialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and 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.react.modules.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.AccessibilityDelegateCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.facebook.infer.annotation.Assertions
|
||||
import com.facebook.react.R
|
||||
import com.facebook.react.modules.dialog.DialogModule.AlertFragmentListener
|
||||
|
||||
/** A fragment used to display the dialog. */
|
||||
@SuppressLint("fragment_subclass_nonempty_constructor")
|
||||
public class AlertFragment : DialogFragment, DialogInterface.OnClickListener {
|
||||
|
||||
private val listener: AlertFragmentListener?
|
||||
|
||||
public constructor() {
|
||||
listener = null
|
||||
}
|
||||
|
||||
@SuppressLint("ValidFragment")
|
||||
internal constructor(listener: AlertFragmentListener?, arguments: Bundle?) {
|
||||
this.listener = listener
|
||||
setArguments(arguments)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
|
||||
createDialog(requireActivity(), requireArguments(), this)
|
||||
|
||||
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||
listener?.onClick(dialog, which)
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
listener?.onDismiss(dialog)
|
||||
}
|
||||
|
||||
public companion object {
|
||||
internal const val ARG_TITLE: String = "title"
|
||||
internal const val ARG_MESSAGE: String = "message"
|
||||
internal const val ARG_BUTTON_POSITIVE: String = "button_positive"
|
||||
internal const val ARG_BUTTON_NEGATIVE: String = "button_negative"
|
||||
internal const val ARG_BUTTON_NEUTRAL: String = "button_neutral"
|
||||
internal const val ARG_ITEMS: String = "items"
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("DEPRECATION")
|
||||
public fun createDialog(
|
||||
activityContext: Context,
|
||||
arguments: Bundle,
|
||||
fragment: DialogInterface.OnClickListener
|
||||
): Dialog =
|
||||
if (isAppCompatTheme(activityContext)) {
|
||||
createAppCompatDialog(activityContext, arguments, fragment)
|
||||
} else {
|
||||
createAppDialog(activityContext, arguments, fragment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current activity is a descendant of an AppCompat theme. This check is required
|
||||
* to safely display an AppCompat dialog. If the current activity is not a descendant of an
|
||||
* AppCompat theme and we attempt to render an AppCompat dialog, this will cause a crash.
|
||||
*
|
||||
* @returns true if the current activity is a descendant of an AppCompat theme.
|
||||
*/
|
||||
private fun isAppCompatTheme(activityContext: Context): Boolean {
|
||||
val attributes =
|
||||
activityContext.obtainStyledAttributes(androidx.appcompat.R.styleable.AppCompatTheme)
|
||||
val isAppCompat =
|
||||
attributes.hasValue(androidx.appcompat.R.styleable.AppCompatTheme_windowActionBar)
|
||||
attributes.recycle()
|
||||
return isAppCompat
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom dialog title View that has the role of "Heading" and focusable for
|
||||
* accessibility purposes.
|
||||
*
|
||||
* @returns accessible TextView title
|
||||
*/
|
||||
private fun getAccessibleTitle(activityContext: Context, titleText: String): View {
|
||||
val inflater = LayoutInflater.from(activityContext)
|
||||
|
||||
// This layout matches the sizing and styling of AlertDialog's title_template (minus the icon)
|
||||
// since the whole thing gets tossed out when setting a custom title
|
||||
val titleContainer: View = inflater.inflate(R.layout.alert_title_layout, null)
|
||||
|
||||
val accessibleTitle: TextView =
|
||||
Assertions.assertNotNull<TextView>(
|
||||
titleContainer.findViewById<TextView>(R.id.alert_title))
|
||||
accessibleTitle.text = titleText
|
||||
accessibleTitle.isFocusable = true
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
accessibleTitle.isAccessibilityHeading = true
|
||||
} else {
|
||||
ViewCompat.setAccessibilityDelegate(
|
||||
accessibleTitle,
|
||||
object : AccessibilityDelegateCompat() {
|
||||
override fun onInitializeAccessibilityNodeInfo(
|
||||
view: View,
|
||||
info: AccessibilityNodeInfoCompat
|
||||
) {
|
||||
super.onInitializeAccessibilityNodeInfo(accessibleTitle, info)
|
||||
info.isHeading = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return titleContainer
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dialog compatible only with AppCompat activities. This function should be kept in
|
||||
* sync with [createAppDialog].
|
||||
*/
|
||||
private fun createAppCompatDialog(
|
||||
activityContext: Context,
|
||||
arguments: Bundle,
|
||||
fragment: DialogInterface.OnClickListener
|
||||
): Dialog {
|
||||
val builder = AlertDialog.Builder(activityContext)
|
||||
|
||||
if (arguments.containsKey(ARG_TITLE)) {
|
||||
val title = Assertions.assertNotNull<String>(arguments.getString(ARG_TITLE))
|
||||
val accessibleTitle = getAccessibleTitle(activityContext, title)
|
||||
builder.setCustomTitle(accessibleTitle)
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
|
||||
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment)
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEGATIVE)) {
|
||||
builder.setNegativeButton(arguments.getString(ARG_BUTTON_NEGATIVE), fragment)
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEUTRAL)) {
|
||||
builder.setNeutralButton(arguments.getString(ARG_BUTTON_NEUTRAL), fragment)
|
||||
}
|
||||
// if both message and items are set, Android will only show the message
|
||||
// and ignore the items argument entirely
|
||||
if (arguments.containsKey(ARG_MESSAGE)) {
|
||||
builder.setMessage(arguments.getString(ARG_MESSAGE))
|
||||
}
|
||||
if (arguments.containsKey(ARG_ITEMS)) {
|
||||
builder.setItems(arguments.getCharSequenceArray(ARG_ITEMS), fragment)
|
||||
}
|
||||
|
||||
return builder.create()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dialog compatible with non-AppCompat activities. This function should be kept in
|
||||
* sync with [createAppCompatDialog].
|
||||
*/
|
||||
@Deprecated(
|
||||
"non-AppCompat dialogs are deprecated and will be removed in a future version.",
|
||||
replaceWith = ReplaceWith("createAppCompatDialog(activityContext, arguments, fragment)"),
|
||||
)
|
||||
private fun createAppDialog(
|
||||
activityContext: Context,
|
||||
arguments: Bundle,
|
||||
fragment: DialogInterface.OnClickListener
|
||||
): Dialog {
|
||||
val builder = android.app.AlertDialog.Builder(activityContext)
|
||||
|
||||
if (arguments.containsKey(ARG_TITLE)) {
|
||||
val title = Assertions.assertNotNull<String>(arguments.getString(ARG_TITLE))
|
||||
val accessibleTitle = getAccessibleTitle(activityContext, title)
|
||||
builder.setCustomTitle(accessibleTitle)
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
|
||||
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment)
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEGATIVE)) {
|
||||
builder.setNegativeButton(arguments.getString(ARG_BUTTON_NEGATIVE), fragment)
|
||||
}
|
||||
if (arguments.containsKey(ARG_BUTTON_NEUTRAL)) {
|
||||
builder.setNeutralButton(arguments.getString(ARG_BUTTON_NEUTRAL), fragment)
|
||||
}
|
||||
// if both message and items are set, Android will only show the message
|
||||
// and ignore the items argument entirely
|
||||
if (arguments.containsKey(ARG_MESSAGE)) {
|
||||
builder.setMessage(arguments.getString(ARG_MESSAGE))
|
||||
}
|
||||
if (arguments.containsKey(ARG_ITEMS)) {
|
||||
builder.setItems(arguments.getCharSequenceArray(ARG_ITEMS), fragment)
|
||||
}
|
||||
|
||||
return builder.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user