Java tutorial
/* DVR Commander for TiVo allows control of a TiVo Premiere device. Copyright (C) 2011 Anthony Lieuallen (arantius@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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.arantius.tivocommander; import java.io.IOException; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Calendar; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.TimeZone; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; public class Utils { public static boolean DEBUG_LOG = false; public static int LOG_BUFFER_SIZE = 1000; private static final String LOG_TAG = "tivo_commander"; private static final ArrayDeque<String> mLogBuffer = new ArrayDeque<String>(LOG_BUFFER_SIZE); private static final SimpleDateFormat mLogDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); private static final ObjectMapper mMapper = new ObjectMapper(); private static final ObjectWriter mMapperPretty = mMapper.writerWithDefaultPrettyPrinter(); @TargetApi(11) public final static void activateHomeButton(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { ActionBar ab = activity.getActionBar(); ab.setDisplayHomeAsUpEnabled(true); } } private final static Class<? extends Activity> activityForMenuId(int menuId) { switch (menuId) { case android.R.id.home: return NowShowing.class; case R.id.menu_item_about: return About.class; case R.id.menu_item_settings: return Settings.class; case R.id.menu_item_devices: return Discover.class; case R.id.menu_item_help: return Help.class; case R.id.menu_item_remote: return Remote.class; case R.id.menu_item_my_shows: return MyShows.class; case R.id.menu_item_search: return Search.class; case R.id.menu_item_season_pass: return SeasonPass.class; case R.id.menu_item_todo: return ToDo.class; } return null; } @SuppressLint("NewApi") final static void addToMenu(Menu menu, Activity activity, int itemId, int iconId, String title, int showAsAction) { if (Utils.activityForMenuId(itemId) == activity.getClass()) { return; } MenuItem menuitem = menu.add(Menu.NONE, itemId, Menu.NONE, title); menuitem.setIcon(iconId); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { menuitem.setShowAsAction(showAsAction); } } @SuppressLint("InlinedApi") public final static void createFullOptionsMenu(Menu menu, Activity activity) { Utils.activateHomeButton(activity); addToMenu(menu, activity, R.id.menu_item_remote, R.drawable.icon_remote, "Remote", MenuItem.SHOW_AS_ACTION_IF_ROOM); addToMenu(menu, activity, R.id.menu_item_my_shows, R.drawable.icon_tv32, "My Shows", MenuItem.SHOW_AS_ACTION_IF_ROOM); addToMenu(menu, activity, R.id.menu_item_search, R.drawable.icon_search, "Search", MenuItem.SHOW_AS_ACTION_IF_ROOM); addToMenu(menu, activity, R.id.menu_item_todo, R.drawable.icon_todo, "To Do List", MenuItem.SHOW_AS_ACTION_NEVER); addToMenu(menu, activity, R.id.menu_item_season_pass, R.drawable.icon_seasonpass, "Season Pass Manager", MenuItem.SHOW_AS_ACTION_NEVER); addToMenu(menu, activity, R.id.menu_item_devices, R.drawable.icon_devices, "Devices", MenuItem.SHOW_AS_ACTION_NEVER); addToMenu(menu, activity, R.id.menu_item_settings, R.drawable.icon_help, "Settings", MenuItem.SHOW_AS_ACTION_NEVER); addToMenu(menu, activity, R.id.menu_item_help, R.drawable.icon_help, "Help", MenuItem.SHOW_AS_ACTION_NEVER); addToMenu(menu, activity, R.id.menu_item_about, R.drawable.icon_info, "About", MenuItem.SHOW_AS_ACTION_NEVER); } @SuppressLint("InlinedApi") public final static void createShortOptionsMenu(Menu menu, Activity activity) { addToMenu(menu, activity, R.id.menu_item_settings, R.drawable.icon_cog, "Settings", MenuItem.SHOW_AS_ACTION_NEVER); addToMenu(menu, activity, R.id.menu_item_help, R.drawable.icon_help, "Help", MenuItem.SHOW_AS_ACTION_NEVER); addToMenu(menu, activity, R.id.menu_item_about, R.drawable.icon_info, "About", MenuItem.SHOW_AS_ACTION_NEVER); } public static final String findImageUrl(JsonNode node) { String url = null; int biggestSize = 0; int size = 0; for (JsonNode image : node.path("image")) { size = image.path("width").asInt() * image.path("height").asInt(); if (size > biggestSize) { biggestSize = size; url = image.path("imageUrl").asText(); } } return url; } public final static String getVersion(Context context) { String version = " v"; try { PackageManager pm = context.getPackageManager(); version += pm.getPackageInfo(context.getPackageName(), 0).versionName; } catch (NameNotFoundException e) { version = ""; } return version; } public static final String join(String glue, List<String> strings) { Iterator<String> it = strings.iterator(); StringBuilder out = new StringBuilder(); String s; while (it.hasNext()) { s = it.next(); if (s == null || s == "") { continue; } out.append(s); if (it.hasNext()) { out.append(glue); } } return out.toString(); } public static final String join(String glue, String... strings) { return join(glue, Arrays.asList(strings)); } public final static void log(String message) { Log.i(LOG_TAG, message); logAddToBuffer(message, "I"); } public final static synchronized void logAddToBuffer(String message, String level) { mLogBuffer.add(mLogDateFormat.format(Calendar.getInstance().getTime()) + " " + level + " " + message); while (mLogBuffer.size() >= LOG_BUFFER_SIZE) { mLogBuffer.pop(); } } public final static synchronized String logBufferAsString() { StringBuilder sb = new StringBuilder(); try { for (Iterator<String> itr = mLogBuffer.iterator(); itr.hasNext();) { sb.append(itr.next()); sb.append("\n"); } } catch (ConcurrentModificationException e) { sb.append("Concurrent modification!"); } return sb.toString(); } public final static void logDebug(String message) { if (DEBUG_LOG) { Log.d(LOG_TAG, message); logAddToBuffer(message, "D"); } } public final static void logError(String message) { Log.e(LOG_TAG, message); logAddToBuffer(message, "E"); } public final static void logError(String message, Throwable e) { Log.e(LOG_TAG, message, e); logAddToBuffer(message, "E"); logAddToBuffer(Log.getStackTraceString(e), "E"); } public final static void logRpc(Object obj) { String json = Utils.stringifyToPrettyJson(obj); for (String line : json.split(System.getProperty("line.separator"))) { Utils.logDebug(line); } } public final static boolean onOptionsItemSelected(MenuItem item, Activity srcActivity) { return onOptionsItemSelected(item, srcActivity, false); } public final static boolean onOptionsItemSelected(MenuItem item, Activity srcActivity, boolean homeIsBack) { if (android.R.id.home == item.getItemId() && homeIsBack) { srcActivity.finish(); return true; } Class<? extends Activity> targetActivity = Utils.activityForMenuId(item.getItemId()); if (targetActivity == null) { Utils.logError("Unknown menu item ID: " + Integer.toString(item.getItemId())); return false; } Intent intent = new Intent(srcActivity, targetActivity); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); srcActivity.startActivity(intent); return true; } public final static Date parseDateStr(String dateStr) { SimpleDateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd", Locale.US); return parseDateTimeStr(dateParser, dateStr); } private final static Date parseDateTimeStr(SimpleDateFormat dateParser, String dateStr) { TimeZone tz = TimeZone.getTimeZone("UTC"); dateParser.setTimeZone(tz); ParsePosition pp = new ParsePosition(0); return dateParser.parse(dateStr, pp); } public final static Date parseDateTimeStr(String dateStr) { SimpleDateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); return parseDateTimeStr(dateParser, dateStr); } public final static JsonNode parseJson(String json) { try { return mMapper.readValue(json, JsonNode.class); } catch (JsonMappingException e) { Log.e(LOG_TAG, "parseJson failure", e); } catch (JsonParseException e) { Log.e(LOG_TAG, "parseJson failure", e); } catch (IOException e) { Log.e(LOG_TAG, "parseJson failure", e); } logError("When parsing:\n" + json); return null; } public final static String stringifyToJson(Object obj) { return stringifyToJson(obj, false); } private final static String stringifyToJson(Object obj, boolean pretty) { try { if (pretty) { return mMapperPretty.writeValueAsString(obj); } else { return mMapper.writeValueAsString(obj); } } catch (JsonGenerationException e) { Log.e(LOG_TAG, "stringifyToJson failure", e); } catch (JsonMappingException e) { Log.e(LOG_TAG, "stringifyToJson failure", e); } catch (IOException e) { Log.e(LOG_TAG, "stringifyToJson failure", e); } return null; } public final static String stringifyToPrettyJson(Object obj) { return stringifyToJson(obj, true); } public final static String stripQuotes(String s) { if (s.length() <= 1) return s; if ('"' == s.charAt(0) && '"' == s.charAt(s.length() - 1)) { return s.substring(1, s.length() - 1); } return s; } public final static SubscriptionType subscriptionTypeForRecording(JsonNode recording) { if ("inProgress".equals(recording.path("state").asText())) { return SubscriptionType.RECORDING; } String subType = recording.path("subscriptionIdentifier").path(0).path("subscriptionType").asText(); if ("seasonPass".equals(subType)) { return SubscriptionType.SEASON_PASS; } else if ("singleOffer".equals(subType)) { return SubscriptionType.SINGLE_OFFER; } else if ("wishList".equals(subType)) { return SubscriptionType.WISHLIST; } else { logError("Unsupported subscriptionType string: " + subType); return null; } } public final static void toast(Activity activity, int messageId, int length) { Context ctx = activity.getBaseContext(); Toast.makeText(ctx, messageId, length).show(); } public final static void toast(Activity activity, String message, int length) { Context ctx = activity.getBaseContext(); Toast.makeText(ctx, message, length).show(); } public final static String ucFirst(String s) { if (s == null) { return null; } return s.substring(0, 1).toUpperCase(Locale.US) + s.substring(1); } }