/* * Copyright (C) 2005-2009 Team XBMC * http://xbmc.org * * 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; either version 2, or (at your option) * any later version. * * 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 XBMC Remote; see the file license. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ package org.xbmc.android.remote.presentation.controller; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.xbmc.android.remote.R.drawable; import org.xbmc.android.remote.business.ManagerFactory; import org.xbmc.android.remote.presentation.activity.NowPlayingActivity; import org.xbmc.android.remote.presentation.activity.UrlIntentActivity; import org.xbmc.android.util.YoutubeURLParser; import org.xbmc.api.business.DataResponse; import org.xbmc.api.business.IControlManager; import org.xbmc.api.business.IInfoManager; import org.xbmc.api.business.IVideoManager; import org.xbmc.api.info.SystemInfo; import org.xbmc.api.object.Movie; import org.xbmc.api.presentation.INotifiableController; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; /** * Class that is called when XBMC locally tries to open any media content. The * user then has an additional option "Play in XBMC". * * @author Team XBMC */ public class UrlIntentController extends AbstractController implements IController, INotifiableController { private static final String IMDb_SHARE_PREFIX = "IMDb:"; private static final String CONFIRM_PLAY_ON_XBMC = "setting_confirm_play_on_xbmc"; private final IControlManager mControlManager; private final IInfoManager mInfoManager; private final IVideoManager mVideoManager; private DataResponse<String> mXbmcStatusHandler; public UrlIntentController(Activity activity, Handler handler) { super.onCreate(activity, handler); mInfoManager = ManagerFactory.getInfoManager(this); mControlManager = ManagerFactory.getControlManager(this); mVideoManager = ManagerFactory.getVideoManager(this); } /* (non-Javadoc) * @see org.xbmc.android.remote.presentation.controller.IController#onActivityPause() */ public void onActivityPause() { mInfoManager.setController(null); mControlManager.setController(null); mVideoManager.setController(null); super.onActivityPause(); } /* (non-Javadoc) * @see org.xbmc.android.remote.presentation.controller.IController#onActivityResume(android.app.Activity) */ public void onActivityResume(Activity activity) { super.onActivityResume(activity); mInfoManager.setController(this); mControlManager.setController(this); mVideoManager.setController(this); mInfoManager.getSystemInfo(mXbmcStatusHandler, SystemInfo.SYSTEM_BUILD_VERSION, mActivity.getApplicationContext()); } public void playUrl(String url) { mControlManager.playUrl(new DataResponse<Boolean>(), url, mActivity.getApplicationContext()); } public void setupStatusHandler() { mXbmcStatusHandler = new DataResponse<String>() { public void run() { if (!value.equals("")) { checkIntent(); } } }; } @Override public void onError(Exception exception) { final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); try { throw exception; } catch (Exception e) { builder.setTitle("Unable to send URL to XBMC!"); builder.setMessage(e.getMessage()); builder.setNeutralButton("Ok", new OnClickListener() { public void onClick(DialogInterface dialog, int which) { mActivity.finish(); } }); final AlertDialog alert = builder.create(); try { alert.show(); } catch (Exception ee) { e.printStackTrace(); } } } private static boolean isValidURL(String path){ if(path == null || path.equals("")) return false; try{ new URL(path); } catch(MalformedURLException e) { return false; } return true; } private static String hasURL(String path){ String[] words = path.split(" "); for (String word : words) { if (word.startsWith("http://") || word.startsWith("https://")) { return word; } } return null; } /** * Checks the intent that created/resumed this activity. Used to see if we are being handed * an URL that should be passed to XBMC. */ private void checkIntent(){ Intent intent = mActivity.getIntent(); final String action = intent.getAction(); if(action != null) { Log.i("CHECKINTENT", action); if ( action.equals(UrlIntentActivity.ACTION) ){ //final String path = intent.getData().toString(); final String path = hasURL(intent.getStringExtra(Intent.EXTRA_TEXT)); final String subjectTxt = intent.getStringExtra(Intent.EXTRA_SUBJECT); if(isIMDB(subjectTxt)){ handleIMDb(path); cleaupIntent(intent); } else if(isValidURL(path)){ handleURL(path); cleaupIntent(intent); } } } } private void handleIMDb(String path) { final Pattern pattern = Pattern.compile("^http://imdb\\.com/title/(tt\\d{7})$", Pattern.MULTILINE); final Matcher matcher = pattern.matcher(path); if(matcher.find()){ final String imdbIdRequested = matcher.group(1); final ProgressDialog dialog = ProgressDialog.show(mActivity, "Loading Movies", null, true); mVideoManager.getMovies(new DataResponse<ArrayList<Movie>>(){ public void run() { dialog.dismiss(); processImdbResults(imdbIdRequested, value); }; }, mActivity.getApplicationContext()); }else{ Toast.makeText(mActivity, "Error parsing IMDb link", Toast.LENGTH_LONG).show(); //TODO: Externalize string mActivity.finish(); } } private void processImdbResults(String imdbIdRequested, ArrayList<Movie> movies) { Movie movieFound = null; for(Movie movie : movies){ if(movie.getIMDbId().equals(imdbIdRequested)){ movieFound = movie; break; } } if(movieFound == null){ Toast.makeText(mActivity, "Movie not found in XBMC library", Toast.LENGTH_LONG).show(); //TODO: Externalize string mActivity.finish(); }else{ maybePlayMovie(movieFound); } } private void maybePlayMovie(final Movie movie) { // Toast.makeText(mActivity, movie.getName(), Toast.LENGTH_LONG).show(); final String message = "Do you want to play\n" + movie.getName() + "\non XBMC?"; boolean confirmPlayOnXBMC = getConfirmPlayOnXBMC(); if (confirmPlayOnXBMC) { final Builder builder = new Builder(mActivity); builder.setTitle("Play Movie on XBMC?"); builder.setMessage(message); builder.setCancelable(true); builder.setIcon(drawable.icon); builder.setNeutralButton("Yes", new android.content.DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { playMovieThreaded(movie); mActivity.finish(); } }); builder.setCancelable(true); builder.setNegativeButton("No", new android.content.DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); mActivity.finish(); } }); final AlertDialog alert = builder.create(); try { alert.show(); } catch (Exception e) { e.printStackTrace(); } } else { playMovieThreaded(movie); mActivity.finish(); } } private void playMovieThreaded(Movie movie) { mControlManager.playFile(new DataResponse<Boolean>() { public void run() { if (value) { mActivity.startActivity(new Intent(mActivity.getApplicationContext(), NowPlayingActivity.class)); } } }, movie.getPath(), 1, mActivity.getApplicationContext()); } private boolean isIMDB(String subjectTxt) { return subjectTxt.startsWith(IMDb_SHARE_PREFIX); } private void cleaupIntent(Intent intent) { //cleanup so we won't trigger again. intent.setAction(null); intent.setData(null); } private void handleURL(final String path) { // If it is a youtube URL, we have to parse the video id from it and send it to the youtube plugin. // The syntax for that is plugin://plugin.video.youtube/?path=/root/search%26action=play_video%26videoid=VIDEOID Uri playuri = Uri.parse(path); final String url; final String message; String youtubeVideoId = YoutubeURLParser.parseYoutubeURL(playuri); if(youtubeVideoId != null){ url = "plugin://plugin.video.youtube/?path=/root/search&action=play_video&videoid=" + youtubeVideoId; message = "Do you want to play\nYoutube video " + youtubeVideoId + " on XBMC? Youtube addon required!"; }else{ // Not a youtube URL or unparsable YouTube URL so just pass it on to XBMC as-is url = playuri.toString(); message = "Do you want to play\n" + path + "\n on XBMC?"; } boolean confirmPlayOnXBMC = getConfirmPlayOnXBMC(); if (confirmPlayOnXBMC) { final Builder builder = new Builder(mActivity); builder.setTitle("Play URL on XBMC?"); builder.setMessage(message); builder.setCancelable(true); builder.setIcon(drawable.icon); builder.setNeutralButton("Yes", new android.content.DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { playUrlThreaded(url); mActivity.finish(); } }); builder.setCancelable(true); builder.setNegativeButton("No", new android.content.DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); mActivity.finish(); } }); final AlertDialog alert = builder.create(); try { alert.show(); } catch (Exception e) { e.printStackTrace(); } } else { playUrlThreaded(url); mActivity.finish(); } } private void playUrlThreaded(final String url) { new Thread(){ public void run(){ Looper.prepare(); playUrl(url); Looper.loop(); } }.start(); } private boolean getConfirmPlayOnXBMC() { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity.getApplicationContext()); boolean confirmPlayOnXBMC = prefs.getBoolean(CONFIRM_PLAY_ON_XBMC, true); return confirmPlayOnXBMC; } }