Android Open Source - p1keyboard Bluez I M E






From Project

Back to project page p1keyboard.

License

The source code is released under:

GNU Lesser General Public License

If you think the Android project p1keyboard listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/* Copyright (C) 2011, Kenneth Skovhede
 * http://www.hexad.dk, opensource@hexad.dk
 * /* w ww. j a va2s.  c  o  m*/
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
package mobi.omegacentauri.p1keyboard;

import mobi.omegacentauri.p1keyboard.R;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.inputmethodservice.InputMethodService;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.Toast;

public class BluezIME extends InputMethodService {

  private static final String SESSION_ID = "mobi.omegacentauri.p1keyboard.ime.controller";
  
  private static final boolean D = false;
  private static final String LOG_NAME = "BluezIME:BluezInput";
  private final String[] m_connectedIds = new String[Preferences.MAX_NO_OF_CONTROLLERS];
  private Preferences m_prefs;
  
  private NotificationManager m_notificationManager;
  private Notification m_notification;
  private int[][] m_keyMappingCache = null;
  private int[][] m_metaKeyMappingCache = null;
  
  private PowerManager.WakeLock m_wakelock = null;
  private int m_wakelocktype = 0;
  
  @Override
  public void onCreate() {
    super.onCreate();
    m_prefs = new Preferences(this);
    
    m_notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
    m_notification = new Notification(R.drawable.icon, getString(R.string.app_name), System.currentTimeMillis());
    m_keyMappingCache = new int[Preferences.MAX_NO_OF_CONTROLLERS][Math.max(FutureKeyCodes.FUTURE_MAX_KEYCODE, KeyEvent.getMaxKeyCode()) + 1];
    m_metaKeyMappingCache = new int[Preferences.MAX_NO_OF_CONTROLLERS][m_keyMappingCache[0].length];
    for(int i = 0; i < m_keyMappingCache.length; i++) {
      for(int j = 0; j < m_keyMappingCache[i].length; j++) {
        m_keyMappingCache[i][j] = -1;
        m_metaKeyMappingCache[i][j] = -1;
      }
    }
        
    setNotificationText(getString(R.string.ime_starting));
    acquireWakeLock();

        registerReceiver(connectReceiver, new IntentFilter(BluezService.EVENT_CONNECTED));
        registerReceiver(connectingReceiver, new IntentFilter(BluezService.EVENT_CONNECTING));
        registerReceiver(disconnectReceiver, new IntentFilter(BluezService.EVENT_DISCONNECTED));
        registerReceiver(errorReceiver, new IntentFilter(BluezService.EVENT_ERROR));
        registerReceiver(preferenceChangedHandler, new IntentFilter(Preferences.PREFERENCES_UPDATED));
        registerReceiver(activityHandler, new IntentFilter(BluezService.EVENT_KEYPRESS));
        registerReceiver(activityHandler, new IntentFilter(BluezService.EVENT_DIRECTIONALCHANGE));
      registerReceiver(bluetoothStateMonitor, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
  }
  
  private void setNotificationText(CharSequence message) {
    Intent i = new Intent(this, BluezIMESettings.class);
    PendingIntent pi = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
    m_notification.setLatestEventInfo(this, getString(R.string.app_name), message, pi);
    m_notificationManager.notify(1, m_notification);
  }
  
  private void acquireWakeLock() {
    if (m_wakelock == null) {
      int wakelocktype = m_prefs.getWakeLock();
      if (wakelocktype != Preferences.NO_WAKE_LOCK) {
        try 
        { 
          m_wakelock = ((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(wakelocktype, "mobi.omegacentauri.p1keyboard.WakeLock"); 
          m_wakelock.acquire();
          m_wakelocktype = wakelocktype;
        }
        catch (Throwable e) {
          Log.e(LOG_NAME, e.getMessage());
        }
        
      }
    }
  }
  
  private void releaseWakeLock() {
    if (m_wakelock != null) {
      m_wakelock.release();
      m_wakelock = null;
      m_wakelocktype = 0;
    }
  }
  
  private int getConnectedCount() {
    int count = 0;
    for(int i = 0; i < m_connectedIds.length; i++)
      if (m_connectedIds[i] != null)
        count++;
    
    return count;
  }

  @Override
  public View onCreateInputView() {
    super.onCreateInputView();
    return null;
  }

  @Override
  public View onCreateCandidatesView() {
    super.onCreateCandidatesView();
    return null;
  }
  
  @Override
  public void onStartInputView(EditorInfo info, boolean restarting) {
    super.onStartInputView(info, restarting);
    
    Log.d(LOG_NAME, "Start input view");

        if (getConnectedCount() != m_prefs.getControllerCount())
          connect();
  }

  @Override
  public void onStartInput(EditorInfo attribute, boolean restarting) {
    super.onStartInput(attribute, restarting);
    
    //Reconnect if we lost connection
    if (getConnectedCount() != m_prefs.getControllerCount())
      connect();
  }
  
  private void connect() {
      Log.d(LOG_NAME, "Connecting");
      
      if (m_prefs.getManageBluetooth()) {
        try {
          if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
              //Unfortunately this does not work because there is no view
              // associated with this IME, and thus we cannot show popup dialogs
            //ImprovedBluetoothDevice.ActivateBluetooth(this, m_inputView);
            
            //NOTE: This violates the guidelines that state that the user should 
            // be asked explicitly before turning BT on 
            BluetoothAdapter.getDefaultAdapter().enable();
  
            Toast.makeText(this, this.getString(R.string.enabling_bluetooth), Toast.LENGTH_SHORT).show();
            setNotificationText(this.getString(R.string.enabling_bluetooth));
  
            //We will connect when BT is on
            return;
          }
        } catch (Exception ex) {
          Log.e(LOG_NAME, "Failed to activate bluetooth: " + ex.getMessage());
        }
      }
      
      for(int i = 0; i < m_prefs.getControllerCount(); i++) {
        String address = m_prefs.getSelectedDeviceAddress(i);
        String driver = m_prefs.getSelectedDriverName(i);
  
        Intent intent = new Intent(this, BluezService.class);
        intent.setAction(BluezService.REQUEST_CONNECT);
        intent.putExtra(BluezService.REQUEST_CONNECT_ADDRESS, address);
        intent.putExtra(BluezService.REQUEST_CONNECT_DRIVER, driver);
        intent.putExtra(BluezService.SESSION_ID, SESSION_ID + i);
        intent.putExtra(BluezService.REQUEST_CONNECT_CREATE_NOTIFICATION, false);
      startService(intent);
      }
  }
  
  @Override
  public void onFinishInput() {
        super.onFinishInput();

        Log.d(LOG_NAME, "Finish input view");
  }
  
  @Override
  public void onDestroy() {
    super.onDestroy();
    
    Log.d(LOG_NAME, "Destroy IME");
    
    m_notificationManager.cancel(1);
    
    if (getConnectedCount() > 0) {
      Log.d(LOG_NAME, "Disconnecting");

      for(int i = 0; i < m_connectedIds.length; i++) {
        if (m_connectedIds[i] != null) {
          Intent intent = new Intent(this, BluezService.class);
          intent.setAction(BluezService.REQUEST_DISCONNECT);
          intent.putExtra(BluezService.SESSION_ID, m_connectedIds[i]);
          startService(intent);
        }
      }
    }
    
    releaseWakeLock();
    
        unregisterReceiver(connectReceiver);
        unregisterReceiver(connectingReceiver);
        unregisterReceiver(disconnectReceiver);
        unregisterReceiver(errorReceiver);
        unregisterReceiver(preferenceChangedHandler);
        unregisterReceiver(activityHandler);
        unregisterReceiver(bluetoothStateMonitor);
        
        connectReceiver = null;
        connectingReceiver = null;
        disconnectReceiver = null;
        activityHandler = null;
        errorReceiver = null;
        preferenceChangedHandler = null;
        bluetoothStateMonitor = null;
        
        if (m_prefs.getManageBluetooth()) {
          try {
            if (BluetoothAdapter.getDefaultAdapter().isEnabled()) {
              Toast.makeText(this, this.getString(R.string.disabling_bluetooth), Toast.LENGTH_SHORT).show();
              BluetoothAdapter.getDefaultAdapter().disable();
            }
          } catch (Exception ex) {
            Log.e(LOG_NAME, "Failed to turn BT off: " + ex.getMessage());
          }
        }
  }
  
  private BroadcastReceiver connectingReceiver =  new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      String sid = intent.getStringExtra(BluezService.SESSION_ID);
      if (sid == null || !sid.startsWith(SESSION_ID))
        return;
      
      String address = intent.getStringExtra(BluezService.EVENT_CONNECTING_ADDRESS);
      Toast.makeText(BluezIME.this, String.format(getString(R.string.ime_connecting), address), Toast.LENGTH_SHORT).show();
      setNotificationText(String.format(getString(R.string.ime_connecting), address));
    }
  };

  private BroadcastReceiver connectReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      String sid = intent.getStringExtra(BluezService.SESSION_ID);
      if (sid == null || !sid.startsWith(SESSION_ID))
        return;

      int controllerNo = -1;
      try { controllerNo = Integer.parseInt(sid.substring(SESSION_ID.length())); }
      catch (Throwable t) { if (D) Log.w(LOG_NAME, "Failed to parse connectId: " + sid); }

      if (controllerNo < 0 || controllerNo >= m_connectedIds.length)
        return;
      
      if (D) Log.d(LOG_NAME, "Connect received");
      Toast.makeText(context, String.format(context.getString(R.string.connected_to_device_message), intent.getStringExtra(BluezService.EVENT_CONNECTED_ADDRESS)), Toast.LENGTH_SHORT).show();
      m_connectedIds[controllerNo] = sid;
      setNotificationText(String.format(getString(R.string.ime_connected), intent.getStringExtra(BluezService.EVENT_CONNECTED_ADDRESS)));
    }
  };
  
  private BroadcastReceiver disconnectReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      String sid = intent.getStringExtra(BluezService.SESSION_ID);
      if (sid == null || !sid.startsWith(SESSION_ID))
        return;

      Log.d(LOG_NAME, "Disconnect received");
      Toast.makeText(context, String.format(context.getString(R.string.disconnected_from_device_message), intent.getStringExtra(BluezService.EVENT_DISCONNECTED_ADDRESS)), Toast.LENGTH_SHORT).show();
      for(int i = 0; i < m_connectedIds.length; i++) {
        if (sid.equals(m_connectedIds[i]))
          m_connectedIds[i] = null;
      }
      
      setNotificationText(getString(R.string.ime_disconnected));
    }
  };
  
  private BroadcastReceiver errorReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      String sid = intent.getStringExtra(BluezService.SESSION_ID);
      if (sid == null || !sid.startsWith(SESSION_ID))
        return;

      if (D) Log.d(LOG_NAME, "Error received");
      Toast.makeText(context, String.format(context.getString(R.string.error_message_generic), intent.getStringExtra(BluezService.EVENT_ERROR_SHORT)), Toast.LENGTH_SHORT).show();
      setNotificationText(String.format(getString(R.string.ime_error), intent.getStringExtra(BluezService.EVENT_ERROR_SHORT)));
    }
  };

  private BroadcastReceiver preferenceChangedHandler = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {

      //Clear the key mapping cache
      for(int i = 0; i < m_keyMappingCache.length; i++) {
        for(int j = 0; j < m_keyMappingCache[i].length; j++) {
          m_keyMappingCache[i][j] = -1;
          m_metaKeyMappingCache[i][j] = -1;
        }
      }
      
      if (getConnectedCount() > 0)
        connect();
      
      if (m_wakelocktype != m_prefs.getWakeLock()) {
        releaseWakeLock();
        acquireWakeLock();
      }
    }
  };

  private BroadcastReceiver activityHandler = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      String sid = intent.getStringExtra(BluezService.SESSION_ID);
      if (sid == null || !sid.startsWith(SESSION_ID))
        return;
      

      if (D) Log.d(LOG_NAME, "Update event received");
      
      try {
        int controllerNo = Integer.parseInt(sid.substring(SESSION_ID.length()));

        InputConnection ic = getCurrentInputConnection();
        long eventTime = SystemClock.uptimeMillis();

        if (intent.getAction().equals(BluezService.EVENT_KEYPRESS)) {
          int action = intent.getIntExtra(BluezService.EVENT_KEYPRESS_ACTION, KeyEvent.ACTION_DOWN);
          int key = intent.getIntExtra(BluezService.EVENT_KEYPRESS_KEY, 0);
          int metakey = intent.getIntExtra(BluezService.EVENT_KEYPRESS_MODIFIERS, 0);
          int special = intent.getIntExtra(BluezService.EVENT_KEYPRESS_SPECIAL, 0);
          
          if (special > 0) {
            switch(special) {
            case BluezService.SPECIAL_COPY:
              ic.performContextMenuAction(android.R.id.copy);
              break;
            case BluezService.SPECIAL_CUT:
              ic.performContextMenuAction(android.R.id.cut);
              break;
            case BluezService.SPECIAL_PASTE:
              ic.performContextMenuAction(android.R.id.paste);
              break;
            case BluezService.SPECIAL_SELECT_ALL:
              ic.performContextMenuAction(android.R.id.selectAll);
              break;
            case BluezService.SPECIAL_HOME:
              Intent i = new Intent(Intent.ACTION_MAIN);
              i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
              i.addCategory(Intent.CATEGORY_HOME);
              startActivity(i);
              break;
            case BluezService.SPECIAL_UNICODE:
              ic.commitText(String.valueOf(Character.toChars(key)),1 );
            }
            
            return;
          }

          //This construct ensures that we can perform lock free
          // access to m_keyMappingCache and never risk sending -1 
          // as the keyCode
          if (controllerNo >= m_keyMappingCache.length || key >= m_keyMappingCache[controllerNo].length) {
            Log.e(LOG_NAME, "Key reported by driver: " + key + ", size of keymapping array: " + m_keyMappingCache[0].length + ", controller no " + controllerNo + ", expected controllers: " + m_keyMappingCache.length);
          } else {
            int translatedKey = m_keyMappingCache[controllerNo][key];
            if (translatedKey == -1) {
              translatedKey = m_prefs.getKeyMapping(key, controllerNo);
              m_keyMappingCache[controllerNo][key] = translatedKey;
            }
            
            //TODO: This conflicts slightly with keyboard, because we have no way of knowing.
            // if the mapping is deliberately without a meta key, or just default.
            //So if we have a case where the controller sends a meta modifier, we
            // do not apply the user chosen override.
            //Currently this is not a problem, because only the keyboard HID sends the modifier,
            // and the user cannot set the modifier anyway
            if (metakey == 0) {
              metakey = m_metaKeyMappingCache[controllerNo][key];
              if (metakey == -1) {
                metakey = m_prefs.getMetaKeyMapping(key, controllerNo);
                m_metaKeyMappingCache[controllerNo][key] = metakey;
              }
            }

            if (D) Log.d(LOG_NAME, "Sending key event: " + (action == KeyEvent.ACTION_DOWN ? "Down" : "Up") + " - " + key + " - " + metakey);
            ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, action, translatedKey, 0, metakey, 0, 0, KeyEvent.FLAG_SOFT_KEYBOARD));
          }
        }
      } catch (Exception ex) {
        Log.e(LOG_NAME, "Failed to send key events: " + ex.toString());
      }
    }
  };
  
  private BroadcastReceiver bluetoothStateMonitor = new BroadcastReceiver() {
    
    @Override
    public void onReceive(Context context, Intent intent) {
      int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
      if (state == BluetoothAdapter.STATE_ON) {
        if (getConnectedCount() == 0)
          connect();
      }
    }
  };
}




Java Source Code List

mobi.omegacentauri.p1keyboard.BGP100Reader.java
mobi.omegacentauri.p1keyboard.BluezDriverInterface.java
mobi.omegacentauri.p1keyboard.BluezForegroundService.java
mobi.omegacentauri.p1keyboard.BluezIMESettings.java
mobi.omegacentauri.p1keyboard.BluezIME.java
mobi.omegacentauri.p1keyboard.BluezService.java
mobi.omegacentauri.p1keyboard.ButtonConfiguration.java
mobi.omegacentauri.p1keyboard.DataDumpReader.java
mobi.omegacentauri.p1keyboard.DeviceScanActivity.java
mobi.omegacentauri.p1keyboard.FutureKeyCodes.java
mobi.omegacentauri.p1keyboard.GameStopReader.java
mobi.omegacentauri.p1keyboard.HIDKeyboard.java
mobi.omegacentauri.p1keyboard.HIDReaderBase.java
mobi.omegacentauri.p1keyboard.HIDipega.java
mobi.omegacentauri.p1keyboard.ImprovedBluetoothDevice.java
mobi.omegacentauri.p1keyboard.PalmOneWirelessKeyboardReader.java
mobi.omegacentauri.p1keyboard.PhonejoyReader.java
mobi.omegacentauri.p1keyboard.Preferences.java
mobi.omegacentauri.p1keyboard.RfcommReader.java
mobi.omegacentauri.p1keyboard.WiimoteReader.java
mobi.omegacentauri.p1keyboard.ZeemoteReader.java
mobi.omegacentauri.p1keyboard.iCadeReader.java
mobi.omegacentauri.p1keyboard.iControlPadReader.java