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 fScanProgress->SetBarColor(activeColor); 219 220 fAddButton->SetEnabled(false); 221 fInquiryButton->SetEnabled(false); 222 223 BMessageRunner::StartSending(fMessenger, fSecondsMessage, 1000000, timer); 224 225 scanningTime = 1; 226 fScanning = true; 227 228 break; 229 230 case kMsgFinish: 231 232 retrievalIndex = 0; 233 fScanning = false; 234 fRetrieving = true; 235 labelPlaced = false; 236 fScanProgress->SetTo(100); 237 fScanProgress->SetTrailingText(B_TRANSLATE("Retrieving names...")); 238 BMessageRunner::StartSending(fMessenger, fRetrieveMessage, 1000000, 1); 239 240 break; 241 242 case kMsgSecond: 243 if (fScanning && scanningTime < timer) { 244 // TODO time formatting could use Locale Kit 245 246 // TODO should not be needed if SetMaxValue works... 247 fScanProgress->SetTo(scanningTime * 100 / timer); 248 BString elapsedTime = B_TRANSLATE("Remaining %1 seconds"); 249 250 BString seconds(""); 251 seconds << (int)(timer - scanningTime); 252 253 elapsedTime.ReplaceFirst("%1", seconds.String()); 254 fScanProgress->SetTrailingText(elapsedTime.String()); 255 256 scanningTime = scanningTime + 1; 257 } 258 break; 259 260 case kMsgRetrieve: 261 262 if (fRetrieving) { 263 264 if (retrievalIndex < fDiscoveryAgent->RetrieveDevices(0).CountItems()) { 265 266 if (!labelPlaced) { 267 268 labelPlaced = true; 269 BString progressText(B_TRANSLATE("Retrieving name of %1")); 270 271 BString namestr; 272 namestr << bdaddrUtils::ToString(fDiscoveryAgent 273 ->RetrieveDevices(0).ItemAt(retrievalIndex) 274 ->GetBluetoothAddress()); 275 progressText.ReplaceFirst("%1", namestr.String()); 276 fScanProgress->SetTrailingText(progressText.String()); 277 278 } else { 279 // Really erally expensive operation should be done in a separate thread 280 // once Haiku gets a BarberPole in API replacing the progress bar 281 ((DeviceListItem*)fRemoteList->ItemAt(retrievalIndex)) 282 ->SetDevice((BluetoothDevice*) fDiscoveryAgent 283 ->RetrieveDevices(0).ItemAt(retrievalIndex)); 284 fRemoteList->InvalidateItem(retrievalIndex); 285 286 retrievalIndex++; 287 labelPlaced = false; 288 } 289 290 BMessageRunner::StartSending(fMessenger, fRetrieveMessage, 500000, 1); 291 292 } else { 293 294 fRetrieving = false; 295 retrievalIndex = 0; 296 297 fScanProgress->SetBarColor( 298 ui_color(B_PANEL_BACKGROUND_COLOR)); 299 fScanProgress->SetTrailingText( 300 B_TRANSLATE("Scanning completed.")); 301 fInquiryButton->SetEnabled(true); 302 UpdateListStatus(); 303 } 304 } 305 306 break; 307 308 default: 309 BWindow::MessageReceived(message); 310 break; 311 } 312 } 313 314 315 void 316 InquiryPanel::UpdateListStatus(void) 317 { 318 if (fRemoteList->CurrentSelection() < 0 || fScanning || fRetrieving) 319 fAddButton->SetEnabled(false); 320 else 321 fAddButton->SetEnabled(true); 322 } 323 324 325 bool 326 InquiryPanel::QuitRequested(void) 327 { 328 329 return true; 330 } 331