ca.rmen.android.scrumchatter.dialog.InputDialogFragment.java Source code

Java tutorial

Introduction

Here is the source code for ca.rmen.android.scrumchatter.dialog.InputDialogFragment.java

Source

/*
 * Copyright 2013, 2017 Carmen Alvarez
 *
 * This file is part of Scrum Chatter.
 *
 * Scrum Chatter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scrum Chatter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scrum Chatter. If not, see <http://www.gnu.org/licenses/>.
 */
package ca.rmen.android.scrumchatter.dialog;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;

import ca.rmen.android.scrumchatter.databinding.InputDialogEditTextBinding;
import ca.rmen.android.scrumchatter.util.Log;

import android.view.LayoutInflater;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import ca.rmen.android.scrumchatter.Constants;
import ca.rmen.android.scrumchatter.R;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

/**
 * A dialog fragment with an EditText for user text input.
 */
public class InputDialogFragment extends DialogFragment { // NO_UCD (use default)

    private static final String TAG = Constants.TAG + "/" + InputDialogFragment.class.getSimpleName();

    private String mEnteredText;

    public interface InputValidator {
        /**
         * @param input the text entered by the user.
         * @return an error string if the input has a problem, null if the input is valid.
         */
        @WorkerThread
        @Nullable
        String getError(Context context, CharSequence input, Bundle extras);
    }

    /**
     * The activity owning this dialog fragment should implement this interface to be notified when the user submits entered text.
     */
    public interface DialogInputListener {
        void onInputEntered(int actionId, String input, Bundle extras);
    }

    public InputDialogFragment() {
        super();
    }

    @Override
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Log.v(TAG, "onCreateDialog: savedInstanceState = " + savedInstanceState);
        if (savedInstanceState != null)
            mEnteredText = savedInstanceState.getString(DialogFragmentFactory.EXTRA_ENTERED_TEXT);
        Bundle arguments = getArguments();
        final int actionId = arguments.getInt(DialogFragmentFactory.EXTRA_ACTION_ID);

        final InputDialogEditTextBinding binding = DataBindingUtil.inflate(LayoutInflater.from(getActivity()),
                R.layout.input_dialog_edit_text, null, false);

        final Bundle extras = arguments.getBundle(DialogFragmentFactory.EXTRA_EXTRAS);
        final Class<?> inputValidatorClass = (Class<?>) arguments
                .getSerializable(DialogFragmentFactory.EXTRA_INPUT_VALIDATOR_CLASS);
        final String prefilledText = arguments.getString(DialogFragmentFactory.EXTRA_ENTERED_TEXT);
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

        builder.setTitle(arguments.getString(DialogFragmentFactory.EXTRA_TITLE));
        builder.setView(binding.getRoot());
        binding.edit.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
        binding.edit.setHint(arguments.getString(DialogFragmentFactory.EXTRA_INPUT_HINT));
        binding.edit.setText(prefilledText);
        if (!TextUtils.isEmpty(mEnteredText))
            binding.edit.setText(mEnteredText);

        // Notify the activity of the click on the OK button.
        OnClickListener listener = null;
        if ((getActivity() instanceof DialogInputListener)) {
            listener = (dialog, which) -> {
                FragmentActivity activity = getActivity();
                if (activity == null)
                    Log.w(TAG, "User clicked on dialog after it was detached from activity. Monkey?");
                else
                    ((DialogInputListener) activity).onInputEntered(actionId, binding.edit.getText().toString(),
                            extras);
            };
        }
        builder.setNegativeButton(android.R.string.cancel, null);
        builder.setPositiveButton(android.R.string.ok, listener);

        final AlertDialog dialog = builder.create();
        // Show the keyboard when the EditText gains focus.
        binding.edit.setOnFocusChangeListener((v, hasFocus) -> {
            if (hasFocus) {
                Window window = dialog.getWindow();
                if (window != null) {
                    window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
                }
            }
        });

        final Context context = getActivity();
        try {
            final InputValidator validator = inputValidatorClass == null ? null
                    : (InputValidator) inputValidatorClass.newInstance();
            Log.v(TAG, "input validator = " + validator);
            // Validate the text as the user types.
            binding.edit.addTextChangedListener(new TextWatcher() {

                @Override
                public void afterTextChanged(Editable s) {
                }

                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    mEnteredText = binding.edit.getText().toString();
                    if (validator != null)
                        validateText(context, dialog, binding.edit, validator, actionId, extras);
                }
            });
            dialog.setOnShowListener(dialogInterface -> {
                Log.v(TAG, "onShow");
                validateText(context, dialog, binding.edit, validator, actionId, extras);
            });
        } catch (Exception e) {
            Log.e(TAG, "Could not instantiate validator " + inputValidatorClass + ": " + e.getMessage(), e);
        }

        return dialog;
    }

    @Override
    public void onSaveInstanceState(Bundle bundle) {
        Log.v(TAG, "onSaveInstanceState: bundle = " + bundle);
        bundle.putString(DialogFragmentFactory.EXTRA_ENTERED_TEXT, mEnteredText);
        super.onSaveInstanceState(bundle);
    }

    /**
     * Invoke the input validator in a background thread. If the validator returns an error, the given edit text will be updated with the error in a tooltip.
     */
    private static void validateText(final Context context, AlertDialog dialog, final EditText editText,
            final InputValidator validator, final int actionId, final Bundle extras) {
        Log.v(TAG, "validateText: input = " + editText.getText().toString() + ", actionId = " + actionId
                + ", extras = " + extras);
        // Start off with everything a-ok.
        editText.setError(null);
        final Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
        okButton.setEnabled(!TextUtils.isEmpty(editText.getText()));

        // Search for an error in background thread, update the dialog in the UI thread.
        Maybe.fromCallable(() -> validator.getError(context, editText.getText().toString().trim(), extras))
                .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(error -> {
                    // If the input is invalid, highlight the error
                    // and disable the OK button.
                    editText.setError(error);
                    okButton.setEnabled(false);
                });
    }
}