/** * Copyright (C) 2012 Iordan Iordanov * Copyright (C) 2009 Michael A. MacDonald * * This 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 2 of the License, or * (at your option) any later version. * * This software 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 this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ package com.iiordanov.bVNC.dialogs; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Map.Entry; import com.iiordanov.bVNC.ConnectionBean; import com.iiordanov.bVNC.ConnectionSettable; import com.iiordanov.bVNC.MetaKeyBase; import com.iiordanov.bVNC.MetaKeyBean; import com.iiordanov.bVNC.Utils; import com.iiordanov.bVNC.RemoteCanvasActivity; import com.iiordanov.bVNC.Database; import com.iiordanov.bVNC.input.RemoteKeyboard; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; import android.os.Bundle; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.LinearLayout.LayoutParams; import android.widget.AdapterView; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import com.iiordanov.bVNC.*; import com.iiordanov.freebVNC.*; import com.iiordanov.aRDP.*; import com.iiordanov.freeaRDP.*; import com.iiordanov.aSPICE.*; import com.iiordanov.freeaSPICE.*; /** * @author Michael A. MacDonald * */ public class MetaKeyDialog extends Dialog implements ConnectionSettable { CheckBox _checkShift; CheckBox _checkCtrl; CheckBox _checkAlt; CheckBox _checkSuper; TextView _textKeyDesc; EditText _textListName; Spinner _spinnerKeySelect; Spinner _spinnerKeysInList; Spinner _spinnerLists; Database _database; static ArrayList<MetaList> _lists; ArrayList<MetaKeyBean> _keysInList; long _listId; RemoteCanvasActivity _canvasActivity; MetaKeyBean _currentKeyBean; public static final String[] EMPTY_ARGS = new String[0]; ConnectionBean _connection; /** * @param context */ public MetaKeyDialog(Context context) { super(context); setOwnerActivity((Activity)context); _canvasActivity = (RemoteCanvasActivity)context; } /* (non-Javadoc) * @see android.app.Dialog#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { _canvasActivity.getMenuInflater().inflate(R.menu.metakeymenu, menu); menu.findItem(R.id.itemDeleteKeyList).setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { /* (non-Javadoc) * @see android.view.MenuItem.OnMenuItemClickListener#onMenuItemClick(android.view.MenuItem) */ @Override public boolean onMenuItemClick(MenuItem item) { Utils.showYesNoPrompt(_canvasActivity, "Delete key list", "Delete list "+_textListName.getText().toString(), new OnClickListener() { /* (non-Javadoc) * @see android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int) */ @Override public void onClick(DialogInterface dialog, int which) { int position = _spinnerLists.getSelectedItemPosition(); if (position == Spinner.INVALID_POSITION) return; _listId = _lists.get(position).get_Id(); if (_listId > 1) { _lists.remove(position); ArrayAdapter<String> adapter = getSpinnerAdapter(_spinnerLists); adapter.remove(adapter.getItem(position)); SQLiteDatabase db = _database.getWritableDatabase(); db.execSQL(MessageFormat.format("DELETE FROM {0} WHERE {1} = {2}", MetaKeyBean.GEN_TABLE_NAME, MetaKeyBean.GEN_FIELD_METALISTID, _listId)); db.execSQL(MessageFormat.format("DELETE FROM {0} WHERE {1} = {2}", MetaList.GEN_TABLE_NAME, MetaList.GEN_FIELD__ID, _listId)); _connection.setMetaListId(1); _connection.save(db); setMetaKeyList(); } } }, null); return true; } }); menu.findItem(R.id.itemDeleteKey).setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { /* (non-Javadoc) * @see android.view.MenuItem.OnMenuItemClickListener#onMenuItemClick(android.view.MenuItem) */ @Override public boolean onMenuItemClick(MenuItem item) { final int position = _spinnerKeysInList.getSelectedItemPosition(); if (position != Spinner.INVALID_POSITION) { final MetaKeyBean toDelete = _keysInList.get(position); Utils.showYesNoPrompt(_canvasActivity, "Delete from list", "Delete key " + toDelete.getKeyDesc(), new OnClickListener() { /* (non-Javadoc) * @see android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int) */ @Override public void onClick(DialogInterface dialog, int which) { getSpinnerAdapter(_spinnerKeysInList).remove(toDelete.getKeyDesc()); _keysInList.remove(position); SQLiteDatabase db = _database.getWritableDatabase(); db.execSQL( MessageFormat.format("DELETE FROM {0} WHERE {1} = {2}", MetaKeyBean.GEN_TABLE_NAME, MetaKeyBean.GEN_FIELD_METALISTID, toDelete.get_Id()) ); if (_connection.getLastMetaKeyId() == toDelete.get_Id()) { _connection.setLastMetaKeyId(0); _connection.save(db); } int newPos = _spinnerKeysInList.getSelectedItemPosition(); if (newPos != Spinner.INVALID_POSITION && newPos < _keysInList.size()) { _currentKeyBean = new MetaKeyBean(_keysInList.get(newPos)); updateDialogForCurrentKey(); } } }, null); } return true; } }); return true; } /* (non-Javadoc) * @see android.app.Dialog#onMenuOpened(int, android.view.Menu) */ @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.findItem(R.id.itemDeleteKeyList).setEnabled(_currentKeyBean.getMetaListId()>1); menu.findItem(R.id.itemDeleteKey).setEnabled(_spinnerKeysInList.getSelectedItemPosition()!=Spinner.INVALID_POSITION); return true; } /* (non-Javadoc) * @see android.app.Dialog#onCreate(android.os.Bundle) */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.metakey); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.dimAmount = 1.0f; lp.width = LayoutParams.FILL_PARENT; lp.height = LayoutParams.WRAP_CONTENT; getWindow().setAttributes(lp); setTitle(R.string.meta_key_title); _checkShift = (CheckBox)findViewById(R.id.checkboxShift); _checkCtrl = (CheckBox)findViewById(R.id.checkboxCtrl); _checkAlt = (CheckBox)findViewById(R.id.checkboxAlt); _checkSuper = (CheckBox)findViewById(R.id.checkboxSuper); _textKeyDesc = (TextView)findViewById(R.id.textKeyDesc); _textListName = (EditText)findViewById(R.id.textListName); _spinnerKeySelect = (Spinner)findViewById(R.id.spinnerKeySelect); _spinnerKeysInList = (Spinner)findViewById(R.id.spinnerKeysInList); _spinnerLists = (Spinner)findViewById(R.id.spinnerLists); _database = _canvasActivity.getDatabase(); if (_lists == null) { _lists = new ArrayList<MetaList>(); MetaList.getAll(_database.getReadableDatabase(), MetaList.GEN_TABLE_NAME, _lists, MetaList.GEN_NEW); } _spinnerKeySelect.setAdapter(new ArrayAdapter<String>(getOwnerActivity(), R.layout.key_list_entry, MetaKeyBean.allKeysNames)); _spinnerKeySelect.setSelection(0); setListSpinner(); _checkShift.setOnCheckedChangeListener(new MetaCheckListener(RemoteKeyboard.SHIFT_MASK)); _checkAlt.setOnCheckedChangeListener(new MetaCheckListener(RemoteKeyboard.ALT_MASK)); _checkCtrl.setOnCheckedChangeListener(new MetaCheckListener(RemoteKeyboard.CTRL_MASK)); _checkSuper.setOnCheckedChangeListener(new MetaCheckListener(RemoteKeyboard.SUPER_MASK)); _spinnerLists.setOnItemSelectedListener(new OnItemSelectedListener() { /* (non-Javadoc) * @see android.widget.AdapterView.OnItemSelectedListener#onItemSelected(android.widget.AdapterView, android.view.View, int, long) */ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { _connection.setMetaListId(_lists.get(position).get_Id()); _connection.Gen_update(_database.getWritableDatabase()); setMetaKeyList(); } /* (non-Javadoc) * @see android.widget.AdapterView.OnItemSelectedListener#onNothingSelected(android.widget.AdapterView) */ public void onNothingSelected(AdapterView<?> parent) { } }); _spinnerKeysInList.setOnItemSelectedListener(new OnItemSelectedListener() { /* (non-Javadoc) * @see android.widget.AdapterView.OnItemSelectedListener#onItemSelected(android.widget.AdapterView, android.view.View, int, long) */ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { _currentKeyBean = new MetaKeyBean(_keysInList.get(position)); updateDialogForCurrentKey(); } /* (non-Javadoc) * @see android.widget.AdapterView.OnItemSelectedListener#onNothingSelected(android.widget.AdapterView) */ public void onNothingSelected(AdapterView<?> parent) { } }); _spinnerKeySelect.setOnItemSelectedListener(new OnItemSelectedListener() { /* (non-Javadoc) * @see android.widget.AdapterView.OnItemSelectedListener#onItemSelected(android.widget.AdapterView, android.view.View, int, long) */ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (_currentKeyBean == null) { _currentKeyBean = new MetaKeyBean(0,0,MetaKeyBean.allKeys.get(position)); } else { _currentKeyBean.setKeyBase(MetaKeyBean.allKeys.get(position)); } updateDialogForCurrentKey(); } /* (non-Javadoc) * @see android.widget.AdapterView.OnItemSelectedListener#onNothingSelected(android.widget.AdapterView) */ public void onNothingSelected(AdapterView<?> parent) { } }); ((Button)findViewById(R.id.buttonSend)).setOnClickListener(new View.OnClickListener() { /* (non-Javadoc) * @see android.view.View.OnClickListener#onClick(android.view.View) */ public void onClick(View v) { sendCurrentKey(); dismiss(); } }); ((Button)findViewById(R.id.buttonNewList)).setOnClickListener(new View.OnClickListener() { /* (non-Javadoc) * @see android.view.View.OnClickListener#onClick(android.view.View) */ @Override public void onClick(View v) { MetaList newList = new MetaList(); newList.setName("New"); SQLiteDatabase db = _database.getWritableDatabase(); newList.Gen_insert(db); _connection.setMetaListId(newList.get_Id()); _connection.save(db); _lists.add(newList); getSpinnerAdapter(_spinnerLists).add(newList.getName()); setMetaKeyList(); } }); ((Button)findViewById(R.id.buttonCopyList)).setOnClickListener(new View.OnClickListener() { /* (non-Javadoc) * @see android.view.View.OnClickListener#onClick(android.view.View) */ @Override public void onClick(View v) { MetaList newList = new MetaList(); newList.setName("Copy of " + _textListName.getText().toString()); SQLiteDatabase db = _database.getWritableDatabase(); newList.Gen_insert(db); db.execSQL(MessageFormat.format(getCopyListString(), newList.get_Id(), _listId)); _connection.setMetaListId(newList.get_Id()); _connection.save(db); _lists.add(newList); getSpinnerAdapter(_spinnerLists).add(newList.getName()); setMetaKeyList(); } }); } private static String copyListString; private String getCopyListString() { if (copyListString==null) { StringBuilder sb = new StringBuilder("INSERT INTO "); sb.append(MetaKeyBean.GEN_TABLE_NAME); sb.append(" ( "); sb.append(MetaKeyBean.GEN_FIELD_METALISTID); StringBuilder fieldList = new StringBuilder(); for (Entry<String,Object> s : _currentKeyBean.Gen_getValues().valueSet()) { if (!s.getKey().equals(MetaKeyBean.GEN_FIELD__ID) && !s.getKey().equals(MetaKeyBean.GEN_FIELD_METALISTID)) { fieldList.append(','); fieldList.append(s.getKey()); } } String fl = fieldList.toString(); sb.append(fl); sb.append(" ) SELECT {0} "); sb.append(fl); sb.append(" FROM "); sb.append(MetaKeyBean.GEN_TABLE_NAME); sb.append(" WHERE "); sb.append(MetaKeyBean.GEN_FIELD_METALISTID); sb.append(" = {1}"); copyListString = sb.toString(); } return copyListString; } private boolean _justStarted; /* (non-Javadoc) * @see android.app.Dialog#onStart() */ @Override protected void onStart() { takeKeyEvents(true); _justStarted = true; super.onStart(); View v = getCurrentFocus(); if (v!=null) v.clearFocus(); } /* (non-Javadoc) * @see android.app.Dialog#onStop() */ @Override protected void onStop() { int i = 0; for (MetaList l : _lists) { if (l.get_Id() == _listId) { String s = _textListName.getText().toString(); if (! s.equals(l.getName())) { l.setName(s); l.Gen_update(_database.getWritableDatabase()); ArrayAdapter<String> adapter = getSpinnerAdapter(_spinnerLists); adapter.remove(adapter.getItem(i)); adapter.insert(s,i); } break; } i++; } takeKeyEvents(false); super.onStop(); } /* (non-Javadoc) * @see android.app.Dialog#onKeyDown(int, android.view.KeyEvent) */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { _justStarted = false; if (keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_MENU && getCurrentFocus() == null) { int flags = event.getMetaState(); int currentFlags = _currentKeyBean.getMetaFlags(); MetaKeyBase base = MetaKeyBean.keysByKeyCode.get(keyCode); if (base != null) { if (0 != (flags & KeyEvent.META_SHIFT_ON)) { currentFlags |= RemoteKeyboard.SHIFT_MASK; } if (0 != (flags & KeyEvent.META_ALT_ON)) { currentFlags |= RemoteKeyboard.ALT_MASK; } _currentKeyBean.setKeyBase(base); } else { // Toggle flags according to meta keys if (0 != (flags & KeyEvent.META_SHIFT_ON)) { currentFlags ^= RemoteKeyboard.SHIFT_MASK; } if (0 != (flags & KeyEvent.META_ALT_ON)) { currentFlags ^= RemoteKeyboard.ALT_MASK; } if (keyCode == KeyEvent.KEYCODE_SEARCH) { currentFlags ^= RemoteKeyboard.CTRL_MASK; } } _currentKeyBean.setMetaFlags(currentFlags); updateDialogForCurrentKey(); return true; } return super.onKeyDown(keyCode, event); } /* (non-Javadoc) * @see android.app.Dialog#onKeyUp(int, android.view.KeyEvent) */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (! _justStarted && keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_MENU && getCurrentFocus()==null) { if (MetaKeyBean.keysByKeyCode.get(keyCode) != null) { sendCurrentKey(); dismiss(); } return true; } _justStarted = false; return super.onKeyUp(keyCode, event); } @SuppressWarnings("unchecked") private static ArrayAdapter<String> getSpinnerAdapter(Spinner spinner) { return (ArrayAdapter<String>)spinner.getAdapter(); } void sendCurrentKey() { int index = Collections.binarySearch(_keysInList, _currentKeyBean); SQLiteDatabase db = _database.getWritableDatabase(); if (index < 0) { int insertionPoint = -(index + 1); _currentKeyBean.Gen_insert(db); _keysInList.add(insertionPoint,_currentKeyBean); getSpinnerAdapter(_spinnerKeysInList).insert(_currentKeyBean.getKeyDesc(), insertionPoint); _spinnerKeysInList.setSelection(insertionPoint); _connection.setLastMetaKeyId(_currentKeyBean.get_Id()); } else { MetaKeyBean bean = _keysInList.get(index); _connection.setLastMetaKeyId(bean.get_Id()); _spinnerKeysInList.setSelection(index); } _connection.Gen_update(db); _canvasActivity.getCanvas().getKeyboard().sendMetaKey(_currentKeyBean); } void setMetaKeyList() { long listId = _connection.getMetaListId(); if (listId!=_listId) { for (int i=0; i<_lists.size(); ++i) { MetaList list = _lists.get(i); if (list.get_Id()==listId) { _spinnerLists.setSelection(i); _keysInList = new ArrayList<MetaKeyBean>(); Cursor c = _database.getReadableDatabase().rawQuery( MessageFormat.format("SELECT * FROM {0} WHERE {1} = {2} ORDER BY KEYDESC", MetaKeyBean.GEN_TABLE_NAME, MetaKeyBean.GEN_FIELD_METALISTID, listId), EMPTY_ARGS); MetaKeyBean.Gen_populateFromCursor( c, _keysInList, MetaKeyBean.NEW); c.close(); ArrayList<String> keys = new ArrayList<String>(_keysInList.size()); int selectedOffset = 0; long lastSelectedKeyId = _canvasActivity.getConnection().getLastMetaKeyId(); for (int j=0; j<_keysInList.size(); j++) { MetaKeyBean key = _keysInList.get(j); keys.add( key.getKeyDesc()); if (lastSelectedKeyId==key.get_Id()) { selectedOffset = j; } } _spinnerKeysInList.setAdapter(new ArrayAdapter<String>(getOwnerActivity(), R.layout.key_list_entry, keys)); if (keys.size()>0) { _spinnerKeysInList.setSelection(selectedOffset); _currentKeyBean = new MetaKeyBean(_keysInList.get(selectedOffset)); } else { _currentKeyBean = new MetaKeyBean(listId, 0, MetaKeyBean.allKeys.get(0)); } updateDialogForCurrentKey(); _textListName.setText(list.getName()); break; } } _listId = listId; } } private void updateDialogForCurrentKey() { int flags = _currentKeyBean.getMetaFlags(); _checkAlt.setChecked(0 != (flags & (RemoteKeyboard.ALT_MASK | RemoteKeyboard.RALT_MASK))); _checkShift.setChecked(0 != (flags & (RemoteKeyboard.SHIFT_MASK | RemoteKeyboard.RSHIFT_MASK))); _checkCtrl.setChecked(0 != (flags & (RemoteKeyboard.CTRL_MASK | RemoteKeyboard.RCTRL_MASK))); _checkSuper.setChecked(0 != (flags & (RemoteKeyboard.SUPER_MASK | RemoteKeyboard.RSUPER_MASK))); MetaKeyBase base = null; if (_currentKeyBean.isMouseClick()) { base = MetaKeyBean.keysByMouseButton.get(_currentKeyBean.getMouseButtons()); } else { base = MetaKeyBean.keysByKeySym.get(_currentKeyBean.getKeySym()); } if (base != null) { int index = Collections.binarySearch(MetaKeyBean.allKeys,base); if (index >= 0) { _spinnerKeySelect.setSelection(index); } } _textKeyDesc.setText(_currentKeyBean.getKeyDesc()); } public void setConnection(ConnectionBean conn) { if ( _connection != conn) { _connection = conn; setMetaKeyList(); } } void setListSpinner() { ArrayList<String> listNames = new ArrayList<String>(_lists.size()); for (int i=0; i<_lists.size(); ++i) { MetaList l = _lists.get(i); listNames.add(l.getName()); } _spinnerLists.setAdapter(new ArrayAdapter<String>(getOwnerActivity(), R.layout.key_list_entry, listNames)); } /** * @author Michael A. MacDonald * */ class MetaCheckListener implements OnCheckedChangeListener { private int _mask; MetaCheckListener(int mask) { _mask = mask; } /* (non-Javadoc) * @see android.widget.CompoundButton.OnCheckedChangeListener#onCheckedChanged(android.widget.CompoundButton, boolean) */ @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { _currentKeyBean.setMetaFlags(_currentKeyBean.getMetaFlags() | _mask); } else { _currentKeyBean.setMetaFlags(_currentKeyBean.getMetaFlags() & ~_mask); } _textKeyDesc.setText(_currentKeyBean.getKeyDesc()); } } }