/* * TV-Browser for Android * Copyright (C) 2013 René Mach (rene@tvbrowser.org) * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to use, copy, modify or merge the Software, * furthermore to publish and distribute the Software free of charge without modifications and to * permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package org.tvbrowser.utils; import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.tvbrowser.content.TvBrowserContentProvider; import org.tvbrowser.devplugin.Plugin; import org.tvbrowser.devplugin.PluginHandler; import org.tvbrowser.devplugin.PluginMenu; import org.tvbrowser.devplugin.PluginServiceConnection; import org.tvbrowser.devplugin.Program; import org.tvbrowser.filter.CategoryFilter; import org.tvbrowser.filter.ChannelFilter; import org.tvbrowser.filter.KeywordFilter; import org.tvbrowser.settings.SettingConstants; import org.tvbrowser.tvbrowser.ActivityFavoriteEdit; import org.tvbrowser.tvbrowser.ActivityTvBrowserSearchResults; import org.tvbrowser.tvbrowser.DontWantToSeeExclusion; import org.tvbrowser.tvbrowser.Favorite; import org.tvbrowser.tvbrowser.InfoActivity; import org.tvbrowser.tvbrowser.NamedFields; import org.tvbrowser.tvbrowser.R; import org.tvbrowser.tvbrowser.ServiceUpdateReminders; import org.tvbrowser.tvbrowser.TvBrowser; import org.tvbrowser.view.SwipeScrollView; import org.tvbrowser.widgets.ImportantProgramsListWidget; import org.tvbrowser.widgets.RunningProgramsListWidget; import org.tvbrowser.widgets.WidgetToggleReminderState; import android.annotation.SuppressLint; import android.app.Activity; import android.app.NotificationManager; import android.app.ProgressDialog; import android.app.SearchManager; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentProviderOperation; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.OperationApplicationException; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.GradientDrawable.Orientation; import android.graphics.drawable.LayerDrawable; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.RemoteException; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.Html; import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.TextWatcher; import android.text.format.DateFormat; import android.text.method.LinkMovementMethod; import android.text.style.BackgroundColorSpan; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; import android.text.style.LeadingMarginSpan; import android.text.style.StyleSpan; import android.text.style.UnderlineSpan; import android.util.Log; import android.util.TypedValue; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowManager.BadTokenException; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.Spinner; import android.widget.TextView; public class UiUtils { public static final SimpleDateFormat LONG_DAY_FORMAT = new SimpleDateFormat("EEEE", Locale.getDefault()); private static final HashMap<String, Integer> VALUE_MAP; public static final int EXPIRED_COLOR_KEY = 0; public static final int MARKED_COLOR_KEY = 1; public static final int MARKED_FAVORITE_COLOR_KEY = 2; public static final int MARKED_REMINDER_COLOR_KEY = 3; public static final int MARKED_SYNC_COLOR_KEY = 4; public static final int ON_AIR_BACKGROUND_KEY = 5; public static final int ON_AIR_PROGRESS_KEY = 6; public static final int RUNNING_TIME_SELECTION_KEY = 7; public static final int I_DONT_WANT_TO_SEE_HIGHLIGHT_COLOR_KEY = 8; static { VALUE_MAP = new HashMap<String, Integer>(); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_ACTORS, R.id.detail_actors); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_REGIE, R.id.detail_regie); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_CUSTOM_INFO, R.id.detail_custom); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_AGE_LIMIT, R.id.detail_age_limit); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_NETTO_PLAY_TIME, R.id.detail_netto_playtime); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_VPS, R.id.detail_vps); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_SCRIPT, R.id.detail_script); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_REPETITION_FROM, R.id.detail_repetition_from); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_MUSIC, R.id.detail_music); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_MODERATION, R.id.detail_moderation); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_REPETITION_ON, R.id.detail_repetition_on); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_SEASON_NUMBER, R.id.detail_season); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_PRODUCER, R.id.detail_producer); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_CAMERA, R.id.detail_camera); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_CUT, R.id.detail_cut); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_OTHER_PERSONS, R.id.detail_other_persons); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_PRODUCTION_FIRM, R.id.detail_production_firm); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_AGE_LIMIT_STRING, R.id.detail_age_limit_string); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_LAST_PRODUCTION_YEAR, R.id.detail_last_production_year); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_ADDITIONAL_INFO, R.id.detail_additional_info); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_SERIES, R.id.detail_series); VALUE_MAP.put(TvBrowserContentProvider.DATA_KEY_RATING, R.id.detail_rating); } public static void showProgramInfo(final Context context, long id, View parent, Handler handler) { showProgramInfo(context, id, null, parent, handler); } private static boolean mShowingProgramInfo = false; public static synchronized void showProgramInfo(final Context context, final long id, final Activity finish, final View parent, final Handler handler) { if(!mShowingProgramInfo && IOUtils.isDatabaseAccessible(context)) { mShowingProgramInfo = true; handler.post(new Runnable() { @Override public void run() { AsyncTask<Void, Void, Boolean> createInfoTask = new AsyncTask<Void, Void, Boolean>() { private View mLayout; private ProgressDialog mProgress; private Handler mHandler; private Runnable mShowWaiting; private boolean mShowWaitingDialog; private boolean mHasSpannableActors; private long mPreviousId; private long mNextId; @Override protected void onPreExecute() { mPreviousId = -1; mNextId = -1; mShowWaitingDialog = true; mHandler = new Handler(); mShowWaiting = new Runnable() { @Override public void run() { mProgress = new ProgressDialog(context); mProgress.setMessage(context.getString(R.string.waiting_detail_show)); if(mShowWaitingDialog) { try { mProgress.show(); }catch(BadTokenException e) {} } } }; int layout = PrefUtils.getBooleanValue(R.string.PREF_DETAIL_ALLOW_SWIPE, R.bool.pref_detail_allow_swipe) ? R.layout.dialog_detail_swipe : R.layout.dialog_detail; mLayout = ((LayoutInflater)context.getSystemService( Context.LAYOUT_INFLATER_SERVICE)).inflate(layout, parent instanceof ViewGroup ? (ViewGroup)parent : null, false); mHandler.postDelayed(mShowWaiting, 700); } @Override protected Boolean doInBackground(Void... params) { Boolean result = Boolean.FALSE; final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); final Cursor c = context.getContentResolver().query(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_DATA, id), null, null, null, TvBrowserContentProvider.KEY_ID + " ASC LIMIT 1"); try { if(IOUtils.prepareAccessFirst(c)) { mHasSpannableActors = false; result = Boolean.TRUE; float textScale = Float.parseFloat(PrefUtils.getStringValue(R.string.DETAIL_TEXT_SCALE, R.string.detail_text_scale_default)); final long startTime = c.getLong(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_STARTTIME)); final long endTime = c.getLong(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_ENDTIME)); final ImageButton handleReminder = (ImageButton)mLayout.findViewById(R.id.detail_handle_reminder); if(startTime <= System.currentTimeMillis()) { handleReminder.setVisibility(View.GONE); } else { final AtomicBoolean userRemind = new AtomicBoolean(c.getInt(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER)) == 1); final AtomicBoolean favoriteRemind = new AtomicBoolean(c.getInt(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER)) == 1); if(userRemind.get() || favoriteRemind.get()) { handleReminder.setImageResource(R.drawable.ic_action_remove_alarm); CompatUtils.setBackground(handleReminder, ContextCompat.getDrawable(context, R.drawable.button_selector_remove)); } else { handleReminder.setImageResource(R.drawable.ic_action_add_alarm); CompatUtils.setBackground(handleReminder, ContextCompat.getDrawable(context, R.drawable.button_selector_add)); } handleReminder.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ContentValues values = new ContentValues(); boolean add = true; if(userRemind.get() || favoriteRemind.get()) { add = false; values.put(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER, false); values.put(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER, false); } else { values.put(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER, true); } if(context.getContentResolver().update(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_DATA, id), values, null, null) == 1) { if(add) { userRemind.set(true); ServiceUpdateReminders.startReminderUpdate(context); ProgramUtils.addReminderId(context, id); mHandler.post(new Runnable() { @Override public void run() { handleReminder.setImageResource(R.drawable.ic_action_remove_alarm); CompatUtils.setBackground(handleReminder, ContextCompat.getDrawable(context, R.drawable.button_selector_remove)); handleReminder.invalidate(); } }); } else { userRemind.set(false); favoriteRemind.set(false); IOUtils.removeReminder(context, id); ProgramUtils.removeReminderId(context, id); ServiceUpdateReminders.startReminderUpdate(context); mHandler.post(new Runnable() { @Override public void run() { handleReminder.setImageResource(R.drawable.ic_action_add_alarm); CompatUtils.setBackground(handleReminder, ContextCompat.getDrawable(context, R.drawable.button_selector_add)); handleReminder.invalidate(); } }); } Intent intent = new Intent(SettingConstants.MARKINGS_CHANGED); intent.putExtra(SettingConstants.EXTRA_MARKINGS_ID, id); LocalBroadcastManager.getInstance(context).sendBroadcastSync(intent); UiUtils.updateImportantProgramsWidget(context); UiUtils.updateRunningProgramsWidget(context); } } }); } final int favoriteMatchHighlightColor = PrefUtils.getIntValue(R.string.PREF_DETAIL_HIGHLIGHT_COLOR, ContextCompat.getColor(context, R.color.pref_detail_highlight_color_default)); final Favorite[] markedFromFavorites = Favorite.getFavoritesForUniqueId(context, id); final ArrayList<FavoriteTypePattern> patternList = new ArrayList<FavoriteTypePattern>(); final BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(favoriteMatchHighlightColor); if(markedFromFavorites.length > 0) { for(Favorite favorite : markedFromFavorites) { if(favorite.getType() != Favorite.RESTRICTION_RULES_TYPE) { patternList.add(new FavoriteTypePattern(favorite.getType(), Pattern.compile(".*?("+favorite.getSearchValue().replace("%", ".*?")+").*?", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE))); } } } TextView date = (TextView)mLayout.findViewById(R.id.detail_date_channel); TextView title = (TextView)mLayout.findViewById(R.id.detail_title); TextView genre = (TextView)mLayout.findViewById(R.id.detail_genre); TextView info = (TextView)mLayout.findViewById(R.id.detail_info); TextView episode = (TextView)mLayout.findViewById(R.id.detail_episode_title); TextView shortDescription = (TextView)mLayout.findViewById(R.id.detail_short_description); TextView description = (TextView)mLayout.findViewById(R.id.detail_description); TextView link = (TextView)mLayout.findViewById(R.id.detail_link); TextView pictureDescription = (TextView)mLayout.findViewById(R.id.detail_picture_description); TextView pictureCopyright = (TextView)mLayout.findViewById(R.id.detail_picture_copyright); date.setTextSize(TypedValue.COMPLEX_UNIT_PX, date.getTextSize() * textScale); title.setTextSize(TypedValue.COMPLEX_UNIT_PX, date.getTextSize() * textScale); genre.setTextSize(TypedValue.COMPLEX_UNIT_PX, genre.getTextSize() * textScale); info.setTextSize(TypedValue.COMPLEX_UNIT_PX, info.getTextSize() * textScale); episode.setTextSize(TypedValue.COMPLEX_UNIT_PX, episode.getTextSize() * textScale); shortDescription.setTextSize(TypedValue.COMPLEX_UNIT_PX, shortDescription.getTextSize() * textScale); description.setTextSize(TypedValue.COMPLEX_UNIT_PX, description.getTextSize() * textScale); link.setTextSize(TypedValue.COMPLEX_UNIT_PX, link.getTextSize() * textScale); pictureDescription.setTextSize(TypedValue.COMPLEX_UNIT_PX, pictureDescription.getTextSize() * textScale); pictureCopyright.setTextSize(TypedValue.COMPLEX_UNIT_PX, pictureCopyright.getTextSize() * textScale); final Resources resources = context.getResources(); if(!SettingConstants.IS_DARK_THEME && !(finish instanceof InfoActivity)) { date.setTextColor(ContextCompat.getColor(context, R.color.detail_date_channel_color_light)); } final int channelID = c.getInt(c.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_CHANNEL_ID)); Cursor channel = null; try { channel = context.getContentResolver().query(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_CHANNELS, channelID), new String[] {TvBrowserContentProvider.CHANNEL_KEY_NAME,TvBrowserContentProvider.CHANNEL_KEY_LOGO,TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER}, null, null, null); if(IOUtils.prepareAccessFirst(channel)) { final StringBuilder channelName = new StringBuilder(); if(PrefUtils.getBooleanValue(R.string.SHOW_SORT_NUMBER_IN_DETAILS, R.bool.show_sort_number_in_details_default)) { final int columnIndex = channel.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER); if(!channel.isNull(columnIndex)) { channelName.append(channel.getString(columnIndex)).append(". "); } } channelName.append(channel.getString(channel.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_NAME))); final Date start = new Date(startTime); final java.text.DateFormat dateFormat = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT, Locale.getDefault()); final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(context); date.setText(resources.getString(R.string.detail_date_format, start, dateFormat.format(start), timeFormat.format(start), timeFormat.format(new Date(endTime)), channelName)); Bitmap logo = UiUtils.createBitmapFromByteArray(channel.getBlob(channel.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_LOGO))); if(logo != null) { float scale = resources.getDisplayMetrics().density; int width = (int)(logo.getWidth() * scale); int height = (int)(logo.getHeight() * scale); BitmapDrawable l = new BitmapDrawable(resources, logo); int color = PrefUtils.getIntValue(R.string.PREF_LOGO_BACKGROUND_COLOR, ContextCompat.getColor(context, R.color.pref_logo_background_color_default)); GradientDrawable background = new GradientDrawable(Orientation.BOTTOM_TOP,new int[] {color,color}); int add = 2; if(PrefUtils.getBooleanValue(R.string.PREF_LOGO_BORDER, R.bool.pref_logo_border_default)) { add = 3; background.setStroke(1, PrefUtils.getIntValue(R.string.PREF_LOGO_BORDER_COLOR, ContextCompat.getColor(context, R.color.pref_logo_border_color_default))); } background.setBounds(0, 0, width + add, height + add); LayerDrawable logoDrawable = new LayerDrawable(new Drawable[] {background,l}); logoDrawable.setBounds(0, 0, width + add, height + add); l.setBounds(2, 2, width, height); date.setCompoundDrawables(logoDrawable, null, null, null); } } } finally { IOUtils.close(channel); } String year = ""; int yearInt = -1; if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_ORIGIN))) { year = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_ORIGIN)); } if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_YEAR))) { if(year.length() > 0) { year += " "; } yearInt = c.getInt(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_YEAR)); year += yearInt; } if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_LAST_PRODUCTION_YEAR))) { int test = c.getInt(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_LAST_PRODUCTION_YEAR)); if(yearInt < test) { if(year.length() > 0) { if(yearInt != -1) { year += "-"; } else { year += " "; } } year += test; } } String originalTitle = null; String titleTest = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_TITLE)); if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_TITLE_ORIGINAL))) { originalTitle = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_TITLE_ORIGINAL)); } if(originalTitle == null || originalTitle.equals(titleTest)) { checkAndAddHiglightingForFavorites(title, titleTest, patternList, true, backgroundColorSpan); //title.setText(titleTest); } else { checkAndAddHiglightingForFavorites(title, titleTest + "/" + originalTitle, patternList, true, backgroundColorSpan); //title.setText(titleTest + "/" + originalTitle); } if(!PrefUtils.getBooleanValue(R.string.SHOW_PICTURE_IN_DETAILS, R.bool.show_picture_in_details_default) || c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_PICTURE)) || c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_PICTURE_COPYRIGHT))) { pictureCopyright.setVisibility(View.GONE); pictureDescription.setVisibility(View.GONE); } else { if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_PICTURE_DESCRIPTION))) { pictureDescription.setText(c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_PICTURE_DESCRIPTION))); } pictureCopyright.setText(c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_PICTURE_COPYRIGHT))); Bitmap image = UiUtils.createBitmapFromByteArray(c.getBlob(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_PICTURE))); if(image != null) { BitmapDrawable b = new BitmapDrawable(resources, image); float zoom = Float.parseFloat(PrefUtils.getStringValue(R.string.DETAIL_PICTURE_ZOOM, R.string.detail_picture_zoom_default)) * resources.getDisplayMetrics().density; b.setBounds(0, 0, (int)(image.getWidth() * zoom), (int)(image.getHeight() * zoom)); if(PrefUtils.getStringValue(R.string.DETAIL_PICTURE_DESCRIPTION_POSITION, R.string.detail_picture_description_position_default).equals("0")) { pictureDescription.setCompoundDrawables(b, null, null, null); } else { pictureDescription.setCompoundDrawables(null, b, null, null); } } } if(PrefUtils.getBooleanValue(R.string.SHOW_GENRE_IN_DETAILS, R.bool.show_genre_in_details_default) && !c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_GENRE))) { checkAndAddHiglightingForFavorites(genre, c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_GENRE)) + (year.length() > 0 ? " - " + year : ""), patternList, false, backgroundColorSpan); // genre.setText(); } else if(year.length() > 0) { checkAndAddHiglightingForFavorites(genre, year, patternList, false, backgroundColorSpan); //genre.setText(year); } else { genre.setVisibility(View.GONE); } Spannable infoValue = IOUtils.getInfoString(c.getInt(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_CATEGORIES)), resources); if(PrefUtils.getBooleanValue(R.string.SHOW_INFO_IN_DETAILS, R.bool.show_info_in_details_default) && infoValue != null) { info.setText(infoValue); } else { info.setVisibility(View.GONE); } String number = ""; if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_NUMBER))) { if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_COUNT))) { number = IOUtils.decodeSingleFieldValueToMultipleEpisodeString(c.getInt(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_NUMBER))) + "/" + c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_COUNT)) + " "; } else { number = IOUtils.decodeSingleFieldValueToMultipleEpisodeString(c.getInt(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_NUMBER))) + " "; } } String originalEpisode = null; if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE_ORIGINAL))) { originalEpisode = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE_ORIGINAL)); } if(PrefUtils.getBooleanValue(R.string.SHOW_EPISODE_IN_DETAILS, R.bool.show_episode_in_details_default) && !c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE))) { String episodeTest = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE)); if(originalEpisode == null || episodeTest.equals(originalEpisode)) { checkAndAddHiglightingForFavorites(episode, number + episodeTest, patternList, false, backgroundColorSpan); //episode.setText(number + episodeTest); } else { checkAndAddHiglightingForFavorites(episode, number + episodeTest + "/" + originalEpisode, patternList, false, backgroundColorSpan); //episode.setText(number + episodeTest + "/" + originalEpisode); } } else if(PrefUtils.getBooleanValue(R.string.SHOW_EPISODE_IN_DETAILS, R.bool.show_episode_in_details_default) && number.trim().length() > 0) { checkAndAddHiglightingForFavorites(episode, number, patternList, false, backgroundColorSpan); //episode.setText(number); } else { episode.setVisibility(View.GONE); } String shortDescriptionValue = null; String descriptionValue = null; boolean showShortDescription = true; boolean showEpgPaidInfo = PrefUtils.getBooleanValue(R.string.PREF_EPGPAID_DESCRIPTION_MISSING_INFO, R.bool.pref_epgpaid_description_missing_default); if(showEpgPaidInfo) { final Set<String> channelIds = PrefUtils.getStringSetValue(R.string.PREF_EPGPAID_DATABASE_CHANNEL_IDS, new HashSet<String>()); showEpgPaidInfo = channelIds.contains(String.valueOf(channelID)); if(showEpgPaidInfo) { final File epgPaidDir = new File(IOUtils.getDownloadDirectory(context),"epgPaidData"); if(epgPaidDir.isDirectory()) { final File[] test = epgPaidDir.listFiles(); showEpgPaidInfo = (test.length <= 2); } } } if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_SHORT_DESCRIPTION))) { shortDescriptionValue = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_SHORT_DESCRIPTION)).trim(); } if(!c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_DESCRIPTION))) { descriptionValue = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_DESCRIPTION)); if(shortDescriptionValue != null) { String test = shortDescriptionValue; if(test.endsWith("...")) { test = test.substring(0,test.length()-3); } else if(test.endsWith("\u2026")) { test = test.substring(0,test.length()-1); } test = test.replaceAll("\\s+", " ").trim(); showShortDescription = !descriptionValue.replaceAll("\\s+", " ").trim().startsWith(test); if(showShortDescription) { shortDescriptionValue = shortDescriptionValue.trim() + "\n"; } String[] paragraphs = descriptionValue.split("\n+"); if(paragraphs.length > 1) { String para0 = paragraphs[0].replaceAll("\\s+", " ").trim(); String para1 = paragraphs[1].replaceAll("\\s+", " ").trim(); if(para1.startsWith(para0) || para0.startsWith(para1)) { descriptionValue = descriptionValue.substring(paragraphs[0].length()+1).trim(); } else { int testIndex = para0.indexOf("."); if(testIndex > 0) { para0 = para0.substring(testIndex+1).trim(); if(!para0.isEmpty() && para1.endsWith(para0)) { descriptionValue = descriptionValue.substring(paragraphs[0].length()+1).trim(); } } } } } } if(showEpgPaidInfo && shortDescriptionValue == null && descriptionValue == null) { descriptionValue = context.getString(R.string.detail_missing_description_text); } if(shortDescriptionValue == null || !showShortDescription) { shortDescription.setVisibility(View.GONE); } else { checkAndAddHiglightingForFavorites(shortDescription, shortDescriptionValue.replaceAll("\\s{4,}", "\n\n").replaceAll("\\r", "").replaceAll("\\n{2,}", "\n\n"), patternList, false, backgroundColorSpan); } if(descriptionValue != null) { checkAndAddHiglightingForFavorites(description, descriptionValue.replaceAll("\\s{4,}", "\n\n").replaceAll("\\r", "").replaceAll("\\n{2,}", "\n\n"), patternList, false, backgroundColorSpan); } else { description.setVisibility(View.GONE); } if(PrefUtils.getBooleanValue(R.string.SHOW_LINK_IN_DETAILS, R.bool.show_link_in_details_default) && !c.isNull(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_WEBSITE_LINK))) { String linkText = c.getString(c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_WEBSITE_LINK)); link.setText(linkText); link.setMovementMethod(LinkMovementMethod.getInstance()); } else { link.setVisibility(View.GONE); } Set<String> keys = VALUE_MAP.keySet(); for(String key : keys) { boolean enabled = pref.getBoolean("details_" + key, true); TextView textView = (TextView)mLayout.findViewById(VALUE_MAP.get(key)); textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textView.getTextSize() * textScale); if(textView != null && enabled && !c.isNull(c.getColumnIndex(key))) { String text = c.getString(c.getColumnIndex(key)); if(key.equals(TvBrowserContentProvider.DATA_KEY_ADDITIONAL_INFO) && descriptionValue != null && text.equals(descriptionValue)) { text = ""; } if(text.trim().length() > 0) { try { String name = resources.getString((Integer)R.string.class.getField(key).get(null)); boolean endWith = false; if(name.endsWith(":")) { endWith = true; } if(key.equals(TvBrowserContentProvider.DATA_KEY_ACTORS)){ String separator = null; if(text.contains("\n")) { separator = "\n+"; } else if(text.contains(",")) { separator = ",\\s*"; } SpannableStringBuilder actors = new SpannableStringBuilder(name); actors.setSpan(new StyleSpan(Typeface.BOLD), 0, name.length()-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); actors.setSpan(new UnderlineSpan(), 0, name.length()-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); int screenSizeHalf = CompatUtils.getScreenSize(context).x/2; if(separator != null) { String[] lines = text.split(separator); float maxLength = 0; TextPaint actorPaint = textView.getPaint(); for(int i = 0; i < lines.length; i++) { if(lines[i].contains("\t\t-\t\t")) { separator = "\t\t-\t\t"; } else if(lines[i].contains("(")) { separator = "\\s*\\("; } else { separator = null; } if(separator != null) { String[] parts = lines[i].split(separator); if(parts.length > 1) { float test = actorPaint.measureText(parts[0]); if(test > screenSizeHalf) { String secondSeparator = null; if(parts[0].contains("(")) { secondSeparator = "("; } else if(parts[0].contains(" ")) { secondSeparator = " "; } else if(parts[0].contains("-")) { secondSeparator = "-"; } if(secondSeparator != null) { int index = parts[0].indexOf(secondSeparator); String firstPart = parts[0].substring(0, index).trim(); String secondPart = parts[0].substring(index).trim(); float firstLength = actorPaint.measureText(firstPart); float secondLength = actorPaint.measureText(secondPart); if(secondLength > screenSizeHalf) { int whiteSpaceIndex = secondPart.indexOf(" "); if(whiteSpaceIndex >= 0) { String thirdPart = secondPart.substring(0, whiteSpaceIndex).trim(); String fifthPart = secondPart.substring(whiteSpaceIndex).trim(); secondLength = Math.max(actorPaint.measureText(thirdPart), actorPaint.measureText(fifthPart)); if(secondLength < screenSizeHalf) { secondPart = thirdPart + "\n" + fifthPart; } } } test = Math.max(firstLength, secondLength); if(test < screenSizeHalf) { lines[i] = lines[i].replace(parts[0], firstPart + "\n" + secondPart); } } } maxLength = Math.max(maxLength,test); } } } if(maxLength > 0 && maxLength <= screenSizeHalf) { for(int i = 0; i < lines.length; i++) { String[] parts = lines[i].split(separator); if(i > 0) { actors.append("\n"); } if(parts.length > 1) { if(parts[1].trim().endsWith(")") || parts[1].trim().endsWith(",")) { parts[1] = parts[1].substring(0,parts[1].length()-1); } if(parts[0].contains("\n")) { if(parts[0].indexOf("\n") != parts[0].lastIndexOf("\n")) { parts[1] += "\n\n"; } else { parts[1] += "\n"; } } actors.append(parts[1]); actors.setSpan(new ActorColumnSpan(parts[0],(int)maxLength+10, patternList, favoriteMatchHighlightColor), actors.length()-parts[1].length(), actors.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); mHasSpannableActors = true; } else { actors.append(lines[i]); } } } else { actors.append(text); } } else { actors.append(text); } checkAndAddHiglightingForFavorites(textView, actors, patternList, false, backgroundColorSpan); //textView.setText(actors); } else { if(key.equals(TvBrowserContentProvider.DATA_KEY_VPS)) { Calendar temp = Calendar.getInstance(); temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(text) / 60); temp.set(Calendar.MINUTE, Integer.parseInt(text) % 60); text = DateFormat.getTimeFormat(context).format(temp.getTime()); } text = text.replace("\n", "<br>"); name = "<b><u>" + name.replace("\n", "<br>") + "</u></b>" + (endWith ? " " : ""); //SpannableStringBuilder textSpan = new SpannableStringBuilder(Html.fromHtml(name + text)); checkAndAddHiglightingForFavorites(textView, Html.fromHtml(name + text), patternList, false, backgroundColorSpan); //textView.setText(textSpan); } } catch (Exception e) { textView.setVisibility(View.GONE); } } else { textView.setVisibility(View.GONE); } } else if(textView != null) { textView.setVisibility(View.GONE); } } final String[] projection = {TvBrowserContentProvider.KEY_ID}; Cursor prev = null; try { prev = context.getContentResolver().query(TvBrowserContentProvider.CONTENT_URI_DATA, projection, TvBrowserContentProvider.DATA_KEY_STARTTIME + "<" + startTime + " AND " + TvBrowserContentProvider.CHANNEL_KEY_CHANNEL_ID + " IS " + channelID + " AND NOT " + TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE, null, TvBrowserContentProvider.DATA_KEY_STARTTIME + " DESC LIMIT 1"); if(IOUtils.prepareAccessFirst(prev)) { mPreviousId = prev.getLong(prev.getColumnIndex(TvBrowserContentProvider.KEY_ID)); } }finally { IOUtils.close(prev); } Cursor next = null; try { next = context.getContentResolver().query(TvBrowserContentProvider.CONTENT_URI_DATA, projection, TvBrowserContentProvider.DATA_KEY_STARTTIME + ">" + startTime + " AND " + TvBrowserContentProvider.CHANNEL_KEY_CHANNEL_ID + " IS " + channelID + " AND NOT " + TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE, null, TvBrowserContentProvider.DATA_KEY_STARTTIME + " ASC LIMIT 1"); if(IOUtils.prepareAccessFirst(next)) { mNextId = next.getLong(next.getColumnIndex(TvBrowserContentProvider.KEY_ID)); } }finally { IOUtils.close(next); } } }finally { IOUtils.close(c); } return result; } @Override protected void onPostExecute(Boolean result) { mHandler.removeCallbacks(mShowWaiting); mShowWaitingDialog = false; if(mProgress != null && mProgress.isShowing()) { try { mProgress.dismiss(); }catch(IllegalArgumentException e) {} } if(result) { AlertDialog.Builder builder = new AlertDialog.Builder(context); if(finish != null) { builder.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { finish.finish(); } }); } builder.setView(mLayout); final AlertDialog test = builder.create(); /* Stupid hack for wrong measured dialog size */ if(mHasSpannableActors && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { final OnPreDrawListener preDrawListener = new OnPreDrawListener() { @Override public boolean onPreDraw() { mLayout.getViewTreeObserver().removeOnPreDrawListener(this); int scrollHeight = mLayout.findViewById(R.id.test_test_test).getMeasuredHeight(); if(scrollHeight < mLayout.getMeasuredHeight()) { test.getWindow().setLayout(mLayout.getMeasuredWidth()+40, scrollHeight+100); } return true; } }; mLayout.getViewTreeObserver().addOnPreDrawListener(preDrawListener); } if(mLayout instanceof SwipeScrollView) { mLayout.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { boolean result = false; if(((SwipeScrollView)mLayout).isSwipeLeft()) { ((SwipeScrollView)mLayout).resetSwipe(); if(mNextId != -1) { test.dismiss(); UiUtils.showProgramInfo(context, mNextId, finish, parent, handler); result = true; } } else if(((SwipeScrollView)mLayout).isSwipeRight()) { ((SwipeScrollView)mLayout).resetSwipe(); if(mPreviousId != -1) { test.dismiss(); UiUtils.showProgramInfo(context, mPreviousId, finish, parent, handler); result = true; } } return result; } }); } try { test.show(); }catch(BadTokenException e) {} } new Thread("SHOW PROGRAM INFO ALLOWING THREAD") { @Override public void run() { try { sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } mShowingProgramInfo = false; } }.start(); } }; createInfoTask.execute(); } }); } } public static void createContextMenu(final Context context, ContextMenu menu, final long id) { new MenuInflater(context).inflate(R.menu.program_context, menu); String[] projection = TvBrowserContentProvider.getColumnArrayWithMarkingColums(ProgramUtils.DATA_CHANNEL_PROJECTION); if(IOUtils.isDatabaseAccessible(context)) { Cursor cursor = null; try { cursor = context.getContentResolver().query(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_DATA_WITH_CHANNEL, id), projection, null, null, null); if(IOUtils.prepareAccessFirst(cursor)) { final Program pluginProgram = ProgramUtils.createProgramFromDataCursor(context, cursor);/*new Program(id, startTime, endTime, title, shortDescription, description, episodeTitle, channel);*/ boolean showReminder = true; boolean isFavoriteReminder = false; boolean createFavorite = true; for(String column : TvBrowserContentProvider.MARKING_COLUMNS) { int index = cursor.getColumnIndex(column); if(index >= 0) { if(column.equals(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER)) { showReminder = column.equals(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER) && cursor.getInt(index) == 0; } else if(column.equals(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER)) { isFavoriteReminder = column.equals(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER) && cursor.getInt(index) == 1; } if(column.equals(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE)) { createFavorite = column.equals(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE) && cursor.getInt(index) == 0; } } } showReminder = showReminder && !isFavoriteReminder; boolean showDontWantToSee = cursor.getInt(cursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE)) == 0; boolean isFutureReminder = pluginProgram.getStartTimeInUTC() > System.currentTimeMillis() - 5 * 60000; boolean showSyncRemove = cursor.getInt(cursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_SYNC)) == 1; menu.findItem(R.id.prog_add_reminder).setVisible(showReminder && isFutureReminder); menu.findItem(R.id.prog_remove_reminder).setVisible(!showReminder); menu.findItem(R.id.create_favorite_item).setVisible(createFavorite); menu.findItem(R.id.edit_favorite_item).setVisible(!createFavorite); menu.findItem(R.id.program_popup_dont_want_to_see).setVisible(showDontWantToSee && !SettingConstants.UPDATING_FILTER); menu.findItem(R.id.program_popup_want_to_see).setVisible(!showDontWantToSee && !SettingConstants.UPDATING_FILTER); menu.findItem(R.id.program_popup_remove_sync).setVisible(showSyncRemove); if(PluginHandler.hasPlugins()) { PluginServiceConnection[] connections = PluginHandler.getAvailablePlugins(); for(final PluginServiceConnection pluginService : connections) { final Plugin plugin = pluginService.getPlugin(); if(plugin != null && pluginService.isActivated()) { try { PluginMenu[] actions = plugin.getContextMenuActionsForProgram(pluginProgram); if(actions != null) { for(final PluginMenu pluginMenu : actions) { MenuItem item = menu.add(-1,Menu.NONE,Menu.NONE,pluginMenu.getTitle()); item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { try { if(plugin.onProgramContextMenuSelected(pluginProgram, pluginMenu)) { if(pluginService.getPluginMarkIcon() != null) { ProgramUtils.markProgram(context, pluginProgram, pluginService.getId()); } else { ProgramUtils.markProgram(context, pluginProgram, null); } } } catch (Throwable t) { // catch all possible exceptions and errors of plugins Log.d("info23", "", t); } return true; } }); } } } catch (Throwable t) { // catch all possible exceptions and errors of plugins Log.d("info23", "", t); } } } } } if(context != null && context instanceof ActivityTvBrowserSearchResults) { menu.findItem(R.id.program_popup_search_repetition).setVisible(false); } } finally { IOUtils.close(cursor); } } } public static void searchForRepetition(final Context activity, String title, String episode, View parent) { final AlertDialog.Builder builder = new AlertDialog.Builder(activity); final RelativeLayout layout = (RelativeLayout)((LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.search_repetition_layout, parent instanceof ViewGroup ? (ViewGroup)parent : null, false); final Resources resources = activity.getResources(); final EditText titleText = (EditText)layout.findViewById(R.id.search_repetition_title); final EditText episodeText = (EditText)layout.findViewById(R.id.search_repetition_episode); if(title != null) { titleText.setText(title); } if(episode != null) { episodeText.setText(episode); } builder.setTitle(resources.getString(R.string.program_popup_search_repetition)); builder.setView(layout); builder.setPositiveButton(resources.getString(android.R.string.search_go), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent search = new Intent(activity,ActivityTvBrowserSearchResults.class); search.putExtra(SearchManager.QUERY, String.valueOf(titleText.getText())); String episode = String.valueOf(episodeText.getText()).trim(); if(episode.length() > 0) { search.putExtra(ActivityTvBrowserSearchResults.QUERY_EXTRA_EPISODE_KEY, episode); } activity.startActivity(search); } }); builder.setNegativeButton(resources.getString(android.R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.show(); } @SuppressLint("NewApi") public static boolean handleContextMenuSelection(final Context activity, MenuItem item, long programID, final View menuView, View parent) { if(IOUtils.isDatabaseAccessible(activity)) { String title = null; String episode = null; ArrayList<String> markedColumns = new ArrayList<String>(); Cursor info = null; try { info = activity.getContentResolver().query(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_DATA, programID), TvBrowserContentProvider.getColumnArrayWithMarkingColums(TvBrowserContentProvider.DATA_KEY_TITLE,TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE), null, null,null); if(IOUtils.prepareAccessFirst(info)) { for(String column : TvBrowserContentProvider.MARKING_COLUMNS) { int index = info.getColumnIndex(column); if(index >= 0 && info.getInt(index) == 1) { markedColumns.add(column); } } title = info.getString(info.getColumnIndex(TvBrowserContentProvider.DATA_KEY_TITLE)); if(!info.isNull(info.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE))) { episode = info.getString(info.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE)); } } } finally { IOUtils.close(info); } ContentValues values = new ContentValues(); if(item.getItemId() == R.id.create_favorite_item) { UiUtils.editFavorite(null, activity, title); return true; } else if(item.getItemId() == R.id.edit_favorite_item) { final Favorite[] exludeFavorites = Favorite.getFavoritesForUniqueId(activity, programID); if(exludeFavorites.length == 1) { UiUtils.editFavorite(exludeFavorites[0], activity, null); } else if(exludeFavorites.length > 1) { final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.dialog_edit_favorite_select_title); final ArrayList<String> items = new ArrayList<String>(); for(Favorite favorite : exludeFavorites) { items.add(favorite.getName()); } builder.setItems(items.toArray(new String[items.size()]), new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { UiUtils.editFavorite(exludeFavorites[which], activity, null); } }); builder.setNegativeButton(android.R.string.cancel, null); final AlertDialog d = builder.create(); d.show(); } return true; } else if(item.getItemId() == R.id.program_popup_search_repetition) { searchForRepetition(activity,title,episode,parent); } else if(item.getItemId() == R.id.prog_add_reminder) { if(markedColumns.contains(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER) || markedColumns.contains(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER)) { return true; } else { values.put(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER, true); } ProgramUtils.addReminderId(activity, programID); //addReminder(activity.getApplicationContext(),programID,0,UiUtils.class,true); } else if(item.getItemId() == R.id.prog_remove_reminder) { if(!(markedColumns.contains(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER) || markedColumns.contains(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER))) { return true; } values.put(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER, false); values.put(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER, 0); values.put(TvBrowserContentProvider.DATA_KEY_REMOVED_REMINDER, true); ProgramUtils.removeReminderId(activity, programID); if(menuView != null) { markedColumns.remove(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER); markedColumns.remove(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER); if(markedColumns.isEmpty()) { menuView.setBackgroundResource(android.R.drawable.list_selector_background); } else { handleMarkings(activity, null, menuView, IOUtils.getStringArrayFromList(markedColumns)); } } IOUtils.removeReminder(activity.getApplicationContext(),programID); } else if(item.getItemId() == R.id.program_popup_dont_want_to_see) { if(title != null) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.action_dont_want_to_see); View view = ((LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.dont_want_to_see_edit, parent instanceof ViewGroup ? (ViewGroup)parent : null, false); final TextView exclusion = (TextView)view.findViewById(R.id.dont_want_to_see_value); final CheckBox caseSensitive = (CheckBox)view.findViewById(R.id.dont_want_to_see_case_sensitve); exclusion.setText(title); builder.setView(view); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if(!SettingConstants.UPDATING_FILTER) { SettingConstants.UPDATING_FILTER = true; String key = activity.getResources().getString(R.string.I_DONT_WANT_TO_SEE_ENTRIES); Set<String> dontWantToSeeSet = PreferenceManager.getDefaultSharedPreferences(activity).getStringSet(key, null); HashSet<String> newDontWantToSeeSet = new HashSet<String>(); String exclusionText = exclusion.getText().toString().trim(); boolean caseSensitiveValue = caseSensitive.isChecked(); if(exclusionText.length() > 0) { if(dontWantToSeeSet != null) { newDontWantToSeeSet.addAll(dontWantToSeeSet); } final String exclusion = exclusionText + ";;" + (caseSensitiveValue ? "1" : "0"); new Thread() { public void run() { if(activity instanceof TvBrowser) { ((TvBrowser)activity).updateProgressIcon(true); } Context applicationContext = activity.getApplicationContext(); NotificationCompat.Builder builder; builder = new NotificationCompat.Builder(activity); builder.setSmallIcon(R.drawable.ic_stat_notify); builder.setOngoing(true); builder.setContentTitle(activity.getResources().getText(R.string.action_dont_want_to_see)); builder.setContentText(activity.getResources().getText(R.string.dont_want_to_see_refresh_notification_text)); int notifyID = 4; NotificationManager notification = (NotificationManager)activity.getSystemService(Context.NOTIFICATION_SERVICE); notification.notify(notifyID, builder.build()); ArrayList<ContentProviderOperation> updateValuesList = new ArrayList<ContentProviderOperation>(); String title = null; Cursor c = null; try { c = activity.getContentResolver().query(TvBrowserContentProvider.CONTENT_URI_DATA, new String[] {TvBrowserContentProvider.KEY_ID,TvBrowserContentProvider.DATA_KEY_TITLE}, " NOT " +TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE, null, TvBrowserContentProvider.KEY_ID); if(IOUtils.prepareAccess(c)) { int size = c.getCount(); int count = 0; builder.setProgress(100, 0, true); notification.notify(notifyID, builder.build()); int keyColumn = c.getColumnIndex(TvBrowserContentProvider.KEY_ID); int titleColumn = c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_TITLE); DontWantToSeeExclusion exclusionValue = new DontWantToSeeExclusion(exclusion); int lastPercent = 0; while(c.moveToNext()) { int percent = (int)(count++/(float)size * 100); if(lastPercent != percent) { lastPercent = percent; builder.setProgress(100,percent, false); notification.notify(notifyID, builder.build()); } title = c.getString(titleColumn); if(UiUtils.filter(title, exclusionValue)) { ContentValues values = new ContentValues(); values.put(TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE, 1); ContentProviderOperation.Builder opBuilder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_DATA_UPDATE, c.getLong(keyColumn))); opBuilder.withValues(values); updateValuesList.add(opBuilder.build()); } else { try { sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } } } finally { IOUtils.close(c); } if(!updateValuesList.isEmpty()) { try { activity.getContentResolver().applyBatch(TvBrowserContentProvider.AUTHORITY, updateValuesList); sendDontWantToSeeChangedBroadcast(applicationContext,true); } catch (RemoteException e) { e.printStackTrace(); } catch (OperationApplicationException e) { e.printStackTrace(); } } notification.cancel(notifyID); if(activity instanceof TvBrowser) { ((TvBrowser)activity).updateProgressIcon(false); } SettingConstants.UPDATING_FILTER = false; } }.start(); newDontWantToSeeSet.add(exclusion); Editor edit = PreferenceManager.getDefaultSharedPreferences(activity).edit(); edit.putStringSet(key, newDontWantToSeeSet); edit.commit(); } } } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {} }); builder.show(); } return true; } else if(item.getItemId() == R.id.program_popup_want_to_see) { if(title != null && !SettingConstants.UPDATING_FILTER) { SettingConstants.UPDATING_FILTER = true; Set<String> exclusionValues = PrefUtils.getStringSetValue(R.string.I_DONT_WANT_TO_SEE_ENTRIES, null); //ArrayList<> HashSet<String> newExclusionSet = new HashSet<String>(); final ArrayList<DontWantToSeeExclusion> exclusionList = new ArrayList<DontWantToSeeExclusion>(); for(String exclusion : exclusionValues) { if(!filter(title, new DontWantToSeeExclusion(exclusion))) { newExclusionSet.add(exclusion); exclusionList.add(new DontWantToSeeExclusion(exclusion)); } } new Thread() { public void run() { if(activity instanceof TvBrowser) { ((TvBrowser)activity).updateProgressIcon(true); } Context applicationContext = activity.getApplicationContext(); NotificationCompat.Builder builder; builder = new NotificationCompat.Builder(activity); builder.setSmallIcon(R.drawable.ic_stat_notify); builder.setOngoing(true); builder.setContentTitle(activity.getResources().getText(R.string.action_dont_want_to_see)); builder.setContentText(activity.getResources().getText(R.string.dont_want_to_see_refresh_notification_text)); int notifyID = 3; NotificationManager notification = (NotificationManager)activity.getSystemService(Context.NOTIFICATION_SERVICE); notification.notify(notifyID, builder.build()); ArrayList<ContentProviderOperation> updateValuesList = new ArrayList<ContentProviderOperation>(); Cursor c = null; try { c = activity.getContentResolver().query(TvBrowserContentProvider.CONTENT_URI_DATA, new String[] {TvBrowserContentProvider.KEY_ID,TvBrowserContentProvider.DATA_KEY_TITLE}, TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE, null, TvBrowserContentProvider.KEY_ID); if(IOUtils.prepareAccess(c)) { int size = c.getCount(); int count = 0; builder.setProgress(size, 0, true); notification.notify(notifyID, builder.build()); int keyColumn = c.getColumnIndex(TvBrowserContentProvider.KEY_ID); int titleColumn = c.getColumnIndex(TvBrowserContentProvider.DATA_KEY_TITLE); DontWantToSeeExclusion[] exclusionArr = exclusionList.toArray(new DontWantToSeeExclusion[exclusionList.size()]); while(c.moveToNext()) { builder.setProgress(size, count++, false); notification.notify(notifyID, builder.build()); String title = c.getString(titleColumn); ContentValues values = new ContentValues(); values.put(TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE, (UiUtils.filter(activity, title, exclusionArr) ? 1 : 0)); ContentProviderOperation.Builder opBuilder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_DATA_UPDATE, c.getLong(keyColumn))); opBuilder.withValues(values); updateValuesList.add(opBuilder.build()); } notification.cancel(notifyID); } } finally { IOUtils.close(c); } if(!updateValuesList.isEmpty()) { try { activity.getContentResolver().applyBatch(TvBrowserContentProvider.AUTHORITY, updateValuesList); sendDontWantToSeeChangedBroadcast(applicationContext,false); } catch (RemoteException e) { e.printStackTrace(); } catch (OperationApplicationException e) { e.printStackTrace(); } } if(activity instanceof TvBrowser) { ((TvBrowser)activity).updateProgressIcon(false); } SettingConstants.UPDATING_FILTER = false; } }.start(); Editor edit = PreferenceManager.getDefaultSharedPreferences(activity).edit(); edit.putStringSet(activity.getResources().getString(R.string.I_DONT_WANT_TO_SEE_ENTRIES), newExclusionSet); edit.commit(); } return true; } else { if(!markedColumns.contains(TvBrowserContentProvider.DATA_KEY_MARKING_SYNC)) { return true; } values.put(TvBrowserContentProvider.DATA_KEY_MARKING_SYNC, false); values.put(TvBrowserContentProvider.DATA_KEY_REMOVED_SYNC, true); ProgramUtils.removeSyncId(activity, programID); if(menuView != null) { markedColumns.remove(TvBrowserContentProvider.DATA_KEY_MARKING_SYNC); if(markedColumns.isEmpty()) { menuView.setBackgroundResource(android.R.drawable.list_selector_background); } else { handleMarkings(activity, null, menuView, IOUtils.getStringArrayFromList(markedColumns)); } } } if(values.size() > 0) { if(menuView != null) { menuView.invalidate(); } activity.getContentResolver().update(ContentUris.withAppendedId(TvBrowserContentProvider.CONTENT_URI_DATA, programID), values, null, null); sendMarkingChangedBroadcast(activity, programID, true); ServiceUpdateReminders.startReminderUpdate(activity); } } return true; } public static void sendDontWantToSeeChangedBroadcast(Context context, boolean added) { Intent intent = new Intent(SettingConstants.DONT_WANT_TO_SEE_CHANGED); intent.putExtra(SettingConstants.DONT_WANT_TO_SEE_ADDED_EXTRA, added); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); updateImportantProgramsWidget(context); updateRunningProgramsWidget(context); } public static void sendMarkingChangedBroadcast(Context context, long programID, boolean onlyUpdate) { Intent intent = new Intent(SettingConstants.MARKINGS_CHANGED); intent.putExtra(SettingConstants.EXTRA_MARKINGS_ID, programID); intent.putExtra(SettingConstants.EXTRA_MARKINGS_ONLY_UPDATE, onlyUpdate); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); updateImportantProgramsWidget(context.getApplicationContext()); } private static class RunningDrawable extends Drawable { private Paint mBase; private Paint mSecond; private long mStartTime; private long mEndTime; private boolean mVertical; public RunningDrawable(Paint base, Paint second, long startTime, long endTime, boolean vertical) { mBase = base; mSecond = second; mStartTime = startTime; mEndTime = endTime; mVertical = vertical; } @Override public void draw(Canvas canvas) { if(mStartTime <= System.currentTimeMillis() && System.currentTimeMillis() < mEndTime) { long expiredSeconds = System.currentTimeMillis() - mStartTime; float percent = expiredSeconds/(float)(mEndTime - mStartTime); if(mVertical) { int topHeight = (int)(getBounds().height() * percent); canvas.drawRect(0, 0, getBounds().width(), topHeight, mBase); canvas.drawRect(0, topHeight, getBounds().width(), getBounds().height(), mSecond); } else { int leftWidth = (int)(getBounds().width() * percent); canvas.drawRect(0, 0, leftWidth, getBounds().height(), mBase); canvas.drawRect(leftWidth, 0, getBounds().width(), getBounds().height(), mSecond); } } } @Override public int getOpacity() { return PixelFormat.UNKNOWN; } @Override public void setAlpha(int alpha) {} @Override public void setColorFilter(ColorFilter cf) {} } public static void handleMarkings(Context activity, Cursor cursor, View view, String[] markingValues) { handleMarkings(activity, cursor, view, markingValues, null, false); } public static void handleMarkings(Context context, Cursor cursor, View view, String[] markingValues, Handler handler, boolean vertical) { long startTime = cursor != null ? cursor.getLong(cursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_STARTTIME)) : 0; long endTime = cursor != null ? cursor.getLong(cursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_ENDTIME)) : 0; handleMarkings(context, cursor, startTime, endTime, view, markingValues,handler,vertical); } public static void handleMarkings(Context context, Cursor cursor, long startTime, long endTime, View view, String[] markingValues) { handleMarkings(context, cursor, startTime, endTime, view, markingValues, null); } public static void handleMarkings(Context context, Cursor cursor, long startTime, long endTime, final View view, String[] markingValues, Handler handler) { handleMarkings(context, cursor, startTime, endTime, view, markingValues, handler, false); } public static void handleMarkings(Context context, Cursor cursor, long startTime, long endTime, final View view, String[] markedColumns, Handler handler, boolean vertical) { final LayerDrawable draw = getMarkingsDrawable(context, cursor, startTime, endTime, markedColumns, vertical); if(handler == null) { view.invalidate(); CompatUtils.setBackground(view, draw); } else{ handler.post(new Runnable() { @Override public void run() { view.invalidate(); CompatUtils.setBackground(view, draw); } }); } } public static LayerDrawable getMarkingsDrawable(Context context, Cursor cursor, long startTime, long endTime, String[] markedColumns,boolean vertical) { if(markedColumns == null && cursor != null) { ArrayList<String> markedColumnList = new ArrayList<String>(); for(String column : TvBrowserContentProvider.MARKING_COLUMNS) { int index = cursor.getColumnIndex(column); int keyIndex = cursor.getColumnIndex(TvBrowserContentProvider.KEY_ID); if(index >= 0 && cursor.getInt(index) >= 1) { markedColumnList.add(column); } else if(keyIndex >= 0 && column.equals(TvBrowserContentProvider.DATA_KEY_MARKING_MARKING) && ProgramUtils.isMarkedWithIcon(context, cursor.getLong(keyIndex))) { markedColumnList.add(column); } } markedColumns = IOUtils.getStringArrayFromList(markedColumnList); } Paint base = new Paint(); base.setStyle(Paint.Style.FILL_AND_STROKE); Paint second = null; if(PrefUtils.getBooleanValue(R.string.PREF_SHOW_PROGRESS, R.bool.pref_show_progress_default) && startTime <= System.currentTimeMillis() && System.currentTimeMillis() <= endTime) { base.setColor(getColor(ON_AIR_PROGRESS_KEY, context)); second = new Paint(); second.setStyle(Paint.Style.FILL_AND_STROKE); second.setColor(getColor(ON_AIR_BACKGROUND_KEY, context)); } else { base = null; } final ArrayList<Drawable> draw = new ArrayList<Drawable>(); if(base != null) { RunningDrawable running = new RunningDrawable(base, second, startTime, endTime, vertical); draw.add(running); } if(markedColumns != null && markedColumns.length > 0) { if(markedColumns.length > 1) { int[] colors = new int[markedColumns.length]; for(int i = 0; i < markedColumns.length; i++) { Integer color = SettingConstants.MARK_COLOR_KEY_MAP.get(markedColumns[i]); if(markedColumns[i].equals(TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE) && PrefUtils.getStringValue(R.string.PREF_I_DONT_WANT_TO_SEE_FILTER_TYPE, R.string.pref_i_dont_want_to_see_filter_type_default).equals(context.getResources().getStringArray(R.array.pref_simple_string_value_array2)[0])) { color = null; } if(color != null) { colors[i] = getColor(color, context); } } GradientDrawable gd = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,colors); gd.setCornerRadius(0f); draw.add(gd); } else { Integer color = SettingConstants.MARK_COLOR_KEY_MAP.get(markedColumns[0]); if(markedColumns[0].equals(TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE) && PrefUtils.getStringValue(R.string.PREF_I_DONT_WANT_TO_SEE_FILTER_TYPE, R.string.pref_i_dont_want_to_see_filter_type_default).equals(context.getResources().getStringArray(R.array.pref_simple_string_value_array2)[0])) { color = null; } if(color != null) { draw.add(new ColorDrawable(getColor(color, context))); } } } draw.add(ContextCompat.getDrawable(context, android.R.drawable.list_selector_background)); return new LayerDrawable(draw.toArray(new Drawable[draw.size()])); } public static void editFavorite(final Favorite fav, final Context activity, String searchString) { Intent startEditFavorite = new Intent(activity, ActivityFavoriteEdit.class); startEditFavorite.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if(fav != null) { startEditFavorite.putExtra(Favorite.FAVORITE_EXTRA, fav); } else if(searchString != null) { startEditFavorite.putExtra(Favorite.SEARCH_EXTRA, searchString); } activity.startActivity(startEditFavorite); } public static String formatDate(long date, Context context, boolean onlyDays) { return formatDate(date, context, onlyDays, false); } public static String formatDate(long date, Context context, boolean onlyDays, boolean withDayString) { return formatDate(date, context, onlyDays, withDayString, false); } @SuppressLint("SimpleDateFormat") public static String formatDate(long date, Context context, boolean onlyDays, boolean withDayString, boolean withDate) { Calendar progDate = Calendar.getInstance(); progDate.setTimeInMillis(date); Calendar today = Calendar.getInstance(); String value = null; if(progDate.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) { value = context.getText(R.string.today).toString(); } else { today.add(Calendar.DAY_OF_YEAR, 1); if(progDate.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) { value = context.getText(R.string.tomorrow).toString(); } else { today.add(Calendar.DAY_OF_YEAR, -2); if(progDate.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) { value = context.getText(R.string.yesterday).toString(); } } } if(value == null || withDate) { if(value == null) { value = ""; } else if(value.trim().length() > 0) { value += ", "; } if(!onlyDays) { SimpleDateFormat df = (SimpleDateFormat)java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT); String pattern = df.toLocalizedPattern().replaceAll(".?[Yy].?", ""); if(withDayString) { pattern = "EEE " + pattern; } if(pattern.contains(".")) { pattern += "."; } SimpleDateFormat mdf = new SimpleDateFormat(pattern); value += mdf.format(progDate.getTime()); } else if(withDayString) { SimpleDateFormat mdf = new SimpleDateFormat("EEE "); value += mdf.format(progDate.getTime()); } } return value; } @SuppressLint("SimpleDateFormat") public static void formatDayView(Activity activity, Cursor cursor, View view, int startDayLabelID) {try { long date = cursor.getLong(cursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_STARTTIME)); TextView text = (TextView)view; SimpleDateFormat day = new SimpleDateFormat("EEE", Locale.getDefault()); TextView startDay = (TextView)((View)view.getParent()).findViewById(/*R.id.startDayLabelPL*/startDayLabelID); startDay.setText(day.format(new Date(date))); CharSequence startDayValue = formatDate(date, activity, true); if(startDayValue != null && startDayValue.toString().trim().length() > 0) { startDay.setText(startDayValue + ", " + startDay.getText()); } Calendar progDate = Calendar.getInstance(); progDate.setTimeInMillis(date); SimpleDateFormat df = (SimpleDateFormat)java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT); String pattern = df.toLocalizedPattern().replaceAll(".?[Yy].?", ""); pattern = pattern.replace(".MM", ". MMM"); pattern = pattern.replace("MM.", "MMM. "); SimpleDateFormat mdf = new SimpleDateFormat(pattern); text.setText(mdf.format(progDate.getTime())); UiUtils.handleMarkings(activity, cursor, ((RelativeLayout)view.getParent()), null);}catch(Throwable t) {} } public static int convertDpToPixel(float dp, Resources res) { // Get the screen's density scale final float scale = res.getDisplayMetrics().density; // Convert the dps to pixels, based on density scale return (int) (dp * scale + 0.5f); } public static float convertPixelsToSp(float px, Context context) { float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; return px/scaledDensity; } public static boolean filter(String title, DontWantToSeeExclusion exclusion) { return exclusion.matches(title); } public static boolean filter(Context context, String title, DontWantToSeeExclusion[] values) { boolean found = false; if(title != null) { if(values == null) { Set<String> exclusionValues = PrefUtils.getStringSetValue(R.string.I_DONT_WANT_TO_SEE_ENTRIES, null); //ArrayList<> values = new DontWantToSeeExclusion[exclusionValues.size()]; int i = 0; for(String exclusion : exclusionValues) { values[i++] = new DontWantToSeeExclusion(exclusion); } } if(values != null) { for(DontWantToSeeExclusion value : values) { if(filter(title, value)) { found = true; break; } } } /* if(clear) { Editor edit = PreferenceManager.getDefaultSharedPreferences(context).edit(); edit.putStringSet(context.getResources().getString(R.string.I_DONT_WANT_TO_SEE_ENTRIES), cleared); edit.commit(); }*/ } return found; } public static final int getColor(int key, Context context) throws NullPointerException { if(context == null) { throw new NullPointerException("Context parameter is null."); } return getColor(key, PreferenceManager.getDefaultSharedPreferences(context),context); } public static final int getColor(int key, SharedPreferences pref, Context context) throws NullPointerException { if(pref == null) { throw new NullPointerException("Preferences parameter is null."); } else if(context == null) { throw new NullPointerException("Context parameter is null."); } int color = 0; switch(key) { case EXPIRED_COLOR_KEY: if(SettingConstants.IS_DARK_THEME) { color = SettingConstants.EXPIRED_DARK_COLOR; } else { color = SettingConstants.EXPIRED_LIGHT_COLOR; } break; case MARKED_COLOR_KEY: color = pref.getInt(context.getString(R.string.PREF_COLOR_MARKED), ContextCompat.getColor(context, R.color.pref_color_mark_tvb_style_default));break; case MARKED_FAVORITE_COLOR_KEY: color = pref.getInt(context.getString(R.string.PREF_COLOR_FAVORITE), ContextCompat.getColor(context, R.color.pref_color_mark_favorite_tvb_style_default));break; case MARKED_REMINDER_COLOR_KEY: color = pref.getInt(context.getString(R.string.PREF_COLOR_REMINDER), ContextCompat.getColor(context, R.color.pref_color_mark_reminder_tvb_style_default));break; case MARKED_SYNC_COLOR_KEY: color = pref.getInt(context.getString(R.string.PREF_COLOR_SYNC), ContextCompat.getColor(context, R.color.pref_color_mark_sync_tvb_style_favorite_default));break; case ON_AIR_BACKGROUND_KEY: color = pref.getInt(context.getString(R.string.PREF_COLOR_ON_AIR_BACKGROUND), ContextCompat.getColor(context, R.color.pref_color_on_air_background_tvb_style_default));break; case ON_AIR_PROGRESS_KEY: color = pref.getInt(context.getString(R.string.PREF_COLOR_ON_AIR_PROGRESS), ContextCompat.getColor(context, R.color.pref_color_on_air_progress_tvb_style_default));break; case RUNNING_TIME_SELECTION_KEY: color = pref.getInt(context.getString(R.string.PREF_RUNNING_TIME_SELECTION), ContextCompat.getColor(context, R.color.pref_color_running_time_selection_background_tvb_style_default));break; case I_DONT_WANT_TO_SEE_HIGHLIGHT_COLOR_KEY: color = pref.getInt(context.getString(R.string.PREF_I_DONT_WANT_TO_SEE_HIGHLIGHT_COLOR), ContextCompat.getColor(context, R.color.i_dont_want_to_see_highlight));break; } return color; } public static void handleConfigurationChange(Handler handler, final BaseAdapter adapter, Configuration newConfig) { if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { handler.post(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); } }); } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ handler.post(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); } }); } } public static Bitmap drawableToBitmap (Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable)drawable).getBitmap(); } Bitmap bitmap = Bitmap.createBitmap(drawable.getBounds().width(), drawable.getBounds().height(), Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } public static int[] getColorValues(int color) { int[] colorValues = new int[4]; colorValues[0] = (color >> 24) & 0xFF; colorValues[1] = (color >> 16) & 0xFF; colorValues[2] = (color >> 8) & 0xFF; colorValues[3] = color & 0xFF; return colorValues; } public static int getColorForValues(int[] colorValues) { int color = 0; int index = 0; if(colorValues.length == 4) { color = color | (colorValues[index++] << 24); } if(colorValues.length >= 3) { color = color | (colorValues[index++] << 16); color = color | (colorValues[index++] << 8); color = color | (colorValues[index++]); } return color; } public static void scaleTextViews(View view, float scale) { if(view instanceof ViewGroup) { ViewGroup group = (ViewGroup)view; for(int i = 0; i < group.getChildCount(); i++) { scaleTextViews(group.getChildAt(i), scale); } } else if(view instanceof TextView) { TextView text = (TextView)view; text.setTextSize(TypedValue.COMPLEX_UNIT_PX, text.getTextSize() * scale); } } public static Bitmap createBitmapFromByteArray(byte[] data) { Bitmap logoBitmap = null; if(data != null && data.length > 0) { try { logoBitmap = BitmapFactory.decodeByteArray(data, 0, data.length); }catch(NullPointerException e) {} } return logoBitmap; } public static String getDontWantToSeeFilterString(Context context) { String returnValue = ""; if(PrefUtils.getStringValue(R.string.PREF_I_DONT_WANT_TO_SEE_FILTER_TYPE, R.string.pref_i_dont_want_to_see_filter_type_default).equals(context.getResources().getStringArray(R.array.pref_simple_string_value_array2)[0])) { returnValue = " AND ( NOT " + TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE + " ) "; } return returnValue; } public static void updateImportantProgramsWidget(Context context) { Log.d("info2", "updateImportantProgramsWidget"); try { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext()); ComponentName importantProgramsWidget = new ComponentName(context, ImportantProgramsListWidget.class); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(importantProgramsWidget); Log.d("info2", "appWidgetIds " + appWidgetIds); appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.important_widget_list_view); }catch(Throwable t) {Log.d("info2", "", t);} } public static void updateRunningProgramsWidget(Context context) { Log.d("info2", "updateRunningProgramsWidget"); try { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext()); ComponentName importantProgramsWidget = new ComponentName(context, RunningProgramsListWidget.class); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(importantProgramsWidget); appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.running_widget_list_view); Log.d("info2", "appWidgetIds " + appWidgetIds); }catch(Throwable t) {Log.d("info2", "", t);} } public static final void reloadWidgets(Context context) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext()); ComponentName programsWidget = new ComponentName(context.getApplicationContext(), ImportantProgramsListWidget.class); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(programsWidget); for(int appWidgetId : appWidgetIds) { Intent update = new Intent(SettingConstants.UPDATE_IMPORTANT_APP_WIDGET); update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); context.sendBroadcast(update); } programsWidget = new ComponentName(context.getApplicationContext(), RunningProgramsListWidget.class); appWidgetIds = appWidgetManager.getAppWidgetIds(programsWidget); for(int appWidgetId : appWidgetIds) { Intent update = new Intent(SettingConstants.UPDATE_RUNNING_APP_WIDGET); update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); context.sendBroadcast(update); } } public static void updateToggleReminderStateWidget(Context context) { Log.d("info2", "updateToggleReminderStateWidget"); try { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext()); ComponentName importantProgramsWidget = new ComponentName(context, WidgetToggleReminderState.class); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(importantProgramsWidget); for(int appWidgetId : appWidgetIds) { WidgetToggleReminderState.updateAppWidget(context, appWidgetManager, appWidgetId); } //appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.running_widget_list_view); Log.d("info2", "appWidgetIds " + appWidgetIds); }catch(Throwable t) {Log.d("info2", "", t);} } public static void showChannelFilterSelection(Context context, final ChannelFilter channelFilter, ViewGroup parent) { showChannelFilterSelection(context, channelFilter, parent, null); } public static void showChannelFilterSelection(Context context, final ChannelFilter channelFilter, ViewGroup parent, final Runnable cancelCallBack) { if(IOUtils.isDatabaseAccessible(context)) { String[] projection = { TvBrowserContentProvider.KEY_ID, TvBrowserContentProvider.CHANNEL_KEY_NAME, TvBrowserContentProvider.CHANNEL_KEY_SELECTION, TvBrowserContentProvider.CHANNEL_KEY_LOGO, TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER }; int[] channelRestriction = channelFilter.getFilteredChannelIds(); final LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE ); // inflate channel selection view View channelSelectionView = inflater.inflate(R.layout.dialog_channel_selection_list, parent, false); channelSelectionView.findViewById(R.id.channel_country_label).setVisibility(View.GONE); channelSelectionView.findViewById(R.id.channel_country_value).setVisibility(View.GONE); channelSelectionView.findViewById(R.id.channel_category_label).setVisibility(View.GONE); channelSelectionView.findViewById(R.id.channel_category_value).setVisibility(View.GONE); final EditText filterName = (EditText)channelSelectionView.findViewById(R.id.channel_selection_input_id_name); if(channelFilter.getName() == null) { channelSelectionView.findViewById(R.id.channel_selection_label_id_name).setVisibility(View.GONE); filterName.setVisibility(View.GONE); } else { filterName.setText(channelFilter.getName()); } final ListView list = (ListView)channelSelectionView.findViewById(R.id.channel_selection_list); final Bitmap defaultLogo = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher); final ArrayAdapter<AdapterChannel> channelAdapter = new ArrayAdapter<AdapterChannel>(context, android.R.layout.simple_list_item_1) { public View getView(int position, View convertView, ViewGroup parent) { AdapterChannel value = getItem(position); ChannelViewHolder holder = null; if (convertView == null) { holder = new ChannelViewHolder(); convertView = inflater.inflate(R.layout.channel_row, parent, false); holder.mTextView = (TextView)convertView.findViewById(R.id.row_of_channel_text); holder.mCheckBox = (CheckBox)convertView.findViewById(R.id.row_of_channel_selection); holder.mLogo = (ImageView)convertView.findViewById(R.id.row_of_channel_icon); convertView.setTag(holder); } else { holder = (ChannelViewHolder)convertView.getTag(); } holder.mTextView.setText(value.mName); holder.mCheckBox.setChecked(value.mSelected); list.setItemChecked(position, value.mSelected); Bitmap logo = value.mChannelLogo; if(logo != null) { holder.mLogo.setImageBitmap(logo); } else { holder.mLogo.setImageBitmap(defaultLogo); } return convertView; } }; Cursor channels = null; try{ channels = context.getContentResolver().query(TvBrowserContentProvider.CONTENT_URI_CHANNELS, projection, TvBrowserContentProvider.CHANNEL_KEY_SELECTION, null, TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER + ", " + TvBrowserContentProvider.CHANNEL_KEY_NAME); if(IOUtils.prepareAccess(channels)) { final int idColumn = channels.getColumnIndex(TvBrowserContentProvider.KEY_ID); final int logoColumn = channels.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_LOGO); final int nameColumn = channels.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_NAME); final int orderNumberColumn = channels.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER); while(channels.moveToNext()) { int channelID = channels.getInt(idColumn); String name = channels.getString(nameColumn); int orderNumber = channels.getInt(orderNumberColumn); byte[] logo = channels.getBlob(logoColumn); if(orderNumber < 1) { name = "-. " + name; } else { name = orderNumber + ". " + name; } Bitmap channelLogo = UiUtils.createBitmapFromByteArray(logo); if(channelLogo != null) { BitmapDrawable l = new BitmapDrawable(context.getResources(), channelLogo); ColorDrawable background = new ColorDrawable(SettingConstants.LOGO_BACKGROUND_COLOR); background.setBounds(0, 0, channelLogo.getWidth()+2,channelLogo.getHeight()+2); LayerDrawable logoDrawable = new LayerDrawable(new Drawable[] {background,l}); logoDrawable.setBounds(background.getBounds()); l.setBounds(2, 2, channelLogo.getWidth(), channelLogo.getHeight()); channelLogo = UiUtils.drawableToBitmap(logoDrawable); } channelAdapter.add(new AdapterChannel(channelID, name, channelLogo, isRestricted(channelRestriction, channelID))); } } }finally { IOUtils.close(channels); } list.setAdapter(channelAdapter); channelSelectionView.findViewById(R.id.channel_selection_select_all).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for(int i = 0; i < channelAdapter.getCount(); i++) { channelAdapter.getItem(i).mSelected = true; } list.invalidateViews(); } }); channelSelectionView.findViewById(R.id.channel_selection_remove_selection).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for(int i = 0; i < channelAdapter.getCount(); i++) { channelAdapter.getItem(i).mSelected = false; } list.invalidateViews(); } }); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { CheckBox check = (CheckBox)view.findViewById(R.id.row_of_channel_selection); if(check != null) { check.setChecked(!check.isChecked()); channelAdapter.getItem(position).mSelected = check.isChecked(); list.setItemChecked(position, check.isChecked()); } } }); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setView(channelSelectionView); builder.setCancelable(false); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ArrayList<Integer> channelIDList = new ArrayList<Integer>(); boolean allSelected = true; for(int i = 0; i < channelAdapter.getCount(); i++) { AdapterChannel item = channelAdapter.getItem(i); if(item.mSelected) { channelIDList.add(item.mChannelID); } else { allSelected = false; } } if(allSelected || channelIDList.isEmpty()) { channelFilter.setFilterValues(null, null); } else { int[] ids = new int[channelIDList.size()]; for(int i = 0; i < ids.length; i++) { ids[i] = channelIDList.get(i); } String name = null; if(filterName.getVisibility() == View.VISIBLE) { name = filterName.getText().toString().trim(); } channelFilter.setFilterValues(name,ids); } } }); if(cancelCallBack != null) { builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { cancelCallBack.run(); } }); } else { builder.setNegativeButton(android.R.string.cancel, null); } AlertDialog dialog = builder.create(); dialog.show(); if(filterName.getVisibility() == View.VISIBLE) { final Button ok = dialog.getButton(AlertDialog.BUTTON_POSITIVE); ok.setEnabled(filterName.getText().toString().trim().length() > 0); filterName.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void afterTextChanged(Editable s) { ok.setEnabled(filterName.getText().toString().trim().length() > 0); } }); } } } public static void showCategorySelection(Context context, final CategoryFilter categoryFilter, ViewGroup parent, final Runnable cancelCallBack) { String[] names = IOUtils.getInfoStringArrayNames(context.getResources()); int[] restrictionIndices = categoryFilter.getCategoriyIndicies(); final LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE ); View selectionView = inflater.inflate(R.layout.dialog_filter_categories, parent, false); final EditText nameInput = (EditText)selectionView.findViewById(R.id.dialog_filter_categories_input_name); final Spinner operationSelection = (Spinner)selectionView.findViewById(R.id.dialog_filter_categories_selection_operation); final ListView listView = (ListView)selectionView.findViewById(R.id.dialog_filter_categories_list); if(categoryFilter.getName() == null) { selectionView.findViewById(R.id.dialog_filter_categories_label_name).setVisibility(View.GONE); nameInput.setVisibility(View.GONE); operationSelection.setVisibility(View.GONE); selectionView.findViewById(R.id.dialog_filter_categories_label_operation).setVisibility(View.GONE); } else { operationSelection.setSelection(categoryFilter.getOperation().equals("AND") ? 0 : 1); nameInput.setText(categoryFilter.getName()); } final ArrayAdapter<AdapterCategory> categoryAdapter = new ArrayAdapter<AdapterCategory>(context, android.R.layout.simple_list_item_1) { public View getView(int position, View convertView, ViewGroup parent) { AdapterCategory value = getItem(position); CategoryViewHolder holder = null; if (convertView == null) { holder = new CategoryViewHolder(); convertView = inflater.inflate(R.layout.list_row_selection, parent, false); holder.mTextView = (TextView)convertView.findViewById(R.id.row_selection_text); holder.mCheckBox = (CheckBox)convertView.findViewById(R.id.row_selection_selection); convertView.setTag(holder); } else { holder = (CategoryViewHolder)convertView.getTag(); } holder.mTextView.setText(value.mName); holder.mCheckBox.setChecked(value.mSelected); listView.setItemChecked(position, value.mSelected); return convertView; } }; for(int i = 0; i < names.length; i++) { boolean selected = false; if(restrictionIndices != null) { for(int index : restrictionIndices) { if(i == index) { selected = true; break; } } } int[] color = IOUtils.getActivatedColorFor(PrefUtils.getStringValue(SettingConstants.CATEGORY_COLOR_PREF_KEY_ARR[i], null)); SpannableString name = new SpannableString(names[i]); if(color[0] == 1) { name.setSpan(new ForegroundColorSpan(color[1]), 0, name.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } AdapterCategory category = new AdapterCategory(i, name, selected); categoryAdapter.add(category); } listView.setAdapter(categoryAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { CheckBox check = (CheckBox)view.findViewById(R.id.row_selection_selection); if(check != null) { check.setChecked(!check.isChecked()); categoryAdapter.getItem(position).mSelected = check.isChecked(); listView.setItemChecked(position, check.isChecked()); } } }); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setView(selectionView); builder.setCancelable(false); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ArrayList<Integer> channelIDList = new ArrayList<Integer>(); boolean allSelected = true; for(int i = 0; i < categoryAdapter.getCount(); i++) { AdapterCategory item = categoryAdapter.getItem(i); if(item.mSelected) { channelIDList.add(item.mIndex); } else { allSelected = false; } } if(allSelected || channelIDList.isEmpty()) { categoryFilter.setFilterValues(null, null, null); } else { int[] indicies = new int[channelIDList.size()]; for(int i = 0; i < indicies.length; i++) { indicies[i] = channelIDList.get(i); } String name = null; if(nameInput.getVisibility() == View.VISIBLE) { name = nameInput.getText().toString().trim(); } String operation = "AND"; if(operationSelection.getVisibility() == View.VISIBLE) { if(operationSelection.getSelectedItemPosition() == 1) { operation = "OR"; } } categoryFilter.setFilterValues(name,operation,indicies); } } }); if(cancelCallBack != null) { builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { cancelCallBack.run(); } }); } else { builder.setNegativeButton(android.R.string.cancel, null); } AlertDialog dialog = builder.create(); dialog.show(); if(nameInput.getVisibility() == View.VISIBLE) { final Button ok = dialog.getButton(AlertDialog.BUTTON_POSITIVE); ok.setEnabled(nameInput.getText().toString().trim().length() > 0); nameInput.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void afterTextChanged(Editable s) { ok.setEnabled(nameInput.getText().toString().trim().length() > 0); } }); } } public static void showKeywordSelection(Context context, final KeywordFilter keywordFilter, ViewGroup parent, final Runnable cancelCallBack) { final LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE ); View selectionView = inflater.inflate(R.layout.dialog_filter_keyword, parent, false); final EditText nameInput = (EditText)selectionView.findViewById(R.id.dialog_filter_keyword_input_name); final EditText keywordInput = (EditText)selectionView.findViewById(R.id.dialog_filter_keyword_input_keyword); final Spinner columnSelection = (Spinner)selectionView.findViewById(R.id.dialog_filter_keyword_selection_column); nameInput.setText(keywordFilter.getName()); keywordInput.setText(keywordFilter.getKeyword()); final ArrayAdapter<NamedFields> columnSelectionAdatper = new ArrayAdapter<NamedFields>(context, android.R.layout.simple_list_item_1); columnSelection.setAdapter(columnSelectionAdatper); for(int i = 0; i < TvBrowserContentProvider.TEXT_SEARCHABLE_COLUMN_ARRAY.length; i++) { final String column = TvBrowserContentProvider.TEXT_SEARCHABLE_COLUMN_ARRAY[i]; columnSelectionAdatper.add(new NamedFields(context, column)); } columnSelectionAdatper.sort(NamedFields.COMPARATOR); for(int i = 0; i < columnSelectionAdatper.getCount(); i++) { if(columnSelectionAdatper.getItem(i).getColumn().equals(keywordFilter.getColumn())) { columnSelection.setSelection(i); } } AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setView(selectionView); builder.setCancelable(false); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { keywordFilter.setFilterValues(nameInput.getText().toString().trim(), keywordInput.getText().toString(), ((NamedFields)columnSelection.getSelectedItem()).getColumn()); } }); if(cancelCallBack != null) { builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { cancelCallBack.run(); } }); } else { builder.setNegativeButton(android.R.string.cancel, null); } AlertDialog dialog = builder.create(); dialog.show(); if(nameInput.getVisibility() == View.VISIBLE) { final Button ok = dialog.getButton(AlertDialog.BUTTON_POSITIVE); ok.setEnabled(nameInput.getText().toString().trim().length() > 0 && keywordInput.getText().toString().trim().length() > 0); final TextWatcher textWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void afterTextChanged(Editable s) { ok.setEnabled(nameInput.getText().toString().trim().length() > 0 && keywordInput.getText().toString().trim().length() > 0); } }; nameInput.addTextChangedListener(textWatcher); keywordInput.addTextChangedListener(textWatcher); } } private static final class AdapterCategory { int mIndex; Spannable mName; boolean mSelected; public AdapterCategory(int index, Spannable name, boolean selected) { mIndex = index; mName = name; mSelected = selected; } } private static final class CategoryViewHolder { CheckBox mCheckBox; TextView mTextView; } /** * View holder for custom cursor adapter of channel selection. * * @author René Mach */ private static final class ChannelViewHolder { CheckBox mCheckBox; TextView mTextView; ImageView mLogo; } private static boolean isRestricted(int[] channelIDs, int id) { boolean returnValue = channelIDs == null; if(!returnValue) { for(int channelID : channelIDs) { if(id == channelID) { returnValue = true; break; } } } return returnValue; } private static final class AdapterChannel { int mChannelID; String mName; Bitmap mChannelLogo; boolean mSelected; public AdapterChannel(int channelID, String name, Bitmap channelLogo, boolean selected) { mChannelID = channelID; mName = name; mChannelLogo = channelLogo; mSelected = selected; } } public static final int getThemeResourceId() { if(SettingConstants.IS_DARK_THEME) { return R.style.AppDarkTheme; } return R.style.AppTheme; } public static final ImageSpan createImageSpan(Context context, int drawable) { Drawable icon = ContextCompat.getDrawable(context, drawable); float zoom = 16f/icon.getIntrinsicHeight() * context.getResources().getDisplayMetrics().density; icon.setBounds(0, 0, (int)(icon.getIntrinsicWidth() * zoom), (int)(icon.getIntrinsicHeight() * zoom)); if(!SettingConstants.IS_DARK_THEME) { icon.setColorFilter(new PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.MULTIPLY)); } else { icon.setColorFilter(null); } return new ImageSpan(icon,ImageSpan.ALIGN_BASELINE); } private static final class ActorColumnSpan implements LeadingMarginSpan { private String[] mActorParts; private int mMargin; private int mFirstBaseline; private boolean mHighlight; private int mHighlightColor; public ActorColumnSpan(String actor, int leadingMargin, ArrayList<FavoriteTypePattern> patternList, int color) { mHighlight = false; for(FavoriteTypePattern pattern : patternList) { if(pattern.isNotOnlyTitleType() && pattern.mPattern.matcher(actor.replaceAll("\\n+", " ")).find()) { mHighlight = true; break; } } mActorParts = actor.split("\n"); mMargin = leadingMargin; mFirstBaseline = -1; mHighlightColor = color; } @Override public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) { if(first && (mFirstBaseline == -1 || mFirstBaseline == baseline)) { mFirstBaseline = baseline; if(mHighlight) { c.save(); c.clipRect(x+dir, top, x+dir+p.measureText(mActorParts[0]), bottom); c.drawColor(mHighlightColor); c.restore(); } c.drawText(mActorParts[0], x + dir, baseline, p); if(mActorParts.length > 1) { if(mHighlight) { c.save(); c.clipRect(x+dir, 2*bottom-top, x+dir+p.measureText(mActorParts[1]), bottom); c.drawColor(mHighlightColor); c.restore(); } c.drawText(mActorParts[1], x + dir, baseline + (bottom-top), p); } if(mActorParts.length > 2) { if(mHighlight) { c.save(); c.clipRect(x+dir, 3*bottom-top, x+dir+p.measureText(mActorParts[2]), bottom); c.drawColor(mHighlightColor); c.restore(); } c.drawText(mActorParts[2], x + dir, baseline + 2*(bottom-top), p); } } } @Override public int getLeadingMargin(boolean first) { return mMargin; } } private static void checkAndAddHiglightingForFavorites(TextView view, CharSequence text, ArrayList<FavoriteTypePattern> patternList, boolean allPatterns, BackgroundColorSpan backgroundColorSpan) { SpannableStringBuilder string = new SpannableStringBuilder(text); for(FavoriteTypePattern typePattern : patternList) { if(allPatterns || typePattern.isNotOnlyTitleType()) { string = typePattern.addHighlighting(string, backgroundColorSpan); } } view.setText(string); } public static final class FavoriteTypePattern { private int mType; private Pattern mPattern; public FavoriteTypePattern(int type, Pattern pattern) { mType = type; mPattern = pattern; } public SpannableStringBuilder addHighlighting(SpannableStringBuilder text, BackgroundColorSpan backgroundColorSpan) { final Matcher m = mPattern.matcher(text); int pos = 0; while(m.find(pos)) { text.setSpan(backgroundColorSpan, m.start(1), m.end(1), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); pos = m.end(); } return text; } public boolean isNotOnlyTitleType() { return mType != Favorite.KEYWORD_ONLY_TITLE_TYPE; } } }