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