xref: /haiku/src/preferences/bluetooth/InquiryPanel.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
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