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
32using Bluetooth::DeviceListItem;
33
34// private funcionaility provided by kit
35extern uint8 GetInquiryTime();
36
37static const uint32 kMsgStart = 'InSt';
38static const uint32 kMsgFinish = 'InFn';
39static const uint32 kMsgShowDebug = 'ShDG';
40
41static const uint32 kMsgInquiry = 'iQbt';
42static const uint32 kMsgAddListDevice = 'aDdv';
43
44static const uint32 kMsgSelected = 'isLt';
45static const uint32 kMsgSecond = 'sCMs';
46static const uint32 kMsgRetrieve = 'IrEt';
47
48
49class PanelDiscoveryListener : public DiscoveryListener {
50
51public:
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
88private:
89	InquiryPanel*	fInquiryPanel;
90
91};
92
93
94InquiryPanel::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
175void
176InquiryPanel::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
324void
325InquiryPanel::UpdateListStatus(void)
326{
327	if (fRemoteList->CurrentSelection() < 0 || fScanning || fRetrieving)
328		fAddButton->SetEnabled(false);
329	else
330		fAddButton->SetEnabled(true);
331}
332
333
334bool
335InquiryPanel::QuitRequested(void)
336{
337
338	return true;
339}
340