13 * |
13 * |
14 * Description: |
14 * Description: |
15 * |
15 * |
16 */ |
16 */ |
17 |
17 |
18 |
|
19 #include "btdevicedata.h" |
18 #include "btdevicedata.h" |
|
19 #include <QDateTime> |
|
20 #include <btservices/advancedevdiscoverer.h> |
|
21 #include "btuiutil.h" |
|
22 #include "btqtconstants.h" |
|
23 |
|
24 class DevTypeIconMapping { |
|
25 public: |
|
26 int majorDevClass; // major device class value from CoD |
|
27 int minorDevClass; // minor device class value from CoD |
|
28 int majorProperty; // one of major properties defined in BtDeviceModel |
|
29 int minorProperty; // one of minor properties defined in BtDeviceModel |
|
30 char* connectedIcon; // the icon name for connected device |
|
31 char* unconnectedIcon; // the icon name for not connected device. |
|
32 }; |
|
33 |
|
34 // mapping table from major and minor Device Classes to device types and icons |
|
35 // which are specifically defined in btapplication namespace. |
|
36 // (Note audio device mapping is not in this table due to its complex logic) |
|
37 static const DevTypeIconMapping DeviceTypeIconMappingTable[] = |
|
38 { |
|
39 {EMajorDeviceComputer, 0, BtDeviceModel::Computer, 0, |
|
40 ":/icons/qgn_prop_bt_computer_connect.svg", ":/icons/qgn_prop_bt_computer.svg" }, |
|
41 {EMajorDevicePhone, 0, BtDeviceModel::Phone, 0, |
|
42 ":/icons/qgn_prop_bt_phone_connect.svg", ":/icons/qgn_prop_bt_phone.svg"}, |
|
43 {EMajorDeviceLanAccessPoint, 0, BtDeviceModel::LANAccessDev, 0, |
|
44 ":/icons/qgn_prop_bt_misc.svg", ":/icons/qgn_prop_bt_misc.svg" }, |
|
45 {EMajorDevicePeripheral, EMinorDevicePeripheralKeyboard, |
|
46 BtDeviceModel::Peripheral, BtDeviceModel::Keyboard, |
|
47 ":/icons/qgn_prop_bt_keyboard_connect.svg", ":/icons/qgn_prop_bt_keyboard.svg"}, |
|
48 {EMajorDevicePeripheral, EMinorDevicePeripheralPointer, |
|
49 BtDeviceModel::Peripheral, BtDeviceModel::Mouse, |
|
50 ":/icons/qgn_prop_bt_mouse_connect.svg", ":/icons/qgn_prop_bt_mouse.svg"}, |
|
51 {EMajorDeviceImaging, 0, BtDeviceModel::ImagingDev, 0, |
|
52 ":/icons/qgn_prop_bt_printer_connect.svg", ":/icons/qgn_prop_bt_printer.svg"}, |
|
53 {EMajorDeviceWearable, 0, BtDeviceModel::WearableDev, 0, |
|
54 ":/icons/qgn_prop_bt_misc.svg", ":/icons/qgn_prop_bt_misc.svg"}, |
|
55 {EMajorDeviceToy, 0, BtDeviceModel::Toy, 0, |
|
56 ":/icons/qgn_prop_bt_misc.svg", ":/icons/qgn_prop_bt_misc.svg"}, |
|
57 }; |
|
58 |
|
59 static const int DeviceTypeIconMappingTableSize = |
|
60 sizeof( DeviceTypeIconMappingTable ) / sizeof( DevTypeIconMapping ); |
20 |
61 |
21 /*! |
62 /*! |
22 Constructor. |
63 Constructor. |
23 */ |
64 */ |
24 BtDeviceData::BtDeviceData( |
65 BtDeviceData::BtDeviceData( BtDeviceModel& model, QObject *parent ) |
25 const QSharedPointer<BtuiModelDataSource> &data, |
66 : QObject( parent ), mModel( model ), mDiscover( 0 ) |
26 QObject *parent) |
67 { |
27 : QObject( parent ), mData( data ), mBtengConnMan(0) |
68 mDeviceRepo = 0; |
28 { |
69 isSearchingDevice = false; |
29 TRAP_IGNORE({ |
70 TRAP_IGNORE({ |
30 mBtengConnMan = CBTEngConnMan::NewL( this ); |
71 mDeviceRepo = CBtDevRepository::NewL(); |
31 mDeviceRepo = CBtDevRepository::NewL( ); |
|
32 }); |
72 }); |
33 |
|
34 Q_CHECK_PTR( mBtengConnMan ); |
|
35 Q_CHECK_PTR( mDeviceRepo ); |
73 Q_CHECK_PTR( mDeviceRepo ); |
|
74 TRAP_IGNORE( mDeviceRepo->AddObserverL( this ) ); |
|
75 |
|
76 if ( mDeviceRepo->IsInitialized() ) { |
|
77 initializeDataStore(); |
|
78 } |
36 } |
79 } |
37 |
80 |
38 /*! |
81 /*! |
39 Destructor. |
82 Destructor. |
40 */ |
83 */ |
41 BtDeviceData::~BtDeviceData() |
84 BtDeviceData::~BtDeviceData() |
42 { |
85 { |
43 delete mBtengConnMan; |
|
44 delete mDeviceRepo; |
86 delete mDeviceRepo; |
45 } |
87 delete mDiscover; |
46 |
88 } |
47 void BtDeviceData::ConnectComplete( TBTDevAddr& addr, TInt err, |
89 |
48 RBTDevAddrArray* conflicts ) |
90 |
49 { |
91 /*! |
50 Q_UNUSED( addr ); |
92 Tells whether the given column is in the range of the setting list. |
51 Q_UNUSED( err ); |
93 |
52 Q_UNUSED( conflicts ); |
94 \param row the row number to be checked |
53 } |
95 \param col the column number to be checked |
54 |
96 |
55 void BtDeviceData::DisconnectComplete( TBTDevAddr& addr, TInt err ) |
97 \return true if the given row and column are valid; false otherwise. |
56 { |
98 */ |
57 Q_UNUSED( addr ); |
99 bool BtDeviceData::isValid( int row, int column) const |
58 Q_UNUSED( err ); |
100 { |
59 } |
101 return row >= 0 && row < mData.count() && column == 0; |
60 |
102 } |
61 void BtDeviceData::BtDeviceDeleted( const TBTDevAddr& addr ) |
103 |
62 { |
104 /*! |
63 Q_UNUSED( addr ); |
105 \return the total amount of rows. |
64 } |
106 |
65 |
107 */ |
66 void BtDeviceData::BtDeviceAdded( const CBTDevice& device ) |
108 int BtDeviceData::rowCount() const |
67 { |
109 { |
68 Q_UNUSED( device ); |
110 return mData.count(); |
69 } |
111 } |
70 |
112 |
71 void BtDeviceData::BtDeviceChanged( const CBTDevice& device, TUint similarity ) |
113 /*! |
72 { |
114 \return the total amount of columns. |
73 Q_UNUSED( device ); |
115 |
74 Q_UNUSED( similarity ); |
116 */ |
75 } |
117 int BtDeviceData::columnCount() const |
76 |
118 { |
|
119 return 1; |
|
120 } |
|
121 |
|
122 /*! |
|
123 Gets the value within a data item. |
|
124 \param val contains the value at return. |
|
125 \param row the row number which the value is from |
|
126 \param col the column number which the value is from |
|
127 \param role the role idenfier of the value. |
|
128 */ |
|
129 void BtDeviceData::data(QVariant& val, int row, int col, int role ) const |
|
130 { |
|
131 if ( isValid( row, col ) ) { |
|
132 val = mData.at( row ).value( role ); |
|
133 } |
|
134 else { |
|
135 val = QVariant( QVariant::Invalid ); |
|
136 } |
|
137 } |
|
138 |
|
139 /*! |
|
140 Gets the whole item data at the specified column |
|
141 \param row the row number of the item data to be returned |
|
142 \param col the column number of the item data to be returned |
|
143 \return the item data |
|
144 */ |
|
145 BtuiModelDataItem BtDeviceData::itemData( int row, int col ) const |
|
146 { |
|
147 if ( isValid( row, col ) ) { |
|
148 return mData.at( row ); |
|
149 } |
|
150 return BtuiModelDataItem(); |
|
151 } |
|
152 |
|
153 |
|
154 /*! |
|
155 Requests the model to searching Bluetooth devices. |
|
156 \return true if the request is accepted; false otherwise |
|
157 */ |
|
158 bool BtDeviceData::searchDevice() |
|
159 { |
|
160 int err ( 0 ); |
|
161 removeTransientDevices(); |
|
162 if ( !mDiscover ) { |
|
163 TRAP(err, mDiscover = CAdvanceDevDiscoverer::NewL( *mDeviceRepo, *this) ); |
|
164 } |
|
165 if ( !err ) { |
|
166 TRAP(err, mDiscover->DiscoverDeviceL() ); |
|
167 } |
|
168 isSearchingDevice = true; |
|
169 return err == 0; |
|
170 } |
|
171 |
|
172 /*! |
|
173 Cancels a possible outstanding device search request. |
|
174 */ |
|
175 void BtDeviceData::cancelSearchDevice() |
|
176 { |
|
177 if ( mDiscover ) { |
|
178 isSearchingDevice = false; |
|
179 mDiscover->CancelDiscovery(); |
|
180 } |
|
181 } |
|
182 |
|
183 /*! |
|
184 Removes transient (not-in-registry) devices |
|
185 (added as the result of device search). |
|
186 */ |
|
187 void BtDeviceData::removeTransientDevices() |
|
188 { |
|
189 // clear in-range property for all device items in this model. |
|
190 int cnt = mData.count(); |
|
191 for ( int i = mData.count() - 1; i > -1; --i) |
|
192 { |
|
193 const BtuiModelDataItem& qtdev = mData.at(i); |
|
194 if(isDeviceInRange(qtdev)) { |
|
195 if(isDeviceInRegistry(qtdev)) { |
|
196 // we cannot remove this device as it is in registry. |
|
197 // remove it in-range property. |
|
198 setMajorProperty(mData[i], BtDeviceModel::InRange, false); |
|
199 updateRssi(mData[i], RssiInvalid); |
|
200 mModel.emitDataChanged( i, 0, this ); |
|
201 } |
|
202 else { |
|
203 // this device is not in-registry. Delete it from local |
|
204 // store. |
|
205 mModel.beginRemoveRows(QModelIndex(), i, i); |
|
206 mData.removeAt( i ); |
|
207 mModel.endRemoveRows(); |
|
208 } |
|
209 } |
|
210 } |
|
211 } |
|
212 |
|
213 /*! |
|
214 callback from repository. |
|
215 re-initialize our store. |
|
216 */ |
|
217 void BtDeviceData::RepositoryInitialized() |
|
218 { |
|
219 initializeDataStore(); |
|
220 } |
|
221 |
|
222 /*! |
|
223 callback from repository. |
|
224 update our store. |
|
225 */ |
|
226 void BtDeviceData::DeletedFromRegistry( const TBTDevAddr& addr ) |
|
227 { |
|
228 int i = indexOf( addr ); |
|
229 if ( i > -1 ) { |
|
230 if ( isSearchingDevice && isDeviceInRange( mData.at(i) ) ) { |
|
231 // device searching is ongoing, and it is in-range. we can not |
|
232 // remore it from model now. |
|
233 // clear-registry related properties, so that |
|
234 // we get a chance to clean it after device searching later. |
|
235 setMajorProperty(mData[i], BtDeviceModel::RegistryProperties, false); |
|
236 mModel.emitDataChanged( i, 0, this ); |
|
237 } |
|
238 else { |
|
239 mModel.beginRemoveRows(QModelIndex(), i, i); |
|
240 mData.removeAt( i ); |
|
241 mModel.endRemoveRows(); |
|
242 } |
|
243 } |
|
244 } |
|
245 |
|
246 /*! |
|
247 callback from repository. |
|
248 update our store. |
|
249 */ |
|
250 void BtDeviceData::AddedToRegistry( const CBtDevExtension& dev ) |
|
251 { |
|
252 ChangedInRegistry( dev, 0 ); |
|
253 } |
|
254 |
|
255 /*! |
|
256 callback from repository. |
|
257 update our store. |
|
258 */ |
|
259 void BtDeviceData::ChangedInRegistry( |
|
260 const CBtDevExtension& dev, TUint similarity ) |
|
261 { |
|
262 int i = indexOf( dev.Addr() ); |
|
263 if ( i == -1 ) { |
|
264 BtuiModelDataItem devData; |
|
265 if ( !isSearchingDevice ) { |
|
266 // Rssi is only available at device inquiry stage. |
|
267 // We initialize this property to an invalid value |
|
268 updateRssi(devData, RssiInvalid); |
|
269 } |
|
270 // add device-in-registry property: |
|
271 setMajorProperty(devData, BtDeviceModel::InRegistry, true); |
|
272 updateDeviceProperty(devData, dev, 0 ); |
|
273 mModel.beginInsertRows( QModelIndex(), mData.count(), mData.count() ); |
|
274 mData.append( devData ); |
|
275 mModel.endInsertRows(); |
|
276 } |
|
277 else { |
|
278 updateDeviceProperty(mData[i], dev, similarity ); |
|
279 setMajorProperty(mData[i], BtDeviceModel::InRegistry, true); |
|
280 mModel.emitDataChanged( i, 0, this ); |
|
281 } |
|
282 } |
|
283 |
|
284 /*! |
|
285 callback from repository. |
|
286 update our store. |
|
287 */ |
|
288 void BtDeviceData::ServiceConnectionChanged( |
|
289 const CBtDevExtension& dev, TBool connected ) |
|
290 { |
|
291 int i = indexOf( dev.Addr() ); |
|
292 if ( i > -1 ) { |
|
293 int preconn = BtDeviceModel::Connected |
|
294 & mData[i][BtDeviceModel::MajorPropertyRole].toInt(); |
|
295 // we only update and signal if connection status is really |
|
296 // changed: |
|
297 if ( ( preconn != 0 && !connected ) |
|
298 || ( preconn == 0 && connected ) ) { |
|
299 setMajorProperty(mData[i], BtDeviceModel::Connected, connected ); |
|
300 mModel.emitDataChanged( i, 0, this ); |
|
301 } |
|
302 } |
|
303 // it is impossible that a device has connected but it is not in |
|
304 // our local store according to current bteng services. |
|
305 // need to take more care in future when this becomes possible. |
|
306 } |
|
307 |
|
308 /*! |
|
309 callback from device search. |
|
310 update our store. |
|
311 */ |
|
312 void BtDeviceData::HandleNextDiscoveryResultL( |
|
313 const TInquirySockAddr& inqAddr, const TDesC& name ) |
|
314 { |
|
315 int pos = indexOf( inqAddr.BTAddr() ); |
|
316 const CBtDevExtension* dev = mDeviceRepo->Device( inqAddr.BTAddr() ); |
|
317 |
|
318 //RssiRole |
|
319 int rssi( RssiInvalid ); // initialize to an invalid value. |
|
320 if( inqAddr.ResultFlags() & TInquirySockAddr::ERssiValid ) { |
|
321 rssi = inqAddr.Rssi(); |
|
322 } |
|
323 |
|
324 if ( pos == -1 ) { |
|
325 BtuiModelDataItem devData; |
|
326 setMajorProperty(devData, BtDeviceModel::InRange, true); |
|
327 updateRssi(devData, rssi); |
|
328 CBtDevExtension* devExt(NULL); |
|
329 TRAP_IGNORE( { |
|
330 devExt = CBtDevExtension::NewLC( inqAddr, name ); |
|
331 CleanupStack::Pop(); }); |
|
332 updateDeviceProperty(devData, *devExt, 0); |
|
333 delete devExt; |
|
334 mModel.beginInsertRows( QModelIndex(), mData.count(), mData.count() ); |
|
335 mData.append( devData ); |
|
336 mModel.endInsertRows(); |
|
337 } |
|
338 else { |
|
339 setMajorProperty(mData[pos], BtDeviceModel::InRange, true); |
|
340 updateRssi(mData[pos], rssi); |
|
341 mModel.emitDataChanged( pos, 0, this ); |
|
342 } |
|
343 } |
|
344 |
|
345 /*! |
|
346 callback from device search. |
|
347 inform client. |
|
348 */ |
|
349 void BtDeviceData::HandleDiscoveryCompleted( TInt error ) |
|
350 { |
|
351 isSearchingDevice = false; |
|
352 mModel.emitdeviceSearchCompleted( (int) error ); |
|
353 } |
|
354 |
|
355 void BtDeviceData::initializeDataStore() |
|
356 { |
|
357 // it is possible that we are searching devices. |
|
358 // We use a simple but not-so-efficient method to update the model. |
|
359 |
|
360 // If the device store is not empty, we clear |
|
361 // registry property from these devices first. |
|
362 for (int i = 0; i < mData.count(); ++i) { |
|
363 setMajorProperty(mData[i], BtDeviceModel::RegistryProperties, false); |
|
364 } |
|
365 if ( mData.count() ) { |
|
366 // need to update view because we have changed device properties. |
|
367 QModelIndex top = mModel.createIndex(0, 0, this); |
|
368 QModelIndex bottom = mModel.createIndex(mData.count() - 1, 0, this); |
|
369 mModel.emitDataChanged( top, bottom ); |
|
370 } |
|
371 |
|
372 const RDevExtensionArray& devs = mDeviceRepo->AllDevices(); |
|
373 for (int i = 0; i < devs.Count(); ++i) { |
|
374 int pos = indexOf( devs[i]->Addr() ); |
|
375 if ( pos > -1 ) { |
|
376 // add device-in-registry property: |
|
377 setMajorProperty(mData[pos], BtDeviceModel::InRegistry, true); |
|
378 updateDeviceProperty(mData[pos], *(devs[i]), 0); |
|
379 mModel.emitDataChanged( pos, 0, this ); |
|
380 } |
|
381 else { |
|
382 BtuiModelDataItem devData; |
|
383 // add device-in-registry property: |
|
384 setMajorProperty(devData, BtDeviceModel::InRegistry, true); |
|
385 updateDeviceProperty(devData, *( devs[i] ), 0 ); |
|
386 mModel.beginInsertRows(QModelIndex(), mData.count(), mData.count() ); |
|
387 mData.append( devData ); |
|
388 mModel.endInsertRows(); |
|
389 } |
|
390 } |
|
391 } |
|
392 |
|
393 void BtDeviceData::updateDeviceProperty(BtuiModelDataItem& qtdev, |
|
394 const CBtDevExtension& dev, TUint similarity ) |
|
395 { |
|
396 // similarity is not used currently. |
|
397 // It is possible to gain better performance |
|
398 // with this info to avoid re-manipulate |
|
399 // unchanged properties. |
|
400 Q_UNUSED(similarity); |
|
401 |
|
402 //DevDisplayNameRole |
|
403 QString str = QString::fromUtf16( |
|
404 dev.Alias().Ptr(), dev.Alias().Length() ); |
|
405 qtdev[BtDeviceModel::NameAliasRole] = QVariant( str ); |
|
406 |
|
407 //DevAddrReadableRole |
|
408 addrSymbianToReadbleString( str, dev.Addr() ); |
|
409 qtdev[BtDeviceModel::ReadableBdaddrRole] = QVariant( str ); |
|
410 |
|
411 //LastUsedTimeRole |
|
412 TDateTime symDt = dev.Device().Used().DateTime(); |
|
413 QDate date( symDt.Year(), symDt.Month(), symDt.Day() ); |
|
414 QTime time( symDt.Hour(), symDt.Minute(), symDt.MicroSecond() / 1000 ); |
|
415 QDateTime qdt(date, time); |
|
416 qtdev[BtDeviceModel::LastUsedTimeRole] = QVariant(qdt); |
|
417 |
|
418 // set paired status: |
|
419 setMajorProperty(qtdev, BtDeviceModel::Bonded, isBonded( dev.Device() )); |
|
420 |
|
421 // set blocked status: |
|
422 setMajorProperty(qtdev, BtDeviceModel::Blocked, |
|
423 dev.Device().GlobalSecurity().Banned() ); |
|
424 // set trusted status: |
|
425 setMajorProperty(qtdev, BtDeviceModel::Trusted, |
|
426 dev.Device().GlobalSecurity().NoAuthorise() ); |
|
427 |
|
428 //CoDRole |
|
429 //MinorPropertyRole |
|
430 int cod = static_cast<int>( dev.Device().DeviceClass().DeviceClass() ); |
|
431 qtdev[BtDeviceModel::CoDRole] = QVariant(cod); |
|
432 |
|
433 // Initially, clear CoD related properties: |
|
434 int majorProperty = qtdev[BtDeviceModel::MajorPropertyRole].toInt() & |
|
435 ~BtDeviceModel::CodProperties; |
|
436 |
|
437 int minorProperty = BtDeviceModel::NullProperty; |
|
438 |
|
439 // device type must be mapped according to CoD: |
|
440 int majorDevCls = dev.Device().DeviceClass().MajorDeviceClass(); |
|
441 int minorDevCls = dev.Device().DeviceClass().MinorDeviceClass(); |
|
442 |
|
443 int i; |
|
444 for (i = 0; i < DeviceTypeIconMappingTableSize; ++i ) { |
|
445 if ( DeviceTypeIconMappingTable[i].majorDevClass == majorDevCls && |
|
446 ( DeviceTypeIconMappingTable[i].minorDevClass == 0 || |
|
447 DeviceTypeIconMappingTable[i].minorDevClass == minorDevCls ) ) { |
|
448 // device classes match a item in table, get the mapping: |
|
449 majorProperty |= DeviceTypeIconMappingTable[i].majorProperty; |
|
450 minorProperty |= DeviceTypeIconMappingTable[i].minorProperty; |
|
451 break; |
|
452 } |
|
453 } |
|
454 |
|
455 // AV device mapping are not defined in the table, do mapping here if no device |
|
456 // type mapped so far: |
|
457 if ( i == DeviceTypeIconMappingTableSize) { |
|
458 // audio device, carkit, headset or speaker: |
|
459 if( ( majorDevCls == EMajorDeviceAV) |
|
460 || (dev.Device().DeviceClass().MajorServiceClass() == EMajorServiceRendering |
|
461 && majorDevCls != EMajorDeviceImaging) ) { |
|
462 |
|
463 majorProperty |= BtDeviceModel::AVDev; |
|
464 |
|
465 if( minorDevCls == EMinorDeviceAVCarAudio ) { |
|
466 // carkit: |
|
467 minorProperty |= BtDeviceModel::Carkit; |
|
468 } |
|
469 else { |
|
470 // headset: |
|
471 minorProperty |= BtDeviceModel::Headset; |
|
472 } |
|
473 } |
|
474 } |
|
475 |
|
476 qtdev[BtDeviceModel::MajorPropertyRole] = QVariant( majorProperty ); |
|
477 qtdev[BtDeviceModel::MinorPropertyRole] = QVariant( minorProperty ); |
|
478 } |
|
479 |
|
480 int BtDeviceData::indexOf( const TBTDevAddr& addr ) const |
|
481 { |
|
482 QString addrStr; |
|
483 addrSymbianToReadbleString( addrStr, addr ); |
|
484 for (int i = 0; i < mData.count(); ++i ) { |
|
485 if ( mData.at( i ).value( BtDeviceModel::ReadableBdaddrRole ) |
|
486 == addrStr ) { |
|
487 return i; |
|
488 } |
|
489 } |
|
490 return -1; |
|
491 } |
|
492 |
|
493 void BtDeviceData::updateRssi(BtuiModelDataItem& qtdev, int rssi ) |
|
494 { |
|
495 qtdev[BtDeviceModel::RssiRole] = QVariant( rssi ); |
|
496 } |
|
497 |
|
498 /*! |
|
499 Add the specified major property to the device if addto is true. |
|
500 Otherwise the property is removed from the device. |
|
501 */ |
|
502 void BtDeviceData::setMajorProperty( |
|
503 BtuiModelDataItem& qtdev, int prop, bool addto) |
|
504 { |
|
505 if ( addto ) { |
|
506 qtdev[BtDeviceModel::MajorPropertyRole] = |
|
507 QVariant( qtdev[BtDeviceModel::MajorPropertyRole].toInt() | prop); |
|
508 } |
|
509 else { |
|
510 qtdev[BtDeviceModel::MajorPropertyRole] = |
|
511 QVariant( qtdev[BtDeviceModel::MajorPropertyRole].toInt() & ~prop); |
|
512 } |
|
513 } |
|
514 |
|
515 bool BtDeviceData::isDeviceInRange( const BtuiModelDataItem& qtdev ) |
|
516 { |
|
517 return BtDeviceModel::InRange & qtdev[BtDeviceModel::MajorPropertyRole].toInt(); |
|
518 } |
|
519 |
|
520 bool BtDeviceData::isDeviceInRegistry( const BtuiModelDataItem& qtdev ) |
|
521 { |
|
522 return BtDeviceModel::InRegistry & qtdev[BtDeviceModel::MajorPropertyRole].toInt(); |
|
523 } |