bluetoothengine/btui/btuimodel/btdevicedata.cpp
changeset 31 a0ea99b6fa53
parent 29 48ae3789ce00
child 40 997690c3397a
--- a/bluetoothengine/btui/btuimodel/btdevicedata.cpp	Wed May 05 09:56:48 2010 +0300
+++ b/bluetoothengine/btui/btuimodel/btdevicedata.cpp	Mon May 17 11:06:23 2010 +0300
@@ -15,24 +15,67 @@
 *
 */
 
+#include "btdevicedata.h"
+#include <QDateTime>
+#include <btservices/advancedevdiscoverer.h>
+#include "btuiutil.h"
+#include "btqtconstants.h"
 
-#include "btdevicedata.h"
+class DevTypeIconMapping {
+public:    
+    int majorDevClass; // major device class value from CoD
+    int minorDevClass; // minor device class value from CoD
+    int majorProperty;   // one of major properties defined in BtDeviceModel
+    int minorProperty;   // one of minor properties defined in BtDeviceModel
+    char* connectedIcon; // the icon name for connected device
+    char* unconnectedIcon; // the icon name for not connected device.
+};
+
+// mapping table from major and minor Device Classes to device types and icons
+// which are specifically defined in btapplication namespace.
+// (Note audio device mapping is not in this table due to its complex logic)
+static const DevTypeIconMapping DeviceTypeIconMappingTable[] =
+{
+{EMajorDeviceComputer, 0, BtDeviceModel::Computer, 0, 
+        ":/icons/qgn_prop_bt_computer_connect.svg", ":/icons/qgn_prop_bt_computer.svg" },
+{EMajorDevicePhone,    0, BtDeviceModel::Phone,    0, 
+        ":/icons/qgn_prop_bt_phone_connect.svg", ":/icons/qgn_prop_bt_phone.svg"},
+{EMajorDeviceLanAccessPoint, 0, BtDeviceModel::LANAccessDev, 0, 
+        ":/icons/qgn_prop_bt_misc.svg", ":/icons/qgn_prop_bt_misc.svg" },
+{EMajorDevicePeripheral, EMinorDevicePeripheralKeyboard, 
+        BtDeviceModel::Peripheral, BtDeviceModel::Keyboard,
+        ":/icons/qgn_prop_bt_keyboard_connect.svg", ":/icons/qgn_prop_bt_keyboard.svg"},
+{EMajorDevicePeripheral, EMinorDevicePeripheralPointer, 
+        BtDeviceModel::Peripheral, BtDeviceModel::Mouse,
+        ":/icons/qgn_prop_bt_mouse_connect.svg", ":/icons/qgn_prop_bt_mouse.svg"},
+{EMajorDeviceImaging, 0, BtDeviceModel::ImagingDev, 0, 
+        ":/icons/qgn_prop_bt_printer_connect.svg", ":/icons/qgn_prop_bt_printer.svg"},
+{EMajorDeviceWearable, 0, BtDeviceModel::WearableDev, 0, 
+        ":/icons/qgn_prop_bt_misc.svg", ":/icons/qgn_prop_bt_misc.svg"},
+{EMajorDeviceToy, 0, BtDeviceModel::Toy, 0,
+        ":/icons/qgn_prop_bt_misc.svg", ":/icons/qgn_prop_bt_misc.svg"},
+};
+
+static const int DeviceTypeIconMappingTableSize = 
+        sizeof( DeviceTypeIconMappingTable ) / sizeof( DevTypeIconMapping );
 
 /*!
     Constructor.
  */
-BtDeviceData::BtDeviceData(
-        const QSharedPointer<BtuiModelDataSource> &data,
-        QObject *parent)
-    : QObject( parent ), mData( data ), mBtengConnMan(0)
+BtDeviceData::BtDeviceData( BtDeviceModel& model, QObject *parent )
+    : QObject( parent ), mModel( model ), mDiscover( 0 )
 {
+    mDeviceRepo = 0;
+    isSearchingDevice = false;
     TRAP_IGNORE({
-        mBtengConnMan = CBTEngConnMan::NewL( this );
-        mDeviceRepo = CBtDevRepository::NewL( );
+        mDeviceRepo = CBtDevRepository::NewL();
     });
+    Q_CHECK_PTR( mDeviceRepo );
+    TRAP_IGNORE( mDeviceRepo->AddObserverL( this ) );
     
-    Q_CHECK_PTR( mBtengConnMan );
-    Q_CHECK_PTR( mDeviceRepo );
+    if ( mDeviceRepo->IsInitialized() ) {
+        initializeDataStore();
+    }
 }
 
 /*!
@@ -40,37 +83,441 @@
  */
 BtDeviceData::~BtDeviceData()
 {
-    delete mBtengConnMan;
     delete mDeviceRepo;
+    delete mDiscover;
+}
+
+
+/*!
+    Tells whether the given column is in the range of the setting list.
+    
+    \param row the row number to be checked
+    \param col the column number to be checked
+    
+    \return true if the given row and column are valid; false otherwise.
+*/
+bool BtDeviceData::isValid( int row, int column) const
+{
+    return row >= 0 && row < mData.count() && column == 0;
+}
+
+/*!
+    \return the total amount of rows.
+    
+*/
+int BtDeviceData::rowCount() const
+{
+    return mData.count();
+}
+
+/*!
+    \return the total amount of columns.
+    
+*/
+int BtDeviceData::columnCount() const
+{
+    return 1;
+}
+
+/*!
+    Gets the value within a data item.
+    \param val contains the value at return.
+    \param row the row number which the value is from
+    \param col the column number which the value is from
+    \param role the role idenfier of the value.
+ */
+void BtDeviceData::data(QVariant& val, int row,  int col, int role ) const
+{
+    if ( isValid( row, col ) ) {
+        val = mData.at( row ).value( role );
+    }
+    else {
+        val = QVariant( QVariant::Invalid );
+    }
+}
+
+/*!
+    Gets the whole item data at the specified column
+    \param row the row number of the item data to be returned
+    \param col the column number of the item data to be returned
+    \return the item data
+ */
+BtuiModelDataItem BtDeviceData::itemData( int row, int col ) const
+{
+    if ( isValid( row, col ) ) {
+        return mData.at( row );
+    }
+    return BtuiModelDataItem();
+}
+
+
+/*!
+    Requests the model to searching Bluetooth devices.
+    \return true if the request is accepted; false otherwise
+ */
+bool BtDeviceData::searchDevice()
+{
+    int err ( 0 );
+    removeTransientDevices();
+    if ( !mDiscover ) {
+        TRAP(err, mDiscover = CAdvanceDevDiscoverer::NewL( *mDeviceRepo, *this) );
+    }
+    if ( !err ) {
+        TRAP(err, mDiscover->DiscoverDeviceL() );
+    }
+    isSearchingDevice = true;
+    return err == 0;
+}
+
+/*!
+    Cancels a possible outstanding device search request.
+ */
+void BtDeviceData::cancelSearchDevice()
+{
+    if ( mDiscover ) {
+        isSearchingDevice = false;
+        mDiscover->CancelDiscovery();
+    }
 }
 
-void BtDeviceData::ConnectComplete( TBTDevAddr& addr, TInt err, 
-        RBTDevAddrArray* conflicts ) 
+/*!
+    Removes transient (not-in-registry) devices 
+    (added as the result of device search).
+ */
+void BtDeviceData::removeTransientDevices()
+{
+    // clear in-range property for all device items in this model.
+    int cnt = mData.count();
+    for ( int i = mData.count() - 1; i > -1; --i)
+        {
+        const BtuiModelDataItem& qtdev = mData.at(i);
+        if(isDeviceInRange(qtdev)) {
+            if(isDeviceInRegistry(qtdev)) {
+                // we cannot remove this device as it is in registry.
+                // remove it in-range property.
+                setMajorProperty(mData[i], BtDeviceModel::InRange, false);
+                updateRssi(mData[i], RssiInvalid);
+                mModel.emitDataChanged( i, 0, this );
+            }
+            else {
+                // this device is not in-registry. Delete it from local
+                // store.
+                mModel.beginRemoveRows(QModelIndex(), i, i);
+                mData.removeAt( i );
+                mModel.endRemoveRows();
+            }
+        }
+    }
+}
+
+/*!
+    callback from repository.
+    re-initialize our store.
+ */
+void BtDeviceData::RepositoryInitialized() 
+{
+    initializeDataStore();
+}
+
+/*!
+    callback from repository.
+    update our store.
+ */
+void BtDeviceData::DeletedFromRegistry( const TBTDevAddr& addr ) 
 {
-    Q_UNUSED( addr );
-    Q_UNUSED( err );
-    Q_UNUSED( conflicts );
+    int i = indexOf( addr );
+    if ( i > -1 ) {
+        if ( isSearchingDevice && isDeviceInRange( mData.at(i) ) ) {
+            // device searching is ongoing, and it is in-range. we can not 
+            // remore it from model now.
+            // clear-registry related properties, so that
+            // we get a chance to clean it after device searching later.
+            setMajorProperty(mData[i], BtDeviceModel::RegistryProperties, false);
+            mModel.emitDataChanged( i, 0, this );
+        }
+        else {
+            mModel.beginRemoveRows(QModelIndex(), i, i);
+            mData.removeAt( i );
+            mModel.endRemoveRows();
+        }
+    }
+}
+
+/*!
+    callback from repository.
+    update our store.
+ */
+void BtDeviceData::AddedToRegistry( const CBtDevExtension& dev ) 
+{
+    ChangedInRegistry( dev, 0 );
+}
+
+/*!
+    callback from repository.
+    update our store.
+ */
+void BtDeviceData::ChangedInRegistry( 
+        const CBtDevExtension& dev, TUint similarity )
+{
+    int i = indexOf( dev.Addr() );
+    if ( i == -1 ) {
+        BtuiModelDataItem devData;
+        if ( !isSearchingDevice ) {
+            // Rssi is only available at device inquiry stage. 
+            // We initialize this property to an invalid value
+            updateRssi(devData, RssiInvalid);
+        }
+        // add device-in-registry property:
+        setMajorProperty(devData, BtDeviceModel::InRegistry, true);
+        updateDeviceProperty(devData, dev, 0 );
+        mModel.beginInsertRows( QModelIndex(), mData.count(), mData.count() );
+        mData.append( devData );
+        mModel.endInsertRows();
+    }
+    else {
+        updateDeviceProperty(mData[i], dev, similarity );
+        setMajorProperty(mData[i], BtDeviceModel::InRegistry, true);
+        mModel.emitDataChanged( i, 0, this );
+    }
 }
 
-void BtDeviceData::DisconnectComplete( TBTDevAddr& addr, TInt err ) 
+/*!
+    callback from repository.
+    update our store.
+ */
+void BtDeviceData::ServiceConnectionChanged(
+        const CBtDevExtension& dev, TBool connected )
 {
-    Q_UNUSED( addr );
-    Q_UNUSED( err );
+    int i = indexOf( dev.Addr() );
+    if ( i > -1 ) {
+        int preconn =  BtDeviceModel::Connected 
+                & mData[i][BtDeviceModel::MajorPropertyRole].toInt();
+        // we only update and signal if connection status is really
+        // changed:
+        if ( ( preconn != 0 && !connected )
+            || ( preconn == 0 && connected ) ) {
+            setMajorProperty(mData[i], BtDeviceModel::Connected, connected );
+            mModel.emitDataChanged( i, 0, this );
+        }
+    }
+    // it is impossible that a device has connected but it is not in
+    // our local store according to current bteng services.
+    // need to take more care in future when this becomes possible.
 }
 
-void BtDeviceData::BtDeviceDeleted( const TBTDevAddr& addr ) 
+/*!
+    callback from device search.
+    update our store.
+ */
+void BtDeviceData::HandleNextDiscoveryResultL( 
+        const TInquirySockAddr& inqAddr, const TDesC& name )
 {
-    Q_UNUSED( addr );
+    int pos = indexOf( inqAddr.BTAddr() );
+    const CBtDevExtension* dev = mDeviceRepo->Device( inqAddr.BTAddr() );
+    
+    //RssiRole
+    int rssi( RssiInvalid ); // initialize to an invalid value.
+    if( inqAddr.ResultFlags() & TInquirySockAddr::ERssiValid ) {
+        rssi = inqAddr.Rssi();
+    }
+    
+    if ( pos == -1 ) {
+        BtuiModelDataItem devData;
+        setMajorProperty(devData, BtDeviceModel::InRange, true);
+        updateRssi(devData, rssi);
+        CBtDevExtension* devExt(NULL);
+        TRAP_IGNORE( {
+            devExt = CBtDevExtension::NewLC( inqAddr, name );
+            CleanupStack::Pop(); });
+        updateDeviceProperty(devData, *devExt, 0);
+        delete devExt;
+        mModel.beginInsertRows( QModelIndex(), mData.count(), mData.count() );
+        mData.append( devData );
+        mModel.endInsertRows();
+    }
+    else {
+        setMajorProperty(mData[pos], BtDeviceModel::InRange, true);
+        updateRssi(mData[pos], rssi);
+        mModel.emitDataChanged( pos, 0, this );
+    }
+}
+
+/*!
+    callback from device search.
+    inform client.
+ */
+void BtDeviceData::HandleDiscoveryCompleted( TInt error )
+{
+    isSearchingDevice = false;
+    mModel.emitdeviceSearchCompleted( (int) error );
+}
+
+void BtDeviceData::initializeDataStore()
+    {
+    // it is possible that we are searching devices.
+    // We use a simple but not-so-efficient method to update the model.
+    
+    // If the device store is not empty, we clear
+    // registry property from these devices first.
+    for (int i = 0; i < mData.count(); ++i) {
+        setMajorProperty(mData[i], BtDeviceModel::RegistryProperties, false);
+    }
+    if ( mData.count() ) {
+        // need to update view because we have changed device properties.
+        QModelIndex top = mModel.createIndex(0, 0, this);
+        QModelIndex bottom = mModel.createIndex(mData.count() - 1, 0, this);
+        mModel.emitDataChanged( top, bottom );
+    }
+
+    const RDevExtensionArray& devs = mDeviceRepo->AllDevices();
+    for (int i = 0; i < devs.Count(); ++i) {
+        int pos = indexOf( devs[i]->Addr() );
+        if ( pos > -1 ) {
+            // add device-in-registry property:
+            setMajorProperty(mData[pos], BtDeviceModel::InRegistry, true);            
+            updateDeviceProperty(mData[pos], *(devs[i]), 0);
+            mModel.emitDataChanged( pos, 0, this );
+        }
+        else {
+            BtuiModelDataItem devData;
+            // add device-in-registry property:
+            setMajorProperty(devData, BtDeviceModel::InRegistry, true);
+            updateDeviceProperty(devData, *( devs[i] ), 0 );
+            mModel.beginInsertRows(QModelIndex(), mData.count(), mData.count() );
+            mData.append( devData );
+            mModel.endInsertRows();
+        }
+    }
 }
 
-void BtDeviceData::BtDeviceAdded( const CBTDevice& device ) 
+void BtDeviceData::updateDeviceProperty(BtuiModelDataItem& qtdev,
+        const CBtDevExtension& dev, TUint similarity )
 {
-    Q_UNUSED( device );
+    // similarity is not used currently. 
+    // It is possible to gain better performance
+    // with this info to avoid re-manipulate
+    // unchanged properties.
+    Q_UNUSED(similarity);
+    
+    //DevDisplayNameRole
+    QString str = QString::fromUtf16( 
+            dev.Alias().Ptr(), dev.Alias().Length() );
+    qtdev[BtDeviceModel::NameAliasRole] = QVariant( str );
+
+    //DevAddrReadableRole
+    addrSymbianToReadbleString( str, dev.Addr() );
+    qtdev[BtDeviceModel::ReadableBdaddrRole] = QVariant( str );
+
+    //LastUsedTimeRole
+    TDateTime symDt = dev.Device().Used().DateTime();
+    QDate date( symDt.Year(), symDt.Month(), symDt.Day() );
+    QTime time( symDt.Hour(), symDt.Minute(), symDt.MicroSecond() / 1000 );
+    QDateTime qdt(date, time);
+    qtdev[BtDeviceModel::LastUsedTimeRole] = QVariant(qdt);
+ 
+    // set paired status:
+    setMajorProperty(qtdev, BtDeviceModel::Bonded, isBonded( dev.Device() ));
+    
+    // set blocked status:
+    setMajorProperty(qtdev, BtDeviceModel::Blocked, 
+            dev.Device().GlobalSecurity().Banned() );
+    // set trusted status:
+    setMajorProperty(qtdev, BtDeviceModel::Trusted, 
+            dev.Device().GlobalSecurity().NoAuthorise() );
+
+    //CoDRole
+    //MinorPropertyRole
+    int cod = static_cast<int>( dev.Device().DeviceClass().DeviceClass() );
+    qtdev[BtDeviceModel::CoDRole] = QVariant(cod);
+    
+    // Initially, clear CoD related properties:
+    int majorProperty = qtdev[BtDeviceModel::MajorPropertyRole].toInt() & 
+        ~BtDeviceModel::CodProperties;
+    
+    int minorProperty = BtDeviceModel::NullProperty;
+    
+    // device type must be mapped according to CoD:
+    int majorDevCls = dev.Device().DeviceClass().MajorDeviceClass();
+    int minorDevCls = dev.Device().DeviceClass().MinorDeviceClass();
+
+    int i;
+    for (i = 0; i < DeviceTypeIconMappingTableSize; ++i ) {
+        if ( DeviceTypeIconMappingTable[i].majorDevClass == majorDevCls &&
+             ( DeviceTypeIconMappingTable[i].minorDevClass == 0 || 
+               DeviceTypeIconMappingTable[i].minorDevClass == minorDevCls ) ) {
+             // device classes match a item in table, get the mapping:
+            majorProperty |= DeviceTypeIconMappingTable[i].majorProperty;
+            minorProperty |= DeviceTypeIconMappingTable[i].minorProperty;
+            break;
+         }
+    }
+    
+    // AV device mapping are not defined in the table, do mapping here if no device
+    // type mapped so far:
+    if ( i == DeviceTypeIconMappingTableSize) {
+        // audio device, carkit, headset or speaker:
+        if( ( majorDevCls == EMajorDeviceAV) 
+            || (dev.Device().DeviceClass().MajorServiceClass() == EMajorServiceRendering 
+            && majorDevCls != EMajorDeviceImaging) ) {
+            
+            majorProperty |= BtDeviceModel::AVDev;
+            
+            if( minorDevCls == EMinorDeviceAVCarAudio ) {
+                // carkit:
+                minorProperty |= BtDeviceModel::Carkit; 
+            }
+            else {
+                // headset:
+                minorProperty |= BtDeviceModel::Headset;
+            }
+        }
+    }
+    
+    qtdev[BtDeviceModel::MajorPropertyRole] = QVariant( majorProperty );
+    qtdev[BtDeviceModel::MinorPropertyRole] = QVariant( minorProperty );
 }
 
-void BtDeviceData::BtDeviceChanged( const CBTDevice& device, TUint similarity )
+int BtDeviceData::indexOf( const TBTDevAddr& addr ) const
 {
-    Q_UNUSED( device );
-    Q_UNUSED( similarity );
+    QString addrStr;
+    addrSymbianToReadbleString( addrStr, addr );
+    for (int i = 0; i < mData.count(); ++i ) {
+        if ( mData.at( i ).value( BtDeviceModel::ReadableBdaddrRole ) 
+                == addrStr ) {
+            return i;
+        }
+    }
+    return -1;
 }
 
+void BtDeviceData::updateRssi(BtuiModelDataItem& qtdev, int rssi )
+    {
+    qtdev[BtDeviceModel::RssiRole] = QVariant( rssi );
+    }
+
+/*!
+    Add the specified major property to the device if addto is true.
+    Otherwise the property is removed from the device. 
+ */
+void BtDeviceData::setMajorProperty(
+        BtuiModelDataItem& qtdev, int prop, bool addto)
+{
+    if ( addto ) {
+        qtdev[BtDeviceModel::MajorPropertyRole] = 
+            QVariant( qtdev[BtDeviceModel::MajorPropertyRole].toInt() | prop);
+    }
+    else {
+        qtdev[BtDeviceModel::MajorPropertyRole] = 
+            QVariant( qtdev[BtDeviceModel::MajorPropertyRole].toInt() & ~prop);
+    }
+}
+
+bool BtDeviceData::isDeviceInRange( const BtuiModelDataItem& qtdev )
+{   
+    return BtDeviceModel::InRange & qtdev[BtDeviceModel::MajorPropertyRole].toInt();
+}
+
+bool BtDeviceData::isDeviceInRegistry( const BtuiModelDataItem& qtdev )
+{
+    return BtDeviceModel::InRegistry & qtdev[BtDeviceModel::MajorPropertyRole].toInt();
+}