/*
* TV-Browser for Android
* Copyright (C) 2013-2014 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.widgets;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import org.tvbrowser.content.TvBrowserContentProvider;
import org.tvbrowser.settings.SettingConstants;
import org.tvbrowser.tvbrowser.R;
import org.tvbrowser.utils.CompatUtils;
import org.tvbrowser.utils.IOUtils;
import org.tvbrowser.utils.PrefUtils;
import org.tvbrowser.utils.UiUtils;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
/**
* Service for showing important programs as widget.
*
* @author René Mach
*/
public class ImportantProgramsRemoteViewsService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new ImportantProgramsRemoteViewsFactory(getApplicationContext(),intent.getExtras());
}
class ImportantProgramsRemoteViewsFactory implements RemoteViewsFactory {
private Context mContext;
private Cursor mCursor;
private HashMap<String, Integer> mMarkingColumsIndexMap;
private PendingIntent mPendingUpdate;
private PendingIntent mPendingRunning;
private int mAppWidgetId;
private int mIdIndex;
private int mStartTimeIndex;
private int mEndTimeIndex;
private int mTitleIndex;
private int mChannelNameIndex;
private int mOrderNumberIndex;
private int mLogoIndex;
private int mEpisodeIndex;
private int mCategoryIndex;
private int mMarkingPluginsIndex;
private int mMarkingFavoriteIndex;
private int mMarkingReminderIndex;
private int mMarkingFavoriteReminderIndex;
private int mMarkingSyncIndex;
private int mVerticalPadding;
private boolean mShowChannelName;
private boolean mShowChannelLogo;
private boolean mShowBigChannelLogo;
private boolean mShowEpisode;
private boolean mShowCategories;
private boolean mShowMarkings;
private boolean mShowOrderNumber;
private boolean mChannelClickToProgramsList;
private float mTextScale;
private int[] mUserDefindedColorChannel;
private int[] mUserDefindedColorDate;
private int[] mUserDefindedColorTime;
private int[] mUserDefindedColorTitel;
private int[] mUserDefindedColorCategoryDefault;
private int[] mUserDefindedColorEpisode;
private void executeQuery() {
IOUtils.close(mCursor);
cancelAlarms();
if(IOUtils.isDatabaseAccessible(mContext)) {
final String[] projection = new String[] {
TvBrowserContentProvider.KEY_ID,
TvBrowserContentProvider.DATA_KEY_STARTTIME,
TvBrowserContentProvider.DATA_KEY_ENDTIME,
TvBrowserContentProvider.DATA_KEY_TITLE,
TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE,
TvBrowserContentProvider.DATA_KEY_MARKING_MARKING,
TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE,
TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER,
TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER,
TvBrowserContentProvider.DATA_KEY_MARKING_SYNC,
TvBrowserContentProvider.DATA_KEY_CATEGORIES,
TvBrowserContentProvider.CHANNEL_KEY_NAME,
TvBrowserContentProvider.CHANNEL_KEY_LOGO,
TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER,
TvBrowserContentProvider.CHANNEL_KEY_CHANNEL_ID
};
String where = "";
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mContext);
ArrayList<String> columns = new ArrayList<String>();
String channels = pref.getString(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_PROGRAM_LIST_CHANNELS), "");
int type = pref.getInt(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_IMPORTANT_TYPE), 0);
if(pref.getBoolean(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_IMPORTANT_SHOWN_MARKED), true)) {
columns.add(TvBrowserContentProvider.DATA_KEY_MARKING_MARKING);
}
if(pref.getBoolean(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_IMPORTANT_SHOWN_FAVORITE), true)) {
columns.add(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE);
}
if(pref.getBoolean(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_IMPORTANT_SHOWN_REMINDER), true)) {
columns.add(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER);
columns.add(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER);
}
if(pref.getBoolean(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_IMPORTANT_SHOWN_SYNCHRONIZED), true)) {
columns.add(TvBrowserContentProvider.DATA_KEY_MARKING_SYNC);
}
if(!columns.isEmpty()) {
where = " ( " + TvBrowserContentProvider.DATA_KEY_ENDTIME + ">=" + System.currentTimeMillis() + " ) AND ( " + TextUtils.join(" OR ", columns) + " ) AND NOT " + TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE;
}
else {
where += TvBrowserContentProvider.DATA_KEY_ENDTIME + "<0 ";
}
mUserDefindedColorChannel = IOUtils.getActivatedColorFor(pref.getString(mContext.getString(R.string.PREF_WIDGET_COLOR_CHANNEL), null));
mUserDefindedColorDate = IOUtils.getActivatedColorFor(pref.getString(mContext.getString(R.string.PREF_WIDGET_COLOR_DATE), null));
mUserDefindedColorTime = IOUtils.getActivatedColorFor(pref.getString(mContext.getString(R.string.PREF_WIDGET_COLOR_TIME), mContext.getString(R.string.pref_widget_color_time_default)));
mUserDefindedColorTitel = IOUtils.getActivatedColorFor(pref.getString(mContext.getString(R.string.PREF_WIDGET_COLOR_TITLE), mContext.getString(R.string.pref_widget_color_title_default)));
mUserDefindedColorCategoryDefault = IOUtils.getActivatedColorFor(pref.getString(mContext.getString(R.string.PREF_WIDGET_COLOR_CATEGORY), null));
mUserDefindedColorEpisode = IOUtils.getActivatedColorFor(pref.getString(mContext.getString(R.string.PREF_WIDGET_COLOR_EPISODE), null));
String limit = "";
if(pref.getBoolean(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_IMPORTANT_LIMIT), false)) {
limit = " LIMIT " + String.valueOf(pref.getInt(mAppWidgetId+"_"+mContext.getString(R.string.WIDGET_CONFIG_IMPORTANT_LIMIT_COUNT), 15));
}
if(type == 1) {
where = TvBrowserContentProvider.DATA_KEY_ENDTIME + ">=" + System.currentTimeMillis();
if(channels.trim().length() > 0) {
where += " AND " + TvBrowserContentProvider.CHANNEL_KEY_CHANNEL_ID + " IN ( " + channels + " ) ";
}
where += " AND NOT " + TvBrowserContentProvider.DATA_KEY_DONT_WANT_TO_SEE;
}
final long token = Binder.clearCallingIdentity();
try {
if(IOUtils.isDatabaseAccessible(getApplicationContext())) {
mCursor = getApplicationContext().getContentResolver().query(TvBrowserContentProvider.CONTENT_URI_DATA_WITH_CHANNEL, projection, where, null, TvBrowserContentProvider.DATA_KEY_STARTTIME + ", " + TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER + limit);
if(mCursor != null) {
mMarkingColumsIndexMap = new HashMap<String, Integer>();
mIdIndex = mCursor.getColumnIndex(TvBrowserContentProvider.KEY_ID);
mStartTimeIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_STARTTIME);
mEndTimeIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_ENDTIME);
mTitleIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_TITLE);
mChannelNameIndex = mCursor.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_NAME);
mOrderNumberIndex = mCursor.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_ORDER_NUMBER);
mLogoIndex = mCursor.getColumnIndex(TvBrowserContentProvider.CHANNEL_KEY_CHANNEL_ID);
mEpisodeIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_EPISODE_TITLE);
mCategoryIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_CATEGORIES);
mMarkingPluginsIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_MARKING);
mMarkingFavoriteIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE);
mMarkingReminderIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_REMINDER);
mMarkingFavoriteReminderIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_FAVORITE_REMINDER);
mMarkingSyncIndex = mCursor.getColumnIndex(TvBrowserContentProvider.DATA_KEY_MARKING_SYNC);
final String logoNamePref = PrefUtils.getStringValue(R.string.PREF_WIDGET_CHANNEL_LOGO_NAME, R.string.pref_widget_channel_logo_name_default);
mShowEpisode = PrefUtils.getBooleanValue(R.string.PREF_WIDGET_SHOW_EPISODE, R.bool.pref_widget_show_episode_default);
mShowCategories = PrefUtils.getBooleanValue(R.string.PREF_WIDGET_SHOW_CATEGORIES, R.bool.pref_widget_show_categories_default);
mShowMarkings = PrefUtils.getBooleanValue(R.string.PREF_WIDGET_SHOW_MARKINGS, R.bool.pref_widget_show_markings_default);
mShowChannelName = (logoNamePref.equals("0") || logoNamePref.equals("2"));
mShowChannelLogo = (logoNamePref.equals("0") || logoNamePref.equals("1") || logoNamePref.equals("3"));
mShowBigChannelLogo = logoNamePref.equals("3");
mShowOrderNumber = PrefUtils.getBooleanValue(R.string.PREF_WIDGET_SHOW_SORT_NUMBER, R.bool.pref_widget_show_sort_number_default);
mChannelClickToProgramsList = PrefUtils.getBooleanValue(R.string.PREF_WIDGET_CLICK_TO_CHANNEL_TO_LIST, R.bool.pref_widget_click_to_channel_to_list_default);
mTextScale = Float.valueOf(PrefUtils.getStringValue(R.string.PREF_WIDGET_TEXT_SCALE, R.string.pref_widget_text_scale_default));
mVerticalPadding = UiUtils.convertDpToPixel((int)(Float.parseFloat(PrefUtils.getStringValue(R.string.PREF_WIDGET_VERTICAL_PADDING_SIZE, R.string.pref_widget_vertical_padding_size_default))/2),mContext.getResources());
for(String column : TvBrowserContentProvider.MARKING_COLUMNS) {
final int index = mCursor.getColumnIndex(column);
if(index >= 0) {
mMarkingColumsIndexMap.put(column, index);
}
}
if(mCursor.getCount() > 0 && mCursor.moveToFirst()) {
final long startTime = mCursor.getLong(mStartTimeIndex);
if(startTime > System.currentTimeMillis()) {
final AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
final Intent update = new Intent(SettingConstants.UPDATE_IMPORTANT_APP_WIDGET);
update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
final PendingIntent pending = PendingIntent.getBroadcast(mContext, (int)(startTime/60000), update, PendingIntent.FLAG_UPDATE_CURRENT);
CompatUtils.setAlarmExact(mContext, alarm, AlarmManager.RTC, startTime + 100, pending);
}
else {
startRunningAlarm();
}
}
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
public ImportantProgramsRemoteViewsFactory(Context context, Bundle extras) {
PrefUtils.initialize(context);
mContext = context;
SettingConstants.initializeLogoMap(context, false);
if(extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);
}
else {
mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
}
}
@Override
public void onCreate() {
mTextScale = 1.0f;
executeQuery();
}
@Override
public void onDataSetChanged() {
executeQuery();
}
@Override
public int getCount() {
if(mCursor != null && !mCursor.isClosed()) {
return mCursor.getCount();
}
return 0;
}
@Override
public long getItemId(int position) {
if(mCursor != null && !mCursor.isClosed() && mCursor.moveToPosition(position)) {
return mCursor.getLong(mIdIndex);
}
return position;
}
@Override
public RemoteViews getViewAt(int position) {
final RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.important_programs_widget_row);
if(mCursor != null && !mCursor.isClosed() && mCursor.getCount() > position) {
mCursor.moveToPosition(position);
final String id = mCursor.getString(mIdIndex);
final long startTime = mCursor.getLong(mStartTimeIndex);
final long endTime = mCursor.getLong(mEndTimeIndex);
final CharSequence title = WidgetUtils.getColoredString(mCursor.getString(mTitleIndex),mUserDefindedColorTitel);
CharSequence name = mCursor.getString(mChannelNameIndex);
final String shortName = SettingConstants.SHORT_CHANNEL_NAMES.get(name);
String number = null;
final CharSequence episodeTitle = (mShowEpisode && !mCursor.isNull(mEpisodeIndex)) ? WidgetUtils.getColoredString(mCursor.getString(mEpisodeIndex),mUserDefindedColorEpisode) : null;
Spannable categorySpan = (mShowCategories && !mCursor.isNull(mCategoryIndex)) ? IOUtils.getInfoString(mCursor.getInt(mCategoryIndex), getResources(), true, mUserDefindedColorCategoryDefault[0] == 1 ? mUserDefindedColorCategoryDefault[1] : null) : null;
Spannable marking = WidgetUtils.getMarkings(mContext, mCursor, mShowMarkings, mMarkingPluginsIndex, mMarkingFavoriteIndex, mMarkingReminderIndex, mMarkingFavoriteReminderIndex, mMarkingSyncIndex);
if(shortName != null) {
name = shortName;
}
if(mShowOrderNumber) {
number = mCursor.getString(mOrderNumberIndex);
if(number == null) {
number = "0";
}
number += ".";
name = number + " " + name;
}
Drawable logo = null;
int channelKey = mCursor.getInt(mLogoIndex);
if(mShowChannelLogo) {
if(mShowBigChannelLogo) {
logo = SettingConstants.MEDIUM_LOGO_MAP.get(channelKey);
}
else {
logo = SettingConstants.SMALL_LOGO_MAP.get(channelKey);
}
}
CompatUtils.setRemoteViewsPadding(rv, R.id.important_programs_widget_row, 0, mVerticalPadding, 0, mVerticalPadding);
final CharSequence date = WidgetUtils.getColoredString(UiUtils.formatDate(startTime, mContext, false, true, true),mUserDefindedColorDate);
final CharSequence time = WidgetUtils.getColoredString(DateFormat.getTimeFormat(mContext).format(new Date(startTime)),mUserDefindedColorTime);
if(startTime <= System.currentTimeMillis() && endTime > System.currentTimeMillis()) {
final int length = (int)(endTime - startTime) / 60000;
final int progress = (int)(System.currentTimeMillis() - startTime) / 60000;
rv.setProgressBar(R.id.important_programs_widget_row_progress, length, progress, false);
rv.setViewVisibility(R.id.important_programs_widget_row_progress, View.VISIBLE);
rv.setViewVisibility(R.id.important_programs_widget_row_start_date1, View.GONE);
}
else {
rv.setTextViewText(R.id.important_programs_widget_row_start_date1, date);
rv.setViewVisibility(R.id.important_programs_widget_row_progress, View.GONE);
rv.setViewVisibility(R.id.important_programs_widget_row_start_date1, View.VISIBLE);
}
rv.setTextViewText(R.id.important_programs_widget_row_start_time1, time);
rv.setTextViewText(R.id.important_programs_widget_row_title1, marking != null ? TextUtils.concat(title,marking) : title);
if(categorySpan != null && categorySpan.toString().trim().length() > 0) {
rv.setViewVisibility(R.id.important_programs_widget_row_categories, View.VISIBLE);
rv.setTextViewText(R.id.important_programs_widget_row_categories, categorySpan);
}
else {
rv.setViewVisibility(R.id.important_programs_widget_row_categories, View.GONE);
}
if(mShowChannelName || logo == null) {
name = WidgetUtils.getColoredString(name, mUserDefindedColorChannel);
rv.setTextViewText(R.id.important_programs_widget_row_channel_name1, name);
rv.setViewVisibility(R.id.important_programs_widget_row_channel_name1, View.VISIBLE);
}
else if(number != null) {
rv.setTextViewText(R.id.important_programs_widget_row_channel_name1, number);
rv.setViewVisibility(R.id.important_programs_widget_row_channel_name1, View.VISIBLE);
}
else {
rv.setViewVisibility(R.id.important_programs_widget_row_channel_name1, View.GONE);
}
ArrayList<String> markedColumns = new ArrayList<String>();
for(String column : TvBrowserContentProvider.MARKING_COLUMNS) {
final Integer index = mMarkingColumsIndexMap.get(column);
if(index != null && mCursor.getInt(index) == 1) {
markedColumns.add(column);
}
}
if(logo != null && ((BitmapDrawable)logo).getBitmap() != null) {
rv.setImageViewBitmap(R.id.important_programs_widget_row_channel_logo1, ((BitmapDrawable)logo).getBitmap());
rv.setViewVisibility(R.id.important_programs_widget_row_channel_logo1, View.VISIBLE);
}
else {
rv.setViewVisibility(R.id.important_programs_widget_row_channel_logo1, View.GONE);
}
if(episodeTitle != null) {
rv.setTextViewText(R.id.important_programs_widget_row_episode, episodeTitle);
rv.setViewVisibility(R.id.important_programs_widget_row_episode, View.VISIBLE);
}
else {
rv.setViewVisibility(R.id.important_programs_widget_row_episode, View.GONE);
}
if(!CompatUtils.isKeyguardWidget(mAppWidgetId, mContext)) {
final Intent fillInIntent = new Intent();
fillInIntent.putExtra(SettingConstants.REMINDER_PROGRAM_ID_EXTRA, Long.valueOf(id));
rv.setOnClickFillInIntent(R.id.important_programs_widget_row_program, fillInIntent);
if(mChannelClickToProgramsList) {
final Intent startTvbProgramList = new Intent();
startTvbProgramList.putExtra(SettingConstants.CHANNEL_ID_EXTRA, channelKey);
startTvbProgramList.putExtra(SettingConstants.EXTRA_START_TIME, startTime);
startTvbProgramList.putExtra(SettingConstants.EXTRA_END_TIME, endTime);
rv.setOnClickFillInIntent(R.id.important_programs_widget_row_channel, startTvbProgramList);
}
}
}
else {
rv.setTextViewText(R.id.important_programs_widget_row_title1, "Unknown");
rv.setViewVisibility(R.id.important_programs_widget_row_start_time1, View.GONE);
rv.setViewVisibility(R.id.important_programs_widget_row_episode, View.GONE);
rv.setViewVisibility(R.id.important_programs_widget_row_progress, View.GONE);
rv.setViewVisibility(R.id.important_programs_widget_row_channel, View.GONE);
}
float titleFontSize = mTextScale * UiUtils.convertPixelsToSp(mContext.getResources().getDimension(R.dimen.title_font_size),mContext);
rv.setFloat(R.id.important_programs_widget_row_channel_name1, "setTextSize", titleFontSize);
rv.setFloat(R.id.important_programs_widget_row_start_date1, "setTextSize", titleFontSize);
rv.setFloat(R.id.important_programs_widget_row_title1, "setTextSize", titleFontSize);
rv.setFloat(R.id.important_programs_widget_row_start_time1, "setTextSize", titleFontSize);
rv.setFloat(R.id.important_programs_widget_row_categories, "setTextSize", mTextScale * UiUtils.convertPixelsToSp(mContext.getResources().getDimension(R.dimen.prog_list_categories_font_size),mContext));
rv.setFloat(R.id.important_programs_widget_row_episode, "setTextSize", mTextScale * UiUtils.convertPixelsToSp(mContext.getResources().getDimension(R.dimen.episode_font_size),mContext));
return rv;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public void onDestroy() {
IOUtils.close(mCursor);
cancelAlarms();
}
private void cancelAlarms() {
if(mPendingUpdate != null) {
((AlarmManager)getSystemService(ALARM_SERVICE)).cancel(mPendingUpdate);
mPendingUpdate = null;
}
if(mPendingRunning != null) {
((AlarmManager)getSystemService(ALARM_SERVICE)).cancel(mPendingRunning);
mPendingRunning = null;
}
}
private void startRunningAlarm() {
if(mPendingRunning == null) {
final Intent update = new Intent(SettingConstants.UPDATE_IMPORTANT_APP_WIDGET);
update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
mPendingRunning = PendingIntent.getBroadcast(mContext, (int)mAppWidgetId, update, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC, ((System.currentTimeMillis()/60000) * 60000) + 60100, 60000, mPendingRunning);
}
}
}
}