/* * 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.util.ArrayList; import java.util.HashMap; import java.util.Observable; import java.util.Observer; import org.xbmc.android.remote.R; import org.xbmc.android.remote.business.AbstractManager; import org.xbmc.android.remote.business.ManagerFactory; import org.xbmc.android.remote.presentation.activity.GestureRemoteActivity; import org.xbmc.android.remote.presentation.activity.HomeActivity; import org.xbmc.android.remote.presentation.activity.HostSettingsActivity; import org.xbmc.android.remote.presentation.activity.ListActivity; import org.xbmc.android.remote.presentation.activity.MovieLibraryActivity; import org.xbmc.android.remote.presentation.activity.MusicLibraryActivity; import org.xbmc.android.remote.presentation.activity.NowPlayingActivity; import org.xbmc.android.remote.presentation.activity.RemoteActivity; import org.xbmc.android.remote.presentation.activity.TvShowLibraryActivity; import org.xbmc.android.remote.presentation.notification.NowPlayingNotificationManager; import org.xbmc.android.util.ClientFactory; import org.xbmc.android.util.ConnectionFactory; import org.xbmc.android.util.HostFactory; import org.xbmc.android.util.PowerDown; import org.xbmc.android.util.WakeOnLan; import org.xbmc.android.util.WifiHelper; import org.xbmc.api.business.DataResponse; import org.xbmc.api.business.IInfoManager; import org.xbmc.api.business.IMusicManager; import org.xbmc.api.business.INotifiableManager; import org.xbmc.api.business.ITvShowManager; import org.xbmc.api.business.IVideoManager; import org.xbmc.api.info.SystemInfo; import org.xbmc.api.object.Actor; import org.xbmc.api.object.Album; import org.xbmc.api.object.Episode; import org.xbmc.api.object.Host; import org.xbmc.api.object.ICoverArt; import org.xbmc.api.object.Movie; import org.xbmc.api.object.Season; import org.xbmc.api.object.TvShow; import org.xbmc.api.presentation.INotifiableController; import org.xbmc.api.type.MediaType; import org.xbmc.httpapi.BroadcastListener; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.net.wifi.WifiManager.WifiLock; import android.os.Bundle; import android.os.CountDownTimer; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.GridView; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; public class HomeController extends AbstractController implements INotifiableController, IController, Observer, OnSharedPreferenceChangeListener { private static final int HOME_ACTION_REMOTE = 0; private static final int HOME_ACTION_MUSIC = 1; private static final int HOME_ACTION_VIDEOS = 2; private static final int HOME_ACTION_PICTURES = 3; private static final int HOME_ACTION_NOWPLAYING = 4; private static final int HOME_ACTION_RECONNECT = 5; private static final int HOME_ACTION_WOL = 6; private static final int HOME_ACTION_TVSHOWS = 7; private static final int HOME_ACTION_POWERDOWN = 8; private IInfoManager mInfoManager; private static final String TAG = "HomeController"; private static final boolean DEBUG = false; private DataResponse<String> mUpdateVersionHandler; private HomeAdapter mHomeMenu; private HomeAdapter mOfflineMenu; private int mNumCoversDownloaded = 0; private WolCounter mWolCounter; private final HomeItem mHomeWol = new HomeItem(HOME_ACTION_WOL, R.drawable.icon_home_power, "Power On", "Turn your XBMC's"); private final GridView mMenuGrid; public HomeController(Activity activity, Handler handler, GridView menuGrid) { super.onCreate(activity, handler); mInfoManager = ManagerFactory.getInfoManager(this); mMenuGrid = menuGrid; setupMenuItems(menuGrid); // BroadcastListener bcl = BroadcastListener.getInstance(ConnectionManager.getHttpClient(this)); // bcl.addObserver(this); } public View.OnClickListener getOnHostChangeListener() { return new OnClickListener() { public void onClick(View v) { openHostChanger(); } }; } /** * Opens the host changer popup. */ public void openHostChanger() { // granted, this is butt-ugly. better ideas, be my guest. final ArrayList<Host> hosts = HostFactory.getHosts(mActivity.getApplicationContext()); final HashMap<Integer, Host> hostMap = new HashMap<Integer, Host>(); final CharSequence[] names = new CharSequence[hosts.size()]; int i = 0; for (Host host : hosts) { names[i] = host.name; hostMap.put(i, host); i++; } if (hosts.size() > 0) { AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); builder.setTitle("Pick your XBMC!"); builder.setItems(names, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { final Host host = hostMap.get(which); if (HostFactory.host != null && HostFactory.host.id == host.id) { Toast.makeText(mActivity.getApplicationContext(), "You've picked the same host as the current.", Toast.LENGTH_SHORT).show(); } else { Log.i(TAG, "Switching host to " + (host == null ? "<null>" : host.addr) + "."); HostFactory.saveHost(mActivity.getApplicationContext(), host); final GridView menuGrid = (GridView)mActivity.findViewById(R.id.HomeItemGridView); resetupOfflineMenuItems(); setHomeAdapter(menuGrid, mOfflineMenu); final Button versionButton = (Button)mActivity.findViewById(R.id.home_version_button); versionButton.setText("Connecting..."); Toast.makeText(mActivity.getApplicationContext(), "Changed host to " + host.toString() + ".", Toast.LENGTH_SHORT).show(); ClientFactory.resetClient(host); mInfoManager.getSystemInfo(mUpdateVersionHandler, SystemInfo.SYSTEM_BUILD_VERSION, mActivity.getApplicationContext()); } } }); AlertDialog dialog = builder.create(); dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, WindowManager.LayoutParams.FLAG_BLUR_BEHIND); dialog.show(); } else { Toast.makeText(mActivity.getApplicationContext(), "No XBMC hosts defined, please do that first.", Toast.LENGTH_LONG).show(); Intent intent = new Intent(mActivity, HostSettingsActivity.class); mActivity.startActivity(intent); } } public void setupVersionHandler(final Handler handler, final Button versionTextView, final GridView homeItemGrid) { mUpdateVersionHandler = new DataResponse<String>() { public void run() { if (!mPaused) { handler.post(new Runnable() { public void run() { if (!ConnectionFactory.isNetworkAvailable(mActivity.getApplicationContext())) { versionTextView.setText("No network"); } if (!value.equals("")) { if (mWolCounter != null) { mWolCounter.cancel(); } versionTextView.setText("XBMC " + value); homeItemGrid.setAdapter(mHomeMenu); NowPlayingNotificationManager.getInstance(mActivity.getApplicationContext()).startNotificating(); } else { versionTextView.setText("Check Settings and retry"); homeItemGrid.setAdapter(mOfflineMenu); } } }); } } }; } private void setupMenuItems(GridView menuGrid) { final HomeItem remote = new HomeItem(HOME_ACTION_REMOTE, R.drawable.icon_home_remote, "Remote Control", "Use as"); final ArrayList<HomeItem> homeItems = new ArrayList<HomeItem>(); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity.getApplicationContext()); if (prefs.getBoolean("setting_show_home_music", true)) homeItems.add(new HomeItem(HOME_ACTION_MUSIC, R.drawable.icon_home_music, "Music", "Listen to")); if (prefs.getBoolean("setting_show_home_movies", true)) homeItems.add(new HomeItem(HOME_ACTION_VIDEOS, R.drawable.icon_home_movie, "Movies", "Watch your")); if (prefs.getBoolean("setting_show_home_tv", true)) homeItems.add(new HomeItem(HOME_ACTION_TVSHOWS, R.drawable.icon_home_tv, "TV Shows", "Watch your")); if (prefs.getBoolean("setting_show_home_pictures", true)) homeItems.add(new HomeItem(HOME_ACTION_PICTURES, R.drawable.icon_home_picture, "Pictures", "Browse your")); prefs.registerOnSharedPreferenceChangeListener(this); homeItems.add(new HomeItem(HOME_ACTION_NOWPLAYING, R.drawable.icon_home_playing, "Now Playing", "See what's")); homeItems.add(remote); if (prefs.getBoolean("setting_show_home_powerdown", false)) homeItems.add(new HomeItem(HOME_ACTION_POWERDOWN, R.drawable.icon_home_power, "Power Off", "Turn your XBMC off")); final ArrayList<HomeItem> offlineItems = new ArrayList<HomeItem>(); offlineItems.add(remote); offlineItems.add(new HomeItem(HOME_ACTION_RECONNECT, R.drawable.icon_home_reconnect, "Connect", "Try again to")); // final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity.getApplicationContext()); // final String wolMac = prefs.getString("setting_wol", ""); if (HostFactory.host != null && !"".equals(HostFactory.host.mac_addr)) offlineItems.add(mHomeWol); mHomeMenu = new HomeAdapter(mActivity, homeItems); mOfflineMenu = new HomeAdapter(mActivity, offlineItems); setHomeAdapter(menuGrid, mOfflineMenu); } /** * Due to host changing we need to resetup the offline items with checking of WOL prefs and WiFi activation. * @param menuGrid */ private void resetupOfflineMenuItems() { mOfflineMenu.remove(mHomeWol); if (HostFactory.host != null && !"".equals(HostFactory.host.mac_addr)) mOfflineMenu.add(mHomeWol); } private void setHomeAdapter(GridView menuGrid, HomeAdapter adapter) { menuGrid.setAdapter(adapter); menuGrid.setOnItemClickListener(getHomeMenuOnClickListener()); menuGrid.setSelected(true); menuGrid.setSelection(0); } private OnItemClickListener getHomeMenuOnClickListener() { return new OnItemClickListener() { public void onItemClick(AdapterView<?> listView, View v, int position, long ID) { HomeItem item = (HomeItem)listView.getAdapter().getItem(position); final Host host = HostFactory.host; Intent intent = null; switch (item.ID) { case HOME_ACTION_REMOTE: final int mode = mActivity.getSharedPreferences("global", Context.MODE_PRIVATE).getInt(RemoteController.LAST_REMOTE_PREFNAME, -1); if (mode == RemoteController.LAST_REMOTE_GESTURE) { intent = new Intent(v.getContext(), GestureRemoteActivity.class); } else { intent = new Intent(v.getContext(), RemoteActivity.class); } intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NO_HISTORY); break; case HOME_ACTION_MUSIC: intent = new Intent(v.getContext(), MusicLibraryActivity.class); break; case HOME_ACTION_VIDEOS: intent = new Intent(v.getContext(), MovieLibraryActivity.class); break; case HOME_ACTION_TVSHOWS: intent = new Intent(v.getContext(), TvShowLibraryActivity.class); break; case HOME_ACTION_PICTURES: intent = new Intent(v.getContext(), ListActivity.class); intent.putExtra(ListController.EXTRA_LIST_CONTROLLER, new FileListController()); intent.putExtra(ListController.EXTRA_SHARE_TYPE, MediaType.PICTURES); intent.putExtra(ListController.EXTRA_PATH, ""); break; case HOME_ACTION_NOWPLAYING: intent = new Intent(v.getContext(), NowPlayingActivity.class); break; case HOME_ACTION_RECONNECT: ((Button)mActivity.findViewById(R.id.home_version_button)).setText("Reconnecting..."); ClientFactory.resetClient(host); mInfoManager.getSystemInfo(mUpdateVersionHandler, SystemInfo.SYSTEM_BUILD_VERSION, mActivity.getApplicationContext()); break; case HOME_ACTION_WOL: WakeOnLan wol = new WakeOnLan(); if (wol.sendMagicPacket(host.mac_addr, host.wol_port)) { // If succeeded in sending the magic packet, begin the countdown if(mWolCounter != null) mWolCounter.cancel(); mWolCounter = new WolCounter(host.wol_wait * 1000,1000); mWolCounter.start(); } break; case HOME_ACTION_POWERDOWN: PowerDown powerdown = new PowerDown(); powerdown.ShowDialog(mActivity); break; } if (intent != null) { mActivity.startActivity(intent); } } }; } public void update(Observable observable, Object data) { if (data instanceof BroadcastListener.Event) { BroadcastListener.Event event = (BroadcastListener.Event)data; switch (event.id) { case BroadcastListener.EVENT_ON_PROGRESS_CHANGED: Log.i("broadcast", "EVENT_ON_PROGRESS_CHANGED: " + event.getInt(0)); break; default: Log.i("broadcast", "EVENT: " + event.id + ", int = " + event.getInt(0)); break; } } } private class HomeAdapter extends ArrayAdapter<HomeItem> { private Activity mActivity; HomeAdapter(Activity activity, ArrayList<HomeItem> items) { super(activity, R.layout.home_item, items); mActivity = activity; } public View getView(int position, View convertView, ViewGroup parent) { View row; if (convertView == null) { LayoutInflater inflater = mActivity.getLayoutInflater(); row = inflater.inflate(R.layout.home_item, null); } else { row = convertView; } HomeItem item = this.getItem(position); TextView title = (TextView)row.findViewById(R.id.TitleTextView); TextView subtitle = (TextView)row.findViewById(R.id.SubtitleTextView); ImageView icon = (ImageView)row.findViewById(R.id.IconImageView); title.setText(item.title); subtitle.setText(item.subtitle); icon.setImageResource(item.icon); return row; } } private class HomeItem { public final int ID, icon; public final String title, subtitle; public HomeItem(int ID, int icon, String title, String subtitle) { this.ID = ID; this.icon = icon; this.title = title; this.subtitle = subtitle; } } public class WolCounter extends CountDownTimer { private Button textCount; public WolCounter(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval); textCount = ((Button)mActivity.findViewById(R.id.home_version_button)); } @Override public void onFinish() { ((Button)mActivity.findViewById(R.id.home_version_button)).setText("Attempting to reconnect..."); mInfoManager.getSystemInfo(mUpdateVersionHandler, SystemInfo.SYSTEM_BUILD_VERSION, mActivity.getApplicationContext()); mWolCounter = null; } @Override public void onTick(long millisUntilFinished) { textCount.setText("Waiting for " + millisUntilFinished/1000 + " more seconds..."); } } public class ProgressThread extends Thread { private Handler mHandlerOut; private Handler mHandlerIn; private int mTotal; private int mPosition; public final static int MSG_NEXT = 0; public final static int MSG_QUIT = 1; public final static String DATA_TYPE = "type"; public final static String DATA_TOTAL = "total"; public final static String DATA_POSITION = "pos"; public final static String DATA_COVER = "cover"; private final int mType; private final ProgressDialog mProgressDialog; public ProgressThread(Handler h, int type, ProgressDialog progressDialog) { super("Cover download progress Thread"); if (DEBUG) Log.i(TAG, "[ProgressThread] Creating."); mHandlerOut = h; mType = type; mProgressDialog = progressDialog; } public void cancel() { if (DEBUG) Log.i(TAG, "[ProgressThread] Cancelling."); Message msgStart = mHandlerIn.obtainMessage(); Bundle b = new Bundle(); b.putInt(DATA_TYPE, mType); msgStart.what = MSG_QUIT; msgStart.setData(b); mHandlerOut.sendMessage(msgStart); } public Handler getHandlerIn() { return mHandlerIn; } public ArrayList<ICoverArt> getCovers() { switch (mType) { case HomeActivity.MENU_COVER_DOWNLOAD_MOVIES: final IVideoManager vm = ManagerFactory.getVideoManager(HomeController.this); final ArrayList<Movie> movies = vm.getMovies(mActivity.getApplicationContext()); return new ArrayList<ICoverArt>(movies); case HomeActivity.MENU_COVER_DOWNLOAD_MUSIC: final IMusicManager mm = ManagerFactory.getMusicManager(HomeController.this); final ArrayList<Album> albums = mm.getAlbums(mActivity.getApplicationContext()); return new ArrayList<ICoverArt>(albums); case HomeActivity.MENU_COVER_DOWNLOAD_ACTORS: final IVideoManager vm2 = ManagerFactory.getVideoManager(HomeController.this); final ArrayList<Actor> actors = vm2.getActors(mActivity.getApplicationContext()); return new ArrayList<ICoverArt>(actors); case HomeActivity.MENU_COVER_DOWNLOAD_TVSHOWS: final ITvShowManager tsm = ManagerFactory.getTvManager(HomeController.this); final ArrayList<TvShow> shows = tsm.getTvShows(mActivity.getApplicationContext()); return new ArrayList<ICoverArt>(shows); case HomeActivity.MENU_COVER_DOWNLOAD_TVSEASONS: final ITvShowManager tsm2 = ManagerFactory.getTvManager(HomeController.this); final ArrayList<Season> seasons = tsm2.getAllSeasons(mActivity.getApplicationContext()); return new ArrayList<ICoverArt>(seasons); case HomeActivity.MENU_COVER_DOWNLOAD_TVEPISODES: final ITvShowManager tsm3 = ManagerFactory.getTvManager(HomeController.this); final ArrayList<Episode> episodes = tsm3.getAllEpisodes(mActivity.getApplicationContext()); return new ArrayList<ICoverArt>(episodes); default: return null; } } public void run() { if (DEBUG) Log.i(TAG, "[ProgressThread] Starting progress thread."); final ArrayList<ICoverArt> covers = getCovers(); mTotal = covers.size(); mPosition = 0; mProgressDialog.setMax(covers.size()); boolean started = false; final WifiLock lock; if(HostFactory.host != null && HostFactory.host.wifi_only) { lock = WifiHelper.getInstance(mActivity).getNewWifiLock("BatchDownloader"); lock.acquire(); } else lock = null; final PowerManager pm = (PowerManager) mActivity.getSystemService(Context.POWER_SERVICE); final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG); wl.acquire(); Looper.prepare(); mHandlerIn = new Handler() { public void handleMessage(Message msgIn) { switch (msgIn.what) { case MSG_NEXT: if (DEBUG) Log.i(TAG, "[ProgressThread] New message received, posting back new cover."); Message msgOut = mHandlerOut.obtainMessage(); Bundle b = new Bundle(); b.putInt(DATA_TOTAL, mTotal); b.putInt(DATA_POSITION, mPosition); b.putInt(DATA_TYPE, mType); if (mPosition < mTotal) { b.putSerializable(DATA_COVER, covers.get(mPosition)); } msgOut.setData(b); mHandlerOut.sendMessage(msgOut); mPosition++; break; case MSG_QUIT: if (DEBUG) Log.i(TAG, "[ProgressThread] Exiting."); Looper.myLooper().quit(); break; } } }; if (!started) { mNumCoversDownloaded = 0; started = true; Message msgStart = mHandlerOut.obtainMessage(); Bundle b = new Bundle(); b.putInt(DATA_TYPE, mType); msgStart.what = MSG_NEXT; msgStart.setData(b); mHandlerIn.sendMessage(msgStart); if (DEBUG) Log.i(TAG, "[ProgressThread] Not started, kicking on...."); } Looper.loop(); if (lock != null) { lock.release(); } wl.release(); } } /** * Handles messages coming in to mHandlerOut * @param msg * @param progressDialog * @param progressThread */ public void onHandleMessage(Message msg, ProgressDialog progressDialog, final ProgressThread progressThread) { int total = msg.getData().getInt(ProgressThread.DATA_TOTAL); int position = msg.getData().getInt(ProgressThread.DATA_POSITION); int type = msg.getData().getInt(ProgressThread.DATA_TYPE); if (msg.what != ProgressThread.MSG_QUIT) { if (total > 0) { progressDialog.setProgress(position); if (position < total) { final ICoverArt cover = (ICoverArt)msg.getData().getSerializable(ProgressThread.DATA_COVER); if (DEBUG) Log.i(TAG, "New download message received for position " + position + ": " + cover.getName()); if (AbstractManager.cacheCover(cover, (INotifiableManager)mInfoManager, mActivity.getApplicationContext())) { mNumCoversDownloaded++; } if (DEBUG) Log.i(TAG, "Cover Downloaded, sending new (empty) message to progress thread."); final Handler handlerIn = progressThread.getHandlerIn(); if (progressThread.isAlive() && handlerIn != null) { handlerIn.sendEmptyMessage(ProgressThread.MSG_NEXT); } else { if (DEBUG) Log.i(TAG, "Thread dead, exiting."); return; } } else { mActivity.dismissDialog(type); progressThread.getHandlerIn().sendEmptyMessage(ProgressThread.MSG_QUIT); Toast toast = Toast.makeText(mActivity, mNumCoversDownloaded + " posters downloaded.", Toast.LENGTH_SHORT); toast.show(); } } else { mActivity.dismissDialog(type); Toast toast = Toast.makeText(mActivity, "No posters downloaded, libary empty?", Toast.LENGTH_LONG); toast.show(); } } else { mActivity.dismissDialog(type); progressThread.getHandlerIn().sendEmptyMessage(ProgressThread.MSG_QUIT); Toast toast = Toast.makeText(mActivity, "Aborted, " + mNumCoversDownloaded + " posters downloaded.", Toast.LENGTH_SHORT); toast.show(); } } public void onActivityPause() { mInfoManager.setController(null); super.onActivityPause(); } public void onActivityResume(Activity activity) { super.onActivityResume(activity); mInfoManager.setController(this); mInfoManager.getSystemInfo(mUpdateVersionHandler, SystemInfo.SYSTEM_BUILD_VERSION, mActivity.getApplicationContext()); } public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals("setting_show_home_music") || key.equals("setting_show_home_movies") || key.equals("setting_show_home_tv") || key.equals("setting_show_home_pictures") || key.equals("setting_show_home_powerdown")) { setupMenuItems(mMenuGrid); } } }