eltos / SimpleDialogFragments

An Android library to create dialogs with ease and handle user interaction reliably, using fragments and material design.
Apache License 2.0
119 stars 17 forks source link

How to customize button behaviour, so I can reuse it in many parts of the application? #73

Closed mendhak closed 3 years ago

mendhak commented 3 years ago

Hello again.

What I'm trying to do is create a dialog that shows a 'COPY' button. When user presses that, it copies the current message into the clipboard. Ideally, without handling its onResult in every Activity that I call it from. Maybe you can point out a better approach to do this.

image

Here's what I've tried, to extend the SimpleDialog, since the SimpleDialog already does what I want, I thought to extend it instead of doing a completely custom one. And then in the onResult I want to copy it to the user's clipboard.

package com.mendhak.gpslogger.ui.components;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import eltos.simpledialogfragment.SimpleDialog;

public class SimpleErrorDialog extends SimpleDialog implements SimpleDialog.OnDialogResultListener {

    public static final String TAG = "SimpleErrorDialog.";
    public String msgHtml;

    public static SimpleErrorDialog build(){
        SimpleErrorDialog sed = new SimpleErrorDialog();
        sed.neut("COPY");
        return sed;
    }

    @Override
    public SimpleErrorDialog msgHtml(String message) {
        this.msgHtml = message;
        super.msgHtml(msgHtml);
        return this;
    }

    @Override
    public boolean onResult(@NonNull String dialogTag, int which, @NonNull Bundle extras) {
        Log.w("TAG","COPIED");
        android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
        android.content.ClipData clip = android.content.ClipData.newPlainText("Gpslogger error message", this.msgHtml);
        clipboard.setPrimaryClip(clip);
        return false;
    }

}

I call it from my code like so:

SimpleErrorDialog.build().title(R.string.error).msgHtml("This is the error message.").show(activity, "ERROR");

The dialog displays, but when I press 'COPY' the onResult is never called.

eltos commented 3 years ago

Hi @mendhak

the OnDialogResultListener and it's onResult method are ment for the hosting fragment or activity, not the dialog fragment itself. You better overwrite the callResultListener method instead, or alternatively set a custom OnClickListener on the neutral button. Please find the respective examples below.

Also, I strongly advise you to not use a field msgHtml to store information with the dialog for two reasons:

Method 1 (dialog closes upon pressing the COPY button)

Extending SimpleDialog and overwriting callResultListener

public class SimpleErrorDialog extends SimpleDialog<SimpleErrorDialog> {

    public static SimpleErrorDialog build(){
        return new SimpleErrorDialog()
                .title(R.string.error)
                .neut(R.string.copy);
    }

    @Override
    protected boolean callResultListener(int which, @Nullable Bundle extras) {
        if (which == OnDialogResultListener.BUTTON_NEUTRAL) {
            ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clip;
            if (getArgs().getBoolean(HTML)){
                // getMessage returns HTML text as set by .msgHtml(...)
                clip = ClipData.newHtmlText(getTitle(), plainTextFromHtml(getMessage()), getMessage());
            } else {
                // getMessage returns plain text as set by .msg(...)
                clip = ClipData.newPlainText(getTitle(), getMessage());
            }
            clipboard.setPrimaryClip(clip);
            Toast.makeText(getContext(), "Error message copied to clipboard", Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.callResultListener(which, extras);
    }
}

Method 2 (dialog remains open upon pressing the COPY button)

Extending CustomViewDialog and setting a custom OnClickListener

public class SimpleErrorDialog extends CustomViewDialog<SimpleErrorDialog> {

    public static SimpleErrorDialog build(){
        return new SimpleErrorDialog()
                .title(R.string.error)
                .neut(R.string.copy);
    }

    @Override
    protected void onDialogShown() {
        Button button = getNeutralButton();
        if (button != null) {
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
                    ClipData clip;
                    if (getArgs().getBoolean(HTML)){
                        // getMessage returns HTML text as set by .msgHtml(...)
                        clip = ClipData.newHtmlText(getTitle(), plainTextFromHtml(getMessage()), getMessage());
                    } else {
                        // getMessage returns plain text as set by .msg(...)
                        clip = ClipData.newPlainText(getTitle(), getMessage());
                    }
                    clipboard.setPrimaryClip(clip);
                    Toast.makeText(getContext(), "Error message copied to clipboard", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    @Override
    protected View onCreateContentView(Bundle savedInstanceState) {
        return new ViewStub(getContext()); // no special content required
    }
}

Usage for both cases

SimpleErrorDialog.build()
    .msg(...) // or .msgHtml(...)
    .show(this);
mendhak commented 3 years ago

That's very good thanks! I see, the key is callResultListener. This will be very useful as I have lots of 'commonly repeated' dialogs scattered about, so I can put this in one place.

Yes no worries about the msgHTML field, it was just me messing about. That getArgs() looks quite useful too for the future, if I'm wrapping more functionality.

I was able to simplify the Method 1 to:

public class SimpleErrorDialog extends SimpleDialog<SimpleErrorDialog> {

    public static SimpleErrorDialog build(){
        return new SimpleErrorDialog()
                .title(R.string.error)
                .neut(R.string.copy);
    }

    @Override
    protected boolean callResultListener(int which, @Nullable Bundle extras) {
        if (which == OnDialogResultListener.BUTTON_NEUTRAL) {
            String plainMessage = Html.fromHtml(getMessage()).toString();
            android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
            android.content.ClipData clip = android.content.ClipData.newPlainText("GPSLogger error message", plainMessage);
            clipboard.setPrimaryClip(clip);

            Toast.makeText(getContext(), "Error message copied to clipboard", Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.callResultListener(which, extras);
    }
}

As always thanks for your help!