1 /* 2 * Copyright 2008-09, Oliver Ruiz Dorantes, <oliver.ruiz.dorantes_at_gmail.com> 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include <Alert.h> 7 #include <Button.h> 8 #include <Catalog.h> 9 #include <GroupLayoutBuilder.h> 10 #include <Locale.h> 11 #include <ListView.h> 12 #include <ListItem.h> 13 #include <MessageRunner.h> 14 #include <ScrollView.h> 15 #include <StatusBar.h> 16 #include <SpaceLayoutItem.h> 17 #include <TextView.h> 18 #include <TabView.h> 19 20 #include <bluetooth/bdaddrUtils.h> 21 #include <bluetooth/DiscoveryAgent.h> 22 #include <bluetooth/DiscoveryListener.h> 23 #include <bluetooth/LocalDevice.h> 24 25 #include "defs.h" 26 #include "DeviceListItem.h" 27 #include "InquiryPanel.h" 28 29 #define B_TRANSLATE_CONTEXT "Inquiry panel" 30 31 using Bluetooth::DeviceListItem; 32 33 // private funcionaility provided by kit 34 extern uint8 GetInquiryTime(); 35 36 static const uint32 kMsgStart = 'InSt'; 37 static const uint32 kMsgFinish = 'InFn'; 38 static const uint32 kMsgShowDebug = 'ShDG'; 39 40 static const uint32 kMsgInquiry = 'iQbt'; 41 static const uint32 kMsgAddListDevice = 'aDdv'; 42 43 static const uint32 kMsgSelected = 'isLt'; 44 static const uint32 kMsgSecond = 'sCMs'; 45 static const uint32 kMsgRetrieve = 'IrEt'; 46 47 48 class PanelDiscoveryListener : public DiscoveryListener { 49 50 public: 51 52 PanelDiscoveryListener(InquiryPanel* iPanel) 53 : 54 DiscoveryListener(), 55 fInquiryPanel(iPanel) 56 { 57 58 } 59 60 61 void 62 DeviceDiscovered(RemoteDevice* btDevice, DeviceClass cod) 63 { 64 BMessage* message = new BMessage(kMsgAddListDevice); 65 66 message->AddPointer("remoteItem", new DeviceListItem(btDevice)); 67 68 fInquiryPanel->PostMessage(message); 69 } 70 71 72 void 73 InquiryCompleted(int discType) 74 { 75 BMessage* message = new BMessage(kMsgFinish); 76 fInquiryPanel->PostMessage(message); 77 } 78 79 80 void 81 InquiryStarted(status_t status) 82 { 83 BMessage* message = new BMessage(kMsgStart); 84 fInquiryPanel->PostMessage(message); 85 } 86 87 private: 88 InquiryPanel* fInquiryPanel; 89 90 }; 91 92 93 InquiryPanel::InquiryPanel(BRect frame, LocalDevice* lDevice) 94 : 95 BWindow(frame, "Bluetooth", B_FLOATING_WINDOW, 96 B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS, B_ALL_WORKSPACES ), 97 fMessenger(this), 98 fScanning(false), 99 fRetrieving(false), 100 fLocalDevice(lDevice) 101 102 { 103 SetLayout(new BGroupLayout(B_HORIZONTAL)); 104 105 fScanProgress = new BStatusBar("status", 106 B_TRANSLATE("Scanning progress"), ""); 107 activeColor = fScanProgress->BarColor(); 108 109 if (fLocalDevice == NULL) 110 fLocalDevice = LocalDevice::GetLocalDevice(); 111 112 fMessage = new BTextView("description", B_WILL_DRAW); 113 fMessage->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 114 fMessage->SetLowColor(fMessage->ViewColor()); 115 fMessage->MakeEditable(false); 116 fMessage->MakeSelectable(false); 117 118 fInquiryButton = new BButton("Inquiry", B_TRANSLATE("Inquiry"), 119 new BMessage(kMsgInquiry), B_WILL_DRAW); 120 121 fAddButton = new BButton("add", B_TRANSLATE("Add device to list"), 122 new BMessage(kMsgAddToRemoteList), B_WILL_DRAW); 123 fAddButton->SetEnabled(false); 124 125 fRemoteList = new BListView("AttributeList", B_SINGLE_SELECTION_LIST); 126 fRemoteList->SetSelectionMessage(new BMessage(kMsgSelected)); 127 128 fScrollView = new BScrollView("ScrollView", fRemoteList, 0, false, true); 129 fScrollView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 130 131 if (fLocalDevice != NULL) { 132 fMessage->SetText(B_TRANSLATE( 133 "Check that the Bluetooth capabilities of your" 134 " remote device are activated. Press 'Inquiry' to start scanning." 135 " The needed time for the retrieval of the names is unknown, " 136 "although should not take more than 3 seconds per device. " 137 "Afterwards you will be able to add them to your main list," 138 " where you will be able to pair with them.")); 139 fInquiryButton->SetEnabled(true); 140 fDiscoveryAgent = fLocalDevice->GetDiscoveryAgent(); 141 fDiscoveryListener = new PanelDiscoveryListener(this); 142 143 144 SetTitle((const char*)(fLocalDevice->GetFriendlyName().String())); 145 146 147 } else { 148 fMessage->SetText(B_TRANSLATE("There isn't any Bluetooth LocalDevice " 149 "registered on the system.")); 150 fInquiryButton->SetEnabled(false); 151 } 152 153 fRetrieveMessage = new BMessage(kMsgRetrieve); 154 fSecondsMessage = new BMessage(kMsgSecond); 155 156 157 AddChild(BGroupLayoutBuilder(B_VERTICAL, 10) 158 .Add(fMessage) 159 .Add(BSpaceLayoutItem::CreateVerticalStrut(5)) 160 .Add(fScanProgress) 161 .Add(BSpaceLayoutItem::CreateVerticalStrut(5)) 162 .Add(fScrollView) 163 .Add(BSpaceLayoutItem::CreateVerticalStrut(5)) 164 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 165 .Add(fAddButton) 166 .AddGlue() 167 .Add(fInquiryButton) 168 ) 169 .SetInsets(15, 25, 15, 15) 170 ); 171 } 172 173 174 void 175 InquiryPanel::MessageReceived(BMessage* message) 176 { 177 static float timer = 0; // expected time of the inquiry process 178 static float scanningTime = 0; 179 static int32 retrievalIndex = 0; 180 static bool labelPlaced = false; 181 182 switch (message->what) { 183 case kMsgInquiry: 184 185 fDiscoveryAgent->StartInquiry(BT_GIAC, fDiscoveryListener, GetInquiryTime()); 186 187 timer = BT_BASE_INQUIRY_TIME * GetInquiryTime() + 1; 188 // does it works as expected? 189 fScanProgress->SetMaxValue(timer); 190 191 break; 192 193 case kMsgAddListDevice: 194 { 195 DeviceListItem* listItem; 196 197 message->FindPointer("remoteItem", (void **)&listItem); 198 199 fRemoteList->AddItem(listItem); 200 } 201 break; 202 203 case kMsgAddToRemoteList: 204 { 205 message->PrintToStream(); 206 int32 index = fRemoteList->CurrentSelection(0); 207 DeviceListItem* item = (DeviceListItem*) fRemoteList->RemoveItem(index);; 208 209 BMessage message(kMsgAddToRemoteList); 210 message.AddPointer("device", item); 211 212 be_app->PostMessage(&message); 213 // TODO: all others listitems can be deleted 214 } 215 break; 216 217 case kMsgSelected: 218 UpdateListStatus(); 219 break; 220 221 case kMsgStart: 222 fRemoteList->MakeEmpty(); 223 fScanProgress->Reset(); 224 fScanProgress->SetTo(1); 225 fScanProgress->SetTrailingText(B_TRANSLATE("Starting scan...")); 226 fScanProgress->SetBarColor(activeColor); 227 228 fAddButton->SetEnabled(false); 229 fInquiryButton->SetEnabled(false); 230 231 BMessageRunner::StartSending(fMessenger, fSecondsMessage, 1000000, timer); 232 233 scanningTime = 1; 234 fScanning = true; 235 236 break; 237 238 case kMsgFinish: 239 240 retrievalIndex = 0; 241 fScanning = false; 242 fRetrieving = true; 243 labelPlaced = false; 244 fScanProgress->SetTo(100); 245 fScanProgress->SetTrailingText(B_TRANSLATE("Retrieving names...")); 246 BMessageRunner::StartSending(fMessenger, fRetrieveMessage, 1000000, 1); 247 248 break; 249 250 case kMsgSecond: 251 if (fScanning && scanningTime < timer) { 252 // TODO time formatting could use Locale Kit 253 BString elapsedTime = B_TRANSLATE("Remaining "); 254 255 // TODO should not be needed if SetMaxValue works... 256 fScanProgress->SetTo(scanningTime * 100 / timer); 257 258 elapsedTime << (int)(timer - scanningTime) << 259 B_TRANSLATE(" seconds"); 260 fScanProgress->SetTrailingText(elapsedTime.String()); 261 262 scanningTime = scanningTime + 1; 263 } 264 break; 265 266 case kMsgRetrieve: 267 268 if (fRetrieving) { 269 270 if (retrievalIndex < fDiscoveryAgent->RetrieveDevices(0).CountItems()) { 271 272 if (!labelPlaced) { 273 274 labelPlaced = true; 275 BString progressText = 276 B_TRANSLATE("Retrieving name of "); 277 progressText << bdaddrUtils::ToString(fDiscoveryAgent 278 ->RetrieveDevices(0).ItemAt(retrievalIndex) 279 ->GetBluetoothAddress()); 280 fScanProgress->SetTrailingText(progressText.String()); 281 282 } else { 283 // Really erally expensive operation should be done in a separate thread 284 // once Haiku gets a BarberPole in API replacing the progress bar 285 ((DeviceListItem*)fRemoteList->ItemAt(retrievalIndex)) 286 ->SetDevice((BluetoothDevice*) fDiscoveryAgent 287 ->RetrieveDevices(0).ItemAt(retrievalIndex)); 288 fRemoteList->InvalidateItem(retrievalIndex); 289 290 retrievalIndex++; 291 labelPlaced = false; 292 } 293 294 BMessageRunner::StartSending(fMessenger, fRetrieveMessage, 500000, 1); 295 296 } else { 297 298 fRetrieving = false; 299 retrievalIndex = 0; 300 301 fScanProgress->SetBarColor( 302 ui_color(B_PANEL_BACKGROUND_COLOR)); 303 fScanProgress->SetTrailingText( 304 B_TRANSLATE("Scanning completed.")); 305 fInquiryButton->SetEnabled(true); 306 UpdateListStatus(); 307 } 308 } 309 310 break; 311 312 default: 313 BWindow::MessageReceived(message); 314 break; 315 } 316 } 317 318 319 void 320 InquiryPanel::UpdateListStatus(void) 321 { 322 if (fRemoteList->CurrentSelection() < 0 || fScanning || fRetrieving) 323 fAddButton->SetEnabled(false); 324 else 325 fAddButton->SetEnabled(true); 326 } 327 328 329 bool 330 InquiryPanel::QuitRequested(void) 331 { 332 333 return true; 334 } 335