/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.videos.model; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.NullProgressMonitor; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.utilities.HttpUtils; import com.motorola.studio.android.videos.Activator; import com.motorola.studio.android.videos.implementation.youtube.YoutubeVideoServiceProvider; import com.motorola.studio.android.videos.model.extension.VideoServiceProvider; /** * Manage the actions related to videos, including loading them and * providing any related information to other classes. */ public class VideoManager { /* * Sort options */ public static final int SORT_MOST_RECENT = 0; public static final int SORT_MOST_VIEWED = 1; public static final int SORT_TOP_RATED = 2; /* * Video channels and source definitions (XML) */ public static final String VIDEOS_DEFINITIONS = "resources/motodev_videos.xml"; public static final String TAG_USER = "user"; public static final String TAG_USER_ATT_NAME = "name"; public static final String TAG_CHANNEL = "channel"; public static final String TAG_CHANNEL_ATT_NAME = "name"; public static final String TAG_CHANNEL_ATT_DISPLAY_NAME = "display_name"; // "fixed" or "variable" public static final String TAG_CHANNEL_ATT_ORDER = "order"; public static final String TAG_CHANNEL_ATT_ORDER_FIXED = "fixed"; // "true" or "false" public static final String TAG_CHANNEL_ATT_DEFAULT = "default"; /* * Singleton */ private static VideoManager instance; /* * The selected video service provider */ private VideoServiceProvider videoServiceProvider = null; /* * Credentials information to be passed to the video service provider */ private String userName = null; /* * The video channel objects (the videos themselves are attributes of these objects) */ private String defaultVideoChannel = null; // Map "channel name" -> object that represent the video channel private final Map<String, VideoChannel> channelsMap = new HashMap<String, VideoChannel>(); private final List<VideoChannel> channelsList = new ArrayList<VideoChannel>(); /** * Singleton * * @return a unique VideoManager instance * @throws Exception */ public static synchronized VideoManager getInstance() throws Exception { if (instance == null) { instance = new VideoManager(); } return instance; } /** * Load all channels and videos */ public void load() throws Exception { channelsMap.clear(); channelsList.clear(); File file = new File(FileLocator.toFileURL( Activator.getDefault().getBundle().getEntry(VIDEOS_DEFINITIONS)).getPath()); DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = documentBuilder.parse(file); /* * Get service username */ NodeList user = doc.getElementsByTagName(TAG_USER); Node userNameNode = user.item(0).getAttributes().getNamedItem(TAG_USER_ATT_NAME); userName = userNameNode.getNodeValue(); /* * Initialize video service provider */ videoServiceProvider = new YoutubeVideoServiceProvider(userName); /* * Get all channels */ NodeList channels = doc.getElementsByTagName(TAG_CHANNEL); for (int i = 0; i < channels.getLength(); i++) { Node channel = channels.item(i); String displayName = channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_DISPLAY_NAME) .getTextContent(); String name = channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_NAME).getTextContent(); boolean order = false; boolean defaultChannel = false; if (channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_ORDER) != null) { order = channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_ORDER) .getTextContent().equals(TAG_CHANNEL_ATT_ORDER_FIXED) ? true : false; } if (channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_DEFAULT) != null) { defaultChannel = channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_DEFAULT) .getTextContent().equals(new Boolean(true).toString()) ? true : false; } /* * Create and populate the channels */ VideoChannel videoChannel = new VideoChannel(name); videoChannel.setDisplayName(displayName); videoChannel.setOrdered(order); videoChannel.setDefaultChannel(defaultChannel); if (defaultChannel) { defaultVideoChannel = name; } List<Video> videos = videoServiceProvider.loadVideos(videoChannel); if (videos != null) { videoChannel.setVideos(videos); } else { videoChannel.setActive(false); } channelsMap.put(name, videoChannel); channelsList.add(videoChannel); } } /** * Sort all videos from all channels * * @param criteria */ public void sort(final int criteria) { for (VideoChannel channel : channelsList) { if (!channel.isOrdered()) { List<Video> videos = channel.getVideos(); Collections.sort(videos, new Comparator<Video>() { public int compare(Video arg0, Video arg1) { int result = 0; switch (criteria) { case VideoManager.SORT_MOST_RECENT: result = arg0.getDate().compareTo(arg1.getDate()); break; case VideoManager.SORT_MOST_VIEWED: result = arg0.getViews() >= arg1.getViews() ? 1 : -1; break; case VideoManager.SORT_TOP_RATED: result = arg0.getRating() >= arg1.getRating() ? 1 : -1; break; } return result; } }); } } } /** * Get videos from a channel * * @param channel channel to retrieve the videos */ public List<Video> getVideos(VideoChannel channel) { List<Video> videos = null; if (channelsMap.get(channel.getName()) != null) { videos = channelsMap.get(channel.getName()).getVideos(); } return videos; } /** * Get all channels * * @return all channels */ public List<VideoChannel> getChannels() { return channelsList; } /** * Get only the active channels * * @return the active channels, which were found by the video service provider */ public List<VideoChannel> getActiveChannels() { List<VideoChannel> activeVideoChannels = new ArrayList<VideoChannel>(); for (VideoChannel channel : channelsList) { if (channel.isActive()) { activeVideoChannels.add(channel); } } return activeVideoChannels; } /** * Get the default channel, to be selected in the UI by default * * @return the default channel */ public VideoChannel getDefaultChannel() { return channelsMap.get(defaultVideoChannel); } /** * Rate a video * * @param video video to be rated * @param rate video rate */ public void rate(Video video, int rate) throws Exception { // delegate to the video service provider videoServiceProvider.rate(video, rate); } /** * Search all videos from all channels * * @param keyword search keyword * @return only the videos that match the keyword */ public List<Video> search(String keyword) { List<Video> selectedVideos = new ArrayList<Video>(); // split the keyword into tokens String[] tokens = keyword.split("\\s"); int numberOfTokens = tokens.length; /* * For each channel, search the videos and try to find all * tokens on each video */ int visibleVideosInChannel; for (VideoChannel channel : channelsList) { visibleVideosInChannel = 0; for (Video video : channel.getVideos()) { int tokensFound = 0; if (numberOfTokens > 0) { StringBuffer sb = new StringBuffer(); sb.append(video.getTitle()); sb.append(" "); sb.append(video.getDescription()); sb.append(" "); sb.append(video.getKeywords()); for (int i = 0; i < tokens.length; i++) { String token = tokens[i]; if (sb.toString().toUpperCase().contains(token.toUpperCase())) { tokensFound++; } } } // if all tokens were found, mark the video as visible ... if (tokensFound == numberOfTokens) { video.setVisible(true); visibleVideosInChannel++; selectedVideos.add(video); } // ... otherwise, mark the video as not visible else { video.setVisible(false); } } channel.setVisibleVideos(visibleVideosInChannel); } return selectedVideos; } /** * Get the video thumbnail from the internet, given its URL * * @param url video URL * @return the file representing the downloaded thumbnail */ public File getThumbnail(URL url) { File cacheIconFile = null; HttpUtils httpUtils = new HttpUtils(); InputStream inStream = null; FileOutputStream outStream = null; try { inStream = httpUtils.getInputStreamForUrl(url.toString(), new NullProgressMonitor()); File remoteFile = new File(url.toString()); String[] remoteFileName = remoteFile.getName().split("\\."); cacheIconFile = File.createTempFile(remoteFileName[0], "." + remoteFileName[1]); cacheIconFile.deleteOnExit(); outStream = new FileOutputStream(cacheIconFile); byte[] buf = new byte[1024]; int len; while ((len = inStream.read(buf)) > 0) { outStream.write(buf, 0, len); } } catch (Exception e) { StudioLogger.error(this.getClass(), "Error while retrieving video thumbnail", e); } finally { try { inStream.close(); outStream.close(); } catch (Exception e) { StudioLogger.error(e.getMessage()); } } return cacheIconFile; } }