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 <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 SetLayout(new BGroupLayout(B_HORIZONTAL)); 105 106 fScanProgress = new BStatusBar("status", 107 B_TRANSLATE("Scanning progress"), ""); 108 activeColor = fScanProgress->BarColor(); 109 110 if (fLocalDevice == NULL) 111 fLocalDevice = LocalDevice::GetLocalDevice(); 112 113 fMessage = new BTextView("description", B_WILL_DRAW); 114 fMessage->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 115 fMessage->SetLowColor(fMessage->ViewColor()); 116 fMessage->MakeEditable(false); 117 fMessage->MakeSelectable(false); 118 119 fInquiryButton = new BButton("Inquiry", B_TRANSLATE("Inquiry"), 120 new BMessage(kMsgInquiry), B_WILL_DRAW); 121 122 fAddButton = new BButton("add", B_TRANSLATE("Add device to list"), 123 new BMessage(kMsgAddToRemoteList), B_WILL_DRAW); 124 fAddButton->SetEnabled(false); 125 126 fRemoteList = new BListView("AttributeList", B_SINGLE_SELECTION_LIST); 127 fRemoteList->SetSelectionMessage(new BMessage(kMsgSelected)); 128 129 fScrollView = new BScrollView("ScrollView", fRemoteList, 0, false, true); 130 fScrollView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 131 132 if (fLocalDevice != NULL) { 133 fMessage->SetText(B_TRANSLATE( 134 "Check that the Bluetooth capabilities of your" 135 " remote device are activated. Press 'Inquiry' to start scanning." 136 " The needed time for the retrieval of the names is unknown, " 137 "although should not take more than 3 seconds per device. " 138 "Afterwards you will be able to add them to your main list," 139 " where you will be able to pair with them.")); 140 fInquiryButton->SetEnabled(true); 141 fDiscoveryAgent = fLocalDevice->GetDiscoveryAgent(); 142 fDiscoveryListener = new PanelDiscoveryListener(this); 143 144 145 SetTitle((const char*)(fLocalDevice->GetFriendlyName().String())); 146 147 148 } else { 149 fMessage->SetText(B_TRANSLATE("There isn't any Bluetooth LocalDevice " 150 "registered on the system.")); 151 fInquiryButton->SetEnabled(false); 152 } 153 154 fRetrieveMessage = new BMessage(kMsgRetrieve); 155 fSecondsMessage = new BMessage(kMsgSecond); 156 157 158 AddChild(BGroupLayoutBuilder(B_VERTICAL, 10) 159 .Add(fMessage) 160 .Add(BSpaceLayoutItem::CreateVerticalStrut(5)) 161 .Add(fScanProgress) 162 .Add(BSpaceLayoutItem::CreateVerticalStrut(5)) 163 .Add(fScrollView) 164 .Add(BSpaceLayoutItem::CreateVerticalStrut(5)) 165 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 166 .Add(fAddButton) 167 .AddGlue() 168 .Add(fInquiryButton) 169 ) 170 .SetInsets(15, 25, 15, 15) 171 ); 172 } 173 174 175 void 176 InquiryPanel::MessageReceived(BMessage* message) 177 { 178 static float timer = 0; // expected time of the inquiry process 179 static float scanningTime = 0; 180 static int32 retrievalIndex = 0; 181 static bool labelPlaced = false; 182 183 switch (message->what) { 184 case kMsgInquiry: 185 186 fDiscoveryAgent->StartInquiry(BT_GIAC, fDiscoveryListener, GetInquiryTime()); 187 188 timer = BT_BASE_INQUIRY_TIME * GetInquiryTime() + 1; 189 // does it works as expected? 190 fScanProgress->SetMaxValue(timer); 191 192 break; 193 194 case kMsgAddListDevice: 195 { 196 DeviceListItem* listItem; 197 198 message->FindPointer("remoteItem", (void **)&listItem); 199 200 fRemoteList->AddItem(listItem); 201 } 202 break; 203 204 case kMsgAddToRemoteList: 205 { 206 message->PrintToStream(); 207 int32 index = fRemoteList->CurrentSelection(0); 208 DeviceListItem* item = (DeviceListItem*) fRemoteList->RemoveItem(index);; 209 210 BMessage message(kMsgAddToRemoteList); 211 message.AddPointer("device", item); 212 213 be_app->PostMessage(&message); 214 // TODO: all others listitems can be deleted 215 } 216 break; 217 218 case kMsgSelected: 219 UpdateListStatus(); 220 break; 221 222 case kMsgStart: 223 fRemoteList->MakeEmpty(); 224 fScanProgress->Reset(); 225 fScanProgress->SetTo(1); 226 fScanProgress->SetTrailingText(B_TRANSLATE("Starting scan...")); 227 fScanProgress->SetBarColor(activeColor); 228 229 fAddButton->SetEnabled(false); 230 fInquiryButton->SetEnabled(false); 231 232 BMessageRunner::StartSending(fMessenger, fSecondsMessage, 1000000, timer); 233 234 scanningTime = 1; 235 fScanning = true; 236 237 break; 238 239 case kMsgFinish: 240 241 retrievalIndex = 0; 242 fScanning = false; 243 fRetrieving = true; 244 labelPlaced = false; 245 fScanProgress->SetTo(100); 246 fScanProgress->SetTrailingText(B_TRANSLATE("Retrieving names...")); 247 BMessageRunner::StartSending(fMessenger, fRetrieveMessage, 1000000, 1); 248 249 break; 250 251 case kMsgSecond: 252 if (fScanning && scanningTime < timer) { 253 // TODO time formatting could use Locale Kit 254 255 // TODO should not be needed if SetMaxValue works... 256 fScanProgress->SetTo(scanningTime * 100 / timer); 257 BString elapsedTime = B_TRANSLATE("Remaining %1 seconds"); 258 259 BString seconds(""); 260 seconds << (int)(timer - scanningTime); 261 262 elapsedTime.ReplaceFirst("%1", seconds.String()); 263 fScanProgress->SetTrailingText(elapsedTime.String()); 264 265 scanningTime = scanningTime + 1; 266 } 267 break; 268 269 case kMsgRetrieve: 270 271 if (fRetrieving) { 272 273 if (retrievalIndex < fDiscoveryAgent->RetrieveDevices(0).CountItems()) { 274 275 if (!labelPlaced) { 276 277 labelPlaced = true; 278 BString progressText(B_TRANSLATE("Retrieving name of %1")); 279 280 BString namestr; 281 namestr << bdaddrUtils::ToString(fDiscoveryAgent 282 ->RetrieveDevices(0).ItemAt(retrievalIndex) 283 ->GetBluetoothAddress()); 284 progressText.ReplaceFirst("%1", namestr.String()); 285 fScanProgress->SetTrailingText(progressText.String()); 286 287 } else { 288 // Really erally expensive operation should be done in a separate thread 289 // once Haiku gets a BarberPole in API replacing the progress bar 290 ((DeviceListItem*)fRemoteList->ItemAt(retrievalIndex)) 291 ->SetDevice((BluetoothDevice*) fDiscoveryAgent 292 ->RetrieveDevices(0).ItemAt(retrievalIndex)); 293 fRemoteList->InvalidateItem(retrievalIndex); 294 295 retrievalIndex++; 296 labelPlaced = false; 297 } 298 299 BMessageRunner::StartSending(fMessenger, fRetrieveMessage, 500000, 1); 300 301 } else { 302 303 fRetrieving = false; 304 retrievalIndex = 0; 305 306 fScanProgress->SetBarColor( 307 ui_color(B_PANEL_BACKGROUND_COLOR)); 308 fScanProgress->SetTrailingText( 309 B_TRANSLATE("Scanning completed.")); 310 fInquiryButton->SetEnabled(true); 311 UpdateListStatus(); 312 } 313 } 314 315 break; 316 317 default: 318 BWindow::MessageReceived(message); 319 break; 320 } 321 } 322 323 324 void 325 InquiryPanel::UpdateListStatus(void) 326 { 327 if (fRemoteList->CurrentSelection() < 0 || fScanning || fRetrieving) 328 fAddButton->SetEnabled(false); 329 else 330 fAddButton->SetEnabled(true); 331 } 332 333 334 bool 335 InquiryPanel::QuitRequested(void) 336 { 337 338 return true; 339 } 340