Java tutorial
/* MythDroid: Android MythTV Remote Copyright (C) 2009-2010 foobum@gmail.com 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package org.mythdroid.remote; import java.io.IOException; import org.mythdroid.Enums.Extras; import org.mythdroid.Globals; import org.mythdroid.R; import org.mythdroid.Enums.Key; import org.mythdroid.frontend.FrontendLocation; import org.mythdroid.mdd.MDDManager; import org.mythdroid.mdd.MDDMenuListener; import org.mythdroid.resource.Messages; import org.mythdroid.util.ErrUtil; import android.R.drawable; import android.content.ComponentName; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.support.v4.view.MenuItemCompat; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; /** Remote for menu / guide navigation */ public class NavRemote extends Remote { final private static int MENU_GESTURE = 0, MENU_BUTTON = 1, MENU_MENU = 2; final private Handler handler = new Handler(); private FrontendLocation lastLoc = null; private MDDManager mddMgr = null; private TextView locView = null, itemView = null; private String locS = null, itemS = null; private boolean gesture = false, jumpGuide = false, calledByRemote = false, calledByTVRemote = false, calledByMusicRemote = false; // Update the location displays, posted from updateLoc() with a delay private Runnable updateLocViews = new Runnable() { @Override public void run() { if (locS != null && locView != null) locView.setText(locS); if (itemS != null && itemView != null) itemView.setText(itemS); } }; final private MDDMenuListener mddListener = new MDDMenuListener() { @Override public void onMenuItem(final String menu, final String item) { handler.post(new Runnable() { @Override public void run() { if (locView == null || itemView == null) return; // Cancel any pending updates from updateLoc() handler.removeCallbacks(updateLocViews); if (menu != null && menu.length() > 0) { locView.setText(menu); locS = menu; } itemView.setText(item); itemS = item; } }); } }; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); gesture = PreferenceManager.getDefaultSharedPreferences(this).getString("tvDefaultStyle", "") //$NON-NLS-1$ //$NON-NLS-2$ .equals(Messages.getString("NavRemote.0")); // Gesture //$NON-NLS-1$ setupViews(gesture); listenToGestures(gesture); if (getIntent().hasExtra(Extras.GUIDE.toString())) jumpGuide = true; String calledBy = null; ComponentName caller = getCallingActivity(); if (caller != null) calledBy = caller.getShortClassName(); if (calledBy != null) { if (calledBy.endsWith(".TVRemote")) //$NON-NLS-1$ calledByTVRemote = true; else if (calledBy.endsWith(".MusicRemote")) //$NON-NLS-1$ calledByMusicRemote = true; } calledByRemote = calledByTVRemote || calledByMusicRemote; } @Override public void onResume() { super.onResume(); try { feMgr = Globals.getFrontend(this); } catch (IOException e) { ErrUtil.err(this, e); finish(); return; } // Preserve the menu item string through updateLoc(), if we have one String itemSTmp = itemS; try { if (jumpGuide) feMgr.jumpTo("guidegrid"); //$NON-NLS-1$ updateLoc(); } catch (IOException e) { ErrUtil.err(this, e); } catch (IllegalArgumentException e) { ErrUtil.reportErr(this, e); } if (!calledByRemote) { try { mddMgr = new MDDManager(feMgr.addr); } catch (IOException e) { mddMgr = null; } if (mddMgr != null) { mddMgr.setMenuListener(mddListener); } } // Restore the menu item string that got wiped by updateLoc() itemS = itemSTmp; updateLocViews.run(); } private void cleanup() { if (feMgr != null && !calledByRemote) feMgr.disconnect(); feMgr = null; if (mddMgr != null) mddMgr.shutdown(); mddMgr = null; } @Override public void onPause() { super.onPause(); cleanup(); } @Override public void onDestroy() { super.onDestroy(); cleanup(); } @Override public void onConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); setupViews(gesture); try { if (feMgr == null) onResume(); updateLoc(); } catch (IOException e) { ErrUtil.err(this, e); finish(); } catch (IllegalArgumentException e) { ErrUtil.reportErr(this, e); finish(); } } @Override public void onClick(View v) { if (feMgr != null) try { switch (v.getId()) { case R.id.back: feMgr.sendKey(Key.ESCAPE); break; case R.id.enter: feMgr.sendKey(Key.ENTER); break; case R.id.up: feMgr.sendKey(Key.UP); break; case R.id.down: feMgr.sendKey(Key.DOWN); break; case R.id.left: feMgr.sendKey(Key.LEFT); break; case R.id.right: feMgr.sendKey(Key.RIGHT); break; } } catch (IOException e) { ErrUtil.err(this, e); } catch (IllegalArgumentException e) { ErrUtil.reportErr(this, e); } super.onClick(v); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuItemCompat.setShowAsAction( menu.add(Menu.NONE, MENU_MENU, Menu.NONE, R.string.menu).setIcon(drawable.ic_menu_more), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); menu.add(Menu.NONE, MENU_BUTTON, Menu.NONE, R.string.btnIface).setIcon(android.R.drawable.ic_menu_add); menu.add(Menu.NONE, MENU_GESTURE, Menu.NONE, R.string.gestIface).setIcon(R.drawable.ic_menu_finger); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (gesture) { menu.findItem(MENU_BUTTON).setVisible(true); menu.findItem(MENU_GESTURE).setVisible(false); } else { menu.findItem(MENU_BUTTON).setVisible(false); menu.findItem(MENU_GESTURE).setVisible(true); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_MENU: try { if (feMgr != null) feMgr.sendKey(Key.MENU); } catch (IOException e) { ErrUtil.err(this, e); } return true; case MENU_GESTURE: gesture = true; break; case MENU_BUTTON: gesture = false; break; default: return super.onOptionsItemSelected(item); } setupViews(gesture); listenToGestures(gesture); return true; } @Override public boolean onKeyDown(int code, KeyEvent event) { if (feMgr != null && code == KeyEvent.KEYCODE_BACK && calledByTVRemote) { try { feMgr.sendKey(Key.ESCAPE); } catch (IOException e) { ErrUtil.err(this, e); } return true; } return super.onKeyDown(code, event); } @Override public void onActivityResult(int reqCode, int resCode, Intent data) { if (resCode == REMOTE_RESULT_FINISH) finish(); } @Override protected void onAction() { try { updateLoc(); } catch (IOException e) { ErrUtil.err(this, e); } catch (IllegalArgumentException e) { ErrUtil.reportErr(this, e); } } /** * Setup the interactive views * @param gesture true for 'gesture' layout, false for 'button' */ private void setupViews(boolean gesture) { setContentView(gesture ? R.layout.nav_gesture_remote : R.layout.nav_remote); locView = (TextView) findViewById(R.id.loc); itemView = (TextView) findViewById(R.id.item); if (locS != null) locView.setText(locS); if (itemS != null) itemView.setText(itemS); if (feMgr != null) try { updateLoc(); } catch (IOException e) { ErrUtil.err(this, e); } catch (IllegalArgumentException e) { ErrUtil.reportErr(this, e); } if (gesture) { findViewById(R.id.back).setOnClickListener(this); return; } final View view = findViewById(R.id.nav_remote); for (final View v : view.getTouchables()) { v.setOnClickListener(this); v.setFocusable(false); } } /** * Update the frontend location display, start another remote if appropriate */ private void updateLoc() throws IOException, IllegalArgumentException { if (calledByMusicRemote || feMgr == null || locView == null || itemView == null) return; FrontendLocation newLoc = feMgr.getLoc(); locS = newLoc.niceLocation; itemS = ""; //$NON-NLS-1$ if (mddMgr == null) { locView.setText(locS); itemView.setText(itemS); } else if (locS != null && !locS.equals("Unknown")) { //$NON-NLS-1$ /* * The location displays will be updated by MDD at some point but * if it happens to be >= a few ms after we set it from the * FrontendLocation we get an ugly 'flicker'. So we delay the * update from FrontendLocation by 50ms and if an update from * MDD arrives in the meantime, cancel the FrontendLocation based * update. */ handler.postDelayed(updateLocViews, 50); } if (newLoc.video) { if (calledByRemote) finish(); else { if (lastLoc != null) Globals.setLastLocation(lastLoc); final Intent intent = new Intent().setClass(this, TVRemote.class) .putExtra(Extras.DONTJUMP.toString(), true); if (newLoc.livetv) intent.putExtra(Extras.LIVETV.toString(), true); startActivityForResult(intent, 0); } } else if (newLoc.music) { if (!calledByRemote && lastLoc != null) Globals.setLastLocation(lastLoc); startActivity( new Intent().setClass(this, MusicRemote.class).putExtra(Extras.DONTJUMP.toString(), true)); } else lastLoc = newLoc; } }