2016年8月3日 星期三

Bluetooth car 的android source code study



他用了四個imagebutton


private ImageButton mimageButton1;//upprivate ImageButton mimageButton2;//downprivate ImageButton mimageButton3;//rightprivate ImageButton mimageButton4;//left



代表上面那四個button

先來看  onCreate()

 @Override  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      if(D) Log.e(TAG, "+++ ON CREATE +++");

      // Set up the window layout      requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
      setContentView(R.layout.main);
      getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);

      // Set up the custom title      mTitle = (TextView) findViewById(R.id.title_left_text);
      mTitle.setText(R.string.app_name);
      mTitle = (TextView) findViewById(R.id.title_right_text);

      // 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;
      }
      mimageButton1 = (ImageButton)findViewById(R.id.imageButton1);    //up            mimageButton2 = (ImageButton)findViewById(R.id.imageButton2);   //down          mimageButton3 = (ImageButton)findViewById(R.id.imageButton3);   //right         mimageButton4 = (ImageButton)findViewById(R.id.imageButton4);  //left              
      //  set listener 的function...  就是把  Init_send_val 設成相對的value
      mimageButton1.setOnTouchListener(new ImageButton.OnTouchListener(){
       @Override             public boolean onTouch(View v, MotionEvent event) {    
           if(event.getAction() == MotionEvent.ACTION_DOWN){       
              Init_send_val = KEY_UP ;      
                Log.v(TAG,"User press the button");
           }else if(event.getAction() == MotionEvent.ACTION_UP){       
                Init_send_val = KEY_NOTHING;      
                Log.v(TAG,"User release the button");
           }       
           return false;       
       }       
      });
      
      mimageButton2.setOnTouchListener(new ImageButton.OnTouchListener(){
       @Override             public boolean onTouch(View v, MotionEvent event) {    
           if(event.getAction() == MotionEvent.ACTION_DOWN){       
              Init_send_val = KEY_DOWN ;      
                Log.v(TAG,"User press the button");
           }else if(event.getAction() == MotionEvent.ACTION_UP){       
                Init_send_val = KEY_NOTHING;      
              Log.v(TAG,"User release the button");
           }       
           return false;       
       }       
      }); 
      
      mimageButton3.setOnTouchListener(new ImageButton.OnTouchListener(){
       @Override             public boolean onTouch(View v, MotionEvent event) {    
           if(event.getAction() == MotionEvent.ACTION_DOWN){       
              Init_send_val = KEY_RIGHT ;      
              Log.v(TAG,"User press the button");
           }else if(event.getAction() == MotionEvent.ACTION_UP){       
                Init_send_val = KEY_NOTHING;      
                Log.v(TAG,"User release the button");
           }       
           return false;       
       }       
      });
      
      mimageButton4.setOnTouchListener(new ImageButton.OnTouchListener(){
       @Override             public boolean onTouch(View v, MotionEvent event) {    
           if(event.getAction() == MotionEvent.ACTION_DOWN){       
              Init_send_val = KEY_LEFT ;      
              Log.v(TAG,"User press the button");
           }else if(event.getAction() == MotionEvent.ACTION_UP){       
                Init_send_val = KEY_NOTHING;      
                Log.v(TAG,"User release the button");
           }       
           return false;       
       }       
      });

然後在onResume() 的時候...去判斷  mChatService是不是null..

如果是null...  就呼叫mChatService.start();

@Overridepublic 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();
        }
    }
}


start()的實作如下:

啟動了  mSecureAcceptThread 和 mInsecureAcceptThread 這兩種  AcceptThread 的thread 

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();
    }
}

宣告一個執行緒
mHandlerTime.removeCallbacks(car_timer);
mHandlerTime.postDelayed(car_timer, 400);
          
  }



car_timer 的實作如下

其實就是定期的去check mChatService.getState()

如果已經連線....就把 Init_send_val 的  value 送出去...

透過  mChatService.write(send);

然後每400ms 執行一次   mHandlerTime.postDelayed(this, 400);

private final Runnable car_timer = new Runnable()
  {
    public void run()
    {
      if (mChatService.getState() == BluetoothChatService.STATE_CONNECTED) {
           if(Init_send_val == KEY_NOTHING)
           {
               // Get the message bytes and tell the BluetoothChatService to write               byte[] send = new byte[] {(byte) 0xAA, (byte) 0xBB, 0x02, 0x00,0x02};
               mChatService.write(send);
               // Reset out string buffer to zero and clear the edit text field               mOutStringBuffer.setLength(0);
               
           }
           else if(Init_send_val == KEY_UP )
           {
               // Get the message bytes and tell the BluetoothChatService to write               byte[] send = new byte[] {(byte) 0xAA, (byte) 0xBB, 0x02, (byte) 0xF1, 0x03};
               mChatService.write(send);
               // Reset out string buffer to zero and clear the edit text field               mOutStringBuffer.setLength(0);
               
           }
           else if(Init_send_val== KEY_DOWN )
           {
               // Get the message bytes and tell the BluetoothChatService to write               byte[] send = new byte[] {(byte) 0xAA, (byte) 0xBB, 0x02, (byte) 0xF2, 0x00};
               mChatService.write(send);
               // Reset out string buffer to zero and clear the edit text field               mOutStringBuffer.setLength(0);         
           }
           else if(Init_send_val == KEY_RIGHT )
           {
               // Get the message bytes and tell the BluetoothChatService to write               byte[] send = new byte[] {(byte) 0xAA, (byte) 0xBB, 0x02, (byte) 0xF4, 0x06};
               mChatService.write(send);
               // Reset out string buffer to zero and clear the edit text field               mOutStringBuffer.setLength(0);
           }
           else if(Init_send_val == KEY_LEFT )
           {
                     // Get the message bytes and tell the BluetoothChatService to write               byte[] send = new byte[] {(byte) 0xAA, (byte) 0xBB, 0x02, (byte) 0xF8, 0x0A};
               mChatService.write(send);
               // Reset out string buffer to zero and clear the edit text field               mOutStringBuffer.setLength(0);
           }
      }

        mHandlerTime.postDelayed(this, 400);
       
    }
  };




當按下menu ...就會執行下面的code

@Overridepublic boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.option_menu, menu);
    return true;
}

這個會去讀  menu/option_menu.xml

裡面的內容如下: 就是上面那3個 item....
<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" />
    <item android:id="@+id/insecure_connect_scan"          android:icon="@android:drawable/ic_menu_search"          android:title="@string/insecure_connect" />
    <item android:id="@+id/discoverable"          android:icon="@android:drawable/ic_menu_mylocation"          android:title="@string/discoverable" />
</menu>




//  就是上面螢幕顯示的3個 item....
@Overridepublic 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;
}


這時候如果我們按下  secure_connect_scan...

就會去執行    DeviceListActivity 這個class  


先來看onCreate()

// Member fieldsprivate BluetoothAdapter mBtAdapter;

這裡分兩個  ArayAdapter...

一個還沒有配對的....  一個是已經配對的...
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
private ArrayAdapter<String> mNewDevicesArrayAdapter;


@Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Setup the window    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.device_list);

    // Set result CANCELED incase 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);
    }
}


可以看到上面的畫面

從 layout/device_list.xml

其實就是用一個垂直的LinearLayout


<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"    />

從這個 xml 可以看出

Paired Device 是 TextView

下面就是接一個Listview.... 會把所有掃到的blue-tooth 列出來

然後再一個 TextView  ...秀出 "Other Available Devices"

下面就是接一個Listview.... 會把所有掃到的其他的已經有的blue-tooth 列出來

最後再一個button...   去scan device

這個button 的執行程式如下:

Button scanButton = (Button) findViewById(R.id.button_scan);
scanButton.setOnClickListener(new OnClickListener() {
    public void onClick(View v) {
        doDiscovery();
        v.setVisibility(View.GONE);
    }
});

會呼叫 doDiscovery()...  會呼叫一個progressbar()

最後會呼叫  
 mBtAdapter.startDiscovery();


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();
}

當在某個項目上按下去....就會跳來執行這個function  

因為  pairedListView.setOnItemClickListener(mDeviceClickListener);
       newDevicesListView.setOnItemClickListener(mDeviceClickListener);

這兩行設定的
    

秀出 這個device 的字串和MAC address..  然後就切換回去剛剛的intent()

因為呼叫了

   setResult(Activity.RESULT_OK, intent);
  finish()....

// The on-click listener for all devices in the ListViewsprivate 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();
    }
};

就會去執行這個function.....

首先會先判斷  requestCode... 因為我們是選  REQUEST_CONNECT_DEVICE_SECURE

所以會去跑到這裡

  if (resultCode == Activity.RESULT_OK) {
            connectDevice(data, true);
        }

然後會再看  resultCode...  由於剛剛傳回來的  RESULT_OK

所以將會去執行   connectDevice(data, true);

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 occured            Log.d(TAG, "BT not enabled");
            Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
            finish();
        }
    }
}

connectDevice()的實作如下:

 String address = data.getExtras()
        .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);

這個會得到剛剛放進去的 device_address  ->    String address = info.substring(info.length() - 17);

然後呼叫    mBluetoothAdapter.getRemoteDevice(address)

得到  BluetoothDevice device   這個device...

最後再呼叫  mChatService.connect(device, secure) 去進行連線...


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);
}


接下來看 connect()....

其實就是啟動了  mConnectThread thread...


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);
}

這邊的code 就跟blue-tooth chat 的範例一樣了







沒有留言:

張貼留言