Android DialogFragment против Диалога


244

Google рекомендует использовать DialogFragmentвместо простого Dialogс помощью Fragments API, но абсурдно использовать изолированное DialogFragmentдля простого окна подтверждения «да-нет». Какова лучшая практика в этом случае?


5
Короче говоря, между прочим, просто Dialogили AlertDialog.Builder::create()::show()создаст диалоговое окно, которое исчезает при повороте экрана.
user1032613

Ответы:


83

Да, используйте, DialogFragmentи в onCreateDialogлюбом случае вы можете просто использовать AlertDialog Builder для создания простого AlertDialogс кнопками подтверждения Да / Нет. Не очень много кода на всех.

Что касается обработки событий в вашем фрагменте, то существуют разные способы сделать это, но я просто определяю сообщение Handlerв my Fragment, передаю его в DialogFragmentконструктор via, а затем передаю сообщения обратно в обработчик моего фрагмента в соответствии с различными событиями click. Опять разные способы сделать это, но у меня работает следующее.

В диалоговом окне удерживайте сообщение и создайте его экземпляр в конструкторе:

private Message okMessage;
...
okMessage = handler.obtainMessage(MY_MSG_WHAT, MY_MSG_OK);

Реализуйте onClickListenerв своем диалоговом окне и затем вызовите обработчик соответствующим образом:

public void onClick(.....
    if (which == DialogInterface.BUTTON_POSITIVE) {
        final Message toSend = Message.obtain(okMessage);
        toSend.sendToTarget();
    }
 }

редактировать

И, как Messageможно продать, вы можете сохранить его onSaveInstanceStateи восстановить его

outState.putParcelable("okMessage", okMessage);

Затем в onCreate

if (savedInstanceState != null) {
    okMessage = savedInstanceState.getParcelable("okMessage");
}

4
Проблема не в okMessage - проблема в okMessage, targetкоторая будет нулевой, если вы загрузите ее из Bundle. Если цель сообщения равна нулю и вы используете sendToTarget, вы получите исключение NullPointerException - не потому, что сообщение равно нулю, а потому, что его целью является.
hrnt

2
В чем преимущества использования DialogFragment вместо Dialog?
Рафаэль Петегроссо

80
Преимущество использования DialogFragment состоит в том, что весь жизненный цикл диалога будет обработан для вас. Вы никогда не получите ошибку «диалог утек ...» снова. Перейдите в DialogFragment и забудьте о диалогах.
Сниколас

6
Я думаю, что setArguments () и getArguments () должны использоваться вместо передачи okMessage через конструктор.
pjv

1
Ну, я довольно легко использую Builder и управляю деятельностью с этим android: configChanges = "locale | keyboardHidden | ориентация | screenSize", и я не вижу никаких проблем в приложениях ...
Renetik

67

Вы можете создавать общие подклассы DialogFragment, такие как YesNoDialog и OkDialog, и передавать заголовок и сообщение, если вы часто используете диалоги в своем приложении.

public class YesNoDialog extends DialogFragment
{
    public static final String ARG_TITLE = "YesNoDialog.Title";
    public static final String ARG_MESSAGE = "YesNoDialog.Message";

    public YesNoDialog()
    {

    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        Bundle args = getArguments();
        String title = args.getString(ARG_TITLE);
        String message = args.getString(ARG_MESSAGE);

        return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
                }
            })
            .create();
    }
}

Затем позвоните, используя следующее:

    DialogFragment dialog = new YesNoDialog();
    Bundle args = new Bundle();
    args.putString(YesNoDialog.ARG_TITLE, title);
    args.putString(YesNoDialog.ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(this, YES_NO_CALL);
    dialog.show(getFragmentManager(), "tag");

И обработать результат в onActivityResult.


Да, DialogFragment обрабатывает все события жизненного цикла для вас.
ашишдух

1
Я думаю, что это не так, потому что после поворота старый диалог все еще существует, и он сохраняет присваиватель для старого несуществующего фрагмента (dialog.setTargetFragment (this, YES_NO_CALL);), поэтому после поворота getTargetFragment (). OnActivityResult не работает
Malachiasz

7
что есть YES_NO_CALL, getFragmentManager()а onActivityResult?
msysmilu

2
YES_NO_CALLэто пользовательский int, который является кодом запроса. getFragmentManager()получает диспетчер фрагментов для действия и onActivityResult()является методом обратного вызова жизненного цикла фрагмента.
ашишдух

3
Замените getFragmentManager () на getSupportFragmentManager ();
Авинаш Верма

33

Используйте DialogFragment поверх AlertDialog:


  • С момента внедрения API уровня 13 :

    ShowDialog метод от деятельности является устаревшим . Вызывать диалог в другом месте кода не рекомендуется, так как вам придется управлять диалогом самостоятельно (например, изменение ориентации).

  • Разница DialogFragment - AlertDialog

    Они так сильно отличаются? Из ссылки Android относительно DialogFragment :

    DialogFragment - это фрагмент, который отображает диалоговое окно, плавающее поверх окна его активности. Этот фрагмент содержит объект Dialog, который отображается в зависимости от состояния фрагмента. Контроль диалога (принятия решения , когда нужно показать, скрыть, отклонить его) должно быть сделано через API здесь , а не прямые вызовы на диалоге.

  • Другие заметки

    • Фрагменты - естественная эволюция в структуре Android из-за разнообразия устройств с различными размерами экрана.
    • DialogFra фрагменты и фрагменты доступны в библиотеке поддержки, что делает класс пригодным для использования во всех текущих используемых версиях Android.

28

Я бы порекомендовал использовать DialogFragment.

Конечно, создать диалог «Да / Нет» с ним довольно сложно, учитывая, что это должно быть довольно простой задачей, но создание аналогичного диалогового окна с Dialogудивительно сложным также.

(Жизненный цикл активности усложняет задачу - вы должны разрешить Activityуправление жизненным циклом диалогового окна - и нет способа передать пользовательские параметры, например пользовательское сообщение, Activity.showDialogесли используются уровни API ниже 8)

Приятно то, что вы, как правило, DialogFragmentдовольно легко можете создать свою собственную абстракцию на вершине .


Как вы будете обрабатывать обратные вызовы в диалоговом окне оповещения (да, нет)?
Алексей Захаров

Самым простым способом было бы реализовать метод Activity, который принимает Stringпараметр. Например, когда пользователь нажимает «Да», диалоговое окно вызывает метод Activity с параметром «accept». Эти параметры указываются при отображении диалогового окна, например AskDialog.ask («Согласны ли вы с этими условиями?», «Согласен», «не согласен»);
hrnt

5
Но мне нужен обратный вызов внутри фрагмента, а не активности. Я могу использовать setTargetFragment и привести его к интерфейсу. Но это ад.
Алексей Захаров

Вы также можете получить целевой фрагмент, установив тег для цели и используя FragmentManager's findFragmentByTag. Но да, для этого требуется немало кода.
hrnt

@ AlexeyZakharov Я знаю, что это примерно на 5 лет позже, но вы могли бы сдать Fragment this и получить Activity extendsсвой Interface. Осторожно с многопоточностью, вы можете отбрасывать интерфейсные вызовы, когда они не нужны, если ваш параллелизм не проверяется. Не уверен, что это делает с памятью и спагетти с круговой зависимостью, кто-то еще хотел бы присоединиться? Другой вариант - Message/ Handlerно у вас все еще могут быть проблемы с параллелизмом.
трикология

8

Общий AlertDialogFragment с шаблоном Builder

В своем проекте я AlertDialog.Builderуже много использовал, прежде чем обнаружил, что это проблематично. Тем не менее, я не хотел менять столько кода в моем приложении. Кроме того, я на самом деле являюсь поклонником передачи в OnClickListenersкачестве анонимных классов там, где они необходимы (то есть при использовании setPositiveButton()и setNegativeButton()т. Д.), Вместо того, чтобы реализовывать тысячи методов обратного вызова для связи между фрагментом диалога и фрагментом-держателем, который может, в моё мнение, приведёт к очень запутанному и сложному коду. Особенно, если у вас есть несколько разных диалогов в одном фрагменте, а затем нужно различать в реализациях обратного вызова, какой диалог отображается в данный момент.

Поэтому я объединил разные подходы для создания универсального AlertDialogFragmentвспомогательного класса, который можно использовать в точности так AlertDialog :


РЕШЕНИЕ

( ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ, что я использую лямбда-выражения Java 8 в своем коде, поэтому вам, возможно, придется изменить части кода, если вы еще не используете лямбда-выражения .)

/**
 * Helper class for dialog fragments to show a {@link AlertDialog}. It can be used almost exactly
 * like a {@link AlertDialog.Builder}
 * <p />
 * Creation Date: 22.03.16
 *
 * @author felix, http://flx-apps.com/
 */
public class AlertDialogFragment extends DialogFragment {
    protected FragmentActivity activity;
    protected Bundle args;
    protected String tag = AlertDialogFragment.class.getSimpleName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activity = getActivity();
        args = getArguments();
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = setDialogDefaults(new AlertDialog.Builder(getActivity())).create();

        if (args.containsKey("gravity")) {
            dialog.getWindow().getAttributes().gravity = args.getInt("gravity");
        }

        dialog.setOnShowListener(d -> {
            if (dialog != null && dialog.findViewById((android.R.id.message)) != null) {
                ((TextView) dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
            }
        });
        return dialog;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);

        if (args.containsKey("onDismissListener")) {
            Parcelable onDismissListener = args.getParcelable("onDismissListener");
            if (onDismissListener != null && onDismissListener instanceof ParcelableOnDismissListener) {
                ((ParcelableOnDismissListener) onDismissListener).onDismiss(this);
            }
        }
    }

    /**
     * Sets default dialog properties by arguments which were set using {@link #builder(FragmentActivity)}
     */
    protected AlertDialog.Builder setDialogDefaults(AlertDialog.Builder builder) {
        args = getArguments();
        activity = getActivity();

        if (args.containsKey("title")) {
            builder.setTitle(args.getCharSequence("title"));
        }

        if (args.containsKey("message")) {
            CharSequence message = args.getCharSequence("message");
            builder.setMessage(message);
        }

        if (args.containsKey("viewId")) {
            builder.setView(getActivity().getLayoutInflater().inflate(args.getInt("viewId"), null));
        }

        if (args.containsKey("positiveButtonText")) {
            builder.setPositiveButton(args.getCharSequence("positiveButtonText"), (dialog, which) -> {
                onButtonClicked("positiveButtonListener", which);
            });
        }

        if (args.containsKey("negativeButtonText")) {
            builder.setNegativeButton(args.getCharSequence("negativeButtonText"), (dialog, which) -> {
                onButtonClicked("negativeButtonListener", which);
            });
        }

        if (args.containsKey("neutralButtonText")) {
            builder.setNeutralButton(args.getCharSequence("neutralButtonText"), (dialog, which) -> {
                onButtonClicked("neutralButtonListener", which);
            });
        }

        if (args.containsKey("items")) {
            builder.setItems(args.getStringArray("items"), (dialog, which) -> {
                onButtonClicked("itemClickListener", which);
            });
        }

        // @formatter:off
        // FIXME this a pretty hacky workaround: we don't want to show the dialog if onClickListener of one of the dialog's button click listener were lost
        //       the problem is, that there is no (known) solution for parceling a OnClickListener in the long term (only for state changes like orientation change,
        //       but not if the Activity was completely lost)
        if (
                (args.getParcelable("positiveButtonListener") != null && !(args.getParcelable("positiveButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("negativeButtonListener") != null && !(args.getParcelable("negativeButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("neutralButtonListener") != null && !(args.getParcelable("neutralButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("itemClickListener") != null && !(args.getParcelable("itemClickListener") instanceof ParcelableOnClickListener))
        ) {
            new DebugMessage("Forgot onClickListener. Needs to be dismissed.")
                    .logLevel(DebugMessage.LogLevel.VERBOSE)
                    .show();
            try {
                dismissAllowingStateLoss();
            } catch (NullPointerException | IllegalStateException ignored) {}
        }
        // @formatter:on

        return builder;
    }

    public interface OnDismissListener {
        void onDismiss(AlertDialogFragment dialogFragment);
    }

    public interface OnClickListener {
        void onClick(AlertDialogFragment dialogFragment, int which);
    }

    protected void onButtonClicked(String buttonKey, int which) {
        ParcelableOnClickListener parcelableOnClickListener = getArguments().getParcelable(buttonKey);
        if (parcelableOnClickListener != null) {
            parcelableOnClickListener.onClick(this, which);
        }
    }

    // region Convenience Builder Pattern class almost similar to AlertDialog.Builder
    // =============================================================================================

    public AlertDialogFragment builder(FragmentActivity activity) {
        this.activity = activity;
        this.args = new Bundle();
        return this;
    }

    public AlertDialogFragment addArguments(Bundle bundle) {
        args.putAll(bundle);
        return this;
    }

    public AlertDialogFragment setTitle(int titleStringId) {
        return setTitle(activity.getString(titleStringId));
    }

    public AlertDialogFragment setTitle(CharSequence title) {
        args.putCharSequence("title", title);
        return this;
    }

    public AlertDialogFragment setMessage(int messageStringId) {
        return setMessage(activity.getString(messageStringId));
    }

    public AlertDialogFragment setMessage(CharSequence message) {
        args.putCharSequence("message", message);
        return this;
    }

    public AlertDialogFragment setPositiveButton(int textStringId, OnClickListener onClickListener) {
        return setPositiveButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setPositiveButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("positiveButtonText", text);
        args.putParcelable("positiveButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNegativeButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNegativeButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNegativeButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("negativeButtonText", text);
        args.putParcelable("negativeButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNeutralButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNeutralButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNeutralButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("neutralButtonText", text);
        args.putParcelable("neutralButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setOnDismissListener(OnDismissListener onDismissListener) {
        if (onDismissListener == null) {
            return this;
        }

        Parcelable p = new ParcelableOnDismissListener() {
            @Override
            public void onDismiss(AlertDialogFragment dialogFragment) {
                onDismissListener.onDismiss(dialogFragment);
            }
        };
        args.putParcelable("onDismissListener", p);
        return this;
    }

    public AlertDialogFragment setItems(String[] items, AlertDialogFragment.OnClickListener onClickListener) {
        args.putStringArray("items", items);
        args.putParcelable("itemClickListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setView(int viewId) {
        args.putInt("viewId", viewId);
        return this;
    }

    public AlertDialogFragment setGravity(int gravity) {
        args.putInt("gravity", gravity);
        return this;
    }

    public AlertDialogFragment setTag(String tag) {
        this.tag = tag;
        return this;
    }

    public AlertDialogFragment create() {
        setArguments(args);
        return AlertDialogFragment.this;
    }

    public AlertDialogFragment show() {
        create();
        try {
            super.show(activity.getSupportFragmentManager(), tag);
        }
        catch (IllegalStateException e1) {

            /**
             * this whole part is used in order to attempt to show the dialog if an
             * {@link IllegalStateException} was thrown (it's kinda comparable to
             * {@link FragmentTransaction#commitAllowingStateLoss()} 
             * So you can remove all those dirty hacks if you are sure that you are always
             * properly showing dialogs in the right moments
             */

            new DebugMessage("got IllegalStateException attempting to show dialog. trying to hack around.")
                    .logLevel(DebugMessage.LogLevel.WARN)
                    .exception(e1)
                    .show();

            try {
                Field mShownByMe = DialogFragment.class.getDeclaredField("mShownByMe");
                mShownByMe.setAccessible(true);
                mShownByMe.set(this, true);
                Field mDismissed = DialogFragment.class.getDeclaredField("mDismissed");
                mDismissed.setAccessible(true);
                mDismissed.set(this, false);
            }
            catch (Exception e2) {
                new DebugMessage("error while showing dialog")
                        .exception(e2)
                        .logLevel(DebugMessage.LogLevel.ERROR)
                        .show();
            }
            FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
            transaction.add(this, tag);
            transaction.commitAllowingStateLoss(); // FIXME hacky and unpredictable workaround
        }
        return AlertDialogFragment.this;
    }

    @Override
    public int show(FragmentTransaction transaction, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    protected ParcelableOnClickListener createParcelableOnClickListener(AlertDialogFragment.OnClickListener onClickListener) {
        if (onClickListener == null) {
            return null;
        }

        return new ParcelableOnClickListener() {
            @Override
            public void onClick(AlertDialogFragment dialogFragment, int which) {
                onClickListener.onClick(dialogFragment, which);
            }
        };
    }

    /**
     * Parcelable OnClickListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnClickListener extends ResultReceiver implements AlertDialogFragment.OnClickListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnClickListener() {
            super(null);
        }

        @Override
        public abstract void onClick(AlertDialogFragment dialogFragment, int which);
    }

    /**
     * Parcelable OnDismissListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnDismissListener extends ResultReceiver implements AlertDialogFragment.OnDismissListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnDismissListener() {
            super(null);
        }

        @Override
        public abstract void onDismiss(AlertDialogFragment dialogFragment);
    }


    // =============================================================================================
    // endregion
}

ИСПОЛЬЗОВАНИЕ

// showing a normal alert dialog with state loss on configuration changes (like device rotation)
new AlertDialog.Builder(getActivity())
        .setTitle("Are you sure? (1)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

// showing a dialog fragment using the helper class with no state loss on configuration changes
new AlertDialogFragment.builder(getActivity())
        .setTitle("Are you sure? (2)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

Я публикую это здесь не только для того, чтобы поделиться своим решением, но и потому, что я хотел спросить вас, люди, за ваше мнение: этот подход в какой-то степени законен или проблематичен?


3
Это очень интересная идея, но я не думаю, что дизайн API работает. Если вы передаете OnClickListener в setPositiveButton (), когда устройство поворачивается и фрагмент воссоздается из аргументов Bundle, OnClickListeners не будет должным образом воссоздан из Parcelable. Основная проблема заключается в том, что вы не можете воссоздать слушателя во время ротации, но интерфейс API (который принимает интерфейсы) требует этого. Я хотел бы, чтобы это было не так (как мне нравится идея).
Xargs

1
Хорошая идея, но, как говорит @Xargs, она не работает. Переданные слушатели не воссоздаются правильно при ротации.
Грэм Борланд

Мои результаты состоят в том, что оно фактически работает при ротации и возобновлении работы приложения (например, после перехода на домашний экран), но не при восстановлении активности после ее полного уничтожения (тогда OnClickListeners действительно теряются). (Проверено на Android 4.4.4 и Android 5.1.1)
flxapps

Я не тестировал эту точную реализацию, но из того, что я тестировал, прослушиваемый объект, передаваемый в пакет фрагментов, корректно вызывается при воссоздании. Я понятия не имею, почему, но это похоже на работу.
Саад Фарук

@flxapps, в случае пользовательского представления, как вы можете получить дочерние представления и изменить их свойства или применить слушателей? В вашем классе вы не возвращаете ни одного экземпляра диалога, и это может вызвать исключение, если кто-то попытается получить дочерние представления
Zubair Rehman

5

Могу ли я предложить небольшое упрощение ответа @ ashishduh:

public class AlertDialogFragment extends DialogFragment {
public static final String ARG_TITLE = "AlertDialog.Title";
public static final String ARG_MESSAGE = "AlertDialog.Message";

public static void showAlert(String title, String message, Fragment targetFragment) {
    DialogFragment dialog = new AlertDialogFragment();
    Bundle args = new Bundle();
    args.putString(ARG_TITLE, title);
    args.putString(ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(targetFragment, 0);
    dialog.show(targetFragment.getFragmentManager(), "tag");
}

public AlertDialogFragment() {}

@NonNull
@Override
public AlertDialog onCreateDialog(Bundle savedInstanceState)
{
    Bundle args = getArguments();
    String title = args.getString(ARG_TITLE, "");
    String message = args.getString(ARG_MESSAGE, "");

    return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .create();
}

Это устраняет необходимость для пользователя (класса) быть знакомым с внутренними компонентами компонента и делает его использование действительно простым:

AlertDialogFragment.showAlert(title, message, this);

PS В моем случае мне понадобилось простое диалоговое окно с предупреждением, вот что я создал. Вы можете применить подход к Да / Нет или любому другому типу, который вам нужен.


1

Используйте диалог для простых диалогов да или нет.

Когда вам нужны более сложные представления, в которых вам нужно овладеть жизненным циклом, таким как oncreate, запрос разрешений, любое переопределение жизненного цикла, я бы использовал фрагмент диалога. Таким образом, вы разделяете разрешения и любой другой код, который должен работать в диалоговом окне, не связываясь с активностью вызова.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.