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