Red wine
Red wine

Reputation: 172

Add a neutral button to androidx EditTextPrefence / DialogPreference

I have been using my own preference class derived from EditTextPreference to include a neutral button (so that the user can select a default value). I did this by overriding onPrepareDialogBuilder (from searching stackoverflow of course. )

override fun onPrepareDialogBuilder(builder: AlertDialog.Builder?) {  //So can set have a button to set the default value
    super.onPrepareDialogBuilder(builder)
    builder?.setNeutralButton("Default") { dialogInterface: DialogInterface, i: Int ->
        text = sharedPreferences.getString(dftKey, "")
    }
}

However, this method does not seem possible with the androidx EditTextPreference. Any suggestions on how to add a neutral button to androidx's EditTextPreference would be appreciated.

Upvotes: 0

Views: 271

Answers (1)

Red wine
Red wine

Reputation: 172

Having set up my settings activity as per https://developer.android.com/guide/topics/ui/settings I have found that I can add the PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback interface to the AppCompatActivity and override onPreferenceDisplayDialog to show my own dialog.

Edit 15/1/23 Someone wanted to know more about fun myPrefDialog, so I have added some more code. Hope it helps. It makes it rather lengthy and not "completely complete"

        class MySettingsActivity: AppCompatActivity(),  PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
        
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
        
                supportFragmentManager
                        .beginTransaction()
                        .replace(android.R.id.content, MyPreferenceFragment())
                        .commit()
            }
        
        
            override fun onPreferenceDisplayDialog(caller: PreferenceFragmentCompat, pref: Preference?): Boolean {
                var handeld = false
                if(pref is MyEditTextPreference) {
                    myPrefDialog(caller, pref)
                    handled = true
                }
                return handled
            }
        }
    
    open class TlEditTextPreference: EditTextPreference {
        constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
        constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
        constructor(context: Context) : super(context)
    
        protected  var dlgOptions = 0
        internal var origTitle = title.toString()
        var valueBeforeEdit: String = ""
    
        override fun onAttached() {
            super.onAttached()
            PrefUtils.chkSetPrefDefault(this)
    
    //These lines are a debug warning
            dialogTitle = "Don't use default dialog."
            dialogMessage = "Override onPreferenceDisplayDialog in the preference activity\n"
                    " to call my own dialog"
            positiveButtonText = "Hey stupid, read the warning above"
        }
    
        override fun setText(text: String?) {
            super.setText(text)
            updateTitle()
        }
    
        fun updateTitle() {
            try {
                title = "$origTitle [$text]"
            } catch (e: Exception) {
                e.printStackTrace()
            }
    
        }
    
        open fun myPrefDialog(context: Context): Boolean {
            valueBeforeEdit = text ?: ""
            dlgOptions = dlgOptions or Misc.idoSTART_SELECTED or Misc.idoDONE_KEY
            val dft = PrefUtils.chkSetPrefDefault(this)
            Misc.inputDlg2(context, title.toString(), "(Default $dft)",valueBeforeEdit, "Cancel", "Default", "OK", dlgOptions)
            { editText, which ->
                when(which) {
                    Misc.BTN_POS -> text = editText.text.toString()
                    Misc.BTN_NEUTRAL -> text = dft
                }
            }
            return true
        }
    }


const val idoNONE = 0
const val idoNO_FULLSCREEN = 1
const val idoSTART_SELECTED = 2
const val idoDONE_KEY = 4
const val idoNUMERIC_KEYPAD = 8
const val idoFIRST_LETTER_LOWERCASE = 0x10
const val idoPASSWORD = 0x20
const val idoAll_CAPS = 0x40

    class inputDlg2(context: Context, title: String, msg: String, text: String, btnNeg: String?, btnNeutral: String?,
                    btnPos: String?, options: Int = idoNONE, dialogCallback: (editText: EditText, which: Int) -> Unit){
        val inflater = LayoutInflater.from(context)
//        val rv = (context as Activity).window.decorView

        val rootView: ViewGroup = (context as Activity).findViewById(android.R.id.content) as ViewGroup
        val promptView = inflater.inflate(R.layout.input_box, rootView, false)

        val db = AlertDialog.Builder(context)
        var inputDlgEditText = promptView.findViewById(R.id.edittext) as EditText
        val ocListener = DialogInterface.OnClickListener { dialog, which ->
            lastInputDlgText = inputDlgEditText.text.toString()
            dialogCallback(inputDlgEditText, which)
        }

        init {
            inputDlgEditText.setText(text)
            inputDlgEditText.setTextIsSelectable(true)

            if (options and idoNUMERIC_KEYPAD != 0) {
//                inputDlgEditText.inputType = InputType.TYPE_NUMBER_FLAG_DECIMAL
//                inputDlgEditText.setRawInputType(Configuration.KEYBOARD_12KEY)
                inputDlgEditText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED
            } else
                inputDlgEditText.inputType = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or inputDlgEditText.inputType
            if(options and idoPASSWORD != 0)
                inputDlgEditText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD

            if (options and idoSTART_SELECTED != 0)
                inputDlgEditText.setSelectAllOnFocus(true)  //So that last current string will be selected and disapear if start typing
            if(options and idoFIRST_LETTER_LOWERCASE != 0)
                inputDlgEditText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
            if(options and idoAll_CAPS != 0)
                inputDlgEditText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS

            if(options and idoPASSWORD != 0)
                inputDlgEditText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD

            var editorInfoFlags = 0
            if (options and idoNO_FULLSCREEN != 0) {
                inputDlgEditText.isSingleLine = true  //Added in API29
                editorInfoFlags = editorInfoFlags or EditorInfo.IME_FLAG_NO_FULLSCREEN    //to disable full screen editor
            }
            if (options and idoDONE_KEY != 0)
                editorInfoFlags = editorInfoFlags or EditorInfo.IME_ACTION_DONE
            inputDlgEditText.imeOptions = editorInfoFlags
            val message = promptView.findViewById(R.id.inputPrompt) as TextView
            message.text = msg
            db.setView(promptView)
            db.setTitle(title)
            if (btnPos != null) db.setPositiveButton(btnPos, ocListener)
            if (btnNeutral != null) db.setNeutralButton(btnNeutral, ocListener)
            if (btnNeg != null) db.setNegativeButton(btnNeg, ocListener)
            db.setIcon(android.R.drawable.ic_dialog_alert)

            val dlg = db.create()

            dlg.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)   //Show the software keyboard immediately
            val wmlp = dlg.window!!.attributes
            wmlp.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
            wmlp.x = 0   //x position
            //        wmlp.y = 100;   //y position

            inputDlgEditText.requestFocus()
            dlg.show()

            inputDlgEditText.setOnTouchListener { v, event ->
                val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                imm.showSoftInput(inputDlgEditText, 0)
                false
            }

            if(options and idoDONE_KEY != 0) {  //Callback when the done key pressed
                inputDlgEditText.setOnEditorActionListener { textView: TextView?, action: Int, keyEvent: KeyEvent? ->
                    if (action == EditorInfo.IME_ACTION_DONE) {
                        dialogCallback(inputDlgEditText, Misc.BTN_POS)
                        dlg.dismiss()
                    }
                    false
                }
            }

        }
    }

Upvotes: 0

Related Questions