1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.android.car.kitchensink.bluetooth; 18 19 import android.app.PendingIntent; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothDevicePicker; 23 import android.bluetooth.BluetoothMapClient; 24 import android.bluetooth.BluetoothProfile; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.support.annotation.Nullable; 32 import android.support.v4.app.Fragment; 33 import android.util.Log; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.widget.Button; 38 import android.widget.CheckBox; 39 import android.widget.EditText; 40 import android.widget.TextView; 41 42 import com.google.android.car.kitchensink.R; 43 44 import java.util.List; 45 46 public class MapMceTestFragment extends Fragment { 47 static final String MESSAGE_TO_SEND = "Im Busy Driving"; 48 private static final String TAG = "CAR.BLUETOOTH.KS"; 49 BluetoothMapClient mMapProfile; 50 BluetoothAdapter mBluetoothAdapter; 51 Button mDevicePicker; 52 Button mDeviceDisconnect; 53 TextView mMessage; 54 EditText mOriginator; 55 TextView mOriginatorDisplayName; 56 CheckBox mSent; 57 CheckBox mDelivered; 58 TextView mBluetoothDevice; 59 PendingIntent mSentIntent; 60 PendingIntent mDeliveredIntent; 61 NotificationReceiver mTransmissionStatusReceiver; 62 Object mLock = new Object(); 63 private Intent mSendIntent; 64 private Intent mDeliveryIntent; 65 66 @Override 67 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 68 @Nullable Bundle savedInstanceState) { 69 View v = inflater.inflate(R.layout.sms_received, container, false); 70 71 Button reply = (Button) v.findViewById(R.id.reply); 72 Button checkMessages = (Button) v.findViewById(R.id.check_messages); 73 mBluetoothDevice = (TextView) v.findViewById(R.id.bluetoothDevice); 74 mOriginator = (EditText) v.findViewById(R.id.messageOriginator); 75 mOriginatorDisplayName = (TextView) v.findViewById(R.id.messageOriginatorDisplayName); 76 mSent = (CheckBox) v.findViewById(R.id.sent_checkbox); 77 mDelivered = (CheckBox) v.findViewById(R.id.delivered_checkbox); 78 mSendIntent = new Intent(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY); 79 mDeliveryIntent = new Intent(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY); 80 mMessage = (TextView) v.findViewById(R.id.messageContent); 81 mDevicePicker = (Button) v.findViewById(R.id.bluetooth_pick_device); 82 mDeviceDisconnect = (Button) v.findViewById(R.id.bluetooth_disconnect_device); 83 //TODO add manual entry option for phone number 84 reply.setOnClickListener(new View.OnClickListener() { 85 @Override 86 public void onClick(View view) { 87 sendMessage(new Uri[]{Uri.parse(mOriginator.getText().toString())}, 88 MESSAGE_TO_SEND); 89 } 90 }); 91 92 checkMessages.setOnClickListener(new View.OnClickListener() { 93 @Override 94 public void onClick(View view) { 95 getMessages(); 96 } 97 }); 98 99 // Pick a bluetooth device 100 mDevicePicker.setOnClickListener(new View.OnClickListener() { 101 @Override 102 public void onClick(View view) { 103 launchDevicePicker(); 104 } 105 }); 106 mDeviceDisconnect.setOnClickListener(new View.OnClickListener() { 107 @Override 108 public void onClick(View view) { 109 disconnectDevice(mBluetoothDevice.getText().toString()); 110 } 111 }); 112 113 mTransmissionStatusReceiver = new NotificationReceiver(); 114 return v; 115 } 116 117 void launchDevicePicker() { 118 IntentFilter filter = new IntentFilter(); 119 filter.addAction(BluetoothDevicePicker.ACTION_DEVICE_SELECTED); 120 getContext().registerReceiver(mPickerReceiver, filter); 121 122 Intent intent = new Intent(BluetoothDevicePicker.ACTION_LAUNCH); 123 intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 124 getContext().startActivity(intent); 125 } 126 127 void disconnectDevice(String device) { 128 mMapProfile.disconnect(mBluetoothAdapter.getRemoteDevice((device))); 129 } 130 131 @Override 132 public void onResume() { 133 super.onResume(); 134 135 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 136 mBluetoothAdapter.getProfileProxy(getContext(), new MapServiceListener(), 137 BluetoothProfile.MAP_CLIENT); 138 139 IntentFilter intentFilter = new IntentFilter(); 140 intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY); 141 intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY); 142 intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED); 143 intentFilter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED); 144 getContext().registerReceiver(mTransmissionStatusReceiver, intentFilter); 145 } 146 147 @Override 148 public void onPause() { 149 super.onPause(); 150 getContext().unregisterReceiver(mTransmissionStatusReceiver); 151 } 152 153 private void getMessages() { 154 synchronized (mLock) { 155 BluetoothDevice remoteDevice; 156 try { 157 remoteDevice = mBluetoothAdapter.getRemoteDevice( 158 mBluetoothDevice.getText().toString()); 159 } catch (java.lang.IllegalArgumentException e) { 160 Log.e(TAG, e.toString()); 161 return; 162 } 163 164 if (mMapProfile != null) { 165 Log.d(TAG, "Getting Messages"); 166 mMapProfile.getUnreadMessages(remoteDevice); 167 } 168 } 169 } 170 171 private void sendMessage(Uri[] recipients, String message) { 172 synchronized (mLock) { 173 BluetoothDevice remoteDevice; 174 try { 175 remoteDevice = mBluetoothAdapter.getRemoteDevice( 176 mBluetoothDevice.getText().toString()); 177 } catch (java.lang.IllegalArgumentException e) { 178 Log.e(TAG, e.toString()); 179 return; 180 } 181 mSent.setChecked(false); 182 mDelivered.setChecked(false); 183 if (mMapProfile != null) { 184 Log.d(TAG, "Sending reply"); 185 if (recipients == null) { 186 Log.d(TAG, "Recipients is null"); 187 return; 188 } 189 if (mBluetoothDevice == null) { 190 Log.d(TAG, "BluetoothDevice is null"); 191 return; 192 } 193 194 mSentIntent = PendingIntent.getBroadcast(getContext(), 0, mSendIntent, 195 PendingIntent.FLAG_ONE_SHOT); 196 mDeliveredIntent = PendingIntent.getBroadcast(getContext(), 0, mDeliveryIntent, 197 PendingIntent.FLAG_ONE_SHOT); 198 mMapProfile.sendMessage( 199 remoteDevice, 200 recipients, message, mSentIntent, mDeliveredIntent); 201 } 202 } 203 } 204 205 class MapServiceListener implements BluetoothProfile.ServiceListener { 206 @Override 207 public void onServiceConnected(int profile, BluetoothProfile proxy) { 208 synchronized (mLock) { 209 mMapProfile = (BluetoothMapClient) proxy; 210 List<BluetoothDevice> connectedDevices = proxy.getConnectedDevices(); 211 if (connectedDevices.size() > 0) { 212 mBluetoothDevice.setText(connectedDevices.get(0).getAddress()); 213 } 214 } 215 } 216 217 @Override 218 public void onServiceDisconnected(int profile) { 219 synchronized (mLock) { 220 mMapProfile = null; 221 } 222 } 223 } 224 225 private class NotificationReceiver extends BroadcastReceiver { 226 @Override 227 public void onReceive(Context context, Intent intent) { 228 String action = intent.getAction(); 229 synchronized (mLock) { 230 if (action.equals(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED)) { 231 if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0) 232 == BluetoothProfile.STATE_CONNECTED) { 233 mBluetoothDevice.setText(((BluetoothDevice) intent.getParcelableExtra( 234 BluetoothDevice.EXTRA_DEVICE)).getAddress()); 235 } else if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0) 236 == BluetoothProfile.STATE_DISCONNECTED) { 237 mBluetoothDevice.setText("Disconnected"); 238 } 239 } else if (action.equals(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY)) { 240 mSent.setChecked(true); 241 } else if (action.equals( 242 BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY)) { 243 mDelivered.setChecked(true); 244 } else if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) { 245 String senderUri = 246 intent.getStringExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_URI); 247 if (senderUri == null) { 248 senderUri = "<null>"; 249 } 250 251 String senderName = intent.getStringExtra( 252 BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME); 253 if (senderName == null) { 254 senderName = "<null>"; 255 } 256 257 mMessage.setText(intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 258 mOriginator.setText(senderUri); 259 mOriginatorDisplayName.setText(senderName); 260 } 261 } 262 } 263 } 264 265 private final BroadcastReceiver mPickerReceiver = new BroadcastReceiver() { 266 @Override 267 public void onReceive(Context context, Intent intent) { 268 String action = intent.getAction(); 269 270 Log.v(TAG, "mPickerReceiver got " + action); 271 272 if (BluetoothDevicePicker.ACTION_DEVICE_SELECTED.equals(action)) { 273 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 274 Log.v(TAG, "mPickerReceiver got " + device); 275 mMapProfile.connect(device); 276 277 // The receiver can now be disabled. 278 getContext().unregisterReceiver(mPickerReceiver); 279 } 280 } 281 }; 282 }