1
/****************************************************************************************
2
 * Copyright (c) 2007 Jeff Mitchell <kde-dev@emailgoeshere.com>                         *
3
 *                                                                                      *
4
 * This program is free software; you can redistribute it and/or modify it under        *
5
 * the terms of the GNU General Public License as published by the Free Software        *
6
 * Foundation; either version 2 of the License, or (at your option) any later           *
7
 * version.                                                                             *
8
 *                                                                                      *
9
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
10
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
11
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
12
 *                                                                                      *
13
 * You should have received a copy of the GNU General Public License along with         *
14
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
15
 ****************************************************************************************/
16
17
#define DEBUG_PREFIX "MediaDeviceCache"
18
19
#include "MediaDeviceCache.h"
20
21
#include "core/support/Amarok.h"
22
#include "core/support/Debug.h"
23
24
#include <KConfig>
25
#include <solid/device.h>
26
#include <solid/deviceinterface.h>
27
#include <solid/devicenotifier.h>
28
#include <solid/genericinterface.h>
29
#include <solid/opticaldisc.h>
30
#include <solid/portablemediaplayer.h>
31
#include <solid/storageaccess.h>
32
#include <solid/storagedrive.h>
33
#include <solid/storagevolume.h>
34
35
#include <QDir>
36
#include <QFile>
37
#include <QList>
38
39
MediaDeviceCache* MediaDeviceCache::s_instance = 0;
40
41
MediaDeviceCache::MediaDeviceCache() : QObject()
42
                             , m_type()
43
                             , m_name()
44
                             , m_volumes()
45
{
46
    DEBUG_BLOCK
47
    s_instance = this;
48
    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceAdded( const QString & ) ),
49
             this, SLOT( slotAddSolidDevice( const QString & ) ) );
50
    connect( Solid::DeviceNotifier::instance(), SIGNAL( deviceRemoved( const QString & ) ),
51
             this, SLOT( slotRemoveSolidDevice( const QString & ) ) );
52
}
53
54
MediaDeviceCache::~MediaDeviceCache()
55
{
56
    s_instance = 0;
57
}
58
59
void
60
MediaDeviceCache::refreshCache()
61
{
62
    DEBUG_BLOCK
63
    m_type.clear();
64
    m_name.clear();
65
    QList<Solid::Device> deviceList = Solid::Device::listFromType( Solid::DeviceInterface::PortableMediaPlayer );
66
    foreach( const Solid::Device &device, deviceList )
67
    {
68
        if( device.as<Solid::StorageDrive>() )
69
        {
70
            debug() << "Found Solid PMP that is also a StorageDrive, skipping";
71
            continue;
72
        }
73
        debug() << "Found Solid::DeviceInterface::PortableMediaPlayer with udi = " << device.udi();
74
        debug() << "Device name is = " << device.product() << " and was made by " << device.vendor();
75
        m_type[device.udi()] = MediaDeviceCache::SolidPMPType;
76
        m_name[device.udi()] = device.vendor() + " - " + device.product();
77
    }
78
    deviceList = Solid::Device::listFromType( Solid::DeviceInterface::StorageAccess );
79
    foreach( const Solid::Device &device, deviceList )
80
    {
81
        debug() << "Found Solid::DeviceInterface::StorageAccess with udi = " << device.udi();
82
        debug() << "Device name is = " << device.product() << " and was made by " << device.vendor();
83
84
        const Solid::StorageAccess* ssa = device.as<Solid::StorageAccess>();
85
        const Solid::OpticalDisc * opt = device.as<Solid::OpticalDisc>();
86
87
        if ( opt && opt->availableContent() & Solid::OpticalDisc::Audio )
88
        {
89
            m_type[ device.udi() ] = MediaDeviceCache::SolidAudioCdType;
90
            m_name[ device.udi() ] = device.vendor() + " - " + device.product();
91
        }
92
        else if( ssa )
93
        {
94
            if( !m_volumes.contains( device.udi() ) )
95
            {
96
                connect( ssa, SIGNAL( accessibilityChanged(bool, const QString&) ),
97
                    this, SLOT( slotAccessibilityChanged(bool, const QString&) ) );
98
                m_volumes.append( device.udi() );
99
            }
100
            if( ssa->isAccessible() )
101
            {
102
                m_type[device.udi()] = MediaDeviceCache::SolidVolumeType;
103
                m_name[device.udi()] = ssa->filePath();
104
            }
105
            else
106
            {
107
                debug() << "Solid device is not accessible, will wait until it is to consider it added.";
108
            }
109
        }
110
    }
111
    deviceList = Solid::Device::listFromType( Solid::DeviceInterface::StorageDrive );
112
    foreach( const Solid::Device &device, deviceList )
113
    {
114
        debug() << "Found Solid::DeviceInterface::StorageDrive with udi = " << device.udi();
115
        debug() << "Device name is = " << device.product() << " and was made by " << device.vendor();
116
117
        if( device.as<Solid::StorageDrive>() )
118
        {
119
            m_type[device.udi()] = MediaDeviceCache::SolidGenericType;
120
            m_name[device.udi()] = device.vendor() + " - " + device.product();
121
        }
122
    }
123
    deviceList = Solid::Device::allDevices();
124
    foreach( const Solid::Device &device, deviceList )
125
    {
126
        if( const Solid::GenericInterface *generic = device.as<Solid::GenericInterface>() )
127
        {
128
            if( m_type.contains( device.udi() ) )
129
                continue;
130
131
            const QMap<QString, QVariant> properties = generic->allProperties();
132
            if( !properties.contains("info.capabilities") )
133
                continue;
134
135
            const QStringList capabilities = properties["info.capabilities"].toStringList();
136
            if( !capabilities.contains("afc") )
137
                continue;
138
139
            debug() << "Found AFC capable Solid::DeviceInterface::GenericInterface with udi = " << device.udi();
140
            debug() << "Device name is = " << device.product() << " and was made by " << device.vendor();
141
142
            m_type[device.udi()] = MediaDeviceCache::SolidGenericType;
143
            m_name[device.udi()] = device.vendor() + " - " + device.product();
144
        }
145
    }
146
    KConfigGroup config = Amarok::config( "PortableDevices" );
147
    const QStringList manualDeviceKeys = config.entryMap().keys();
148
    foreach( const QString &udi, manualDeviceKeys )
149
    {
150
        if( udi.startsWith( "manual" ) )
151
        {
152
            debug() << "Found manual device with udi = " << udi;
153
            m_type[udi] = MediaDeviceCache::ManualType;
154
            m_name[udi] = udi.split( '|' )[1];
155
        }
156
    }
157
}
158
159
void
160
MediaDeviceCache::slotAddSolidDevice( const QString &udi )
161
{
162
    DEBUG_BLOCK
163
    Solid::Device device( udi );
164
    debug() << "Found new Solid device with udi = " << device.udi();
165
    debug() << "Device name is = " << device.product() << " and was made by " << device.vendor();
166
    Solid::StorageAccess *ssa = device.as<Solid::StorageAccess>();
167
168
    Solid::OpticalDisc * opt = device.as<Solid::OpticalDisc>();
169
170
171
    if( m_type.contains( udi ) )
172
    {
173
        debug() << "Duplicate UDI trying to be added: " << udi;
174
        return;
175
    }
176
177
    if ( opt && opt->availableContent() & Solid::OpticalDisc::Audio )
178
    {
179
        debug() << "device is an Audio CD";
180
        m_type[udi] = MediaDeviceCache::SolidAudioCdType;
181
        m_name[udi] = device.vendor() + " - " + device.product();
182
    }
183
    else if( device.as<Solid::StorageDrive>() )
184
    {
185
        debug() << "device is a Storage drive, still need a volume";
186
        m_type[udi] = MediaDeviceCache::SolidGenericType;
187
        m_name[udi] = device.vendor() + " - " + device.product();
188
    }
189
    else if( ssa )
190
    {
191
        debug() << "volume is generic storage";
192
        if( !m_volumes.contains( device.udi() ) )
193
        {
194
            connect( ssa, SIGNAL( accessibilityChanged(bool, const QString&) ),
195
                this, SLOT( slotAccessibilityChanged(bool, const QString&) ) );
196
            m_volumes.append( device.udi() );
197
        }
198
        if( ssa->isAccessible() )
199
        {
200
            m_type[udi] = MediaDeviceCache::SolidVolumeType;
201
            m_name[udi] = ssa->filePath();
202
        }
203
        else
204
        {
205
            debug() << "storage volume is not accessible right now, not adding.";
206
            return;
207
        }
208
    }
209
    else if( device.as<Solid::PortableMediaPlayer>() )
210
    {
211
        debug() << "device is a PMP";
212
        m_type[udi] = MediaDeviceCache::SolidPMPType;
213
        m_name[udi] = device.vendor() + " - " + device.product();
214
    }
215
    else if( const Solid::GenericInterface *generic = device.as<Solid::GenericInterface>() )
216
    {
217
        const QMap<QString, QVariant> properties = generic->allProperties();
218
        if( !properties.contains("info.capabilities") )
219
        {
220
            debug() << "udi " << udi << " does not describe a portable media player or storage volume";
221
            return;
222
        }
223
224
        const QStringList capabilities = properties["info.capabilities"].toStringList();
225
        if( !capabilities.contains("afc") )
226
        {
227
            debug() << "udi " << udi << " does not describe a portable media player or storage volume";
228
            return;
229
        }
230
231
        debug() << "udi" << udi << "is AFC cabable (Apple mobile device)";
232
        m_type[udi] = MediaDeviceCache::SolidGenericType;
233
        m_name[udi] = device.vendor() + " - " + device.product();
234
    }
235
    else
236
    {
237
        debug() << "udi " << udi << " does not describe a portable media player or storage volume";
238
        return;
239
    }
240
    emit deviceAdded( udi );
241
}
242
243
void
244
MediaDeviceCache::slotRemoveSolidDevice( const QString &udi )
245
{
246
    DEBUG_BLOCK
247
    debug() << "udi is: " << udi;
248
    Solid::Device device( udi );
249
    if( m_volumes.contains( udi ) )
250
    {
251
        disconnect( device.as<Solid::StorageAccess>(), SIGNAL( accessibilityChanged(bool, const QString&) ),
252
                    this, SLOT( slotAccessibilityChanged(bool, const QString&) ) );
253
        m_volumes.removeAll( udi );
254
        emit deviceRemoved( udi );
255
    }
256
    if( m_type.contains( udi ) )
257
    {
258
        m_type.remove( udi );
259
        m_name.remove( udi );
260
        emit deviceRemoved( udi );
261
        return;
262
    }
263
    debug() << "Odd, got a deviceRemoved at udi " << udi << " but it did not seem to exist in the first place...";
264
    emit deviceRemoved( udi );
265
}
266
267
void
268
MediaDeviceCache::slotAccessibilityChanged( bool accessible, const QString &udi )
269
{
270
    debug() << "accessibility of device " << udi << " has changed to accessible = " << (accessible ? "true":"false");
271
    if( accessible )
272
    {
273
        Solid::Device device( udi );
274
        m_type[udi] = MediaDeviceCache::SolidVolumeType;
275
        Solid::StorageAccess *ssa = device.as<Solid::StorageAccess>();
276
        if( ssa )
277
            m_name[udi] = ssa->filePath();
278
        emit deviceAdded( udi );
279
        return;
280
    }
281
    else
282
    {
283
        if( m_type.contains( udi ) )
284
        {
285
            m_type.remove( udi );
286
            m_name.remove( udi );
287
            emit deviceRemoved( udi );
288
            return;
289
        }
290
        debug() << "Got accessibility changed to false but was not there in the first place...";
291
    }
292
293
    emit accessibilityChanged( accessible, udi );
294
}
295
296
MediaDeviceCache::DeviceType
297
MediaDeviceCache::deviceType( const QString &udi ) const
298
{
299
    if( m_type.contains( udi ) )
300
    {
301
        return m_type[udi];
302
    }
303
    return MediaDeviceCache::InvalidType;
304
}
305
306
const QString
307
MediaDeviceCache::deviceName( const QString &udi ) const
308
{
309
    if( m_name.contains( udi ) )
310
    {
311
        return m_name[udi];
312
    }
313
    return "ERR_NO_NAME"; //Should never happen!
314
}
315
316
bool
317
MediaDeviceCache::isGenericEnabled( const QString &udi ) const
318
{
319
    DEBUG_BLOCK
320
    if( m_type[udi] != MediaDeviceCache::SolidVolumeType )
321
    {
322
        debug() << "Not SolidVolumeType, returning false";
323
        return false;
324
    }
325
    Solid::Device device( udi );
326
    Solid::StorageAccess* ssa = device.as<Solid::StorageAccess>();
327
    if( !ssa || !ssa->isAccessible() )
328
    {
329
        debug() << "Not able to convert to StorageAccess or not accessible, returning false";
330
        return false;
331
    }
332
    if( device.parent().as<Solid::PortableMediaPlayer>() )
333
    {
334
        debug() << "Could convert parent to PortableMediaPlayer, returning true";
335
        return true;
336
    }
337
    if( QFile::exists( ssa->filePath() + QDir::separator() + ".is_audio_player" ) )
338
    {
339
        return true;
340
    }
341
    return false;
342
}
343
344
const QString
345
MediaDeviceCache::volumeMountPoint( const QString &udi ) const
346
{
347
    DEBUG_BLOCK
348
    Solid::Device device( udi );
349
    Solid::StorageAccess* ssa = device.as<Solid::StorageAccess>();
350
    if( !ssa || !ssa->isAccessible() )
351
    {
352
        debug() << "Not able to convert to StorageAccess or not accessible, returning empty";
353
        return QString();
354
    }
355
    return ssa->filePath();
356
}
357
358
#include "MediaDeviceCache.moc"