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