File indexing completed on 2024-04-21 04:47:52
0001 /**************************************************************************************** 0002 * Copyright (c) 2007 Jeff Mitchell <kde-dev@emailgoeshere.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "MediaDeviceCache" 0018 0019 #include "MediaDeviceCache.h" 0020 0021 #include "core/support/Amarok.h" 0022 #include "core/support/Debug.h" 0023 0024 #include <Solid/Block> 0025 #include <Solid/Device> 0026 #include <Solid/DeviceInterface> 0027 #include <Solid/DeviceNotifier> 0028 #include <Solid/GenericInterface> 0029 #include <Solid/OpticalDisc> 0030 #include <Solid/PortableMediaPlayer> 0031 #include <Solid/StorageAccess> 0032 #include <Solid/StorageDrive> 0033 #include <Solid/StorageVolume> 0034 0035 #include <QDir> 0036 #include <QFile> 0037 #include <QList> 0038 0039 #include <KConfigGroup> 0040 0041 MediaDeviceCache* MediaDeviceCache::s_instance = nullptr; 0042 0043 MediaDeviceCache::MediaDeviceCache() : QObject() 0044 , m_type() 0045 , m_name() 0046 , m_volumes() 0047 { 0048 DEBUG_BLOCK 0049 s_instance = this; 0050 connect( Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded, 0051 this, &MediaDeviceCache::slotAddSolidDevice ); 0052 connect( Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved, 0053 this, &MediaDeviceCache::slotRemoveSolidDevice ); 0054 } 0055 0056 MediaDeviceCache::~MediaDeviceCache() 0057 { 0058 s_instance = nullptr; 0059 } 0060 0061 void 0062 MediaDeviceCache::refreshCache() 0063 { 0064 DEBUG_BLOCK 0065 m_type.clear(); 0066 m_name.clear(); 0067 QList<Solid::Device> deviceList = Solid::Device::listFromType( Solid::DeviceInterface::PortableMediaPlayer ); 0068 foreach( const Solid::Device &device, deviceList ) 0069 { 0070 if( device.as<Solid::StorageDrive>() ) 0071 { 0072 debug() << "Found Solid PMP that is also a StorageDrive, skipping"; 0073 continue; 0074 } 0075 debug() << "Found Solid::DeviceInterface::PortableMediaPlayer with udi = " << device.udi(); 0076 debug() << "Device name is = " << device.product() << " and was made by " << device.vendor(); 0077 m_type[device.udi()] = MediaDeviceCache::SolidPMPType; 0078 m_name[device.udi()] = device.vendor() + " - " + device.product(); 0079 } 0080 deviceList = Solid::Device::listFromType( Solid::DeviceInterface::StorageAccess ); 0081 foreach( const Solid::Device &device, deviceList ) 0082 { 0083 debug() << "Found Solid::DeviceInterface::StorageAccess with udi = " << device.udi(); 0084 debug() << "Device name is = " << device.product() << " and was made by " << device.vendor(); 0085 0086 const Solid::StorageAccess* ssa = device.as<Solid::StorageAccess>(); 0087 0088 if( ssa ) 0089 { 0090 if( !m_volumes.contains( device.udi() ) ) 0091 { 0092 connect( ssa, &Solid::StorageAccess::accessibilityChanged, 0093 this, &MediaDeviceCache::slotAccessibilityChanged ); 0094 m_volumes.append( device.udi() ); 0095 } 0096 if( ssa->isAccessible() ) 0097 { 0098 m_type[device.udi()] = MediaDeviceCache::SolidVolumeType; 0099 m_name[device.udi()] = ssa->filePath(); 0100 m_accessibility[ device.udi() ] = true; 0101 } 0102 else 0103 { 0104 m_accessibility[ device.udi() ] = false; 0105 debug() << "Solid device is not accessible, will wait until it is to consider it added."; 0106 } 0107 } 0108 } 0109 deviceList = Solid::Device::listFromType( Solid::DeviceInterface::StorageDrive ); 0110 foreach( const Solid::Device &device, deviceList ) 0111 { 0112 debug() << "Found Solid::DeviceInterface::StorageDrive with udi = " << device.udi(); 0113 debug() << "Device name is = " << device.product() << " and was made by " << device.vendor(); 0114 0115 if( device.as<Solid::StorageDrive>() ) 0116 { 0117 m_type[device.udi()] = MediaDeviceCache::SolidGenericType; 0118 m_name[device.udi()] = device.vendor() + " - " + device.product(); 0119 } 0120 } 0121 deviceList = Solid::Device::listFromType( Solid::DeviceInterface::OpticalDisc ); 0122 foreach( const Solid::Device &device, deviceList ) 0123 { 0124 debug() << "Found Solid::DeviceInterface::OpticalDisc with udi = " << device.udi(); 0125 debug() << "Device name is = " << device.product() << " and was made by " << device.vendor(); 0126 0127 const Solid::OpticalDisc * opt = device.as<Solid::OpticalDisc>(); 0128 0129 if ( opt && opt->availableContent() & Solid::OpticalDisc::Audio ) 0130 { 0131 debug() << "device is an Audio CD"; 0132 m_type[device.udi()] = MediaDeviceCache::SolidAudioCdType; 0133 m_name[device.udi()] = device.vendor() + " - " + device.product(); 0134 } 0135 } 0136 KConfigGroup config = Amarok::config( "PortableDevices" ); 0137 const QStringList manualDeviceKeys = config.entryMap().keys(); 0138 foreach( const QString &udi, manualDeviceKeys ) 0139 { 0140 if( udi.startsWith( "manual" ) ) 0141 { 0142 debug() << "Found manual device with udi = " << udi; 0143 m_type[udi] = MediaDeviceCache::ManualType; 0144 m_name[udi] = udi.split( '|' )[1]; 0145 } 0146 } 0147 } 0148 0149 void 0150 MediaDeviceCache::slotAddSolidDevice( const QString &udi ) 0151 { 0152 DEBUG_BLOCK 0153 Solid::Device device( udi ); 0154 debug() << "Found new Solid device with udi = " << device.udi(); 0155 debug() << "Device name is = " << device.product() << " and was made by " << device.vendor(); 0156 Solid::StorageAccess *ssa = device.as<Solid::StorageAccess>(); 0157 0158 Solid::OpticalDisc * opt = device.as<Solid::OpticalDisc>(); 0159 0160 if ( opt && opt->availableContent() & Solid::OpticalDisc::Audio ) 0161 { 0162 debug() << "device is an Audio CD"; 0163 m_type[udi] = MediaDeviceCache::SolidAudioCdType; 0164 m_name[udi] = device.vendor() + " - " + device.product(); 0165 } 0166 else if( ssa ) 0167 { 0168 debug() << "volume is generic storage"; 0169 if( !m_volumes.contains( device.udi() ) ) 0170 { 0171 connect( ssa, &Solid::StorageAccess::accessibilityChanged, 0172 this, &MediaDeviceCache::slotAccessibilityChanged ); 0173 m_volumes.append( device.udi() ); 0174 } 0175 if( ssa->isAccessible() ) 0176 { 0177 m_type[udi] = MediaDeviceCache::SolidVolumeType; 0178 m_name[udi] = ssa->filePath(); 0179 } 0180 else 0181 { 0182 debug() << "storage volume is not accessible right now, not adding."; 0183 return; 0184 } 0185 } 0186 else if( device.is<Solid::StorageDrive>() ) 0187 { 0188 debug() << "device is a Storage drive, still need a volume"; 0189 m_type[udi] = MediaDeviceCache::SolidGenericType; 0190 m_name[udi] = device.vendor() + " - " + device.product(); 0191 } 0192 else if( device.is<Solid::PortableMediaPlayer>() ) 0193 { 0194 debug() << "device is a PMP"; 0195 m_type[udi] = MediaDeviceCache::SolidPMPType; 0196 m_name[udi] = device.vendor() + " - " + device.product(); 0197 } 0198 else if( const Solid::GenericInterface *generic = device.as<Solid::GenericInterface>() ) 0199 { 0200 const QMap<QString, QVariant> properties = generic->allProperties(); 0201 /* At least iPod touch 3G and iPhone 3G do not advertise AFC (Apple File 0202 * Connection) capabilities. Therefore we have to white-list them so that they are 0203 * still recognised ad iPods 0204 * 0205 * @see IpodConnectionAssistant::identify() for a quirk that is currently also 0206 * needed for proper identification of iPhone-like devices. 0207 */ 0208 if ( !device.product().contains("iPod") && !device.product().contains("iPhone")) 0209 { 0210 if( !properties.contains("info.capabilities") ) 0211 { 0212 debug() << "udi " << udi << " does not describe a portable media player or storage volume"; 0213 return; 0214 } 0215 0216 const QStringList capabilities = properties["info.capabilities"].toStringList(); 0217 if( !capabilities.contains("afc") ) 0218 { 0219 debug() << "udi " << udi << " does not describe a portable media player or storage volume"; 0220 return; 0221 } 0222 } 0223 0224 debug() << "udi" << udi << "is AFC capable (Apple mobile device)"; 0225 m_type[udi] = MediaDeviceCache::SolidGenericType; 0226 m_name[udi] = device.vendor() + " - " + device.product(); 0227 } 0228 else 0229 { 0230 debug() << "udi " << udi << " does not describe a portable media player or storage volume"; 0231 return; 0232 } 0233 Q_EMIT deviceAdded( udi ); 0234 } 0235 0236 void 0237 MediaDeviceCache::slotRemoveSolidDevice( const QString &udi ) 0238 { 0239 DEBUG_BLOCK 0240 debug() << "udi is: " << udi; 0241 Solid::Device device( udi ); 0242 if( m_volumes.contains( udi ) ) 0243 { 0244 disconnect( device.as<Solid::StorageAccess>(), &Solid::StorageAccess::accessibilityChanged, 0245 this, &MediaDeviceCache::slotAccessibilityChanged ); 0246 m_volumes.removeAll( udi ); 0247 Q_EMIT deviceRemoved( udi ); 0248 } 0249 if( m_type.contains( udi ) ) 0250 { 0251 m_type.remove( udi ); 0252 m_name.remove( udi ); 0253 Q_EMIT deviceRemoved( udi ); 0254 return; 0255 } 0256 debug() << "Odd, got a deviceRemoved at udi " << udi << " but it did not seem to exist in the first place..."; 0257 Q_EMIT deviceRemoved( udi ); 0258 } 0259 0260 void 0261 MediaDeviceCache::slotAccessibilityChanged( bool accessible, const QString &udi ) 0262 { 0263 debug() << "accessibility of device " << udi << " has changed to accessible = " << (accessible ? "true":"false"); 0264 if( accessible ) 0265 { 0266 Solid::Device device( udi ); 0267 m_type[udi] = MediaDeviceCache::SolidVolumeType; 0268 Solid::StorageAccess *ssa = device.as<Solid::StorageAccess>(); 0269 if( ssa ) 0270 m_name[udi] = ssa->filePath(); 0271 Q_EMIT deviceAdded( udi ); 0272 return; 0273 } 0274 else 0275 { 0276 if( m_type.contains( udi ) ) 0277 { 0278 m_type.remove( udi ); 0279 m_name.remove( udi ); 0280 Q_EMIT deviceRemoved( udi ); 0281 return; 0282 } 0283 debug() << "Got accessibility changed to false but was not there in the first place..."; 0284 } 0285 0286 Q_EMIT accessibilityChanged( accessible, udi ); 0287 } 0288 0289 MediaDeviceCache::DeviceType 0290 MediaDeviceCache::deviceType( const QString &udi ) const 0291 { 0292 if( m_type.contains( udi ) ) 0293 { 0294 return m_type[udi]; 0295 } 0296 return MediaDeviceCache::InvalidType; 0297 } 0298 0299 const QString 0300 MediaDeviceCache::deviceName( const QString &udi ) const 0301 { 0302 if( m_name.contains( udi ) ) 0303 { 0304 return m_name[udi]; 0305 } 0306 return "ERR_NO_NAME"; //Should never happen! 0307 } 0308 0309 const QString 0310 MediaDeviceCache::device( const QString &udi ) const 0311 { 0312 DEBUG_BLOCK 0313 Solid::Device device( udi ); 0314 Solid::Device parent( device.parent() ); 0315 if( !parent.isValid() ) 0316 { 0317 debug() << udi << "has no parent, returning null string."; 0318 return QString(); 0319 } 0320 0321 Solid::Block* sb = parent.as<Solid::Block>(); 0322 if( !sb ) 0323 { 0324 debug() << parent.udi() << "failed to convert to Block, returning null string."; 0325 return QString(); 0326 } 0327 0328 return sb->device(); 0329 } 0330 0331 bool 0332 MediaDeviceCache::isGenericEnabled( const QString &udi ) const 0333 { 0334 DEBUG_BLOCK 0335 if( m_type[udi] != MediaDeviceCache::SolidVolumeType ) 0336 { 0337 debug() << "Not SolidVolumeType, returning false"; 0338 return false; 0339 } 0340 Solid::Device device( udi ); 0341 Solid::StorageAccess* ssa = device.as<Solid::StorageAccess>(); 0342 if( !ssa || !ssa->isAccessible() ) 0343 { 0344 debug() << "Not able to convert to StorageAccess or not accessible, returning false"; 0345 return false; 0346 } 0347 if( device.parent().as<Solid::PortableMediaPlayer>() ) 0348 { 0349 debug() << "Could convert parent to PortableMediaPlayer, returning true"; 0350 return true; 0351 } 0352 if( QFile::exists( ssa->filePath() + QLatin1Char('/') + ".is_audio_player" ) ) 0353 { 0354 return true; 0355 } 0356 return false; 0357 } 0358 0359 const QString 0360 MediaDeviceCache::volumeMountPoint( const QString &udi ) const 0361 { 0362 DEBUG_BLOCK 0363 Solid::Device device( udi ); 0364 Solid::StorageAccess* ssa = device.as<Solid::StorageAccess>(); 0365 if( !ssa || !ssa->isAccessible() ) 0366 { 0367 debug() << "Not able to convert to StorageAccess or not accessible, returning empty"; 0368 return QString(); 0369 } 0370 return ssa->filePath(); 0371 } 0372 0373