Android Tutorial - Hardware
- Bluetooth
- Storage
- Joystick
- Display
- Gps
- PowerManager
- SensorManager
- USB
- Vibrator
Bluetooth Adapter Util
//package com.hustaty.android.bluetooth; import android.bluetooth.BluetoothAdapter; import android.util.Log; /** * * @author Slavomir Hustaty * */ public class BluetoothAdapterUtil { // logger entry private final static String LOG_TAG = BluetoothAdapterUtil.class.getSimpleName(); private static final int LOOP_WAIT_TIME = 500; private static final int MAX_REPETITIONS_COUNT = 30; public static void startBluetoothAdapter() { try { waitUntilBluetoothAdapterIsInState(BluetoothAdapter.STATE_ON, MAX_REPETITIONS_COUNT); } catch (Exception e) { Log.d(LOG_TAG, e.getMessage()); } } public static void stopBluetoothAdapter() { try { waitUntilBluetoothAdapterIsInState(BluetoothAdapter.STATE_OFF, MAX_REPETITIONS_COUNT); } catch (Exception e) { Log.d(LOG_TAG, e.getMessage()); } } /** * waits until BluetoothAdapter is in required state * * @param state * @throws Exception */ private static void waitUntilBluetoothAdapterIsInState(int state, int remainingLoops) throws Exception { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if(remainingLoops > 0) { switch (state) { case BluetoothAdapter.STATE_OFF: if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) { waitNMillis(LOOP_WAIT_TIME); waitUntilBluetoothAdapterIsInState(BluetoothAdapter.STATE_OFF, remainingLoops - 1); } else if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) { Log.d(LOG_TAG, "BluetoothAdapter is in state OFF"); return; } else { // ensure we're not waiting for Godot ;) bluetoothAdapter.disable(); waitUntilBluetoothAdapterIsInState(BluetoothAdapter.STATE_OFF, remainingLoops - 1); } break; case BluetoothAdapter.STATE_ON: if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_TURNING_ON) { waitNMillis(LOOP_WAIT_TIME); waitUntilBluetoothAdapterIsInState(BluetoothAdapter.STATE_ON, remainingLoops - 1); } else if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { Log.d(LOG_TAG, "BluetoothAdapter is in state ON"); return; } else { // ensure we're not waiting for Godot ;) bluetoothAdapter.enable(); waitUntilBluetoothAdapterIsInState(BluetoothAdapter.STATE_ON, remainingLoops - 1); } break; default: throw new Exception( "You can check only final states of BluetoothAdapter(STATE_ON|STATE_OFF)."); } } else { Log.e(LOG_TAG, "Error on waiting while BluetoothAdapter changes state to #" + state + ". "); return; } } /** * delay N milliseconds * * @param n */ private static void waitNMillis(long n) { try { Thread.sleep(n); } catch (InterruptedException e) { Log.e(LOG_TAG, "#waitNMillis() " + e.getMessage()); } } }
Bluetooth Chat
package com.example.android.BluetoothChat; import java.util.Set; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; /** * This Activity appears as a dialog. It lists any paired devices and * devices detected in the area after discovery. When a device is chosen * by the user, the MAC address of the device is sent back to the parent * Activity in the result Intent. */ public class DeviceListActivity extends Activity { // Debugging private static final String TAG = "DeviceListActivity"; private static final boolean D = true; // Return Intent extra public static String EXTRA_DEVICE_ADDRESS = "device_address"; // Member fields private BluetoothAdapter mBtAdapter; private ArrayAdapter<String> mPairedDevicesArrayAdapter; private ArrayAdapter<String> mNewDevicesArrayAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Setup the window requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.device_list); // Set result CANCELED in case the user backs out setResult(Activity.RESULT_CANCELED); // Initialize the button to perform device discovery Button scanButton = (Button) findViewById(R.id.button_scan); scanButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { doDiscovery(); v.setVisibility(View.GONE); } }); // Initialize array adapters. One for already paired devices and // one for newly discovered devices mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name); mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name); // Find and set up the ListView for paired devices ListView pairedListView = (ListView) findViewById(R.id.paired_devices); pairedListView.setAdapter(mPairedDevicesArrayAdapter); pairedListView.setOnItemClickListener(mDeviceClickListener); // Find and set up the ListView for newly discovered devices ListView newDevicesListView = (ListView) findViewById(R.id.new_devices); newDevicesListView.setAdapter(mNewDevicesArrayAdapter); newDevicesListView.setOnItemClickListener(mDeviceClickListener); // Register for broadcasts when a device is discovered IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver, filter); // Register for broadcasts when discovery has finished filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver, filter); // Get the local Bluetooth adapter mBtAdapter = BluetoothAdapter.getDefaultAdapter(); // Get a set of currently paired devices Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices(); // If there are paired devices, add each one to the ArrayAdapter if (pairedDevices.size() > 0) { findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); for (BluetoothDevice device : pairedDevices) { mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } else { String noDevices = getResources().getText(R.string.none_paired).toString(); mPairedDevicesArrayAdapter.add(noDevices); } } @Override protected void onDestroy() { super.onDestroy(); // Make sure we're not doing discovery anymore if (mBtAdapter != null) { mBtAdapter.cancelDiscovery(); } // Unregister broadcast listeners this.unregisterReceiver(mReceiver); } /** * Start device discover with the BluetoothAdapter */ private void doDiscovery() { if (D) Log.d(TAG, "doDiscovery()"); // Indicate scanning in the title setProgressBarIndeterminateVisibility(true); setTitle(R.string.scanning); // Turn on sub-title for new devices findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE); // If we're already discovering, stop it if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } // Request discover from BluetoothAdapter mBtAdapter.startDiscovery(); } // The on-click listener for all devices in the ListViews private OnItemClickListener mDeviceClickListener = new OnItemClickListener() { public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) { // Cancel discovery because it's costly and we're about to connect mBtAdapter.cancelDiscovery(); // Get the device MAC address, which is the last 17 chars in the View String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); // Create the result Intent and include the MAC address Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); // Set result and finish this Activity setResult(Activity.RESULT_OK, intent); finish(); } }; // The BroadcastReceiver that listens for discovered devices and // changes the title when discovery is finished private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // If it's already paired, skip it, because it's been listed already if (device.getBondState() != BluetoothDevice.BOND_BONDED) { mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } // When discovery is finished, change the Activity title } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); setTitle(R.string.select_device); if (mNewDevicesArrayAdapter.getCount() == 0) { String noDevices = getResources().getText(R.string.none_found).toString(); mNewDevicesArrayAdapter.add(noDevices); } } } }; } package com.example.android.BluetoothChat; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; /** * This class does all the work for setting up and managing Bluetooth * connections with other devices. It has a thread that listens for * incoming connections, a thread for connecting with a device, and a * thread for performing data transmissions when connected. */ public class BluetoothChatService { // Debugging private static final String TAG = "BluetoothChatService"; private static final boolean D = true; // Name for the SDP record when creating server socket private static final String NAME_SECURE = "BluetoothChatSecure"; private static final String NAME_INSECURE = "BluetoothChatInsecure"; // Unique UUID for this application private static final UUID MY_UUID_SECURE = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"); private static final UUID MY_UUID_INSECURE = UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66"); // Member fields private final BluetoothAdapter mAdapter; private final Handler mHandler; private AcceptThread mSecureAcceptThread; private AcceptThread mInsecureAcceptThread; private ConnectThread mConnectThread; private ConnectedThread mConnectedThread; private int mState; // Constants that indicate the current connection state public static final int STATE_NONE = 0; // we're doing nothing public static final int STATE_LISTEN = 1; // now listening for incoming connections public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection public static final int STATE_CONNECTED = 3; // now connected to a remote device /** * Constructor. Prepares a new BluetoothChat session. * @param context The UI Activity Context * @param handler A Handler to send messages back to the UI Activity */ public BluetoothChatService(Context context, Handler handler) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mState = STATE_NONE; mHandler = handler; } /** * Set the current state of the chat connection * @param state An integer defining the current connection state */ private synchronized void setState(int state) { if (D) Log.d(TAG, "setState() " + mState + " -> " + state); mState = state; // Give the new state to the Handler so the UI Activity can update mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget(); } /** * Return the current connection state. */ public synchronized int getState() { return mState; } /** * Start the chat service. Specifically start AcceptThread to begin a * session in listening (server) mode. Called by the Activity onResume() */ public synchronized void start() { if (D) Log.d(TAG, "start"); // Cancel any thread attempting to make a connection if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} // Cancel any thread currently running a connection if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} setState(STATE_LISTEN); // Start the thread to listen on a BluetoothServerSocket if (mSecureAcceptThread == null) { mSecureAcceptThread = new AcceptThread(true); mSecureAcceptThread.start(); } if (mInsecureAcceptThread == null) { mInsecureAcceptThread = new AcceptThread(false); mInsecureAcceptThread.start(); } } /** * Start the ConnectThread to initiate a connection to a remote device. * @param device The BluetoothDevice to connect * @param secure Socket Security type - Secure (true) , Insecure (false) */ public synchronized void connect(BluetoothDevice device, boolean secure) { if (D) Log.d(TAG, "connect to: " + device); // Cancel any thread attempting to make a connection if (mState == STATE_CONNECTING) { if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} } // Cancel any thread currently running a connection if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} // Start the thread to connect with the given device mConnectThread = new ConnectThread(device, secure); mConnectThread.start(); setState(STATE_CONNECTING); } /** * Start the ConnectedThread to begin managing a Bluetooth connection * @param socket The BluetoothSocket on which the connection was made * @param device The BluetoothDevice that has been connected */ public synchronized void connected(BluetoothSocket socket, BluetoothDevice device, final String socketType) { if (D) Log.d(TAG, "connected, Socket Type:" + socketType); // Cancel the thread that completed the connection if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} // Cancel any thread currently running a connection if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;} // Cancel the accept thread because we only want to connect to one device if (mSecureAcceptThread != null) { mSecureAcceptThread.cancel(); mSecureAcceptThread = null; } if (mInsecureAcceptThread != null) { mInsecureAcceptThread.cancel(); mInsecureAcceptThread = null; } // Start the thread to manage the connection and perform transmissions mConnectedThread = new ConnectedThread(socket, socketType); mConnectedThread.start(); // Send the name of the connected device back to the UI Activity Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME); Bundle bundle = new Bundle(); bundle.putString(BluetoothChat.DEVICE_NAME, device.getName()); msg.setData(bundle); mHandler.sendMessage(msg); setState(STATE_CONNECTED); } /** * Stop all threads */ public synchronized void stop() { if (D) Log.d(TAG, "stop"); if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } if (mSecureAcceptThread != null) { mSecureAcceptThread.cancel(); mSecureAcceptThread = null; } if (mInsecureAcceptThread != null) { mInsecureAcceptThread.cancel(); mInsecureAcceptThread = null; } setState(STATE_NONE); } /** * Write to the ConnectedThread in an unsynchronized manner * @param out The bytes to write * @see ConnectedThread#write(byte[]) */ public void write(byte[] out) { // Create temporary object ConnectedThread r; // Synchronize a copy of the ConnectedThread synchronized (this) { if (mState != STATE_CONNECTED) return; r = mConnectedThread; } // Perform the write unsynchronized r.write(out); } /** * Indicate that the connection attempt failed and notify the UI Activity. */ private void connectionFailed() { // Send a failure message back to the Activity Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString(BluetoothChat.TOAST, "Unable to connect device"); msg.setData(bundle); mHandler.sendMessage(msg); // Start the service over to restart listening mode BluetoothChatService.this.start(); } /** * Indicate that the connection was lost and notify the UI Activity. */ private void connectionLost() { // Send a failure message back to the Activity Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST); Bundle bundle = new Bundle(); bundle.putString(BluetoothChat.TOAST, "Device connection was lost"); msg.setData(bundle); mHandler.sendMessage(msg); // Start the service over to restart listening mode BluetoothChatService.this.start(); } /** * This thread runs while listening for incoming connections. It behaves * like a server-side client. It runs until a connection is accepted * (or until cancelled). */ private class AcceptThread extends Thread { // The local server socket private final BluetoothServerSocket mmServerSocket; private String mSocketType; public AcceptThread(boolean secure) { BluetoothServerSocket tmp = null; mSocketType = secure ? "Secure":"Insecure"; // Create a new listening server socket try { if (secure) { tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE); } else { tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord( NAME_INSECURE, MY_UUID_INSECURE); } } catch (IOException e) { Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e); } mmServerSocket = tmp; } public void run() { if (D) Log.d(TAG, "Socket Type: " + mSocketType + "BEGIN mAcceptThread" + this); setName("AcceptThread" + mSocketType); BluetoothSocket socket = null; // Listen to the server socket if we're not connected while (mState != STATE_CONNECTED) { try { // This is a blocking call and will only return on a // successful connection or an exception socket = mmServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e); break; } // If a connection was accepted if (socket != null) { synchronized (BluetoothChatService.this) { switch (mState) { case STATE_LISTEN: case STATE_CONNECTING: // Situation normal. Start the connected thread. connected(socket, socket.getRemoteDevice(), mSocketType); break; case STATE_NONE: case STATE_CONNECTED: // Either not ready or already connected. Terminate new socket. try { socket.close(); } catch (IOException e) { Log.e(TAG, "Could not close unwanted socket", e); } break; } } } } if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType); } public void cancel() { if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this); try { mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e); } } } /** * This thread runs while attempting to make an outgoing connection * with a device. It runs straight through; the connection either * succeeds or fails. */ private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; private String mSocketType; public ConnectThread(BluetoothDevice device, boolean secure) { mmDevice = device; BluetoothSocket tmp = null; mSocketType = secure ? "Secure" : "Insecure"; // Get a BluetoothSocket for a connection with the // given BluetoothDevice try { if (secure) { tmp = device.createRfcommSocketToServiceRecord( MY_UUID_SECURE); } else { tmp = device.createInsecureRfcommSocketToServiceRecord( MY_UUID_INSECURE); } } catch (IOException e) { Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); } mmSocket = tmp; } public void run() { Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType); setName("ConnectThread" + mSocketType); // Always cancel discovery because it will slow down a connection mAdapter.cancelDiscovery(); // Make a connection to the BluetoothSocket try { // This is a blocking call and will only return on a // successful connection or an exception mmSocket.connect(); } catch (IOException e) { // Close the socket try { mmSocket.close(); } catch (IOException e2) { Log.e(TAG, "unable to close() " + mSocketType + " socket during connection failure", e2); } connectionFailed(); return; } // Reset the ConnectThread because we're done synchronized (BluetoothChatService.this) { mConnectThread = null; } // Start the connected thread connected(mmSocket, mmDevice, mSocketType); } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e); } } } /** * This thread runs during a connection with a remote device. * It handles all incoming and outgoing transmissions. */ private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket, String socketType) { Log.d(TAG, "create ConnectedThread: " + socketType); mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // Get the BluetoothSocket input and output streams try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { Log.e(TAG, "temp sockets not created", e); } mmInStream = tmpIn; mmOutStream = tmpOut; } public void run() { Log.i(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[1024]; int bytes; // Keep listening to the InputStream while connected while (true) { try { // Read from the InputStream bytes = mmInStream.read(buffer); // Send the obtained bytes to the UI Activity mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer) .sendToTarget(); } catch (IOException e) { Log.e(TAG, "disconnected", e); connectionLost(); // Start the service over to restart listening mode BluetoothChatService.this.start(); break; } } } /** * Write to the connected OutStream. * @param buffer The bytes to write */ public void write(byte[] buffer) { try { mmOutStream.write(buffer); // Share the sent message back to the UI Activity mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer) .sendToTarget(); } catch (IOException e) { Log.e(TAG, "Exception during write", e); } } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } } } package com.example.android.BluetoothChat; import android.app.ActionBar; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.view.inputmethod.EditorInfo; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; /** * This is the main Activity that displays the current chat session. */ public class BluetoothChat extends Activity { // Debugging private static final String TAG = "BluetoothChat"; private static final boolean D = true; // Message types sent from the BluetoothChatService Handler public static final int MESSAGE_STATE_CHANGE = 1; public static final int MESSAGE_READ = 2; public static final int MESSAGE_WRITE = 3; public static final int MESSAGE_DEVICE_NAME = 4; public static final int MESSAGE_TOAST = 5; // Key names received from the BluetoothChatService Handler public static final String DEVICE_NAME = "device_name"; public static final String TOAST = "toast"; // Intent request codes private static final int REQUEST_CONNECT_DEVICE_SECURE = 1; private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2; private static final int REQUEST_ENABLE_BT = 3; // Layout Views private ListView mConversationView; private EditText mOutEditText; private Button mSendButton; // Name of the connected device private String mConnectedDeviceName = null; // Array adapter for the conversation thread private ArrayAdapter<String> mConversationArrayAdapter; // String buffer for outgoing messages private StringBuffer mOutStringBuffer; // Local Bluetooth adapter private BluetoothAdapter mBluetoothAdapter = null; // Member object for the chat services private BluetoothChatService mChatService = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(D) Log.e(TAG, "+++ ON CREATE +++"); // Set up the window layout setContentView(R.layout.main); // Get local Bluetooth adapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // If the adapter is null, then Bluetooth is not supported if (mBluetoothAdapter == null) { Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); finish(); return; } } @Override public void onStart() { super.onStart(); if(D) Log.e(TAG, "++ ON START ++"); // If BT is not on, request that it be enabled. // setupChat() will then be called during onActivityResult if (!mBluetoothAdapter.isEnabled()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, REQUEST_ENABLE_BT); // Otherwise, setup the chat session } else { if (mChatService == null) setupChat(); } } @Override public synchronized void onResume() { super.onResume(); if(D) Log.e(TAG, "+ ON RESUME +"); // Performing this check in onResume() covers the case in which BT was // not enabled during onStart(), so we were paused to enable it... // onResume() will be called when ACTION_REQUEST_ENABLE activity returns. if (mChatService != null) { // Only if the state is STATE_NONE, do we know that we haven't started already if (mChatService.getState() == BluetoothChatService.STATE_NONE) { // Start the Bluetooth chat services mChatService.start(); } } } private void setupChat() { Log.d(TAG, "setupChat()"); // Initialize the array adapter for the conversation thread mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message); mConversationView = (ListView) findViewById(R.id.in); mConversationView.setAdapter(mConversationArrayAdapter); // Initialize the compose field with a listener for the return key mOutEditText = (EditText) findViewById(R.id.edit_text_out); mOutEditText.setOnEditorActionListener(mWriteListener); // Initialize the send button with a listener that for click events mSendButton = (Button) findViewById(R.id.button_send); mSendButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { // Send a message using content of the edit text widget TextView view = (TextView) findViewById(R.id.edit_text_out); String message = view.getText().toString(); sendMessage(message); } }); // Initialize the BluetoothChatService to perform bluetooth connections mChatService = new BluetoothChatService(this, mHandler); // Initialize the buffer for outgoing messages mOutStringBuffer = new StringBuffer(""); } @Override public synchronized void onPause() { super.onPause(); if(D) Log.e(TAG, "- ON PAUSE -"); } @Override public void onStop() { super.onStop(); if(D) Log.e(TAG, "-- ON STOP --"); } @Override public void onDestroy() { super.onDestroy(); // Stop the Bluetooth chat services if (mChatService != null) mChatService.stop(); if(D) Log.e(TAG, "--- ON DESTROY ---"); } private void ensureDiscoverable() { if(D) Log.d(TAG, "ensure discoverable"); if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent); } } /** * Sends a message. * @param message A string of text to send. */ private void sendMessage(String message) { // Check that we're actually connected before trying anything if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) { Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show(); return; } // Check that there's actually something to send if (message.length() > 0) { // Get the message bytes and tell the BluetoothChatService to write byte[] send = message.getBytes(); mChatService.write(send); // Reset out string buffer to zero and clear the edit text field mOutStringBuffer.setLength(0); mOutEditText.setText(mOutStringBuffer); } } // The action listener for the EditText widget, to listen for the return key private TextView.OnEditorActionListener mWriteListener = new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { // If the action is a key-up event on the return key, send the message if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) { String message = view.getText().toString(); sendMessage(message); } if(D) Log.i(TAG, "END onEditorAction"); return true; } }; private final void setStatus(int resId) { final ActionBar actionBar = getActionBar(); actionBar.setSubtitle(resId); } private final void setStatus(CharSequence subTitle) { final ActionBar actionBar = getActionBar(); actionBar.setSubtitle(subTitle); } // The Handler that gets information back from the BluetoothChatService private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_STATE_CHANGE: if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1); switch (msg.arg1) { case BluetoothChatService.STATE_CONNECTED: setStatus(getString(R.string.title_connected_to, mConnectedDeviceName)); mConversationArrayAdapter.clear(); break; case BluetoothChatService.STATE_CONNECTING: setStatus(R.string.title_connecting); break; case BluetoothChatService.STATE_LISTEN: case BluetoothChatService.STATE_NONE: setStatus(R.string.title_not_connected); break; } break; case MESSAGE_WRITE: byte[] writeBuf = (byte[]) msg.obj; // construct a string from the buffer String writeMessage = new String(writeBuf); mConversationArrayAdapter.add("Me: " + writeMessage); break; case MESSAGE_READ: byte[] readBuf = (byte[]) msg.obj; // construct a string from the valid bytes in the buffer String readMessage = new String(readBuf, 0, msg.arg1); mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage); break; case MESSAGE_DEVICE_NAME: // save the connected device's name mConnectedDeviceName = msg.getData().getString(DEVICE_NAME); Toast.makeText(getApplicationContext(), "Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT).show(); break; case MESSAGE_TOAST: Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_SHORT).show(); break; } } }; public void onActivityResult(int requestCode, int resultCode, Intent data) { if(D) Log.d(TAG, "onActivityResult " + resultCode); switch (requestCode) { case REQUEST_CONNECT_DEVICE_SECURE: // When DeviceListActivity returns with a device to connect if (resultCode == Activity.RESULT_OK) { connectDevice(data, true); } break; case REQUEST_CONNECT_DEVICE_INSECURE: // When DeviceListActivity returns with a device to connect if (resultCode == Activity.RESULT_OK) { connectDevice(data, false); } break; case REQUEST_ENABLE_BT: // When the request to enable Bluetooth returns if (resultCode == Activity.RESULT_OK) { // Bluetooth is now enabled, so set up a chat session setupChat(); } else { // User did not enable Bluetooth or an error occurred Log.d(TAG, "BT not enabled"); Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show(); finish(); } } } private void connectDevice(Intent data, boolean secure) { // Get the device MAC address String address = data.getExtras() .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); // Get the BluetoothDevice object BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); // Attempt to connect to the device mChatService.connect(device, secure); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.option_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { Intent serverIntent = null; switch (item.getItemId()) { case R.id.secure_connect_scan: // Launch the DeviceListActivity to see devices and do scan serverIntent = new Intent(this, DeviceListActivity.class); startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE); return true; case R.id.insecure_connect_scan: // Launch the DeviceListActivity to see devices and do scan serverIntent = new Intent(this, DeviceListActivity.class); startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE); return true; case R.id.discoverable: // Ensure this device is discoverable by others ensureDiscoverable(); return true; } return false; } } //res\layout\custom_title.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" > <TextView android:id="@+id/title_left_text" android:layout_alignParentLeft="true" android:ellipsize="end" android:singleLine="true" style="?android:attr/windowTitleStyle" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" /> <TextView android:id="@+id/title_right_text" android:layout_alignParentRight="true" android:ellipsize="end" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="match_parent" android:textColor="#fff" android:layout_weight="1" /> </RelativeLayout> //res\layout\device_list.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/title_paired_devices" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/title_paired_devices" android:visibility="gone" android:background="#666" android:textColor="#fff" android:paddingLeft="5dp" /> <ListView android:id="@+id/paired_devices" android:layout_width="match_parent" android:layout_height="wrap_content" android:stackFromBottom="true" android:layout_weight="1" /> <TextView android:id="@+id/title_new_devices" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/title_other_devices" android:visibility="gone" android:background="#666" android:textColor="#fff" android:paddingLeft="5dp" /> <ListView android:id="@+id/new_devices" android:layout_width="match_parent" android:layout_height="wrap_content" android:stackFromBottom="true" android:layout_weight="2" /> <Button android:id="@+id/button_scan" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/button_scan" /> </LinearLayout> //res\layout\device_name.xml <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:padding="5dp" /> //res\layout\main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android:id="@+id/in" android:layout_width="match_parent" android:layout_height="match_parent" android:stackFromBottom="true" android:transcriptMode="alwaysScroll" android:layout_weight="1" /> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" > <EditText android:id="@+id/edit_text_out" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="bottom" /> <Button android:id="@+id/button_send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send" /> </LinearLayout> </LinearLayout> //res\layout\message.xml <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:padding="5dp" /> //res\menu\option_menu.xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/secure_connect_scan" android:icon="@android:drawable/ic_menu_search" android:title="@string/secure_connect" android:showAsAction="ifRoom|withText" /> <item android:id="@+id/insecure_connect_scan" android:icon="@android:drawable/ic_menu_search" android:title="@string/insecure_connect" android:showAsAction="ifRoom|withText" /> <item android:id="@+id/discoverable" android:icon="@android:drawable/ic_menu_mylocation" android:title="@string/discoverable" android:showAsAction="ifRoom|withText" /> </menu> //res\values\strings.xml <?xml version="1.0" encoding="utf-8"?> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_name">Bluetooth Chat</string> <!-- BluetoothChat --> <string name="send">Send</string> <string name="not_connected">You are not connected to a device</string> <string name="bt_not_enabled_leaving">Bluetooth was not enabled. Leaving Bluetooth Chat.</string> <string name="title_connecting">connecting...</string> <string name="title_connected_to">connected to <xliff:g id="device_name">%1$s</xliff:g></string> <string name="title_not_connected">not connected</string> <!-- DeviceListActivity --> <string name="scanning">scanning for devices...</string> <string name="select_device">select a device to connect</string> <string name="none_paired">No devices have been paired</string> <string name="none_found">No devices found</string> <string name="title_paired_devices">Paired Devices</string> <string name="title_other_devices">Other Available Devices</string> <string name="button_scan">Scan for devices</string> <!-- Options Menu --> <string name="secure_connect">Connect a device - Secure</string> <string name="insecure_connect">Connect a device - Insecure</string> <string name="discoverable">Make discoverable</string> </resources>
External Storage Directory
package app.test; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class Test extends Activity { private EditText textBox; private static final int READ_BLOCK_SIZE = 100; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); InputStream is = this.getResources().openRawResource(R.raw.textfile); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String str = null; try { while ((str = br.readLine()) != null) { Toast.makeText(getBaseContext(), str, Toast.LENGTH_SHORT) .show(); } is.close(); br.close(); } catch (IOException e) { e.printStackTrace(); } textBox = (EditText) findViewById(R.id.txtText1); Button saveBtn = (Button) findViewById(R.id.btnSave); Button loadBtn = (Button) findViewById(R.id.btnLoad); saveBtn.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { String str = textBox.getText().toString(); try { /* * //---Internal Storage--- FileOutputStream fOut = * openFileOutput("textfile.txt", MODE_WORLD_READABLE); * */ File sdCard = Environment.getExternalStorageDirectory(); File directory = new File(sdCard.getAbsolutePath() + "/MyFiles"); directory.mkdirs(); File file = new File(directory, "textfile.txt"); FileOutputStream fOut = new FileOutputStream(file); OutputStreamWriter osw = new OutputStreamWriter(fOut); osw.write(str); osw.flush(); osw.close(); Toast.makeText(getBaseContext(),"File saved successfully!", Toast.LENGTH_SHORT).show(); textBox.setText(""); } catch (IOException ioe) { ioe.printStackTrace(); } } }); loadBtn.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { try { // ---SD Storage--- File sdCard = Environment.getExternalStorageDirectory(); File directory = new File(sdCard.getAbsolutePath() + "/MyFiles"); File file = new File(directory, "textfile.txt"); FileInputStream fIn = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(fIn); char[] inputBuffer = new char[READ_BLOCK_SIZE]; String s = ""; int charRead; while ((charRead = isr.read(inputBuffer)) > 0) { String readString = String.copyValueOf(inputBuffer, 0, charRead); s += readString; inputBuffer = new char[READ_BLOCK_SIZE]; } textBox.setText(s); Toast.makeText(getBaseContext(), "File loaded successfully!", Toast.LENGTH_SHORT) .show(); } catch (IOException ioe) { ioe.printStackTrace(); } } }); } } //raw/textfile.txt The quick brown fox jumps over the lazy dog // main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Please enter some text" /> <EditText android:id="@+id/txtText1" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/btnSave" android:text="Save" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/btnLoad" android:text="Load" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
A trivial joystick based physics game to demonstrate joystick handling.
package com.example.android.apis.view; import com.example.android.apis.R; import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.InputDevice; import android.view.InputEvent; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.InputDevice.MotionRange; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; public class GameControllerInput extends Activity { private static final String TAG = "GameControllerInput"; private SparseArray<InputDeviceState> mInputDeviceStates; private GameView mGame; private ListView mSummaryList; private SummaryAdapter mSummaryAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInputDeviceStates = new SparseArray<InputDeviceState>(); mSummaryAdapter = new SummaryAdapter(this, getResources()); setContentView(R.layout.game_controller_input); mGame = (GameView) findViewById(R.id.game); mSummaryList = (ListView) findViewById(R.id.summary); mSummaryList.setAdapter(mSummaryAdapter); mSummaryList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mSummaryAdapter.onItemClick(position); } }); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mGame.requestFocus(); } @Override public boolean dispatchKeyEvent(KeyEvent event) { // Update device state for visualization and logging. InputDeviceState state = getInputDeviceState(event); if (state != null) { switch (event.getAction()) { case KeyEvent.ACTION_DOWN: if (state.onKeyDown(event)) { mSummaryAdapter.show(state); } break; case KeyEvent.ACTION_UP: if (state.onKeyUp(event)) { mSummaryAdapter.show(state); } break; } } return super.dispatchKeyEvent(event); } @Override public boolean dispatchGenericMotionEvent(MotionEvent event) { // Check that the event came from a joystick since a generic motion event // could be almost anything. if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 && event.getAction() == MotionEvent.ACTION_MOVE) { // Update device state for visualization and logging. InputDeviceState state = getInputDeviceState(event); if (state != null && state.onJoystickMotion(event)) { mSummaryAdapter.show(state); } } return super.dispatchGenericMotionEvent(event); } private InputDeviceState getInputDeviceState(InputEvent event) { final int deviceId = event.getDeviceId(); InputDeviceState state = mInputDeviceStates.get(deviceId); if (state == null) { final InputDevice device = event.getDevice(); if (device == null) { return null; } state = new InputDeviceState(device); mInputDeviceStates.put(deviceId, state); Log.i(TAG, device.toString()); } return state; } /** * Tracks the state of joystick axes and game controller buttons for a particular * input device for diagnostic purposes. */ private static class InputDeviceState { private final InputDevice mDevice; private final int[] mAxes; private final float[] mAxisValues; private final SparseIntArray mKeys; public InputDeviceState(InputDevice device) { mDevice = device; int numAxes = 0; for (MotionRange range : device.getMotionRanges()) { if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { numAxes += 1; } } mAxes = new int[numAxes]; mAxisValues = new float[numAxes]; int i = 0; for (MotionRange range : device.getMotionRanges()) { if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { numAxes += 1; } mAxes[i++] = range.getAxis(); } mKeys = new SparseIntArray(); } public InputDevice getDevice() { return mDevice; } public int getAxisCount() { return mAxes.length; } public int getAxis(int axisIndex) { return mAxes[axisIndex]; } public float getAxisValue(int axisIndex) { return mAxisValues[axisIndex]; } public int getKeyCount() { return mKeys.size(); } public int getKeyCode(int keyIndex) { return mKeys.keyAt(keyIndex); } public boolean isKeyPressed(int keyIndex) { return mKeys.valueAt(keyIndex) != 0; } public boolean onKeyDown(KeyEvent event) { final int keyCode = event.getKeyCode(); if (isGameKey(keyCode)) { if (event.getRepeatCount() == 0) { final String symbolicName = KeyEvent.keyCodeToString(keyCode); mKeys.put(keyCode, 1); Log.i(TAG, mDevice.getName() + " - Key Down: " + symbolicName); } return true; } return false; } public boolean onKeyUp(KeyEvent event) { final int keyCode = event.getKeyCode(); if (isGameKey(keyCode)) { int index = mKeys.indexOfKey(keyCode); if (index >= 0) { final String symbolicName = KeyEvent.keyCodeToString(keyCode); mKeys.put(keyCode, 0); Log.i(TAG, mDevice.getName() + " - Key Up: " + symbolicName); } return true; } return false; } public boolean onJoystickMotion(MotionEvent event) { StringBuilder message = new StringBuilder(); message.append(mDevice.getName()).append(" - Joystick Motion:\n"); final int historySize = event.getHistorySize(); for (int i = 0; i < mAxes.length; i++) { final int axis = mAxes[i]; final float value = event.getAxisValue(axis); mAxisValues[i] = value; message.append(" ").append(MotionEvent.axisToString(axis)).append(": "); // Append all historical values in the batch. for (int historyPos = 0; historyPos < historySize; historyPos++) { message.append(event.getHistoricalAxisValue(axis, historyPos)); message.append(", "); } // Append the current value. message.append(value); message.append("\n"); } Log.i(TAG, message.toString()); return true; } // Check whether this is a key we care about. // In a real game, we would probably let the user configure which keys to use // instead of hardcoding the keys like this. private static boolean isGameKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_SPACE: return true; default: return KeyEvent.isGamepadButton(keyCode); } } } /** * A list adapter that displays a summary of the device state. */ private static class SummaryAdapter extends BaseAdapter { private static final int BASE_ID_HEADING = 1 << 10; private static final int BASE_ID_DEVICE_ITEM = 2 << 10; private static final int BASE_ID_AXIS_ITEM = 3 << 10; private static final int BASE_ID_KEY_ITEM = 4 << 10; private final Context mContext; private final Resources mResources; private final SparseArray<Item> mDataItems = new SparseArray<Item>(); private final ArrayList<Item> mVisibleItems = new ArrayList<Item>(); private final Heading mDeviceHeading; private final TextColumn mDeviceNameTextColumn; private final Heading mAxesHeading; private final Heading mKeysHeading; private InputDeviceState mState; public SummaryAdapter(Context context, Resources resources) { mContext = context; mResources = resources; mDeviceHeading = new Heading(BASE_ID_HEADING | 0, mResources.getString(R.string.game_controller_input_heading_device)); mDeviceNameTextColumn = new TextColumn(BASE_ID_DEVICE_ITEM | 0, mResources.getString(R.string.game_controller_input_label_device_name)); mAxesHeading = new Heading(BASE_ID_HEADING | 1, mResources.getString(R.string.game_controller_input_heading_axes)); mKeysHeading = new Heading(BASE_ID_HEADING | 2, mResources.getString(R.string.game_controller_input_heading_keys)); } public void onItemClick(int position) { if (mState != null) { Toast toast = Toast.makeText( mContext, mState.getDevice().toString(), Toast.LENGTH_LONG); toast.show(); } } public void show(InputDeviceState state) { mState = state; mVisibleItems.clear(); // Populate device information. mVisibleItems.add(mDeviceHeading); mDeviceNameTextColumn.setContent(state.getDevice().getName()); mVisibleItems.add(mDeviceNameTextColumn); // Populate axes. mVisibleItems.add(mAxesHeading); final int axisCount = state.getAxisCount(); for (int i = 0; i < axisCount; i++) { final int axis = state.getAxis(i); final int id = BASE_ID_AXIS_ITEM | axis; TextColumn column = (TextColumn) mDataItems.get(id); if (column == null) { column = new TextColumn(id, MotionEvent.axisToString(axis)); mDataItems.put(id, column); } column.setContent(Float.toString(state.getAxisValue(i))); mVisibleItems.add(column); } // Populate keys. mVisibleItems.add(mKeysHeading); final int keyCount = state.getKeyCount(); for (int i = 0; i < keyCount; i++) { final int keyCode = state.getKeyCode(i); final int id = BASE_ID_KEY_ITEM | keyCode; TextColumn column = (TextColumn) mDataItems.get(id); if (column == null) { column = new TextColumn(id, KeyEvent.keyCodeToString(keyCode)); mDataItems.put(id, column); } column.setContent(mResources.getString(state.isKeyPressed(i) ? R.string.game_controller_input_key_pressed : R.string.game_controller_input_key_released)); mVisibleItems.add(column); } notifyDataSetChanged(); } @Override public boolean hasStableIds() { return true; } @Override public int getCount() { return mVisibleItems.size(); } @Override public Item getItem(int position) { return mVisibleItems.get(position); } @Override public long getItemId(int position) { return getItem(position).getItemId(); } @Override public View getView(int position, View convertView, ViewGroup parent) { return getItem(position).getView(convertView, parent); } private static abstract class Item { private final int mItemId; private final int mLayoutResourceId; private View mView; public Item(int itemId, int layoutResourceId) { mItemId = itemId; mLayoutResourceId = layoutResourceId; } public long getItemId() { return mItemId; } public View getView(View convertView, ViewGroup parent) { if (mView == null) { LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); mView = inflater.inflate(mLayoutResourceId, parent, false); initView(mView); } updateView(mView); return mView; } protected void initView(View view) { } protected void updateView(View view) { } } private static class Heading extends Item { private final String mLabel; public Heading(int itemId, String label) { super(itemId, R.layout.game_controller_input_heading); mLabel = label; } @Override public void initView(View view) { TextView textView = (TextView) view; textView.setText(mLabel); } } private static class TextColumn extends Item { private final String mLabel; private String mContent; private TextView mContentView; public TextColumn(int itemId, String label) { super(itemId, R.layout.game_controller_input_text_column); mLabel = label; } public void setContent(String content) { mContent = content; } @Override public void initView(View view) { TextView textView = (TextView) view.findViewById(R.id.label); textView.setText(mLabel); mContentView = (TextView) view.findViewById(R.id.content); } @Override public void updateView(View view) { mContentView.setText(mContent); } } } } package com.example.android.apis.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Paint.Style; import android.os.Handler; import android.os.SystemClock; import android.util.AttributeSet; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * A trivial joystick based physics game to demonstrate joystick handling. * * @see GameControllerInput */ public class GameView extends View { private final long ANIMATION_TIME_STEP = 1000 / 60; private final int MAX_OBSTACLES = 12; private final Random mRandom; private Ship mShip; private final List<Bullet> mBullets; private final List<Obstacle> mObstacles; private long mLastStepTime; private InputDevice mLastInputDevice; private static final int DPAD_STATE_LEFT = 1 << 0; private static final int DPAD_STATE_RIGHT = 1 << 1; private static final int DPAD_STATE_UP = 1 << 2; private static final int DPAD_STATE_DOWN = 1 << 3; private int mDPadState; private float mShipSize; private float mMaxShipThrust; private float mMaxShipSpeed; private float mBulletSize; private float mBulletSpeed; private float mMinObstacleSize; private float mMaxObstacleSize; private float mMinObstacleSpeed; private float mMaxObstacleSpeed; private final Runnable mAnimationRunnable = new Runnable() { public void run() { animateFrame(); } }; public GameView(Context context, AttributeSet attrs) { super(context, attrs); mRandom = new Random(); mBullets = new ArrayList<Bullet>(); mObstacles = new ArrayList<Obstacle>(); setFocusable(true); setFocusableInTouchMode(true); float baseSize = getContext().getResources().getDisplayMetrics().density * 5f; float baseSpeed = baseSize * 3; mShipSize = baseSize * 3; mMaxShipThrust = baseSpeed * 0.25f; mMaxShipSpeed = baseSpeed * 12; mBulletSize = baseSize; mBulletSpeed = baseSpeed * 12; mMinObstacleSize = baseSize * 2; mMaxObstacleSize = baseSize * 12; mMinObstacleSpeed = baseSpeed; mMaxObstacleSpeed = baseSpeed * 3; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Reset the game when the view changes size. reset(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { ensureInitialized(); // Handle DPad keys and fire button on initial down but not on auto-repeat. boolean handled = false; if (event.getRepeatCount() == 0) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: mShip.setHeadingX(-1); mDPadState |= DPAD_STATE_LEFT; handled = true; break; case KeyEvent.KEYCODE_DPAD_RIGHT: mShip.setHeadingX(1); mDPadState |= DPAD_STATE_RIGHT; handled = true; break; case KeyEvent.KEYCODE_DPAD_UP: mShip.setHeadingY(-1); mDPadState |= DPAD_STATE_UP; handled = true; break; case KeyEvent.KEYCODE_DPAD_DOWN: mShip.setHeadingY(1); mDPadState |= DPAD_STATE_DOWN; handled = true; break; default: if (isFireKey(keyCode)) { fire(); handled = true; } break; } } if (handled) { step(event.getEventTime()); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { ensureInitialized(); // Handle keys going up. boolean handled = false; switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: mShip.setHeadingX(0); mDPadState &= ~DPAD_STATE_LEFT; handled = true; break; case KeyEvent.KEYCODE_DPAD_RIGHT: mShip.setHeadingX(0); mDPadState &= ~DPAD_STATE_RIGHT; handled = true; break; case KeyEvent.KEYCODE_DPAD_UP: mShip.setHeadingY(0); mDPadState &= ~DPAD_STATE_UP; handled = true; break; case KeyEvent.KEYCODE_DPAD_DOWN: mShip.setHeadingY(0); mDPadState &= ~DPAD_STATE_DOWN; handled = true; break; default: if (isFireKey(keyCode)) { handled = true; } break; } if (handled) { step(event.getEventTime()); return true; } return super.onKeyUp(keyCode, event); } private static boolean isFireKey(int keyCode) { return KeyEvent.isGamepadButton(keyCode) || keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SPACE; } @Override public boolean onGenericMotionEvent(MotionEvent event) { ensureInitialized(); // Check that the event came from a joystick since a generic motion event // could be almost anything. if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 && event.getAction() == MotionEvent.ACTION_MOVE) { // Cache the most recently obtained device information. // The device information may change over time but it can be // somewhat expensive to query. if (mLastInputDevice == null || mLastInputDevice.getId() != event.getDeviceId()) { mLastInputDevice = event.getDevice(); // It's possible for the device id to be invalid. // In that case, getDevice() will return null. if (mLastInputDevice == null) { return false; } } // Ignore joystick while the DPad is pressed to avoid conflicting motions. if (mDPadState != 0) { return true; } // Process all historical movement samples in the batch. final int historySize = event.getHistorySize(); for (int i = 0; i < historySize; i++) { processJoystickInput(event, i); } // Process the current movement sample in the batch. processJoystickInput(event, -1); return true; } return super.onGenericMotionEvent(event); } private void processJoystickInput(MotionEvent event, int historyPos) { // Get joystick position. // Many game pads with two joysticks report the position of the second joystick // using the Z and RZ axes so we also handle those. // In a real game, we would allow the user to configure the axes manually. float x = getCenteredAxis(event, mLastInputDevice, MotionEvent.AXIS_X, historyPos); if (x == 0) { x = getCenteredAxis(event, mLastInputDevice, MotionEvent.AXIS_HAT_X, historyPos); } if (x == 0) { x = getCenteredAxis(event, mLastInputDevice, MotionEvent.AXIS_Z, historyPos); } float y = getCenteredAxis(event, mLastInputDevice, MotionEvent.AXIS_Y, historyPos); if (y == 0) { y = getCenteredAxis(event, mLastInputDevice, MotionEvent.AXIS_HAT_Y, historyPos); } if (y == 0) { y = getCenteredAxis(event, mLastInputDevice, MotionEvent.AXIS_RZ, historyPos); } // Set the ship heading. mShip.setHeading(x, y); step(historyPos < 0 ? event.getEventTime() : event.getHistoricalEventTime(historyPos)); } private static float getCenteredAxis(MotionEvent event, InputDevice device, int axis, int historyPos) { final InputDevice.MotionRange range = device.getMotionRange(axis, event.getSource()); if (range != null) { final float flat = range.getFlat(); final float value = historyPos < 0 ? event.getAxisValue(axis) : event.getHistoricalAxisValue(axis, historyPos); // Ignore axis values that are within the 'flat' region of the joystick axis center. // A joystick at rest does not always report an absolute position of (0,0). if (Math.abs(value) > flat) { return value; } } return 0; } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { // Turn on and off animations based on the window focus. // Alternately, we could update the game state using the Activity onResume() // and onPause() lifecycle events. if (hasWindowFocus) { getHandler().postDelayed(mAnimationRunnable, ANIMATION_TIME_STEP); mLastStepTime = SystemClock.uptimeMillis(); } else { getHandler().removeCallbacks(mAnimationRunnable); mDPadState = 0; if (mShip != null) { mShip.setHeading(0, 0); mShip.setVelocity(0, 0); } } super.onWindowFocusChanged(hasWindowFocus); } private void fire() { if (mShip != null && !mShip.isDestroyed()) { Bullet bullet = new Bullet(); bullet.setPosition(mShip.getBulletInitialX(), mShip.getBulletInitialY()); bullet.setVelocity(mShip.getBulletVelocityX(mBulletSpeed), mShip.getBulletVelocityY(mBulletSpeed)); mBullets.add(bullet); } } private void ensureInitialized() { if (mShip == null) { reset(); } } private void reset() { mShip = new Ship(); mBullets.clear(); mObstacles.clear(); } void animateFrame() { long currentStepTime = SystemClock.uptimeMillis(); step(currentStepTime); Handler handler = getHandler(); if (handler != null) { handler.postAtTime(mAnimationRunnable, currentStepTime + ANIMATION_TIME_STEP); invalidate(); } } private void step(long currentStepTime) { float tau = (currentStepTime - mLastStepTime) * 0.001f; mLastStepTime = currentStepTime; ensureInitialized(); // Move the ship. mShip.accelerate(tau, mMaxShipThrust, mMaxShipSpeed); if (!mShip.step(tau)) { reset(); } // Move the bullets. int numBullets = mBullets.size(); for (int i = 0; i < numBullets; i++) { final Bullet bullet = mBullets.get(i); if (!bullet.step(tau)) { mBullets.remove(i); i -= 1; numBullets -= 1; } } // Move obstacles. int numObstacles = mObstacles.size(); for (int i = 0; i < numObstacles; i++) { final Obstacle obstacle = mObstacles.get(i); if (!obstacle.step(tau)) { mObstacles.remove(i); i -= 1; numObstacles -= 1; } } // Check for collisions between bullets and obstacles. for (int i = 0; i < numBullets; i++) { final Bullet bullet = mBullets.get(i); for (int j = 0; j < numObstacles; j++) { final Obstacle obstacle = mObstacles.get(j); if (bullet.collidesWith(obstacle)) { bullet.destroy(); obstacle.destroy(); break; } } } // Check for collisions between the ship and obstacles. for (int i = 0; i < numObstacles; i++) { final Obstacle obstacle = mObstacles.get(i); if (mShip.collidesWith(obstacle)) { mShip.destroy(); obstacle.destroy(); break; } } // Spawn more obstacles offscreen when needed. // Avoid putting them right on top of the ship. OuterLoop: while (mObstacles.size() < MAX_OBSTACLES) { final float minDistance = mShipSize * 4; float size = mRandom.nextFloat() * (mMaxObstacleSize - mMinObstacleSize) + mMinObstacleSize; float positionX, positionY; int tries = 0; do { int edge = mRandom.nextInt(4); switch (edge) { case 0: positionX = -size; positionY = mRandom.nextInt(getHeight()); break; case 1: positionX = getWidth() + size; positionY = mRandom.nextInt(getHeight()); break; case 2: positionX = mRandom.nextInt(getWidth()); positionY = -size; break; default: positionX = mRandom.nextInt(getWidth()); positionY = getHeight() + size; break; } if (++tries > 10) { break OuterLoop; } } while (mShip.distanceTo(positionX, positionY) < minDistance); float direction = mRandom.nextFloat() * (float) Math.PI * 2; float speed = mRandom.nextFloat() * (mMaxObstacleSpeed - mMinObstacleSpeed) + mMinObstacleSpeed; float velocityX = (float) Math.cos(direction) * speed; float velocityY = (float) Math.sin(direction) * speed; Obstacle obstacle = new Obstacle(); obstacle.setPosition(positionX, positionY); obstacle.setSize(size); obstacle.setVelocity(velocityX, velocityY); mObstacles.add(obstacle); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Draw the ship. if (mShip != null) { mShip.draw(canvas); } // Draw bullets. int numBullets = mBullets.size(); for (int i = 0; i < numBullets; i++) { final Bullet bullet = mBullets.get(i); bullet.draw(canvas); } // Draw obstacles. int numObstacles = mObstacles.size(); for (int i = 0; i < numObstacles; i++) { final Obstacle obstacle = mObstacles.get(i); obstacle.draw(canvas); } } static float pythag(float x, float y) { return (float) Math.sqrt(x * x + y * y); } static int blend(float alpha, int from, int to) { return from + (int) ((to - from) * alpha); } static void setPaintARGBBlend(Paint paint, float alpha, int a1, int r1, int g1, int b1, int a2, int r2, int g2, int b2) { paint.setARGB(blend(alpha, a1, a2), blend(alpha, r1, r2), blend(alpha, g1, g2), blend(alpha, b1, b2)); } private abstract class Sprite { protected float mPositionX; protected float mPositionY; protected float mVelocityX; protected float mVelocityY; protected float mSize; protected boolean mDestroyed; protected float mDestroyAnimProgress; public void setPosition(float x, float y) { mPositionX = x; mPositionY = y; } public void setVelocity(float x, float y) { mVelocityX = x; mVelocityY = y; } public void setSize(float size) { mSize = size; } public float distanceTo(float x, float y) { return pythag(mPositionX - x, mPositionY - y); } public float distanceTo(Sprite other) { return distanceTo(other.mPositionX, other.mPositionY); } public boolean collidesWith(Sprite other) { // Really bad collision detection. return !mDestroyed && !other.mDestroyed && distanceTo(other) <= Math.max(mSize, other.mSize) + Math.min(mSize, other.mSize) * 0.5f; } public boolean isDestroyed() { return mDestroyed; } public boolean step(float tau) { mPositionX += mVelocityX * tau; mPositionY += mVelocityY * tau; if (mDestroyed) { mDestroyAnimProgress += tau / getDestroyAnimDuration(); if (mDestroyAnimProgress >= 1.0f) { return false; } } return true; } public abstract void draw(Canvas canvas); public abstract float getDestroyAnimDuration(); protected boolean isOutsidePlayfield() { final int width = GameView.this.getWidth(); final int height = GameView.this.getHeight(); return mPositionX < 0 || mPositionX >= width || mPositionY < 0 || mPositionY >= height; } protected void wrapAtPlayfieldBoundary() { final int width = GameView.this.getWidth(); final int height = GameView.this.getHeight(); while (mPositionX <= -mSize) { mPositionX += width + mSize * 2; } while (mPositionX >= width + mSize) { mPositionX -= width + mSize * 2; } while (mPositionY <= -mSize) { mPositionY += height + mSize * 2; } while (mPositionY >= height + mSize) { mPositionY -= height + mSize * 2; } } public void destroy() { mDestroyed = true; step(0); } } private class Ship extends Sprite { private static final float CORNER_ANGLE = (float) Math.PI * 2 / 3; private static final float TO_DEGREES = (float) (180.0 / Math.PI); private float mHeadingX; private float mHeadingY; private float mHeadingAngle; private float mHeadingMagnitude; private final Paint mPaint; private final Path mPath; public Ship() { mPaint = new Paint(); mPaint.setStyle(Style.FILL); setPosition(getWidth() * 0.5f, getHeight() * 0.5f); setVelocity(0, 0); setSize(mShipSize); mPath = new Path(); mPath.moveTo(0, 0); mPath.lineTo((float)Math.cos(-CORNER_ANGLE) * mSize, (float)Math.sin(-CORNER_ANGLE) * mSize); mPath.lineTo(mSize, 0); mPath.lineTo((float)Math.cos(CORNER_ANGLE) * mSize, (float)Math.sin(CORNER_ANGLE) * mSize); mPath.lineTo(0, 0); } public void setHeadingX(float x) { mHeadingX = x; updateHeading(); } public void setHeadingY(float y) { mHeadingY = y; updateHeading(); } public void setHeading(float x, float y) { mHeadingX = x; mHeadingY = y; updateHeading(); } private void updateHeading() { mHeadingMagnitude = pythag(mHeadingX, mHeadingY); if (mHeadingMagnitude > 0.1f) { mHeadingAngle = (float) Math.atan2(mHeadingY, mHeadingX); } } private float polarX(float radius) { return (float) Math.cos(mHeadingAngle) * radius; } private float polarY(float radius) { return (float) Math.sin(mHeadingAngle) * radius; } public float getBulletInitialX() { return mPositionX + polarX(mSize); } public float getBulletInitialY() { return mPositionY + polarY(mSize); } public float getBulletVelocityX(float relativeSpeed) { return mVelocityX + polarX(relativeSpeed); } public float getBulletVelocityY(float relativeSpeed) { return mVelocityY + polarY(relativeSpeed); } public void accelerate(float tau, float maxThrust, float maxSpeed) { final float thrust = mHeadingMagnitude * maxThrust; mVelocityX += polarX(thrust); mVelocityY += polarY(thrust); final float speed = pythag(mVelocityX, mVelocityY); if (speed > maxSpeed) { final float scale = maxSpeed / speed; mVelocityX = mVelocityX * scale; mVelocityY = mVelocityY * scale; } } @Override public boolean step(float tau) { if (!super.step(tau)) { return false; } wrapAtPlayfieldBoundary(); return true; } public void draw(Canvas canvas) { setPaintARGBBlend(mPaint, mDestroyAnimProgress, 255, 63, 255, 63, 0, 255, 0, 0); canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.translate(mPositionX, mPositionY); canvas.rotate(mHeadingAngle * TO_DEGREES); canvas.drawPath(mPath, mPaint); canvas.restore(); } @Override public float getDestroyAnimDuration() { return 1.0f; } } private class Bullet extends Sprite { private final Paint mPaint; public Bullet() { mPaint = new Paint(); mPaint.setStyle(Style.FILL); setSize(mBulletSize); } @Override public boolean step(float tau) { if (!super.step(tau)) { return false; } return !isOutsidePlayfield(); } public void draw(Canvas canvas) { setPaintARGBBlend(mPaint, mDestroyAnimProgress, 255, 255, 255, 0, 0, 255, 255, 255); canvas.drawCircle(mPositionX, mPositionY, mSize, mPaint); } @Override public float getDestroyAnimDuration() { return 0.125f; } } private class Obstacle extends Sprite { private final Paint mPaint; public Obstacle() { mPaint = new Paint(); mPaint.setARGB(255, 127, 127, 255); mPaint.setStyle(Style.FILL); } @Override public boolean step(float tau) { if (!super.step(tau)) { return false; } wrapAtPlayfieldBoundary(); return true; } public void draw(Canvas canvas) { setPaintARGBBlend(mPaint, mDestroyAnimProgress, 255, 127, 127, 255, 0, 255, 0, 0); canvas.drawCircle(mPositionX, mPositionY, mSize * (1.0f - mDestroyAnimProgress), mPaint); } @Override public float getDestroyAnimDuration() { return 0.25f; } } } //layout/game_controller_input.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/description" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/game_controller_input_description" android:padding="12dip" /> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:padding="12dip"> <ListView android:id="@+id/summary" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:padding="3dip"> </ListView> <com.example.android.apis.view.GameView android:id="@+id/game" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:background="#000000" android:padding="3dip" /> </LinearLayout> </LinearLayout>
oystick View
//package com.MobileAnarchy.Android.Widgets.Joystick; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Shader; import android.util.AttributeSet; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; interface JoystickClickedListener { public void OnClicked(); public void OnReleased(); } public class JoystickView extends View { public static final int INVALID_POINTER_ID = -1; private final boolean D = false; String TAG = "JoystickView"; private Paint dbgPaint1; private Paint dbgPaint2; private Paint bgPaint; private Paint handlePaint; private int innerPadding; private int bgRadius; private int handleRadius; private int movementRadius; private int handleInnerBoundaries; private JoystickMovedListener moveListener; private JoystickClickedListener clickListener; //# of pixels movement required between reporting to the listener private float moveResolution; private boolean yAxisInverted; private boolean autoReturnToCenter; //Max range of movement in user coordinate system public final static int CONSTRAIN_BOX = 0; public final static int CONSTRAIN_CIRCLE = 1; private int movementConstraint; private float movementRange; public final static int COORDINATE_CARTESIAN = 0; //Regular cartesian coordinates public final static int COORDINATE_DIFFERENTIAL = 1; //Uses polar rotation of 45 degrees to calc differential drive paramaters private int userCoordinateSystem; //Records touch pressure for click handling private float touchPressure; private boolean clicked; private float clickThreshold; //Last touch point in view coordinates private int pointerId = INVALID_POINTER_ID; private float touchX, touchY; //Last reported position in view coordinates (allows different reporting sensitivities) private float reportX, reportY; //Handle center in view coordinates private float handleX, handleY; //Center of the view in view coordinates private int cX, cY; //Size of the view in view coordinates private int dimX, dimY; //Cartesian coordinates of last touch point - joystick center is (0,0) private int cartX, cartY; //Polar coordinates of the touch point from joystick center private double radial; private double angle; //User coordinates of last touch point private int userX, userY; //Offset co-ordinates (used when touch events are received from parent's coordinate origin) private int offsetX; private int offsetY; public JoystickView(Context context) { super(context); initJoystickView(); } public JoystickView(Context context, AttributeSet attrs) { super(context, attrs); initJoystickView(); } public JoystickView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initJoystickView(); } private void initJoystickView() { setFocusable(true); dbgPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG); dbgPaint1.setColor(Color.RED); dbgPaint1.setStrokeWidth(1); dbgPaint1.setStyle(Paint.Style.STROKE); dbgPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG); dbgPaint2.setColor(Color.GREEN); dbgPaint2.setStrokeWidth(1); dbgPaint2.setStyle(Paint.Style.STROKE); bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); bgPaint.setStyle(Paint.Style.FILL); handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); handlePaint.setColor(Color.rgb(8, 8, 8)); handlePaint.setStrokeWidth(1); handlePaint.setStyle(Paint.Style.FILL_AND_STROKE); innerPadding = 10; setMovementRange(150); setMoveResolution(0.1f); setClickThreshold(0.4f); setYAxisInverted(false); setUserCoordinateSystem(COORDINATE_CARTESIAN); setAutoReturnToCenter(true); } public void setAutoReturnToCenter(boolean autoReturnToCenter) { this.autoReturnToCenter = autoReturnToCenter; } public boolean isAutoReturnToCenter() { return autoReturnToCenter; } public void setUserCoordinateSystem(int userCoordinateSystem) { if (userCoordinateSystem < COORDINATE_CARTESIAN || movementConstraint > COORDINATE_DIFFERENTIAL) Log.e(TAG, "invalid value for userCoordinateSystem"); else this.userCoordinateSystem = userCoordinateSystem; } public int getUserCoordinateSystem() { return userCoordinateSystem; } public void setMovementConstraint(int movementConstraint) { if (movementConstraint < CONSTRAIN_BOX || movementConstraint > CONSTRAIN_CIRCLE) Log.e(TAG, "invalid value for movementConstraint"); else this.movementConstraint = movementConstraint; } public int getMovementConstraint() { return movementConstraint; } public boolean isYAxisInverted() { return yAxisInverted; } public void setYAxisInverted(boolean yAxisInverted) { this.yAxisInverted = yAxisInverted; } /** * Set the pressure sensitivity for registering a click * @param clickThreshold threshold 0...1.0f inclusive. 0 will cause clicks to never be reported, 1.0 is a very hard click */ public void setClickThreshold(float clickThreshold) { if (clickThreshold < 0 || clickThreshold > 1.0f) Log.e(TAG, "clickThreshold must range from 0...1.0f inclusive"); else this.clickThreshold = clickThreshold; } public float getClickThreshold() { return clickThreshold; } public void setMovementRange(float movementRange) { this.movementRange = movementRange; } public float getMovementRange() { return movementRange; } public void setMoveResolution(float moveResolution) { this.moveResolution = moveResolution; } public float getMoveResolution() { return moveResolution; } public void setOnJostickMovedListener(JoystickMovedListener listener) { this.moveListener = listener; } public void setOnJostickClickedListener(JoystickClickedListener listener) { this.clickListener = listener; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Here we make sure that we have a perfect circle int measuredWidth = measure(widthMeasureSpec); int measuredHeight = measure(heightMeasureSpec); setMeasuredDimension(measuredWidth, measuredHeight); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); int d = Math.min(getMeasuredWidth(), getMeasuredHeight()); dimX = d; dimY = d; cX = d / 2; cY = d / 2; bgRadius = dimX/2 - innerPadding; // Texturize the back of the joysticks Bitmap backTexture = null;//BitmapFactory.decodeResource(getResources(), R.drawable.joy_back); backTexture = Bitmap.createScaledBitmap(backTexture, dimX, dimY, true); BitmapShader backShader = new BitmapShader(backTexture, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR); bgPaint.setShader(backShader); handleRadius = (int)(d * 0.25); handleInnerBoundaries = handleRadius; movementRadius = Math.min(cX, cY) - handleInnerBoundaries; } private int measure(int measureSpec) { int result = 0; // Decode the measurement specifications. int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.UNSPECIFIED) { // Return a default size of 200 if no bounds are specified. result = 200; } else { // As you want to fill the available space // always return the full available bounds. result = specSize; } return result; } @Override protected void onDraw(Canvas canvas) { canvas.save(); // Draw the background canvas.drawCircle(cX, cY, bgRadius, bgPaint); // Draw the handle handleX = touchX + cX; handleY = touchY + cY; canvas.drawCircle(handleX, handleY, handleRadius, handlePaint); if (D) { canvas.drawRect(1, 1, getMeasuredWidth()-1, getMeasuredHeight()-1, dbgPaint1); canvas.drawCircle(handleX, handleY, 3, dbgPaint1); if ( movementConstraint == CONSTRAIN_CIRCLE ) { canvas.drawCircle(cX, cY, this.movementRadius, dbgPaint1); } else { canvas.drawRect(cX-movementRadius, cY-movementRadius, cX+movementRadius, cY+movementRadius, dbgPaint1); } //Origin to touch point canvas.drawLine(cX, cY, handleX, handleY, dbgPaint2); int baseY = (int) (touchY < 0 ? cY + handleRadius : cY - handleRadius); canvas.drawText(String.format("%s (%.0f,%.0f)", TAG, touchX, touchY), handleX-20, baseY-7, dbgPaint2); canvas.drawText("("+ String.format("%.0f, %.1f", radial, angle * 57.2957795) + (char) 0x00B0 + ")", handleX-20, baseY+15, dbgPaint2); } // Log.d(TAG, String.format("touch(%f,%f)", touchX, touchY)); // Log.d(TAG, String.format("onDraw(%.1f,%.1f)\n\n", handleX, handleY)); canvas.restore(); } // Constrain touch within a box private void constrainBox() { touchX = Math.max(Math.min(touchX, movementRadius), -movementRadius); touchY = Math.max(Math.min(touchY, movementRadius), -movementRadius); } // Constrain touch within a circle private void constrainCircle() { float diffX = touchX; float diffY = touchY; double radial = Math.sqrt((diffX*diffX) + (diffY*diffY)); if ( radial > movementRadius ) { touchX = (int)((diffX / radial) * movementRadius); touchY = (int)((diffY / radial) * movementRadius); } } public void setPointerId(int id) { this.pointerId = id; } public int getPointerId() { return pointerId; } @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { return processMoveEvent(ev); } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { if ( pointerId != INVALID_POINTER_ID ) { // Log.d(TAG, "ACTION_UP"); returnHandleToCenter(); setPointerId(INVALID_POINTER_ID); } break; } case MotionEvent.ACTION_POINTER_UP: { if ( pointerId != INVALID_POINTER_ID ) { final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final int pointerId = ev.getPointerId(pointerIndex); if ( pointerId == this.pointerId ) { // Log.d(TAG, "ACTION_POINTER_UP: " + pointerId); returnHandleToCenter(); setPointerId(INVALID_POINTER_ID); return true; } } break; } case MotionEvent.ACTION_DOWN: { if ( pointerId == INVALID_POINTER_ID ) { int x = (int) ev.getX(); if ( x >= offsetX && x < offsetX + dimX ) { setPointerId(ev.getPointerId(0)); // Log.d(TAG, "ACTION_DOWN: " + getPointerId()); return true; } } break; } case MotionEvent.ACTION_POINTER_DOWN: { if ( pointerId == INVALID_POINTER_ID ) { final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final int pointerId = ev.getPointerId(pointerIndex); int x = (int) ev.getX(pointerId); if ( x >= offsetX && x < offsetX + dimX ) { // Log.d(TAG, "ACTION_POINTER_DOWN: " + pointerId); setPointerId(pointerId); return true; } } break; } } return false; } private boolean processMoveEvent(MotionEvent ev) { if ( pointerId != INVALID_POINTER_ID ) { final int pointerIndex = ev.findPointerIndex(pointerId); // Translate touch position to center of view float x = ev.getX(pointerIndex); touchX = x - cX - offsetX; float y = ev.getY(pointerIndex); touchY = y - cY - offsetY; // Log.d(TAG, String.format("ACTION_MOVE: (%03.0f, %03.0f) => (%03.0f, %03.0f)", x, y, touchX, touchY)); reportOnMoved(); invalidate(); touchPressure = ev.getPressure(pointerIndex); reportOnPressure(); return true; } return false; } private void reportOnMoved() { if ( movementConstraint == CONSTRAIN_CIRCLE ) constrainCircle(); else constrainBox(); calcUserCoordinates(); if (moveListener != null) { boolean rx = Math.abs(touchX - reportX) >= moveResolution; boolean ry = Math.abs(touchY - reportY) >= moveResolution; if (rx || ry) { this.reportX = touchX; this.reportY = touchY; // Log.d(TAG, String.format("moveListener.OnMoved(%d,%d)", (int)userX, (int)userY)); moveListener.OnMoved(userX, userY); } } } private void calcUserCoordinates() { //First convert to cartesian coordinates cartX = (int)(touchX / movementRadius * movementRange); cartY = (int)(touchY / movementRadius * movementRange); radial = Math.sqrt((cartX*cartX) + (cartY*cartY)); angle = Math.atan2(cartY, cartX); //Invert Y axis if requested if ( !yAxisInverted ) cartY *= -1; if ( userCoordinateSystem == COORDINATE_CARTESIAN ) { userX = cartX; userY = cartY; } else if ( userCoordinateSystem == COORDINATE_DIFFERENTIAL ) { userX = cartY + cartX / 4; userY = cartY - cartX / 4; if ( userX < -movementRange ) userX = (int)-movementRange; if ( userX > movementRange ) userX = (int)movementRange; if ( userY < -movementRange ) userY = (int)-movementRange; if ( userY > movementRange ) userY = (int)movementRange; } } //Simple pressure click private void reportOnPressure() { // Log.d(TAG, String.format("touchPressure=%.2f", this.touchPressure)); if ( clickListener != null ) { if ( clicked && touchPressure < clickThreshold ) { clickListener.OnReleased(); this.clicked = false; // Log.d(TAG, "reset click"); invalidate(); } else if ( !clicked && touchPressure >= clickThreshold ) { clicked = true; clickListener.OnClicked(); // Log.d(TAG, "click"); invalidate(); performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } } } private void returnHandleToCenter() { if ( autoReturnToCenter ) { final int numberOfFrames = 5; final double intervalsX = (0 - touchX) / numberOfFrames; final double intervalsY = (0 - touchY) / numberOfFrames; for (int i = 0; i < numberOfFrames; i++) { final int j = i; postDelayed(new Runnable() { @Override public void run() { touchX += intervalsX; touchY += intervalsY; reportOnMoved(); invalidate(); if (moveListener != null && j == numberOfFrames - 1) { moveListener.OnReturnedToCenter(); } } }, i * 40); } if (moveListener != null) { moveListener.OnReleased(); } } } public void setTouchOffset(int x, int y) { offsetX = x; offsetY = y; } } interface JoystickMovedListener { public void OnMoved(int pan, int tilt); public void OnReleased(); public void OnReturnedToCenter(); }
Get Display Metrics
package app.test; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.List; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.telephony.TelephonyManager; import android.util.DisplayMetrics; import android.util.Log; class CMDExecute { public synchronized String run(String[] cmd, String workdirectory) throws IOException { String result = ""; try { ProcessBuilder builder = new ProcessBuilder(cmd); // set working directory if (workdirectory != null) builder.directory(new File(workdirectory)); builder.redirectErrorStream(true); Process process = builder.start(); InputStream in = process.getInputStream(); byte[] re = new byte[1024]; while (in.read(re) != -1) { System.out.println(new String(re)); result = result + new String(re); } in.close(); } catch (Exception ex) { ex.printStackTrace(); } return result; } } public class Main { private static StringBuffer buffer; public static String getDisplayMetrics(Context cx) { String str = ""; DisplayMetrics dm = new DisplayMetrics(); dm = cx.getApplicationContext().getResources().getDisplayMetrics(); int screenWidth = dm.widthPixels; int screenHeight = dm.heightPixels; float density = dm.density; float xdpi = dm.xdpi; float ydpi = dm.ydpi; str += "The absolute width:" + String.valueOf(screenWidth) + "pixels\n"; str += "The absolute heightin:" + String.valueOf(screenHeight) + "pixels\n"; str += "The logical density of the display.:" + String.valueOf(density) + "\n"; str += "X dimension :" + String.valueOf(xdpi) + "pixels per inch\n"; str += "Y dimension :" + String.valueOf(ydpi) + "pixels per inch\n"; return str; } }
GpsUtils contains code snippets for GPS.
//package backend.snippets; import android.app.Activity; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Bundle; /** * GpsUtils contains code snippets for GPS. * @author Maurya * */ class GpsUtils { static Location loc; // CLEAN THIS THING! private static LocationManager locationManager; void getLocation(Activity activity) { // Acquire a reference to the system Location Manager locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE); // Register the listener with the Location Manager to receive location updates locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, new LocationUpdateHandler()); } static Location getLastKnownLocation() { String locationProvider = LocationManager.GPS_PROVIDER; // Or use LocationManager.GPS_PROVIDER Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider); return lastKnownLocation; } public class LocationUpdateHandler implements LocationListener { public void onLocationChanged(Location location) { // Called when a new location is found by the network location provider. makeUseOfNewLocation(location); } private void makeUseOfNewLocation(Location location) { loc = location; // Remove the listener you previously added locationManager.removeUpdates(this); } public void onStatusChanged(String provider, int status, Bundle extras) {} public void onProviderEnabled(String provider) {} public void onProviderDisabled(String provider) {} } }
Gps Helper
//package djrain.lib; import android.content.Context; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Bundle; import android.util.Log; public class GpsHelper implements LocationListener { private static final String tag = "LocationHelper"; private String bestProvider; private LocationManager locationManager; private OnLocationChangeListener listener; private Location location; private String gpsProvider; static long MINTIME = 3000; static float MINDISTANCE = 0f; static float SPEED_UNIT = 1f; static public interface OnLocationChangeListener { public void onLocationChanged(Location location); } void setOnLocationChangeListener(OnLocationChangeListener listener) { this.listener = listener; } public Location getLocation() { return location; } public GpsHelper(Context context) { locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); findBestProvider(); } public void findBestProvider() { // if(bestProvider != null && // locationManager.isProviderEnabled(bestProvider)){ // } Criteria criteria = new Criteria(); bestProvider = locationManager.getBestProvider(criteria, false); location = locationManager.getLastKnownLocation(bestProvider); Log.e("Lilo", "find best provider:" + bestProvider); printProvider(bestProvider); // if(bestProvider == null) { gpsProvider = LocationManager.GPS_PROVIDER; bestProvider = gpsProvider; } } protected void onResume() { locationManager.requestLocationUpdates(bestProvider, MINTIME, MINDISTANCE, this); } protected void onPause() { locationManager.removeUpdates(this); } public void onLocationChanged(Location location) { if (location == null) return; printLocation(location); this.location = location; if (listener != null) { listener.onLocationChanged(location); } } public void onProviderDisabled(String provider) { Log.e(tag, "onProviderDisabled: " + provider); } public void onProviderEnabled(String provider) { Log.e(tag, "onProviderEnabled: " + provider); } public void onStatusChanged(String provider, int status, Bundle extras) { Log.e(tag, "onStatusChanged: " + provider + "," + status); } private void printLocation(Location location) { if (location == null) { Log.e(tag, "Location[unknown]"); } else { Log.e(tag, "" + location.getSpeed()); Log.e(tag, "" + location.getLongitude()); Log.e(tag, "" + location.getLatitude()); } } private void printProvider(String provider) { LocationProvider info = locationManager.getProvider(provider); StringBuilder builder = new StringBuilder(); builder.append("LocationProvider[").append("name=").append(info.getName()).append(",enabled=").append(locationManager.isProviderEnabled(provider)).append(",getAccuracy=") .append(info.getAccuracy()).append(",getPowerRequirement=").append(info.getPowerRequirement()).append(",hasMonetaryCost=").append(info.hasMonetaryCost()).append(",requiresCell=") .append(info.requiresCell()).append(",requiresNetwork=").append(info.requiresNetwork()).append(",requiresSatellite=").append(info.requiresSatellite()).append(",supportsAltitude=") .append(info.supportsAltitude()).append(",supportsBearing=").append(info.supportsBearing()).append(",supportsSpeed=").append(info.supportsSpeed()).append("]"); Log.e(tag, builder.toString()); } }
Using PowerManager
package app.test; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.os.Environment; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.provider.Settings; import android.util.Log; public class Test extends Activity implements SensorEventListener { private WakeLock mWakelock = null; private SensorManager mMgr; private Sensor mAccel; private BufferedWriter mLog; final private SimpleDateFormat mTimeFormat = new SimpleDateFormat("HH:mm:ss - "); private int mSavedTimeout; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mMgr = (SensorManager) this.getSystemService(SENSOR_SERVICE); mAccel = mMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); try { String filename = Environment.getExternalStorageDirectory().getAbsolutePath() +"/accel.log"; mLog = new BufferedWriter(new FileWriter(filename, true)); } catch(Exception e) { e.printStackTrace(); finish(); } PowerManager pwrMgr = (PowerManager) this.getSystemService(POWER_SERVICE); mWakelock = pwrMgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Accel"); mWakelock.acquire(); try { mSavedTimeout = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT); } catch(Exception e) { mSavedTimeout = 120000; } Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, 5000); } public BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { writeLog("The screen has turned off"); mMgr.unregisterListener(Test.this); mMgr.registerListener(Test.this, mAccel, SensorManager.SENSOR_DELAY_NORMAL); } } }; @Override protected void onStart() { mMgr.registerListener(this, mAccel, SensorManager.SENSOR_DELAY_NORMAL); IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); registerReceiver(mReceiver, filter); super.onStart(); } @Override protected void onStop() { mMgr.unregisterListener(this, mAccel); unregisterReceiver(mReceiver); try { mLog.flush(); } catch (IOException e) { } super.onStop(); } @Override protected void onDestroy() { try { mLog.flush(); mLog.close(); } catch(Exception e) { } Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT, mSavedTimeout); mWakelock.release(); super.onDestroy(); } public void onAccuracyChanged(Sensor sensor, int accuracy) { } public void onSensorChanged(SensorEvent event) { writeLog("Got a sensor event: " + event.values[0] + ", " + event.values[1] + ", " + event.values[2]); } private void writeLog(String str) { try { Date now = new Date(); mLog.write(mTimeFormat.format(now)); mLog.write(str); mLog.write("\n"); } catch(IOException ioe) { ioe.printStackTrace(); } } }
SensorManager Demo
//main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TableLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:stretchColumns="0,1,2"> <TableRow android:layout_weight="1"> <View android:id="@+id/top" android:layout_column="1" /> </TableRow> <TableRow android:layout_weight="1"> <View android:id="@+id/left" android:layout_column="0" /> <View android:id="@+id/right" android:layout_column="2" /> </TableRow> <TableRow android:layout_weight="1"> <View android:id="@+id/bottom" android:layout_column="1" /> </TableRow> </TableLayout> <TextView android:id="@+id/values" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" /> </RelativeLayout> package app.test; import android.app.Activity; import android.graphics.Color; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class Test extends Activity implements SensorEventListener { private SensorManager mSensorManager; private Sensor mAccelerometer; private TextView valueView; private View mTop, mBottom, mLeft, mRight; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); valueView = (TextView)findViewById(R.id.values); mTop = findViewById(R.id.top); mBottom = findViewById(R.id.bottom); mLeft = findViewById(R.id.left); mRight = findViewById(R.id.right); } protected void onResume() { super.onResume(); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI); } protected void onPause() { super.onPause(); mSensorManager.unregisterListener(this); } public void onAccuracyChanged(Sensor sensor, int accuracy) { } public void onSensorChanged(SensorEvent event) { float[] values = event.values; float x = values[0]/10; float y = values[1]/10; int scaleFactor; if(x > 0) { scaleFactor = (int)Math.min(x*255, 255); mRight.setBackgroundColor(Color.TRANSPARENT); mLeft.setBackgroundColor(Color.argb(scaleFactor, 255, 0, 0)); } else { scaleFactor = (int)Math.min(Math.abs(x)*255, 255); mRight.setBackgroundColor(Color.argb(scaleFactor, 255, 0, 0)); mLeft.setBackgroundColor(Color.TRANSPARENT); } if(y > 0) { scaleFactor = (int)Math.min(y*255, 255); mTop.setBackgroundColor(Color.TRANSPARENT); mBottom.setBackgroundColor(Color.argb(scaleFactor, 255, 0, 0)); } else { scaleFactor = (int)Math.min(Math.abs(y)*255, 255); mTop.setBackgroundColor(Color.argb(scaleFactor, 255, 0, 0)); mBottom.setBackgroundColor(Color.TRANSPARENT); } valueView.setText(String.format("X: %1$1.2f, Y: %2$1.2f, Z: %3$1.2f", values[0], values[1], values[2])); } }
Using SensorManager to calculate acceleration and check if it is still
//package com.hairy.nipples.util; import android.hardware.SensorManager; class AccelerationUtil { public static double calculateAcceleration(float[] values) { double acceleration = Math.sqrt(Math.pow(values[0], 2) + Math.pow(values[1], 2) + Math.pow(values[2], 2)); return acceleration; } public static boolean isStandingStill(float[] accelerationValues) { double acceleration = calculateAcceleration(accelerationValues); // If acceleration doesn't differ from earth's gravity more than 10%, then // it's safe to assume that phone is standing still if (acceleration > SensorManager.GRAVITY_EARTH * 0.9 && acceleration < SensorManager.GRAVITY_EARTH * 1.1) { return true; } return false; } }
USB device that supports the adb protocol
package com.android.adb; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbRequest; import android.util.SparseArray; import java.util.LinkedList; /* This class represents a USB device that supports the adb protocol. */ public class AdbDevice { private final AdbTestActivity mActivity; private final UsbDeviceConnection mDeviceConnection; private final UsbEndpoint mEndpointOut; private final UsbEndpoint mEndpointIn; private String mSerial; // pool of requests for the OUT endpoint private final LinkedList<UsbRequest> mOutRequestPool = new LinkedList<UsbRequest>(); // pool of requests for the IN endpoint private final LinkedList<UsbRequest> mInRequestPool = new LinkedList<UsbRequest>(); // list of currently opened sockets private final SparseArray<AdbSocket> mSockets = new SparseArray<AdbSocket>(); private int mNextSocketId = 1; private final WaiterThread mWaiterThread = new WaiterThread(); public AdbDevice(AdbTestActivity activity, UsbDeviceConnection connection, UsbInterface intf) { mActivity = activity; mDeviceConnection = connection; mSerial = connection.getSerial(); UsbEndpoint epOut = null; UsbEndpoint epIn = null; // look for our bulk endpoints for (int i = 0; i < intf.getEndpointCount(); i++) { UsbEndpoint ep = intf.getEndpoint(i); if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { if (ep.getDirection() == UsbConstants.USB_DIR_OUT) { epOut = ep; } else { epIn = ep; } } } if (epOut == null || epIn == null) { throw new IllegalArgumentException("not all endpoints found"); } mEndpointOut = epOut; mEndpointIn = epIn; } // return device serial number public String getSerial() { return mSerial; } // get an OUT request from our pool public UsbRequest getOutRequest() { synchronized(mOutRequestPool) { if (mOutRequestPool.isEmpty()) { UsbRequest request = new UsbRequest(); request.initialize(mDeviceConnection, mEndpointOut); return request; } else { return mOutRequestPool.removeFirst(); } } } // return an OUT request to the pool public void releaseOutRequest(UsbRequest request) { synchronized (mOutRequestPool) { mOutRequestPool.add(request); } } // get an IN request from the pool public UsbRequest getInRequest() { synchronized(mInRequestPool) { if (mInRequestPool.isEmpty()) { UsbRequest request = new UsbRequest(); request.initialize(mDeviceConnection, mEndpointIn); return request; } else { return mInRequestPool.removeFirst(); } } } public void start() { mWaiterThread.start(); connect(); } public AdbSocket openSocket(String destination) { AdbSocket socket; synchronized (mSockets) { int id = mNextSocketId++; socket = new AdbSocket(this, id); mSockets.put(id, socket); } if (socket.open(destination)) { return socket; } else { return null; } } private AdbSocket getSocket(int id) { synchronized (mSockets) { return mSockets.get(id); } } public void socketClosed(AdbSocket socket) { synchronized (mSockets) { mSockets.remove(socket.getId()); } } // send a connect command private void connect() { AdbMessage message = new AdbMessage(); message.set(AdbMessage.A_CNXN, AdbMessage.A_VERSION, AdbMessage.MAX_PAYLOAD, "host::\0"); message.write(this); } // handle connect response private void handleConnect(AdbMessage message) { if (message.getDataString().startsWith("device:")) { log("connected"); mActivity.deviceOnline(this); } } public void stop() { synchronized (mWaiterThread) { mWaiterThread.mStop = true; } } // dispatch a message from the device void dispatchMessage(AdbMessage message) { int command = message.getCommand(); switch (command) { case AdbMessage.A_SYNC: log("got A_SYNC"); break; case AdbMessage.A_CNXN: handleConnect(message); break; case AdbMessage.A_OPEN: case AdbMessage.A_OKAY: case AdbMessage.A_CLSE: case AdbMessage.A_WRTE: AdbSocket socket = getSocket(message.getArg1()); if (socket == null) { log("ERROR socket not found"); } else { socket.handleMessage(message); } break; } } void log(String s) { mActivity.log(s); } private class WaiterThread extends Thread { public boolean mStop; public void run() { // start out with a command read AdbMessage currentCommand = new AdbMessage(); AdbMessage currentData = null; // FIXME error checking currentCommand.readCommand(getInRequest()); while (true) { synchronized (this) { if (mStop) { return; } } UsbRequest request = mDeviceConnection.requestWait(); if (request == null) { break; } AdbMessage message = (AdbMessage)request.getClientData(); request.setClientData(null); AdbMessage messageToDispatch = null; if (message == currentCommand) { int dataLength = message.getDataLength(); // read data if length > 0 if (dataLength > 0) { message.readData(getInRequest(), dataLength); currentData = message; } else { messageToDispatch = message; } currentCommand = null; } else if (message == currentData) { messageToDispatch = message; currentData = null; } if (messageToDispatch != null) { // queue another read first currentCommand = new AdbMessage(); currentCommand.readCommand(getInRequest()); // then dispatch the current message dispatchMessage(messageToDispatch); } // put request back into the appropriate pool if (request.getEndpoint() == mEndpointOut) { releaseOutRequest(request); } else { synchronized (mInRequestPool) { mInRequestPool.add(request); } } } } } } //src\com\android\adb\AdbMessage.java package com.android.adb; import android.hardware.usb.UsbRequest; import java.nio.ByteBuffer; import java.nio.ByteOrder; /* This class encapsulates and adb command packet */ public class AdbMessage { // command names public static final int A_SYNC = 0x434e5953; public static final int A_CNXN = 0x4e584e43; public static final int A_OPEN = 0x4e45504f; public static final int A_OKAY = 0x59414b4f; public static final int A_CLSE = 0x45534c43; public static final int A_WRTE = 0x45545257; // ADB protocol version public static final int A_VERSION = 0x01000000; public static final int MAX_PAYLOAD = 4096; private final ByteBuffer mMessageBuffer; private final ByteBuffer mDataBuffer; public AdbMessage() { mMessageBuffer = ByteBuffer.allocate(24); mDataBuffer = ByteBuffer.allocate(MAX_PAYLOAD); mMessageBuffer.order(ByteOrder.LITTLE_ENDIAN); mDataBuffer.order(ByteOrder.LITTLE_ENDIAN); } // sets the fields in the command header public void set(int command, int arg0, int arg1, byte[] data) { mMessageBuffer.putInt(0, command); mMessageBuffer.putInt(4, arg0); mMessageBuffer.putInt(8, arg1); mMessageBuffer.putInt(12, (data == null ? 0 : data.length)); mMessageBuffer.putInt(16, (data == null ? 0 : checksum(data))); mMessageBuffer.putInt(20, command ^ 0xFFFFFFFF); if (data != null) { mDataBuffer.put(data, 0, data.length); } } public void set(int command, int arg0, int arg1) { set(command, arg0, arg1, (byte[])null); } public void set(int command, int arg0, int arg1, String data) { // add trailing zero data += "\0"; set(command, arg0, arg1, data.getBytes()); } // returns the command's message ID public int getCommand() { return mMessageBuffer.getInt(0); } // returns command's first argument public int getArg0() { return mMessageBuffer.getInt(4); } // returns command's second argument public int getArg1() { return mMessageBuffer.getInt(8); } // returns command's data buffer public ByteBuffer getData() { return mDataBuffer; } // returns command's data length public int getDataLength() { return mMessageBuffer.getInt(12); } // returns command's data as a string public String getDataString() { int length = getDataLength(); if (length == 0) return null; // trim trailing zero return new String(mDataBuffer.array(), 0, length - 1); } public boolean write(AdbDevice device) { synchronized (device) { UsbRequest request = device.getOutRequest(); request.setClientData(this); if (request.queue(mMessageBuffer, 24)) { int length = getDataLength(); if (length > 0) { request = device.getOutRequest(); request.setClientData(this); if (request.queue(mDataBuffer, length)) { return true; } else { device.releaseOutRequest(request); return false; } } return true; } else { device.releaseOutRequest(request); return false; } } } public boolean readCommand(UsbRequest request) { request.setClientData(this); return request.queue(mMessageBuffer, 24); } public boolean readData(UsbRequest request, int length) { request.setClientData(this); return request.queue(mDataBuffer, length); } private static String extractString(ByteBuffer buffer, int offset, int length) { byte[] bytes = new byte[length]; for (int i = 0; i < length; i++) { bytes[i] = buffer.get(offset++); } return new String(bytes); } @Override public String toString() { String commandName = extractString(mMessageBuffer, 0, 4); int dataLength = getDataLength(); String result = "Adb Message: " + commandName + " arg0: " + getArg0() + " arg1: " + getArg1() + " dataLength: " + dataLength; if (dataLength > 0) { result += (" data: \"" + getDataString() + "\""); } return result; } private static int checksum(byte[] data) { int result = 0; for (int i = 0; i < data.length; i++) { int x = data[i]; // dang, no unsigned ints in java if (x < 0) x += 256; result += x; } return result; } } //src\com\android\adb\AdbSocket.java package com.android.adb; /* This class represents an adb socket. adb supports multiple independent * socket connections to a single device. Typically a socket is created * for each adb command that is executed. */ public class AdbSocket { private final AdbDevice mDevice; private final int mId; private int mPeerId; public AdbSocket(AdbDevice device, int id) { mDevice = device; mId = id; } public int getId() { return mId; } public boolean open(String destination) { AdbMessage message = new AdbMessage(); message.set(AdbMessage.A_OPEN, mId, 0, destination); if (! message.write(mDevice)) { return false; } synchronized (this) { try { wait(); } catch (InterruptedException e) { return false; } } return true; } public void handleMessage(AdbMessage message) { switch (message.getCommand()) { case AdbMessage.A_OKAY: mPeerId = message.getArg0(); synchronized (this) { notify(); } break; case AdbMessage.A_WRTE: mDevice.log(message.getDataString()); sendReady(); break; } } private void sendReady() { AdbMessage message = new AdbMessage(); message.set(AdbMessage.A_OKAY, mId, mPeerId); message.write(mDevice); } } //src\com\android\adb\AdbTestActivity.java package com.android.adb; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Rect; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.widget.TextView; /* Main activity for the adb test program */ public class AdbTestActivity extends Activity { private static final String TAG = "AdbTestActivity"; private TextView mLog; private UsbManager mManager; private UsbDevice mDevice; private UsbDeviceConnection mDeviceConnection; private UsbInterface mInterface; private AdbDevice mAdbDevice; private static final int MESSAGE_LOG = 1; private static final int MESSAGE_DEVICE_ONLINE = 2; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.adb); mLog = (TextView)findViewById(R.id.log); mManager = (UsbManager)getSystemService(Context.USB_SERVICE); // check for existing devices for (UsbDevice device : mManager.getDeviceList().values()) { UsbInterface intf = findAdbInterface(device); if (setAdbInterface(device, intf)) { break; } } // listen for new devices IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); registerReceiver(mUsbReceiver, filter); } @Override public void onDestroy() { unregisterReceiver(mUsbReceiver); setAdbInterface(null, null); super.onDestroy(); } public void log(String s) { Message m = Message.obtain(mHandler, MESSAGE_LOG); m.obj = s; mHandler.sendMessage(m); } private void appendLog(String text) { Rect r = new Rect(); mLog.getDrawingRect(r); int maxLines = r.height() / mLog.getLineHeight() - 1; text = mLog.getText() + "\n" + text; // see how many lines we have int index = text.lastIndexOf('\n'); int count = 0; while (index > 0 && count <= maxLines) { count++; index = text.lastIndexOf('\n', index - 1); } // truncate to maxLines if (index > 0) { text = text.substring(index + 1); } mLog.setText(text); } public void deviceOnline(AdbDevice device) { Message m = Message.obtain(mHandler, MESSAGE_DEVICE_ONLINE); m.obj = device; mHandler.sendMessage(m); } private void handleDeviceOnline(AdbDevice device) { log("device online: " + device.getSerial()); device.openSocket("shell:exec logcat"); } // Sets the current USB device and interface private boolean setAdbInterface(UsbDevice device, UsbInterface intf) { if (mDeviceConnection != null) { if (mInterface != null) { mDeviceConnection.releaseInterface(mInterface); mInterface = null; } mDeviceConnection.close(); mDevice = null; mDeviceConnection = null; } if (device != null && intf != null) { UsbDeviceConnection connection = mManager.openDevice(device); if (connection != null) { log("open succeeded"); if (connection.claimInterface(intf, false)) { log("claim interface succeeded"); mDevice = device; mDeviceConnection = connection; mInterface = intf; mAdbDevice = new AdbDevice(this, mDeviceConnection, intf); log("call start"); mAdbDevice.start(); return true; } else { log("claim interface failed"); connection.close(); } } else { log("open failed"); } } if (mDeviceConnection == null && mAdbDevice != null) { mAdbDevice.stop(); mAdbDevice = null; } return false; } // searches for an adb interface on the given USB device static private UsbInterface findAdbInterface(UsbDevice device) { Log.d(TAG, "findAdbInterface " + device); int count = device.getInterfaceCount(); for (int i = 0; i < count; i++) { UsbInterface intf = device.getInterface(i); if (intf.getInterfaceClass() == 255 && intf.getInterfaceSubclass() == 66 && intf.getInterfaceProtocol() == 1) { return intf; } } return null; } BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); UsbInterface intf = findAdbInterface(device); if (intf != null) { log("Found adb interface " + intf); setAdbInterface(device, intf); } } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); String deviceName = device.getDeviceName(); if (mDevice != null && mDevice.equals(deviceName)) { log("adb interface removed"); setAdbInterface(null, null); } } } }; Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_LOG: appendLog((String)msg.obj); break; case MESSAGE_DEVICE_ONLINE: handleDeviceOnline((AdbDevice)msg.obj); break; } } }; } //res\layout\adb.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/log" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="25dp" android:textSize="18sp" android:textColor="#ffffffff" /> </LinearLayout> // //res\xml\device_filter.xml <?xml version="1.0" encoding="utf-8"?> <resources> <usb-device class="255" subclass="66" protocol="1" /> </resources>
Detect USB device
package com.android.missilelauncher; import java.nio.ByteBuffer; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbRequest; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; public class MissileLauncherActivity extends Activity implements View.OnClickListener, Runnable { private static final String TAG = "MissileLauncherActivity"; private Button mFire; private UsbManager mUsbManager; private UsbDevice mDevice; private UsbDeviceConnection mConnection; private UsbEndpoint mEndpointIntr; private SensorManager mSensorManager; private Sensor mGravitySensor; // USB control commands private static final int COMMAND_UP = 1; private static final int COMMAND_DOWN = 2; private static final int COMMAND_RIGHT = 4; private static final int COMMAND_LEFT = 8; private static final int COMMAND_FIRE = 16; private static final int COMMAND_STOP = 32; private static final int COMMAND_STATUS = 64; // constants for accelerometer orientation private static final int TILT_LEFT = 1; private static final int TILT_RIGHT = 2; private static final int TILT_UP = 4; private static final int TILT_DOWN = 8; private static final double THRESHOLD = 5.0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.launcher); mFire = (Button)findViewById(R.id.fire); mFire.setOnClickListener(this); mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE); mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); mGravitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); } @Override public void onPause() { super.onPause(); mSensorManager.unregisterListener(mGravityListener); } @Override public void onResume() { super.onResume(); mSensorManager.registerListener(mGravityListener, mGravitySensor, SensorManager.SENSOR_DELAY_NORMAL); Intent intent = getIntent(); Log.d(TAG, "intent: " + intent); String action = intent.getAction(); UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { setDevice(device); } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { if (mDevice != null && mDevice.equals(device)) { setDevice(null); } } } @Override public void onDestroy() { super.onDestroy(); } private void setDevice(UsbDevice device) { Log.d(TAG, "setDevice " + device); if (device.getInterfaceCount() != 1) { Log.e(TAG, "could not find interface"); return; } UsbInterface intf = device.getInterface(0); // device should have one endpoint if (intf.getEndpointCount() != 1) { Log.e(TAG, "could not find endpoint"); return; } // endpoint should be of type interrupt UsbEndpoint ep = intf.getEndpoint(0); if (ep.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) { Log.e(TAG, "endpoint is not interrupt type"); return; } mDevice = device; mEndpointIntr = ep; if (device != null) { UsbDeviceConnection connection = mUsbManager.openDevice(device); if (connection != null && connection.claimInterface(intf, true)) { Log.d(TAG, "open SUCCESS"); mConnection = connection; Thread thread = new Thread(this); thread.start(); } else { Log.d(TAG, "open FAIL"); mConnection = null; } } } private void sendCommand(int control) { synchronized (this) { if (control != COMMAND_STATUS) { Log.d(TAG, "sendMove " + control); } if (mConnection != null) { byte[] message = new byte[1]; message[0] = (byte)control; // Send command via a control request on endpoint zero mConnection.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0); } } } public void onClick(View v) { if (v == mFire) { sendCommand(COMMAND_FIRE); } } private int mLastValue = 0; SensorEventListener mGravityListener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { // compute current tilt int value = 0; if (event.values[0] < -THRESHOLD) { value += TILT_LEFT; } else if (event.values[0] > THRESHOLD) { value += TILT_RIGHT; } if (event.values[1] < -THRESHOLD) { value += TILT_UP; } else if (event.values[1] > THRESHOLD) { value += TILT_DOWN; } if (value != mLastValue) { mLastValue = value; // send motion command if the tilt changed switch (value) { case TILT_LEFT: sendCommand(COMMAND_LEFT); break; case TILT_RIGHT: sendCommand(COMMAND_RIGHT); break; case TILT_UP: sendCommand(COMMAND_UP); break; case TILT_DOWN: sendCommand(COMMAND_DOWN); break; default: sendCommand(COMMAND_STOP); break; } } } public void onAccuracyChanged(Sensor sensor, int accuracy) { // ignore } }; @Override public void run() { ByteBuffer buffer = ByteBuffer.allocate(1); UsbRequest request = new UsbRequest(); request.initialize(mConnection, mEndpointIntr); byte status = -1; while (true) { // queue a request on the interrupt endpoint request.queue(buffer, 1); // send poll status command sendCommand(COMMAND_STATUS); // wait for status event if (mConnection.requestWait() == request) { byte newStatus = buffer.get(0); if (newStatus != status) { Log.d(TAG, "got status " + newStatus); status = newStatus; if ((status & COMMAND_FIRE) != 0) { // stop firing sendCommand(COMMAND_STOP); } } try { Thread.sleep(100); } catch (InterruptedException e) { } } else { Log.e(TAG, "requestWait failed, exiting"); break; } } } } // //res\layout\launcher.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/fire" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="25dp" android:layout_marginBottom="25dp" android:layout_marginLeft="25dp" android:layout_marginRight="25dp" android:textSize="36sp" android:text="@string/fire"> </Button> </LinearLayout> // //res\values\strings.xml <?xml version="1.0" encoding="utf-8"?> <resources> <string name="fire">Fire!</string> </resources> // //res\xml\device_filter.xml <resources> <!-- vendor and product ID for Dream Cheeky USB Missle Launcher --> <usb-device vendor-id="2689" product-id="1793" /> <!-- vendor and product ID for Dream Cheeky Wireless USB Missle Launcher --> <usb-device vendor-id="2689" product-id="65281" /> </resources>
Demonstrates android.os.Vibrator android.os.Vibrator class.
package com.example.android.apis.os; // Need the following import to get access to the app resources, since this // class is in a sub-package. import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Vibrator; import android.view.View; import android.widget.TextView; import com.example.android.apis.R; public class MorseCode extends Activity { /** Our text view */ private TextView mTextView; /** * Initialization of the Activity after it is first created. Must at least * call {@link android.app.Activity#setContentView setContentView()} to * describe what is to be displayed in the screen. */ @Override protected void onCreate(Bundle savedInstanceState) { // Be sure to call the super class. super.onCreate(savedInstanceState); // See assets/res/any/layout/hello_world.xml for this // view layout definition, which is being set here as // the content of our screen. setContentView(R.layout.morse_code); // Set the OnClickListener for the button so we see when it's pressed. findViewById(R.id.button).setOnClickListener(mClickListener); // Save the text view so we don't have to look it up each time mTextView = (TextView)findViewById(R.id.text); } /** Called when the button is pushed */ View.OnClickListener mClickListener = new View.OnClickListener() { public void onClick(View v) { // Get the text out of the view String text = mTextView.getText().toString(); // convert it using the function defined above. See the docs for // android.os.Vibrator for more info about the format of this array long[] pattern = MorseCodeConverter.pattern(text); // Start the vibration Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(pattern, -1); } }; } /** Class that implements the text to morse code coversion */ class MorseCodeConverter { private static final long SPEED_BASE = 100; static final long DOT = SPEED_BASE; static final long DASH = SPEED_BASE * 3; static final long GAP = SPEED_BASE; static final long LETTER_GAP = SPEED_BASE * 3; static final long WORD_GAP = SPEED_BASE * 7; /** The characters from 'A' to 'Z' */ private static final long[][] LETTERS = new long[][] { /* A */ new long[] { DOT, GAP, DASH }, /* B */ new long[] { DASH, GAP, DOT, GAP, DOT, GAP, DOT }, /* C */ new long[] { DASH, GAP, DOT, GAP, DASH, GAP, DOT }, /* D */ new long[] { DASH, GAP, DOT, GAP, DOT }, /* E */ new long[] { DOT }, /* F */ new long[] { DOT, GAP, DOT, GAP, DASH, GAP, DOT }, /* G */ new long[] { DASH, GAP, DASH, GAP, DOT }, /* H */ new long[] { DOT, GAP, DOT, GAP, DOT, GAP, DOT }, /* I */ new long[] { DOT, GAP, DOT }, /* J */ new long[] { DOT, GAP, DASH, GAP, DASH, GAP, DASH }, /* K */ new long[] { DASH, GAP, DOT, GAP, DASH }, /* L */ new long[] { DOT, GAP, DASH, GAP, DOT, GAP, DOT }, /* M */ new long[] { DASH, GAP, DASH }, /* N */ new long[] { DASH, GAP, DOT }, /* O */ new long[] { DASH, GAP, DASH, GAP, DASH }, /* P */ new long[] { DOT, GAP, DASH, GAP, DASH, GAP, DOT }, /* Q */ new long[] { DASH, GAP, DASH, GAP, DOT, GAP, DASH }, /* R */ new long[] { DOT, GAP, DASH, GAP, DOT }, /* S */ new long[] { DOT, GAP, DOT, GAP, DOT }, /* T */ new long[] { DASH }, /* U */ new long[] { DOT, GAP, DOT, GAP, DASH }, /* V */ new long[] { DOT, GAP, DOT, GAP, DASH }, /* W */ new long[] { DOT, GAP, DASH, GAP, DASH }, /* X */ new long[] { DASH, GAP, DOT, GAP, DOT, GAP, DASH }, /* Y */ new long[] { DASH, GAP, DOT, GAP, DASH, GAP, DASH }, /* Z */ new long[] { DASH, GAP, DASH, GAP, DOT, GAP, DOT }, }; /** The characters from '0' to '9' */ private static final long[][] NUMBERS = new long[][] { /* 0 */ new long[] { DASH, GAP, DASH, GAP, DASH, GAP, DASH, GAP, DASH }, /* 1 */ new long[] { DOT, GAP, DASH, GAP, DASH, GAP, DASH, GAP, DASH }, /* 2 */ new long[] { DOT, GAP, DOT, GAP, DASH, GAP, DASH, GAP, DASH }, /* 3 */ new long[] { DOT, GAP, DOT, GAP, DOT, GAP, DASH, GAP, DASH }, /* 4 */ new long[] { DOT, GAP, DOT, GAP, DOT, GAP, DOT, GAP, DASH }, /* 5 */ new long[] { DOT, GAP, DOT, GAP, DOT, GAP, DOT, GAP, DOT }, /* 6 */ new long[] { DASH, GAP, DOT, GAP, DOT, GAP, DOT, GAP, DOT }, /* 7 */ new long[] { DASH, GAP, DASH, GAP, DOT, GAP, DOT, GAP, DOT }, /* 8 */ new long[] { DASH, GAP, DASH, GAP, DASH, GAP, DOT, GAP, DOT }, /* 9 */ new long[] { DASH, GAP, DASH, GAP, DASH, GAP, DASH, GAP, DOT }, }; private static final long[] ERROR_GAP = new long[] { GAP }; /** Return the pattern data for a given character */ static long[] pattern(char c) { if (c >= 'A' && c <= 'Z') { return LETTERS[c - 'A']; } if (c >= 'a' && c <= 'z') { return LETTERS[c - 'a']; } else if (c >= '0' && c <= '9') { return NUMBERS[c - '0']; } else { return ERROR_GAP; } } static long[] pattern(String str) { boolean lastWasWhitespace; int strlen = str.length(); // Calculate how long our array needs to be. int len = 1; lastWasWhitespace = true; for (int i=0; i<strlen; i++) { char c = str.charAt(i); if (Character.isWhitespace(c)) { if (!lastWasWhitespace) { len++; lastWasWhitespace = true; } } else { if (!lastWasWhitespace) { len++; } lastWasWhitespace = false; len += pattern(c).length; } } // Generate the pattern array. Note that we put an extra element of 0 // in at the beginning, because the pattern always starts with the pause, // not with the vibration. long[] result = new long[len+1]; result[0] = 0; int pos = 1; lastWasWhitespace = true; for (int i=0; i<strlen; i++) { char c = str.charAt(i); if (Character.isWhitespace(c)) { if (!lastWasWhitespace) { result[pos] = WORD_GAP; pos++; lastWasWhitespace = true; } } else { if (!lastWasWhitespace) { result[pos] = LETTER_GAP; pos++; } lastWasWhitespace = false; long[] letter = pattern(c); System.arraycopy(letter, 0, result, pos, letter.length); pos += letter.length; } } return result; } } //main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="4dip" /> <Button android:id="@+id/button" android:text="vibrate" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
Using Vibrator
import android.content.Context; import android.content.SharedPreferences; import android.os.Vibrator; class Utils { public static SharedPreferences preferencias; public static Vibrator v; public static final int CORTO = 50; public static final int LARGO = 500; static public void vibrar(Context ctx, int t){ //boolean activo = Utils.p.getBoolean("cbVibrar", true); if(!preferencias.getBoolean("cbVibrar", true)) return; v = (Vibrator) ctx.getSystemService(Context.VIBRATOR_SERVICE); v.vibrate(t); } }