com.abcvoipsip.ui.dialpad.DialerFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.abcvoipsip.ui.dialpad.DialerFragment.java

Source

/**
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
 * This file is part of CSipSimple.
 *
 *  CSipSimple 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.
 *  If you own a pjsip commercial license you can also redistribute it
 *  and/or modify it under the terms of the GNU Lesser General Public License
 *  as an android library.
 *
 *  CSipSimple 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 CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.abcvoipsip.ui.dialpad;

import android.app.AlertDialog;
import android.app.PendingIntent.CanceledException;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Resources;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ToneGenerator;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.SupportActivity;
import android.support.v4.view.Menu;
import android.support.v4.view.MenuItem;
import android.support.v4.view.MenuItem.OnMenuItemClickListener;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.Editable;
import android.text.Selection;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.DialerKeyListener;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;

import com.abcvoipsip.R;
import com.abcvoipsip.api.SipConfigManager;
import com.abcvoipsip.api.SipManager;
import com.abcvoipsip.api.SipProfile;
import com.abcvoipsip.service.OutgoingCall;
import com.abcvoipsip.ui.SipHome.ViewPagerVisibilityListener;
import com.abcvoipsip.utils.CallHandler;
import com.abcvoipsip.utils.Compatibility;
import com.abcvoipsip.utils.DialingFeedback;
import com.abcvoipsip.utils.Log;
import com.abcvoipsip.utils.PreferencesWrapper;
import com.abcvoipsip.utils.Theme;
import com.abcvoipsip.utils.CallHandler.onLoadListener;
import com.abcvoipsip.utils.contacts.ContactsSearchAdapter;
import com.abcvoipsip.widgets.AccountChooserButton;
import com.abcvoipsip.widgets.DialerCallBar;
import com.abcvoipsip.widgets.Dialpad;
import com.abcvoipsip.widgets.AccountChooserButton.OnAccountChangeListener;
import com.abcvoipsip.widgets.DialerCallBar.OnDialActionListener;
import com.abcvoipsip.widgets.Dialpad.OnDialKeyListener;
import com.abcvoipsip.api.ISipService;

public class DialerFragment extends Fragment implements OnClickListener, OnLongClickListener, OnDialKeyListener,
        TextWatcher, OnDialActionListener, ViewPagerVisibilityListener, OnKeyListener {

    private static final String THIS_FILE = "DialerFragment";

    protected static final int PICKUP_PHONE = 0;

    private Drawable digitsBackground, digitsEmptyBackground;
    private DigitsEditText digits;
    private ImageButton switchTextView;

    private View digitDialer;

    private LinearLayout digitsWrapper;
    private AccountChooserButton accountChooserButton;
    private boolean isDigit/* , isTablet */;

    private DialingFeedback dialFeedback;

    private final int[] buttonsToAttach = new int[] { R.id.button0, R.id.switchTextView };

    // TimingLogger timings = new TimingLogger("SIP_HOME", "test");

    private ISipService service;
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName arg0, IBinder arg1) {
            service = ISipService.Stub.asInterface(arg1);
            /*
             * timings.addSplit("Service connected"); if(configurationService !=
             * null) { timings.dumpToLog(); }
             */
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            service = null;
        }
    };

    // private GestureDetector gestureDetector;
    private Dialpad dialPad;

    private PreferencesWrapper prefsWrapper;
    // private TextView domainTextHelper;

    /* private EditSipUri sipTextUri; */
    private AlertDialog missingVoicemailDialog;

    // Auto completion for text mode
    private ListView autoCompleteList;
    private ContactsSearchAdapter autoCompleteAdapter;

    private DialerCallBar callBar;
    private boolean mDualPane;

    private DialerAutocompleteDetailsFragment autoCompleteFragment;
    private PhoneNumberFormattingTextWatcher digitFormater;

    // private ImageButton backFlipTextDialerButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDualPane = getResources().getBoolean(R.bool.use_dual_panes);
        digitFormater = new PhoneNumberFormattingTextWatcher();
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.dialer_digit, container, false);
        // Store the backgrounds objects that will be in use later
        Resources r = getResources();

        digitsBackground = r.getDrawable(R.drawable.btn_dial_textfield_active);
        digitsEmptyBackground = r.getDrawable(R.drawable.btn_dial_textfield_normal);

        // Store some object that could be useful later
        digits = (DigitsEditText) v.findViewById(R.id.digitsText);
        digitsWrapper = (LinearLayout) v.findViewById(R.id.topField);
        dialPad = (Dialpad) v.findViewById(R.id.dialPad);
        callBar = (DialerCallBar) v.findViewById(R.id.dialerCallBar);
        autoCompleteList = (ListView) v.findViewById(R.id.autoCompleteList);
        accountChooserButton = (AccountChooserButton) v.findViewById(R.id.accountChooserButton);
        switchTextView = (ImageButton) v.findViewById(R.id.switchTextView);

        // isTablet = Compatibility.isTabletScreen(getActivity());

        // Digits field setup
        isDigit = prefsWrapper.startIsDigit();
        digits.setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView tv, int action, KeyEvent arg2) {
                if (action == EditorInfo.IME_ACTION_GO) {
                    placeCall();
                    return true;
                }
                return false;
            }
        });

        // Account chooser button setup
        accountChooserButton.setShowExternals(true);
        accountChooserButton.setOnAccountChangeListener(new OnAccountChangeListener() {
            @Override
            public void onChooseAccount(SipProfile account) {
                long accId = SipProfile.INVALID_ID;
                if (account != null) {
                    accId = account.id;
                }
                autoCompleteAdapter.setSelectedAccount(accId);
            }
        });

        // Dialpad
        dialPad.setOnDialKeyListener(this);

        // Auto complete list in case of text
        autoCompleteAdapter = new ContactsSearchAdapter(getActivity());

        // We only need to add the autocomplete list if we
        DialerCallBar listCallBar = new DialerCallBar(getActivity(), null);
        listCallBar.setOnDialActionListener(this);
        autoCompleteList.addFooterView(listCallBar);
        autoCompleteList.setAdapter(autoCompleteAdapter);
        autoCompleteList.setOnItemClickListener(new OnAutoCompleteListItemClicked(autoCompleteAdapter));

        // Bottom bar setup
        callBar.setOnDialActionListener(this);

        // Ensure that current mode (text/digit) is applied
        setTextDialing(!isDigit);

        // Init other buttons
        initButtons(v);

        // Apply third party theme if any
        applyTheme();

        v.setOnKeyListener(this);

        return v;
    }

    private void applyTheme() {
        String theme = prefsWrapper.getPreferenceStringValue(SipConfigManager.THEME);
        if (!TextUtils.isEmpty(theme)) {
            new Theme(getActivity(), theme, new Theme.onLoadListener() {
                @Override
                public void onLoad(Theme t) {

                    dialPad.applyTheme(t);
                    // t.applyBackgroundDrawable(deleteButton,
                    // "btn_dial_delete");
                    // t.applyBackgroundDrawable(dialButton, "btn_dial_action");
                    t.applyBackgroundDrawable(getView().findViewById(R.id.vmButton), "btn_dial_action_left_normal");

                    // Bg ... to be done
                    Drawable bg = t.getDrawableResource("dialpad_bg");
                    if (bg != null) {
                        if (bg instanceof BitmapDrawable) {
                            BitmapDrawable dbg = (BitmapDrawable) bg;
                            dbg.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
                        }
                        digitDialer.setBackgroundDrawable(bg);
                    }

                    Drawable dAct = t.getDrawableResource("btn_dial_textfield_activated");
                    Drawable dEmpt = t.getDrawableResource("btn_dial_textfield_normal");
                    if (dAct != null && dEmpt != null) {
                        digitsBackground = dAct;
                        digitsEmptyBackground = dEmpt;
                        afterTextChanged(digits.getText());
                    }
                }
            });
        }
    }

    @Override
    public void onAttach(SupportActivity activity) {
        super.onAttach(activity);
        getActivity().bindService(new Intent(SipManager.INTENT_SIP_SERVICE), connection, Context.BIND_AUTO_CREATE);
        // timings.addSplit("Bind asked for two");
        if (prefsWrapper == null) {
            prefsWrapper = new PreferencesWrapper(getActivity());
        }
        if (dialFeedback == null) {
            dialFeedback = new DialingFeedback(getActivity(), false);
        }

        dialFeedback.resume();

    }

    @Override
    public void onDetach() {
        try {
            getActivity().unbindService(connection);
        } catch (Exception e) {
            // Just ignore that
            Log.w(THIS_FILE, "Unable to un bind", e);
        }
        dialFeedback.pause();
        super.onDetach();
    }

    private void attachButtonListener(View v, int id) {
        Log.d(THIS_FILE, "Attaching " + id);
        ImageButton button = (ImageButton) v.findViewById(id);
        button.setOnClickListener(this);

        if (id == R.id.button0 || id == R.id.deleteButton) {
            button.setOnLongClickListener(this);
        }
    }

    private void initButtons(View v) {
        for (int buttonId : buttonsToAttach) {
            attachButtonListener(v, buttonId);
        }

        digits.setOnClickListener(this);
        digits.setKeyListener(DialerKeyListener.getInstance());
        digits.addTextChangedListener(this);
        digits.setCursorVisible(false);
        afterTextChanged(digits.getText());
    }

    private void keyPressed(int keyCode) {
        KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
        digits.onKeyDown(keyCode, event);
    }

    private class OnAutoCompleteListItemClicked implements OnItemClickListener {
        private ContactsSearchAdapter searchAdapter;

        /**
         * Instanciate with a ContactsSearchAdapter adapter to search in when a
         * contact entry is clicked
         * 
         * @param adapter the adapter to use
         */
        public OnAutoCompleteListItemClicked(ContactsSearchAdapter adapter) {
            searchAdapter = adapter;
        }

        @Override
        public void onItemClick(AdapterView<?> list, View v, int position, long id) {
            Object selectedItem = searchAdapter.getItem(position);
            if (selectedItem != null) {
                CharSequence newValue = searchAdapter.getFilter().convertResultToString(selectedItem);
                setTextFieldValue(newValue);
            }
        }

    }

    public void onClick(View view) {
        // ImageButton b = null;
        int view_id = view.getId();

        if (view_id == R.id.button0) {
            dialFeedback.giveFeedback(ToneGenerator.TONE_DTMF_0);
            keyPressed(KeyEvent.KEYCODE_0);
        } else if (view_id == R.id.switchTextView) {
            // Set as text dialing if we are currently digit dialing
            setTextDialing(isDigit);
        } else if (view_id == R.id.digitsText) {
            if (digits.length() != 0) {
                digits.setCursorVisible(true);
            }
        }
    }

    public boolean onLongClick(View view) {
        // ImageButton b = (ImageButton)view;
        int vId = view.getId();
        if (vId == R.id.button0) {
            dialFeedback.hapticFeedback();
            keyPressed(KeyEvent.KEYCODE_PLUS);
            return true;
        }
        return false;
    }

    public void afterTextChanged(Editable input) {
        // Change state of digit dialer
        final boolean notEmpty = digits.length() != 0;
        digitsWrapper.setBackgroundDrawable(notEmpty ? digitsBackground : digitsEmptyBackground);
        digitsWrapper.setPadding(0, 0, 0, 0);
        callBar.setEnabled(notEmpty);

        if (!notEmpty && isDigit) {
            digits.setCursorVisible(false);
        }

        // If single pane for smartphone use autocomplete list
        if (!isDigit && !mDualPane) {
            if (digits.length() >= 2) {
                autoCompleteAdapter.getFilter().filter(digits.getText().toString());
            } else {
                autoCompleteAdapter.swapCursor(null);
            }
        }
        // Dual pane : always use autocomplete list
        if (mDualPane && autoCompleteFragment != null) {
            autoCompleteFragment.filter(digits.getText().toString());
        }
    }

    /**
     * Set the mode of the text/digit input.
     * 
     * @param target True if text mode. False if digit mode
     */
    private void setTextDialing(boolean target) {
        isDigit = !target;
        digits.setIsDigit(isDigit, true);
        if (isDigit) {
            digits.addTextChangedListener(digitFormater);
        } else {
            digits.removeTextChangedListener(digitFormater);
        }
        digits.getText().clear();
        digits.setCursorVisible(!isDigit);
        dialPad.setVisibility(isDigit ? View.VISIBLE : View.GONE);

        callBar.setVisibility(isDigit ? View.VISIBLE : View.GONE);
        autoCompleteList.setVisibility(isDigit ? View.GONE : View.VISIBLE);
        switchTextView.setImageResource(isDigit ? R.drawable.ic_menu_switch_txt : R.drawable.ic_menu_switch_digit);

        // Invalidate to ask to require the text button to a digit button
        getSupportActivity().invalidateOptionsMenu();
    }

    /**
     * Set the value of the text field and put caret at the end
     * 
     * @param value the new text to see in the text field
     */
    public void setTextFieldValue(CharSequence value) {
        digits.setText(value);
        // make sure we keep the caret at the end of the text view
        Editable spannable = digits.getText();
        Selection.setSelection(spannable, spannable.length());
    }

    // @Override
    public void onTrigger(int keyCode, int dialTone) {
        dialFeedback.giveFeedback(dialTone);
        keyPressed(keyCode);
    }

    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        // Nothing to do here

    }

    @Override
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        afterTextChanged(digits.getText());
        accountChooserButton.setChangeable(TextUtils.isEmpty(digits.getText().toString()));
    }

    // Options
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        boolean showInActionBar = Compatibility.isCompatible(14) || Compatibility.isTabletScreen(getActivity());

        int ifRoomIfSplit = showInActionBar ? MenuItem.SHOW_AS_ACTION_IF_ROOM : MenuItem.SHOW_AS_ACTION_NEVER;

        MenuItem delMenu = menu.add(isDigit ? R.string.switch_to_text : R.string.switch_to_digit);
        delMenu.setIcon(isDigit ? R.drawable.ic_menu_switch_txt : R.drawable.ic_menu_switch_digit)
                .setShowAsAction(ifRoomIfSplit);
        delMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                setTextDialing(isDigit);
                return true;
            }
        });
    }

    @Override
    public void placeCall() {
        if (service == null) {
            return;
        }
        String toCall = "";
        Long accountToUse = SipProfile.INVALID_ID;
        // Find account to use
        SipProfile acc = accountChooserButton.getSelectedAccount();
        if (acc != null) {
            accountToUse = acc.id;
        }
        // Find number to dial
        if (isDigit) {
            toCall = PhoneNumberUtils.stripSeparators(digits.getText().toString());
        } else {
            toCall = digits.getText().toString();
            // Add default scheme
            if (!toCall.startsWith("sip:") && !toCall.startsWith("sips:") && !toCall.startsWith("tel:")) {
                if (acc != null && acc.transport == SipProfile.TRANSPORT_TLS) {
                    toCall = "sips:" + toCall;
                } else {
                    toCall = "sip:" + toCall;
                }
            }
        }

        if (TextUtils.isEmpty(toCall)) {
            return;
        }

        // Well we have now the fields, clear theses fields
        digits.getText().clear();

        // -- MAKE THE CALL --//
        if (accountToUse >= 0) {
            // It is a SIP account, try to call service for that
            try {
                service.makeCall(toCall, accountToUse.intValue());
            } catch (RemoteException e) {
                Log.e(THIS_FILE, "Service can't be called to make the call");
            }
        } else if (accountToUse != SipProfile.INVALID_ID) {
            // It's an external account, find correct external account
            CallHandler ch = new CallHandler(getActivity());
            ch.loadFrom(accountToUse, toCall, new onLoadListener() {
                @Override
                public void onLoad(CallHandler ch) {
                    placePluginCall(ch);
                }
            });
        }
    }

    @Override
    public void placeVMCall() {
        Long accountToUse = SipProfile.INVALID_ID;
        SipProfile acc = null;
        acc = accountChooserButton.getSelectedAccount();
        if (acc != null) {
            accountToUse = acc.id;
        }

        if (accountToUse >= 0) {
            SipProfile vmAcc = SipProfile.getProfileFromDbId(getActivity(), acc.id,
                    new String[] { SipProfile.FIELD_VOICE_MAIL_NBR });
            if (!TextUtils.isEmpty(vmAcc.vm_nbr)) {
                // Account already have a VM number
                try {
                    service.makeCall(vmAcc.vm_nbr, (int) acc.id);
                } catch (RemoteException e) {
                    Log.e(THIS_FILE, "Service can't be called to make the call");
                }
            } else {
                // Account has no VM number, propose to create one
                final long editedAccId = acc.id;
                LayoutInflater factory = LayoutInflater.from(getActivity());
                final View textEntryView = factory.inflate(R.layout.alert_dialog_text_entry, null);

                missingVoicemailDialog = new AlertDialog.Builder(getActivity()).setTitle(acc.display_name)
                        .setView(textEntryView)
                        .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {

                                if (missingVoicemailDialog != null) {
                                    TextView tf = (TextView) missingVoicemailDialog.findViewById(R.id.vmfield);
                                    if (tf != null) {
                                        String vmNumber = tf.getText().toString();
                                        if (!TextUtils.isEmpty(vmNumber)) {
                                            ContentValues cv = new ContentValues();
                                            cv.put(SipProfile.FIELD_VOICE_MAIL_NBR, vmNumber);

                                            int updated = getActivity().getContentResolver()
                                                    .update(ContentUris.withAppendedId(
                                                            SipProfile.ACCOUNT_ID_URI_BASE, editedAccId), cv, null,
                                                            null);
                                            Log.d(THIS_FILE, "Updated accounts " + updated);
                                        }
                                    }
                                    missingVoicemailDialog.hide();
                                }
                            }
                        }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if (missingVoicemailDialog != null) {
                                    missingVoicemailDialog.hide();
                                }
                            }
                        }).create();

                // When the dialog is up, completely hide the in-call UI
                // underneath (which is in a partially-constructed state).
                missingVoicemailDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

                missingVoicemailDialog.show();
            }
        } else if (accountToUse == CallHandler.getAccountIdForCallHandler(getActivity(),
                "com.abcvoipsip/com.abcvoipsip.plugins.telephony.CallHandler")) {
            // Case gsm voice mail
            TelephonyManager tm = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
            String vmNumber = tm.getVoiceMailNumber();

            if (!TextUtils.isEmpty(vmNumber)) {
                OutgoingCall.ignoreNext = vmNumber;
                Intent intent = new Intent(Intent.ACTION_CALL, Uri.fromParts("tel", vmNumber, null));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            } else {

                missingVoicemailDialog = new AlertDialog.Builder(getActivity()).setTitle(R.string.gsm)
                        .setMessage(R.string.no_voice_mail_configured)
                        .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                if (missingVoicemailDialog != null) {
                                    missingVoicemailDialog.hide();
                                }
                            }
                        }).create();

                // When the dialog is up, completely hide the in-call UI
                // underneath (which is in a partially-constructed state).
                missingVoicemailDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

                missingVoicemailDialog.show();
            }
        }
        // TODO : manage others ?... for now, no way to do so cause no vm stored
    }

    private void placePluginCall(CallHandler ch) {
        try {
            String nextExclude = ch.getNextExcludeTelNumber();
            if (nextExclude != null) {
                OutgoingCall.ignoreNext = nextExclude;
            }
            ch.getIntent().send();
        } catch (CanceledException e) {
            Log.e(THIS_FILE, "Pending intent cancelled", e);
        }
    }

    @Override
    public void deleteChar() {
        keyPressed(KeyEvent.KEYCODE_DEL);
    }

    @Override
    public void deleteAll() {
        digits.getText().clear();
    }

    private final static String TAG_AUTOCOMPLETE_SIDE_FRAG = "autocomplete_dial_side_frag";

    @Override
    public void onVisibilityChanged(boolean visible) {
        if (visible && getResources().getBoolean(R.bool.use_dual_panes)) {
            // That's far to be optimal we should consider uncomment tests for reusing fragment
            // if (autoCompleteFragment == null) {
            autoCompleteFragment = new DialerAutocompleteDetailsFragment();

            if (digits != null) {
                Bundle bundle = new Bundle();
                bundle.putCharSequence(DialerAutocompleteDetailsFragment.EXTRA_FILTER_CONSTRAINT,
                        digits.getText().toString());

                autoCompleteFragment.setArguments(bundle);

            }
            // }
            // if
            // (getFragmentManager().findFragmentByTag(TAG_AUTOCOMPLETE_SIDE_FRAG)
            // != autoCompleteFragment) {
            // Execute a transaction, replacing any existing fragment
            // with this one inside the frame.
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.replace(R.id.details, autoCompleteFragment, TAG_AUTOCOMPLETE_SIDE_FRAG);
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            ft.commitAllowingStateLoss();

            // }
        }
    }

    @Override
    public boolean onKey(View arg0, int keyCode, KeyEvent arg2) {
        KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);

        return digits.onKeyDown(keyCode, event);
    }

}