/*
* Created on Jul 31, 2009
* Created by Paul Gardner
*
* Copyright 2009 Vuze, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
package com.aelitis.azureus.core.devices.impl;
import java.io.*;
import java.util.*;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.AsyncDispatcher;
import com.aelitis.azureus.core.devices.Device;
import com.aelitis.azureus.core.devices.DeviceManagerListener;
import com.aelitis.azureus.core.devices.DeviceMediaRenderer;
import com.aelitis.azureus.core.devices.DeviceTemplate;
import com.aelitis.azureus.core.drivedetector.DriveDetectedInfo;
import com.aelitis.azureus.core.drivedetector.DriveDetectedListener;
import com.aelitis.azureus.core.drivedetector.DriveDetectorFactory;
import com.aelitis.azureus.util.MapUtils;
public class
DeviceDriveManager
implements DriveDetectedListener
{
private DeviceManagerImpl manager;
private Map<String,DeviceMediaRendererManual> device_map = new HashMap<String, DeviceMediaRendererManual>();
private AsyncDispatcher async_dispatcher = new AsyncDispatcher();
private boolean listener_added;
protected
DeviceDriveManager(
DeviceManagerImpl _manager )
{
manager = _manager;
manager.addListener(
new DeviceManagerListener()
{
public void
deviceAdded(
Device device )
{
}
public void
deviceChanged(
Device device )
{
}
public void
deviceAttentionRequest(
Device device )
{
}
public void
deviceRemoved(
Device device )
{
synchronized( device_map ){
Iterator<Map.Entry<String,DeviceMediaRendererManual>> it = device_map.entrySet().iterator();
while( it.hasNext()){
Map.Entry<String,DeviceMediaRendererManual> entry = it.next();
if ( entry.getValue() == device ){
it.remove();
}
}
}
}
public void
deviceManagerLoaded()
{
}
});
if ( manager.getAutoSearch()){
listener_added = true;
DriveDetectorFactory.getDeviceDetector().addListener( this );
}
}
protected void
search()
{
async_dispatcher.dispatch(
new AERunnable()
{
public void
runSupport()
{
if ( listener_added ){
DriveDetectedInfo[] info = DriveDetectorFactory.getDeviceDetector().getDetectedDriveInfo();
for ( DriveDetectedInfo i: info ){
driveRemoved( i );
driveDetected( i );
}
return;
}
try{
// this should synchronously first any discovered drives
DriveDetectorFactory.getDeviceDetector().addListener( DeviceDriveManager.this );
}finally{
DriveDetectorFactory.getDeviceDetector().removeListener( DeviceDriveManager.this );
}
}
});
}
public void
driveDetected(
final DriveDetectedInfo info )
{
//System.out.println("DD " + info.getLocation() + " via " + Debug.getCompressedStackTrace());
async_dispatcher.dispatch(new AERunnable() {
public void runSupport() {
Map<String, Object> infoMap = info.getInfoMap();
boolean isWritableUSB = MapUtils.getMapBoolean(infoMap, "isWritableUSB", false);
File root = info.getLocation();
String sProdID = MapUtils.getMapString(infoMap, "ProductID",
MapUtils.getMapString(infoMap, "Product Name", "")).trim();
String sVendor = MapUtils.getMapString(infoMap, "VendorID",
MapUtils.getMapString(infoMap, "Vendor Name", "")).trim();
// Historically, we gave IDs to Motorola, Samsung, and HTC phones
// based on their vendor and product id only. We need to maintain
// this ID in order to not create duplicates.
// Fortunately, both Motorola and Samsung include their model id in the
// sProdID. HTC doesn't, however, their PID doesn't identify unique
// models anyway, so including that wouldn't have helped anyway
if ((sVendor.equalsIgnoreCase("htc") && sProdID.equalsIgnoreCase("android phone"))
|| (sVendor.toLowerCase().contains("motorola") && sProdID.length() > 0)
|| sVendor.equalsIgnoreCase("samsung")) {
if (isWritableUSB && sVendor.equalsIgnoreCase("samsung")) {
// Samsungs that start with Y are MP3 players
// Samsungs that don't have a dash aren't smart phones (none that we know of anyway..)
// Fake not writable so we remove the device instead of adding it
isWritableUSB = !sProdID.startsWith("Y")
&& sProdID.matches(".*[A-Z]-.*");
}
String name = sProdID.startsWith(sVendor) ? "" : sVendor;
if (sVendor.length() > 0) {
name += " ";
}
name += sProdID;
String id = "android.";
id += sProdID.replaceAll(" ", ".").toLowerCase();
if (sVendor.length() > 0) {
id += "." + sVendor.replaceAll(" ", ".").toLowerCase();
}
if (isWritableUSB) {
addDevice(name, id, root, new File(root, "videos"), true);
} else {
//Fixup old bug where we were adding Samsung hard drives as devices
Device existingDevice = getDeviceMediaRendererByClassification(id);
if (existingDevice != null) {
existingDevice.remove();
}
}
return;
} else if (isWritableUSB && sVendor.toLowerCase().equals("rim")) {
String name = sVendor;
if (name.length() > 0) {
name += " ";
}
name += sProdID;
String id = "";
id += sProdID.replaceAll(" ", ".").toLowerCase();
if (sVendor.length() > 0) {
id += "." + sVendor.replaceAll(" ", ".").toLowerCase();
}
DeviceMediaRendererManual device = addDevice(name, id, root, new File(root, "videos"), false);
if (device != null) {
device.setImageID("bb");
}
return;
}
if (!isWritableUSB) {
return;
}
if (root.exists()) {
File[] folders = root.listFiles();
if (folders != null) {
Set<String> names = new HashSet<String>();
for (File file : folders) {
names.add(file.getName().toLowerCase());
}
if (names.contains("psp") && names.contains("video")) {
addDevice("PSP", "sony.PSP", root, new File(root, "VIDEO"), false);
return;
}
}
}
String pid = MapUtils.getMapString(infoMap, "PID", null);
String vid = MapUtils.getMapString(infoMap, "VID", null);
if (pid != null && vid != null) {
String name = sProdID.startsWith(sVendor) ? "" : sVendor;
if (name.length() > 0) {
name += " ";
}
name += sProdID;
String id = "";
id += sProdID.replaceAll(" ", ".").toLowerCase();
id += "." + pid.toLowerCase();
if (sVendor.length() > 0) {
id += "." + sVendor.replaceAll(" ", ".").toLowerCase();
}
id += "." + vid.toLowerCase();
// cheap hack to detect the PSP when it has no psp or video dir
if (id.equals("\"psp\".ms.02d2.sony.054c")
|| id.equals("\"psp\".ms.0381.sony.054c")) {
if (addDevice("PSP", "sony.PSP", root, new File(root, "VIDEO"), false) != null) {
return;
}
}
addDevice(name, id, root, new File(root, "video"), true);
}
}
});
}
protected DeviceMediaRenderer getDeviceMediaRendererByClassification(String target_classification) {
DeviceImpl[] devices = manager.getDevices();
for ( DeviceImpl device: devices ){
if ( device instanceof DeviceMediaRenderer ){
DeviceMediaRenderer renderer = (DeviceMediaRenderer)device;
String classification = renderer.getClassification();
if ( classification.equalsIgnoreCase( target_classification )){
return renderer;
}
}
}
return null;
}
protected DeviceMediaRendererManual addDevice(
String target_name,
String target_classification,
File root,
File target_directory,
boolean generic)
{
DeviceMediaRenderer existingDevice = getDeviceMediaRendererByClassification(target_classification);
if (existingDevice instanceof DeviceMediaRendererManual ) {
mapDevice( (DeviceMediaRendererManual) existingDevice, root, target_directory );
existingDevice.setGenericUSB(generic);
return (DeviceMediaRendererManual) existingDevice;
}
DeviceTemplate[] templates = manager.getDeviceTemplates( Device.DT_MEDIA_RENDERER );
DeviceMediaRendererManual renderer = null;
for ( DeviceTemplate template: templates ){
if ( template.getClassification().equalsIgnoreCase( target_classification )){
try{
renderer = (DeviceMediaRendererManual)template.createInstance( target_name );
break;
}catch( Throwable e ){
log( "Failed to add device", e );
}
}
}
if ( renderer == null ){
// damn, the above doesn't work until devices is turned on...
try{
renderer = (DeviceMediaRendererManual)manager.createDevice( Device.DT_MEDIA_RENDERER, null, target_classification, target_name, true );
}catch( Throwable e ){
log( "Failed to add device", e );
}
}
if ( renderer != null ){
try{
renderer.setAutoCopyToFolder( true );
// This will cause a change event
renderer.setGenericUSB(generic);
mapDevice( renderer, root, target_directory );
return renderer;
}catch( Throwable e ){
log( "Failed to add device", e );
}
}
return renderer;
}
public void
driveRemoved(
final DriveDetectedInfo info )
{
async_dispatcher.dispatch(
new AERunnable()
{
public void
runSupport()
{
unMapDevice( info.getLocation());
}
});
}
protected void
mapDevice(
DeviceMediaRendererManual renderer,
File root,
File copy_to )
{
DeviceMediaRendererManual existing;
synchronized( device_map ){
existing = device_map.put( root.getAbsolutePath(), renderer );
}
if ( existing != null && existing != renderer ){
log( "Unmapped " + existing.getName() + " from " + root );
existing.setCopyToFolder( null );
}
log( "Mapped " + renderer.getName() + " to " + root );
renderer.setCopyToFolder( copy_to );
renderer.setLivenessDetectable( true );
renderer.alive();
}
protected void
unMapDevice(
File root )
{
DeviceMediaRendererManual existing;
synchronized( device_map ){
existing = device_map.remove( root.getAbsolutePath());
}
if ( existing != null ){
log( "Unmapped " + existing.getName() + " from " + root );
existing.setCopyToFolder( null );
existing.dead();
}
}
protected void
log(
String str )
{
manager.log( "DriveMan: " + str );
}
protected void
log(
String str,
Throwable e )
{
manager.log( "DriveMan: " + str, e );
}
}