summaryrefslogtreecommitdiff
path: root/vdrmanager/app/src/main/java
diff options
context:
space:
mode:
authorlado <herrlado@gmail.com>2015-06-09 14:07:14 +0200
committerlado <herrlado@gmail.com>2015-06-09 14:07:14 +0200
commit23d920c759303602c2a6cde38983b2aa4dbb82c2 (patch)
treed0d83c36e0698402c783ebf62e6fe7d8ee85bed2 /vdrmanager/app/src/main/java
parentbe5e0132d51a829326478c668e4eac78e2c9e4d8 (diff)
downloadvdr-manager-23d920c759303602c2a6cde38983b2aa4dbb82c2.tar.gz
vdr-manager-23d920c759303602c2a6cde38983b2aa4dbb82c2.tar.bz2
StudioStructur
Diffstat (limited to 'vdrmanager/app/src/main/java')
-rw-r--r--vdrmanager/app/src/main/java/com/viewpagerindicator/CirclePageIndicator.java437
-rw-r--r--vdrmanager/app/src/main/java/com/viewpagerindicator/PageIndicator.java58
-rw-r--r--vdrmanager/app/src/main/java/com/viewpagerindicator/TitlePageIndicator.java672
-rw-r--r--vdrmanager/app/src/main/java/com/viewpagerindicator/TitleProvider.java28
-rw-r--r--vdrmanager/app/src/main/java/de/androvdr/widget/AnimatedTextView.java101
-rw-r--r--vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesome.java25
-rw-r--r--vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesomeButton.java36
-rw-r--r--vdrmanager/app/src/main/java/de/androvdr/widget/LruCache.java396
-rw-r--r--vdrmanager/app/src/main/java/de/androvdr/widget/SquareButton.java35
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/QueuedWork.java101
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/StringUtils.java180
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/VdrSharedPreferencesImpl.java527
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/ZonePicker.java336
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/C.java15
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/Intents.java19
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/VdrManagerApp.java159
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/AbstractSettingsActivity.java70
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api10Adapter.java30
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api11Adapter.java112
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api14Adapter.java73
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api7Adapter.java107
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8Adapter.java32
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8BackupPreferencesListener.java43
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api9Adapter.java58
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapter.java152
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapterFactory.java56
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupActivity.java89
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupAsyncTask.java110
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupPreferencesListener.java27
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupSettingsActivity.java121
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Constants.java135
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContentTypeIds.java33
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContextualActionModeCallback.java34
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/DialogUtils.java101
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ExternalFileBackup.java277
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/FileUtils.java199
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IOUtils.java217
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IntentUtils.java47
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/LineIterator.java5
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/PreferenceBackupHelper.java172
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreActivity.java114
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreAsyncTask.java136
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreChooserActivity.java127
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AliveState.java36
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AudioTrack.java61
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Channel.java176
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Epg.java82
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgCache.java12
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchParams.java45
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValue.java56
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValues.java66
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Event.java156
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventContentGroup.java23
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventFormatter.java66
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventListItem.java164
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/FetchEditTextPreference.java94
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/HasAudio.java5
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/MenuActionHandler.java16
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/OrmLiteBasePreferenceActivity.java89
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/P.java11
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Preferences.java783
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecentChannelDAO.java86
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecenteChannel.java59
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Recording.java257
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecordingListItem.java45
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timer.java276
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/TimerMatch.java8
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timerable.java22
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Vdr.java732
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/VdrSharedPreferences.java218
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/WakeupState.java36
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/DBAccess.java241
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/EPGSearchSuggestionsProvider.java23
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/About.java35
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseActivity.java651
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventAdapter.java365
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventListActivity.java576
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BasePreferencesActivity.java203
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseTimerEditActivity.java244
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/CertificateProblemDialog.java105
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelAdapter.java258
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelEventAdapter.java22
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelHolder.java10
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListActivity.java637
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListFragment.java8
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ColoredButton.java92
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgDetailsActivity.java768
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchListActivity.java290
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchTimesListActivity.java147
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventEpgListActivity.java489
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventListItemHolder.java19
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/PreferencesActivity.java144
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingAdapter.java137
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingDir.java71
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingListActivity.java568
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SimpleGestureFilter.java170
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SvdrpProgressDialog.java69
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEpgListActivity.java428
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEventAdapter.java19
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerDetailsActivity.java578
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerEventAdapter.java31
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerListActivity.java291
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/Utils.java772
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrListActivity.java287
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrManagerActivity.java341
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrPreferencesActivity.java508
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/HITK.java77
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/RemoteActivity.java395
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/AsyncProgressTask.java63
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ChannelsTask.java11
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/CreateTimerTask.java20
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteRecordingTask.java18
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteTimerTask.java20
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ModifyTimerTask.java24
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ToggleTimerTask.java27
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/VoidAsyncTask.java7
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/date/DateFormatter.java49
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/http/HttpHelper.java263
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/AliveClient.java47
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/CertificateProblemListener.java39
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/ChannelClient.java184
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/DelRecordingClient.java49
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/EpgClient.java92
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/MySSLSocketFactory.java253
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/RecordingClient.java31
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SetTimerClient.java90
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpAsyncTask.java165
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java533
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpEvent.java24
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpException.java35
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpExceptionListener.java5
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpFinishedListener.java9
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpListener.java9
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpResultListener.java6
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpStartListener.java15
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SwitchChannelClient.java57
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/TimerClient.java47
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/AsyncWakeupTask.java114
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeOnLanClient.java140
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgress.java26
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgressType.java8
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupUrlClient.java53
-rw-r--r--vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/Wakeuper.java20
-rw-r--r--vdrmanager/app/src/main/java/org/fueri/reeldroid/network/DeviceManager.java150
144 files changed, 22254 insertions, 0 deletions
diff --git a/vdrmanager/app/src/main/java/com/viewpagerindicator/CirclePageIndicator.java b/vdrmanager/app/src/main/java/com/viewpagerindicator/CirclePageIndicator.java
new file mode 100644
index 0000000..901d45f
--- /dev/null
+++ b/vdrmanager/app/src/main/java/com/viewpagerindicator/CirclePageIndicator.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2011 Patrik Akerfeldt
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import de.bjusystems.vdrmanager.R;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Draws circles (one for each view). The current view position is filled and
+ * others are only stroked.
+ */
+public class CirclePageIndicator extends View implements PageIndicator {
+ public static final int HORIZONTAL = 0;
+ public static final int VERTICAL = 1;
+
+ private float mRadius;
+ private final Paint mPaintStroke;
+ private final Paint mPaintFill;
+ private ViewPager mViewPager;
+ private ViewPager.OnPageChangeListener mListener;
+ private int mCurrentPage;
+ private int mSnapPage;
+ private int mCurrentOffset;
+ private int mScrollState;
+ private int mPageSize;
+ private int mOrientation;
+ private boolean mCentered;
+ private boolean mSnap;
+
+
+ public CirclePageIndicator(Context context) {
+ this(context, null);
+ }
+
+ public CirclePageIndicator(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.circlePageIndicatorStyle);
+ }
+
+ public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ //Load defaults from resources
+ final Resources res = getResources();
+ final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
+ final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);
+ final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color);
+ final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width);
+ final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);
+ final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered);
+ final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap);
+
+ //Retrieve styles attributes
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, R.style.Widget_CirclePageIndicator);
+
+ mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered);
+ mOrientation = a.getInt(R.styleable.CirclePageIndicator_orientation, defaultOrientation);
+ mPaintStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaintStroke.setStyle(Style.STROKE);
+ mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor));
+ mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth));
+ mPaintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaintFill.setStyle(Style.FILL);
+ mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor));
+ mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);
+ mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap);
+
+ a.recycle();
+ }
+
+
+ public void setCentered(boolean centered) {
+ mCentered = centered;
+ invalidate();
+ }
+
+ public boolean isCentered() {
+ return mCentered;
+ }
+
+ public void setFillColor(int fillColor) {
+ mPaintFill.setColor(fillColor);
+ invalidate();
+ }
+
+ public int getFillColor() {
+ return mPaintFill.getColor();
+ }
+
+ public void setOrientation(int orientation) {
+ switch (orientation) {
+ case HORIZONTAL:
+ case VERTICAL:
+ mOrientation = orientation;
+ updatePageSize();
+ requestLayout();
+ break;
+
+ default:
+ throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL.");
+ }
+ }
+
+ public int getOrientation() {
+ return mOrientation;
+ }
+
+ public void setStrokeColor(int strokeColor) {
+ mPaintStroke.setColor(strokeColor);
+ invalidate();
+ }
+
+ public int getStrokeColor() {
+ return mPaintStroke.getColor();
+ }
+
+ public void setStrokeWidth(float strokeWidth) {
+ mPaintStroke.setStrokeWidth(strokeWidth);
+ invalidate();
+ }
+
+ public float getStrokeWidth() {
+ return mPaintStroke.getStrokeWidth();
+ }
+
+ public void setRadius(float radius) {
+ mRadius = radius;
+ invalidate();
+ }
+
+ public float getRadius() {
+ return mRadius;
+ }
+
+ public void setSnap(boolean snap) {
+ mSnap = snap;
+ invalidate();
+ }
+
+ public boolean isSnap() {
+ return mSnap;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.view.View#onDraw(android.graphics.Canvas)
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int longSize;
+ int longPaddingBefore;
+ int longPaddingAfter;
+ int shortPaddingBefore;
+ if (mOrientation == HORIZONTAL) {
+ longSize = getWidth();
+ longPaddingBefore = getPaddingLeft();
+ longPaddingAfter = getPaddingRight();
+ shortPaddingBefore = getPaddingTop();
+ } else {
+ longSize = getHeight();
+ longPaddingBefore = getPaddingTop();
+ longPaddingAfter = getPaddingBottom();
+ shortPaddingBefore = getPaddingLeft();
+ }
+
+ final int count = mViewPager.getAdapter().getCount();
+ final float threeRadius = mRadius * 3;
+ final float shortOffset = shortPaddingBefore + mRadius;
+ float longOffset = longPaddingBefore + mRadius;
+ if (mCentered) {
+ longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f);
+ }
+
+ float dX;
+ float dY;
+
+ //Draw stroked circles
+ for (int iLoop = 0; iLoop < count; iLoop++) {
+ float drawLong = longOffset + (iLoop * threeRadius);
+ if (mOrientation == HORIZONTAL) {
+ dX = drawLong;
+ dY = shortOffset;
+ } else {
+ dX = shortOffset;
+ dY = drawLong;
+ }
+ canvas.drawCircle(dX, dY, mRadius, mPaintStroke);
+ }
+
+ //Draw the filled circle according to the current scroll
+ float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius;
+ if (!mSnap && (mPageSize != 0)) {
+ cx += (mCurrentOffset * 1.0f / mPageSize) * threeRadius;
+ }
+ if (mOrientation == HORIZONTAL) {
+ dX = longOffset + cx;
+ dY = shortOffset;
+ } else {
+ dX = shortOffset;
+ dY = longOffset + cx;
+ }
+ canvas.drawCircle(dX, dY, mRadius, mPaintFill);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ final int count = mViewPager.getAdapter().getCount();
+ final int longSize = (mOrientation == HORIZONTAL) ? getWidth() : getHeight();
+ final float halfLongSize = longSize / 2;
+ final float halfCircleLongSize = (count * 3 * mRadius) / 2;
+ final float pointerValue = (mOrientation == HORIZONTAL) ? event.getX() : event.getY();
+
+ if ((mCurrentPage > 0) && (pointerValue < halfLongSize - halfCircleLongSize)) {
+ setCurrentItem(mCurrentPage - 1);
+ return true;
+ } else if ((mCurrentPage < count - 1) && (pointerValue > halfLongSize + halfCircleLongSize)) {
+ setCurrentItem(mCurrentPage + 1);
+ return true;
+ }
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ public void setViewPager(ViewPager view) {
+ if (view.getAdapter() == null) {
+ throw new IllegalStateException("ViewPager does not have adapter instance.");
+ }
+ mViewPager = view;
+ mViewPager.setOnPageChangeListener(this);
+ updatePageSize();
+ invalidate();
+ }
+
+ private void updatePageSize() {
+ if (mViewPager != null) {
+ mPageSize = (mOrientation == HORIZONTAL) ? mViewPager.getWidth() : mViewPager.getHeight();
+ }
+ }
+
+ public void setViewPager(ViewPager view, int initialPosition) {
+ setViewPager(view);
+ setCurrentItem(initialPosition);
+ }
+
+
+ public void setCurrentItem(int item) {
+ if (mViewPager == null) {
+ throw new IllegalStateException("ViewPager has not been bound.");
+ }
+ mViewPager.setCurrentItem(item);
+ mCurrentPage = item;
+ invalidate();
+ }
+
+
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+
+ if (mListener != null) {
+ mListener.onPageScrollStateChanged(state);
+ }
+ }
+
+
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ mCurrentPage = position;
+ mCurrentOffset = positionOffsetPixels;
+ updatePageSize();
+ invalidate();
+
+ if (mListener != null) {
+ mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+ }
+ }
+
+
+ public void onPageSelected(int position) {
+ if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+ mCurrentPage = position;
+ mSnapPage = position;
+ invalidate();
+ }
+
+ if (mListener != null) {
+ mListener.onPageSelected(position);
+ }
+ }
+
+
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ mListener = listener;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.view.View#onMeasure(int, int)
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mOrientation == HORIZONTAL) {
+ setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));
+ } else {
+ setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));
+ }
+ }
+
+ /**
+ * Determines the width of this view
+ *
+ * @param measureSpec
+ * A measureSpec packed into an int
+ * @return The width of the view, honoring constraints from measureSpec
+ */
+ private int measureLong(int measureSpec) {
+ int result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode == MeasureSpec.EXACTLY) {
+ //We were told how big to be
+ result = specSize;
+ } else {
+ //Calculate the width according the views count
+ final int count = mViewPager.getAdapter().getCount();
+ result = (int)(getPaddingLeft() + getPaddingRight()
+ + (count * 2 * mRadius) + (count - 1) * mRadius + 1);
+ //Respect AT_MOST value if that was what is called for by measureSpec
+ if (specMode == MeasureSpec.AT_MOST) {
+ result = Math.min(result, specSize);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Determines the height of this view
+ *
+ * @param measureSpec
+ * A measureSpec packed into an int
+ * @return The height of the view, honoring constraints from measureSpec
+ */
+ private int measureShort(int measureSpec) {
+ int result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode == MeasureSpec.EXACTLY) {
+ //We were told how big to be
+ result = specSize;
+ } else {
+ //Measure the height
+ result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1);
+ //Respect AT_MOST value if that was what is called for by measureSpec
+ if (specMode == MeasureSpec.AT_MOST) {
+ result = Math.min(result, specSize);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState)state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ mCurrentPage = savedState.currentPage;
+ mSnapPage = savedState.currentPage;
+ requestLayout();
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState savedState = new SavedState(superState);
+ savedState.currentPage = mCurrentPage;
+ return savedState;
+ }
+
+ static class SavedState extends BaseSavedState {
+ int currentPage;
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ currentPage = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(currentPage);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/vdrmanager/app/src/main/java/com/viewpagerindicator/PageIndicator.java b/vdrmanager/app/src/main/java/com/viewpagerindicator/PageIndicator.java
new file mode 100644
index 0000000..64786d2
--- /dev/null
+++ b/vdrmanager/app/src/main/java/com/viewpagerindicator/PageIndicator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 Patrik Akerfeldt
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.viewpagerindicator;
+
+import android.support.v4.view.ViewPager;
+
+/**
+ * A PageIndicator is responsible to show an visual indicator on the total views
+ * number and the current visible view.
+ */
+public interface PageIndicator extends ViewPager.OnPageChangeListener {
+ /**
+ * Bind the indicator to a ViewPager.
+ *
+ * @param view
+ */
+ public void setViewPager(ViewPager view);
+
+ /**
+ * Bind the indicator to a ViewPager.
+ *
+ * @param view
+ * @param initialPosition
+ */
+ public void setViewPager(ViewPager view, int initialPosition);
+
+ /**
+ * <p>Set the current page of both the ViewPager and indicator.</p>
+ *
+ * <p>This <strong>must</strong> be used if you need to set the page before
+ * the views are drawn on screen (e.g., default start page).</p>
+ *
+ * @param item
+ */
+ public void setCurrentItem(int item);
+
+ /**
+ * Set a page change listener which will receive forwarded events.
+ *
+ * @param listener
+ */
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
+}
diff --git a/vdrmanager/app/src/main/java/com/viewpagerindicator/TitlePageIndicator.java b/vdrmanager/app/src/main/java/com/viewpagerindicator/TitlePageIndicator.java
new file mode 100644
index 0000000..c04f5ed
--- /dev/null
+++ b/vdrmanager/app/src/main/java/com/viewpagerindicator/TitlePageIndicator.java
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2011 Patrik Akerfeldt
+ * Copyright (C) 2011 Francisco Figueiredo Jr.
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+import java.util.ArrayList;
+
+import de.bjusystems.vdrmanager.R;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * A TitlePageIndicator is a PageIndicator which displays the title of left view
+ * (if exist), the title of the current select view (centered) and the title of
+ * the right view (if exist). When the user scrolls the ViewPager then titles are
+ * also scrolled.
+ */
+public class TitlePageIndicator extends View implements PageIndicator {
+ /**
+ * Percentage indicating what percentage of the screen width away from
+ * center should the underline be fully faded. A value of 0.25 means that
+ * halfway between the center of the screen and an edge.
+ */
+ private static final float SELECTION_FADE_PERCENTAGE = 0.25f;
+
+ /**
+ * Percentage indicating what percentage of the screen width away from
+ * center should the selected text bold turn off. A value of 0.05 means
+ * that 10% between the center and an edge.
+ */
+ private static final float BOLD_FADE_PERCENTAGE = 0.05f;
+
+ public enum IndicatorStyle {
+ None(0), Triangle(1), Underline(2);
+
+ public final int value;
+
+ private IndicatorStyle(int value) {
+ this.value = value;
+ }
+
+ public static IndicatorStyle fromValue(int value) {
+ for (IndicatorStyle style : IndicatorStyle.values()) {
+ if (style.value == value) {
+ return style;
+ }
+ }
+ return null;
+ }
+ }
+
+ private ViewPager mViewPager;
+ private ViewPager.OnPageChangeListener mListener;
+ private TitleProvider mTitleProvider;
+ private int mCurrentPage;
+ private int mCurrentOffset;
+ private int mScrollState;
+ private final Paint mPaintText;
+ private boolean mBoldText;
+ private int mColorText;
+ private int mColorSelected;
+ private Path mPath;
+ private final Paint mPaintFooterLine;
+ private IndicatorStyle mFooterIndicatorStyle;
+ private final Paint mPaintFooterIndicator;
+ private float mFooterIndicatorHeight;
+ private float mFooterIndicatorUnderlinePadding;
+ private float mFooterPadding;
+ private float mTitlePadding;
+ private float mTopPadding;
+ /** Left and right side padding for not active view titles. */
+ private float mClipPadding;
+ private float mFooterLineHeight;
+
+
+ public TitlePageIndicator(Context context) {
+ this(context, null);
+ }
+
+ public TitlePageIndicator(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.titlePageIndicatorStyle);
+ }
+
+ public TitlePageIndicator(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ //Load defaults from resources
+ final Resources res = getResources();
+ final int defaultFooterColor = res.getColor(R.color.default_title_indicator_footer_color);
+ final float defaultFooterLineHeight = res.getDimension(R.dimen.default_title_indicator_footer_line_height);
+ final int defaultFooterIndicatorStyle = res.getInteger(R.integer.default_title_indicator_footer_indicator_style);
+ final float defaultFooterIndicatorHeight = res.getDimension(R.dimen.default_title_indicator_footer_indicator_height);
+ final float defaultFooterIndicatorUnderlinePadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_underline_padding);
+ final float defaultFooterPadding = res.getDimension(R.dimen.default_title_indicator_footer_padding);
+ final int defaultSelectedColor = res.getColor(R.color.default_title_indicator_selected_color);
+ final boolean defaultSelectedBold = res.getBoolean(R.bool.default_title_indicator_selected_bold);
+ final int defaultTextColor = res.getColor(R.color.default_title_indicator_text_color);
+ final float defaultTextSize = res.getDimension(R.dimen.default_title_indicator_text_size);
+ final float defaultTitlePadding = res.getDimension(R.dimen.default_title_indicator_title_padding);
+ final float defaultClipPadding = res.getDimension(R.dimen.default_title_indicator_clip_padding);
+ final float defaultTopPadding = res.getDimension(R.dimen.default_title_indicator_top_padding);
+
+ //Retrieve styles attributes
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitlePageIndicator, defStyle, R.style.Widget_TitlePageIndicator);
+
+ //Retrieve the colors to be used for this view and apply them.
+ mFooterLineHeight = a.getDimension(R.styleable.TitlePageIndicator_footerLineHeight, defaultFooterLineHeight);
+ mFooterIndicatorStyle = IndicatorStyle.fromValue(a.getInteger(R.styleable.TitlePageIndicator_footerIndicatorStyle, defaultFooterIndicatorStyle));
+ mFooterIndicatorHeight = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorHeight, defaultFooterIndicatorHeight);
+ mFooterIndicatorUnderlinePadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorUnderlinePadding, defaultFooterIndicatorUnderlinePadding);
+ mFooterPadding = a.getDimension(R.styleable.TitlePageIndicator_footerPadding, defaultFooterPadding);
+ mTopPadding = a.getDimension(R.styleable.TitlePageIndicator_topPadding, defaultTopPadding);
+ mTitlePadding = a.getDimension(R.styleable.TitlePageIndicator_titlePadding, defaultTitlePadding);
+ mClipPadding = a.getDimension(R.styleable.TitlePageIndicator_clipPadding, defaultClipPadding);
+ mColorSelected = a.getColor(R.styleable.TitlePageIndicator_selectedColor, defaultSelectedColor);
+ mColorText = a.getColor(R.styleable.TitlePageIndicator_textColor, defaultTextColor);
+ mBoldText = a.getBoolean(R.styleable.TitlePageIndicator_selectedBold, defaultSelectedBold);
+
+ final float textSize = a.getDimension(R.styleable.TitlePageIndicator_textSize, defaultTextSize);
+ final int footerColor = a.getColor(R.styleable.TitlePageIndicator_footerColor, defaultFooterColor);
+ mPaintText = new Paint();
+ mPaintText.setTextSize(textSize);
+ mPaintText.setAntiAlias(true);
+ mPaintFooterLine = new Paint();
+ mPaintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE);
+ mPaintFooterLine.setStrokeWidth(mFooterLineHeight);
+ mPaintFooterLine.setColor(footerColor);
+ mPaintFooterIndicator = new Paint();
+ mPaintFooterIndicator.setStyle(Paint.Style.FILL_AND_STROKE);
+ mPaintFooterIndicator.setColor(footerColor);
+
+ a.recycle();
+ }
+
+
+ public int getFooterColor() {
+ return mPaintFooterLine.getColor();
+ }
+
+ public void setFooterColor(int footerColor) {
+ mPaintFooterLine.setColor(footerColor);
+ mPaintFooterIndicator.setColor(footerColor);
+ invalidate();
+ }
+
+ public float getFooterLineHeight() {
+ return mFooterLineHeight;
+ }
+
+ public void setFooterLineHeight(float footerLineHeight) {
+ mFooterLineHeight = footerLineHeight;
+ mPaintFooterLine.setStrokeWidth(mFooterLineHeight);
+ invalidate();
+ }
+
+ public float getFooterIndicatorHeight() {
+ return mFooterIndicatorHeight;
+ }
+
+ public void setFooterIndicatorHeight(float footerTriangleHeight) {
+ mFooterIndicatorHeight = footerTriangleHeight;
+ invalidate();
+ }
+
+ public float getFooterIndicatorPadding() {
+ return mFooterPadding;
+ }
+
+ public void setFooterIndicatorPadding(float footerIndicatorPadding) {
+ mFooterPadding = footerIndicatorPadding;
+ invalidate();
+ }
+
+ public IndicatorStyle getFooterIndicatorStyle() {
+ return mFooterIndicatorStyle;
+ }
+
+ public void setFooterIndicatorStyle(IndicatorStyle indicatorStyle) {
+ mFooterIndicatorStyle = indicatorStyle;
+ invalidate();
+ }
+
+ public int getSelectedColor() {
+ return mColorSelected;
+ }
+
+ public void setSelectedColor(int selectedColor) {
+ mColorSelected = selectedColor;
+ invalidate();
+ }
+
+ public boolean isSelectedBold() {
+ return mBoldText;
+ }
+
+ public void setSelectedBold(boolean selectedBold) {
+ mBoldText = selectedBold;
+ invalidate();
+ }
+
+ public int getTextColor() {
+ return mColorText;
+ }
+
+ public void setTextColor(int textColor) {
+ mPaintText.setColor(textColor);
+ mColorText = textColor;
+ invalidate();
+ }
+
+ public float getTextSize() {
+ return mPaintText.getTextSize();
+ }
+
+ public void setTextSize(float textSize) {
+ mPaintText.setTextSize(textSize);
+ invalidate();
+ }
+
+ public float getTitlePadding() {
+ return this.mTitlePadding;
+ }
+
+ public void setTitlePadding(float titlePadding) {
+ mTitlePadding = titlePadding;
+ invalidate();
+ }
+
+ public float getTopPadding() {
+ return this.mTopPadding;
+ }
+
+ public void setTopPadding(float topPadding) {
+ mTopPadding = topPadding;
+ invalidate();
+ }
+
+ public float getClipPadding() {
+ return this.mClipPadding;
+ }
+
+ public void setClipPadding(float clipPadding) {
+ mClipPadding = clipPadding;
+ invalidate();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.view.View#onDraw(android.graphics.Canvas)
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ //Calculate views bounds
+ ArrayList<RectF> bounds = calculateAllBounds(mPaintText);
+
+ final int count = mViewPager.getAdapter().getCount();
+ final int countMinusOne = count - 1;
+ final float halfWidth = getWidth() / 2f;
+ final int left = getLeft();
+ final float leftClip = left + mClipPadding;
+ final int width = getWidth();
+ final int height = getHeight();
+ final int right = left + width;
+ final float rightClip = right - mClipPadding;
+
+ int page = mCurrentPage;
+ float offsetPercent;
+ if (mCurrentOffset <= halfWidth) {
+ offsetPercent = 1.0f * mCurrentOffset / width;
+ } else {
+ page += 1;
+ offsetPercent = 1.0f * (width - mCurrentOffset) / width;
+ }
+ final boolean currentSelected = (offsetPercent <= SELECTION_FADE_PERCENTAGE);
+ final boolean currentBold = (offsetPercent <= BOLD_FADE_PERCENTAGE);
+ final float selectedPercent = (SELECTION_FADE_PERCENTAGE - offsetPercent) / SELECTION_FADE_PERCENTAGE;
+
+ //Verify if the current view must be clipped to the screen
+ RectF curPageBound = bounds.get(mCurrentPage);
+ float curPageWidth = curPageBound.right - curPageBound.left;
+ if (curPageBound.left < leftClip) {
+ //Try to clip to the screen (left side)
+ clipViewOnTheLeft(curPageBound, curPageWidth, left);
+ }
+ if (curPageBound.right > rightClip) {
+ //Try to clip to the screen (right side)
+ clipViewOnTheRight(curPageBound, curPageWidth, right);
+ }
+
+ //Left views starting from the current position
+ if (mCurrentPage > 0) {
+ for (int i = mCurrentPage - 1; i >= 0; i--) {
+ RectF bound = bounds.get(i);
+ //Is left side is outside the screen
+ if (bound.left < leftClip) {
+ float w = bound.right - bound.left;
+ //Try to clip to the screen (left side)
+ clipViewOnTheLeft(bound, w, left);
+ //Except if there's an intersection with the right view
+ RectF rightBound = bounds.get(i + 1);
+ //Intersection
+ if (bound.right + mTitlePadding > rightBound.left) {
+ bound.left = rightBound.left - w - mTitlePadding;
+ bound.right = bound.left + w;
+ }
+ }
+ }
+ }
+ //Right views starting from the current position
+ if (mCurrentPage < countMinusOne) {
+ for (int i = mCurrentPage + 1 ; i < count; i++) {
+ RectF bound = bounds.get(i);
+ //If right side is outside the screen
+ if (bound.right > rightClip) {
+ float w = bound.right - bound.left;
+ //Try to clip to the screen (right side)
+ clipViewOnTheRight(bound, w, right);
+ //Except if there's an intersection with the left view
+ RectF leftBound = bounds.get(i - 1);
+ //Intersection
+ if (bound.left - mTitlePadding < leftBound.right) {
+ bound.left = leftBound.right + mTitlePadding;
+ bound.right = bound.left + w;
+ }
+ }
+ }
+ }
+
+ //Now draw views
+ for (int i = 0; i < count; i++) {
+ //Get the title
+ RectF bound = bounds.get(i);
+ //Only if one side is visible
+ if ((bound.left > left && bound.left < right) || (bound.right > left && bound.right < right)) {
+ final boolean currentPage = (i == page);
+ //Only set bold if we are within bounds
+ mPaintText.setFakeBoldText(currentPage && currentBold && mBoldText);
+
+ //Draw text as unselected
+ mPaintText.setColor(mColorText);
+ canvas.drawText(mTitleProvider.getTitle(i), bound.left, bound.bottom + mTopPadding, mPaintText);
+
+ //If we are within the selected bounds draw the selected text
+ if (currentPage && currentSelected) {
+ mPaintText.setColor(mColorSelected);
+ mPaintText.setAlpha((int)((mColorSelected >>> 24) * selectedPercent));
+ canvas.drawText(mTitleProvider.getTitle(i), bound.left, bound.bottom + mTopPadding, mPaintText);
+ }
+ }
+ }
+
+ //Draw the footer line
+ mPath = new Path();
+ mPath.moveTo(0, height - mFooterLineHeight / 2f);
+ mPath.lineTo(width, height - mFooterLineHeight / 2f);
+ mPath.close();
+ canvas.drawPath(mPath, mPaintFooterLine);
+
+ switch (mFooterIndicatorStyle) {
+ case Triangle:
+ mPath = new Path();
+ mPath.moveTo(halfWidth, height - mFooterLineHeight - mFooterIndicatorHeight);
+ mPath.lineTo(halfWidth + mFooterIndicatorHeight, height - mFooterLineHeight);
+ mPath.lineTo(halfWidth - mFooterIndicatorHeight, height - mFooterLineHeight);
+ mPath.close();
+ canvas.drawPath(mPath, mPaintFooterIndicator);
+ break;
+
+ case Underline:
+ if (!currentSelected) {
+ break;
+ }
+
+ RectF underlineBounds = bounds.get(page);
+ mPath = new Path();
+ mPath.moveTo(underlineBounds.left - mFooterIndicatorUnderlinePadding, height - mFooterLineHeight);
+ mPath.lineTo(underlineBounds.right + mFooterIndicatorUnderlinePadding, height - mFooterLineHeight);
+ mPath.lineTo(underlineBounds.right + mFooterIndicatorUnderlinePadding, height - mFooterLineHeight - mFooterIndicatorHeight);
+ mPath.lineTo(underlineBounds.left - mFooterIndicatorUnderlinePadding, height - mFooterLineHeight - mFooterIndicatorHeight);
+ mPath.close();
+
+ mPaintFooterIndicator.setAlpha((int)(0xFF * selectedPercent));
+ canvas.drawPath(mPath, mPaintFooterIndicator);
+ mPaintFooterIndicator.setAlpha(0xFF);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ final int count = mViewPager.getAdapter().getCount();
+ final int width = getWidth();
+ final float halfWidth = width / 2f;
+ final float sixthWidth = width / 6f;
+
+ if ((mCurrentPage > 0) && (event.getX() < halfWidth - sixthWidth)) {
+ mViewPager.setCurrentItem(mCurrentPage - 1);
+ return true;
+ } else if ((mCurrentPage < count - 1) && (event.getX() > halfWidth + sixthWidth)) {
+ mViewPager.setCurrentItem(mCurrentPage + 1);
+ return true;
+ }
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ /**
+ * Set bounds for the right textView including clip padding.
+ *
+ * @param curViewBound
+ * current bounds.
+ * @param curViewWidth
+ * width of the view.
+ */
+ private void clipViewOnTheRight(RectF curViewBound, float curViewWidth, int right) {
+ curViewBound.right = right - mClipPadding;
+ curViewBound.left = curViewBound.right - curViewWidth;
+ }
+
+ /**
+ * Set bounds for the left textView including clip padding.
+ *
+ * @param curViewBound
+ * current bounds.
+ * @param curViewWidth
+ * width of the view.
+ */
+ private void clipViewOnTheLeft(RectF curViewBound, float curViewWidth, int left) {
+ curViewBound.left = left + mClipPadding;
+ curViewBound.right = mClipPadding + curViewWidth;
+ }
+
+ /**
+ * Calculate views bounds and scroll them according to the current index
+ *
+ * @param paint
+ * @param currentIndex
+ * @return
+ */
+ private ArrayList<RectF> calculateAllBounds(Paint paint) {
+ ArrayList<RectF> list = new ArrayList<RectF>();
+ //For each views (If no values then add a fake one)
+ final int count = mViewPager.getAdapter().getCount();
+ final int width = getWidth();
+ final int halfWidth = width / 2;
+ for (int i = 0; i < count; i++) {
+ RectF bounds = calcBounds(i, paint);
+ float w = (bounds.right - bounds.left);
+ float h = (bounds.bottom - bounds.top);
+ bounds.left = (halfWidth) - (w / 2) - mCurrentOffset + ((i - mCurrentPage) * width);
+ bounds.right = bounds.left + w;
+ bounds.top = 0;
+ bounds.bottom = h;
+ list.add(bounds);
+ }
+
+ return list;
+ }
+
+ /**
+ * Calculate the bounds for a view's title
+ *
+ * @param index
+ * @param paint
+ * @return
+ */
+ private RectF calcBounds(int index, Paint paint) {
+ //Calculate the text bounds
+ RectF bounds = new RectF();
+ bounds.right = paint.measureText(mTitleProvider.getTitle(index));
+ bounds.bottom = paint.descent() - paint.ascent();
+ return bounds;
+ }
+
+
+ public void setViewPager(ViewPager view) {
+ if (view.getAdapter() == null) {
+ throw new IllegalStateException("ViewPager does not have adapter instance.");
+ }
+ if (!(view.getAdapter() instanceof TitleProvider)) {
+ throw new IllegalStateException("ViewPager adapter must implement TitleProvider to be used with TitlePageIndicator.");
+ }
+ mViewPager = view;
+ mViewPager.setOnPageChangeListener(this);
+ mTitleProvider = (TitleProvider)mViewPager.getAdapter();
+ invalidate();
+ }
+
+
+ public void setViewPager(ViewPager view, int initialPosition) {
+ setViewPager(view);
+ setCurrentItem(initialPosition);
+ }
+
+
+ public void setCurrentItem(int item) {
+ if (mViewPager == null) {
+ throw new IllegalStateException("ViewPager has not been bound.");
+ }
+ mViewPager.setCurrentItem(item);
+ mCurrentPage = item;
+ invalidate();
+ }
+
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+
+ if (mListener != null) {
+ mListener.onPageScrollStateChanged(state);
+ }
+ }
+
+
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ mCurrentPage = position;
+ mCurrentOffset = positionOffsetPixels;
+ invalidate();
+
+ if (mListener != null) {
+ mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+ }
+ }
+
+
+ public void onPageSelected(int position) {
+ if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+ mCurrentPage = position;
+ invalidate();
+ }
+
+ if (mListener != null) {
+ mListener.onPageSelected(position);
+ }
+ }
+
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ mListener = listener;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.view.View#onMeasure(int, int)
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
+ }
+
+ /**
+ * Determines the width of this view
+ *
+ * @param measureSpec
+ * A measureSpec packed into an int
+ * @return The width of the view, honoring constraints from measureSpec
+ */
+ private int measureWidth(int measureSpec) {
+ int result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException(getClass().getSimpleName() + " can only be used in EXACTLY mode.");
+ }
+ result = specSize;
+ return result;
+ }
+
+ /**
+ * Determines the height of this view
+ *
+ * @param measureSpec
+ * A measureSpec packed into an int
+ * @return The height of the view, honoring constraints from measureSpec
+ */
+ private int measureHeight(int measureSpec) {
+ float result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode == MeasureSpec.EXACTLY) {
+ //We were told how big to be
+ result = specSize;
+ } else {
+ //Calculate the text bounds
+ RectF bounds = new RectF();
+ bounds.bottom = mPaintText.descent()-mPaintText.ascent();
+ result = bounds.bottom - bounds.top + mFooterLineHeight + mFooterPadding + mTopPadding;
+ if (mFooterIndicatorStyle != IndicatorStyle.None) {
+ result += mFooterIndicatorHeight;
+ }
+ }
+ return (int)result;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState)state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ mCurrentPage = savedState.currentPage;
+ requestLayout();
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState savedState = new SavedState(superState);
+ savedState.currentPage = mCurrentPage;
+ return savedState;
+ }
+
+ static class SavedState extends BaseSavedState {
+ int currentPage;
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ currentPage = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(currentPage);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/vdrmanager/app/src/main/java/com/viewpagerindicator/TitleProvider.java b/vdrmanager/app/src/main/java/com/viewpagerindicator/TitleProvider.java
new file mode 100644
index 0000000..2a04b65
--- /dev/null
+++ b/vdrmanager/app/src/main/java/com/viewpagerindicator/TitleProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 Patrik Akerfeldt
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.viewpagerindicator;
+
+/**
+ * A TitleProvider provides the title to display according to a view.
+ */
+public interface TitleProvider {
+ /**
+ * Returns the title of the view at position
+ * @param position
+ * @return
+ */
+ public String getTitle(int position);
+}
diff --git a/vdrmanager/app/src/main/java/de/androvdr/widget/AnimatedTextView.java b/vdrmanager/app/src/main/java/de/androvdr/widget/AnimatedTextView.java
new file mode 100644
index 0000000..b6c120b
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/androvdr/widget/AnimatedTextView.java
@@ -0,0 +1,101 @@
+package de.androvdr.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.TextView;
+
+/**
+ * Created by lado on 04.05.15.
+ */
+public class AnimatedTextView extends TextView {
+ private static final int IS_ANIMATING_TAG_ID = "isAnimating".hashCode();
+
+ private Animation fadeInAnimation;
+ private Animation fadeOutAnimation;
+
+ public AnimatedTextView(Context context) {
+ super(context);
+
+ initAnimations(context);
+ }
+
+ public AnimatedTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ initAnimations(context);
+ }
+
+ public AnimatedTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ initAnimations(context);
+ }
+
+ public void initAnimations(Context context) {
+ fadeInAnimation = AnimationUtils.loadAnimation(this.getContext(), android.R.anim.fade_in);
+
+ fadeOutAnimation = new AlphaAnimation(1.0f, 0.0f);
+ fadeOutAnimation.setDuration(100);
+ fadeInAnimation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ setAnimatingFlag(true);
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ setAnimatingFlag(false);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+ });
+
+ fadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ setAnimatingFlag(false);
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+
+ });
+
+ setAnimatingFlag(false);
+ }
+
+ public void fadeOut() {
+ if (getVisibility() == View.VISIBLE) {
+ startAnimation(fadeOutAnimation);
+ setVisibility(View.INVISIBLE);
+ }
+ }
+
+ public void fadeIn() {
+ //if (getVisibility() == View.INVISIBLE && !isAnimating()) {
+// startAnimation(fadeInAnimation);
+ setVisibility(View.VISIBLE);
+ // }
+ }
+
+ private boolean isAnimating() {
+ return (Boolean) getTag(IS_ANIMATING_TAG_ID) == true;
+ }
+
+ private void setAnimatingFlag(boolean isAnimating) {
+ setTag(IS_ANIMATING_TAG_ID, new Boolean(isAnimating));
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesome.java b/vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesome.java
new file mode 100644
index 0000000..77d7c07
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesome.java
@@ -0,0 +1,25 @@
+package de.androvdr.widget;
+
+import android.content.Context;
+import android.graphics.Typeface;
+
+/**
+ * Created by lado on 04.05.15.
+ */
+public class FontAwesome {
+
+ private static Typeface mFont;
+
+
+ public static Typeface getFontAwesome(Context context){
+
+ if(mFont != null){
+ return mFont;
+ }
+
+ mFont = Typeface.createFromAsset(context.getAssets(), "fonts/fontawesome-webfont.ttf");
+ return mFont;
+ }
+
+
+}
diff --git a/vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesomeButton.java b/vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesomeButton.java
new file mode 100644
index 0000000..22c4de1
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/androvdr/widget/FontAwesomeButton.java
@@ -0,0 +1,36 @@
+package de.androvdr.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+/**
+ * Created by lado on 04.05.15.
+ */
+public class FontAwesomeButton extends Button {
+
+
+ public FontAwesomeButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initFontAwesome();
+
+ }
+
+ public FontAwesomeButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initFontAwesome();
+
+ }
+
+ public FontAwesomeButton(Context context) {
+ super(context);
+ initFontAwesome();
+ }
+
+ private void initFontAwesome(){
+ if(isInEditMode() == false) {
+ setTypeface(FontAwesome.getFontAwesome(getContext().getApplicationContext().getApplicationContext()));
+ }
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/androvdr/widget/LruCache.java b/vdrmanager/app/src/main/java/de/androvdr/widget/LruCache.java
new file mode 100644
index 0000000..9186b15
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/androvdr/widget/LruCache.java
@@ -0,0 +1,396 @@
+package de.androvdr.widget;
+
+/**
+ * Created by lado on 04.05.15.
+ */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+ import java.util.LinkedHashMap;
+ import java.util.Map;
+
+/**
+ * BEGIN LAYOUTLIB CHANGE
+ * This is a custom version that doesn't use the non standard LinkedHashMap#eldest.
+ * END LAYOUTLIB CHANGE
+ *
+ * A cache that holds strong references to a limited number of values. Each time
+ * a value is accessed, it is moved to the head of a queue. When a value is
+ * added to a full cache, the value at the end of that queue is evicted and may
+ * become eligible for garbage collection.
+ *
+ * <p>If your cached values hold resources that need to be explicitly released,
+ * override {@link #entryRemoved}.
+ *
+ * <p>If a cache miss should be computed on demand for the corresponding keys,
+ * override {@link #create}. This simplifies the calling code, allowing it to
+ * assume a value will always be returned, even when there's a cache miss.
+ *
+ * <p>By default, the cache size is measured in the number of entries. Override
+ * {@link #sizeOf} to size the cache in different units. For example, this cache
+ * is limited to 4MiB of bitmaps:
+ * <pre> {@code
+ * int cacheSize = 4 * 1024 * 1024; // 4MiB
+ * LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
+ * protected int sizeOf(String key, Bitmap value) {
+ * return value.getByteCount();
+ * }
+ * }}</pre>
+ *
+ * <p>This class is thread-safe. Perform multiple cache operations atomically by
+ * synchronizing on the cache: <pre> {@code
+ * synchronized (cache) {
+ * if (cache.get(key) == null) {
+ * cache.put(key, value);
+ * }
+ * }}</pre>
+ *
+ * <p>This class does not allow null to be used as a key or value. A return
+ * value of null from {@link #get}, {@link #put} or {@link #remove} is
+ * unambiguous: the key was not in the cache.
+ *
+ * <p>This class appeared in Android 3.1 (Honeycomb MR1); it's available as part
+ * of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's
+ * Support Package</a> for earlier releases.
+ */
+public class LruCache<K, V> {
+ private final LinkedHashMap<K, V> map;
+
+ /** Size of this cache in units. Not necessarily the number of elements. */
+ private int size;
+ private int maxSize;
+
+ private int putCount;
+ private int createCount;
+ private int evictionCount;
+ private int hitCount;
+ private int missCount;
+
+ /**
+ * @param maxSize for caches that do not override {@link #sizeOf}, this is
+ * the maximum number of entries in the cache. For all other caches,
+ * this is the maximum sum of the sizes of the entries in this cache.
+ */
+ public LruCache(int maxSize) {
+ if (maxSize <= 0) {
+ throw new IllegalArgumentException("maxSize <= 0");
+ }
+ this.maxSize = maxSize;
+ this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
+ }
+
+ /**
+ * Sets the size of the cache.
+ * @param maxSize The new maximum size.
+ *
+ * @hide
+ */
+ public void resize(int maxSize) {
+ if (maxSize <= 0) {
+ throw new IllegalArgumentException("maxSize <= 0");
+ }
+
+ synchronized (this) {
+ this.maxSize = maxSize;
+ }
+ trimToSize(maxSize);
+ }
+
+ /**
+ * Returns the value for {@code key} if it exists in the cache or can be
+ * created by {@code #create}. If a value was returned, it is moved to the
+ * head of the queue. This returns null if a value is not cached and cannot
+ * be created.
+ */
+ public final V get(K key) {
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ }
+
+ V mapValue;
+ synchronized (this) {
+ mapValue = map.get(key);
+ if (mapValue != null) {
+ hitCount++;
+ return mapValue;
+ }
+ missCount++;
+ }
+
+ /*
+ * Attempt to create a value. This may take a long time, and the map
+ * may be different when create() returns. If a conflicting value was
+ * added to the map while create() was working, we leave that value in
+ * the map and release the created value.
+ */
+
+ V createdValue = create(key);
+ if (createdValue == null) {
+ return null;
+ }
+
+ synchronized (this) {
+ createCount++;
+ mapValue = map.put(key, createdValue);
+
+ if (mapValue != null) {
+ // There was a conflict so undo that last put
+ map.put(key, mapValue);
+ } else {
+ size += safeSizeOf(key, createdValue);
+ }
+ }
+
+ if (mapValue != null) {
+ entryRemoved(false, key, createdValue, mapValue);
+ return mapValue;
+ } else {
+ trimToSize(maxSize);
+ return createdValue;
+ }
+ }
+
+ /**
+ * Caches {@code value} for {@code key}. The value is moved to the head of
+ * the queue.
+ *
+ * @return the previous value mapped by {@code key}.
+ */
+ public final V put(K key, V value) {
+ if (key == null || value == null) {
+ throw new NullPointerException("key == null || value == null");
+ }
+
+ V previous;
+ synchronized (this) {
+ putCount++;
+ size += safeSizeOf(key, value);
+ previous = map.put(key, value);
+ if (previous != null) {
+ size -= safeSizeOf(key, previous);
+ }
+ }
+
+ if (previous != null) {
+ entryRemoved(false, key, previous, value);
+ }
+
+ trimToSize(maxSize);
+ return previous;
+ }
+
+ /**
+ * @param maxSize the maximum size of the cache before returning. May be -1
+ * to evict even 0-sized elements.
+ */
+ private void trimToSize(int maxSize) {
+ while (true) {
+ K key;
+ V value;
+ synchronized (this) {
+ if (size < 0 || (map.isEmpty() && size != 0)) {
+ throw new IllegalStateException(getClass().getName()
+ + ".sizeOf() is reporting inconsistent results!");
+ }
+
+ if (size <= maxSize) {
+ break;
+ }
+
+ // BEGIN LAYOUTLIB CHANGE
+ // get the last item in the linked list.
+ // This is not efficient, the goal here is to minimize the changes
+ // compared to the platform version.
+ Map.Entry<K, V> toEvict = null;
+ for (Map.Entry<K, V> entry : map.entrySet()) {
+ toEvict = entry;
+ }
+ // END LAYOUTLIB CHANGE
+
+ if (toEvict == null) {
+ break;
+ }
+
+ key = toEvict.getKey();
+ value = toEvict.getValue();
+ map.remove(key);
+ size -= safeSizeOf(key, value);
+ evictionCount++;
+ }
+
+ entryRemoved(true, key, value, null);
+ }
+ }
+
+ /**
+ * Removes the entry for {@code key} if it exists.
+ *
+ * @return the previous value mapped by {@code key}.
+ */
+ public final V remove(K key) {
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ }
+
+ V previous;
+ synchronized (this) {
+ previous = map.remove(key);
+ if (previous != null) {
+ size -= safeSizeOf(key, previous);
+ }
+ }
+
+ if (previous != null) {
+ entryRemoved(false, key, previous, null);
+ }
+
+ return previous;
+ }
+
+ /**
+ * Called for entries that have been evicted or removed. This method is
+ * invoked when a value is evicted to make space, removed by a call to
+ * {@link #remove}, or replaced by a call to {@link #put}. The default
+ * implementation does nothing.
+ *
+ * <p>The method is called without synchronization: other threads may
+ * access the cache while this method is executing.
+ *
+ * @param evicted true if the entry is being removed to make space, false
+ * if the removal was caused by a {@link #put} or {@link #remove}.
+ * @param newValue the new value for {@code key}, if it exists. If non-null,
+ * this removal was caused by a {@link #put}. Otherwise it was caused by
+ * an eviction or a {@link #remove}.
+ */
+ protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
+
+ /**
+ * Called after a cache miss to compute a value for the corresponding key.
+ * Returns the computed value or null if no value can be computed. The
+ * default implementation returns null.
+ *
+ * <p>The method is called without synchronization: other threads may
+ * access the cache while this method is executing.
+ *
+ * <p>If a value for {@code key} exists in the cache when this method
+ * returns, the created value will be released with {@link #entryRemoved}
+ * and discarded. This can occur when multiple threads request the same key
+ * at the same time (causing multiple values to be created), or when one
+ * thread calls {@link #put} while another is creating a value for the same
+ * key.
+ */
+ protected V create(K key) {
+ return null;
+ }
+
+ private int safeSizeOf(K key, V value) {
+ int result = sizeOf(key, value);
+ if (result < 0) {
+ throw new IllegalStateException("Negative size: " + key + "=" + value);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the size of the entry for {@code key} and {@code value} in
+ * user-defined units. The default implementation returns 1 so that size
+ * is the number of entries and max size is the maximum number of entries.
+ *
+ * <p>An entry's size must not change while it is in the cache.
+ */
+ protected int sizeOf(K key, V value) {
+ return 1;
+ }
+
+ /**
+ * Clear the cache, calling {@link #entryRemoved} on each removed entry.
+ */
+ public final void evictAll() {
+ trimToSize(-1); // -1 will evict 0-sized elements
+ }
+
+ /**
+ * For caches that do not override {@link #sizeOf}, this returns the number
+ * of entries in the cache. For all other caches, this returns the sum of
+ * the sizes of the entries in this cache.
+ */
+ public synchronized final int size() {
+ return size;
+ }
+
+ /**
+ * For caches that do not override {@link #sizeOf}, this returns the maximum
+ * number of entries in the cache. For all other caches, this returns the
+ * maximum sum of the sizes of the entries in this cache.
+ */
+ public synchronized final int maxSize() {
+ return maxSize;
+ }
+
+ /**
+ * Returns the number of times {@link #get} returned a value that was
+ * already present in the cache.
+ */
+ public synchronized final int hitCount() {
+ return hitCount;
+ }
+
+ /**
+ * Returns the number of times {@link #get} returned null or required a new
+ * value to be created.
+ */
+ public synchronized final int missCount() {
+ return missCount;
+ }
+
+ /**
+ * Returns the number of times {@link #create(Object)} returned a value.
+ */
+ public synchronized final int createCount() {
+ return createCount;
+ }
+
+ /**
+ * Returns the number of times {@link #put} was called.
+ */
+ public synchronized final int putCount() {
+ return putCount;
+ }
+
+ /**
+ * Returns the number of values that have been evicted.
+ */
+ public synchronized final int evictionCount() {
+ return evictionCount;
+ }
+
+ /**
+ * Returns a copy of the current contents of the cache, ordered from least
+ * recently accessed to most recently accessed.
+ */
+ public synchronized final Map<K, V> snapshot() {
+ return new LinkedHashMap<K, V>(map);
+ }
+
+ @Override public synchronized final String toString() {
+ int accesses = hitCount + missCount;
+ int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
+ return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
+ maxSize, hitCount, missCount, hitPercent);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/androvdr/widget/SquareButton.java b/vdrmanager/app/src/main/java/de/androvdr/widget/SquareButton.java
new file mode 100644
index 0000000..6c3ad29
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/androvdr/widget/SquareButton.java
@@ -0,0 +1,35 @@
+package de.androvdr.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+public class SquareButton extends FontAwesomeButton {
+
+ public SquareButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public SquareButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SquareButton(Context context) {
+ super(context);
+ }
+
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ //Get canvas width and height
+ int w = MeasureSpec.getSize(widthMeasureSpec);
+ int h = MeasureSpec.getSize(heightMeasureSpec);
+
+ w = Math.min(w, h);
+ h = w;
+
+ setMeasuredDimension(w, h);
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/QueuedWork.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/QueuedWork.java
new file mode 100644
index 0000000..09bb15b
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/QueuedWork.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.bjusystems.vdrmanager;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Internal utility class to keep track of process-global work that's
+ * outstanding and hasn't been finished yet.
+ *
+ * This was created for writing SharedPreference edits out
+ * asynchronously so we'd have a mechanism to wait for the writes in
+ * Activity.onPause and similar places, but we may use this mechanism
+ * for other things in the future.
+ *
+ *
+ */
+public class QueuedWork {
+
+ // The set of Runnables that will finish or wait on any async
+ // activities started by the application.
+ private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers =
+ new ConcurrentLinkedQueue<Runnable>();
+
+ private static ExecutorService sSingleThreadExecutor = null; // lazy, guarded by class
+
+ /**
+ * Returns a single-thread Executor shared by the entire process,
+ * creating it if necessary.
+ */
+ public static ExecutorService singleThreadExecutor() {
+ synchronized (QueuedWork.class) {
+ if (sSingleThreadExecutor == null) {
+ // TODO: can we give this single thread a thread name?
+ sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+ }
+ return sSingleThreadExecutor;
+ }
+ }
+
+ /**
+ * Add a runnable to finish (or wait for) a deferred operation
+ * started in this context earlier. Typically finished by e.g.
+ * an Activity#onPause. Used by SharedPreferences$Editor#startCommit().
+ *
+ * Note that this doesn't actually start it running. This is just
+ * a scratch set for callers doing async work to keep updated with
+ * what's in-flight. In the common case, caller code
+ * (e.g. SharedPreferences) will pretty quickly call remove()
+ * after an add(). The only time these Runnables are run is from
+ * waitToFinish(), below.
+ */
+ public static void add(Runnable finisher) {
+ sPendingWorkFinishers.add(finisher);
+ }
+
+ public static void remove(Runnable finisher) {
+ sPendingWorkFinishers.remove(finisher);
+ }
+
+ /**
+ * Finishes or waits for async operations to complete.
+ * (e.g. SharedPreferences$Editor#startCommit writes)
+ *
+ * Is called from the Activity base class's onPause(), after
+ * BroadcastReceiver's onReceive, after Service command handling,
+ * etc. (so async work is never lost)
+ */
+ public static void waitToFinish() {
+ Runnable toFinish;
+ while ((toFinish = sPendingWorkFinishers.poll()) != null) {
+ toFinish.run();
+ }
+ }
+
+ /**
+ * Returns true if there is pending work to be done. Note that the
+ * result is out of data as soon as you receive it, so be careful how you
+ * use it.
+ */
+ public static boolean hasPendingWork() {
+ return !sPendingWorkFinishers.isEmpty();
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/StringUtils.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/StringUtils.java
new file mode 100644
index 0000000..97050dc
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/StringUtils.java
@@ -0,0 +1,180 @@
+
+package de.bjusystems.vdrmanager;
+
+import java.util.ArrayList;
+
+
+
+public class StringUtils {
+
+ /**
+ * An empty immutable <code>String</code> array.
+ */
+ public static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+
+ public static final String EMPTY_STRING = "";
+
+
+ /**
+ * Performs the logic for the <code>split</code> and
+ * <code>splitPreserveAllTokens</code> methods that return a maximum array
+ * length.
+ *
+ * @param str the String to parse, may be <code>null</code>
+ * @param separatorChars the separate character
+ * @param max the maximum number of elements to include in the
+ * array. A zero or negative value implies no limit.
+ * @param preserveAllTokens if <code>true</code>, adjacent separators are
+ * treated as empty token separators; if <code>false</code>, adjacent
+ * separators are treated as one separator.
+ * @return an array of parsed Strings, <code>null</code> if null String input
+ */
+ private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) {
+ // Performance tuned for 2.0 (JDK1.4)
+ // Direct code is quicker than StringTokenizer.
+ // Also, StringTokenizer uses isSpace() not isWhitespace()
+
+ if (str == null) {
+ return null;
+ }
+ int len = str.length();
+ if (len == 0) {
+ return EMPTY_STRING_ARRAY;
+ }
+ ArrayList<String> list = new ArrayList<String>();
+ int sizePlus1 = 1;
+ int i = 0, start = 0;
+ boolean match = false;
+ boolean lastMatch = false;
+ if (separatorChars == null) {
+ // Null separator means use whitespace
+ while (i < len) {
+ if (Character.isWhitespace(str.charAt(i))) {
+ if (match || preserveAllTokens) {
+ lastMatch = true;
+ if (sizePlus1++ == max) {
+ i = len;
+ lastMatch = false;
+ }
+ list.add(str.substring(start, i));
+ match = false;
+ }
+ start = ++i;
+ continue;
+ }
+ lastMatch = false;
+ match = true;
+ i++;
+ }
+ } else if (separatorChars.length() == 1) {
+ // Optimise 1 character case
+ char sep = separatorChars.charAt(0);
+ while (i < len) {
+ if (str.charAt(i) == sep) {
+ if (match || preserveAllTokens) {
+ lastMatch = true;
+ if (sizePlus1++ == max) {
+ i = len;
+ lastMatch = false;
+ }
+ list.add(str.substring(start, i));
+ match = false;
+ }
+ start = ++i;
+ continue;
+ }
+ lastMatch = false;
+ match = true;
+ i++;
+ }
+ } else {
+ // standard case
+ while (i < len) {
+ if (separatorChars.indexOf(str.charAt(i)) >= 0) {
+ if (match || preserveAllTokens) {
+ lastMatch = true;
+ if (sizePlus1++ == max) {
+ i = len;
+ lastMatch = false;
+ }
+ list.add(str.substring(start, i));
+ match = false;
+ }
+ start = ++i;
+ continue;
+ }
+ lastMatch = false;
+ match = true;
+ i++;
+ }
+ }
+ if (match || (preserveAllTokens && lastMatch)) {
+ list.add(str.substring(start, i));
+ }
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * <p>Splits the provided text into an array, separators specified,
+ * preserving all tokens, including empty tokens created by adjacent
+ * separators. This is an alternative to using StringTokenizer.</p>
+ *
+ * <p>The separator is not included in the returned String array.
+ * Adjacent separators are treated as separators for empty tokens.
+ * For more control over the split use the StrTokenizer class.</p>
+ *
+ * <p>A <code>null</code> input String returns <code>null</code>.
+ * A <code>null</code> separatorChars splits on whitespace.</p>
+ *
+ * <pre>
+ * StringUtils.splitPreserveAllTokens(null, *) = null
+ * StringUtils.splitPreserveAllTokens("", *) = []
+ * StringUtils.splitPreserveAllTokens("abc def", null) = ["abc", "def"]
+ * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "def"]
+ * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "", def"]
+ * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":") = ["ab", "cd", "ef"]
+ * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":") = ["ab", "cd", "ef", ""]
+ * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
+ * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":") = ["ab", "", cd", "ef"]
+ * StringUtils.splitPreserveAllTokens(":cd:ef", ":") = ["", cd", "ef"]
+ * StringUtils.splitPreserveAllTokens("::cd:ef", ":") = ["", "", cd", "ef"]
+ * StringUtils.splitPreserveAllTokens(":cd:ef:", ":") = ["", cd", "ef", ""]
+ * </pre>
+ *
+ * @param str the String to parse, may be <code>null</code>
+ * @param separatorChars the characters used as the delimiters,
+ * <code>null</code> splits on whitespace
+ * @return an array of parsed Strings, <code>null</code> if null String input
+ * @since 2.1
+ */
+ public static String[] splitPreserveAllTokens(String str, String separatorChars) {
+ return splitWorker(str, separatorChars, -1, true);
+ }
+
+ // Equals
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Compares two Strings, returning <code>true</code> if they are equal.</p>
+ *
+ * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
+ * references are considered to be equal. The comparison is case sensitive.</p>
+ *
+ * <pre>
+ * StringUtils.equals(null, null) = true
+ * StringUtils.equals(null, "abc") = false
+ * StringUtils.equals("abc", null) = false
+ * StringUtils.equals("abc", "abc") = true
+ * StringUtils.equals("abc", "ABC") = false
+ * </pre>
+ *
+ * @see java.lang.String#equals(Object)
+ * @param str1 the first String, may be null
+ * @param str2 the second String, may be null
+ * @return <code>true</code> if the Strings are equal, case sensitive, or
+ * both <code>null</code>
+ */
+ public static boolean equals(String str1, String str2) {
+ return str1 == null ? str2 == null : str1.equals(str2);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/VdrSharedPreferencesImpl.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/VdrSharedPreferencesImpl.java
new file mode 100644
index 0000000..97d6428
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/VdrSharedPreferencesImpl.java
@@ -0,0 +1,527 @@
+/*
+ * Copyrigsht (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.bjusystems.vdrmanager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.CountDownLatch;
+
+import android.content.SharedPreferences;
+import android.os.Looper;
+
+import com.j256.ormlite.dao.RuntimeExceptionDao;
+
+import de.bjusystems.vdrmanager.data.Vdr;
+
+public class VdrSharedPreferencesImpl implements SharedPreferences {
+
+ private static final String TAG = VdrSharedPreferencesImpl.class.getSimpleName();
+
+ private static final boolean DEBUG = false;
+
+ // Lock ordering rules:
+ // - acquire SharedPreferencesImpl.this before EditorImpl.this
+ // - acquire mWritingToDiskLock before EditorImpl.this
+
+ Vdr mVdr;
+
+
+ public Vdr getVdr(){
+ return mVdr;
+ }
+
+ RuntimeExceptionDao<Vdr, Integer> dao;
+
+ private Map<String, Object> mMap; // guarded by 'this'
+ private int mDiskWritesInFlight = 0; // guarded by 'this'
+ private boolean mLoaded = false; // guarded by 'this'
+
+ private final Object mWritingToDiskLock = new Object();
+ private static final Object mContent = new Object();
+ private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
+
+ public VdrSharedPreferencesImpl(Vdr vdr, RuntimeExceptionDao<Vdr, Integer> dao) {
+ mVdr = vdr;
+ this.dao = dao;
+ mLoaded = false;
+ mMap = null;
+ startLoadFromDisk();
+ }
+
+ private void startLoadFromDisk() {
+ synchronized (this) {
+ mLoaded = false;
+ }
+ new Thread("SharedPreferencesImpl-load") {
+ public void run() {
+ synchronized (VdrSharedPreferencesImpl.this) {
+ loadFromDiskLocked();
+ }
+ }
+ }.start();
+ }
+
+ private void loadFromDiskLocked() {
+ if (mLoaded) {
+ return;
+ }
+
+ Map map = mVdr.toMap();
+ //StructStat stat = null;
+ //try {
+ //stat = Libcore.os.stat(mFile.getPath());
+ //if (mFile.canRead()) {
+ //BufferedInputStream str = null;
+ //try {
+ //str = new BufferedInputStream(new FileInputStream(mFile),
+ // 16 * 1024);
+ //map = XmlUtils.readMapXml(str);
+ //} catch (XmlPullParserException e) {
+// Log.w(TAG, "getSharedPreferences", e);
+ // } catch (FileNotFoundException e) {
+ // Log.w(TAG, "getSharedPreferences", e);
+ // } catch (IOException e) {
+ // Log.w(TAG, "getSharedPreferences", e);
+ //} finally {
+ //IoUtils.closeQuietly(str);
+ //}
+ //}
+ //} catch (ErrnoException e) {
+ //}
+ mLoaded = true;
+ if (map != null) {
+ mMap = map;
+ } else {
+ mMap = new HashMap<String, Object>();
+ }
+ notifyAll();
+ }
+
+
+
+ public void registerOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ synchronized (this) {
+ mListeners.put(listener, mContent);
+ }
+ }
+
+ public void unregisterOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ synchronized (this) {
+ mListeners.remove(listener);
+ }
+ }
+
+ private void awaitLoadedLocked() {
+ // if (!mLoaded) {
+ // // Raise an explicit StrictMode onReadFromDisk for this
+ // // thread, since the real read will be in a different
+ // // thread and otherwise ignored by StrictMode.
+ // BlockGuard.getThreadPolicy().onReadFromDisk();
+ // }
+ while (!mLoaded) {
+ try {
+ wait();
+ } catch (InterruptedException unused) {
+ }
+ }
+ }
+
+ public Map<String, ?> getAll() {
+ synchronized (this) {
+ awaitLoadedLocked();
+ // noinspection unchecked
+ return new HashMap<String, Object>(mMap);
+ }
+ }
+
+ public String getString(String key, String defValue) {
+ synchronized (this) {
+ awaitLoadedLocked();
+ String v = String.valueOf(mMap.get(key));
+ return v != null ? v : defValue;
+ }
+ }
+
+ public Set<String> getStringSet(String key, Set<String> defValues) {
+ synchronized (this) {
+ awaitLoadedLocked();
+ Set<String> v = (Set<String>) mMap.get(key);
+ return v != null ? v : defValues;
+ }
+ }
+
+ public int getInt(String key, int defValue) {
+ synchronized (this) {
+ awaitLoadedLocked();
+ Integer v = (Integer) mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+
+ public long getLong(String key, long defValue) {
+ synchronized (this) {
+ awaitLoadedLocked();
+ Long v = (Long) mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+
+ public float getFloat(String key, float defValue) {
+ synchronized (this) {
+ awaitLoadedLocked();
+ Float v = (Float) mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+
+ public boolean getBoolean(String key, boolean defValue) {
+ synchronized (this) {
+ awaitLoadedLocked();
+ Boolean v = (Boolean) mMap.get(key);
+ return v != null ? v : defValue;
+ }
+ }
+
+ public boolean contains(String key) {
+ synchronized (this) {
+ awaitLoadedLocked();
+ return mMap.containsKey(key);
+ }
+ }
+
+ public Editor edit() {
+ // TODO: remove the need to call awaitLoadedLocked() when
+ // requesting an editor. will require some work on the
+ // Editor, but then we should be able to do:
+ //
+ // context.getSharedPreferences(..).edit().putString(..).apply()
+ //
+ // ... all without blocking.
+ synchronized (this) {
+ awaitLoadedLocked();
+ }
+
+ return new EditorImpl();
+ }
+
+ // Return value from EditorImpl#commitToMemory()
+ private static class MemoryCommitResult {
+ public boolean changesMade; // any keys different?
+ public List<String> keysModified; // may be null
+ public Set<OnSharedPreferenceChangeListener> listeners; // may be null
+ public Map<String, Object> mapToWriteToDisk;
+ public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
+ public volatile boolean writeToDiskResult = false;
+
+ public void setDiskWriteResult(boolean result) {
+ writeToDiskResult = result;
+ writtenToDiskLatch.countDown();
+ }
+ }
+
+ public final class EditorImpl implements Editor {
+
+ private final Map<String, Object> mModified = new HashMap<String, Object>();
+
+ private boolean mClear = false;
+
+ public Editor putString(String key, String value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+
+ public Editor putStringSet(String key, Set<String> values) {
+ synchronized (this) {
+ mModified.put(key, (values == null) ? null
+ : new HashSet<String>(values));
+ return this;
+ }
+ }
+
+ public Editor putInt(String key, int value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+
+ public Editor putLong(String key, long value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+
+ public Editor putFloat(String key, float value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+
+ public Editor putBoolean(String key, boolean value) {
+ synchronized (this) {
+ mModified.put(key, value);
+ return this;
+ }
+ }
+
+ public Editor remove(String key) {
+ synchronized (this) {
+ mModified.put(key, this);
+ return this;
+ }
+ }
+
+ public Editor clear() {
+ synchronized (this) {
+ mClear = true;
+ return this;
+ }
+ }
+
+ public void apply() {
+ final MemoryCommitResult mcr = commitToMemory();
+ final Runnable awaitCommit = new Runnable() {
+ public void run() {
+ try {
+ mcr.writtenToDiskLatch.await();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ };
+
+ QueuedWork.add(awaitCommit);
+
+ Runnable postWriteRunnable = new Runnable() {
+ public void run() {
+ awaitCommit.run();
+ QueuedWork.remove(awaitCommit);
+ }
+ };
+
+ VdrSharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
+
+ // Okay to notify the listeners before it's hit disk
+ // because the listeners should always get the same
+ // SharedPreferences instance back, which has the
+ // changes reflected in memory.
+ notifyListeners(mcr);
+ }
+
+ // Returns true if any changes were made
+ private MemoryCommitResult commitToMemory() {
+ MemoryCommitResult mcr = new MemoryCommitResult();
+ synchronized (VdrSharedPreferencesImpl.this) {
+ // We optimistically don't make a deep copy until
+ // a memory commit comes in when we're already
+ // writing to disk.
+ if (mDiskWritesInFlight > 0) {
+ // We can't modify our mMap as a currently
+ // in-flight write owns it. Clone it before
+ // modifying it.
+ // noinspection unchecked
+ mMap = new HashMap<String, Object>(mMap);
+ }
+ mcr.mapToWriteToDisk = mMap;
+ mDiskWritesInFlight++;
+
+ boolean hasListeners = mListeners.size() > 0;
+ if (hasListeners) {
+ mcr.keysModified = new ArrayList<String>();
+ mcr.listeners = new HashSet<OnSharedPreferenceChangeListener>(
+ mListeners.keySet());
+ }
+
+ synchronized (this) {
+ if (mClear) {
+ if (!mMap.isEmpty()) {
+ mcr.changesMade = true;
+ mMap.clear();
+ }
+ mClear = false;
+ }
+
+ for (Map.Entry<String, Object> e : mModified.entrySet()) {
+ String k = e.getKey();
+ Object v = e.getValue();
+ if (v == this) { // magic value for a removal mutation
+ if (!mMap.containsKey(k)) {
+ continue;
+ }
+ mMap.remove(k);
+ } else {
+ boolean isSame = false;
+ if (mMap.containsKey(k)) {
+ Object existingValue = mMap.get(k);
+ if (existingValue != null
+ && existingValue.equals(v)) {
+ continue;
+ }
+ }
+ mMap.put(k, v);
+ }
+
+ mcr.changesMade = true;
+ if (hasListeners) {
+ mcr.keysModified.add(k);
+ }
+ }
+
+ mModified.clear();
+ }
+ }
+ return mcr;
+ }
+
+ public boolean commit() {
+ MemoryCommitResult mcr = commitToMemory();
+ VdrSharedPreferencesImpl.this.enqueueDiskWrite(mcr, null /*
+ * sync write on
+ * this thread
+ * okay
+ */);
+ try {
+ mcr.writtenToDiskLatch.await();
+ } catch (InterruptedException e) {
+ return false;
+ }
+ notifyListeners(mcr);
+ return mcr.writeToDiskResult;
+ }
+
+ private void notifyListeners(final MemoryCommitResult mcr) {
+ if (mcr.listeners == null || mcr.keysModified == null
+ || mcr.keysModified.size() == 0) {
+ return;
+ }
+ //if (Looper.myLooper() == Looper.getMainLooper()) {
+ for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {
+ final String key = mcr.keysModified.get(i);
+ for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
+ if (listener != null) {
+ listener.onSharedPreferenceChanged(
+ VdrSharedPreferencesImpl.this, key);
+ }
+ }
+ }
+ //} else {
+ // Run this function on the main thread.
+ // VdrManagerApp.sMainThreadHandler.post(new Runnable() {
+ // public void run() {
+ // notifyListeners(mcr);
+ // }
+ // });
+ //}
+ }
+ }
+
+ /**
+ * Enqueue an already-committed-to-memory result to be written to disk.
+ *
+ * They will be written to disk one-at-a-time in the order that they're
+ * enqueued.
+ *
+ * @param postWriteRunnable
+ * if non-null, we're being called from apply() and this is the
+ * runnable to run after the write proceeds. if null (from a
+ * regular commit()), then we're allowed to do this disk write on
+ * the main thread (which in addition to reducing allocations and
+ * creating a background thread, this has the advantage that we
+ * catch them in userdebug StrictMode reports to convert them
+ * where possible to apply() ...)
+ */
+ private void enqueueDiskWrite(final MemoryCommitResult mcr,
+ final Runnable postWriteRunnable) {
+ final Runnable writeToDiskRunnable = new Runnable() {
+ public void run() {
+ synchronized (mWritingToDiskLock) {
+ writeToFile(mcr);
+ }
+ synchronized (VdrSharedPreferencesImpl.this) {
+ mDiskWritesInFlight--;
+ }
+ if (postWriteRunnable != null) {
+ postWriteRunnable.run();
+ }
+ }
+ };
+
+ final boolean isFromSyncCommit = (postWriteRunnable == null);
+
+ // Typical #commit() path with fewer allocations, doing a write on
+ // the current thread.
+ if (isFromSyncCommit) {
+ boolean wasEmpty = false;
+ synchronized (VdrSharedPreferencesImpl.this) {
+ wasEmpty = mDiskWritesInFlight == 1;
+ }
+ if (wasEmpty) {
+ writeToDiskRunnable.run();
+ return;
+ }
+ }
+
+ QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
+ }
+
+ // Note: must hold mWritingToDiskLock
+ private void writeToFile(MemoryCommitResult mcr) {
+ // Rename the current file so it may be used as a backup during the next
+ // read
+
+ // Attempt to write the file, delete the backup and return true as
+ // atomically as
+ // possible. If any exception occurs, delete the new file; next time we
+ // will restore
+ // from the backup.
+ // FileOutputStream str = createFileOutputStream(mFile);
+ // if (str == null) {
+ // mcr.setDiskWriteResult(false);
+ // return;
+ // }
+ //
+ // XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
+ mVdr.set(mcr.mapToWriteToDisk);
+ dao.createOrUpdate(mVdr);
+ // FileUtils.sync(str);
+ // str.close();
+ // ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
+ // try {
+ // final StructStat stat = Libcore.os.stat(mFile.getPath());
+ // synchronized (this) {
+ // mStatTimestamp = stat.st_mtime;
+ // mStatSize = stat.st_size;
+ // }
+ // } catch (ErrnoException e) {
+ // // Do nothing
+ // }
+ // Writing was successful, delete the backup file if there is one.
+ mcr.setDiskWriteResult(true);
+ return;
+
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/ZonePicker.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/ZonePicker.java
new file mode 100644
index 0000000..3b1dfbc
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/ZonePicker.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.bjusystems.vdrmanager;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+
+/**
+ * The class displaying a list of time zones that match a filter string
+ * such as "Africa", "Europe", etc. Choosing an item from the list will set
+ * the time zone. Pressing Back without choosing from the list will not
+ * result in a change in the time zone setting.
+ */
+public class ZonePicker extends ListActivity {
+ private static final String TAG = "ZonePicker";
+
+ public static interface ZoneSelectionListener {
+ // You can add any argument if you really need it...
+ public void onZoneSelected(TimeZone tz);
+ }
+
+ private static final String KEY_ID = "id"; // value: String
+ private static final String KEY_DISPLAYNAME = "name"; // value: String
+ private static final String KEY_GMT = "gmt"; // value: String
+ private static final String KEY_OFFSET = "offset"; // value: int (Integer)
+ private static final String XMLTAG_TIMEZONE = "timezone";
+
+ private static final int HOURS_1 = 60 * 60000;
+
+ private static final int MENU_TIMEZONE = Menu.FIRST+1;
+ private static final int MENU_ALPHABETICAL = Menu.FIRST;
+
+ private boolean mSortedByTimezone;
+
+ private SimpleAdapter mTimezoneSortedAdapter;
+ private SimpleAdapter mAlphabeticalAdapter;
+
+ private ZoneSelectionListener mListener;
+
+ private String selectedTimeZone;
+
+ /**
+ * Constructs an adapter with TimeZone list. Sorted by TimeZone in default.
+ *
+ * @param sortedByName use Name for sorting the list.
+ */
+ public static SimpleAdapter constructTimezoneAdapter(Context context,
+ boolean sortedByName) {
+ return constructTimezoneAdapter(context, sortedByName,
+ android.R.layout.simple_list_item_2);
+ }
+
+ /**
+ * Constructs an adapter with TimeZone list. Sorted by TimeZone in default.
+ *
+ * @param sortedByName use Name for sorting the list.
+ */
+ public static SimpleAdapter constructTimezoneAdapter(Context context,
+ boolean sortedByName, int layoutId) {
+ final String[] from = new String[] {KEY_DISPLAYNAME, KEY_GMT};
+ final int[] to = new int[] {android.R.id.text1, android.R.id.text2};
+
+ final String sortKey = (sortedByName ? KEY_DISPLAYNAME : KEY_OFFSET);
+ final MyComparator comparator = new MyComparator(sortKey);
+ final List<HashMap<String, Object>> sortedList = getZones(context);
+ Collections.sort(sortedList, comparator);
+ final SimpleAdapter adapter = new SimpleAdapter(context,
+ sortedList,
+ layoutId,
+ from,
+ to);
+
+ return adapter;
+ }
+
+ /**
+ * Searches {@link TimeZone} from the given {@link SimpleAdapter} object, and returns
+ * the index for the TimeZone.
+ *
+ * @param adapter SimpleAdapter constructed by
+ * {@link #constructTimezoneAdapter(Context, boolean)}.
+ * @param tz TimeZone to be searched.
+ * @return Index for the given TimeZone. -1 when there's no corresponding list item.
+ * returned.
+ */
+ public static int getTimeZoneIndex(SimpleAdapter adapter, TimeZone tz) {
+ final String defaultId = tz.getID();
+ final int listSize = adapter.getCount();
+ for (int i = 0; i < listSize; i++) {
+ // Using HashMap<String, Object> induces unnecessary warning.
+ final HashMap<?,?> map = (HashMap<?,?>)adapter.getItem(i);
+ final String id = (String)map.get(KEY_ID);
+ if (defaultId.equals(id)) {
+ // If current timezone is in this list, move focus to it
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * @param item one of items in adapters. The adapter should be constructed by
+ * {@link #constructTimezoneAdapter(Context, boolean)}.
+ * @return TimeZone object corresponding to the item.
+ */
+ public static TimeZone obtainTimeZoneFromItem(Object item) {
+ return TimeZone.getTimeZone((String)((Map<?, ?>)item).get(KEY_ID));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ selectedTimeZone = getIntent().getStringExtra("current_tz");
+
+ mTimezoneSortedAdapter = constructTimezoneAdapter(this, false);
+ mAlphabeticalAdapter = constructTimezoneAdapter(this, true);
+
+ // Sets the adapter
+ setSorting(true);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_ALPHABETICAL, 0, R.string.zone_list_menu_sort_alphabetically)
+ .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
+ menu.add(0, MENU_TIMEZONE, 0, R.string.zone_list_menu_sort_by_timezone)
+ .setIcon(R.drawable.ic_menu_3d_globe);
+ return true;
+ }
+
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (mSortedByTimezone) {
+ menu.findItem(MENU_TIMEZONE).setVisible(false);
+ menu.findItem(MENU_ALPHABETICAL).setVisible(true);
+ } else {
+ menu.findItem(MENU_TIMEZONE).setVisible(true);
+ menu.findItem(MENU_ALPHABETICAL).setVisible(false);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ case MENU_TIMEZONE:
+ setSorting(true);
+ return true;
+
+ case MENU_ALPHABETICAL:
+ setSorting(false);
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public void setZoneSelectionListener(ZoneSelectionListener listener) {
+ mListener = listener;
+ }
+
+ private void setSorting(boolean sortByTimezone) {
+ final SimpleAdapter adapter =
+ sortByTimezone ? mTimezoneSortedAdapter : mAlphabeticalAdapter;
+ setListAdapter(adapter);
+ mSortedByTimezone = sortByTimezone;
+ final int defaultIndex = getTimeZoneIndex(adapter, TimeZone.getDefault());
+ if (defaultIndex >= 0) {
+ setSelection(defaultIndex);
+ }
+ }
+
+ private static List<HashMap<String, Object>> getZones(Context context) {
+ final List<HashMap<String, Object>> myData = new ArrayList<HashMap<String, Object>>();
+ final long date = Calendar.getInstance().getTimeInMillis();
+ try {
+ XmlResourceParser xrp = context.getResources().getXml(R.xml.timezones);
+ while (xrp.next() != XmlResourceParser.START_TAG)
+ continue;
+ xrp.next();
+ while (xrp.getEventType() != XmlResourceParser.END_TAG) {
+ while (xrp.getEventType() != XmlResourceParser.START_TAG) {
+ if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) {
+ return myData;
+ }
+ xrp.next();
+ }
+ if (xrp.getName().equals(XMLTAG_TIMEZONE)) {
+ String id = xrp.getAttributeValue(0);
+ String displayName = xrp.nextText();
+ addItem(myData, id, displayName, date);
+ }
+ while (xrp.getEventType() != XmlResourceParser.END_TAG) {
+ xrp.next();
+ }
+ xrp.next();
+ }
+ xrp.close();
+ } catch (XmlPullParserException xppe) {
+ Log.e(TAG, "Ill-formatted timezones.xml file");
+ } catch (java.io.IOException ioe) {
+ Log.e(TAG, "Unable to read timezones.xml file");
+ }
+
+ return myData;
+ }
+
+ private static void addItem(
+ List<HashMap<String, Object>> myData, String id, String displayName, long date) {
+ final HashMap<String, Object> map = new HashMap<String, Object>();
+ map.put(KEY_ID, id);
+ map.put(KEY_DISPLAYNAME, displayName);
+ final TimeZone tz = TimeZone.getTimeZone(id);
+ final int offset = tz.getOffset(date);
+ final int p = Math.abs(offset);
+ final StringBuilder name = new StringBuilder();
+ name.append("GMT");
+
+ if (offset < 0) {
+ name.append('-');
+ } else {
+ name.append('+');
+ }
+
+ name.append(p / (HOURS_1));
+ name.append(':');
+
+ int min = p / 60000;
+ min %= 60;
+
+ if (min < 10) {
+ name.append('0');
+ }
+ name.append(min);
+
+ map.put(KEY_GMT, name.toString());
+ map.put(KEY_OFFSET, offset);
+
+ myData.add(map);
+ }
+
+ @Override
+ public void onListItemClick(ListView listView, View v, int position, long id) {
+ final Map<?, ?> map = (Map<?, ?>)listView.getItemAtPosition(position);
+ final String tzId = (String) map.get(KEY_ID);
+
+ // Update the system timezone value
+ //final AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ //alarm.setTimeZone(tzId);
+ final TimeZone tz = TimeZone.getTimeZone(tzId);
+ //if (mListener != null) {
+ // mListener.onZoneSelected(tz);
+ //} else {
+ // onBackPressed();
+ ///}
+ Intent returnIntent = new Intent();
+ if(tz == null){
+ setResult(RESULT_CANCELED, returnIntent);
+ } else {
+ returnIntent.putExtra("new_tz",tz.getID());
+ setResult(RESULT_OK, returnIntent);
+ }
+ finish();
+ }
+
+ private static class MyComparator implements Comparator<HashMap<?, ?>> {
+ private String mSortingKey;
+
+ public MyComparator(String sortingKey) {
+ mSortingKey = sortingKey;
+ }
+
+ public void setSortingKey(String sortingKey) {
+ mSortingKey = sortingKey;
+ }
+
+ public int compare(HashMap<?, ?> map1, HashMap<?, ?> map2) {
+ Object value1 = map1.get(mSortingKey);
+ Object value2 = map2.get(mSortingKey);
+
+ /*
+ * This should never happen, but just in-case, put non-comparable
+ * items at the end.
+ */
+ if (!isComparable(value1)) {
+ return isComparable(value2) ? 1 : 0;
+ } else if (!isComparable(value2)) {
+ return -1;
+ }
+
+ return ((Comparable) value1).compareTo(value2);
+ }
+
+ private boolean isComparable(Object value) {
+ return (value != null) && (value instanceof Comparable);
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/C.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/C.java
new file mode 100644
index 0000000..8156f69
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/C.java
@@ -0,0 +1,15 @@
+package de.bjusystems.vdrmanager.app;
+
+/**
+ *
+ * Some constants
+ * @author lado
+ *
+ *
+ */
+public interface C {
+
+ public static final String DATA_SEPARATOR = ":";
+
+ public static final long ONE_MINUTE_IN_MILLIS = 60 * 1000;
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/Intents.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/Intents.java
new file mode 100644
index 0000000..72feb99
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/Intents.java
@@ -0,0 +1,19 @@
+package de.bjusystems.vdrmanager.app;
+
+public interface Intents {
+
+ //public static final String CURRENT_CHANNEL = "CURRENT_CHANNEL";
+ public static final String HIGHLIGHT = "HIGHLIGHT";
+ public static final String TIMER_OP = "TIMER_OP";
+ public static final int EDIT_TIMER = 0;
+ public static final int ADD_TIMER = 1;
+
+ public static final String VDR_ID = "VDR_ID";
+
+ public static final int EDIT_VDR = 2;
+
+ public static final String EMPTY_CONFIG = "EMPTY_CONFIG";
+
+ public static final String CURRENT_EPG = "CURRENT_EPG";
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/VdrManagerApp.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/VdrManagerApp.java
new file mode 100644
index 0000000..fd301f0
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/app/VdrManagerApp.java
@@ -0,0 +1,159 @@
+package de.bjusystems.vdrmanager.app;
+
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import android.app.Activity;
+import android.app.Application;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.EpgSearchParams;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.data.Vdr;
+
+
+public class VdrManagerApp extends Application {
+
+
+ public VdrManagerApp() {
+ super();
+ initSessionKeyStore();
+ }
+
+ public enum EpgListState {
+ EPG_TIME, EPG_CHANNEL, EPG_SEARCH
+ }
+
+ private EpgListState epgListState;
+ private Event currentEvent;
+ private Timer currentTimer;
+ private Channel currentChannel;
+
+ public static final Locale SYSTEM_LOCALE = Locale.getDefault();
+
+ private Vdr currentVDR;
+
+ public Vdr getCurrentVDR() {
+ return currentVDR;
+ }
+
+ public void setCurrentVDR(final Vdr currentVDR) {
+ this.currentVDR = currentVDR;
+ }
+
+ private List<Event> currentEpgList = new ArrayList<Event>();
+
+ public List<Event> getCurrentEpgList() {
+ return currentEpgList;
+ }
+
+ public void setCurrentEpgList(final List currentEpgList) {
+ this.currentEpgList = currentEpgList;
+ }
+
+ private EpgSearchParams currentSearch;
+ private Class<? extends Activity> nextActivity;
+ private final List<Activity> activitiesToFinish = new ArrayList<Activity>();
+ private boolean reload;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Preferences.init(this);
+ }
+
+ public void clear() {
+ this.currentEvent = null;
+ this.currentTimer = null;
+ this.currentChannel = null;
+ this.currentSearch = null;
+ this.currentEpgList = null;
+ this.epgListState = EpgListState.EPG_TIME;
+ }
+
+ public Event getCurrentEvent() {
+ return currentEvent;
+ }
+
+ public void setCurrentEvent(final Event currentEvent) {
+ this.currentEvent = currentEvent;
+ }
+
+ public Timer getCurrentTimer() {
+ return currentTimer;
+ }
+
+ public void setCurrentTimer(final Timer currentTimer) {
+ this.currentTimer = currentTimer;
+ }
+
+ public Channel getCurrentChannel() {
+ return currentChannel;
+ }
+
+ public void setCurrentChannel(final Channel currentChannel) {
+ clear();
+ this.currentChannel = currentChannel;
+ this.epgListState = EpgListState.EPG_CHANNEL;
+ }
+
+ public EpgSearchParams getCurrentSearch() {
+ return currentSearch;
+ }
+
+ public void setCurrentSearch(final EpgSearchParams currentSearch) {
+ clear();
+ this.currentSearch = currentSearch;
+ this.epgListState = EpgListState.EPG_SEARCH;
+ }
+
+ public EpgListState getEpgListState() {
+ return epgListState;
+ }
+
+ public Class<? extends Activity> getNextActivity() {
+ return nextActivity;
+ }
+
+ public void setNextActivity(final Class<? extends Activity> nextActivity) {
+ this.nextActivity = nextActivity;
+ }
+
+ public List<Activity> getActivitiesToFinish() {
+ return activitiesToFinish;
+ }
+
+ public boolean isReload() {
+ return reload;
+ }
+
+ public void setReload(final boolean reload) {
+ this.reload = reload;
+ }
+
+ /** KeyStore for per app run accepted certificates */
+ private KeyStore sessionKeyStore;
+
+ /**
+ * Gets the temporary accepted certificates
+ * @return KeyStore
+ */
+ public KeyStore getSessionKeyStore() {
+ return sessionKeyStore;
+ }
+
+ /**
+ * Create a new and empty key store
+ */
+ public void initSessionKeyStore() {
+ try {
+ sessionKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ sessionKeyStore.load(null);
+ } catch (final Exception e) {
+ sessionKeyStore = null;
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/AbstractSettingsActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/AbstractSettingsActivity.java
new file mode 100644
index 0000000..ef6dd5e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/AbstractSettingsActivity.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceManager;
+import android.speech.tts.TextToSpeech;
+import android.view.MenuItem;
+
+/**
+ * An abstract activity for all the settings activities.
+ *
+ * @author Jimmy Shih
+ */
+public class AbstractSettingsActivity extends PreferenceActivity {
+
+ private BackupPreferencesListener backupPreferencesListener;
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
+ ApiAdapterFactory.getApiAdapter().configureActionBarHomeAsUp(this);
+
+ PreferenceManager preferenceManager = getPreferenceManager();
+ preferenceManager.setSharedPreferencesName(Constants.SETTINGS_NAME);
+ preferenceManager.setSharedPreferencesMode(Context.MODE_PRIVATE);
+
+ // Set up automatic preferences backup
+ backupPreferencesListener = ApiAdapterFactory.getApiAdapter()
+ .getBackupPreferencesListener(this);
+ preferenceManager.getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(backupPreferencesListener);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() != android.R.id.home) {
+ return super.onOptionsItemSelected(item);
+ }
+ finish();
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ PreferenceManager preferenceManager = getPreferenceManager();
+ preferenceManager.getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(backupPreferencesListener);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api10Adapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api10Adapter.java
new file mode 100644
index 0000000..0527f89
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api10Adapter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import android.annotation.TargetApi;
+
+
+/**
+ * API level 10 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Jimmy Shih
+ */
+@TargetApi(10)
+public class Api10Adapter extends Api9Adapter {
+
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api11Adapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api11Adapter.java
new file mode 100644
index 0000000..ec26635
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api11Adapter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+
+import java.util.List;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.SearchManager;
+import android.content.Context;
+import android.view.MenuItem;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.SearchView;
+
+/**
+ * API level 11 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Jimmy Shih
+ */
+@TargetApi(11)
+public class Api11Adapter extends Api10Adapter {
+
+ @Override
+ public void hideTitle(Activity activity) {
+ // Do nothing
+ }
+
+ @Override
+ public void configureActionBarHomeAsUp(Activity activity) {
+ activity.getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ @Override
+ public void configureListViewContextualMenu(final Activity activity, ListView listView,
+ final ContextualActionModeCallback contextualActionModeCallback) {
+ /*
+
+ listView.setOnItemLongClickListener(new OnItemLongClickListener() {
+ ActionMode actionMode;
+ @Override
+ public boolean onItemLongClick(
+ AdapterView<?> parent, View view, final int position, final long id) {
+ if (actionMode != null) {
+ return false;
+ }
+ actionMode = activity.startActionMode(new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
+ return true;
+ }
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ // Return false to indicate no change.
+ return false;
+ }
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ actionMode = null;
+ }
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ mode.finish();
+ return contextualActionModeCallback.onClick(item.getItemId(), position, id);
+ }
+ });
+ TextView textView = (TextView) view.findViewById(R.id.list_item_name);
+ if (textView != null) {
+ actionMode.setTitle(textView.getText());
+ }
+ view.setSelected(true);
+ return true;
+ }
+ });
+ */
+ };
+
+ @Override
+ public void configureSearchWidget(Activity activity, final MenuItem menuItem) {
+ SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
+ SearchView searchView = (SearchView) menuItem.getActionView();
+ searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.getComponentName()));
+ searchView.setQueryRefinementEnabled(true);
+ }
+
+ @Override
+ public boolean handleSearchMenuSelection(Activity activity) {
+ // Returns false to allow the platform to expand the search widget.
+ return false;
+ }
+
+ @Override
+ public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items) {
+ arrayAdapter.addAll(items);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api14Adapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api14Adapter.java
new file mode 100644
index 0000000..0425191
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api14Adapter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+import android.annotation.TargetApi;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.view.MenuItem;
+import android.widget.SearchView;
+
+/**
+ * API level 14 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Jimmy Shih
+ */
+@TargetApi(14)
+public class Api14Adapter extends Api11Adapter {
+
+ @Override
+ public void configureActionBarHomeAsUp(Activity activity) {
+ ActionBar actionBar = activity.getActionBar();
+ actionBar.setHomeButtonEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ @Override
+ public void configureSearchWidget(Activity activity, final MenuItem menuItem) {
+ super.configureSearchWidget(activity, menuItem);
+ SearchView searchView = (SearchView) menuItem.getActionView();
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ menuItem.collapseActionView();
+ return false;
+ }
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return false;
+ }
+ });
+ searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
+ @Override
+ public boolean onSuggestionSelect(int position) {
+ return false;
+ }
+ @Override
+ public boolean onSuggestionClick(int position) {
+ menuItem.collapseActionView();
+ return false;
+ }
+ });
+ }
+
+ @Override
+ public boolean handleSearchKey(MenuItem menuItem) {
+ menuItem.expandActionView();
+ return true;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api7Adapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api7Adapter.java
new file mode 100644
index 0000000..b89168a
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api7Adapter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+
+import java.util.List;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.view.MenuItem;
+import android.view.Window;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * API level 7 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Bartlomiej Niechwiej
+ */
+public class Api7Adapter implements ApiAdapter {
+
+
+ @Override
+ public BackupPreferencesListener getBackupPreferencesListener(Context context) {
+ return new BackupPreferencesListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ // Do nothing
+ }
+ };
+ }
+
+ @Override
+ public void applyPreferenceChanges(Editor editor) {
+ editor.commit();
+ }
+
+ @Override
+ public void enableStrictMode() {
+ // Not supported
+ }
+
+ @Override
+ public byte[] copyByteArray(byte[] input, int start, int end) {
+ int length = end - start;
+ byte[] output = new byte[length];
+ System.arraycopy(input, start, output, 0, length);
+ return output;
+ }
+
+
+
+ @Override
+ public void hideTitle(Activity activity) {
+ activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+
+ @Override
+ public void configureActionBarHomeAsUp(Activity activity) {
+ // Do nothing
+ }
+
+ @Override
+ public void configureListViewContextualMenu(Activity activity, ListView listView,
+ ContextualActionModeCallback contextualActionModeCallback) {
+ activity.registerForContextMenu(listView);
+ }
+
+ @Override
+ public void configureSearchWidget(Activity activity, MenuItem menuItem) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean handleSearchMenuSelection(Activity activity) {
+ activity.onSearchRequested();
+ return true;
+ }
+
+ @Override
+ public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items) {
+ for (T item : items) {
+ arrayAdapter.add(item);
+ }
+ }
+
+ @Override
+ public boolean handleSearchKey(MenuItem menuItem) {
+ // Return false and allow the framework to handle the search key.
+ return false;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8Adapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8Adapter.java
new file mode 100644
index 0000000..1fc61d3
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8Adapter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import android.content.Context;
+
+/**
+ * API level 8 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Jimmy Shih
+ */
+public class Api8Adapter extends Api7Adapter {
+
+
+ @Override
+ public BackupPreferencesListener getBackupPreferencesListener(Context context) {
+ return new Api8BackupPreferencesListener(context);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8BackupPreferencesListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8BackupPreferencesListener.java
new file mode 100644
index 0000000..90e3976
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api8BackupPreferencesListener.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import android.annotation.TargetApi;
+import android.app.backup.BackupManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * Implementation of {@link BackupPreferencesListener} that calls the
+ * {@link BackupManager}. <br>
+ * For API Level 8 or higher.
+ *
+ * @author Jimmy Shih
+ */
+@TargetApi(8)
+public class Api8BackupPreferencesListener implements BackupPreferencesListener {
+
+ private final BackupManager backupManager;
+
+ public Api8BackupPreferencesListener(Context context) {
+ this.backupManager = new BackupManager(context);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ backupManager.dataChanged();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api9Adapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api9Adapter.java
new file mode 100644
index 0000000..151f317
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Api9Adapter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import java.util.Arrays;
+
+import android.annotation.TargetApi;
+import android.content.SharedPreferences.Editor;
+import android.os.StrictMode;
+import android.util.Log;
+
+/**
+ * API level 9 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Rodrigo Damazio
+ */
+@TargetApi(9)
+public class Api9Adapter extends Api8Adapter {
+
+ @Override
+ public void applyPreferenceChanges(Editor editor) {
+ // Apply asynchronously
+ editor.apply();
+ }
+
+ @Override
+ public void enableStrictMode() {
+ Log.d(Constants.TAG, "Enabling strict mode");
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectDiskWrites()
+ .detectNetwork()
+ .penaltyLog()
+ .build());
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
+ .detectAll()
+ .penaltyLog()
+ .build());
+ }
+
+ @Override
+ public byte[] copyByteArray(byte[] input, int start, int end) {
+ return Arrays.copyOfRange(input, start, end);
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapter.java
new file mode 100644
index 0000000..aa13297
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapter.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import java.util.List;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.view.MenuItem;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * A set of methods that may be implemented differently depending on the Android
+ * API level.
+ *
+ * @author Bartlomiej Niechwiej
+ */
+public interface ApiAdapter {
+
+
+
+ /**
+ * Gets a {@link BackupPreferencesListener}.
+ * <p>
+ * Due to changes in API level 8.
+ *
+ * @param context the context
+ */
+ public BackupPreferencesListener getBackupPreferencesListener(Context context);
+
+ /**
+ * Applies all the changes done to a given preferences editor. Changes may or
+ * may not be applied immediately.
+ * <p>
+ * Due to changes in API level 9.
+ *
+ * @param editor the editor
+ */
+ public void applyPreferenceChanges(SharedPreferences.Editor editor);
+
+ /**
+ * Enables strict mode where supported, only if this is a development build.
+ * <p>
+ * Due to changes in API level 9.
+ */
+ public void enableStrictMode();
+
+ /**
+ * Copies elements from an input byte array into a new byte array, from
+ * indexes start (inclusive) to end (exclusive). The end index must be less
+ * than or equal to the input length.
+ * <p>
+ * Due to changes in API level 9.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @param end the end index
+ * @return a new array containing elements from the input byte array.
+ */
+ public byte[] copyByteArray(byte[] input, int start, int end);
+
+
+
+
+ /**
+ * Hides the title. If the platform supports the action bar, do nothing.
+ * Ideally, with the action bar, we would like to collapse the navigation tabs
+ * into the action bar. However, collapsing is not supported by the
+ * compatibility library.
+ * <p>
+ * Due to changes in API level 11.
+ *
+ * @param activity the activity
+ */
+ public void hideTitle(Activity activity);
+
+ /**
+ * Configures the action bar with the Home button as an Up button. If the
+ * platform doesn't support the action bar, do nothing.
+ * <p>
+ * Due to changes in API level 11.
+ *
+ * @param activity the activity
+ */
+ public void configureActionBarHomeAsUp(Activity activity);
+
+ /**
+ * Configures the list view context menu.
+ * <p>
+ * Due to changes in API level 11.
+ *
+ * @param activity the activity
+ * @param listView the list view
+ * @param contextualActionModeCallback the callback when an item is selected
+ * in the contextual action mode
+ */
+ public void configureListViewContextualMenu(Activity activity, ListView listView,
+ ContextualActionModeCallback contextualActionModeCallback);
+
+ /**
+ * Configures the search widget.
+ *
+ * Due to changes in API level 11.
+ *
+ * @param activity the activity
+ * @param menuItem the search menu item
+ */
+ public void configureSearchWidget(Activity activity, MenuItem menuItem);
+
+ /**
+ * Handles the search menu selection. Returns true if handled.
+ *
+ * Due to changes in API level 11.
+ *
+ * @param activity the activity
+ */
+ public boolean handleSearchMenuSelection(Activity activity);
+
+ /**
+ * Adds all items to an array adapter.
+ *
+ * Due to changes in API level 11.
+ *s
+ * @param arrayAdapter the array adapter
+ * @param items list of items
+ */
+ public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items);
+
+ /**
+ * Handles the search key press. Returns true if handled.
+ *
+ * Due to changes in API level 14.
+ *
+ * @param menu the search menu
+ */
+ public boolean handleSearchKey(MenuItem menu);
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapterFactory.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapterFactory.java
new file mode 100644
index 0000000..5a14c10
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ApiAdapterFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import android.os.Build;
+
+/**
+ * A factory to get the {@link ApiAdapter} for the current device.
+ *
+ * @author Rodrigo Damazio
+ */
+public class ApiAdapterFactory {
+
+ private static ApiAdapter apiAdapter;
+
+ /**
+ * Gets the {@link ApiAdapter} for the current device.
+ */
+ public static ApiAdapter getApiAdapter() {
+ if (apiAdapter == null) {
+ if (Build.VERSION.SDK_INT >= 14) {
+ apiAdapter = new Api14Adapter();
+ return apiAdapter;
+ } else if (Build.VERSION.SDK_INT >= 11) {
+ apiAdapter = new Api11Adapter();
+ return apiAdapter;
+ } else if (Build.VERSION.SDK_INT >= 10) {
+ apiAdapter = new Api10Adapter();
+ return apiAdapter;
+ } else if (Build.VERSION.SDK_INT >= 9) {
+ apiAdapter = new Api9Adapter();
+ return apiAdapter;
+ } else if (Build.VERSION.SDK_INT >= 8) {
+ apiAdapter = new Api8Adapter();
+ return apiAdapter;
+ } else {
+ apiAdapter = new Api7Adapter();
+ return apiAdapter;
+ }
+ }
+ return apiAdapter;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupActivity.java
new file mode 100644
index 0000000..ed0da12
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.widget.Toast;
+import de.bjusystems.vdrmanager.R;
+
+/**
+ * Activity to backup data to the SD card.
+ *
+ * @author Jimmy Shih
+ */
+public class BackupActivity extends Activity {
+
+ private static final int DIALOG_PROGRESS_ID = 0;
+
+ private BackupAsyncTask backupAsyncTask;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Object retained = getLastNonConfigurationInstance();
+ if (retained instanceof BackupAsyncTask) {
+ backupAsyncTask = (BackupAsyncTask) retained;
+ backupAsyncTask.setActivity(this);
+ } else {
+ backupAsyncTask = new BackupAsyncTask(this);
+ backupAsyncTask.execute();
+ }
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ backupAsyncTask.setActivity(null);
+ return backupAsyncTask;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ if (id != DIALOG_PROGRESS_ID) {
+ return null;
+ }
+ return DialogUtils.createSpinnerProgressDialog(
+ this, R.string.settings_backup_now_progress_message, new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
+ }
+
+ /**
+ * Invokes when the associated AsyncTask completes.
+ *
+ * @param success true if the AsyncTask is successful
+ * @param messageId message id to display to user
+ */
+ public void onAsyncTaskCompleted(boolean success, int messageId) {
+ removeDialog(DIALOG_PROGRESS_ID);
+ Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
+ finish();
+ }
+
+ /**
+ * Shows the progress dialog.
+ */
+ public void showProgressDialog() {
+ showDialog(DIALOG_PROGRESS_ID);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupAsyncTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupAsyncTask.java
new file mode 100644
index 0000000..8e98ac0
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupAsyncTask.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+
+import java.io.IOException;
+
+import android.os.AsyncTask;
+import android.util.Log;
+import de.bjusystems.vdrmanager.R;
+
+/**
+ * AsyncTask to backup data to the SD card.
+ *
+ * @author Jimmy Shih
+ */
+public class BackupAsyncTask extends AsyncTask<Void, Integer, Boolean> {
+
+ private static final String TAG = BackupAsyncTask.class.getSimpleName();
+
+ private BackupActivity backupActivity;
+ private final ExternalFileBackup externalFileBackup;
+
+ // true if the AsyncTask result is success
+ private boolean success;
+
+ // true if the AsyncTask has completed
+ private boolean completed;
+
+ // message id to return to the activity
+ private int messageId;
+
+ /**
+ * Creates an AsyncTask.
+ *
+ * @param backupActivity the activity currently associated with this
+ * AsyncTask
+ */
+ public BackupAsyncTask(BackupActivity backupActivity) {
+ this.backupActivity = backupActivity;
+ this.externalFileBackup = new ExternalFileBackup(backupActivity);
+ success = false;
+ completed = false;
+ messageId = R.string.sd_card_save_error;
+ }
+
+ /**
+ * Sets the current {@link BackupActivity} associated with this AyncTask.
+ *
+ * @param activity the current {@link BackupActivity}, can be null
+ */
+ public void setActivity(BackupActivity activity) {
+ this.backupActivity = activity;
+ if (completed && backupActivity != null) {
+ backupActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (backupActivity != null) {
+ backupActivity.showProgressDialog();
+ }
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ if (!FileUtils.isSdCardAvailable()) {
+ messageId = R.string.sd_card_error_no_storage;
+ return false;
+ }
+
+ if (!externalFileBackup.isBackupsDirectoryAvailable(true)) {
+ messageId = R.string.sd_card_save_error_create_dir;
+ return false;
+ }
+
+ try {
+ externalFileBackup.writeToDefaultFile();
+ messageId = R.string.sd_card_save_success;
+ return true;
+ } catch (IOException e) {
+ Log.d(TAG, "IO exception", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ success = result;
+ completed = true;
+ if (backupActivity != null) {
+ backupActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupPreferencesListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupPreferencesListener.java
new file mode 100644
index 0000000..c9a5b2f
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupPreferencesListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+
+/**
+ * Shared preferences listener which notifies the backup system about new data
+ * being available for backup.
+ *
+ * @author Rodrigo Damazio
+ */
+public interface BackupPreferencesListener extends OnSharedPreferenceChangeListener {
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupSettingsActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupSettingsActivity.java
new file mode 100644
index 0000000..e906a20
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/BackupSettingsActivity.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceManager;
+import de.bjusystems.vdrmanager.R;
+
+/**
+ * An activity for accessing the backup settings.
+ *
+ * @author Jimmy Shih
+ */
+public class BackupSettingsActivity extends AbstractSettingsActivity {
+
+ private static final int DIALOG_CONFIRM_RESTORE_ID = 0;
+
+ Preference backupPreference;
+ Preference restorePreference;
+
+ /*
+ * Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
+ * Anonymous inner class will get garbage collected.
+ */
+ private final OnSharedPreferenceChangeListener
+ sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
+ // Note that key can be null
+ //if (PreferencesUtils.getKey(BackupSettingsActivity.this, R.string.recording_track_id_key)
+ // .equals(key)) {
+ //updateUi();
+ //}
+ }
+ };
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ PreferenceManager.getDefaultSharedPreferences(this)
+ .registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
+
+ addPreferencesFromResource(R.xml.backup_settings);
+ backupPreference = findPreference(getString(R.string.settings_backup_now_key));
+ backupPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = IntentUtils.newIntent(BackupSettingsActivity.this, BackupActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ });
+ restorePreference = findPreference(getString(R.string.settings_backup_restore_key));
+ restorePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ showDialog(DIALOG_CONFIRM_RESTORE_ID);
+ return true;
+ }
+ });
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ if (id != DIALOG_CONFIRM_RESTORE_ID) {
+ return null;
+ }
+ return DialogUtils.createConfirmationDialog(this,
+ R.string.settings_backup_restore_confirm_message, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = IntentUtils.newIntent(
+ BackupSettingsActivity.this, RestoreChooserActivity.class);
+ startActivity(intent);
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ //updateUi();
+ }
+
+// /**
+// * Updates the UI based on the recording state.
+// */
+// private void updateUi() {
+// boolean isRecording = PreferencesUtils.getLong(this, R.string.recording_track_id_key)
+// != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
+// backupPreference.setEnabled(!isRecording);
+// restorePreference.setEnabled(!isRecording);
+// backupPreference.setSummary(isRecording ? R.string.settings_not_while_recording
+// : R.string.settings_backup_now_summary);
+// restorePreference.setSummary(isRecording ? R.string.settings_not_while_recording
+// : R.string.settings_backup_restore_summary);
+// }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Constants.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Constants.java
new file mode 100644
index 0000000..491ec6f
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/Constants.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+/**
+ * Constants used by the MyTracks application.
+ *
+ * @author Leif Hendrik Wilden
+ */
+public abstract class Constants {
+
+ /**
+ * Should be used by all log statements
+ */
+ public static final String TAG = "vdrmanager";
+
+ /**
+ * Name of the top-level directory inside the SD card where our files will
+ * be read from/written to.
+ */
+ public static final String SDCARD_TOP_DIR = "vdrmanager";
+
+ /**
+ * The number of distance readings to smooth to get a stable signal.
+ */
+ public static final int DISTANCE_SMOOTHING_FACTOR = 25;
+
+ /**
+ * The number of elevation readings to smooth to get a somewhat accurate
+ * signal.
+ */
+ public static final int ELEVATION_SMOOTHING_FACTOR = 25;
+
+ /**
+ * The number of grade readings to smooth to get a somewhat accurate signal.
+ */
+ public static final int GRADE_SMOOTHING_FACTOR = 5;
+
+ /**
+ * The number of speed reading to smooth to get a somewhat accurate signal.
+ */
+ public static final int SPEED_SMOOTHING_FACTOR = 25;
+
+ /**
+ * Maximum number of track points displayed by the map overlay.
+ */
+ public static final int MAX_DISPLAYED_TRACK_POINTS = 10000;
+
+ /**
+ * Target number of track points displayed by the map overlay.
+ * We may display more than this number of points.
+ */
+ public static final int TARGET_DISPLAYED_TRACK_POINTS = 5000;
+
+ /**
+ * Maximum number of track points ever loaded at once from the provider into
+ * memory.
+ * With a recording frequency of 2 seconds, 15000 corresponds to 8.3 hours.
+ */
+ public static final int MAX_LOADED_TRACK_POINTS = 20000;
+
+ /**
+ * Maximum number of track points ever loaded at once from the provider into
+ * memory in a single call to read points.
+ */
+ public static final int MAX_LOADED_TRACK_POINTS_PER_BATCH = 1000;
+
+ /**
+ * Maximum number of way points displayed by the map overlay.
+ */
+ public static final int MAX_DISPLAYED_WAYPOINTS_POINTS = 128;
+
+ /**
+ * Maximum number of way points that will be loaded at one time.
+ */
+ public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
+
+ /**
+ * Any time segment where the distance traveled is less than this value will
+ * not be considered moving.
+ */
+ public static final double MAX_NO_MOVEMENT_DISTANCE = 2;
+
+ /**
+ * Anything faster than that (in meters per second) will be considered moving.
+ */
+ public static final double MAX_NO_MOVEMENT_SPEED = 0.224;
+
+ /**
+ * Ignore any acceleration faster than this.
+ * Will ignore any speeds that imply accelaration greater than 2g's
+ * 2g = 19.6 m/s^2 = 0.0002 m/ms^2 = 0.02 m/(m*ms)
+ */
+ public static final double MAX_ACCELERATION = 0.02;
+
+ /** Maximum age of a GPS location to be considered current. */
+ public static final long MAX_LOCATION_AGE_MS = 60 * 1000; // 1 minute
+
+ /** Maximum age of a network location to be considered current. */
+ public static final long MAX_NETWORK_AGE_MS = 1000 * 60 * 10; // 10 minutes
+
+ /**
+ * The type of account that we can use for gdata uploads.
+ */
+ public static final String ACCOUNT_TYPE = "com.google";
+
+ /**
+ * The name of extra intent property to indicate whether we want to resume
+ * a previously recorded track.
+ */
+ public static final String
+ RESUME_TRACK_EXTRA_NAME = "com.google.android.apps.mytracks.RESUME_TRACK";
+
+ public static final String MAPSHOP_BASE_URL = "https://maps.google.com/maps/ms";
+
+ public static final String SETTINGS_NAME = "SettingsActivity";
+
+ /**
+ * This is an abstract utility class.
+ */
+ protected Constants() { }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContentTypeIds.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContentTypeIds.java
new file mode 100644
index 0000000..bf48983
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContentTypeIds.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+/**
+ * Utilities for serializing primitive types.
+ *
+ * @author Rodrigo Damazio
+ */
+public class ContentTypeIds {
+ public static final byte BOOLEAN_TYPE_ID = 0;
+ public static final byte LONG_TYPE_ID = 1;
+ public static final byte INT_TYPE_ID = 2;
+ public static final byte FLOAT_TYPE_ID = 3;
+ public static final byte DOUBLE_TYPE_ID = 4;
+ public static final byte STRING_TYPE_ID = 5;
+ public static final byte BLOB_TYPE_ID = 6;
+
+ private ContentTypeIds() { /* Not instantiable */ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContextualActionModeCallback.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContextualActionModeCallback.java
new file mode 100644
index 0000000..d79d4be
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ContextualActionModeCallback.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+/**
+ * Callback when an item in the contextual action mode is selected.
+ *
+ * @author Jimmy Shih
+ */
+public interface ContextualActionModeCallback {
+
+ /**
+ * Invoked when an item is selected.
+ *
+ * @param itemId the context menu item id
+ * @param position the position of the selected row
+ * @param id the id of the selected row, if available
+ */
+ public boolean onClick(int itemId, int position, long id);
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/DialogUtils.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/DialogUtils.java
new file mode 100644
index 0000000..c9b716c
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/DialogUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import de.bjusystems.vdrmanager.R;
+
+/**
+ * Utilities for creating dialogs.
+ *
+ * @author Jimmy Shih
+ */
+public class DialogUtils {
+
+ private DialogUtils() {}
+
+ /**
+ * Creates a confirmation dialog.
+ *
+ * @param context the context
+ * @param messageId the confirmation message id
+ * @param onClickListener the listener to invoke when the user clicks OK
+ */
+ public static Dialog createConfirmationDialog(
+ Context context, int messageId, DialogInterface.OnClickListener onClickListener) {
+ return new AlertDialog.Builder(context)
+ .setCancelable(true)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(context.getString(messageId))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, onClickListener)
+ .setTitle(R.string.generic_confirm_title)
+ .create();
+ }
+
+ /**
+ * Creates a spinner progress dialog.
+ *
+ * @param context the context
+ * @param messageId the progress message id
+ * @param onCancelListener the cancel listener
+ */
+ public static ProgressDialog createSpinnerProgressDialog(
+ Context context, int messageId, DialogInterface.OnCancelListener onCancelListener) {
+ return createProgressDialog(true, context, messageId, onCancelListener);
+ }
+
+ /**
+ * Creates a horizontal progress dialog.
+ *
+ * @param context the context
+ * @param messageId the progress message id
+ * @param onCancelListener the cancel listener
+ * @param formatArgs the format arguments for the messageId
+ */
+ public static ProgressDialog createHorizontalProgressDialog(Context context, int messageId,
+ DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
+ return createProgressDialog(false, context, messageId, onCancelListener, formatArgs);
+ }
+
+ /**
+ * Creates a progress dialog.
+ *
+ * @param spinner true to use the spinner style
+ * @param context the context
+ * @param messageId the progress message id
+ * @param onCancelListener the cancel listener
+ * @param formatArgs the format arguments for the message id
+ */
+ private static ProgressDialog createProgressDialog(boolean spinner, Context context,
+ int messageId, DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
+ ProgressDialog progressDialog = new ProgressDialog(context);
+ progressDialog.setCancelable(true);
+ progressDialog.setIcon(android.R.drawable.ic_dialog_info);
+ progressDialog.setIndeterminate(true);
+ progressDialog.setMessage(context.getString(messageId, formatArgs));
+ progressDialog.setOnCancelListener(onCancelListener);
+ progressDialog.setProgressStyle(spinner ? ProgressDialog.STYLE_SPINNER
+ : ProgressDialog.STYLE_HORIZONTAL);
+ progressDialog.setTitle(R.string.generic_progress_title);
+ return progressDialog;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ExternalFileBackup.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ExternalFileBackup.java
new file mode 100644
index 0000000..790375e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/ExternalFileBackup.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import de.bjusystems.vdrmanager.data.db.DBAccess;
+
+
+/**
+ * Handler for writing or reading single-file backups.
+ *
+ * @author Rodrigo Damazio
+ */
+class ExternalFileBackup {
+ // Filename format - in UTC
+ private static final SimpleDateFormat BACKUP_FILENAME_FORMAT = new SimpleDateFormat(
+ "'backup-'yyyy-MM-dd_HH-mm-ss'.zip'");
+ static {
+ BACKUP_FILENAME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ private static final String BACKUPS_SUBDIR = "backups";
+ private static final int BACKUP_FORMAT_VERSION = 1;
+ private static final String ZIP_ENTRY_NAME = "backup.mybillingbuddy.v"
+ + BACKUP_FORMAT_VERSION;
+ private static final int COMPRESSION_LEVEL = 8;
+
+ private final Context context;
+
+ public ExternalFileBackup(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns whether the backups directory is (or can be made) available.
+ *
+ * @param create
+ * whether to try creating the directory if it doesn't exist
+ */
+ public boolean isBackupsDirectoryAvailable(boolean create) {
+ return getBackupsDirectory(create) != null;
+ }
+
+ /**
+ * Returns the backup directory, or null if not available.
+ *
+ * @param create
+ * whether to try creating the directory if it doesn't exist
+ */
+ private File getBackupsDirectory(boolean create) {
+ String dirName = FileUtils.buildExternalDirectoryPath(BACKUPS_SUBDIR);
+ final File dir = new File(dirName);
+ Log.d(Constants.TAG, "Dir: " + dir.getAbsolutePath());
+ if (create) {
+ // Try to create - if that fails, return null
+ return FileUtils.ensureDirectoryExists(dir) ? dir : null;
+ } else {
+ // Return it if it already exists, otherwise return null
+ return dir.isDirectory() ? dir : null;
+ }
+ }
+
+ /**
+ * Returns a list of available backups to be restored.
+ */
+ public Date[] getAvailableBackups() {
+ File dir = getBackupsDirectory(false);
+ if (dir == null) {
+ return null;
+ }
+ String[] fileNames = dir.list();
+
+ List<Date> backupDates = new ArrayList<Date>(fileNames.length);
+ for (int i = 0; i < fileNames.length; i++) {
+ String fileName = fileNames[i];
+ try {
+ backupDates.add(BACKUP_FILENAME_FORMAT.parse(fileName));
+ } catch (ParseException e) {
+ // Not a backup file, ignore
+ }
+ }
+
+ return backupDates.toArray(new Date[backupDates.size()]);
+ }
+
+ /**
+ * Writes the backup to the default file.
+ */
+ public void writeToDefaultFile() throws IOException {
+ writeToFile(getFileForDate(new Date()));
+ }
+
+ /**
+ * Restores the backup from the given date.
+ */
+ public void restoreFromDate(Date when) throws IOException {
+ restoreFromFile(getFileForDate(when));
+ }
+
+ public void restoreFromFile(String path ) throws IOException {
+ restoreFromFile(new File(path));
+ }
+
+ /**
+ * Produces the proper file descriptor for the given backup date.
+ */
+ private File getFileForDate(Date when) {
+ File dir = getBackupsDirectory(false);
+ String fileName = BACKUP_FILENAME_FORMAT.format(when);
+ File file = new File(dir, fileName);
+ return file;
+ }
+
+ /**
+ * Synchronously writes a backup to the given file.
+ */
+ private void writeToFile(File outputFile) throws IOException {
+ Log.d(Constants.TAG,
+ "Writing backup to file " + outputFile.getAbsolutePath());
+
+ // Create all the auxiliary classes that will do the writing
+ //DatabaseDumper trackDumper = new DatabaseDumper(
+ // BackupColumns.TRACKS_BACKUP_COLUMNS,
+ //BackupColumns.TRACKS_BACKUP_COLUMN_TYPES, false);
+ //DatabaseDumper waypointDumper = new DatabaseDumper(
+ // BackupColumns.WAYPOINTS_BACKUP_COLUMNS,
+ //BackupColumns.WAYPOINTS_BACKUP_COLUMN_TYPES, false);
+ //DatabaseDumper pointDumper = new DatabaseDumper(
+ // BackupColumns.POINTS_BACKUP_COLUMNS,
+ //BackupColumns.POINTS_BACKUP_COLUMN_TYPES, false);
+
+ // Open the target for writing
+ FileOutputStream outputStream = new FileOutputStream(outputFile);
+ ZipOutputStream compressedStream = new ZipOutputStream(outputStream);
+ compressedStream.setLevel(COMPRESSION_LEVEL);
+ compressedStream.putNextEntry(new ZipEntry(ZIP_ENTRY_NAME));
+ DataOutputStream outWriter = new DataOutputStream(compressedStream);
+
+ try {
+ // Dump the entire contents of each table
+// ContentResolver contentResolver = context.getContentResolver();
+// Cursor tracksCursor = contentResolver.query(
+// TracksColumns.CONTENT_URI, null, null, null, null);
+// try {
+// trackDumper.writeAllRows(tracksCursor, outWriter);
+// } finally {
+// tracksCursor.close();
+// }
+//
+// Cursor waypointsCursor = contentResolver.query(
+// WaypointsColumns.CONTENT_URI, null, null, null, null);
+// try {
+// waypointDumper.writeAllRows(waypointsCursor, outWriter);
+// } finally {
+// waypointsCursor.close();
+// }
+//
+// Cursor pointsCursor = contentResolver.query(
+// TrackPointsColumns.CONTENT_URI, null, null, null, null);
+// try {
+// pointDumper.writeAllRows(pointsCursor, outWriter);
+// } finally {
+// pointsCursor.close();
+// }
+
+ // Dump preferences
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ PreferenceBackupHelper preferencesHelper = new PreferenceBackupHelper();
+ preferencesHelper.exportPreferences(preferences, outWriter);
+
+
+ File f = new File(DBAccess.getDataBaseFile());
+ if(f.exists()){
+ compressedStream.putNextEntry(new ZipEntry(DBAccess.DATABASE_NAME));
+ IOUtils.copy(new FileInputStream(DBAccess.getDataBaseFile()), outWriter);
+ }
+
+
+
+
+
+
+ } catch (IOException e) {
+ // We tried to delete the partially created file, but do nothing
+ // if that also fails.
+ if (!outputFile.delete()) {
+ Log.w(Constants.TAG,
+ "Failed to delete file " + outputFile.getAbsolutePath());
+ }
+
+ throw e;
+ } finally {
+ compressedStream.closeEntry();
+ compressedStream.close();
+ }
+ }
+
+ /**
+ * Synchronously restores the backup from the given file.
+ */
+ private void restoreFromFile(File inputFile) throws IOException {
+ Log.d(Constants.TAG,
+ "Restoring from file " + inputFile.getAbsolutePath());
+
+
+
+ ZipFile zipFile = new ZipFile(inputFile, ZipFile.OPEN_READ);
+ ZipEntry zipEntry = zipFile.getEntry(ZIP_ENTRY_NAME);
+ if (zipEntry == null) {
+ throw new IOException("Invalid backup ZIP file");
+ }
+
+ InputStream compressedStream = zipFile.getInputStream(zipEntry);
+ DataInputStream reader = new DataInputStream(compressedStream);
+
+ try {
+
+ // Restore preferences
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ PreferenceBackupHelper preferencesHelper = new PreferenceBackupHelper();
+ preferencesHelper.importPreferences(reader, preferences);
+
+
+
+
+ zipEntry = zipFile.getEntry(DBAccess.DATABASE_NAME);
+ if (zipEntry != null) {
+ IOUtils.copy(zipFile.getInputStream(zipEntry), new FileOutputStream(DBAccess.getDataBaseFile()));
+ deleteJournal(DBAccess.getDataBaseFile());
+ }
+
+ } finally {
+ compressedStream.close();
+ zipFile.close();
+ }
+ }
+
+ private static void deleteJournal(String db){
+ if(db == null){
+ return;
+ }
+ new File(db+"-journal").delete();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/FileUtils.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/FileUtils.java
new file mode 100644
index 0000000..d53a80b
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/FileUtils.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+
+import android.os.Environment;
+
+import java.io.File;
+
+/**
+ * Utilities for dealing with files.
+ *
+ * @author Rodrigo Damazio
+ */
+public class FileUtils {
+
+ private FileUtils() {}
+
+ /**
+ * The maximum FAT32 path length. See the FAT32 spec at
+ * http://msdn.microsoft.com/en-us/windows/hardware/gg463080
+ */
+ static final int MAX_FAT32_PATH_LENGTH = 260;
+
+ /**
+ * Returns whether the SD card is available.
+ */
+ public static boolean isSdCardAvailable() {
+ return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
+ }
+
+ /**
+ * Ensures the given directory exists by creating it and its parents if
+ * necessary.
+ *
+ * @return whether the directory exists (either already existed or was
+ * successfully created)
+ */
+ public static boolean ensureDirectoryExists(File dir) {
+ if (dir.exists() && dir.isDirectory()) {
+ return true;
+ }
+ if (dir.mkdirs()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Builds a path inside the My Tracks directory in the SD card.
+ *
+ * @param components the path components inside the mytracks directory
+ * @return the full path to the destination
+ */
+ public static String buildExternalDirectoryPath(String... components) {
+ StringBuilder dirNameBuilder = new StringBuilder();
+ dirNameBuilder.append(Environment.getExternalStorageDirectory());
+ dirNameBuilder.append(File.separatorChar);
+ dirNameBuilder.append(Constants.SDCARD_TOP_DIR);
+ for (String component : components) {
+ dirNameBuilder.append(File.separatorChar);
+ dirNameBuilder.append(component);
+ }
+ return dirNameBuilder.toString();
+ }
+
+ /**
+ * Builds a filename with the given base name (prefix) and the given
+ * extension, possibly adding a suffix to ensure the file doesn't exist.
+ *
+ * @param directory the directory the file will live in
+ * @param fileBaseName the prefix for the file name
+ * @param extension the file's extension
+ * @return the complete file name, without the directory
+ */
+ public static synchronized String buildUniqueFileName(
+ File directory, String fileBaseName, String extension) {
+ return buildUniqueFileName(directory, fileBaseName, extension, 0);
+ }
+
+ /**
+ * Builds a filename with the given base and the given extension, possibly
+ * adding a suffix to ensure the file doesn't exist.
+ *
+ * @param directory the directory the filename will be located in
+ * @param base the base for the filename
+ * @param extension the extension for the filename
+ * @param suffix the first numeric suffix to try to use, or 0 for none
+ * @return the complete filename, without the directory
+ */
+ private static String buildUniqueFileName(
+ File directory, String base, String extension, int suffix) {
+ String suffixName = "";
+ if (suffix > 0) {
+ suffixName += "(" + Integer.toString(suffix) + ")";
+ }
+ suffixName += "." + extension;
+
+ String baseName = sanitizeFileName(base);
+ baseName = truncateFileName(directory, baseName, suffixName);
+ String fullName = baseName + suffixName;
+
+ if (!new File(directory, fullName).exists()) {
+ return fullName;
+ }
+ return buildUniqueFileName(directory, base, extension, suffix + 1);
+ }
+
+ /**
+ * Sanitizes the name as a valid fat32 filename. For simplicity, fat32
+ * filename characters may be any combination of letters, digits, or
+ * characters with code point values greater than 127. Replaces the invalid
+ * characters with "_" and collapses multiple "_" together.
+ *
+ * @param name name
+ */
+ static String sanitizeFileName(String name) {
+ StringBuffer buffer = new StringBuffer(name.length());
+ for (int i = 0; i < name.length(); i++) {
+ int codePoint = name.codePointAt(i);
+ char character = name.charAt(i);
+ if (Character.isLetterOrDigit(character) || codePoint > 127 || isSpecialFat32(character)) {
+ buffer.appendCodePoint(codePoint);
+ } else {
+ buffer.append("_");
+ }
+ }
+ String result = buffer.toString();
+ return result.replaceAll("_+", "_");
+ }
+
+ /**
+ * Returns true if it is a special FAT32 character.
+ *
+ * @param character the character
+ */
+ private static boolean isSpecialFat32(char character) {
+ switch (character) {
+ case '$':
+ case '%':
+ case '\'':
+ case '-':
+ case '_':
+ case '@':
+ case '~':
+ case '`':
+ case '!':
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '^':
+ case '#':
+ case '&':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ case '[':
+ case ']':
+ case ' ':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Truncates the name if necessary so the filename path length (directory +
+ * name + suffix) meets the Fat32 path limit.
+ *
+ * @param directory directory
+ * @param name name
+ * @param suffix suffix
+ */
+ static String truncateFileName(File directory, String name, String suffix) {
+ // 1 at the end accounts for the FAT32 filename trailing NUL character
+ int requiredLength = directory.getPath().length() + suffix.length() + 1;
+ if (name.length() + requiredLength > MAX_FAT32_PATH_LENGTH) {
+ int limit = MAX_FAT32_PATH_LENGTH - requiredLength;
+ return name.substring(0, limit);
+ } else {
+ return name;
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IOUtils.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IOUtils.java
new file mode 100644
index 0000000..53be61c
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IOUtils.java
@@ -0,0 +1,217 @@
+package de.bjusystems.vdrmanager.backup;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * General IO stream manipulation utilities.
+ * <p>
+ * This class provides static utility methods for input/output operations.
+ * <ul>
+ * <li>closeQuietly - these methods close a stream ignoring nulls and exceptions
+ * <li>toXxx/read - these methods read data from a stream
+ * <li>write - these methods write data to a stream
+ * <li>copy - these methods copy all the data from one stream to another
+ * <li>contentEquals - these methods compare the content of two streams
+ * </ul>
+ * <p>
+ * The byte-to-char methods and char-to-byte methods involve a conversion step.
+ * Two methods are provided in each case, one that uses the platform default
+ * encoding and the other which allows you to specify an encoding. You are
+ * encouraged to always specify an encoding because relying on the platform
+ * default can lead to unexpected results, for example when moving from
+ * development to production.
+ * <p>
+ * All the methods in this class that read a stream are buffered internally.
+ * This means that there is no cause to use a <code>BufferedInputStream</code>
+ * or <code>BufferedReader</code>. The default buffer size of 4K has been shown
+ * to be efficient in tests.
+ * <p>
+ * Wherever possible, the methods in this class do <em>not</em> flush or close
+ * the stream. This is to avoid making non-portable assumptions about the
+ * streams' origin and further use. Thus the caller is still responsible for
+ * closing streams after use.
+ * <p>
+ * Origin of code: Excalibur.
+ *
+ * @author Peter Donald
+ * @author Jeff Turner
+ * @author Matthew Hawthorne
+ * @author Stephen Colebourne
+ * @author Gareth Davis
+ * @author Ian Springer
+ * @author Niall Pemberton
+ * @author Sandy McArthur
+ * @version $Id: IOUtils.java 481854 2006-12-03 18:30:07Z scolebourne $
+ */
+public class IOUtils {
+ // NOTE: This class is focussed on InputStream, OutputStream, Reader and
+ // Writer. Each method should take at least one of these as a parameter,
+ // or return one of them.
+
+ /**
+ * The Unix directory separator character.
+ */
+ public static final char DIR_SEPARATOR_UNIX = '/';
+ /**
+ * The Windows directory separator character.
+ */
+ public static final char DIR_SEPARATOR_WINDOWS = '\\';
+ /**
+ * The system directory separator character.
+ */
+ public static final char DIR_SEPARATOR = File.separatorChar;
+ /**
+ * The Unix line separator string.
+ */
+ public static final String LINE_SEPARATOR_UNIX = "\n";
+ /**
+ * The Windows line separator string.
+ */
+ public static final String LINE_SEPARATOR_WINDOWS = "\r\n";
+ /**
+ * The system line separator string.
+ */
+ public static final String LINE_SEPARATOR;
+ static {
+ // avoid security issues
+ StringWriter buf = new StringWriter(4);
+ PrintWriter out = new PrintWriter(buf);
+ out.println();
+ LINE_SEPARATOR = buf.toString();
+ }
+
+ /**
+ * The default buffer size to use.
+ */
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+ /**
+ * Instances should NOT be constructed in standard programming.
+ */
+ public IOUtils() {
+ super();
+ }
+
+ // copy from InputStream
+ // -----------------------------------------------------------------------
+ /**
+ * Copy bytes from an <code>InputStream</code> to an
+ * <code>OutputStream</code>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ * <p>
+ * Large streams (over 2GB) will return a bytes copied value of
+ * <code>-1</code> after the copy has completed since the correct number of
+ * bytes cannot be returned as an int. For large streams use the
+ * <code>copyLarge(InputStream, OutputStream)</code> method.
+ *
+ * @param input
+ * the <code>InputStream</code> to read from
+ * @param output
+ * the <code>OutputStream</code> to write to
+ * @return the number of bytes copied
+ * @throws NullPointerException
+ * if the input or output is null
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws ArithmeticException
+ * if the byte count is too large
+ * @since Commons IO 1.1
+ */
+ public static int copy(final InputStream input, final OutputStream output)
+ throws IOException {
+ long count = copyLarge(input, output);
+ if (count > Integer.MAX_VALUE) {
+ return -1;
+ }
+ return (int) count;
+ }
+
+ /**
+ * Copy bytes from a large (over 2GB) <code>InputStream</code> to an
+ * <code>OutputStream</code>.
+ * <p>
+ * This method buffers the input internally, so there is no need to use a
+ * <code>BufferedInputStream</code>.
+ *
+ * @param input
+ * the <code>InputStream</code> to read from
+ * @param output
+ * the <code>OutputStream</code> to write to
+ * @return the number of bytes copied
+ * @throws NullPointerException
+ * if the input or output is null
+ * @throws IOException
+ * if an I/O error occurs
+ * @since Commons IO 1.3
+ */
+ public static long copyLarge(final InputStream input,
+ final OutputStream output) throws IOException {
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ long count = 0;
+ int n = 0;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+ /**
+ * Unconditionally close an <code>InputStream</code>.
+ * <p>
+ * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
+ * This is typically used in finally blocks.
+ *
+ * @param input the InputStream to close, may be null or already closed
+ */
+ public static void closeQuietly(InputStream input) {
+ try {
+ if (input != null) {
+ input.close();
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ /**
+ * Unconditionally close an <code>OutputStream</code>.
+ * <p>
+ * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
+ * This is typically used in finally blocks.
+ *
+ * @param output the OutputStream to close, may be null or already closed
+ */
+ public static void closeQuietly(OutputStream output) {
+ try {
+ if (output != null) {
+ output.close();
+ }
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IntentUtils.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IntentUtils.java
new file mode 100644
index 0000000..43131b4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/IntentUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Utilities for creating intents.
+ *
+ * @author Jimmy Shih
+ */
+public class IntentUtils {
+
+ public static final String TEXT_PLAIN_TYPE = "text/plain";
+
+ private IntentUtils() {}
+
+ /**
+ * Creates an intent with {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} and
+ * {@link Intent#FLAG_ACTIVITY_NEW_TASK}.
+ *
+ * @param context the context
+ * @param cls the class
+ */
+ public static final Intent newIntent(Context context, Class<?> cls) {
+ return new Intent(context, cls).addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/LineIterator.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/LineIterator.java
new file mode 100644
index 0000000..c60ee66
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/LineIterator.java
@@ -0,0 +1,5 @@
+package de.bjusystems.vdrmanager.backup;
+
+public class LineIterator {
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/PreferenceBackupHelper.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/PreferenceBackupHelper.java
new file mode 100644
index 0000000..30a32b4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/PreferenceBackupHelper.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package de.bjusystems.vdrmanager.backup;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+/**
+ * Helper for backing up and restoring shared preferences.
+ *
+ * @author Rodrigo Damazio
+ */
+class PreferenceBackupHelper {
+
+ private static final int BUFFER_SIZE = 2048;
+
+ /**
+ * Exports all shared preferences from the given object as a byte array.
+ *
+ * @param preferences the preferences to export
+ * @return the corresponding byte array
+ * @throws IOException if there are any errors while writing to the byte array
+ */
+ public byte[] exportPreferences(SharedPreferences preferences)
+ throws IOException {
+ ByteArrayOutputStream bufStream = new ByteArrayOutputStream(BUFFER_SIZE);
+ DataOutputStream outWriter = new DataOutputStream(bufStream);
+ exportPreferences(preferences, outWriter);
+
+ return bufStream.toByteArray();
+ }
+
+ /**
+ * Exports all shared preferences from the given object into the given output
+ * stream.
+ *
+ * @param preferences the preferences to export
+ * @param outWriter the stream to write them to
+ * @throws IOException if there are any errors while writing the output
+ */
+ public void exportPreferences(
+ SharedPreferences preferences,
+ DataOutputStream outWriter) throws IOException {
+ Map<String, ?> values = preferences.getAll();
+
+ outWriter.writeInt(values.size());
+ for (Map.Entry<String, ?> entry : values.entrySet()) {
+ writePreference(entry.getKey(), entry.getValue(), outWriter);
+ }
+ outWriter.flush();
+ }
+
+ /**
+ * Imports all preferences from the given byte array.
+ *
+ * @param data the byte array to read preferences from
+ * @param preferences the shared preferences to edit
+ * @throws IOException if there are any errors while reading
+ */
+ public void importPreferences(byte[] data, SharedPreferences preferences)
+ throws IOException {
+ ByteArrayInputStream bufStream = new ByteArrayInputStream(data);
+ DataInputStream reader = new DataInputStream(bufStream);
+
+ importPreferences(reader, preferences);
+ }
+
+ /**
+ * Imports all preferences from the given stream.
+ *
+ * @param reader the stream to read from
+ * @param preferences the shared preferences to edit
+ * @throws IOException if there are any errors while reading
+ */
+ public void importPreferences(DataInputStream reader,
+ SharedPreferences preferences) throws IOException {
+ Editor editor = preferences.edit();
+ editor.clear();
+
+ int numPreferences = reader.readInt();
+ for (int i = 0; i < numPreferences; i++) {
+ String name = reader.readUTF();
+ byte typeId = reader.readByte();
+ readAndSetPreference(name, typeId, reader, editor);
+ }
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
+ }
+
+ /**
+ * Reads a single preference and sets it into the given editor.
+ *
+ * @param name the name of the preference to read
+ * @param typeId the type ID of the preference to read
+ * @param reader the reader to read from
+ * @param editor the editor to set the preference in
+ * @throws IOException if there are errors while reading
+ */
+ private void readAndSetPreference(String name, byte typeId,
+ DataInputStream reader, Editor editor) throws IOException {
+ switch (typeId) {
+ case ContentTypeIds.BOOLEAN_TYPE_ID:
+ editor.putBoolean(name, reader.readBoolean());
+ return;
+ case ContentTypeIds.LONG_TYPE_ID:
+ editor.putLong(name, reader.readLong());
+ return;
+ case ContentTypeIds.FLOAT_TYPE_ID:
+ editor.putFloat(name, reader.readFloat());
+ return;
+ case ContentTypeIds.INT_TYPE_ID:
+ editor.putInt(name, reader.readInt());
+ return;
+ case ContentTypeIds.STRING_TYPE_ID:
+ editor.putString(name, reader.readUTF());
+ return;
+ }
+ }
+
+ /**
+ * Writes a single preference.
+ *
+ * @param name the name of the preference to write
+ * @param value the correctly-typed value of the preference
+ * @param writer the writer to write to
+ * @throws IOException if there are errors while writing
+ */
+ private void writePreference(String name, Object value, DataOutputStream writer)
+ throws IOException {
+ writer.writeUTF(name);
+
+ if (value instanceof Boolean) {
+ writer.writeByte(ContentTypeIds.BOOLEAN_TYPE_ID);
+ writer.writeBoolean((Boolean) value);
+ } else if (value instanceof Integer) {
+ writer.writeByte(ContentTypeIds.INT_TYPE_ID);
+ writer.writeInt((Integer) value);
+ } else if (value instanceof Long) {
+ writer.writeByte(ContentTypeIds.LONG_TYPE_ID);
+ writer.writeLong((Long) value);
+ } else if (value instanceof Float) {
+ writer.writeByte(ContentTypeIds.FLOAT_TYPE_ID);
+ writer.writeFloat((Float) value);
+ } else if (value instanceof String) {
+ writer.writeByte(ContentTypeIds.STRING_TYPE_ID);
+ writer.writeUTF((String) value);
+ } else {
+ throw new IllegalArgumentException(
+ "Type " + value.getClass().getName() + " not supported");
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreActivity.java
new file mode 100644
index 0000000..97e4749
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreActivity.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+
+
+import java.util.Date;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.gui.PreferencesActivity;
+
+/**
+ * An activity to restore data from the SD card.
+ *
+ * @author Jimmy Shih
+ */
+public class RestoreActivity extends Activity {
+
+ public static final String EXTRA_DATE = "date";
+ public static final String EXTRA_FILE = "file";
+
+ private static final String TAG = RestoreActivity.class.getSimpleName();
+ private static final int DIALOG_PROGRESS_ID = 0;
+
+ private RestoreAsyncTask restoreAsyncTask;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Object retained = getLastNonConfigurationInstance();
+ if (retained instanceof RestoreAsyncTask) {
+ restoreAsyncTask = (RestoreAsyncTask) retained;
+ restoreAsyncTask.setActivity(this);
+ } else {
+
+ long date = -1L;
+ String file = getIntent().getStringExtra(EXTRA_FILE);
+ if(file != null){
+ restoreAsyncTask = new RestoreAsyncTask(this, file);
+ } else if( (date = getIntent().getLongExtra(EXTRA_DATE, -1L)) != -1L) {
+ restoreAsyncTask = new RestoreAsyncTask(this, new Date(date));
+ } else {
+ Log.d(TAG, "Invalid date or file");
+ finish();
+ return;
+ }
+ restoreAsyncTask.execute();
+ }
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ restoreAsyncTask.setActivity(null);
+ return restoreAsyncTask;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ if (id != DIALOG_PROGRESS_ID) {
+ return null;
+ }
+ return DialogUtils.createSpinnerProgressDialog(this,
+ R.string.settings_backup_restore_progress_message, new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
+ }
+
+ /**
+ * Invokes when the associated AsyncTask completes.
+ *
+ * @param success true if the AsyncTask is successful
+ * @param messageId message id to display to user
+ */
+ public void onAsyncTaskCompleted(boolean success, int messageId) {
+ removeDialog(DIALOG_PROGRESS_ID);
+ Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
+ //Intent intent = IntentUtils.newIntent(this, FixUpJobs.class);
+ //intent.putExtra(Extras.AFTER_RESTORE, Boolean.TRUE);
+ Intent intent = IntentUtils.newIntent(this, PreferencesActivity.class);
+ startActivity(intent);
+ }
+
+ /**
+ * Shows the progress dialog.
+ */
+ public void showProgressDialog() {
+ showDialog(DIALOG_PROGRESS_ID);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreAsyncTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreAsyncTask.java
new file mode 100644
index 0000000..37373f3
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreAsyncTask.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+import java.io.IOException;
+import java.util.Date;
+
+import android.os.AsyncTask;
+import android.util.Log;
+import de.bjusystems.vdrmanager.R;
+
+/**
+ * AsyncTask to restore data from the SD card.
+ *
+ * @author Jimmy Shih
+ */
+public class RestoreAsyncTask extends AsyncTask<Void, Integer, Boolean> {
+
+ private static final String TAG = RestoreAsyncTask.class.getSimpleName();
+
+ private RestoreActivity restoreActivity;
+ private final Date date;
+ private final String path;
+ private final ExternalFileBackup externalFileBackup;
+
+ // true if the AsyncTask result is success
+ private boolean success;
+
+ // true if the AsyncTask has completed
+ private boolean completed;
+
+ // message id to return to the activity
+ private int messageId;
+
+ /**
+ * Creates an AsyncTask.
+ *
+ * @param restoreActivity
+ * the activity currently associated with this AsyncTask
+ * @param date
+ * the date to retore from
+ */
+ public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) {
+ this(restoreActivity, date, null);
+ }
+
+ /**
+ * Creates an AsyncTask.
+ *
+ * @param restoreActivity
+ * the activity currently associated with this AsyncTask
+ * @param date
+ * the date to retore from
+ */
+ public RestoreAsyncTask(RestoreActivity restoreActivity, String path) {
+ this(restoreActivity, null, path);
+ }
+
+ /**
+ * Creates an AsyncTask.
+ *
+ * @param restoreActivity
+ * the activity currently associated with this AsyncTask
+ * @param date
+ * the date to retore from
+ */
+ public RestoreAsyncTask(RestoreActivity restoreActivity, Date date,
+ String path) {
+ this.restoreActivity = restoreActivity;
+ this.date = date;
+ this.path = path;
+ this.externalFileBackup = new ExternalFileBackup(restoreActivity);
+ success = false;
+ completed = false;
+ messageId = R.string.sd_card_import_error;
+ }
+
+ /**
+ * Sets the current {@link RestoreActivity} associated with this AyncTask.
+ *
+ * @param activity
+ * the current {@link RestoreActivity}, can be null
+ */
+ public void setActivity(RestoreActivity activity) {
+ this.restoreActivity = activity;
+ if (completed && restoreActivity != null) {
+ restoreActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (restoreActivity != null) {
+ restoreActivity.showProgressDialog();
+ }
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ try {
+ if (path != null) {
+ externalFileBackup.restoreFromFile(path);
+ } else {
+ externalFileBackup.restoreFromDate(date);
+ }
+ messageId = R.string.sd_card_import_success;
+ return true;
+ } catch (IOException e) {
+ Log.d(TAG, "IO exception", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ success = result;
+ completed = true;
+ if (restoreActivity != null) {
+ restoreActivity.onAsyncTaskCompleted(success, messageId);
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreChooserActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreChooserActivity.java
new file mode 100644
index 0000000..cba6ab0
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/backup/RestoreChooserActivity.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package de.bjusystems.vdrmanager.backup;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.gui.Utils;
+
+/**
+ * An activity to choose a date to restore from.
+ *
+ * @author Jimmy Shih
+ */
+public class RestoreChooserActivity extends Activity {
+
+ private static final Comparator<Date> REVERSE_DATE_ORDER = new Comparator<Date>() {
+ @Override
+ public int compare(Date s1, Date s2) {
+ return s2.compareTo(s1);
+ }
+ };
+ private static final int DIALOG_CHOOSER_ID = 0;
+
+ private Date[] backupDates;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ExternalFileBackup externalFileBackup = new ExternalFileBackup(this);
+
+ // Get the list of existing backups
+ if (!FileUtils.isSdCardAvailable()) {
+ Toast.makeText(this, R.string.sd_card_error_no_storage,
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ if (!externalFileBackup.isBackupsDirectoryAvailable(false)) {
+
+ Toast.makeText(this, R.string.settings_backup_restore_no_backup,
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ doStuff(externalFileBackup);
+
+ }
+
+ private void doStuff(ExternalFileBackup externalFileBackup) {
+ backupDates = externalFileBackup.getAvailableBackups();
+ if (backupDates == null || backupDates.length == 0) {
+ Toast.makeText(this, R.string.settings_backup_restore_no_backup,
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ if (backupDates.length == 1) {
+ Intent intent = IntentUtils.newIntent(this, RestoreActivity.class)
+ .putExtra(RestoreActivity.EXTRA_DATE,
+ backupDates[0].getTime());
+ startActivity(intent);
+ finish();
+ return;
+ }
+
+ Arrays.sort(backupDates, REVERSE_DATE_ORDER);
+ showDialog(DIALOG_CHOOSER_ID);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ if (id != DIALOG_CHOOSER_ID) {
+ return null;
+ }
+ String items[] = new String[backupDates.length];
+ for (int i = 0; i < backupDates.length; i++) {
+ items[i] = Utils.formatDateTime(this, backupDates[i].getTime());
+ }
+ return new AlertDialog.Builder(this)
+ .setCancelable(true)
+ .setItems(items, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = IntentUtils.newIntent(
+ RestoreChooserActivity.this,
+ RestoreActivity.class).putExtra(
+ RestoreActivity.EXTRA_DATE,
+ backupDates[which].getTime());
+ startActivity(intent);
+ finish();
+ }
+ }).setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ }).setTitle(R.string.settings_backup_restore_select_title)
+ .create();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AliveState.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AliveState.java
new file mode 100644
index 0000000..cf96efa
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AliveState.java
@@ -0,0 +1,36 @@
+package de.bjusystems.vdrmanager.data;
+
+public class AliveState {
+
+ public static AliveState ALIVE = new AliveState(0);
+ public static AliveState DEAD = new AliveState(1);
+ public static AliveState UNKNOWN = new AliveState(2);
+
+ private final int value;
+ private static AliveState state;
+
+ private AliveState(final int value) {
+ this.value = value;
+ }
+
+ public static AliveState getState() {
+ return state;
+ }
+
+ public static void setState(final AliveState state) {
+ AliveState.state = state;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof AliveState)) {
+ return false;
+ }
+ return this.value == ((AliveState)o).value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Integer.valueOf(value).hashCode();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AudioTrack.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AudioTrack.java
new file mode 100644
index 0000000..323507f
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/AudioTrack.java
@@ -0,0 +1,61 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AudioTrack {
+
+ private String cached = null;
+
+ public int index;
+
+ public String type;
+
+ public String display;
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(type).append(", ").append(display).append("+").append(index);
+ return sb.toString();
+ };
+
+ private static final ArrayList<AudioTrack> EMPTY = new ArrayList<AudioTrack>(
+ 0);
+
+ /**
+ * a,1,deu|d,2,deu
+ *
+ * @param raw
+ * @return
+ */
+ public static List<AudioTrack> getAudio(String rawAudio) {
+
+ if(rawAudio == null){
+ return EMPTY;
+ }
+
+ String[] splitted = rawAudio.split("\\|");
+
+ if (splitted == null || splitted.length == 0) {
+ return EMPTY;
+ }
+
+ ArrayList<AudioTrack> audio;
+ audio = new ArrayList<AudioTrack>(splitted.length);
+ for (String a : splitted) {
+ String[] ar = a.split(",");
+ if (ar == null || ar.length != 3) {
+ continue;
+ }
+ AudioTrack track = new AudioTrack();
+ track.type = ar[0];
+ track.index = Integer.valueOf(ar[1]);
+ track.display = ar[2];
+ audio.add(track);
+ }
+ return audio;
+
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Channel.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Channel.java
new file mode 100644
index 0000000..8aadb16
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Channel.java
@@ -0,0 +1,176 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.List;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.j256.ormlite.field.DatabaseField;
+import com.j256.ormlite.table.DatabaseTable;
+
+import de.bjusystems.vdrmanager.StringUtils;
+import de.bjusystems.vdrmanager.app.C;
+import static de.bjusystems.vdrmanager.gui.Utils.mapSpecialChars;
+
+@DatabaseTable
+public class Channel implements Parcelable {
+
+ @DatabaseField(id = true)
+ String id;
+ private int number;
+
+ @DatabaseField
+ private String name;
+
+ @DatabaseField(index = true)
+ private String provider;
+
+ @DatabaseField
+ private String rawAudio;
+
+ @DatabaseField(index = true)
+ private String group;
+
+ private String source;
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ private List<AudioTrack> audio;
+
+ public List<AudioTrack> getAudio() {
+ if (audio != null) {
+ return audio;
+ }
+ audio = AudioTrack.getAudio(rawAudio);
+ return audio;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public String getRawAudio() {
+ return rawAudio;
+ }
+
+ public Channel(final String channelData) {
+ String[] words = StringUtils.splitPreserveAllTokens(channelData,
+ C.DATA_SEPARATOR);
+ this.number = Integer.valueOf(words[0].substring(1));
+ if (words.length > 2) {
+ this.name = mapSpecialChars(words[1]);
+ this.provider = mapSpecialChars(words[2]);
+ this.id = words[3];
+ this.rawAudio = words[4];
+ if (words.length > 5) {
+ this.source = words[5];
+ } else {
+ this.source = "Default";
+ }
+ } else {
+ this.name = mapSpecialChars(words[1]);
+ this.id = "-1";
+ this.provider = "Unknown";
+ this.rawAudio = "";
+ }
+
+ }
+
+ public Channel() {
+ this.number = 0;
+ this.name = "Unknown";
+ this.provider = "Unknown";
+ this.id = "Uknwon";
+ this.rawAudio = "";
+ }
+
+ public Channel(Parcel in) {
+ this.number = in.readInt();
+ this.name = in.readString();
+ this.provider = in.readString();
+ this.id = in.readString();
+ this.rawAudio = in.readString();
+ this.source = in.readString();
+ }
+
+ public boolean isGroupSeparator() {
+ return number == 0;
+ }
+
+ public int getNumber() {
+ return number;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getProvider() {
+ return provider;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder text = new StringBuilder();
+ text.append(number);
+ text.append(" - ");
+ text.append(name);
+ // text.append(" : ");
+ // text.append(provider);
+ return text.toString();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(number);
+ dest.writeString(name);
+ dest.writeString(provider);
+ dest.writeString(id);
+ dest.writeString(rawAudio);
+ dest.writeString(source);
+
+ }
+
+ public static final Parcelable.Creator<Channel> CREATOR = new Parcelable.Creator<Channel>() {
+ public Channel createFromParcel(Parcel in) {
+ return new Channel(in);
+ }
+
+ public Channel[] newArray(int size) {
+ return new Channel[size];
+ }
+ };
+
+ public boolean equals(Object o) {
+ if (o instanceof Channel == false) {
+ return false;
+ }
+ if (o == this) {
+ return true;
+ }
+ return ((Channel)o).getId().equals(id);
+ };
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Epg.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Epg.java
new file mode 100644
index 0000000..bfc0c6c
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Epg.java
@@ -0,0 +1,82 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.Date;
+
+import android.text.TextUtils;
+import de.bjusystems.vdrmanager.StringUtils;
+import de.bjusystems.vdrmanager.app.C;
+import de.bjusystems.vdrmanager.gui.Utils;
+import static de.bjusystems.vdrmanager.gui.Utils.mapSpecialChars;
+
+/**
+ * Class for EPG events
+ *
+ * @author bju
+ */
+public class Epg extends Event implements Timerable {
+
+ private Timer timer;
+
+ private TimerMatch timerMatch;
+
+ public TimerMatch getTimerMatch() {
+ return timerMatch;
+ }
+
+ public void setTimerMatch(TimerMatch timerMatch) {
+ this.timerMatch = timerMatch;
+ }
+
+ public Epg(final String line) {
+ final String[] words = StringUtils.splitPreserveAllTokens(line,
+ C.DATA_SEPARATOR);
+ channelNumber = Long.valueOf(words[0].substring(1));
+ channelName = Utils.mapSpecialChars(words[1]);
+ start = new Date(Long.parseLong(words[2]) * 1000);
+ stop = new Date(Long.parseLong(words[3]) * 1000);
+ title = mapSpecialChars(words[4]);
+ description = words.length > 5 ? mapSpecialChars(words[5]) : "";
+ shortText = words.length > 6 ? mapSpecialChars(words[6]) : "";
+ channelId = words.length > 7 ? mapSpecialChars(words[7]) : "";
+ rawAudio = words.length > 8 ? mapSpecialChars(words[8]) : "";
+ if (words.length > 9) {
+ String contents = words[9].trim();
+ if (contents.length() > 0) {
+ String[] caray = contents.split(" ");
+ if (caray.length > 0) {
+ content = new int[caray.length];
+ for (int i = 0; i < caray.length; ++i) {
+ content[i] = Integer.valueOf(caray[i]);
+ }
+ }
+ }
+
+ }
+ if (words.length > 10) {
+ if (TextUtils.isEmpty(words[10]) == false) {
+ vps = Long.valueOf(words[10]) * 1000;
+ }
+ }
+ }
+
+ public Timer getTimer() {
+ return timer;
+ }
+
+ public void setTimer(final Timer timer) {
+ this.timer = timer;
+ timerMatch = Utils.getTimerMatch(this, timer);
+ }
+
+ public TimerState getTimerState() {
+ if (timer == null) {
+ return TimerState.None;
+ } else {
+ return timer.getTimerState();
+ }
+ }
+
+ public Timer createTimer() {
+ return new Timer(this);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgCache.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgCache.java
new file mode 100644
index 0000000..fd1a895
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgCache.java
@@ -0,0 +1,12 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.WeakHashMap;
+
+public class EpgCache {
+
+ public static WeakHashMap<String, ArrayList<Epg>> CACHE = new WeakHashMap<String, ArrayList<Epg>>();
+
+ public static WeakHashMap<String, Date> NEXT_REFRESH = new WeakHashMap<String, Date>();
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchParams.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchParams.java
new file mode 100644
index 0000000..fbcac24
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchParams.java
@@ -0,0 +1,45 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.Date;
+
+import de.bjusystems.vdrmanager.gui.Utils;
+
+/**
+ * Class for EPG events
+ * @author bju
+ */
+public class EpgSearchParams {
+
+ private String channelNumber;
+ private String title;
+ private Date start;
+ private Date end;
+
+ public String getChannelNumber() {
+ return channelNumber;
+ }
+ public void setChannelNumber(final String channelNumber) {
+ this.channelNumber = channelNumber;
+ }
+ public String getTitle() {
+ return title;
+ }
+ public void setTitle(final String title) {
+ this.title = title;
+ }
+ public Date getStart() {
+ return start;
+ }
+ public void setStart(final Date start) {
+ this.start = start;
+ }
+ public Date getEnd() {
+ return end;
+ }
+ public void setEnd(final Date end) {
+ this.end = end;
+ }
+ public String toCommandLine() {
+ return Utils.unMapSpecialChars(title);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValue.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValue.java
new file mode 100644
index 0000000..8ba9b5e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValue.java
@@ -0,0 +1,56 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+public class EpgSearchTimeValue {
+
+ private final int index;
+ private final String text;
+
+ public EpgSearchTimeValue(final int index, final String text) {
+ this.index = index;
+ this.text = text;
+ }
+
+ public EpgSearchTimeValue() {
+ this.index = 0;
+ this.text = "";
+ }
+
+ public String getText(){
+ return text;
+ }
+
+ public String getValue() {
+ switch (index) {
+ case -1:
+ return "adhoc";
+ case 0:
+ return "now";
+ case 1:
+ return "next";
+ default:
+
+ final String[] values = text.split(":");
+ final Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(values[0]));
+ cal.set(Calendar.MINUTE, Integer.parseInt(values[1]));
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+
+ // next day?
+ final Calendar now = new GregorianCalendar();
+ if (now.after(cal)) {
+ cal.add(Calendar.DAY_OF_MONTH, 1);
+ }
+
+ return String.format("%d", cal.getTimeInMillis() / 1000);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return text;
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValues.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValues.java
new file mode 100644
index 0000000..19878f3
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EpgSearchTimeValues.java
@@ -0,0 +1,66 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import android.content.Context;
+import de.bjusystems.vdrmanager.R;
+
+public class EpgSearchTimeValues {
+
+ private final List<EpgSearchTimeValue> values = new ArrayList<EpgSearchTimeValue>();
+ private final Context context;
+
+ public EpgSearchTimeValues(final Context context) {
+ this.context = context;
+ }
+
+ public List<EpgSearchTimeValue> getValues() {
+
+ final Preferences prefs = Preferences.getPreferences();
+
+ // fixed values for now and next
+ values.add(new EpgSearchTimeValue(0, context.getString(R.string.epg_list_time_now)));
+ values.add(new EpgSearchTimeValue(1, context.getString(R.string.epg_list_time_next)));
+
+ // get user defined values
+ final String userValueString = prefs.getEpgSearchTimes();
+
+ final String[] userValues = userValueString.split(",");
+
+ Arrays.sort(userValues);
+
+ for(final String userValue : userValues) {
+ if (userValue.contains(":")) {
+ values.add(new EpgSearchTimeValue(values.size(), userValue));
+ }
+ }
+
+ values.add(new EpgSearchTimeValue(-1, context.getString(R.string.epg_list_time_adhoc)));
+ return values;
+ }
+
+ public void append(EpgSearchTimeValue est){
+ values.add(values.size() - 1, est);
+ }
+
+ public void saveValues(final List<EpgSearchTimeValue> values) {
+
+ // get old values
+ final Preferences prefs = Preferences.getPreferences();
+
+ // add value
+ String newValues = "";
+ for(int i = 2; i < values.size(); i++) {
+ final EpgSearchTimeValue value = values.get(i);
+ if (newValues.length() > 0) {
+ newValues += ",";
+ }
+ newValues += value.toString();
+ }
+
+ // save new values
+ prefs.setEpgSearchTimes(context, newValues);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Event.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Event.java
new file mode 100644
index 0000000..bbd3181
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Event.java
@@ -0,0 +1,156 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.Date;
+import java.util.List;
+
+import android.text.TextUtils;
+
+/**
+ * Basisc class for all Events
+ *
+ * @author bju,lado
+ *
+ */
+public abstract class Event {
+
+ protected Long channelNumber;
+ protected String channelId;
+ protected String channelName;
+
+ protected String title;
+ protected String shortText;
+ protected String description;
+ protected Date start;
+ protected Date stop;
+
+ protected String rawAudio;
+ protected int[] content = {};
+
+ protected long vps = 0;
+
+ public int[] getContent() {
+ return content;
+ }
+
+ private List<AudioTrack> audio;
+
+ public List<AudioTrack> getAudio() {
+ if (audio != null) {
+ return audio;
+ }
+ audio = AudioTrack.getAudio(rawAudio);
+ return audio;
+ }
+
+ public long getDuration() {
+ long millis = getStop().getTime() - getStart().getTime();
+ return millis;
+ }
+
+ public Event() {
+
+ }
+
+ public void setChannelNumber(Long channelNumber) {
+ this.channelNumber = channelNumber;
+ }
+
+ public void setChannelName(String channelName) {
+ this.channelName = channelName;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setShortText(String shortText) {
+ this.shortText = shortText;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setStart(Date start) {
+ this.start = start;
+ }
+
+ public void setStop(Date stop) {
+ this.stop = stop;
+ }
+
+ public Event(Event event) {
+ channelNumber = event.getChannelNumber();
+ channelId = event.getChannelId();
+ channelName = event.getChannelName();
+ title = event.getTitle();
+ shortText = event.getShortText();
+ description = event.getDescription();
+ start = event.getStart();
+ stop = event.getStop();
+ rawAudio = event.rawAudio;
+ content = event.content;
+ vps = event.vps;
+ }
+
+ public Long getChannelNumber() {
+ return channelNumber;
+ }
+
+ public String getChannelName() {
+ return channelName;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public String getShortText() {
+ if (TextUtils.isEmpty(shortText) == false) {
+ return shortText;
+ }
+ if (TextUtils.isEmpty(description) == false) {
+ if (description.length() < 50) {
+ return description;
+ }
+ return TextUtils.substring(description, 0, 50) + "…";
+ }
+ return "";
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Date getStart() {
+ return start;
+ }
+
+ public Date getStop() {
+ return stop;
+ }
+
+ public String getStreamId() {
+ if (channelId != null) {
+ return channelId;
+ }
+ return String.valueOf(channelNumber);
+ }
+
+ public boolean isConflict() {
+ return false;
+ }
+
+ public boolean hasVPS() {
+ return vps > 0;
+ }
+
+ public long getVPS() {
+ return vps;
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventContentGroup.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventContentGroup.java
new file mode 100644
index 0000000..03fcd90
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventContentGroup.java
@@ -0,0 +1,23 @@
+package de.bjusystems.vdrmanager.data;
+
+/**
+ * @author lado
+ *
+ * Based on epg.c from vdr
+ *
+ */
+public interface EventContentGroup {
+
+ static int MovieDrama = 0x10; //
+ static int NewsCurrentAffairs = 0x20; //
+ static int Show = 0x30; //
+ static int Sports = 0x40; //
+ static int ChildrenYouth = 0x50; //
+ static int MusicBalletDance = 0x60; //
+ static int ArtsCulture = 0x70; //
+ static int SocialPoliticalEconomics = 0x80;//
+ static int EducationalScience = 0x90;//
+ static int LeisureHobbies = 0xA0;//
+ static int Special = 0xB0; //
+ static int UserDefined = 0xF0;//
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventFormatter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventFormatter.java
new file mode 100644
index 0000000..6cc7151
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventFormatter.java
@@ -0,0 +1,66 @@
+package de.bjusystems.vdrmanager.data;
+
+import de.bjusystems.vdrmanager.gui.Utils;
+import de.bjusystems.vdrmanager.utils.date.DateFormatter;
+
+public class EventFormatter {
+
+ private String time;
+ private final String stop;
+ private final String date;
+ private final String longDate;
+ private final String title;
+ private final String shortText;
+ private final String description;
+
+ public EventFormatter(final Event event) {
+ this(event,false);
+ }
+ /**
+ * @param event
+ * @param onlyStartTime Event Time is rendered as 'start - stop' if false
+ */
+ public EventFormatter(final Event event, boolean onlyStartTime) {
+ DateFormatter formatter = new DateFormatter(event.getStart());
+ this.date = formatter.getDateString();
+ this.longDate = formatter.getDailyHeader();
+ this.time = formatter.getTimeString();
+ formatter = new DateFormatter(event.getStop());
+ this.stop = formatter.getTimeString();
+ if(onlyStartTime == false){
+ this.time += " - " + stop;
+ }
+ this.title = Utils.mapSpecialChars(event.getTitle());
+ this.shortText = Utils.mapSpecialChars(event.getShortText());
+ this.description = Utils.mapSpecialChars(event.getDescription());
+ }
+
+ public String getShortText() {
+ return shortText;
+ }
+
+
+ public String getStop(){
+ return stop;
+ }
+
+ public String getTime() {
+ return time;
+ }
+
+ public String getDate() {
+ return date;
+ }
+
+ public String getLongDate() {
+ return longDate;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventListItem.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventListItem.java
new file mode 100644
index 0000000..b01aee5
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/EventListItem.java
@@ -0,0 +1,164 @@
+package de.bjusystems.vdrmanager.data;
+
+import de.bjusystems.vdrmanager.gui.Utils;
+
+/**
+ * @author lado
+ *
+ * TODO auf Event Interface umstellen und die Aufrufen an event
+ * delegieren. Das hier ist nicht gut.
+ */
+public class EventListItem extends Event {
+
+ Event event;
+
+ public Event getEvent() {
+ return event;
+ }
+
+ public void setEvent(Event event) {
+ this.event = event;
+ }
+
+ //private final Recording rec;
+ //private final Timer timer;
+ //private final Epg epg;
+ private final String header;
+
+ //
+ // public EventListItem(final Event event){
+ // if(event instanceof Recording){
+ // this((Recording)event);
+ // } else if (event instanceof Timer){
+ // this((Timer)event);
+ // } else {
+ // this((Epg)event);
+ // }
+ // throw new IllegalArgumentException("Uknown event type " + event);
+ // }
+
+
+ public EventListItem(final Event rec) {
+ super(rec);
+ event = rec;
+ this.header = null;
+// this.rec = rec;
+ // this.epg = null;
+ //this.timer = null;
+ }
+//
+// public EventListItem(final Recording rec) {
+// super(rec);
+// event = rec;
+// this.header = null;
+// this.rec = rec;
+// this.epg = null;
+// this.timer = null;
+// }
+//
+// public EventListItem(final Timer timer) {
+// super(timer);
+// event = timer;
+// this.header = null;
+// this.timer = timer;
+// this.epg = null;
+// this.rec = null;
+// }
+//
+// public EventListItem(final Epg epg) {
+// super(epg);
+// event = epg;
+// this.header = null;
+// this.timer = null;
+// this.epg = epg;
+// this.rec = null;
+// }
+//
+// @Override
+// public TimerState getTimerState() {
+// return event.getTimerState();
+// }
+
+ public EventListItem(final String header) {
+ this.header = header;
+ }
+
+ public boolean isHeader() {
+ return header != null;
+ }
+
+// public boolean isTimer() {
+// return event instanceof Timer;
+// }
+
+ public String getHeader() {
+ return header;
+ }
+
+// public Timer getTimer() {
+// return timer;
+// }
+//
+// public Epg getEpg() {
+// return epg;
+// }
+//
+// public Recording getRecording() {
+// return rec;
+// }
+
+ // public Event getEvent() {
+ // return event;
+ // }
+ //
+ public boolean isLive() {
+ return Utils.isLive(this);
+ }
+
+ @Override
+ public String toString() {
+ if (isHeader()) {
+ return "Header: " + header;
+ }
+
+ final EventFormatter formatter = new EventFormatter(this);
+ final StringBuilder text = new StringBuilder();
+ text.append("Timer / Event: ");
+ text.append("Channel: ").append(getChannelNumber());
+ text.append(" (").append(getChannelName()).append("), ");
+ text.append("Zeit: ").append(formatter.getDate()).append(" ")
+ .append(formatter.getTime());
+ return text.toString();
+ }
+
+
+ @Override
+ public long getDuration() {
+ if(event != null){
+ return event.getDuration();
+ }
+ return super.getDuration();
+ }
+
+ @Override
+ public String getChannelId() {
+ if(event != null){
+ return event.getChannelId();
+ }
+ return null;
+ }
+
+ @Override
+ public String getStreamId() {
+ if(event == null){
+ return null;
+ }
+ return event.getStreamId();
+ }
+//
+// @Override
+// public Timer getTimer() {
+// return event.getTimer();
+// }
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/FetchEditTextPreference.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/FetchEditTextPreference.java
new file mode 100644
index 0000000..17a93b7
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/FetchEditTextPreference.java
@@ -0,0 +1,94 @@
+package de.bjusystems.vdrmanager.data;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences.Editor;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import de.bjusystems.vdrmanager.R;
+
+public class FetchEditTextPreference extends DialogPreference{
+
+ private EditText mEditText;
+
+ private String initialValue;
+
+ public String getInitialValue() {
+ return initialValue;
+ }
+
+ public void setInitialValue(String initialValue) {
+ this.initialValue = initialValue;
+ }
+
+ public EditText getmEditText() {
+ return mEditText;
+ }
+
+ public void setEditText(String text) {
+ this.mEditText.setText(text);
+ }
+
+ private ImageButton mButton;
+
+ private String mText;
+ //private CharSequence mCompoundButtonText;
+ private View.OnClickListener mCompoundButtonCallback;
+
+ public FetchEditTextPreference(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ setDialogLayoutResource(R.layout.fetch_preference);
+ }
+
+ public FetchEditTextPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setDialogLayoutResource(R.layout.fetch_preference);
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ View root = super.onCreateDialogView();
+ mEditText = (EditText) root.findViewById(R.id.edit);
+ mButton = (ImageButton) root.findViewById(R.id.button);
+ return root;
+ }
+
+ public void setText(String text) {
+ mText = text;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE: // User clicked OK!
+ mText = mEditText.getText().toString();
+ callChangeListener(mText);
+ Editor editor = (Editor) getEditor();
+ editor.putString(getKey(), mText);
+ editor.commit();
+ break;
+ }
+ super.onClick(dialog, which);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ mEditText.setText(mText);
+ //mButton.setText(mCompoundButtonText);
+
+ // Set a callback to our button.
+ mButton.setOnClickListener(mCompoundButtonCallback);
+ }
+
+ public void setCompoundButtonListener(View.OnClickListener callback) {
+ mCompoundButtonCallback = callback;
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/HasAudio.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/HasAudio.java
new file mode 100644
index 0000000..10d9fd9
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/HasAudio.java
@@ -0,0 +1,5 @@
+package de.bjusystems.vdrmanager.data;
+
+public class HasAudio {
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/MenuActionHandler.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/MenuActionHandler.java
new file mode 100644
index 0000000..b1f1d2e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/MenuActionHandler.java
@@ -0,0 +1,16 @@
+package de.bjusystems.vdrmanager.data;
+
+import android.content.Context;
+
+
+/**
+ * Handler for menu actions
+ * @author bju
+ */
+public interface MenuActionHandler {
+
+ /**
+ * Execute the action
+ */
+ void executeAction(Context context);
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/OrmLiteBasePreferenceActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/OrmLiteBasePreferenceActivity.java
new file mode 100644
index 0000000..eae8302
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/OrmLiteBasePreferenceActivity.java
@@ -0,0 +1,89 @@
+package de.bjusystems.vdrmanager.data;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+import com.j256.ormlite.android.apptools.OpenHelperManager;
+import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
+import com.j256.ormlite.support.ConnectionSource;
+
+public abstract class OrmLiteBasePreferenceActivity<H extends OrmLiteSqliteOpenHelper> extends PreferenceActivity {
+
+
+ private volatile H helper;
+ private volatile boolean created = false;
+ private volatile boolean destroyed = false;
+
+ /**
+ * Get a helper for this action.
+ */
+ public H getHelper() {
+ if (helper == null) {
+ if (!created) {
+ throw new IllegalStateException("A call has not been made to onCreate() yet so the helper is null");
+ } else if (destroyed) {
+ throw new IllegalStateException(
+ "A call to onDestroy has already been made and the helper cannot be used after that point");
+ } else {
+ throw new IllegalStateException("Helper is null for some unknown reason");
+ }
+ } else {
+ return helper;
+ }
+ }
+
+ /**
+ * Get a connection source for this action.
+ */
+ public ConnectionSource getConnectionSource() {
+ return getHelper().getConnectionSource();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ if (helper == null) {
+ helper = getHelperInternal(this);
+ created = true;
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ releaseHelper(helper);
+ destroyed = true;
+ }
+
+ /**
+ * This is called internally by the class to populate the helper object instance. This should not be called directly
+ * by client code unless you know what you are doing. Use {@link #getHelper()} to get a helper instance. If you are
+ * managing your own helper creation, override this method to supply this activity with a helper instance.
+ *
+ * <p>
+ * <b> NOTE: </b> If you override this method, you most likely will need to override the
+ * {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as well.
+ * </p>
+ */
+ protected H getHelperInternal(Context context) {
+ @SuppressWarnings({ "unchecked", "deprecation" })
+ H newHelper = (H) OpenHelperManager.getHelper(context);
+ return newHelper;
+ }
+
+ /**
+ * Release the helper instance created in {@link #getHelperInternal(Context)}. You most likely will not need to call
+ * this directly since {@link #onDestroy()} does it for you.
+ *
+ * <p>
+ * <b> NOTE: </b> If you override this method, you most likely will need to override the
+ * {@link #getHelperInternal(Context)} method as well.
+ * </p>
+ */
+ protected void releaseHelper(H helper) {
+ OpenHelperManager.releaseHelper();
+ this.helper = null;
+ }
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/P.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/P.java
new file mode 100644
index 0000000..b3e9918
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/P.java
@@ -0,0 +1,11 @@
+package de.bjusystems.vdrmanager.data;
+
+public interface P {
+
+ public static String CHANNELS_LAST_ORDER = "CHANNELS_LAST_ORDER";
+
+ public static String EPG_LAST_SORT = "EPG_LAST_SORT";
+
+ public static String CHANNELS_LAST_ORDER_REVERSE = "CHANNELS_LAST_ORDER_ORDER";
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Preferences.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Preferences.java
new file mode 100644
index 0000000..a73a786
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Preferences.java
@@ -0,0 +1,783 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.List;
+import java.util.Locale;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.text.TextUtils;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.StringUtils;
+import de.bjusystems.vdrmanager.data.db.DBAccess;
+
+/**
+ * Class for all preferences
+ *
+ * @author bju, lado
+ */
+public class Preferences {
+
+ public static final String DEFAULT_LANGUAGE_VALUE = "DEFAULT";
+
+ public static final String PREFERENCE_FILE_NAME = "VDR-Manager";
+
+ private static Vdr current;
+
+ public static void set(Context context, String key, int value) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ sharedPrefs.edit().putInt(key, value).commit();
+ }
+
+ public static void set(Context context, String key, boolean value) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ sharedPrefs.edit().putBoolean(key, value).commit();
+ }
+
+ public static int get(Context context, String key, int defValue) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ return sharedPrefs.getInt(key, defValue);
+
+ }
+
+ public static String get(Context context, String key, String defValue) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ return sharedPrefs.getString(key, defValue);
+
+ }
+
+ public static boolean get(Context context, String key, boolean defValue) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ return sharedPrefs.getBoolean(key, defValue);
+
+ }
+
+ public static void setCurrentVdr(Context context, Vdr vdr) {
+ current = vdr;
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ sharedPrefs
+ .edit()
+ .putInt(context.getString(R.string.current_vdr_id_key),
+ current != null ? current.getId() : -1).commit();
+ }
+
+ public Vdr getCurrentVdr() {
+ return current;
+ }
+
+ public int getCurrentVdrContext(Context context) {
+ return getInteger(context, R.string.current_vdr_id_key, -1);
+ }
+
+ /**
+ * user defined epg search times
+ */
+ private String epgSearchTimes;
+ /**
+ * format times AM/PM or 24H
+ *
+ * @since 0.2
+ */
+ private boolean use24hFormat;
+
+ /**
+ * Quits the app on back button
+ */
+ private boolean quiteOnBackButton = true;
+
+ /**
+ * Show IMDB buttons, where possible (e.g. EPG Details)
+ */
+ private boolean showImdbButton = true;
+
+ /**
+ * Show OMDB button in epg details
+ */
+ private boolean showOmdbButton = false;
+
+ /**
+ * Show TMDb button in epg details
+ */
+ private boolean showTmdbButton = false;
+
+ private int maxRecentChannels = 10;
+
+ public int getMaxRecentChannels() {
+ return maxRecentChannels;
+ }
+
+ public boolean isShowTmdbButton() {
+ return showTmdbButton;
+ }
+
+ public boolean isShowOmdbButton() {
+ return showOmdbButton;
+ }
+
+ /**
+ * On Which imdb site to search?
+ */
+ private String imdbUrl = "akas.imdb.com";
+
+ private boolean showChannelNumbers = false;
+
+ public String getEncoding() {
+ return getCurrentVdr().getEncoding();
+ }
+
+ public int getConnectionTimeout() {
+ return getCurrentVdr().getConnectionTimeout();
+ }
+
+ public int getReadTimeout() {
+ return getCurrentVdr().getReadTimeout();
+ }
+
+ public int getTimeout() {
+ return getCurrentVdr().getTimeout();
+ }
+
+ public String getImdbUrl() {
+ return imdbUrl;
+ }
+
+ public void setImdbUrl(String imdbUrl) {
+ this.imdbUrl = imdbUrl;
+ }
+
+ /**
+ * @return whether to shwo imdb button
+ */
+ public boolean isShowImdbButton() {
+ return showImdbButton;
+ }
+
+ public String getStreamingUsername() {
+ return getCurrentVdr().getStreamingUsername();
+ }
+
+ public String getStreamingPassword() {
+ return getCurrentVdr().getStreamingPassword();
+ }
+
+ /**
+ * Properties singleton
+ */
+ private static Preferences thePrefs;
+
+ /**
+ * Whether to send Packets to the custom broadcast address. It is used, if
+ * the address ist not empty
+ *
+ * @return
+ * @since 0.2
+ */
+ public String getWolCustomBroadcast() {
+ return getCurrentVdr().getWolCustomBroadcast();
+ }
+
+ /**
+ * Getter for use24hFormat
+ *
+ * @return
+ * @since 0.2
+ */
+ public boolean isUse24hFormat() {
+ return use24hFormat;
+ }
+
+ /**
+ * Checks for connect using SSL
+ *
+ * @return true, if use SSL connections
+ */
+ public boolean isSecure() {
+ return getCurrentVdr().isSecure();
+ }
+
+ /**
+ * Retrieves the channel filtering mode
+ *
+ * @return true, if channels will be filtered
+ */
+ public boolean isFilterChannels() {
+ return getCurrentVdr().isFilterChannels();
+ }
+
+ /**
+ * Last channel to receive
+ *
+ * @return channel number
+ */
+ public String getChannels() {
+ return isFilterChannels() ? getCurrentVdr().getChannelFilter() : "";
+ }
+
+ /**
+ * Gets the host or IP address
+ *
+ * @return host
+ */
+ public String getHost() {
+ return getCurrentVdr().getHost();
+ }
+
+ /**
+ * Gets the port
+ *
+ * @return port
+ */
+ public int getPort() {
+ return getCurrentVdr().getPort();
+ }
+
+ /**
+ * Gets the port
+ *
+ * @return port
+ */
+ public int getSvdrpPort() {
+ return getCurrentVdr().getSvdrpPort();
+ }
+
+ /**
+ * Gets password
+ *
+ * @return password
+ */
+ public String getPassword() {
+ String pwd = getCurrentVdr().getPassword();
+ if (pwd == null) {
+ return StringUtils.EMPTY_STRING;
+ }
+ return pwd;
+ }
+
+ /**
+ * Checks for enables remote wakeup
+ *
+ * @return true, if remote wakeup is enabled
+ */
+ public boolean isWakeupEnabled() {
+ return getCurrentVdr().isWakeupEnabled();
+ }
+
+ /**
+ * Gets the URL for the wakeup request
+ *
+ * @return wakeup url
+ */
+ public String getWakeupUrl() {
+ return getCurrentVdr().getWakeupUrl();
+ }
+
+ /**
+ * Gets the user for the wakeup url
+ *
+ * @return user name
+ */
+ public String getWakeupUser() {
+ return getCurrentVdr().getWakeupUser();
+ }
+
+ /**
+ * Gets the password for the wakeup url
+ *
+ * @return password
+ */
+ public String getWakeupPassword() {
+ return getCurrentVdr().getWakeupPassword();
+ }
+
+ /**
+ * Checks for enabled alive check
+ *
+ * @return true, if enabled
+ */
+ public boolean isAliveCheckEnabled() {
+ return getCurrentVdr().isAliveCheckEnabled();
+ }
+
+ public boolean isEnableRecStream() {
+ return getCurrentVdr().isEnableRecStreaming();
+ }
+
+ public int getLivePort() {
+ return getCurrentVdr().getLivePort();
+ }
+
+ /**
+ * Gets the time between alive checks
+ *
+ * @return time in seconds
+ */
+ public int getAliveCheckInterval() {
+ return getCurrentVdr().getAliveCheckInterval();
+ }
+
+ /**
+ * Gets the buffer before the event start
+ *
+ * @return pre event buffer
+ */
+ public int getTimerPreMargin() {
+ return getCurrentVdr().getTimerPreMargin();
+ }
+
+ /**
+ * Gets the buffer after the event stop
+ *
+ * @return post event buffer
+ */
+ public int getTimerPostMargin() {
+ return getCurrentVdr().getTimerPostMargin();
+ }
+
+ /**
+ * Gets the default priority
+ *
+ * @return default priority
+ */
+ public int getTimerDefaultPriority() {
+ return getCurrentVdr().getTimerDefaultPriority();
+ }
+
+ /**
+ * Gets the default lifetime
+ *
+ * @return default lifetime
+ */
+ public int getTimerDefaultLifetime() {
+ return getCurrentVdr().getTimerDefaultLifetime();
+ }
+
+ /**
+ * Gets the time values for the epg search
+ *
+ * @return
+ */
+ public String getEpgSearchTimes() {
+ return epgSearchTimes;
+ }
+
+ /**
+ * gets the MAC Address of the vdr host
+ *
+ * @return
+ * @since 0.2
+ */
+ public String getVdrMac() {
+ return getCurrentVdr().getMac();
+ }
+
+ /**
+ * Gets the selection which wakeup method to use
+ *
+ * @return
+ * @since 0.2
+ */
+ public String getWakeupMethod() {
+ return getCurrentVdr().getWakeupMethod();
+ }
+
+ /**
+ * Getter for streaming port
+ *
+ * @return
+ * @since 02.
+ */
+ public int getStreamPort() {
+ return getCurrentVdr().getStreamPort();
+ }
+
+ /**
+ * Getter for selected streaming format
+ *
+ * @return
+ * @since 0.2
+ */
+ public String getStreamFormat() {
+ return getCurrentVdr().getStreamFormat();
+ }
+
+ public int getSmarttvewebPort() {
+ return getCurrentVdr().getSmarttvwebPort();
+ }
+
+ public String getSmarttvewebType() {
+ return getCurrentVdr().getSmarttvwebType();
+ }
+
+ /**
+ * Sets the time values for the epg search
+ *
+ * @param epgSearchTimes new time values
+ */
+ public void setEpgSearchTimes(final Context context,
+ final String epgSearchTimes) {
+
+ final SharedPreferences prefs = getSharedPreferences(context);
+ final SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(context.getString(R.string.epg_search_times_key),
+ epgSearchTimes);
+ editor.commit();
+
+ // reload
+ init(context);
+ }
+
+ /**
+ * Gets the name for the file which preferences will be saved into
+ *
+ * @param context Context
+ * @return filename
+ */
+ public static String getPreferenceFile(final Context context) {
+ return PREFERENCE_FILE_NAME;
+ }
+
+ /**
+ * Show Channel Numbers in the overviews
+ *
+ * @return
+ * @since 0.2
+ */
+ public boolean isShowChannelNumbers() {
+ return showChannelNumbers;
+ }
+
+ /**
+ * getter
+ *
+ * @return
+ */
+ public boolean isEnableRemux() {
+ return getCurrentVdr().isEnableRemux();
+ }
+
+ /**
+ * getter
+ *
+ * @return
+ */
+ public String getRemuxCommand() {
+ return getCurrentVdr().getRemuxCommand();
+ }
+
+ /**
+ * getter
+ *
+ * @return
+ */
+ public String getRemuxParameter() {
+ return getCurrentVdr().getRemuxParameter();
+ }
+
+ /**
+ * getter
+ *
+ * @return
+ */
+ public boolean isQuiteOnBackButton() {
+ return quiteOnBackButton;
+ }
+
+ /**
+ * Gets the previous loaded preferences
+ *
+ * @return preferences
+ */
+ public static Preferences getPreferences() {
+ return thePrefs;
+ }
+
+ public String getRecStreamMethod() {
+ return getCurrentVdr().getRecStreamMethod();
+ }
+
+ /**
+ * Gets the previous loaded preferences, same as getPreferences();
+ *
+ * @return
+ */
+ public static Preferences get() {
+ return thePrefs;
+ }
+
+ private static void initInternal(final Context context) {
+
+ final Preferences prefs = new Preferences();
+
+ prefs.epgSearchTimes = getString(context,
+ R.string.epg_search_times_key, "");
+
+ prefs.use24hFormat = getBoolean(context,
+ R.string.gui_enable_24h_format_key, true);
+
+ prefs.showChannelNumbers = getBoolean(context,
+ R.string.gui_channels_show_channel_numbers_key, false);
+
+ prefs.quiteOnBackButton = getBoolean(context,
+ R.string.qui_quit_on_back_key, true);
+
+ prefs.showImdbButton = getBoolean(context,
+ R.string.qui_show_imdb_button_key, true);
+
+ prefs.showOmdbButton = getBoolean(context,
+ R.string.qui_show_omdb_button_key, false);
+
+ prefs.showTmdbButton = getBoolean(context,
+ R.string.qui_show_tmdb_button_key, false);
+
+ prefs.imdbUrl = getString(context, R.string.qui_imdb_url_key,
+ "akas.imdb.com");
+
+ prefs.maxRecentChannels = getInt(context,
+ R.string.gui_max_recent_channels_key, 10);
+
+
+ thePrefs = prefs;
+ }
+
+ public static void reset() {
+ thePrefs = null;
+ }
+
+ public static void reloadVDR(Context context) {
+ if (current == null) {
+ return;
+ }
+ DBAccess.get(context).getVdrDAO().refresh(current);
+ }
+
+ public static boolean initVDR(final Context context) {
+
+ if (current != null) {
+ return true;
+ }
+
+ int id = getInteger(context, R.string.current_vdr_id_key, -1);
+
+ Vdr vdr = null;
+ if (id != -1) {
+ vdr = DBAccess.get(context).getVdrDAO().queryForId(id);
+ }
+
+ setCurrentVdr(context, vdr);
+
+ if (vdr != null) {
+ return true;
+ }
+
+ List<Vdr> list = DBAccess.get(context).getVdrDAO().queryForAll();
+ if (list != null && list.isEmpty() == false) {
+ vdr = list.get(0);
+ setCurrentVdr(context, vdr);
+ return true;
+ }
+
+ return initFromOldVersion(context);
+ // Intent intent = new Intent();
+ // intent.setClass(context, VdrListActivity.class);
+ // intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ // intent.putExtra(Intents.EMPTY_CONFIG, Boolean.TRUE);
+ // context.startActivity(intent);
+ // Toast.makeText(context, R.string.no_vdr, Toast.LENGTH_SHORT).show();
+ }
+
+ /**
+ * Loads all preferences
+ *
+ * @param context Context
+ * @return Preferences
+ */
+ public static void init(final Context context) {
+ // if (thePrefs != null) {
+ // return;
+ // }
+ synchronized (Preferences.class) {
+ // if (thePrefs != null) {
+ // return;
+ // }
+ initInternal(context);
+ setLocale(context);
+ }
+ }
+
+ private static boolean initFromOldVersion(Context context) {
+
+ Vdr vdr = new Vdr();
+
+ String host = getString(context, R.string.vdr_host_key, null);
+ if (host == null) {
+ return false;
+ }
+ vdr.setHost(host);
+ vdr.setName("Default");
+ vdr.setPort(getInt(context, R.string.vdr_port_key, 6420));
+ vdr.setPassword(getString(context, R.string.vdr_password_key, ""));
+ vdr.setSecure(getBoolean(context, R.string.vdr_ssl_key, false));
+ vdr.setStreamPort(getInt(context, R.string.vdr_stream_port, 3000));
+ vdr.setStreamFormat(getString(context, R.string.vdr_stream_format, "TS"));
+
+ vdr.setAliveCheckEnabled(getBoolean(context,
+ R.string.alive_check_enabled_key, false));
+ vdr.setAliveCheckInterval(getInt(context,
+ R.string.alive_check_interval_key, 60));
+
+ vdr.setChannelFilter(getString(context,
+ R.string.channel_filter_last_key, "").replace(" ", ""));
+ vdr.setFilterChannels(getBoolean(context,
+ R.string.channel_filter_filter_key, false));
+ vdr.setWakeupEnabled(getBoolean(context, R.string.wakeup_enabled_key,
+ false));
+ vdr.setWakeupUrl(getString(context, R.string.wakeup_url_key, ""));
+ vdr.setWakeupUser(getString(context, R.string.wakeup_user_key, ""));
+ vdr.setWakeupPassword(getString(context, R.string.wakeup_password_key,
+ ""));
+
+ vdr.setTimerPreMargin(getInt(context,
+ R.string.timer_pre_start_buffer_key, 5));
+ vdr.setTimerPostMargin(getInt(context,
+ R.string.timer_post_end_buffer_key, 30));
+
+ vdr.setTimerDefaultPriority(getInt(context,
+ R.string.timer_default_priority_key, 99));
+
+ vdr.setTimerDefaultLifetime(getInt(context,
+ R.string.timer_default_lifetime_key, 99));
+
+ vdr.setEpgSearchTimes(getString(context, R.string.epg_search_times_key,
+ ""));
+
+ vdr.setMac(getString(context, R.string.wakeup_wol_mac_key, ""));
+
+ vdr.setWakeupMethod(getString(context, R.string.wakeup_method_key,
+ "url"));
+
+ vdr.setWolCustomBroadcast(getString(context,
+ R.string.wakeup_wol_custom_broadcast_key, ""));
+
+ vdr.setConnectionTimeout(getInt(context, R.string.vdr_conntimeout_key,
+ 10));
+ vdr.setReadTimeout(getInt(context, R.string.vdr_readtimeout_key, 10));
+ vdr.setTimeout(getInt(context, R.string.vdr_timeout_key, 120));
+
+ vdr.setStreamingUsername(getString(context,
+ R.string.streaming_username_key, ""));
+
+ vdr.setStreamingPassword(getString(context,
+ R.string.streaming_password_key, ""));
+
+ vdr.setEncoding(getString(context, R.string.vdr_encoding_key, "utf-8"));
+
+ if (DBAccess.get(context).getVdrDAO().create(vdr) != 1) {
+ return false;
+ }
+
+ setCurrentVdr(context, vdr);
+
+ return true;
+ }
+
+ /**
+ * Gets the persistent preferences
+ *
+ * @param context Context
+ * @return preferences
+ */
+ public static SharedPreferences getSharedPreferences(final Context context) {
+ return context.getSharedPreferences(getPreferenceFile(context),
+ Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Helper for retrieving integer values from preferences
+ *
+ * @param context Context
+ * @param resId ressource id of the preferences name
+ * @param defValue default value
+ * @return value or the default value if not defined
+ */
+ private static int getInt(final Context context, final int resId,
+ final int defValue) {
+ final String value = getString(context, resId, String.valueOf(defValue));
+ if (TextUtils.isEmpty(value) || !TextUtils.isDigitsOnly(value)) {
+ return 0;
+ }
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * Helper for retrieving boolean values from preferences
+ *
+ * @param context Context
+ * @param resId ressource id of the preferences name
+ * @param defValue default value
+ * @return value or the default value if not defined
+ */
+ private static boolean getBoolean(final Context context, final int resId,
+ final boolean defValue) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ return sharedPrefs.getBoolean(context.getString(resId), defValue);
+ }
+
+ /**
+ * Helper for retrieving string values from preferences
+ *
+ * @param context Context
+ * @param resId ressource id of the preferences name
+ * @param defValue default value
+ * @return value or the default value if not defined
+ */
+ private static String getString(final Context context, final int resId,
+ final String defValue) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ return sharedPrefs.getString(context.getString(resId), defValue);
+ }
+
+ private static int getInteger(final Context context, final int resId,
+ final int defValue) {
+ final SharedPreferences sharedPrefs = getSharedPreferences(context);
+ return sharedPrefs.getInt(context.getString(resId), defValue);
+ }
+
+ public String getTimeFormat() {
+ if (isUse24hFormat()) {
+ return "HH:mm";
+ }
+ return "h:mm a";
+ }
+
+ /**
+ * Set locale read from preferences to context.
+ *
+ * @param context {@link Context}
+ */
+ public static void setLocale(final Context context) {
+ String lc = getString(context, R.string.gui_custom_locale_key,
+ DEFAULT_LANGUAGE_VALUE);
+ Locale locale = null;
+ // TODO lado this is very bad.
+ if (lc.equals(DEFAULT_LANGUAGE_VALUE)) {
+ String lang = Locale.getDefault().toString();
+ if (lang.startsWith("de")) {
+ locale = Locale.GERMAN;
+ } else if (lang.startsWith("it")) {
+ locale = Locale.ITALIAN;
+ } else {
+ locale = Locale.ENGLISH;
+ }
+ } else {
+ locale = new Locale(lc);
+ }
+ final Configuration config = new Configuration();
+ config.locale = locale;
+ context.getResources().updateConfiguration(config, null);
+ }
+
+ public boolean isRemoteEnabled() {
+ return getCurrentVdr().isEnableRemote();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecentChannelDAO.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecentChannelDAO.java
new file mode 100644
index 0000000..d8a8c41
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecentChannelDAO.java
@@ -0,0 +1,86 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import android.util.Log;
+
+import com.j256.ormlite.dao.BaseDaoImpl;
+import com.j256.ormlite.support.ConnectionSource;
+
+public class RecentChannelDAO extends BaseDaoImpl<RecenteChannel, String> {
+
+ private static final String TAG = RecentChannelDAO.class.getName();
+
+ public RecentChannelDAO(ConnectionSource connectionSource)
+ throws SQLException {
+ super(connectionSource, RecenteChannel.class);
+ }
+
+ public List<RecenteChannel> loadByLastAccess(long max) {
+ try {
+ return queryBuilder().orderBy("lastAccess", false).limit(max).query();
+ } catch (SQLException e) {
+ Log.w(TAG, e.getMessage(), e);
+ return new ArrayList<RecenteChannel>(0);
+ }
+ }
+
+ public List<RecenteChannel> loadByRecentUse(long max) {
+ try {
+ return queryBuilder().orderBy("count", false).limit(max).query();
+ } catch (SQLException e) {
+ Log.w(TAG, e.getMessage(), e);
+ return new ArrayList<RecenteChannel>(0);
+ }
+ }
+
+ public RecenteChannel queryForId(String id) {
+ try {
+ return super.queryForId(id);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public List<Channel> getRecentChannels(Map<String, Channel> all, List<RecenteChannel> recents){
+
+ List<Channel> filtered = new ArrayList<Channel>();
+ for(RecenteChannel rc : recents){
+ Channel c = all.get(rc.getChannelId());
+ if(c == null){
+ try {
+ delete(rc);
+ } catch (SQLException e) {
+ Log.w(TAG, e.getMessage(), e);
+ }
+ } else {
+ filtered.add(c);
+ }
+ }
+ return filtered;
+ }
+
+ public CreateOrUpdateStatus createOrUpdate(RecenteChannel data) {
+ try {
+ return super.createOrUpdate(data);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public void hit(String channelId) {
+ RecenteChannel rc = queryForId(channelId);
+
+ if (rc == null) {
+ rc = new RecenteChannel();
+ rc.setChannelId(channelId);
+ }
+ rc.touch();
+ rc.incUse();
+ createOrUpdate(rc);
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecenteChannel.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecenteChannel.java
new file mode 100644
index 0000000..b747537
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecenteChannel.java
@@ -0,0 +1,59 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.Date;
+
+import com.j256.ormlite.field.DatabaseField;
+import com.j256.ormlite.table.DatabaseTable;
+
+@DatabaseTable(daoClass=RecentChannelDAO.class)
+public class RecenteChannel {
+
+ @DatabaseField(id=true, generatedId = false)
+ private String channelId;
+
+ @DatabaseField
+ private long count = 0;
+
+ @DatabaseField
+ private Date lastAccess;
+
+ public Date getLastAccess() {
+ return lastAccess;
+ }
+
+ public void setLastAccess(Date lastAccess) {
+ this.lastAccess = lastAccess;
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ public void setCount(long count) {
+ this.count = count;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public void incUse(){
+ count++;
+ }
+
+ public void touch(){
+ lastAccess = new Date();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("[id="+channelId +", count="+count+", lastAccess=" + lastAccess);
+ return super.toString();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Recording.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Recording.java
new file mode 100644
index 0000000..d1ae085
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Recording.java
@@ -0,0 +1,257 @@
+package de.bjusystems.vdrmanager.data;
+
+import static de.bjusystems.vdrmanager.gui.Utils.mapSpecialChars;
+
+import java.util.Date;
+
+import de.bjusystems.vdrmanager.StringUtils;
+import de.bjusystems.vdrmanager.app.C;
+import de.bjusystems.vdrmanager.gui.Utils;
+
+public class Recording extends Event {
+
+ public static String ROOT_FOLDER = "";
+
+ public static final String FOLDERDELIMCHAR = "~";
+
+ public Recording(String line) {
+ String[] words = StringUtils.splitPreserveAllTokens(line,
+ C.DATA_SEPARATOR);
+ int idx = 0;
+ index = Integer.valueOf(words[idx++]);
+ start = new Date(Long.parseLong(words[idx++]) * 1000);
+ stop = new Date(Long.parseLong(words[idx++]) * 1000);
+ channelName = mapSpecialChars(words[idx++]);
+ title = mapSpecialChars(words[idx++]);
+ shortText = mapSpecialChars(words[idx++]);
+ description = mapSpecialChars(words[idx++]);
+ fileName = mapSpecialChars(words[idx++]);
+ try {
+ fileSize = Integer.valueOf(words[idx++]);
+ } catch (NumberFormatException ex) {
+
+ /************** TEMPORARY TO FIX THE BUG UNTIL Server's 0.13 */
+
+ int offset = 0;
+ int count = 0;
+ while(offset != -1){
+ offset = line.indexOf(":", offset + 1);
+ count++;
+ if(count == 5){
+ words = StringUtils.splitPreserveAllTokens(line.substring(0, offset) + Utils.unMapSpecialChars(":") + line.substring(offset+1),
+ C.DATA_SEPARATOR);
+ break;
+ }
+ }
+
+ idx = 0;
+ index = Integer.valueOf(words[idx++]);
+ start = new Date(Long.parseLong(words[idx++]) * 1000);
+ stop = new Date(Long.parseLong(words[idx++]) * 1000);
+ channelName = mapSpecialChars(words[idx++]);
+ title = mapSpecialChars(words[idx++]);
+ shortText = mapSpecialChars(words[idx++]);
+ description = mapSpecialChars(words[idx++]);
+ fileName = mapSpecialChars(words[idx++]);
+ fileSize = Integer.valueOf(words[idx++]);
+
+ if (idx < words.length) {
+ channelId = words[idx++];
+ }
+ if (idx < words.length) {
+ realDuration = Long.parseLong(words[idx++]) * 1000;
+ }
+
+ if (idx < words.length) {
+ devInode = mapSpecialChars(words[idx++]);
+ }
+
+ if (idx < words.length) { // timer
+ String data = words[idx++];
+ if (data != null && data.length() > 0) {
+ timerStopTime = new Date(Long.parseLong(data) * 1000L);
+ }
+ }
+
+ if (idx < words.length) { // name
+ String titleRaw = mapSpecialChars(words[idx++]);
+ int idxdel = titleRaw.lastIndexOf(FOLDERDELIMCHAR);
+ if (idxdel == -1) {
+ title = titleRaw;
+ folder = ROOT_FOLDER;
+ } else {
+ title = titleRaw.substring(idxdel + 1);
+ String foldersRaw = titleRaw.substring(0, idxdel);
+ folder = foldersRaw;
+
+ }
+ } else {
+ folder = ROOT_FOLDER;
+ }
+
+ if (idx < words.length) {
+ if (words[idx++].equals("1")) {
+ neww = true;
+ }
+ }
+ if (title.charAt(0) == '%') {
+ cut = true;
+ title = title.substring(1);
+ }
+ return;
+ }
+
+ if (idx < words.length) {
+ channelId = words[idx++];
+ }
+ if (idx < words.length) {
+ realDuration = Long.parseLong(words[idx++]) * 1000;
+ }
+
+ if (idx < words.length) {
+ devInode = mapSpecialChars(words[idx++]);
+ }
+
+ if (idx < words.length) { // timer
+ String data = words[idx++];
+ if (data != null && data.length() > 0) {
+ timerStopTime = new Date(Long.parseLong(data) * 1000L);
+ }
+ }
+
+ if (idx < words.length) { // name
+ String titleRaw = mapSpecialChars(words[idx++]);
+ int idxdel = titleRaw.lastIndexOf(FOLDERDELIMCHAR);
+ if (idxdel == -1) {
+ title = titleRaw;
+ folder = ROOT_FOLDER;
+ } else {
+ title = titleRaw.substring(idxdel + 1);
+ String foldersRaw = titleRaw.substring(0, idxdel);
+ folder = foldersRaw;
+
+ }
+ } else {
+ folder = ROOT_FOLDER;
+ }
+
+ if (idx < words.length) {
+ if (words[idx++].equals("1")) {
+ neww = true;
+ }
+ }
+ if (title.charAt(0) == '%') {
+ cut = true;
+ title = title.substring(1);
+ }
+
+ }
+
+ private String folder;
+
+ private String fileName;
+
+ private int fileSize;
+
+ private int index;
+
+ private long realDuration = -1;
+
+ private String devInode = null;
+
+ private boolean cut = false;
+
+ private boolean neww = false;
+
+ /**
+ * If it is not null, recording is on going or will be on going until this
+ * date;
+ */
+ private Date timerStopTime = null;
+
+ public Date getTimerStopTime() {
+ return timerStopTime;
+ }
+
+ public void setTimerStopTime(Date timerStopTime) {
+ this.timerStopTime = timerStopTime;
+ }
+
+ public String getDevInode() {
+ return devInode;
+ }
+
+ public void setDevInode(String devInode) {
+ this.devInode = devInode;
+ }
+
+ /**
+ * in millis
+ *
+ * @return
+ */
+ public long getRealDuration() {
+ return realDuration;
+ }
+
+ public long getDuration() {
+ if (timerStopTime != null) {
+ return timerStopTime.getTime() - start.getTime();
+ }
+
+ if (realDuration != -1) {
+ return realDuration;
+ }
+ return super.getDuration();
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public int getFileSize() {
+ return fileSize;
+ }
+
+ public void setFileSize(int fileSize) {
+ this.fileSize = fileSize;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String toCommandLine() {
+ return String.valueOf(index);
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+
+ public String getFolder() {
+ return folder;
+ }
+
+ public void setFolder(String folder) {
+ this.folder = folder;
+ }
+
+ public boolean isCut() {
+ return cut;
+ }
+
+ public boolean isNeww() {
+ return neww;
+ }
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecordingListItem.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecordingListItem.java
new file mode 100644
index 0000000..8395364
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/RecordingListItem.java
@@ -0,0 +1,45 @@
+package de.bjusystems.vdrmanager.data;
+
+import de.bjusystems.vdrmanager.gui.RecordingDir;
+
+
+public class RecordingListItem extends EventListItem {
+
+ public RecordingDir folder;
+
+ //public Integer count = 0;
+
+ public RecordingListItem(Recording rec) {
+ super(rec);
+ }
+
+ public RecordingListItem(String dailyHeader) {
+ super(dailyHeader);
+ }
+
+
+
+ @Override
+ public String getTitle() {
+ if(isFolder()){
+ return folder.getName();
+ }
+ return super.getTitle();
+ }
+
+ public boolean isFolder() {
+
+ return folder != null;
+
+ }
+
+ @Override
+ public String getHeader() {
+ if (isFolder()) {
+ return folder.getName();
+ } else {
+ return super.getHeader();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timer.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timer.java
new file mode 100644
index 0000000..7782dca
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timer.java
@@ -0,0 +1,276 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import android.text.TextUtils;
+import de.bjusystems.vdrmanager.StringUtils;
+import de.bjusystems.vdrmanager.app.C;
+import de.bjusystems.vdrmanager.gui.Utils;
+
+/**
+ * Class for timer data
+ *
+ * @author bju
+ */
+public class Timer extends Event implements Timerable {
+
+ // tfActive = 0x0001,
+ // tfInstant = 0x0002,
+ // tfVps = 0x0004,
+ // tfRecording = 0x0008,
+ // tfAll = 0xFFFF,
+ private static final int ACTIVE = 1;
+ private static final int INSTANT = 2;
+ private static final int VPS = 4;
+ private static final int RECORDING = 8;
+
+ private int number;
+ private int flags;
+ private int priority;
+ private int lifetime;
+ private String weekdays = "-------";
+ private boolean conflict;
+
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public void setLifetime(int lifetime) {
+ this.lifetime = lifetime;
+ }
+
+ public String getWeekdays() {
+ return weekdays;
+ }
+
+ public void setWeekdays(String weekdays) {
+ this.weekdays = weekdays;
+ }
+
+ /**
+ * Constructs a timer from SvdrpHelper result line
+ *
+ * @param timerData
+ * result line
+ */
+ public Timer(final String timerData) {
+
+ final String[] values = StringUtils.splitPreserveAllTokens(timerData,
+ C.DATA_SEPARATOR);
+
+ // number
+ this.number = Integer.valueOf(values[0].substring(1));
+
+ // flags, channel number and channel name
+ this.flags = Integer.valueOf(values[1]);
+ this.channelNumber = Long.valueOf(values[2]);
+ this.channelName = Utils.mapSpecialChars(values[3]);
+
+ // get start and stop
+ this.start = new Date(Long.parseLong(values[4]) * 1000);
+ this.stop = new Date(Long.parseLong(values[5]) * 1000);
+
+ // priority and lifetime
+ this.priority = Integer.valueOf(values[6]);
+ this.lifetime = Integer.valueOf(values[7]);
+
+ // title and description
+ this.title = Utils.mapSpecialChars(values[8]);
+
+ this.description = values.length > 9 ? values[9] : "";// aux
+
+ // 10 and 11 are optional if there where event with this timer
+ this.shortText = values.length > 10 ? Utils.mapSpecialChars(values[10])
+ : "";
+
+ if (values.length > 11) {
+ this.description = values[11]; // if real description, set it
+ }
+
+ if (values.length > 12) {
+ this.channelId = values[12];
+ }
+
+ if (values.length > 13) {
+ this.weekdays = values[13];
+ }
+
+ if (values.length > 14) {
+ this.conflict = values[14].equals("1");
+ }
+
+ description = Utils.mapSpecialChars(description);
+
+ if (values.length > 15) {
+ if (TextUtils.isEmpty(values[15]) == false) {
+ vps = Long.valueOf(values[15]) * 1000;
+ } else if (isVps()) {
+ vps = start.getTime();
+ }
+
+ }
+
+ }
+
+ public Timer copy() {
+ Timer t = new Timer(this);
+ t.flags = flags;
+ t.number = number;
+ t.priority = priority;
+ t.lifetime = lifetime;
+ t.start = new Date(start.getTime());
+ t.stop = new Date(stop.getTime());
+ t.title = title;
+ t.weekdays = weekdays;
+ t.conflict = conflict;
+ t.vps = vps;
+ return t;
+ }
+
+ public Timer(final Event event) {
+
+ this.number = 0;
+ this.flags = ACTIVE;
+ this.channelNumber = event.getChannelNumber();
+ this.channelName = event.getChannelName();
+ this.channelId = event.getChannelId();
+
+ this.start = event.getStart();
+
+ this.stop = event.getStop();
+
+ this.title = event.getTitle();
+ if (Utils.isSerie(event.getContent())) {
+ if (TextUtils.isEmpty(event.getShortText()) == false) {
+ this.title += "~" + event.getShortText();
+ }
+ }
+ this.description = event.getDescription();
+ this.vps = event.getVPS();
+ }
+
+ public boolean isRecurring() {
+ return weekdays.equals("-------") == false;
+ }
+
+ public String toCommandLine() {
+
+ final StringBuilder line = new StringBuilder();
+
+ // line.append(number).append(":");
+ line.append(flags).append(":");
+ line.append(channelNumber).append(":");
+
+ final Calendar cal = Calendar.getInstance();
+ if(isVps() == true){
+ cal.setTimeInMillis(vps);
+ } else {
+ cal.setTime(start);
+ }
+
+ cal.setTimeZone(TimeZone.getTimeZone(Preferences.get().getCurrentVdr()
+ .getServerTimeZone()));
+
+ line.append((weekdays.equals("-------") == false ? weekdays + "@" : "")
+ + String.format("%04d-%02d-%02d:", cal.get(Calendar.YEAR),
+ cal.get(Calendar.MONTH) + 1,
+ cal.get(Calendar.DAY_OF_MONTH)));
+ line.append(String.format("%02d%02d:", cal.get(Calendar.HOUR_OF_DAY),
+ cal.get(Calendar.MINUTE)));
+
+ cal.setTime(stop);
+ line.append(String.format("%02d%02d:", cal.get(Calendar.HOUR_OF_DAY),
+ cal.get(Calendar.MINUTE)));
+
+ line.append(priority).append(":");
+ line.append(lifetime).append(":");
+ line.append(Utils.unMapSpecialChars(title));
+ return line.toString();
+ }
+
+ public int getNumber() {
+ return number;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public int getLifetime() {
+ return lifetime;
+ }
+
+ public TimerState getTimerState() {
+ if (isEnabled()) {
+ if (isRecording()) {
+ return TimerState.Recording;
+ }
+ return TimerState.Active;
+ }
+ return TimerState.Inactive;
+ }
+
+ public boolean isEnabled() {
+ return (flags & ACTIVE) == ACTIVE;
+ }
+
+ public boolean isInstant() {
+ return (flags & INSTANT) == INSTANT;
+ }
+
+ public boolean isVps() {
+ return (flags & VPS) == VPS;
+ }
+
+ public boolean isRecording() {
+ return (flags & RECORDING) == RECORDING;
+ }
+
+ public boolean isConflict() {
+ return conflict;
+ }
+
+ public void setStart(final Date start) {
+ this.start = start;
+ }
+
+ public void setStop(final Date stop) {
+ this.stop = stop;
+ }
+
+ public void setVps(boolean useVps) {
+ if (useVps) {
+ flags = flags | VPS;
+ } else {
+ flags = flags & ~VPS;
+ }
+ }
+
+ public void setEnabled(final boolean enabled) {
+ if (enabled) {
+ flags = flags | ACTIVE;
+ } else {
+ flags = flags & ~ACTIVE;
+ }
+ }
+
+ public Timer getTimer() {
+ return this;
+ }
+
+ public Timer createTimer() {
+ return new Timer(this);
+ }
+
+ @Override
+ public TimerMatch getTimerMatch() {
+
+ if (isConflict())
+ return TimerMatch.Conflict;
+
+ return TimerMatch.Full;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/TimerMatch.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/TimerMatch.java
new file mode 100644
index 0000000..b8126ab
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/TimerMatch.java
@@ -0,0 +1,8 @@
+package de.bjusystems.vdrmanager.data;
+
+public enum TimerMatch {
+ Full, //
+ Begin, //
+ End, //
+ Conflict, //
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timerable.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timerable.java
new file mode 100644
index 0000000..aa371ed
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Timerable.java
@@ -0,0 +1,22 @@
+package de.bjusystems.vdrmanager.data;
+
+
+public interface Timerable {
+
+ public enum TimerState {
+ None,
+ Active,
+ Inactive,
+ Recording,
+ Recorded
+ ;
+ }
+
+ public Timer createTimer();
+
+ public abstract Timer getTimer();
+
+ public TimerState getTimerState();
+
+ public TimerMatch getTimerMatch();
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Vdr.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Vdr.java
new file mode 100644
index 0000000..b7960d3
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/Vdr.java
@@ -0,0 +1,732 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+import com.j256.ormlite.field.DatabaseField;
+import com.j256.ormlite.table.DatabaseTable;
+
+@DatabaseTable
+public class Vdr {
+
+ /**
+ *
+ */
+ @DatabaseField(columnName = "_id", generatedId = true)
+ private Integer id;
+
+ @DatabaseField(columnName = "name")
+ private String name = "VDR";
+
+ /**
+ * Use secure channel
+ */
+ @DatabaseField(defaultValue = "false")
+ private boolean secure;
+
+ /** host name or ip */
+ @DatabaseField
+ private String host = "0.0.0.0";
+
+ /** port */
+ @DatabaseField
+ private Integer port = 6420;
+
+ /** Password */
+ @DatabaseField
+ private String password = "";
+
+ /** should channels be filtered? */
+ @DatabaseField
+ private boolean filterChannels = false;
+
+ /** last channel to retrieve */
+ @DatabaseField
+ private String channelFilter = "";
+
+ /** Enable remote wakeup */
+ @DatabaseField
+ private boolean wakeupEnabled = false;
+
+ /** URL of the wakeup script */
+ @DatabaseField
+ private String wakeupUrl = "";
+
+ /** User for wakeup */
+ @DatabaseField
+ private String wakeupUser = "";
+
+ /** Password for wakeup */
+ @DatabaseField
+ private String wakeupPassword = "";
+
+ /**
+ * vdr mac for wol
+ *
+ * @since 0.2
+ */
+ @DatabaseField
+ private String mac = "";
+
+ /**
+ * which wakeup method to use
+ *
+ * @since 0.2
+ *
+ */
+ @DatabaseField
+ private String wakeupMethod = "wol";
+
+ /** Check for running VDR is enabled */
+ @DatabaseField
+ private boolean aliveCheckEnabled;
+
+ /** Intervall for alive test */
+ @DatabaseField
+ private Integer aliveCheckInterval;
+
+ /** Buffer before event */
+ @DatabaseField
+ private int timerPreMargin = 5;
+
+ /** Buffer after event */
+ @DatabaseField
+ private int timerPostMargin = 30;
+
+ /** Default priority */
+ @DatabaseField
+ private int timerDefaultPriority = 50;
+
+ /** Default lifetime */
+ @DatabaseField
+ private int timerDefaultLifetime = 99;
+
+ /** user defined epg search times */
+ @DatabaseField
+ private String epgSearchTimes = "";
+
+ /**
+ * Which port to use for streaming
+ *
+ * @since 0.2
+ */
+ @DatabaseField
+ private int streamPort = 3000;
+
+ /**
+ * Which format to use for streaming
+ *
+ * @since 0.2
+ */
+ @DatabaseField
+ private String streamFormat = "TS";
+
+ /**
+ * Do not send broadcasts, send directly to the host (router problem)
+ *
+ * @since 0.2
+ */
+ @DatabaseField
+ private String wolCustomBroadcast = "255.255.255.255";
+
+ /**
+ * Use remux ?
+ */
+ @DatabaseField
+ private boolean enableRemux = false;
+
+ /**
+ * Remux command
+ */
+ @DatabaseField
+ private String remuxCommand = "EXT";
+
+ /**
+ * Remux command Parameter
+ */
+ @DatabaseField
+ private String remuxParameter = "QUALITY=DSL1000";
+
+ @DatabaseField
+ private String encoding = "utf-8";
+
+ /**
+ * Connection timeout
+ */
+ @DatabaseField
+ private int connectionTimeout = 10;
+
+ /**
+ * Read Timeout
+ */
+ @DatabaseField
+ private int readTimeout = 10;
+
+ /**
+ * Timeout for a whole command run
+ */
+ @DatabaseField
+ private int timeout = 60;
+
+ @DatabaseField
+ private String streamingUsername = "";
+
+ @DatabaseField
+ private String streamingPassword = "";
+
+ @DatabaseField
+ private int livePort = 8008;
+
+ @DatabaseField
+ private String recStreamMethod = "vdr-live";
+
+ @DatabaseField(columnName="smarttvwebPort")
+ private int smarttvwebPort = 8000;
+
+ @DatabaseField(columnName="smarttvwebType")
+ private String smarttvwebType="ts";
+
+ @DatabaseField
+ private boolean enableRecStreaming = false;
+
+ @DatabaseField(columnName = "stz")
+ private String serverTimeZone = "Europe/Berlin";
+
+ @DatabaseField(columnName="svdrpPort")
+ private int svdrpPort = 6419;
+
+ @DatabaseField(columnName="enableRemote")
+ private boolean enableRemote = true;
+
+ public String getServerTimeZone() {
+ return serverTimeZone;
+ }
+
+ public void setServerTimeZone(String serverTimeZone) {
+ this.serverTimeZone = serverTimeZone;
+ }
+
+ public String getRecStreamMethod() {
+ return recStreamMethod;
+ }
+
+ public void setRecStreamMethod(String recStreamMethod) {
+ this.recStreamMethod = recStreamMethod;
+ }
+
+ public int getLivePort() {
+ return livePort;
+ }
+
+ public void setLivePort(int livePort) {
+ this.livePort = livePort;
+ }
+
+ public boolean isEnableRecStreaming() {
+ return enableRecStreaming;
+ }
+
+ public void setEnableRecStreaming(boolean enableRecStreaming) {
+ this.enableRecStreaming = enableRecStreaming;
+ }
+
+ public String getStreamingPassword() {
+ return streamingPassword;
+ }
+
+ public void setStreamingPassword(String streamingPassword) {
+ this.streamingPassword = streamingPassword;
+ }
+
+ public String getStreamingUsername() {
+ return streamingUsername;
+ }
+
+ public void setStreamingUsername(String streamingUsername) {
+ this.streamingUsername = streamingUsername;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isSecure() {
+ return secure;
+ }
+
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean isFilterChannels() {
+ return filterChannels;
+ }
+
+ public void setFilterChannels(boolean filterChannels) {
+ this.filterChannels = filterChannels;
+ }
+
+ public String getChannelFilter() {
+ return channelFilter;
+ }
+
+ public void setChannelFilter(String channelFilter) {
+ this.channelFilter = channelFilter;
+ }
+
+ public boolean isWakeupEnabled() {
+ return wakeupEnabled;
+ }
+
+ public void setWakeupEnabled(boolean wakeupEnabled) {
+ this.wakeupEnabled = wakeupEnabled;
+ }
+
+ public String getWakeupUrl() {
+ return wakeupUrl;
+ }
+
+ public void setWakeupUrl(String wakeupUrl) {
+ this.wakeupUrl = wakeupUrl;
+ }
+
+ public String getWakeupUser() {
+ return wakeupUser;
+ }
+
+ public void setWakeupUser(String wakeupUser) {
+ this.wakeupUser = wakeupUser;
+ }
+
+ public String getWakeupPassword() {
+ return wakeupPassword;
+ }
+
+ public void setWakeupPassword(String wakeupPassword) {
+ this.wakeupPassword = wakeupPassword;
+ }
+
+ public String getMac() {
+ return mac;
+ }
+
+ public void setMac(String mac) {
+ this.mac = mac;
+ }
+
+ public String getWakeupMethod() {
+ return wakeupMethod;
+ }
+
+ public void setWakeupMethod(String wakeupMethod) {
+ this.wakeupMethod = wakeupMethod;
+ }
+
+ public boolean isAliveCheckEnabled() {
+ return aliveCheckEnabled;
+ }
+
+ public void setAliveCheckEnabled(boolean aliveCheckEnabled) {
+ this.aliveCheckEnabled = aliveCheckEnabled;
+ }
+
+ public int getAliveCheckInterval() {
+ return aliveCheckInterval;
+ }
+
+ public void setAliveCheckInterval(int aliveCheckInterval) {
+ this.aliveCheckInterval = aliveCheckInterval;
+ }
+
+ public int getTimerPreMargin() {
+ return timerPreMargin;
+ }
+
+ public void setTimerPreMargin(int timerPreMargin) {
+ this.timerPreMargin = timerPreMargin;
+ }
+
+ public int getTimerPostMargin() {
+ return timerPostMargin;
+ }
+
+ public void setTimerPostMargin(int timerPostMargin) {
+ this.timerPostMargin = timerPostMargin;
+ }
+
+ public int getTimerDefaultPriority() {
+ return timerDefaultPriority;
+ }
+
+ public void setTimerDefaultPriority(int timerDefaultPriority) {
+ this.timerDefaultPriority = timerDefaultPriority;
+ }
+
+ public int getTimerDefaultLifetime() {
+ return timerDefaultLifetime;
+ }
+
+ public void setTimerDefaultLifetime(int timerDefaultLifetime) {
+ this.timerDefaultLifetime = timerDefaultLifetime;
+ }
+
+ public String getEpgSearchTimes() {
+ return epgSearchTimes;
+ }
+
+ public void setEpgSearchTimes(String epgSearchTimes) {
+ this.epgSearchTimes = epgSearchTimes;
+ }
+
+ public int getStreamPort() {
+ return streamPort;
+ }
+
+ public void setStreamPort(int streamPort) {
+ this.streamPort = streamPort;
+ }
+
+ public String getStreamFormat() {
+ return streamFormat;
+ }
+
+ public void setStreamFormat(String streamFormat) {
+ this.streamFormat = streamFormat;
+ }
+
+ public String getWolCustomBroadcast() {
+ return wolCustomBroadcast;
+ }
+
+ public void setWolCustomBroadcast(String wolCustomBroadcast) {
+ this.wolCustomBroadcast = wolCustomBroadcast;
+ }
+
+ public boolean isEnableRemux() {
+ return enableRemux;
+ }
+
+ public void setEnableRemux(boolean enableRemux) {
+ this.enableRemux = enableRemux;
+ }
+
+ public String getRemuxCommand() {
+ return remuxCommand;
+ }
+
+ public void setRemuxCommand(String remuxCommand) {
+ this.remuxCommand = remuxCommand;
+ }
+
+ public String getRemuxParameter() {
+ return remuxParameter;
+ }
+
+ public void setRemuxParameter(String remuxParameter) {
+ this.remuxParameter = remuxParameter;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public int getConnectionTimeout() {
+ return connectionTimeout;
+ }
+
+ public void setConnectionTimeout(int connectionTimeout) {
+ this.connectionTimeout = connectionTimeout;
+ }
+
+ public int getReadTimeout() {
+ return readTimeout;
+ }
+
+ public void setReadTimeout(int readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+ public int getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ private static <T> T get(Map<String, Object> map, String key) {
+ return get(map, key, null);
+ }
+
+ private static <T> T get(Map<String, Object> map, String key, Object def) {
+ if (map.containsKey(key)) {
+ return (T) map.get(key);
+ }
+ return (T) def;
+ }
+
+ private static Integer getInteger(Map<String, Object> map, String key) {
+ return getInteger(map, key, 0);
+ }
+
+ private static Integer getInteger(Map<String, Object> map, String key,
+ Integer def) {
+ if (map.containsKey(key) == false) {
+ return def;
+ }
+
+ Object obj = get(map, key);
+ if (obj instanceof Integer) {
+ return (Integer) obj;
+ }
+
+ try {
+ return Integer.valueOf(String.valueOf(obj));
+ } catch (NumberFormatException nfe) {
+ return def;
+ }
+ }
+
+ // private static Integer getInteger(Map<String, Object> map, String key) {
+ // if (map.containsKey(key) == false) {
+ // return Integer.valueOf(0);
+ // }
+
+ // Object obj = get(map, key);
+ // if (obj instanceof Integer) {
+ // return (Integer) obj;
+ // }
+ // return Integer.valueOf(String.valueOf(obj));
+ // }
+
+ private static Boolean getBoolean(Map<String, Object> map, String key) {
+ return getBoolean(map, key, false);
+ }
+
+ private static Boolean getBoolean(Map<String, Object> map, String key,
+ boolean defValue) {
+ if (map.containsKey(key) == false) {
+ return defValue;
+ }
+ Object obj = get(map, key);
+ if (obj instanceof Boolean) {
+ return (Boolean) obj;
+ }
+ return Boolean.valueOf(String.valueOf(obj));
+ }
+
+ public HashMap<String, Object> toMap() {
+ HashMap<String, Object> map = new HashMap<String, Object>();
+ map.put("vdr_name", name);
+ map.put("vdr_host", host);
+ map.put("vdr_port", port);
+ map.put("vdr_password", password);
+ map.put("vdr_secure", secure);
+
+ map.put("limit_channels", filterChannels);
+ map.put("last_channel", channelFilter);
+
+ map.put("key_wakeup_enabled", wakeupEnabled);
+ map.put("key_wakeup_url", wakeupUrl);
+ map.put("key_wakeup_user", wakeupUser);
+ map.put("key_wakeup_password", wakeupPassword);
+ map.put("key_wakeup_method", wakeupMethod);
+ map.put("key_wol_custom_broadcast", wolCustomBroadcast);
+ map.put("key_wakeup_wol_mac", mac);
+
+ map.put("key_conntimeout_key", connectionTimeout);
+ map.put("key_vdr_readtimeout", readTimeout);
+ map.put("key_vdr_timeout", timeout);
+
+ map.put("timer_pre_start_buffer", timerPreMargin);
+ map.put("timer_post_end_buffer", timerPostMargin);
+ map.put("timer_default_priority", timerDefaultPriority);
+ map.put("timer_default_lifetime", timerDefaultLifetime);
+
+ map.put("streamingport", streamPort);
+ map.put("key_streaming_password", streamingPassword);
+ map.put("key_streaming_username", streamingUsername);
+ map.put("key_vdr_encoding", encoding);
+ map.put("livetv_streamformat", streamFormat);
+ map.put("remux_command", remuxCommand);
+ map.put("remux_parameter", remuxParameter);
+ map.put("remux_enable", enableRemux);
+ map.put("key_rec_stream_enable", enableRecStreaming);
+ map.put("key_live_port", livePort);
+ map.put("key_recstream_method", recStreamMethod);
+ map.put("key_timezone", serverTimeZone);
+
+
+ map.put("key_smarttvweb_port", smarttvwebPort);
+ map.put("key_smarttvweb_recstream_method", smarttvwebType);
+ map.put("key_remote_enable", enableRemote);
+ map.put("key_svdrp_port", svdrpPort);
+ return map;
+ }
+
+ public void set(Map<String, Object> map) {
+ init(map);
+/*
+ name = get(map, "vdr_name");
+ host = get(map, "vdr_host");
+ port = getInteger(map, "vdr_port");
+ password = get(map, "vdr_password");
+ secure = getBoolean(map, "vdr_secure");
+
+ filterChannels = getBoolean(map, "limit_channels");
+ channelFilter = get(map, "last_channel");
+
+ wakeupEnabled = getBoolean(map, "key_wakeup_enabled");
+ wakeupUrl = get(map, "key_wakeup_url");
+ wakeupUser = get(map, "key_wakeup_user");
+ wakeupPassword = get(map, "key_wakeup_password");
+ wakeupMethod = get(map, "key_wakeup_method");
+ wolCustomBroadcast = get(map, "key_wol_custom_broadcast");
+ mac = get(map, "key_wakeup_wol_mac");
+
+ connectionTimeout = getInteger(map, "key_conntimeout_key");
+ readTimeout = getInteger(map, "key_vdr_readtimeout");
+ timeout = getInteger(map, "key_vdr_timeout");
+
+ timerPreMargin = getInteger(map, "timer_pre_start_buffer"
+ );
+ timerPostMargin = getInteger(map, "timer_post_end_buffer");
+ timerDefaultPriority = getInteger(map, "timer_default_priority");
+ timerDefaultLifetime = getInteger(map, "timer_default_lifetime");
+
+ streamPort = getInteger(map, "streamingport");
+ streamingPassword = get(map, "key_streaming_password");
+ streamingUsername = get(map, "key_streaming_username");
+ encoding = get(map, "key_vdr_encoding");
+ streamFormat = get(map, "livetv_streamformat");
+ remuxCommand = get(map, "remux_command");
+ remuxParameter = get(map, "remux_parameter");
+ enableRemux = getBoolean(map, "remux_enable");
+
+ enableRecStreaming = getBoolean(map, "key_rec_stream_enable");
+ livePort = getInteger(map, "key_live_port");
+ recStreamMethod = get(map, "key_recstream_method");
+ */
+ }
+
+ public void init(Map<String, Object> map) {
+ name = get(map, "vdr_name", "VDR");
+ host = get(map, "vdr_host", "0.0.0.0");
+ port = getInteger(map, "vdr_port", 6420);
+ password = get(map, "vdr_password", "");
+ secure = getBoolean(map, "vdr_secure");
+
+ filterChannels = getBoolean(map, "limit_channels", true);
+ channelFilter = get(map, "last_channel", "");
+
+ wakeupEnabled = getBoolean(map, "key_wakeup_enabled", false);
+ wakeupUrl = get(map, "key_wakeup_url", "");
+ wakeupUser = get(map, "key_wakeup_user", "");
+ wakeupPassword = get(map, "key_wakeup_password", "");
+ wakeupMethod = get(map, "key_wakeup_method", "wol");
+ wolCustomBroadcast = get(map, "key_wol_custom_broadcast", "");
+ mac = get(map, "key_wakeup_wol_mac", "");
+
+ connectionTimeout = getInteger(map, "key_conntimeout_key", 10);
+ readTimeout = getInteger(map, "key_vdr_readtimeout", 10);
+ timeout = getInteger(map, "key_vdr_timeout", 60);
+
+ timerPreMargin = getInteger(map, "timer_pre_start_buffer", 5);
+ timerPostMargin = getInteger(map, "timer_post_end_buffer", 30);
+ timerDefaultPriority = getInteger(map, "timer_default_priority", 50);
+ timerDefaultLifetime = getInteger(map, "timer_default_lifetime",99);
+
+ streamPort = getInteger(map, "streamingport", 3000);
+ streamingPassword = get(map, "key_streaming_password", "");
+ streamingUsername = get(map, "key_streaming_username", "");
+ encoding = get(map, "key_vdr_encoding", "utf-8");
+ streamFormat = get(map, "livetv_streamformat", "TS");
+ remuxCommand = get(map, "remux_command", "EXT");
+ remuxParameter = get(map, "remux_parameter", "");
+ enableRemux = getBoolean(map, "remux_enable");
+
+ enableRecStreaming = getBoolean(map, "key_rec_stream_enable");
+ livePort = getInteger(map, "key_live_port", 8008);
+ recStreamMethod = get(map, "key_recstream_method", "vdr-live");
+ serverTimeZone = get(map, "key_timezone", TimeZone.getDefault().getID());
+
+ smarttvwebPort= getInteger(map, "key_smarttvweb_port", 8000);
+ smarttvwebType= get(map, "key_smarttvweb_recstream_method", "progressive");
+ enableRemote = getBoolean(map, "key_remote_enable", true);
+ svdrpPort = getInteger(map, "key_svdrp_port", 6419);
+ }
+
+ public int getSmarttvwebPort() {
+ return smarttvwebPort;
+ }
+
+ public void setSmarttvwebPort(int smarttvwebPort) {
+ this.smarttvwebPort = smarttvwebPort;
+ }
+
+ public String getSmarttvwebType() {
+ return smarttvwebType;
+ }
+
+ public void setSmarttvwebType(String smarttvwebType) {
+ this.smarttvwebType = smarttvwebType;
+ }
+
+ public int getSvdrpPort() {
+ return svdrpPort;
+ }
+
+ public void setSvdrpPort(int svdrpPort) {
+ this.svdrpPort = svdrpPort;
+ }
+
+ public boolean isEnableRemote() {
+ return enableRemote;
+ }
+
+ public void setEnableRemote(boolean enableRemote) {
+ this.enableRemote = enableRemote;
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/VdrSharedPreferences.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/VdrSharedPreferences.java
new file mode 100644
index 0000000..bb0ad77
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/VdrSharedPreferences.java
@@ -0,0 +1,218 @@
+package de.bjusystems.vdrmanager.data;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import android.content.SharedPreferences;
+
+import com.j256.ormlite.dao.Dao.CreateOrUpdateStatus;
+import com.j256.ormlite.dao.RuntimeExceptionDao;
+
+@Deprecated
+public class VdrSharedPreferences implements SharedPreferences {
+
+ private static final String EMPTY_STRING = "";
+
+ public int commits = 0;
+
+ public RuntimeExceptionDao<Vdr, Integer> dao;
+
+ private Vdr instance;
+
+ public Vdr getInstance() {
+ return instance;
+ }
+
+ public void setInstance(Vdr instance) {
+ this.instance = instance;
+ map.putAll(instance.toMap());
+ }
+
+ protected List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>();
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ public VdrSharedPreferences() {
+
+ }
+
+ // public VdrSharedPreferences(Vdr vdr) {
+
+ // }
+
+ public boolean contains(String key) {
+ return map.containsKey(key);
+ }
+
+ public Editor edit() {
+ return new Editor();
+ }
+
+ public Map<String, Object> getAll() {
+ return map;
+ }
+
+ public boolean getBoolean(String key, boolean defValue) {
+ return get(key, defValue);
+ }
+
+ public float getFloat(String key, float defValue) {
+ return get(key, defValue);
+ }
+
+ public int getInt(String key, int defValue) {
+ String val = get(key, String.valueOf(defValue));
+ try {
+ return Integer.valueOf(val);
+ } catch (Exception ex) {
+ return defValue;
+ }
+ }
+
+ public long getLong(String key, long defValue) {
+ String val = get(key, String.valueOf(defValue));
+ try {
+ return Long.valueOf(val);
+ } catch (Exception ex) {
+ return defValue;
+ }
+ }
+
+ public <T> T get(String key, T defValue) {
+ if (map.containsKey(key)) {
+ return (T) map.get(key);
+ }
+ return defValue;
+ }
+
+ public String getString(String key, String defValue) {
+ Object obj = get(key, defValue);
+ if (obj == null) {
+ return EMPTY_STRING;
+ }
+ return String.valueOf(obj);
+ }
+
+ public Set<String> getStringSet(String arg0, Set<String> arg1) {
+ return null;
+ }
+
+ public void registerOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ public void unregisterOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * @author lado
+ *
+ */
+ public class Editor implements SharedPreferences.Editor {
+
+ Map<String, Object> modified = new HashMap<String, Object>();
+
+ private boolean clear = false;
+
+ public SharedPreferences.Editor clear() {
+ clear = true;
+ modified.clear();
+ return this;
+ }
+
+ public boolean commit() {
+
+ if (instance == null) {
+ map.putAll(modified);
+ return true;
+ }
+
+ instance.set(modified);
+
+ CreateOrUpdateStatus custatus = dao.createOrUpdate(instance);
+
+ boolean status = custatus.isCreated() || custatus.isUpdated();
+
+ if (status == false)
+ return false;
+
+ map.putAll(modified);
+
+ ++commits;
+
+ // and update any listeners
+ for (String key : modified.keySet()) {
+ for (OnSharedPreferenceChangeListener listener : listeners) {
+ listener.onSharedPreferenceChanged(
+ VdrSharedPreferences.this, key);
+ }
+ }
+
+ modified.clear();
+
+ return true;
+ }
+
+ public android.content.SharedPreferences.Editor put(String key,
+ Object value) {
+ synchronized (this) {
+ modified.put(key, value);
+ return this;
+ }
+ }
+
+ public android.content.SharedPreferences.Editor putBoolean(String key,
+ boolean value) {
+ return put(key, value);
+ }
+
+ public android.content.SharedPreferences.Editor putFloat(String key,
+ float value) {
+ return put(key, value);
+ }
+
+ public android.content.SharedPreferences.Editor putInt(String key,
+ int value) {
+ return put(key, value);
+ }
+
+ public android.content.SharedPreferences.Editor putLong(String key,
+ long value) {
+ return put(key, value);
+ }
+
+ public android.content.SharedPreferences.Editor putString(String key,
+ String value) {
+ return put(key, value);
+ }
+
+ public android.content.SharedPreferences.Editor remove(String key) {
+ synchronized (this) {
+ modified.remove(key);
+ return this;
+ }
+ }
+
+ public void apply() {
+ commit();
+ }
+
+ public android.content.SharedPreferences.Editor putStringSet(
+ String key, Set<String> values) {
+ synchronized (this) {
+ modified.put(key, (values == null) ? null
+ : new HashSet<String>(values));
+ return this;
+ }
+ }
+
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/WakeupState.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/WakeupState.java
new file mode 100644
index 0000000..f7917d8
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/WakeupState.java
@@ -0,0 +1,36 @@
+package de.bjusystems.vdrmanager.data;
+
+public class WakeupState {
+
+ public static WakeupState OK = new WakeupState(0);
+ public static WakeupState FAILED = new WakeupState(1);
+ public static WakeupState ERROR = new WakeupState(2);
+
+ private final int value;
+ private static WakeupState state;
+
+ private WakeupState(final int value) {
+ this.value = value;
+ }
+
+ public static WakeupState getState() {
+ return state;
+ }
+
+ public static void setState(final WakeupState state) {
+ WakeupState.state = state;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof WakeupState)) {
+ return false;
+ }
+ return this.value == ((WakeupState)o).value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Integer.valueOf(value).hashCode();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/DBAccess.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/DBAccess.java
new file mode 100644
index 0000000..f854204
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/DBAccess.java
@@ -0,0 +1,241 @@
+package de.bjusystems.vdrmanager.data.db;
+
+import java.sql.SQLException;
+import java.util.TimeZone;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import com.j256.ormlite.android.apptools.OpenHelperManager;
+import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
+import com.j256.ormlite.dao.RuntimeExceptionDao;
+import com.j256.ormlite.support.ConnectionSource;
+import com.j256.ormlite.table.TableUtils;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.RecentChannelDAO;
+import de.bjusystems.vdrmanager.data.RecenteChannel;
+import de.bjusystems.vdrmanager.data.Vdr;
+
+/**
+ * Database helper class used to manage the creation and upgrading of your
+ * database. This class also usually provides the DAOs used by the other
+ * classes.
+ */
+public class DBAccess extends OrmLiteSqliteOpenHelper {
+
+ public static final String TAG = DBAccess.class.getName();
+ // name of the database file for your application -- change to something
+ // appropriate for your app
+ public static final String DATABASE_NAME = "vdrmanager.db";
+ // any time you make changes to your database objects, you may have to
+ // increase the database version
+ // Version 3 since 0.6
+ private static final int DATABASE_VERSION = 5;
+
+ private RuntimeExceptionDao<Vdr, Integer> vdrDAO = null;
+
+ private RecentChannelDAO recentChannelDAO = null;
+
+ public static String getDataBaseFile() {
+ return "/data/data/de.bjusystems.vdrmanager/databases/" + DATABASE_NAME;
+ }
+
+ public DBAccess(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION,
+ R.raw.ormlite_config);
+ }
+
+ /**
+ * This is called when the database is first created. Usually you should
+ * call createTable statements here to create the tables that will store
+ * your data.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
+ try {
+ Log.i(DBAccess.class.getName(), "onCreate");
+ TableUtils.createTable(connectionSource, Vdr.class);
+ TableUtils.createTable(connectionSource, RecenteChannel.class);
+ } catch (SQLException e) {
+ Log.e(DBAccess.class.getName(), "Can't create database", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * This is called when your application is upgraded and it has a higher
+ * version number. This allows you to adjust the various data to match the
+ * new version number.
+ */
+ @Override
+ public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource,
+ int oldVersion, int newVersion) {
+ try {
+
+ // Log.i(DBAccess.class.getName(), "onUpgrade");
+ // TableUtils.dropTable(connectionSource, Vdr.class, true);
+ // after we drop the old databases, we create the new ones
+ // onCreate(db, connectionSource);
+
+ if (oldVersion < 3) {
+ TableUtils.createTable(connectionSource, RecenteChannel.class);
+ getVdrDAO()
+ .executeRaw(
+ "ALTER TABLE `vdr` ADD COLUMN stz varchar;");
+
+ String tz = TimeZone.getDefault().getID();
+ getVdrDAO()
+ .executeRaw(
+ "UPDATE `vdr` set stz = ?", tz);
+ }
+
+ if(oldVersion < 4){
+ getVdrDAO()
+ .executeRaw(
+ "ALTER TABLE `vdr` ADD COLUMN smarttvwebType varchar;");
+ getVdrDAO()
+ .executeRaw(
+ "ALTER TABLE `vdr` ADD COLUMN smarttvwebPort int;");
+
+ getVdrDAO()
+ .executeRaw(
+ "UPDATE `vdr` set smarttvwebPort = ?", "8000");
+ getVdrDAO()
+ .executeRaw(
+ "UPDATE `vdr` set smarttvwebType = ?", "progressive");
+
+
+ }
+
+ if(oldVersion < 5){
+ getVdrDAO()
+ .executeRaw(
+ "ALTER TABLE `vdr` ADD COLUMN enableRemote boolean;");
+ getVdrDAO()
+ .executeRaw(
+ "ALTER TABLE `vdr` ADD COLUMN svdrpPort int;");
+
+ getVdrDAO()
+ .executeRaw(
+ "UPDATE `vdr` set enableRemote = ?", "1");
+ getVdrDAO()
+ .executeRaw(
+ "UPDATE `vdr` set svdrpPort = ?", "6419");
+
+ }
+
+ } catch (SQLException e) {
+ Log.e(DBAccess.class.getName(), "Can't drop databases", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Close the database connections and clear any cached DAOs.
+ */
+ @Override
+ public void close() {
+ super.close();
+ vdrDAO = null;
+ }
+
+ static volatile DBAccess helper;
+ static volatile boolean created = false;
+ static volatile boolean destroyed = false;
+
+ /**
+ * Get a helper for this action.
+ */
+ public static DBAccess get(Context ctx) {
+
+ if (helper == null) {
+ helper = getHelperInternal(ctx);
+ created = true;
+ }
+
+ if (helper == null) {
+ if (!created) {
+ throw new IllegalStateException(
+ "A call has not been made to onCreate() yet so the helper is null");
+ } else if (destroyed) {
+ throw new IllegalStateException(
+ "A call to onDestroy has already been made and the helper cannot be used after that point");
+ } else {
+ throw new IllegalStateException(
+ "Helper is null for some unknown reason");
+ }
+ } else {
+ return helper;
+ }
+ }
+
+ /**
+ * Get a connection source for this action.
+ */
+ public ConnectionSource getConnectionSource(Context ctx) {
+ return get(ctx).getConnectionSource();
+ }
+
+ /**
+ * This is called internally by the class to populate the helper object
+ * instance. This should not be called directly by client code unless you
+ * know what you are doing. Use {@link #getHelper()} to get a helper
+ * instance. If you are managing your own helper creation, override this
+ * method to supply this activity with a helper instance.
+ *
+ * <p>
+ * <b> NOTE: </b> If you override this method, you most likely will need to
+ * override the {@link #releaseHelper(OrmLiteSqliteOpenHelper)} method as
+ * well.
+ * </p>
+ */
+ protected static DBAccess getHelperInternal(Context context) {
+ @SuppressWarnings({ "unchecked", "deprecation" })
+ DBAccess newHelper = (DBAccess) OpenHelperManager.getHelper(context,
+ DBAccess.class);
+ logger.trace("{}: got new helper {} from OpenHelperManager", "",
+ newHelper);
+ return newHelper;
+ }
+
+ /**
+ * Release the helper instance created in
+ * {@link #getHelperInternal(Context)}. You most likely will not need to
+ * call this directly since {@link #onDestroy()} does it for you.
+ *
+ * <p>
+ * <b> NOTE: </b> If you override this method, you most likely will need to
+ * override the {@link #getHelperInternal(Context)} method as well.
+ * </p>
+ */
+ protected void releaseHelper(DBAccess helper) {
+ OpenHelperManager.releaseHelper();
+ logger.trace("{}: helper {} was released, set to null", this, helper);
+ DBAccess.helper = null;
+ }
+
+ public RuntimeExceptionDao<Vdr, Integer> getVdrDAO() {
+ if (vdrDAO == null) {
+ vdrDAO = getRuntimeExceptionDao(Vdr.class);
+ }
+ return vdrDAO;
+ }
+
+ public RecentChannelDAO getRecentChannelDAO() {
+ if (recentChannelDAO == null) {
+ try {
+ recentChannelDAO = getDao(RecenteChannel.class);
+ } catch (SQLException e) {
+ throw new RuntimeException(
+ "Could not create RuntimeExcepitionDao for class "
+ + RecenteChannel.class, e);
+ }
+
+ }
+
+ return recentChannelDAO;
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/EPGSearchSuggestionsProvider.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/EPGSearchSuggestionsProvider.java
new file mode 100644
index 0000000..3ff6162
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/data/db/EPGSearchSuggestionsProvider.java
@@ -0,0 +1,23 @@
+package de.bjusystems.vdrmanager.data.db;
+
+import android.content.SearchRecentSuggestionsProvider;
+
+public class EPGSearchSuggestionsProvider extends
+ SearchRecentSuggestionsProvider {
+
+
+ public final static String AUTHORITY = EPGSearchSuggestionsProvider.class.getName();
+
+ public final static int MODE = DATABASE_MODE_QUERIES;// | DATABASE_MODE_2LINES;
+
+ public EPGSearchSuggestionsProvider() {
+ setupSuggestions(AUTHORITY, MODE);
+ }
+
+
+ public static enum SecondLine {
+ EPG,
+ EPG_SEARCH,
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/About.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/About.java
new file mode 100644
index 0000000..4d6290c
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/About.java
@@ -0,0 +1,35 @@
+package de.bjusystems.vdrmanager.gui;
+
+import de.bjusystems.vdrmanager.R;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.pm.PackageInfo;
+import android.text.SpannableString;
+import android.text.util.Linkify;
+
+public class About {
+
+ static AlertDialog about = null;
+
+ public static void show(Activity activity){
+ if(about == null){
+ String vi = "";
+ PackageInfo pi = Utils.getPackageInfo(activity);
+ if(pi != null){
+ vi = "v"+pi.versionName;
+ }
+ //View view = activity.getLayoutInflater().inflate(R.layout.about, null);
+ final SpannableString s = new SpannableString(activity.getString(R.string.about_text,vi));
+ Linkify.addLinks(s, Linkify.ALL);
+ about = new AlertDialog.Builder(activity)
+ .setTitle(R.string.about_title)
+ .setMessage(s)
+ .setPositiveButton(android.R.string.ok, null)
+ .setCancelable(false)
+ .create();
+ }
+ if(!activity.isFinishing()){
+ about.show();
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseActivity.java
new file mode 100644
index 0000000..9e0ccc4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseActivity.java
@@ -0,0 +1,651 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.List;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.v4.app.ActionBarDrawerToggle;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.view.MenuCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.ViewFlipper;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.remote.RemoteActivity;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpException;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpExceptionListener;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpFinishedListener;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpListener;
+
+public abstract class BaseActivity<Result, T extends ListView> extends
+ ActionBarActivity implements OnClickListener, SvdrpListener,
+ SvdrpExceptionListener, SvdrpFinishedListener<Result> {
+
+ public static final String TAG = BaseActivity.class.getName();
+
+ public static final int MENU_GROUP_REFRESH = 99;
+
+ public static final int MENU_REFRESH = 99;
+
+ protected T listView;
+
+ protected ViewFlipper flipper;
+
+ private Button retry;
+
+ private ProgressDialog progress;
+
+ // protected SvdrpProgressDialog progress;
+
+ private CharSequence mDrawerTitle;
+ private DrawerLayout mDrawerLayout;
+ private ListView mDrawerList;
+ private ActionBarDrawerToggle mDrawerToggle;
+ private String[] mTitles;
+
+ abstract protected String getWindowTitle();
+
+ abstract protected int getMainLayout();
+
+ protected void noInternetConnection() {
+ alert(R.string.no_internet_connection);
+ }
+
+ abstract protected boolean displayingResults();
+
+ /**
+ * When using the ActionBarDrawerToggle, you must call it during
+ * onPostCreate() and onConfigurationChanged()...
+ */
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ protected boolean isForceRefresh() {
+ if (forceRefresh == false) {
+ return false;
+ }
+ forceRefresh = false;
+ return true;
+ }
+
+ protected boolean forceRefresh = false;
+
+ protected void switchNoConnection() {
+ if (flipper == null) {
+ say(R.string.no_connection);
+ return;
+ }
+
+ if (displayingResults()) {
+ say(R.string.no_connection);
+ } else {
+ flipper.setDisplayedChild(1);
+ }
+ }
+
+ protected CertificateProblemDialog getCertificateProblemDialog() {
+ return new CertificateProblemDialog(this);
+ }
+
+ @Override
+ public void onConfigurationChanged(final Configuration newConfig) {
+ Preferences.setLocale(this);
+ super.onConfigurationChanged(newConfig);
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onResume() {
+ Preferences.setLocale(this);
+ // Preferences.init(this);
+ super.onResume();
+ }
+
+ protected void initFlipper() {
+ this.flipper = (ViewFlipper) findViewById(R.id.flipper);
+ retry = (Button) findViewById(R.id.retry_button);
+ retry.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(final View v) {
+ if (v.getId() == R.id.retry_button) {
+ retry();
+ }
+ }
+
+ //
+ // protected void updateWindowTitle(int topic, int subtopic) {
+ // String title;
+ // title = getString(topic);
+ // if (subtopic != -1) {
+ // title += " > " + getString(subtopic);
+ // }
+ // setTitle(title);
+ // }
+ //
+ // protected void updateWindowTitle(String topic, String subtopic) {
+ // String title = topic;
+ // if (subtopic != null) {
+ // title += " > " + subtopic;
+ // }
+ // setTitle(title);
+ // }
+
+ abstract protected int getListNavigationIndex();
+
+ public static final int LIST_NAVIGATION_CHANNELS = 0;
+ public static final int LIST_NAVIGATION_EPG_BY_TIME = 1;
+ public static final int LIST_NAVIGATION_EPG_BY_CHANNEL = 2;
+ public static final int LIST_NAVIGATION_RECORDINGS = 3;
+ public static final int LIST_NAVIGATION_TIMERS = 4;
+ public static final int LIST_NAVIGATION_REMOTE = 5;
+
+ protected boolean hasListNavigation() {
+ return true;
+ }
+
+ protected void initListNavigation() {
+
+ if (hasListNavigation() == false) {
+ return;
+ }
+
+ // getSupportActionBar().setDisplayShowTitleEnabled(false);
+ //
+ // getSupportActionBar().setNavigationMode(
+ // getSupportActionBar().NAVIGATION_MODE_LIST);
+ //
+ // final ArrayAdapter<CharSequence> mSpinnerAdapter = ArrayAdapter
+ // .createFromResource(this, R.array.navigation_array,
+ // android.R.layout.simple_spinner_dropdown_item);
+ //
+ // getSupportActionBar().setListNavigationCallbacks(mSpinnerAdapter,
+ // new OnNavigationListener() {
+ //
+ // private boolean firstHit = true;
+ //
+ // @Override
+ // public boolean onNavigationItemSelected(
+ // final int itemPosition, final long itemId) {
+ //
+ // if (firstHit == true) {
+ // firstHit = false;
+ // return false;
+ // }
+ // switch (itemPosition) {
+ //
+ // case LIST_NAVIGATION_CHANNELS: {
+ // startActivity(ChannelListActivity.class);
+ // return true;
+ // }
+ // case LIST_NAVIGATION_EPG_BY_TIME: {
+ // startActivity(TimeEpgListActivity.class);
+ // return true;
+ // }
+ //
+ // case LIST_NAVIGATION_EPG_BY_CHANNEL: {
+ // startActivity(EventEpgListActivity.class);
+ // return true;
+ // }
+ //
+ // case LIST_NAVIGATION_RECORDINGS: {
+ // startActivity(RecordingListActivity.class);
+ // return true;
+ // }
+ //
+ // case LIST_NAVIGATION_TIMERS: {
+ // startActivity(TimerListActivity.class);
+ // return true;
+ // }
+ //
+ // }
+ // return false;
+ // }
+ // });
+ // getSupportActionBar().setSelectedNavigationItem(
+ // getListNavigationIndex());
+
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Preferences.setLocale(this);
+ setContentView(getMainLayout());
+ progress = new ProgressDialog(this);
+ progress.setCancelable(false);
+ progress.setCanceledOnTouchOutside(false);
+ // progress.setOnCancelListener(new OnCancelListener() {
+ // @Override
+ // public void onCancel(DialogInterface dialog) {
+ //
+ // }
+ // });
+
+ initActionBar();
+
+ initListNavigation();
+
+ initLeftDrawer(savedInstanceState);
+
+ // new OnNavigationListener() {
+ // @Override
+ // public boolean onNavigationItemSelected(int itemPosition, long
+ // itemId) {
+ // System.err.println("itemPosition: "+ itemPosition +", itemId:" +
+ // itemId);
+ // rturn false;
+ // }
+ // });
+
+ // your logic for click listner
+ // setListenerForActionBarCustomView(actionBarView);
+
+ // private void setListenerForActionBarCustomView(View actionBarView) {
+ // ActionBarCustomViewOnClickListener actionBarCustomViewOnClickListener
+ // = new ActionBarCustomViewOnClickListener();
+ // actionBarView.findViewById(R.id.text_view_title).setOnClickListener(actionBarCustomViewOnClickListener);
+ // }
+
+ }
+
+ protected void initLeftDrawer(final Bundle savedInstanceState) {
+
+ mDrawerTitle = getTitle();
+
+ mTitles = getResources().getStringArray(R.array.navigation_array);
+
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ mDrawerList = (ListView) findViewById(R.id.left_drawer);
+
+ // set a custom shadow that overlays the main content when the drawer
+ // opens
+ mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,
+ GravityCompat.START);
+ // set up the drawer's list view with items and click listener
+ mDrawerList.setAdapter(new ArrayAdapter<String>(this,
+ R.layout.drawer_list_item, mTitles));
+ mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
+
+ // enable ActionBar app icon to behave as action to toggle nav drawer
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ // ActionBarDrawerToggle ties together the the proper interactions
+ // between the sliding drawer and the action bar app icon
+ mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
+ mDrawerLayout, /* DrawerLayout object */
+ R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
+ R.string.drawer_open, /* "open drawer" description for accessibility */
+ R.string.drawer_close /* "close drawer" description for accessibility */
+ ) {
+ public void onDrawerClosed(View view) {
+ // getSupportActionBar().setTitle(mTitle);
+ //invalidateOptionsMenu(); // creates call to
+ // onPrepareOptionsMenu()
+ }
+
+ public void onDrawerOpened(View drawerView) {
+ // getSupportActionBar().setTitle(mDrawerTitle);
+ //invalidateOptionsMenu(); // creates call to
+ // onPrepareOptionsMenu()
+ }
+ };
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+
+// if (savedInstanceState == null) {
+// selectItem(0);
+// }
+
+ }
+
+ /* The click listner for ListView in the navigation drawer */
+ private class DrawerItemClickListener implements
+ ListView.OnItemClickListener {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position,
+ long id) {
+ selectItem(position);
+ }
+ }
+
+ private void selectItem(int position) {
+
+ // update selected item and title, then close the drawer
+ mDrawerList.setItemChecked(position, true);
+ // setTitle(mPlanetTitles[position]);
+
+ switch (position) {
+
+ case LIST_NAVIGATION_CHANNELS: {
+ startActivity(ChannelListActivity.class);
+ break;
+ }
+ case LIST_NAVIGATION_EPG_BY_TIME: {
+ startActivity(TimeEpgListActivity.class);
+ break;
+ }
+
+ case LIST_NAVIGATION_EPG_BY_CHANNEL: {
+ startActivity(EventEpgListActivity.class);
+ break;
+ }
+
+ case LIST_NAVIGATION_RECORDINGS: {
+ startActivity(RecordingListActivity.class);
+ break;
+ }
+
+ case LIST_NAVIGATION_TIMERS: {
+ startActivity(TimerListActivity.class);
+ break;
+ }
+ case LIST_NAVIGATION_REMOTE: {
+ startActivity(RemoteActivity.class);
+ break;
+ }
+ }
+
+ mDrawerLayout.closeDrawer(mDrawerList);
+ }
+
+ protected void initActionBar() {
+ getSupportActionBar().setHomeButtonEnabled(true);
+ }
+
+ public void startActivity(final Class<?> clazz) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setClass(this, clazz);
+ startActivity(intent);
+ finish();
+ }
+
+ protected int getBaseMenu() {
+ return R.menu.refresh_filter_menu;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+
+ // MenuItem item;
+ // item = menu.add(MENU_GROUP_REFRESH, MENU_REFRESH, 0,
+ // R.string.refresh);
+ // item.setIcon(R.drawable.ic_menu_refresh);
+ // item.setAlphabeticShortcut('r');
+ final MenuInflater inf = getMenuInflater();
+ inf.inflate(getBaseMenu(), menu);
+
+ // SearchView searchView = (SearchView)
+ // menu.findItem(R.id.menu_search).getActionView();
+ // searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
+
+ return true;
+ }
+
+ abstract protected void refresh();
+
+ abstract protected void retry();
+
+ // abstract protected SvdrpClient<Result> getClient();
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+
+ if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.list_refresh:
+ backupViewSelection();
+ refresh();
+ return true;
+ case R.id.list_filter: {
+ onSearchRequested();
+ return true;
+ }
+ case android.R.id.home:
+ final Intent intent = new Intent(this, VdrManagerActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected void setCurrent(final Channel channel) {
+ getApp().setCurrentChannel(channel);
+ }
+
+ protected VdrManagerApp getApp() {
+ final VdrManagerApp app = (VdrManagerApp) getApplication();
+ return app;
+ }
+
+ // protected Channel getCurrentChannel(){
+ // final Channel channel = ((VdrManagerApp) getApplication())
+ // .getCurrentChannel();
+ // return channel;
+ // }
+
+ protected void say(final int res) {
+ say(this.getString(res));
+ }
+
+ protected void say(final String msg) {
+ Utils.say(this, msg);
+ }
+
+ protected boolean noConnection(final SvdrpEvent event) {
+ switch (event) {
+ case CONNECTION_TIMEOUT:
+ say(R.string.progress_connect_timeout);
+ switchNoConnection();
+ case CONNECT_ERROR:
+ say(R.string.progress_connect_error);
+ switchNoConnection();
+ break;
+ case FINISHED_ABNORMALY:
+ alert(R.string.progress_connect_finished_abnormal);
+ switchNoConnection();
+ break;
+ case LOGIN_ERROR:
+ say(R.string.progress_login_error);
+ switchNoConnection();
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ protected void alert(final String msg) {
+ if (isFinishing()) {
+ return;
+ }
+ new AlertDialog.Builder(this)//
+ .setMessage(msg)//
+ .setPositiveButton(android.R.string.ok, null)//
+ .create()//
+ .show();//
+ }
+
+ protected void alert(final int resId) {
+ alert(getString(resId));
+ }
+
+ protected void restoreViewSelection() {
+ listView.setSelectionFromTop(index, top);
+ }
+
+ protected void backupViewSelection() {
+ index = listView.getFirstVisiblePosition();
+ final View v = listView.getChildAt(0);
+ top = (v == null) ? 0 : v.getTop();
+ }
+
+ int index;
+ int top;
+
+ protected boolean checkInternetConnection() {
+ if (Utils.checkInternetConnection(this)) {
+ return true;
+ }
+ noInternetConnection();
+ return false;
+ }
+
+ // public void svdrpEvent(Result result) {
+ // resultReceived(result);
+ // }
+
+ @Override
+ public void svdrpEvent(final SvdrpEvent event, final Throwable t) {
+ progress.dismiss();
+ Utils.say(this, t.getMessage());
+ }
+
+ protected void addListener(
+ final SvdrpAsyncTask<Result, SvdrpClient<Result>> task) {
+ task.addSvdrpExceptionListener(this);
+ task.addSvdrpListener(this);
+ task.addSvdrpFinishedListener(this);
+ }
+
+ @Override
+ public void svdrpEvent(final SvdrpEvent event) {
+
+ switch (event) {
+ case LOGIN:
+ break;
+ case COMMAND_SENDING:
+ break;
+ case CONNECTING:
+ progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ setMessage(R.string.progress_connect);
+ if (!isFinishing()) {
+ progress.show();
+ }
+ break;
+ case LOGGED_IN:
+ setMessage(R.string.progress_login);
+ break;
+ case COMMAND_SENT:
+ setMessage(getProgressTextId());
+ break;
+ case DISCONNECTING:
+ setMessage(R.string.progress_disconnect);
+ break;
+ case DISCONNECTED:
+ break;
+ case ABORTED:
+ progress.dismiss();
+ say(R.string.aborted);
+ break;
+ case ERROR:
+ progress.dismiss();
+ alert(R.string.epg_client_errors);
+ break;
+ case CONNECTED:
+ connected();
+ break;
+ case CONNECTION_TIMEOUT:
+ case CONNECT_ERROR:
+ case FINISHED_ABNORMALY:
+ case LOGIN_ERROR:
+ progress.dismiss();
+ noConnection(event);
+ break;
+ case CACHE_HIT:
+ progress.dismiss();
+ cacheHit();
+ return;
+ case FINISHED_SUCCESS:
+ progress.dismiss();
+ break;
+ }
+ // case RESULT_RECEIVED:
+ // resultReceived(result);
+ // break;
+ // }
+ }
+
+ protected int getProgressTextId() {
+ return R.string.progress_loading;
+ }
+
+ private void setMessage(final int progressConnect) {
+ progress.setMessage(getString(progressConnect));
+ }
+
+ protected boolean finishedSuccess = false;
+
+ protected void cacheHit() {
+
+ }
+
+ /**
+ * @return false, if no results found
+ */
+ protected abstract boolean finishedSuccess(List<Result> results);
+
+ // /**
+ // * @param result
+ // */
+ // protected abstract void resultReceived(Result result);
+
+ protected void connected() {
+ if (flipper != null) {
+ flipper.setDisplayedChild(0);
+ }
+ }
+
+ public void svdrpException(final SvdrpException exception) {
+ // svdrpException(exception);
+ // Log.w(TAG, exception);
+ alert(getString(R.string.vdr_error_text, exception.getMessage()));
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (progress.isShowing()) {
+ progress.dismiss();
+ }
+ super.onDestroy();
+ }
+
+ @Override
+ public void finished(final List<Result> results) {
+ if (finishedSuccess(results)) {
+ finishedSuccess = true;
+ restoreViewSelection();
+ } else {
+ say(R.string.epg_no_items);
+ }
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventAdapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventAdapter.java
new file mode 100644
index 0000000..17edd42
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventAdapter.java
@@ -0,0 +1,365 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.Recording;
+import de.bjusystems.vdrmanager.data.TimerMatch;
+import de.bjusystems.vdrmanager.data.Timerable;
+
+abstract class BaseEventAdapter<T extends EventListItem> extends
+ ArrayAdapter<T> implements Filterable
+// , SectionIndexer
+{
+
+ protected final static int TYPE_ITEM = 0;
+ protected final static int TYPE_HEADER = 1;
+ protected final int layout;
+ protected final LayoutInflater inflater;
+ protected final List<T> items = new ArrayList<T>();
+
+ protected boolean hideDescription = true;
+
+ protected boolean hideChannelName = false;
+
+ String highlight;
+
+ /**
+ * Lock used to modify the content of {@link #mObjects}. Any write operation
+ * performed on the array should be synchronized on this lock. This lock is
+ * also used by the filter (see {@link #getFilter()} to make a synchronized
+ * copy of the original array of data.
+ */
+ private final Object _Lock = new Object();
+
+ public BaseEventAdapter(final Context context, int layout) {
+ super(context, layout);
+ this.layout = layout;
+ inflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override
+ public void add(T object) {
+ items.add(object);
+ // if (object.isHeader()) {
+ // sections.add(object.getHeader());
+ // }
+ super.add(object);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+
+ // get item
+ final EventListItem item = getItem(position);
+
+ if (item.isHeader()) {
+ return TYPE_HEADER;
+ }
+ return TYPE_ITEM;
+ }
+
+ public static class EventListItemHeaderHolder {
+ TextView header;
+ }
+
+ private boolean canReuseConvertView(View convertView, int itemViewType) {
+ if (convertView == null) {
+ return false;
+ }
+ Object o = convertView.getTag();
+ if (itemViewType == TYPE_ITEM) {
+ return o instanceof EventListItemHolder;
+ }
+
+ if (itemViewType == TYPE_HEADER) {
+ return o instanceof EventListItemHeaderHolder;
+ }
+
+ return false;
+
+ }
+
+ @Override
+ public View getView(final int position, View convertView,
+ final ViewGroup parent) {
+
+ // get item
+ final EventListItem item = getItem(position);
+
+ Object holder = null;
+ int type = getItemViewType(position);
+ if (canReuseConvertView(convertView, type) == false) {
+ switch (type) {
+ case TYPE_ITEM:
+ convertView = inflater.inflate(layout, null);
+ holder = getEventViewHolder(item, convertView);
+ break;
+ case TYPE_HEADER:
+ convertView = inflater.inflate(R.layout.header_item, null);
+ holder = getHeaderViewHolder(item, convertView);
+ break;
+ }
+ convertView.setTag(holder);
+ } else {
+ holder = convertView.getTag();
+ }
+
+ if (type == TYPE_ITEM) {
+ fillEventViewHolder((EventListItemHolder) holder, item);
+ } else {
+ ((EventListItemHeaderHolder) holder).header.setText(item
+ .getHeader());
+ }
+ return convertView;
+ }
+
+ protected EventListItemHolder getEventViewHolder(EventListItem item,
+ View view) {
+
+ EventListItemHolder itemHolder = new EventListItemHolder();
+
+ itemHolder = new EventListItemHolder();
+
+ itemHolder.state = (ImageView) view.findViewById(R.id.timer_item_state);
+ itemHolder.other = (ImageView) view.findViewById(R.id.timer_item_other);
+ itemHolder.time = (TextView) view.findViewById(R.id.timer_item_time);
+ itemHolder.channel = (TextView) view
+ .findViewById(R.id.timer_item_channel);
+ itemHolder.title = (TextView) view.findViewById(R.id.timer_item_title);
+ itemHolder.progress = (ProgressBar) view
+ .findViewById(R.id.timer_progress);
+ itemHolder.shortText = (TextView) view
+ .findViewById(R.id.timer_item_shorttext);
+ itemHolder.duration = (TextView) view
+ .findViewById(R.id.timer_item_duration);
+ itemHolder.description = (TextView) view
+ .findViewById(R.id.event_item_description);
+ return itemHolder;
+ }
+
+ protected void handleState(EventListItemHolder itemHolder,
+ EventListItem item) {
+ if (item.getEvent() instanceof Timerable) {
+ TimerMatch match = ((Timerable) item.getEvent()).getTimerMatch();
+ switch (((Timerable) item.getEvent()).getTimerState()) {
+ case Active:
+ itemHolder.state.setImageResource(Utils.getTimerStateDrawable(
+ match, R.drawable.timer_active,
+ R.drawable.timer_active_begin,
+ R.drawable.timer_active_end,
+ R.drawable.timer_active_conflict));
+ break;
+ case Inactive:
+ itemHolder.state.setImageResource(Utils.getTimerStateDrawable(
+ match, R.drawable.timer_inactive,
+ R.drawable.timer_inactive_begin,
+ R.drawable.timer_inactive_end,
+ R.drawable.timer_inactive));
+ break;
+ case Recording:
+ itemHolder.state.setImageResource(Utils.getTimerStateDrawable(
+ match, R.drawable.timer_recording,
+ R.drawable.timer_recording_begin,
+ R.drawable.timer_recording_end,
+ R.drawable.timer_recording_conflict));
+ break;
+ case None:
+ itemHolder.state.setImageResource(R.drawable.timer_none);
+ break;
+ }
+ } else {
+ itemHolder.state.setImageResource(R.drawable.timer_none);
+ }
+
+ }
+
+ public void fillEventViewHolder(EventListItemHolder itemHolder,
+ EventListItem item) {
+
+ itemHolder.state.setVisibility(View.VISIBLE);
+
+ handleState(itemHolder, item);
+
+ final EventFormatter formatter = getEventFormatter(item);
+ itemHolder.time.setText(formatter.getTime());
+ if (hideChannelName) {
+ itemHolder.channel.setVisibility(View.GONE);
+ } else {
+ itemHolder.channel.setText(item.getChannelName());
+ }
+
+ CharSequence title = Utils.highlight(formatter.getTitle(), highlight);
+ CharSequence shortText = Utils.highlight(formatter.getShortText(),
+ highlight);
+ itemHolder.title.setText(title);
+ itemHolder.shortText.setText(shortText);
+
+ if (TextUtils.isEmpty(formatter.getDescription()) == false
+ && hideDescription == false) {
+ Pair<Boolean, CharSequence> desc = Utils.highlight2(
+ formatter.getDescription(), highlight);
+ itemHolder.description.setVisibility(View.VISIBLE);
+ itemHolder.description.setText(desc.second);
+ } else {
+ itemHolder.description.setVisibility(View.GONE);
+ }
+
+ // TODO better render of duration
+ int p = Utils.getProgress(item.getEvent());
+ if (p == -1) {
+ itemHolder.progress.setVisibility(View.GONE);
+ int dura = Utils.getDuration(item);
+ itemHolder.duration.setText(getContext().getString(
+ R.string.epg_duration_template, dura));
+ } else {
+ itemHolder.progress.setVisibility(View.VISIBLE);
+ itemHolder.progress.setProgress(p);
+ int dura = Utils.getDuration(item.getEvent());
+ int rest = dura - (dura * p / 100);
+ // on live recordings the amount of already recorded time
+ if (item.getEvent() instanceof Recording) {
+ rest = dura - rest;
+ }
+ itemHolder.duration.setText(getContext().getString(
+ R.string.epg_duration_template_live, rest, dura));
+ }
+ }
+
+ protected EventListItemHeaderHolder getHeaderViewHolder(EventListItem item,
+ View view) {
+ EventListItemHeaderHolder itemHolder = new EventListItemHeaderHolder();
+ itemHolder.header = (TextView) view.findViewById(R.id.header_item);
+ return itemHolder;
+ }
+
+ protected EventFormatter getEventFormatter(Event event) {
+ return new EventFormatter(event);
+ }
+
+ protected void addSuper(T item) {
+ super.add(item);
+ }
+
+ protected void clearSuper() {
+ super.clear();
+ }
+
+ public boolean isHideDescription() {
+ return hideDescription;
+ }
+
+ public void setHideDescription(boolean hideDescription) {
+ this.hideDescription = hideDescription;
+ }
+
+ public boolean isHideChannelName() {
+ return hideChannelName;
+ }
+
+ public void setHideChannelName(boolean hideChannelName) {
+ this.hideChannelName = hideChannelName;
+ }
+
+ protected boolean isHeader(EventListItem item) {
+ return item.isHeader();
+ }
+
+ // TODO implement locking in performFiltering, check the parent class
+ // http://stackoverflow.com/questions/5846385/how-to-update-android-listview-with-dynamic-data-in-real-time
+ public Filter getFilter() {
+ return new Filter() {
+ /**
+ *
+ */
+ EventListItem prevHead = null;
+
+ @Override
+ protected FilterResults performFiltering(CharSequence arg0) {
+ highlight = arg0.toString().toLowerCase();
+ ArrayList<EventListItem> result = new ArrayList<EventListItem>();
+ for (EventListItem event : items) {
+ if (isHeader(event)) {
+ prevHead = event;
+ // result.add(event);
+ continue;
+ }
+ if (event.getTitle().toLowerCase()
+ .indexOf(String.valueOf(arg0).toLowerCase()) != -1
+ || event.getShortText()
+ .toLowerCase()
+ .indexOf(String.valueOf(arg0).toLowerCase()) != -1) {
+ if (prevHead != null) {
+ result.add(prevHead);
+ prevHead = null;
+ }
+ result.add(event);
+ }
+ }
+
+ FilterResults fr = new FilterResults();
+ fr.count = result.size();
+ fr.values = result;
+ return fr;
+ }
+
+ @Override
+ protected void publishResults(CharSequence arg0, FilterResults arg1) {
+ clearSuper();
+ for (T item : (ArrayList<T>) arg1.values) {
+ addSuper(item);
+ }
+ notifyDataSetChanged();
+ }
+ };
+ }
+
+ // @Override
+ // public int getPositionForSection(int section) {
+ // return 0;
+ // }
+
+ // @Override
+ // public int getSectionForPosition(int position) {
+ // TODO Auto-generated method stub
+ // return 0;
+ // }
+
+ // ArrayList<String> sections = new ArrayList<String>();
+
+ // @Override
+ // public Object[] getSections() {
+ // try {
+ // return sections.toArray();
+ // } finally {
+ // sections.clear();
+ // }
+ // }
+
+ @Override
+ public void clear() {
+ super.clear();
+ items.clear();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventListActivity.java
new file mode 100644
index 0000000..f1779d8
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseEventListActivity.java
@@ -0,0 +1,576 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.Intents;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.P;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.gui.SimpleGestureFilter.SimpleGestureListener;
+import de.bjusystems.vdrmanager.tasks.VoidAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpException;
+
+/**
+ * @author lado
+ *
+ */
+public abstract class BaseEventListActivity<T extends Event> extends
+ BaseActivity<T, ListView> implements OnItemClickListener,
+ SimpleGestureListener {
+
+ public static final String TAG = BaseEventListActivity.class.getName();
+
+ public static final int MENU_GROUP_SHARE = 90;
+
+ public static final int MENU_SHARE = 90;
+
+ public static final int MENU_GROUP_TO_CAL = 91;
+
+ public static final int MENU_TO_CAL = 91;
+
+ private SimpleGestureFilter detector;
+
+ protected BaseEventAdapter<EventListItem> adapter;
+
+ protected String highlight = null;
+
+ protected Date lastUpdate = null;
+
+ protected static final Date FUTURE = new Date(Long.MAX_VALUE);
+
+ // private static final Date BEGIN = new Date(0);
+
+ protected Channel currentChannel = null;
+
+ // protected List<T> results = new ArrayList<T>();
+
+ AlertDialog sortByDialog = null;
+
+ public static final int MENU_GROUP_DEFAULT = 0;
+
+ public static final int MENU_GROUP_ALPHABET = 1;
+
+ public static final int MENU_GROUP_CHANNEL = 2;
+
+ protected int sortBy;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ sortBy = Preferences.get(this, getViewID() + "_" + P.EPG_LAST_SORT,
+ MENU_GROUP_DEFAULT);
+ // Attach view
+ setTitle(getWindowTitle());
+ initFlipper();
+ detector = new SimpleGestureFilter(this, this);
+
+ initChannel();
+ }
+
+ private void initChannel() {
+ currentChannel = getApp().getCurrentChannel();
+ // currentChannel = getIntent()
+ // .getParcelableExtra(Intents.CURRENT_CHANNEL);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (notifyDataSetChangedOnResume()) {
+ adapter.notifyDataSetChanged();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * de.bjusystems.vdrmanager.gui.BaseActivity#onCreateOptionsMenu(android
+ * .view.Menu)
+ */
+ @Override
+ public boolean onCreateOptionsMenu(
+ final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.epg_list_menu, menu);
+ return true;
+ }
+
+ /**
+ * Prepare the current event and the chained events for
+ *
+ * @param event
+ */
+ protected int prepareDetailsViewData(final EventListItem item, int pos) {
+ final VdrManagerApp app = (VdrManagerApp) getApplication();
+ // remember event for details view and timer things
+ app.setCurrentEvent(item.getEvent());
+ ArrayList<Event> current = new ArrayList<Event>();
+ for (int i = 0; i < adapter.getCount(); ++i) {
+ EventListItem item2 = adapter.getItem(i);
+ if(item2.isHeader()){
+ continue;
+ }
+ current.add(item2.getEvent());
+ }
+ app.setCurrentEpgList(current);
+ for (int i = 0; i < pos; ++i) {
+ if (current.get(i) == item.getEvent()) {
+ return i;
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
+ */
+ @Override
+ public boolean onContextItemSelected(final MenuItem item) {
+
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
+ .getMenuInfo();
+ final EventListItem event = adapter.getItem(info.position);
+
+ final int itemId = item.getItemId();
+
+ switch (itemId) {
+
+ case R.id.epg_item_menu_live_tv: {
+ Utils.stream(this, event);
+ break;
+ }
+
+ case MENU_SHARE: {
+ Utils.shareEvent(this, event);
+ break;
+ }
+
+ case MENU_TO_CAL: {
+ Utils.addCalendarEvent(this, event);
+ break;
+ }
+
+ case R.id.epg_item_menu_switchto: {
+ Utils.switchTo(this, event.getChannelId(), event.getChannelName());
+ break;
+ }
+
+ default:
+ return super.onContextItemSelected(item);
+ }
+
+ return true;
+ }
+
+ protected int getAvailableSortByEntries() {
+ return 0;
+ }
+
+ protected void fillAdapter() {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * de.bjusystems.vdrmanager.gui.BaseActivity#onOptionsItemSelected(android
+ * .view.MenuItem)
+ */
+ public boolean onOptionsItemSelected(
+ final MenuItem item) {
+
+ switch (item.getItemId()) {
+
+ case R.id.epg_list_sort_menu: {
+
+ if (sortByDialog == null) {
+ sortByDialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.sort)
+ .setIcon(android.R.drawable.ic_menu_sort_alphabetically)
+ .setSingleChoiceItems(getAvailableSortByEntries(),
+ sortBy, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(
+ final DialogInterface dialog,
+ final int which) {
+
+ if (sortBy == which) {
+ sortByDialog.dismiss();
+ return;
+ }
+
+ sortBy = which;
+
+ new VoidAsyncTask() {
+
+ @Override
+ protected Void doInBackground(
+ final Void... params) {
+ Preferences
+ .set(BaseEventListActivity.this,
+ getViewID()
+ + "_"
+ + P.EPG_LAST_SORT,
+ sortBy);
+ return null;
+ }
+ }.execute();
+
+ sortByDialog.dismiss();
+ fillAdapter();
+ }
+
+ }).create();
+ }
+
+ sortByDialog.show();
+
+ return true;
+ }
+
+ // switch (item.getItemId()) {
+ // case R.id.epg_menu_search:
+ // startSearchManager();
+ // super.onSearchRequested();
+ // break;
+ // case R.id.epg_menu_times:
+ // intent = new Intent();
+ // /intent.setClass(this, EpgSearchTimesListActivity.class);
+ // startActivity(intent);
+ // break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu,
+ * android.view.View, android.view.ContextMenu.ContextMenuInfo)
+ */
+ @Override
+ public void onCreateContextMenu(final ContextMenu menu, final View v,
+ final ContextMenuInfo menuInfo) {
+
+ // if (v.getId() == R.id.whatson_list) {
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+
+ // set menu title
+ final EventListItem item = adapter.getItem(info.position);
+
+ if (item.isHeader()) {
+ return;
+ }
+
+ final MenuItem mi = menu.findItem(R.id.epg_item_menu_live_tv);
+ if (item.isLive() && item.getStreamId() != null) {
+
+ mi.setVisible(true);
+
+ } else {
+
+ mi.setVisible(false);
+ }
+ menu.add(MENU_GROUP_SHARE, MENU_SHARE, 0, R.string.share);
+ menu.add(MENU_GROUP_TO_CAL, MENU_TO_CAL, 0, R.string.addtocal);
+ super.onCreateContextMenu(menu, v, menuInfo);
+
+ }
+
+ /**
+ * @param parent
+ * @param view
+ * @param position
+ * @param id
+ */
+ @Override
+ public void onItemClick(final AdapterView<?> parent, final View view,
+ final int position, final long id) {
+
+ // find and remember item
+ final EventListItem item = adapter.getItem(position);
+
+ if (item.isHeader()) {
+ return;
+ }
+
+ int current = prepareDetailsViewData(item, position);
+
+ // show details
+ final Intent intent = new Intent(this, EpgDetailsActivity.class);
+ // | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ if (highlight != null) {
+ intent.putExtra(Intents.HIGHLIGHT, highlight);
+ }
+ intent.putExtra(Intents.CURRENT_EPG, current);
+ startActivityForResult(intent,
+ TimerDetailsActivity.REQUEST_CODE_TIMER_MODIFIED);
+ }
+
+ protected boolean notifyDataSetChangedOnResume() {
+ return true;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // if (epgClient != null) {
+ // epgClient.abort();
+ // }
+ // if (progress != null) {
+ // progress.dismiss();
+ // progress = null;
+ // }
+ }
+
+ // protected void resultReceived(T result) {
+ // results.add(result);
+ // }
+
+ @Override
+ protected void onRestoreInstanceState(final Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ final int index = savedInstanceState.getInt("INDEX");
+ final int top = savedInstanceState.getInt("TOP");
+ listView.setSelectionFromTop(index, top);
+ }
+
+ @Override
+ protected void onSaveInstanceState(final Bundle outState) {
+ final int index = listView.getFirstVisiblePosition();
+ final View v = listView.getChildAt(0);
+ final int top = (v == null) ? 0 : v.getTop();
+ outState.putInt("INDEX", index);
+ outState.putInt("TOP", top);
+ super.onSaveInstanceState(outState);
+ }
+
+ protected void dismiss(final AlertDialog dialog) {
+ if (dialog == null) {
+ return;
+ }
+ dialog.dismiss();
+ }
+
+ public boolean onSearchRequested() {
+ final InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMgr.toggleSoftInput(0, 0);
+ return true;
+ }
+
+ protected void startSearchManager() {
+ final Bundle appData = new Bundle();
+ startSearch(highlight, false, appData, false);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(final MotionEvent me) {
+ this.detector.onTouchEvent(me);
+ return super.dispatchTouchEvent(me);
+ }
+
+ @Override
+ public void onSwipe(final int direction) {
+
+ }
+
+ @Override
+ public void onDoubleTap() {
+
+ }
+
+ protected void sortItemsByChannel(final List<T> result) {
+ final Comparator<T> comparator = new Comparator<T>() {
+
+ @Override
+ public int compare(final T item1, final T item2) {
+ return item1.getChannelNumber().compareTo(
+ item2.getChannelNumber());
+ }
+ };
+ Collections.sort(result, comparator);
+ }
+
+ protected void sortItemsByTime(final List<T> result) {
+ sortItemsByTime(result, false);
+ }
+
+ protected void sortItemsByTime(final List<T> result, final boolean reverse) {
+ Collections.sort(result, new TimeAndChannelComparator(reverse));
+ }
+
+ @Override
+ public void svdrpException(final SvdrpException exception) {
+ Log.w(TAG, exception);
+ alert(getString(R.string.vdr_error_text, exception.getMessage()));
+ }
+
+ abstract protected boolean finishedSuccessImpl(List<T> results);
+
+ protected String getViewID() {
+ return this.getClass().getSimpleName();
+ }
+
+ protected void pushResultCountToTitle() {
+ setTitle(getString(R.string.epg_window_title_count, getWindowTitle(),
+ getCACHE().size()));
+ }
+
+ @Override
+ synchronized protected final boolean finishedSuccess(final List<T> results) {
+ // ProgressDialog dialog = new ProgressDialog(this);
+ // dialog.setMessage("Loading");
+ // dialog.show();
+ try {
+ lastUpdate = new Date();
+ final boolean r = finishedSuccessImpl(results);
+ if (r == false) {
+ adapter.clear();
+ adapter.notifyDataSetChanged();
+ }
+ return r;
+ } finally {
+ // dialog.dismiss();
+ // results.clear();
+ }
+ }
+
+ @Override
+ protected boolean displayingResults() {
+ return getCACHE().isEmpty() == false;
+ }
+
+ class TitleComparator implements Comparator<Event> {
+
+ @Override
+ public int compare(final Event lhs, final Event rhs) {
+ if (lhs == null || lhs.getTitle() == null) {
+ return 1;
+ }
+ if (rhs == null || rhs.getTitle() == null) {
+ return 0;
+ }
+ return lhs.getTitle().compareToIgnoreCase(rhs.getTitle());
+ }
+ };
+
+ class TimeAndChannelComparator implements Comparator<Event> {
+ boolean r = false;
+
+ TimeAndChannelComparator() {
+ this(false);
+ }
+
+ TimeAndChannelComparator(final boolean r) {
+ this.r = r;
+ }
+
+ @Override
+ public int compare(final Event item1, final Event item2) {
+
+ final int c = item1.getStart().compareTo(item2.getStart());
+ if (c != 0) {
+ if (r == false) {
+ return c;
+ }
+ return -1 * c;
+ }
+ if (item1.getChannelNumber() == null
+ && item2.getChannelNumber() == null) {
+ return 0;
+ }
+ if (item1.getChannelNumber() == null) {
+ return 1;
+ }
+ if (item2.getChannelNumber() == null) {
+ return -1;
+ }
+ return item1.getChannelNumber().compareTo(item2.getChannelNumber());
+ }
+ }
+
+ class TimeComparator implements Comparator<Event> {
+ boolean r = false;
+
+ TimeComparator(final boolean r) {
+ this.r = r;
+ }
+
+ @Override
+ public int compare(final Event item1, final Event item2) {
+
+ final int c = item1.getStart().compareTo(item2.getStart());
+ if (c == 0) {
+ return c;
+ }
+ if (r == false) {
+ return c;
+ }
+ return -1 * c;
+ }
+ }
+
+ class ChannelComparator implements Comparator<Event> {
+
+ @Override
+ public int compare(final Event item1, final Event item2) {
+
+ if (item1.getChannelNumber() == null
+ && item2.getChannelNumber() == null) {
+ return 0;
+ }
+ if (item1.getChannelNumber() == null) {
+ return 1;
+ }
+ if (item2.getChannelNumber() == null) {
+ return -1;
+ }
+ return item1.getChannelNumber().compareTo(item2.getChannelNumber());
+ }
+ }
+
+ public void clearCache() {
+ getCACHE().clear();
+ }
+
+ protected abstract List<T> getCACHE();
+
+ // @Override
+ // protected void connected() {
+ // super.connected();
+ // results.clear();
+ // }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BasePreferencesActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BasePreferencesActivity.java
new file mode 100644
index 0000000..50e9bb7
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BasePreferencesActivity.java
@@ -0,0 +1,203 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.FetchEditTextPreference;
+
+/**
+ *
+ * Basis class for PreferencesActivities with some goodies in it
+ * @author lado
+ *
+ */
+public abstract class BasePreferencesActivity extends PreferenceActivity {
+
+ protected void updateSummary(Preference ep) {
+ if (ep instanceof EditTextPreference) {
+ updateSummary((EditTextPreference) ep);
+ } else if (ep instanceof ListPreference) {
+ updateSummary((ListPreference) ep);
+ } else if(ep instanceof FetchEditTextPreference){
+ updateSummary((FetchEditTextPreference)ep);
+ }
+ }
+
+ /**
+ * If text set add it to the summary
+ *
+ * @param ep
+ */
+ protected void updateSummary(FetchEditTextPreference ep) {
+ String text = ep.getText();
+ if (text == null) {
+ text = "";
+ }
+ setSummary(text, ep);
+ }
+
+ /**
+ * If text set add it to the summary
+ *
+ * @param ep
+ */
+ protected void updateSummary(EditTextPreference ep) {
+ String text = ep.getText();
+ if (text == null) {
+ return;
+ }
+
+ if(isPassword(ep.getEditText())){
+ text = text.replaceAll(".", "*");
+ }
+
+ setSummary(text, ep);
+ }
+
+ protected boolean isPassword(EditText et){
+ if((et.getInputType() & EditorInfo.TYPE_TEXT_VARIATION_PASSWORD) == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD){
+ return true;
+ }
+ return false;
+ }
+
+ protected void setSummary(CharSequence text, Preference ep){
+ CharSequence sm = ep.getSummary();
+ String sum;
+ if (sm != null) {
+ sum = ep.getSummary().toString();
+ sum = substringBeforeLast(sum,
+ getString(R.string.prefs_current_value)).trim();
+ } else {
+ sum = "";
+ }
+
+ if(TextUtils.isEmpty(text)){
+ text = getString(R.string.prefs_current_value_not_set);
+ }
+
+ if (isBlank(sum)) {
+ sum = getString(R.string.prefs_current_value_template, text);
+ } else {
+ sum = sum + " "
+ + getString(R.string.prefs_current_value_template, text);
+ }
+ ep.setSummary(sum);
+ }
+
+ protected void updateSummary(ListPreference ep) {
+ CharSequence text = ep.getEntry();
+
+ if (text == null) {
+ return;
+ }
+ setSummary(text, ep);
+ }
+
+ /**
+ * <p>
+ * Gets the substring before the last occurrence of a separator. The
+ * separator is not returned.
+ * </p>
+ *
+ * <p>
+ * A <code>null</code> string input will return <code>null</code>. An empty
+ * ("") string input will return the empty string. An empty or
+ * <code>null</code> separator will return the input string.
+ * </p>
+ *
+ * <pre>
+ * StringUtils.substringBeforeLast(null, *) = null
+ * StringUtils.substringBeforeLast("", *) = ""
+ * StringUtils.substringBeforeLast("abcba", "b") = "abc"
+ * StringUtils.substringBeforeLast("abc", "c") = "ab"
+ * StringUtils.substringBeforeLast("a", "a") = ""
+ * StringUtils.substringBeforeLast("a", "z") = "a"
+ * StringUtils.substringBeforeLast("a", null) = "a"
+ * StringUtils.substringBeforeLast("a", "") = "a"
+ * </pre>
+ *
+ * @param str
+ * the String to get a substring from, may be null
+ * @param separator
+ * the String to search for, may be null
+ * @return the substring before the last occurrence of the separator,
+ * <code>null</code> if null String input
+ * @since 2.0
+ */
+ public static String substringBeforeLast(String str, String separator) {
+ if (isEmpty(str) || isEmpty(separator)) {
+ return str;
+ }
+ int pos = str.lastIndexOf(separator);
+ if (pos == -1) {
+ return str;
+ }
+ return str.substring(0, pos);
+ }
+
+ // Empty checks
+ // -----------------------------------------------------------------------
+ /**
+ * <p>
+ * Checks if a String is empty ("") or null.
+ * </p>
+ *
+ * <pre>
+ * StringUtils.isEmpty(null) = true
+ * StringUtils.isEmpty("") = true
+ * StringUtils.isEmpty(" ") = false
+ * StringUtils.isEmpty("bob") = false
+ * StringUtils.isEmpty(" bob ") = false
+ * </pre>
+ *
+ * <p>
+ * NOTE: This method changed in Lang version 2.0. It no longer trims the
+ * String. That functionality is available in isBlank().
+ * </p>
+ *
+ * @param str
+ * the String to check, may be null
+ * @return <code>true</code> if the String is empty or null
+ */
+ public static boolean isEmpty(String str) {
+ return str == null || str.length() == 0;
+ }
+
+ /**
+ * <p>
+ * Checks if a String is whitespace, empty ("") or null.
+ * </p>
+ *
+ * <pre>
+ * StringUtils.isBlank(null) = true
+ * StringUtils.isBlank("") = true
+ * StringUtils.isBlank(" ") = true
+ * StringUtils.isBlank("bob") = false
+ * StringUtils.isBlank(" bob ") = false
+ * </pre>
+ *
+ * @param str
+ * the String to check, may be null
+ * @return <code>true</code> if the String is null, empty or whitespace
+ * @since 2.0
+ */
+ public static boolean isBlank(String str) {
+ int strLen;
+ if (str == null || (strLen = str.length()) == 0) {
+ return true;
+ }
+ for (int i = 0; i < strLen; i++) {
+ if ((Character.isWhitespace(str.charAt(i)) == false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseTimerEditActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseTimerEditActivity.java
new file mode 100644
index 0000000..b377144
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/BaseTimerEditActivity.java
@@ -0,0 +1,244 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.content.Intent;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.Intents;
+import de.bjusystems.vdrmanager.data.EpgCache;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.data.TimerMatch;
+import de.bjusystems.vdrmanager.data.Timerable;
+import de.bjusystems.vdrmanager.tasks.DeleteTimerTask;
+import de.bjusystems.vdrmanager.tasks.ToggleTimerTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+
+/**
+ *
+ * This class is a base class for all the listings, which can deal with timers
+ *
+ * @author lado
+ *
+ * @param <T>
+ * Class extending Event
+ */
+public abstract class BaseTimerEditActivity<T extends Event> extends
+ BaseEventListActivity<T> implements OnClickListener // SvdrpAsyncListener<Timer>,
+{
+
+ // private static final ScheduledExecutorService worker = Executors
+ // .newSingleThreadScheduledExecutor();
+
+ // /@Override
+ // public boolean onPrepareOptionsMenu(Menu menu) {
+ // return super.onPrepareOptionsMenu(menu);
+ // }
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
+ */
+ // @Override
+ public boolean onContextItemSelected(final MenuItem item) {
+ //
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
+ .getMenuInfo();
+ final EventListItem event = adapter.getItem(info.position);
+ getApp().setCurrentEvent(event.getEvent());
+ switch (item.getItemId()) {
+ case R.id.epg_item_menu_timer_add: {
+
+ getApp().setCurrentTimer(createTimer(event));
+ final Intent intent = new Intent();
+ intent.setClass(this, TimerDetailsActivity.class);
+ intent.putExtra(Intents.TIMER_OP, Intents.ADD_TIMER);
+ startActivityForResult(intent,
+ TimerDetailsActivity.REQUEST_CODE_TIMER_ADD);
+ }
+ break;
+ case R.id.epg_item_menu_timer_modify: {
+ getApp().setCurrentTimer(getTimer(event));
+ final Intent intent = new Intent();
+ intent.setClass(this, TimerDetailsActivity.class);
+ intent.putExtra(Intents.TIMER_OP, Intents.EDIT_TIMER);
+ startActivityForResult(intent,
+ TimerDetailsActivity.REQUEST_CODE_TIMER_EDIT);
+ break;
+ }
+ case R.id.epg_item_menu_timer_delete: {
+ backupViewSelection();
+ deleteTimer(getTimer(event));
+ break;
+ }
+ case R.id.epg_item_menu_timer_toggle: {
+ backupViewSelection();
+ toggleTimer(getTimer(event));
+ break;
+ }
+ default:
+ return super.onContextItemSelected(item);
+ }
+
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu,
+ * android.view.View, android.view.ContextMenu.ContextMenuInfo)
+ */
+ @Override
+ public void onCreateContextMenu(final ContextMenu menu, final View v,
+ final ContextMenuInfo menuInfo) {
+ // if (v.getId() == R.id.whatson_list) {
+ final MenuInflater inflater = getMenuInflater();
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+
+ // set menu title
+ final EventListItem item = adapter.getItem(info.position);
+
+ if (item.isHeader()) {
+ return;
+ }
+
+ // final EventFormatter formatter = new EventFormatter(item);
+ menu.setHeaderTitle(item.getTitle());
+
+ inflater.inflate(R.menu.epg_list_item_menu, menu);
+ Timer timer = getTimer(item);
+ // remove unneeded menu items
+ if (timer == null || (Utils.getTimerMatch(item, timer) == TimerMatch.Full == false)) { //Bug #1372
+ super.onCreateContextMenu(menu, v, menuInfo);
+ } else {
+
+ menu.findItem(R.id.epg_item_menu_timer_add).setVisible(false);
+ menu.findItem(R.id.epg_item_menu_timer_modify).setVisible(true);
+ menu.findItem(R.id.epg_item_menu_timer_delete).setVisible(true);
+ final MenuItem enableMenuItem = menu
+ .findItem(R.id.epg_item_menu_timer_toggle);
+ enableMenuItem.setVisible(true);
+ enableMenuItem
+ .setTitle(timer.isEnabled() ? R.string.epg_item_menu_timer_disable
+ : R.string.epg_item_menu_timer_enable);
+ }
+
+
+ }
+
+ protected Timer createTimer(EventListItem item) {
+ Event e = item.getEvent();
+ if (e instanceof Timerable == false) {
+ return null;
+ }
+ return ((Timerable) e).createTimer();
+
+ }
+
+ /**
+ * Extract a Timer from a given {@link EventListItem}
+ *
+ * @param item
+ * @return Timer if any on the event
+ */
+ protected Timer getTimer(EventListItem item) {
+ Event e = item.getEvent();
+ if (e instanceof Timerable == false) {
+ return null;
+ }
+ return ((Timerable) e).getTimer();
+ }
+
+ protected void toggleTimer(final Timer timer) {
+ final ToggleTimerTask task = new ToggleTimerTask(this, timer) {
+ @Override
+ public void finished(SvdrpEvent event) {
+ timerModified(timer);
+ restoreViewSelection();
+ }
+ };
+ task.start();
+ }
+
+ /**
+ * Delete a given timer
+ *
+ * @param timer
+ */
+ protected void deleteTimer(final Timer timer) {
+ // backupViewSelection();
+ final DeleteTimerTask task = new DeleteTimerTask(this, timer) {
+ @Override
+ public void finished(SvdrpEvent event) {
+ timerModified(timer);
+ restoreViewSelection();
+ }
+ };
+ task.start();
+ }
+
+ protected void timerModified() {
+ timerModified(null);
+ }
+
+ /**
+ * Is called, if a timer has been changed and so update of the list is
+ * required
+ */
+ protected void timerModified(final Timer timer) {
+ backupViewSelection();
+ if(timer != null && timer.getChannelId()!=null){
+ EpgCache.CACHE.remove(timer.getChannelId());
+ }
+ // say(R.string.update_will_start_in);
+ // Runnable task = new Runnable() {
+ // public void run() {
+ refresh();
+
+ // }
+ // };
+ // worker.schedule(task, 1000, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.app.Activity#onActivityResult(int, int,
+ * android.content.Intent)
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode != RESULT_OK) {
+ return;
+ }
+ if (requestCode == TimerDetailsActivity.REQUEST_CODE_TIMER_EDIT) {
+ timerModified();
+ return;
+ }
+ if (requestCode == TimerDetailsActivity.REQUEST_CODE_TIMER_MODIFIED) {
+ timerModified();
+ return;
+ }
+
+ if (requestCode == TimerDetailsActivity.REQUEST_CODE_TIMER_ADD) {
+ timerModified();
+ return;
+ }
+
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/CertificateProblemDialog.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/CertificateProblemDialog.java
new file mode 100644
index 0000000..b0c58df
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/CertificateProblemDialog.java
@@ -0,0 +1,105 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.security.cert.X509Certificate;
+import java.util.concurrent.Semaphore;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.utils.svdrp.CertificateProblemListener;
+
+public class CertificateProblemDialog implements CertificateProblemListener {
+
+ /** Context */
+ private final Activity activity;
+ /** User wanted action */
+ CertificateProblemAction action;
+
+ /**
+ * Constructor
+ * @param activity Context
+ */
+ public CertificateProblemDialog(final Activity activity) {
+ this.activity = activity;
+ }
+
+ @Override
+ public CertificateProblemAction reportProblem(final X509Certificate[] chain, final String authType) {
+
+ // Semaphore to implement a modal dialog
+ final Semaphore semaphore = new Semaphore(0, true);
+
+ // certificate properties
+ final String[] values = chain[0].getSubjectDN().getName().split(",");
+ String host = "???";
+ for(String value : values) {
+ if (value.contains("CN=")) {
+ host = value.replace("CN=", "").trim();
+ break;
+ }
+ }
+ final String creationDate = chain[0].getNotBefore().toLocaleString();
+ final String validUntil = chain[0].getNotAfter().toLocaleString();
+
+ // message
+ final CharSequence message = String.format(activity.getString(R.string.certificate_problem_message_text), host, creationDate, validUntil);
+
+ // create dialog builder
+ final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
+ alertDialogBuilder.setTitle(R.string.certificate_problem_message_title);
+ alertDialogBuilder.setMessage(message);
+ alertDialogBuilder.setCancelable(false);
+
+ // buttons
+ alertDialogBuilder.setNegativeButton(R.string.certificate_not_accepted, new OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ action = CertificateProblemAction.ABORT;
+ dialog.cancel();
+ semaphore.release();
+ }
+ });
+ alertDialogBuilder.setNeutralButton(R.string.certificate_accept_once, new OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ action = CertificateProblemAction.ACCEPT_ONCE;
+ dialog.cancel();
+ semaphore.release();
+ }
+ });
+ alertDialogBuilder.setPositiveButton(R.string.certificate_accepted_forever, new OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ action = CertificateProblemAction.ACCEPT_FOREVER;
+ dialog.cancel();
+ semaphore.release();
+ }
+ });
+
+ // show the dialog
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final AlertDialog dialog = alertDialogBuilder.create();
+ dialog.show();
+ }
+ });
+
+
+ try {
+ semaphore.acquire();
+ } catch (final InterruptedException e) {
+ // NOP
+ }
+
+ return action;
+ }
+
+ @Override
+ public Activity getCurrentActivity() {
+ return activity;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelAdapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelAdapter.java
new file mode 100644
index 0000000..289dc63
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelAdapter.java
@@ -0,0 +1,258 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.content.Context;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.TextView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.utils.svdrp.ChannelClient;
+
+class ChannelAdapter extends BaseExpandableListAdapter implements Filterable// ,
+// SectionIndexer
+{
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ private Context context;
+
+ Map<String, ArrayList<Channel>> channels = new HashMap<String, ArrayList<Channel>>();
+
+ ArrayList<String> groups = new ArrayList<String>();
+
+ private boolean showChannelNumber;
+
+ public ChannelAdapter(Context context) {
+ this.context = context;
+ inflater = LayoutInflater.from(context);
+ this.showChannelNumber = Preferences.get().isShowChannelNumbers();
+ }
+
+ private LayoutInflater inflater;
+
+ private int groupBy = -1;
+
+ private boolean reverse = false;
+
+ public void fill(ArrayList<String> groups,
+ Map<String, ArrayList<Channel>> data, int groupBy, boolean reverse) {
+ this.groupBy = groupBy;
+ this.groups.clear();
+ this.groups.addAll(groups);
+ channels.clear();
+ channels.putAll(data);
+ notifyDataSetChanged();
+
+ }
+
+ public Object getChild(int groupPosition, int childPosition) {
+ String gn = groups.get(groupPosition);
+ ArrayList<Channel> channels = this.channels.get(gn);
+ return channels.get(childPosition);
+ }
+
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ // Return a child view. You can load your custom layout here.
+
+ public View getChildView(int groupPosition, int childPosition,
+ boolean isLastChild, View convertView, ViewGroup parent) {
+
+ Channel item = (Channel) getChild(groupPosition, childPosition);
+
+ ChannelHolder itemHolder = new ChannelHolder();
+
+ // recycle view?
+ View view = convertView;
+ if (view == null) {
+ view = inflater.inflate(R.layout.child_layout, null);
+ itemHolder = new ChannelHolder();
+
+ itemHolder.name = (TextView) view.findViewById(R.id.channel_name);
+ itemHolder.type = (ImageView) view.findViewById(R.id.channel_type);
+ itemHolder.aux = (TextView) view.findViewById(R.id.channel_aux);
+
+ view.setTag(itemHolder);
+ } else {
+ itemHolder = (ChannelHolder) view.getTag();
+ }
+
+ //view.setBackgroundColor(Color.BLACK);
+
+ CharSequence name = item.getName();
+ name = Utils.highlight(String.valueOf(name), channelFilter);
+
+ if (showChannelNumber) {
+ name = item.getNumber() + " - " + name;
+ }
+ itemHolder.name.setText(name);
+
+ if (groupBy == ChannelListActivity.MENU_PROVIDER) {
+ itemHolder.aux.setText(item.getGroup());
+ } else if(groupBy == ChannelListActivity.MENU_GROUP) {
+ itemHolder.aux.setText(item.getProvider());
+ } else if(groupBy== ChannelListActivity.MENU_SOURCE){
+ itemHolder.aux.setText(item.getSource());
+ } else {
+ itemHolder.aux.setText(item.getProvider());
+ }
+
+ return view;
+ }
+
+ public int getChildrenCount(int groupPosition) {
+ String gn = groups.get(groupPosition);
+ ArrayList<Channel> channels = this.channels.get(gn);
+ return channels.size();
+ }
+
+ public String getGroup(int groupPosition) {
+ return groups.get(groupPosition);
+ }
+
+ public int getGroupCount() {
+ return groups.size();
+ }
+
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ // Return a group view. You can load your custom layout here.
+
+ public View getGroupView(int groupPosition, boolean isExpanded,
+ View convertView, ViewGroup parent) {
+
+ String group = (String) getGroup(groupPosition);
+
+ int channelCount = this.channels.get(group).size();
+
+ CharSequence groupDisplay = Utils.highlight(group, groupFilter);
+
+ ChannelHolder itemHolder = new ChannelHolder();
+
+ // recycle view?
+ View view = convertView;
+ if (view == null) {
+ view = inflater.inflate(R.layout.group_layout, null);
+ itemHolder = new ChannelHolder();
+
+ itemHolder.name = (TextView) view.findViewById(R.id.group_name);
+ itemHolder.aux = (TextView) view.findViewById(R.id.channel_count);
+ // itemHolder.type = (ImageView)
+ // view.findViewById(R.id.channel_type);
+
+ view.setTag(itemHolder);
+ } else {
+ itemHolder = (ChannelHolder) view.getTag();
+ }
+ itemHolder.name.setText(groupDisplay);
+ itemHolder.aux.setText(String.valueOf(channelCount));
+
+ return view;
+
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public boolean isChildSelectable(int arg0, int arg1) {
+ return true;
+ }
+
+ private String groupFilter = null;
+
+ private String sourceFilter = null;
+
+ private String channelFilter = null;
+
+ public Filter getFilter() {
+ return new Filter() {
+ @Override
+ protected FilterResults performFiltering(CharSequence arg0) {
+ FilterResults fr = new FilterResults();
+ String q = String.valueOf(arg0).toLowerCase();
+ ArrayList<String> groups = new ArrayList<String>();
+ HashMap<String, ArrayList<Channel>> groupChannels = new HashMap<String, ArrayList<Channel>>();
+ if (groupBy == ChannelListActivity.MENU_GROUP) {
+ groupFilter = String.valueOf(arg0).toLowerCase();
+ for (String str : ChannelClient.getChannelGroups()) {
+ String g = str.toLowerCase();
+ if (g.indexOf(q) != -1) {
+ groups.add(str);
+ groupChannels.put(str, ChannelClient
+ .getGroupChannels().get(str));
+ }
+ }
+
+ } else if (groupBy == ChannelListActivity.MENU_PROVIDER) {
+ groupFilter = String.valueOf(arg0).toLowerCase();
+ for (Map.Entry<String, ArrayList<Channel>> p : ChannelClient
+ .getProviderChannels().entrySet()) {
+ String pr = p.getKey();
+ String g = pr.toLowerCase();
+ if (g.indexOf(q) != -1) {
+ groups.add(pr);
+ groupChannels.put(pr, p.getValue());
+ }
+ }
+
+ } else if(groupBy == ChannelListActivity.MENU_SOURCE) {
+ sourceFilter = String.valueOf(arg0).toLowerCase();
+ for (Map.Entry<String, ArrayList<Channel>> p : ChannelClient
+ .getSourceChannels().entrySet()) {
+ String pr = p.getKey();
+ String g = pr.toLowerCase();
+ if (g.indexOf(q) != -1) {
+ groups.add(pr);
+ groupChannels.put(pr, p.getValue());
+ }
+ }
+ } else {
+ channelFilter = String.valueOf(arg0).toLowerCase();
+ ArrayList<Channel> channels = new ArrayList<Channel>();
+ for(Channel c : ChannelClient.getChannels()){
+ String cname = c.getName();
+ String tmp = cname.toLowerCase();
+ if(tmp.indexOf(channelFilter) != -1){
+ channels.add(c);
+ }
+ }
+ String fakeGroup = context.getString(R.string.groupby_name_all_channels_group);
+ groups.add (fakeGroup);
+ groupChannels.put(fakeGroup, channels);
+ }
+ fr.values = Pair.create(groups, groupChannels);
+ return fr;
+ }
+
+ @Override
+ protected void publishResults(CharSequence arg0, FilterResults arg1) {
+ Pair<ArrayList<String>, HashMap<String, ArrayList<Channel>>> res = (Pair<ArrayList<String>, HashMap<String, ArrayList<Channel>>>) arg1.values;
+ fill(res.first, res.second, groupBy, reverse);
+
+ }
+ };
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelEventAdapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelEventAdapter.java
new file mode 100644
index 0000000..475f3e6
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelEventAdapter.java
@@ -0,0 +1,22 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.content.Context;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.EventListItem;
+
+class ChannelEventAdapter extends BaseEventAdapter<EventListItem>
+{
+
+
+ public ChannelEventAdapter(final Context context) {
+ super(context, R.layout.epg_event_item);
+ hideChannelName = true;
+ }
+
+ @Override
+ protected EventFormatter getEventFormatter(Event event) {
+ return new EventFormatter(event,true);
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelHolder.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelHolder.java
new file mode 100644
index 0000000..e103449
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelHolder.java
@@ -0,0 +1,10 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.widget.ImageView;
+import android.widget.TextView;
+
+class ChannelHolder {
+ public ImageView type;
+ public TextView name;
+ public TextView aux;
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListActivity.java
new file mode 100644
index 0000000..0b51f5e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListActivity.java
@@ -0,0 +1,637 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.ActionBarDrawerToggle;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.text.TextUtils;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.ExpandableListView.OnChildClickListener;
+import android.widget.ExpandableListView.OnGroupClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.P;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.RecenteChannel;
+import de.bjusystems.vdrmanager.data.db.DBAccess;
+import de.bjusystems.vdrmanager.tasks.VoidAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.ChannelClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+
+/**
+ * This class is used for showing what's current running on all channels
+ *
+ * @author bju
+ */
+public class ChannelListActivity extends
+ BaseActivity<Channel, ExpandableListView> implements
+ OnChildClickListener, OnGroupClickListener {
+
+ private static final String TAG = ChannelListActivity.class.getName();
+
+ ChannelAdapter adapter;
+
+ Preferences prefs;
+
+ // private static final LinkedList<Channel> RECENT = new
+ // LinkedList<Channel>();
+
+ public static final int MENU_GROUP = 0;
+ public static final int MENU_PROVIDER = 1;
+ public static final int MENU_SOURCE = 2;
+ public static final int MENU_NAME = 3;
+
+ public static final boolean GROUP_NATURAL = false;
+
+ public static final boolean GROUP_REVERSE = true;
+
+ private int groupBy;
+
+ private boolean groupByReverse;
+
+ final static ArrayList<String> ALL_CHANNELS_GROUP = new ArrayList<String>(1);
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Attach view
+
+ setTitle(getWindowTitle());
+ initFlipper();
+
+ groupBy = Preferences.get(this, P.CHANNELS_LAST_ORDER, MENU_GROUP);
+ groupByReverse = Preferences.get(this, P.CHANNELS_LAST_ORDER_REVERSE,
+ GROUP_NATURAL);
+
+ adapter = new ChannelAdapter(this);
+
+ listView = (ExpandableListView) findViewById(R.id.channel_list);
+ listView.setOnChildClickListener(this);
+ listView.setTextFilterEnabled(true);
+ listView.setFastScrollEnabled(true);
+ listView.setAdapter(adapter);
+ // register context menu
+ registerForContextMenu(listView);
+
+
+
+ startChannelQuery();
+
+ }
+
+
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ private void startChannelQuery() {
+ backupViewSelection();
+ startChannelQuery(true);
+ }
+
+ private void startChannelQuery(final boolean useCache) {
+
+ if (checkInternetConnection() == false) {
+ return;
+ }
+
+ final ChannelClient channelClient = new ChannelClient(
+ getCertificateProblemDialog());
+
+ if (useCache == false) {
+ ChannelClient.clearCache();
+ }
+
+ // create background task
+ final SvdrpAsyncTask<Channel, SvdrpClient<Channel>> task = new SvdrpAsyncTask<Channel, SvdrpClient<Channel>>(
+ channelClient);
+
+ addListener(task);
+ // task.addSvdrpExceptionListener(this);
+ // task.addSvdrpResultListener(this);
+ // task.addSvdrpListener(this);
+ // task.addSvdrpFinishedListener(this);
+
+ // start task
+ task.run();
+ }
+
+ static RecentChannelsAdapter RECENT_ADAPTER = null;
+
+ static class RecentChannelsAdapter extends ArrayAdapter<Channel> {
+ private final Activity context;
+ int resId;
+
+ public RecentChannelsAdapter(final Activity context) {
+ super(context, android.R.layout.simple_list_item_1);
+ this.context = context;
+ showChannelNumbers = Preferences.get().isShowChannelNumbers();
+
+ if (Build.VERSION.SDK_INT < 11) {
+ resId = android.R.layout.select_dialog_item;
+ } else {
+ resId = android.R.layout.simple_list_item_1;
+ }
+ }
+
+ public boolean showChannelNumbers;
+
+ @Override
+ public View getView(final int position, final View convertView,
+ final ViewGroup parent) {
+ // recycle view?
+ TextView text1;
+ View view = convertView;
+ if (view == null) {
+ view = this.context.getLayoutInflater().inflate(resId, null);
+ text1 = (TextView) view.findViewById(android.R.id.text1);
+ view.setTag(text1);
+ } else {
+ text1 = (TextView) view.getTag();
+ }
+
+ final Channel c = getItem(position);
+ String text = showChannelNumbers ? text = c.toString() : c
+ .getName();
+ text1.setText(text);
+ return view;
+
+ }
+ }
+
+ private ArrayAdapter<Channel> getRecentAdapter() {
+ if (RECENT_ADAPTER != null) {
+ RECENT_ADAPTER.showChannelNumbers = Preferences.get()
+ .isShowChannelNumbers();
+ RECENT_ADAPTER.notifyDataSetChanged();
+ return RECENT_ADAPTER;
+ }
+
+ RECENT_ADAPTER = new RecentChannelsAdapter(this);
+ return RECENT_ADAPTER;
+
+ }
+
+ private void fillAdapter() {
+ switch (groupBy) {
+ case MENU_GROUP:
+ final ArrayList<String> cgs = ChannelClient.getChannelGroups();
+ adapter.fill(cgs, ChannelClient.getGroupChannels(), groupBy,
+ groupByReverse);
+ if (cgs.size() == 1) {// one group or first no first group
+ listView.expandGroup(0);
+ } else if ((cgs.size() > 1 && TextUtils.isEmpty(cgs.get(0)))) {
+ listView.expandGroup(0);
+ }
+ updateWindowTitle();
+ break;
+
+ case MENU_SOURCE:
+ final ArrayList<String> css = ChannelClient.getChannelSources();
+ adapter.fill(css, ChannelClient.getSourceChannels(), groupBy,
+ groupByReverse);
+ if (css.size() == 1) {// one group or first no first group
+ listView.expandGroup(0);
+ } else if ((css.size() > 1 && TextUtils.isEmpty(css.get(0)))) {
+ listView.expandGroup(0);
+ }
+ updateWindowTitle();
+ break;
+
+ case MENU_PROVIDER:
+ final ArrayList<String> gs = new ArrayList<String>(ChannelClient
+ .getProviderChannels().keySet());
+ adapter.fill(gs, ChannelClient.getProviderChannels(), groupBy,
+ groupByReverse);
+ if (gs.size() == 1) {
+ listView.expandGroup(0);
+ }
+ updateWindowTitle();
+ break;
+ case MENU_NAME:
+ if (ALL_CHANNELS_GROUP.isEmpty()) {
+ ALL_CHANNELS_GROUP
+ .add(getString(R.string.groupby_name_all_channels_group));
+ }
+ final HashMap<String, ArrayList<Channel>> channels = new HashMap<String, ArrayList<Channel>>(
+ 1);
+ ArrayList<Channel> channelsSorted = ChannelClient.getChannels();
+ Collections.sort(channelsSorted, new Comparator<Channel>() {
+
+ @Override
+ public int compare(Channel lhs, Channel rhs) {
+ String lhsn = lhs.getName();
+ String rhsn = rhs.getName();
+ if (lhsn == null) {
+ return 1;
+ }
+ if (rhsn == null) {
+ return -1;
+ }
+ return lhsn.compareToIgnoreCase(rhsn);
+ }
+ });
+ channels.put(getString(R.string.groupby_name_all_channels_group),
+ channelsSorted);
+ adapter.fill(ALL_CHANNELS_GROUP, channels, groupBy, groupByReverse);
+ listView.expandGroup(0);
+ updateWindowTitle();
+ }
+ adapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.channellist, menu);
+
+ return true;
+ }
+
+ private int getAvailableGroupByEntries() {
+ return R.array.channels_group_by;
+ }
+
+ AlertDialog groupByDialog = null;
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+
+ switch (item.getItemId()) {
+ case R.id.channels_groupby:
+ // case MENU_PROVIDER:
+ // case MENU_NAME:
+ if (groupByDialog == null) {
+ groupByDialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.menu_groupby)
+ .setIcon(android.R.drawable.ic_menu_sort_alphabetically)
+ .setSingleChoiceItems(getAvailableGroupByEntries(),
+ groupBy, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(
+ final DialogInterface dialog,
+ final int which) {
+
+ final boolean reversed = which == groupBy ? true
+ : false;
+ groupBy = which;
+ new VoidAsyncTask() {
+
+ @Override
+ protected Void doInBackground(
+ final Void... params) {
+
+ if (reversed) {
+ if (groupByReverse == true) {
+ groupByReverse = false;
+ } else {
+ groupByReverse = true;
+ }
+ Preferences
+ .set(ChannelListActivity.this,
+ P.CHANNELS_LAST_ORDER_REVERSE,
+ groupByReverse);
+
+ } else {
+ Preferences
+ .set(ChannelListActivity.this,
+ P.CHANNELS_LAST_ORDER,
+ groupBy);
+ }
+ return null;
+ }
+ }.execute();
+
+ fillAdapter();
+ groupByDialog.dismiss();
+ }
+ }).create();
+ }
+
+ groupByDialog.show();
+
+ return true;
+ case R.id.channels_recent_channels:
+
+ final String order = Preferences.get(ChannelListActivity.this,
+ "gui_recent_channels_order", "most");
+
+ List<RecenteChannel> rcs = null;
+
+ if (order.equals("most")) {
+ rcs = DBAccess
+ .get(ChannelListActivity.this)
+ .getRecentChannelDAO()
+ .loadByRecentUse(
+ Preferences.get().getMaxRecentChannels());
+ } else if (order.equals("last")) {
+ rcs = DBAccess
+ .get(ChannelListActivity.this)
+ .getRecentChannelDAO()
+ .loadByLastAccess(
+ Preferences.get().getMaxRecentChannels());
+ } else {
+ return true;
+ }
+
+ if (rcs.isEmpty()) {
+ say(R.string.recent_channels_no_history);
+ return true;
+ }
+
+ if (Preferences.get().getMaxRecentChannels() <= 0) {
+ say(R.string.recent_channels_no_history);
+ return true;
+ }
+
+ final ArrayAdapter<Channel> recentAdapter = getRecentAdapter();
+
+ recentAdapter.clear();
+ for (final Channel c : DBAccess.get(ChannelListActivity.this)
+ .getRecentChannelDAO()
+ .getRecentChannels(ChannelClient.getIdChannels(), rcs)) {
+ recentAdapter.add(c);
+ }
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.recent_channels)
+ .setAdapter(getRecentAdapter(),
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(
+ final DialogInterface dialog,
+ final int which) {
+ final Channel c = recentAdapter
+ .getItem(which);
+ startChannelEPG(c);
+ }
+ })//
+ .create().show();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(final ContextMenu menu, final View v,
+ final ContextMenuInfo menuInfo) {
+ final ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
+ final int type = ExpandableListView
+ .getPackedPositionType(info.packedPosition);
+ final int group = ExpandableListView
+ .getPackedPositionGroup(info.packedPosition);
+ final int child = ExpandableListView
+ .getPackedPositionChild(info.packedPosition);
+ // Only create a context menu for child items
+ if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
+ // Array created earlier when we built the expandable list
+ final Channel item = (Channel) adapter.getChild(group, child);
+ // if (v.getId() == R.id.channel_list) {
+
+ final MenuInflater inflater = getMenuInflater();
+ menu.setHeaderTitle(item.getName());
+ inflater.inflate(R.menu.channel_list_item_menu, menu);
+
+ } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ /*
+ * http://projects.vdr-developer.org/issues/722 String grp =
+ * adapter.getGroup(group); final MenuInflater infl =
+ * getMenuInflater(); menu.setHeaderTitle(grp);
+ * infl.inflate(R.menu.channel_list_group_menu, menu);
+ */
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(final MenuItem item) {
+
+ final ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) item
+ .getMenuInfo();
+
+ // String title = ((TextView) info.targetView).getText().toString();
+
+ final int type = ExpandableListView
+ .getPackedPositionType(info.packedPosition);
+
+ Channel channel = null;
+ if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
+ final int groupPos = ExpandableListView
+ .getPackedPositionGroup(info.packedPosition);
+ final int childPos = ExpandableListView
+ .getPackedPositionChild(info.packedPosition);
+ channel = (Channel) adapter.getChild(groupPos, childPos);
+ switch (item.getItemId()) {
+ // case R.id.channel_item_menu_epg:
+ // startChannelEPG(channel);
+ // break;
+ case R.id.channel_item_menu_stream:
+ // show live stream
+ Utils.stream(this, channel);
+ break;
+
+ // case R.id.channel_item_menu_hide:
+ // TODO http://projects.vdr-developer.org/issues/722
+ // break;
+ // case R.id.channel_item_menu_hide_permanent:
+ // TODO http://projects.vdr-developer.org/issues/722
+ // break;
+
+ case R.id.channel_item_menu_switch:
+ Utils.switchTo(this, channel);
+ break;
+ }
+
+ return true;
+ } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ final int groupPos = ExpandableListView
+ .getPackedPositionGroup(info.packedPosition);
+
+ return true;
+ }
+
+ return false;
+
+ }
+
+ @Override
+ public boolean onSearchRequested() {
+ final InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMgr.toggleSoftInput(0, 0);
+ return true;
+ }
+
+ @Override
+ public boolean onGroupClick(final ExpandableListView arg0, final View arg1,
+ final int arg2, final long arg3) {
+ return true;
+ }
+
+ private void startChannelEPG(final Channel channel) {
+ new VoidAsyncTask() {
+
+ @Override
+ protected Void doInBackground(final Void... arg0) {
+ final int max = Preferences.get().getMaxRecentChannels();
+ if (max <= 0) {
+ return null;
+ }
+
+ DBAccess.get(ChannelListActivity.this).getRecentChannelDAO()
+ .hit(channel.getId());
+
+ return null;
+ }
+ }.execute((Void) null);
+ // for(int i = 0; i < recent)
+ // find and remember item
+ // final Channel channel = adapter.getItem(position);
+ // final VdrManagerApp app = (VdrManagerApp) getApplication();
+ // app.setCurrentChannel(channel);
+
+ // show details
+ final Intent intent = new Intent();
+ getApp().setCurrentChannel(channel);
+ // intent.putExtra(Intents.CURRENT_CHANNEL, channel);
+ intent.setClass(this, EventEpgListActivity.class);
+ startActivity(intent);
+ }
+
+ @Override
+ public boolean onChildClick(final ExpandableListView parent, final View v,
+ final int groupPosition, final int childPosition, final long id) {
+ final Channel channel = (Channel) adapter.getChild(groupPosition,
+ childPosition);
+ startChannelEPG(channel);
+ return false;
+ }
+
+ @Override
+ protected void refresh() {
+ backupViewSelection();
+ startChannelQuery(false);
+ }
+
+ @Override
+ protected void retry() {
+ refresh();
+ }
+
+ @Override
+ protected int getMainLayout() {
+ return R.layout.channel_list;
+ }
+
+ private String resolveWindowTitle() {
+ final StringBuilder sb = new StringBuilder();
+ switch (groupBy) {
+ case MENU_NAME:
+ sb.append(getString(R.string.action_menu_channels))
+ .append(" > ")
+ .append(getString(R.string.groupby_name_all_channels_group));
+ break;
+ case MENU_PROVIDER:
+ sb.append(getString(R.string.action_menu_channels))
+ .append(" > ")
+ .append(getString(R.string.groupby_window_title_templte,
+ getString(R.string.groupby_provider)));
+ break;
+ case MENU_GROUP:
+ sb.append(getString(R.string.action_menu_channels))
+ .append(" > ")
+ .append(getString(R.string.groupby_window_title_templte,
+ getString(R.string.groupby_group)));
+ break;
+
+ case MENU_SOURCE: {
+ sb.append(getString(R.string.action_menu_channels))
+ .append(" > ")
+ .append(getString(R.string.groupby_window_title_templte,
+ getString(R.string.groupby_source)));
+ break;
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private void updateWindowTitle() {
+ setTitle(getString(R.string.channels_window_title_count,
+ resolveWindowTitle(), adapter.groups.size(), ChannelClient
+ .getChannels().size()));
+ }
+
+ @Override
+ protected synchronized boolean finishedSuccess(final List<Channel> results) {
+ fillAdapter();
+ restoreViewSelection();
+ updateWindowTitle();
+ return ChannelClient.getChannels().isEmpty() == false;
+ }
+
+ @Override
+ protected void cacheHit() {
+ fillAdapter();
+ restoreViewSelection();
+ }
+
+ @Override
+ protected String getWindowTitle() {
+ return resolveWindowTitle();
+ }
+
+ @Override
+ protected boolean displayingResults() {
+ return ChannelClient.getChannels().isEmpty() == false;
+ }
+
+ @Override
+ protected int getProgressTextId() {
+ return R.string.progress_channels_loading;
+ }
+
+ @Override
+ protected int getListNavigationIndex() {
+ return LIST_NAVIGATION_CHANNELS;
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListFragment.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListFragment.java
new file mode 100644
index 0000000..0ce4cc2
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ChannelListFragment.java
@@ -0,0 +1,8 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.support.v4.app.Fragment;
+
+public class ChannelListFragment extends Fragment {
+
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ColoredButton.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ColoredButton.java
new file mode 100644
index 0000000..f10860c
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/ColoredButton.java
@@ -0,0 +1,92 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.shapes.RectShape;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+import de.bjusystems.vdrmanager.R;
+
+/**
+ * Created by lado on 03.05.15.
+ */
+public class ColoredButton extends Button {
+
+
+private final float defaultRadius = 0.0f;
+
+private int defaultPrimaryColor;
+
+ public ColoredButton(Context context) {
+ this(context, null);
+ }
+
+ public ColoredButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+
+ //@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ public ColoredButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ //defaultPrimaryColor = getResources().getColor(R.color.colorPrimary);
+
+ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColoredButton);
+ int primaryColor = typedArray.getColor(R.styleable.ColoredButton_normalStateColor, defaultPrimaryColor);
+ float radius = typedArray.getDimension(R.styleable.ColoredButton_cornerRadius, defaultRadius);
+
+ int pressedStateColor = primaryColor & 0x00ffffff | 0x96000000;
+ ShapeDrawable shapeSelected = new ShapeDrawable(new RectShape());
+ shapeSelected.getPaint().setColor(pressedStateColor);
+ shapeSelected.getPaint().setPathEffect(new CornerPathEffect(radius));
+ shapeSelected.getPaint().setAntiAlias(true);
+ shapeSelected.getPaint().setStyle(Paint.Style.FILL_AND_STROKE);
+ shapeSelected.getPaint().setStrokeWidth(1);
+
+
+ ShapeDrawable darkenSelected = new ShapeDrawable(new RectShape());
+ darkenSelected.getPaint().setColor(Color.BLACK);
+ darkenSelected.getPaint().setPathEffect(new CornerPathEffect(radius));
+ darkenSelected.getPaint().setAntiAlias(true);
+ if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ darkenSelected.getPaint().setXfermode(new PorterDuffXfermode(PorterDuff.Mode.OVERLAY));
+ }
+ darkenSelected.getPaint().setStyle(Paint.Style.FILL_AND_STROKE);
+ darkenSelected.getPaint().setStrokeWidth(1);
+
+
+ LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{darkenSelected, shapeSelected});
+
+ ShapeDrawable shapeNormal = new ShapeDrawable(new RectShape());
+ shapeNormal.getPaint().setAntiAlias(true);
+ shapeNormal.getPaint().setColor(primaryColor);
+ shapeNormal.getPaint().setPathEffect(new CornerPathEffect(radius));
+ shapeNormal.getPaint().setStyle(Paint.Style.FILL_AND_STROKE);
+ shapeNormal.getPaint().setStrokeWidth(1);
+
+ StateListDrawable states = new StateListDrawable();
+ //Resources res = getResources();
+ states.addState(new int[]{android.R.attr.state_pressed}, layerDrawable);
+ states.addState(new int[]{android.R.attr.state_focused}, layerDrawable);
+ states.addState(new int[]{}, shapeNormal);
+
+ if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){
+ setBackground(states);
+ } else {
+ setBackgroundDrawable(states);
+ }
+ //typedArray.recycle();
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgDetailsActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgDetailsActivity.java
new file mode 100644
index 0000000..c1daefa
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgDetailsActivity.java
@@ -0,0 +1,768 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.AlertDialog;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.support.v7.app.ActionBarActivity;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.viewpagerindicator.TitleProvider;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.Intents;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.Epg;
+import de.bjusystems.vdrmanager.data.EpgCache;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Recording;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.data.TimerMatch;
+import de.bjusystems.vdrmanager.data.Timerable;
+import de.bjusystems.vdrmanager.data.Timerable.TimerState;
+import de.bjusystems.vdrmanager.tasks.CreateTimerTask;
+import de.bjusystems.vdrmanager.tasks.DeleteTimerTask;
+import de.bjusystems.vdrmanager.tasks.ToggleTimerTask;
+import de.bjusystems.vdrmanager.tasks.VoidAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+
+/**
+ * This class is used for showing what's current running on all channels
+ *
+ * @author bju
+ */
+public class EpgDetailsActivity extends ActionBarActivity implements
+ OnClickListener, OnPageChangeListener {
+
+ public static final String TAG = "EpgDetailsActivity";
+
+ public static String IMDB_BASE_URL = "http://%s";
+
+ public static String IMDB_URL_QUERY = "/find?s=tt&q=%s";
+
+ public static String OMDB_URL = "http://www.omdb.org/search?search[text]=%s";
+
+ private static final String IMDB_URL_ENCODING = "UTF-8";
+
+ private static final String OMDB_URL_ENCODING = "UTF-8";
+
+ private static final String TMDB_URL_ENCODING = "UTF-8";
+
+ public static String TMDB_URL = "http://www.themoviedb.org/search?search=%s";
+
+ private String highlight = null;
+
+ // private Event cEvent;
+
+ // private ImageView state;
+
+ private boolean modifed = false;
+
+ // private int current;
+
+ private ViewPager pager;
+
+ private Adapter adapter;
+
+ // private Timerable timerable = null;
+
+ class Adapter extends PagerAdapter implements TitleProvider {
+
+ public Adapter() {
+
+ }
+
+ public String getTitle(int position) {
+ return epgs.get(position).getChannelName();
+ }
+
+ public int getCount() {
+ return epgs.size();
+ }
+
+ public Object instantiateItem(View pager, int position) {
+ View view = getLayoutInflater().inflate(R.layout.epg_detail, null);
+ // Event e = epgs.get(position);
+ publishEPG(view, position);
+ ((ViewPager) pager).addView(view, 0);
+
+ return view;
+ }
+
+ public void destroyItem(View pager, int position, Object view) {
+ ((ViewPager) pager).removeView((View) view);
+ }
+
+ public boolean isViewFromObject(View view, Object object) {
+ return view.equals(object);
+ }
+
+ public void finishUpdate(View view) {
+ }
+
+ public void restoreState(Parcelable p, ClassLoader c) {
+ }
+
+ public Parcelable saveState() {
+ return null;
+ }
+
+ public void startUpdate(View view) {
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ // requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+
+ Intent i = getIntent();
+
+ highlight = i.getStringExtra(Intents.HIGHLIGHT);
+ final int preselect = i.getIntExtra(Intents.CURRENT_EPG, 0);
+
+
+
+ // requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
+ // getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
+ // R.layout.titlebar);
+
+ // Attach view
+ setContentView(R.layout.epgdetails);
+
+ // detector = new SimpleGestureFilter(this, this);
+
+ // state = (ImageView) findViewById(R.id.epg_timer_state);
+
+ final Event epg = getApp().getCurrentEvent();
+ if (epg == null) {
+ finish();
+ }
+
+ final Event cEvent = epg;
+
+ if (epg instanceof Timerable) {
+ // timerable = (Timerable) cEvent;
+ }
+
+
+ pager = (ViewPager) findViewById(R.id.viewpager);
+ pager.setOnPageChangeListener(this);
+
+
+ new VoidAsyncTask() {
+
+ int counter = 0;
+
+ @Override
+ protected void onPreExecute() {
+ setProgressBarIndeterminateVisibility(true);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ // current event
+ final VdrManagerApp app = (VdrManagerApp) getApplication();
+ epgs = app.getCurrentEpgList();
+
+ if (epgs.isEmpty()) {
+ epgs.add(cEvent);
+ return (Void) null;
+ }
+
+ // for (Event e : epgs) {
+ // if (epg.equals(e)) {
+ // break;
+ //}
+ //counter++;
+ //}
+
+ //if (counter == epgs.size()) {// not found?
+ //epgs.add(0, cEvent);
+ //counter = 0;
+ //}
+ if(preselect < epgs.size()){
+ counter = preselect;
+ }
+ return (Void) null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ adapter = new Adapter();
+ pager.setAdapter(adapter);
+ pager.setCurrentItem(counter);
+ onPageSelected(counter);
+ }
+ }.execute((Void) null);
+
+ }
+
+ private void setState(ImageView view, int res) {
+ view.setVisibility(View.VISIBLE);
+ view.setImageResource(res);
+ }
+
+ private static String encode(String str, String enc) {
+ try {
+ return URLEncoder.encode(str, enc);
+ } catch (Exception ex) {
+ Log.w(TAG, ex);
+ return URLEncoder.encode(str);
+ }
+ }
+
+ public void publishEPG(final View view, int position) {
+
+ Event event = epgs.get(position);
+
+ Timerable timerable = null;
+
+ if (event instanceof Timerable) {
+ timerable = (Timerable) event;
+ }
+
+ view.setTag(event);
+ // view.setTag(event);
+
+ final EventFormatter formatter = new EventFormatter(event);
+
+ final TextView title = (TextView) view
+ .findViewById(R.id.epg_detail_title);
+ String titleText = formatter.getTitle();
+ title.setText(Utils.highlight(titleText, highlight));
+ // title.setTextSize(TypedValue.COMPLEX_UNIT_PX, title.getTextSize()
+ // * (float) 1.3);
+
+ ((TextView) view.findViewById(R.id.epg_detail_time)).setText(formatter
+ .getDate() + " " + formatter.getTime());
+
+ TextView dura = (TextView) view.findViewById(R.id.epg_detail_duration);
+
+ ((TextView) view.findViewById(R.id.epg_detail_channel)).setText(event
+ .getChannelName());
+ // ((TextView) findViewById(R.id.epg_detail_date)).setText(formatter
+ // .getLongDate());
+ ImageView state = (ImageView) view.findViewById(R.id.epg_timer_state);
+ if (timerable == null) {
+ setState(state, R.drawable.timer_none);
+ } else {
+
+ TimerMatch match = timerable.getTimerMatch();
+
+ switch (timerable.getTimerState()) {
+ case Active:
+ setState(state, Utils.getTimerStateDrawable(match,
+ R.drawable.timer_active,
+ R.drawable.timer_active_begin,
+ R.drawable.timer_active_end,
+ R.drawable.timer_active_conflict));
+ break;
+ case Inactive:
+ setState(state, Utils.getTimerStateDrawable(match,
+ R.drawable.timer_inactive,
+ R.drawable.timer_inactive_begin,
+ R.drawable.timer_inactive_end,
+ R.drawable.timer_inactive));
+ break;
+ case Recording:
+ setState(state, Utils.getTimerStateDrawable(match,
+ R.drawable.timer_recording,
+ R.drawable.timer_recording_begin,
+ R.drawable.timer_recording_end,
+ R.drawable.timer_recording_conflict));
+ break;
+ default:
+ setState(state, R.drawable.timer_none);
+ }
+ }
+ final TextView shortText = (TextView) view
+ .findViewById(R.id.epg_detail_shorttext);
+ shortText.setText(Utils.highlight(formatter.getShortText(), highlight));
+
+ final TextView textView = (TextView) view
+ .findViewById(R.id.epg_detail_description);
+ textView.setText(Utils.highlight(formatter.getDescription(), highlight));
+
+ if (event.getAudio().isEmpty() == false) {
+ view.findViewById(R.id.audio_block).setVisibility(View.VISIBLE);
+ final TextView audioTracks = (TextView) view
+ .findViewById(R.id.epg_detail_audio);
+ audioTracks.setText(Utils.formatAudio(this, event.getAudio()));
+ } else {
+ view.findViewById(R.id.audio_block).setVisibility(View.GONE);
+ }
+
+ TextView contentView = ((TextView) view
+ .findViewById(R.id.epg_detail_cats));
+ if (event.getContent().length > 0) {
+ contentView.setVisibility(View.VISIBLE);
+ contentView
+ .setText(Utils.getContenString(this, event.getContent()));
+ } else {
+ contentView.setVisibility(View.GONE);
+ }
+
+ // copy color for separator lines
+ // final int color = textView.getTextColors().getDefaultColor();
+ // ((TextView) findViewById(R.id.epg_detail_separator_1))
+ // .setBackgroundColor(color);
+
+ int p = Utils.getProgress(event);
+
+ ((ProgressBar) view.findViewById(R.id.epg_detail_progress))
+ .setProgress(p);
+ int dm = Utils.getDuration(event);
+ if (Utils.isLive(event)) {
+ int rest = dm - (dm * p / 100);
+ dura.setText(getString(R.string.epg_duration_template_live, rest,
+ dm));
+ } else {
+ dura.setText(getString(R.string.epg_duration_template, dm));
+ }
+
+ // ((TextView) view.findViewById(R.id.epg_detail_separator_2))
+ // .setBackgroundColor(color);
+
+ // register button handler
+ if (timerable == null) {
+ view.findViewById(R.id.epg_event_create_timer).setVisibility(
+ View.GONE);
+ } else {
+ setThisAsOnClickListener(view, R.id.epg_event_create_timer);
+ }
+
+ View b = view.findViewById(R.id.epg_event_imdb);
+
+ if (Preferences.get().isShowImdbButton() == false) {
+ b.setVisibility(View.GONE);
+ } else {
+ b.setVisibility(View.VISIBLE);
+ b.setOnClickListener(new OnClickListener() {
+
+ public void onClick(View v) {
+ startFilmDatabaseBrowseIntent(
+ String.format(IMDB_BASE_URL, Preferences.get()
+ .getImdbUrl())
+ + IMDB_URL_QUERY, view, IMDB_URL_ENCODING);
+ }
+ });
+ }
+
+ b = view.findViewById(R.id.epg_event_omdb);
+
+ if (Preferences.get().isShowOmdbButton() == false) {
+ b.setVisibility(View.GONE);
+ } else {
+ b.setVisibility(View.VISIBLE);
+ b.setOnClickListener(new OnClickListener() {
+
+ public void onClick(View v) {
+ startFilmDatabaseBrowseIntent(OMDB_URL, view,
+ OMDB_URL_ENCODING);
+ }
+ });
+ }
+
+ b = view.findViewById(R.id.epg_event_tmdb);
+
+ if (Preferences.get().isShowTmdbButton() == false) {
+ b.setVisibility(View.GONE);
+ } else {
+ b.setVisibility(View.VISIBLE);
+ b.setOnClickListener(new OnClickListener() {
+
+ public void onClick(View v) {
+ startFilmDatabaseBrowseIntent(TMDB_URL, view,
+ TMDB_URL_ENCODING);
+ }
+ });
+ }
+
+ b = view.findViewById(R.id.epg_event_livetv);
+ if (Utils.isLive(event) == false
+ && (event instanceof Recording == false || Preferences.get()
+ .isEnableRecStream() == false)) {
+ b.setVisibility(View.GONE);
+ } else {
+ b.setVisibility(View.VISIBLE);
+ setThisAsOnClickListener(b);
+ }
+ // setThisAsOnClickListener(view, R.id.epg_event_left);
+ // setThisAsOnClickListener(view, R.id.epg_event_right);
+
+ // set button text
+ if (event instanceof Timer) {
+ // timeButton.setText(R.string.epg_event_create_timer_text);
+ } else {
+ // timeButton.setText(R.string.epg_event_modify_timer_text);
+ }
+
+ }
+
+ private void startFilmDatabaseBrowseIntent(String url, View view,
+ String encoding) {
+ final TextView title = (TextView) view
+ .findViewById(R.id.epg_detail_title);
+ url = String.format(url,
+ encode(String.valueOf(title.getText()), encoding));
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setData(Uri.parse(url));
+ i.addCategory(Intent.CATEGORY_BROWSABLE);
+ try {
+ startActivity(i);
+ } catch (ActivityNotFoundException anfe) {
+ Log.w(TAG, anfe);
+ say(anfe.getLocalizedMessage());
+ }
+ }
+
+ private void setThisAsOnClickListener(View v) {
+ if (v != null) {
+ v.setOnClickListener(this);
+ }
+ }
+
+ private void setThisAsOnClickListener(View root, int view) {
+ setThisAsOnClickListener(root.findViewById(view));
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // TODO Check here whether the config has changed for imdb
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ class Wrapper {
+ public int id;
+ public String value;
+
+ public Wrapper(int id) {
+ this.id = id;
+ this.value = getString(id);
+ }
+
+ public String toString() {
+ return value;
+ }
+ }
+
+ public Timer getTimer(Event event) {
+ if (event instanceof Timer) {
+ return (Timer) event;
+ }
+ if (event instanceof Epg) {
+ return ((Epg) event).getTimer();
+ }
+ return null;
+ }
+
+ protected VdrManagerApp getApp() {
+ final VdrManagerApp app = (VdrManagerApp) getApplication();
+ return app;
+ }
+
+ public void onClick(final View v) {
+
+ final Event cEvent = epgs.get(pager.getCurrentItem());
+
+ switch (v.getId()) {
+ case R.id.epg_event_livetv:
+ if (cEvent instanceof Recording) {
+ Utils.streamRecording(this, (Recording) cEvent);
+ } else {
+ Utils.stream(this, String.valueOf(cEvent.getChannelNumber()));
+ }
+ break;
+ case R.id.epg_event_create_timer:
+ final ArrayAdapter<Wrapper> ada = new ArrayAdapter<Wrapper>(this,
+ android.R.layout.simple_dropdown_item_1line);
+ final Timer timer = getTimer(cEvent);
+ TimerMatch tm = Utils.getTimerMatch(cEvent, timer);
+ // remove unneeded menu items
+ if (timer != null && tm == TimerMatch.Full) {
+ ada.add(new Wrapper(R.string.epg_item_menu_timer_modify));
+ ada.add(new Wrapper(R.string.epg_item_menu_timer_delete));
+ if (timer.isEnabled()) {
+ ada.add(new Wrapper(R.string.epg_item_menu_timer_disable));
+ } else {
+ ada.add(new Wrapper(R.string.epg_item_menu_timer_enable));
+ }
+ } else if (cEvent instanceof Recording) {
+ ada.add(new Wrapper(R.string.epg_item_menu_timer_delete));
+ } else {
+
+ ada.add(new Wrapper(R.string.epg_item_menu_timer_add));
+ if (Utils.isLive(cEvent) && (cEvent instanceof Timerable)
+ && ((Timerable) cEvent).getTimer() == null) {
+ ada.add(new Wrapper(R.string.epg_item_menu_timer_record));
+ }
+ }
+
+ final Timerable timerable;
+
+ if (cEvent instanceof Timerable) {
+ timerable = (Timerable) cEvent;
+ } else {
+ return;
+ }
+
+ new AlertDialog.Builder(this)
+ .setAdapter(ada, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ Wrapper w = ada.getItem(which);
+ switch (w.id) {
+ case R.string.epg_item_menu_timer_add: {
+ getApp().setCurrentTimer(
+ timerable.createTimer());
+ final Intent intent = new Intent();
+ intent.setClass(EpgDetailsActivity.this,
+ TimerDetailsActivity.class);
+ intent.putExtra(Intents.TIMER_OP,
+ Intents.ADD_TIMER);
+ startActivityForResult(
+ intent,
+ TimerDetailsActivity.REQUEST_CODE_TIMER_ADD);
+ break;
+ }
+ case R.string.epg_item_menu_timer_modify: {
+ getApp().setCurrentTimer(timer);
+ final Intent intent = new Intent();
+ intent.setClass(EpgDetailsActivity.this,
+ TimerDetailsActivity.class);
+ intent.putExtra(Intents.TIMER_OP,
+ Intents.EDIT_TIMER);
+ startActivityForResult(
+ intent,
+ TimerDetailsActivity.REQUEST_CODE_TIMER_EDIT);
+ break;
+ }
+ case R.string.epg_item_menu_timer_delete: {
+ deleteTimer(timer);
+ break;
+ }
+ case R.string.epg_item_menu_timer_enable:
+ case R.string.epg_item_menu_timer_disable: {
+ toggleTimer(timer);
+ break;
+ }
+
+ case R.string.epg_item_menu_timer_record: {
+ final Timer timer = new Timer(cEvent);
+ final CreateTimerTask task = new CreateTimerTask(
+ EpgDetailsActivity.this, timer) {
+ @Override
+ public void finished(SvdrpEvent event) {
+ modifed = true;
+ EpgCache.CACHE.remove(timer
+ .getChannelId());
+ say(R.string.recording_started);
+ }
+ };
+ task.start();
+
+ }
+ }
+ }
+ }).create()//
+ .show();//
+
+ break;
+ // case R.id.epg_event_imdb:
+
+ // break;
+
+ // case R.id.epg_event_share:
+ // shareEvent(cEvent);
+ // break;
+ }
+ }
+
+ protected void toggleTimer(final Timer timer) {
+ final ToggleTimerTask task = new ToggleTimerTask(this, timer) {
+ @Override
+ public void finished(SvdrpEvent event) {
+ if (event == SvdrpEvent.FINISHED_SUCCESS) {
+ TimerState state = timer.getTimerState();
+ TimerMatch match = timer.getTimerMatch();
+ int res = -1;
+ if (state == TimerState.Active) {
+ res = Utils.getTimerStateDrawable(match,
+ R.drawable.timer_inactive,
+ R.drawable.timer_inactive_begin,
+ R.drawable.timer_inactive_end,
+ R.drawable.timer_inactive);
+ } else if (state == TimerState.Inactive) {
+ res = Utils.getTimerStateDrawable(match,
+ R.drawable.timer_active,
+ R.drawable.timer_active_begin,
+ R.drawable.timer_active_end,
+ R.drawable.timer_active_conflict);
+
+ }
+ if (res != -1) {
+ setState(
+ (ImageView) findViewById(R.id.epg_timer_state),
+ res);
+ }
+ }
+ }
+ };
+ task.start();
+ }
+
+ private List<Event> epgs = new ArrayList<Event>();
+
+ protected void say(int res) {
+ Toast.makeText(this, res, Toast.LENGTH_SHORT).show();
+ }
+
+ protected void say(String msg) {
+ Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(
+ Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.epg_details, menu);
+
+ // mShareActionProvider = (ShareActionProvider)
+ // menu.findItem(R.id.epg_details_menu_share).getActionProvider();
+ // mShareActionProvider.setShareIntent(getDefaultShareIntent());
+
+ return true;
+ }
+
+ private void shareEvent(Event event) {
+ Utils.shareEvent(this, event);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(
+ MenuItem item) {
+
+ Event cEvent = epgs.get(pager.getCurrentItem());
+
+ if (item.getItemId() == R.id.epg_details_menu_share) {
+ shareEvent(cEvent);
+ return true;
+ }
+
+ if (item.getItemId() == R.id.epg_details_menu_add_to_cal) {
+ Utils.addCalendarEvent(this, cEvent);
+ }
+
+ if (item.getItemId() == R.id.epg_details_menu_search_repeat) {
+ Intent intent = new Intent(this, EpgSearchListActivity.class);
+ intent.setAction(Intent.ACTION_SEARCH);
+ intent.putExtra(SearchManager.QUERY, cEvent.getTitle());
+ startActivity(intent);
+ return true;
+ }
+
+ if (item.getItemId() == R.id.epg_details_menu_switch) {
+ Utils.switchTo(this, cEvent.getChannelId(), cEvent.getChannelName());
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ protected void deleteTimer(final Timer timer) {
+ final DeleteTimerTask task = new DeleteTimerTask(this, timer) {
+ @Override
+ public void finished(SvdrpEvent event) {
+ if (event == SvdrpEvent.FINISHED_SUCCESS) {
+ setState((ImageView) findViewById(R.id.epg_timer_state),
+ R.drawable.timer_none);
+ modifed = true;
+ EpgCache.CACHE.remove(timer.getChannelId());
+ }
+ }
+ };
+ task.start();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != RESULT_OK) {
+ return;
+ }
+
+ // View view = pager.getChildAt(current);
+ // ImageView state = (ImageView)
+ // view.findViewById(R.id.epg_timer_state);
+
+ if (requestCode == TimerDetailsActivity.REQUEST_CODE_TIMER_ADD) {
+ modifed = true;
+ // setState(
+ // state,
+ // Utils.isLive(getApp().getCurrentEvent()) ?
+ // R.drawable.timer_recording
+ // : R.drawable.timer_active);
+ } else if (requestCode == TimerDetailsActivity.REQUEST_CODE_TIMER_EDIT) {
+ modifed = true;
+ // ??
+ }
+ adapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (modifed) {
+ setResult(RESULT_OK);
+ finish();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ public void onPageScrollStateChanged(int state) {
+ }
+
+ public void onPageScrolled(int position, float positionOffset,
+ int positionOffsetPixels) {
+ }
+
+ public void onPageSelected(int position) {
+
+ Event cEvent = epgs.get(position);
+ String cn = cEvent.getChannelName();
+ // View view = pager.getChildAt(arg0);
+ // state = (ImageView) view.findViewById(R.id.epg_timer_state);
+ setTitle(getString(R.string.epg_of_a_channel, cn, position + 1,
+ epgs.size()));
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchListActivity.java
new file mode 100644
index 0000000..0c08670
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchListActivity.java
@@ -0,0 +1,290 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+import android.app.SearchManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.SearchRecentSuggestions;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.Epg;
+import de.bjusystems.vdrmanager.data.EpgSearchParams;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.P;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.data.db.EPGSearchSuggestionsProvider;
+import de.bjusystems.vdrmanager.utils.date.DateFormatter;
+import de.bjusystems.vdrmanager.utils.svdrp.EpgClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+
+/**
+ * This class is used for showing what's current running on all channels
+ *
+ * @author bju
+ */
+public class EpgSearchListActivity extends BaseTimerEditActivity<Epg> implements
+ OnItemClickListener {
+
+ protected static ArrayList<Epg> CACHE = new ArrayList<Epg>();
+
+ @Override
+ protected List<Epg> getCACHE() {
+ return CACHE;
+ }
+
+ private void initSearch(final Intent intent) {
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ final String query = intent.getStringExtra(SearchManager.QUERY);
+ if (TextUtils.isEmpty(query) == false) {
+ highlight = query.trim();
+ final SearchRecentSuggestions suggestions = new SearchRecentSuggestions(
+ this, EPGSearchSuggestionsProvider.AUTHORITY,
+ EPGSearchSuggestionsProvider.MODE);
+ suggestions.saveRecentQuery(query, null);
+ }
+ }
+ }
+
+ @Override
+ protected void onNewIntent(final Intent intent) {
+ initSearch(intent);
+ startSearch();
+ }
+
+ private void startSearch() {
+ startEpgQuery();
+ }
+
+ @Override
+ protected String getViewID() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ Preferences.setLocale(this);
+ // Preferences.init(this);
+
+ super.onCreate(savedInstanceState);
+
+ sortBy = Preferences.get(this, getViewID() + "_" + P.EPG_LAST_SORT,
+ MENU_GROUP_CHANNEL);
+
+ final Intent intent = getIntent();
+ initSearch(intent);
+ adapter = new TimeEventAdapter(this);
+
+ // Create adapter for EPG list
+ adapter.setHideDescription(false);
+ listView = (ListView) findViewById(R.id.whatson_list);
+ listView.setAdapter(adapter);
+ listView.setTextFilterEnabled(true);
+ registerForContextMenu(listView);
+ // register EPG item click
+ listView.setOnItemClickListener(this);
+ startSearch();
+ }
+
+ public void onNothingSelected(final AdapterView<?> arg0) {
+ // startTimeEpgQuery(((EpgTimeSpinnerValue)timeSpinner.getAdapter().getItem(0)).getValue());
+ }
+
+ //
+
+ private void startEpgQuery() {
+
+ if (checkInternetConnection() == false) {
+ return;
+ }
+
+ final EpgSearchParams sp = new EpgSearchParams();
+ sp.setTitle(highlight);
+ setTitle(getWindowTitle());
+ final EpgClient epgClient = new EpgClient(sp,
+ getCertificateProblemDialog());
+ // remove old listeners
+ // epgClient.clearSvdrpListener();
+
+ // create background task
+ final SvdrpAsyncTask<Epg, SvdrpClient<Epg>> task = new SvdrpAsyncTask<Epg, SvdrpClient<Epg>>(
+ epgClient);
+
+ // create progress
+ addListener(task);
+
+ // start task
+ task.run();
+ }
+
+ protected void sort() {
+ /* */
+ switch (sortBy) {
+
+ case MENU_GROUP_DEFAULT: {
+ sortItemsByTime(CACHE, false);
+ break;
+ }
+ case MENU_GROUP_ALPHABET: {
+ Collections.sort(CACHE, new TitleComparator());
+ break;
+ }
+ case MENU_GROUP_CHANNEL: {
+ sortItemsByChannel(CACHE);
+ }
+ }
+ }
+
+ @Override
+ protected int getBaseMenu() {
+ return R.menu.refresh_menu;
+ }
+
+ @Override
+ protected synchronized void fillAdapter() {
+
+ adapter.highlight = this.highlight;
+
+ adapter.clear();
+
+ if (CACHE.isEmpty()) {
+ return;
+ }
+
+ sort();
+
+ final Calendar cal = Calendar.getInstance();
+ int day = -1;
+ for (final Event e : CACHE) {
+ cal.setTime(e.getStart());
+ final int eday = cal.get(Calendar.DAY_OF_YEAR);
+ if (eday != day) {
+ day = eday;
+ adapter.add(new EventListItem(new DateFormatter(cal)
+ .getDailyHeader()));
+ }
+ adapter.add(new EventListItem(e));
+ }
+ adapter.notifyDataSetChanged();
+ }
+
+ @Override
+ protected int getAvailableSortByEntries() {
+ return R.array.epg_sort_by_time_alpha_channel;
+ }
+
+ /*
+ * (non-Javadoc) TODO this method also should be used in startEpgQuery on
+ * cache hit
+ *
+ * @see de.bjusystems.vdrmanager.gui.BaseEpgListActivity#finishedSuccess()
+ */
+ @Override
+ protected boolean finishedSuccessImpl(final List<Epg> results) {
+
+ clearCache();
+ for (final Epg e : results) {
+ CACHE.add(e);
+ }
+ pushResultCountToTitle();
+ fillAdapter();
+ listView.setSelectionAfterHeaderView();
+ return adapter.getCount() > 0;
+ }
+
+// @Override
+// protected int prepareDetailsViewData(final EventListItem item, int position) {
+// final VdrManagerApp app = (VdrManagerApp) getApplication();
+// app.setCurrentEvent(item.getEvent());
+// app.setCurrentEpgList(CACHE);
+// for (int i = 0; i < position; ++i) {
+// if (CACHE.get(i) == item.getEvent()) {
+// return i;
+// }
+// }
+//
+// return 0;
+// }
+
+ @Override
+ protected int getMainLayout() {
+ return R.layout.search_epg_list;
+ }
+
+ @Override
+ protected void refresh() {
+ startEpgQuery();
+ }
+
+ @Override
+ protected void retry() {
+ startEpgQuery();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ // MenuItem item;
+ // item = menu.add(MENU_GROUP_NEW_TIMER, MENU_NEW_TIMER, 0,
+ // R.string.new_timer);
+ // item.setIcon(android.R.drawable.ic_menu_add);;
+ // /item.setAlphabeticShortcut('r');
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.epg_search_menu, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.epg_search) {
+ startSearchManager();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected String getWindowTitle() {
+ if (TextUtils.isEmpty(highlight)) {
+ return getString(R.string.epg_by_search);
+ }
+
+ return getString(R.string.epg_by_search_param, highlight);
+ }
+
+ // @Override
+ // public boolean onSearchRequested() {
+ // startSearchManager();
+ // return true;
+ // }
+
+ @Override
+ protected int getListNavigationIndex() {
+ return -1;
+ }
+
+ @Override
+ protected boolean hasListNavigation() {
+ return false;
+ }
+
+ @Override
+ protected void timerModified(final Timer timer) {
+ clearCache();
+ super.timerModified(timer);
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchTimesListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchTimesListActivity.java
new file mode 100644
index 0000000..9be94cb
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EpgSearchTimesListActivity.java
@@ -0,0 +1,147 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.List;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TimePicker;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.EpgSearchTimeValue;
+import de.bjusystems.vdrmanager.data.EpgSearchTimeValues;
+import de.bjusystems.vdrmanager.data.Preferences;
+
+/**
+ * This class is used for showing what's
+ * current running on all channels
+
+ * @author bju
+ */
+public class EpgSearchTimesListActivity extends Activity
+ implements OnClickListener{
+
+ ArrayAdapter<EpgSearchTimeValue> adapter;
+
+ List<EpgSearchTimeValue> values;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Attach view
+ setContentView(R.layout.epg_search_times_list);
+
+ setTitle(R.string.epg_search_times_window);
+
+ // Create adapter for ListView
+ adapter = new ArrayAdapter<EpgSearchTimeValue>(this, R.layout.epg_search_times_item);
+ final ListView listView = (ListView) findViewById(R.id.epg_search_times_list);
+ listView.setAdapter(adapter);
+ registerForContextMenu(listView);
+
+ // create channel list
+ updateList();
+
+ // button handler
+ final Button addButton = (Button) findViewById(R.id.epg_search_times_add);
+ addButton.setOnClickListener(this);
+ }
+
+ public void onClick(final View v) {
+ // show time selection
+ final TimePicker timePicker = new TimePicker(this);
+ timePicker.setIs24HourView(Preferences.get().isUse24hFormat());
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.set_time)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onTimeSet(timePicker.getCurrentHour(), timePicker.getCurrentMinute());
+ }
+ })
+ .setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ dialog.dismiss();
+ }
+ }).setView(timePicker).show();
+ }
+
+ public void onTimeSet(final int hourOfDay, final int minute) {
+
+ final EpgSearchTimeValue time = new EpgSearchTimeValue(values.size(), String.format("%02d:%02d", hourOfDay, minute));
+ values.add(time);
+ adapter.add(time);
+
+ save();
+
+ updateList();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onCreateContextMenu(final ContextMenu menu, final View v, final ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+
+ if (v.getId() == R.id.epg_search_times_list) {
+ final MenuInflater inflater = getMenuInflater();
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
+ menu.setHeaderTitle(values.get(info.position+2).toString());
+
+ inflater.inflate(R.menu.epg_search_time_item_menu, menu);
+ }
+ }
+
+
+
+ @Override
+ public boolean onContextItemSelected(final MenuItem item) {
+
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
+ final EpgSearchTimeValue time = values.get(info.position+2);
+
+ if (item.getItemId() == R.id.epg_search_time_delete) {
+ values.remove(time);
+ adapter.remove(time);
+ save();
+ }
+
+ return true;
+ }
+
+ private void updateList() {
+
+ // get values
+ values = new EpgSearchTimeValues(this).getValues();
+ adapter.clear();
+ for(int i = 2; i < values.size() - 1; i++) {
+ adapter.add(values.get(i));
+ }
+ }
+
+ private void save() {
+ new EpgSearchTimeValues(this).saveValues(values);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventEpgListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventEpgListActivity.java
new file mode 100644
index 0000000..3e73729
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventEpgListActivity.java
@@ -0,0 +1,489 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Epg;
+import de.bjusystems.vdrmanager.data.EpgCache;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.tasks.ChannelsTask;
+import de.bjusystems.vdrmanager.utils.date.DateFormatter;
+import de.bjusystems.vdrmanager.utils.svdrp.ChannelClient;
+import de.bjusystems.vdrmanager.utils.svdrp.EpgClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+
+/**
+ * This class is used for showing what's current running on all channels
+ *
+ * @author bju
+ */
+public class EventEpgListActivity extends BaseTimerEditActivity<Epg> implements
+ OnItemClickListener, OnItemSelectedListener {
+
+ private static final String TAG = EventEpgListActivity.class
+ .getSimpleName();
+
+ // protected static Date nextForceCache = null;
+
+ // private static Channel cachedChannel = null;
+
+ Spinner channelSpinner;
+
+ View switcher;
+
+ ArrayAdapter<Channel> channelSpinnerAdapter;
+
+ // protected static ArrayList<Epg> CACHE = new ArrayList<Epg>();
+
+ private TextView audio;
+
+ private View channelInfo;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ //
+ //
+ // create adapter for channel spinner
+ channelSpinnerAdapter = new ArrayAdapter<Channel>(this,
+ android.R.layout.simple_spinner_item);
+ channelSpinnerAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ channelSpinner = (Spinner) findViewById(R.id.epg_list_channel_spinner);
+ channelSpinner.setAdapter(channelSpinnerAdapter);
+
+ switcher = findViewById(R.id.switch_epg_view);
+ switcher.setOnClickListener(this);
+
+ // add channel values
+ // boolean useChannelNumber = Preferences.get().isShowChannelNumbers();
+
+ // show needed items
+
+ channelInfo = findViewById(R.id.channel_info);
+
+ channelInfo.setOnClickListener(this);
+
+ audio = (TextView) channelInfo.findViewById(R.id.channel_audio);
+
+ adapter = new ChannelEventAdapter(this);
+
+ // if (currentChannel != null) {
+
+ // }
+ // startChannelEpgQuery(channel);
+ // findViewById(R.id.timer_item_channel).setVisibility(View.GONE);
+ // break;
+
+ // Create adapter for EPG list
+ listView = (ListView) findViewById(R.id.whatson_list);
+ listView.setAdapter(adapter);
+ // listView.setFastScrollEnabled(true);
+ listView.setTextFilterEnabled(true);
+ registerForContextMenu(listView);
+
+ // register EPG item click
+ listView.setOnItemClickListener(this);
+
+ if (checkInternetConnection() == false) {
+ return;
+ }
+
+ startQuery();
+
+ }
+
+ @Override
+ protected int getAvailableSortByEntries() {
+ return R.array.epg_sort_by_time_alpha;
+ }
+
+ @Override
+ protected String getViewID() {
+ return EventEpgListActivity.class.getSimpleName();
+ }
+
+ private void startQuery() {
+ new ChannelsTask(this, new ChannelClient(getCertificateProblemDialog())) {
+ @Override
+ public void finished(final SvdrpEvent event) {
+ if (event == SvdrpEvent.CACHE_HIT
+ || event == SvdrpEvent.FINISHED_SUCCESS) {
+ final ArrayList<Channel> channels = ChannelClient
+ .getChannels();
+ currentChannel = getApp().getCurrentChannel();
+ boolean found = false;
+ int count = 0;
+ for (final Channel c : channels) {
+ channelSpinnerAdapter.add(c);
+ if (currentChannel != null && !found) {
+ if (currentChannel.equals(c)) {
+ found = true;
+ } else {
+ count++;
+ }
+ }
+ }
+ channelSpinner.setSelection(count);
+ channelSpinner
+ .setOnItemSelectedListener(EventEpgListActivity.this);
+ } else {
+ noConnection(event);
+ }
+ }
+ }.start();
+
+ }
+
+ void sort() {
+ if (sortBy == BaseEventListActivity.MENU_GROUP_ALPHABET) {
+ Collections.sort(getCache(), new TitleComparator());
+ } else if (sortBy == BaseEventListActivity.MENU_GROUP_DEFAULT) {
+ Collections.sort(getCache(), new TimeComparator(false));
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onItemSelected(final AdapterView<?> parent, final View view,
+ final int position, final long id) {
+ // get spinner value
+ final Channel channel = (Channel) channelSpinner.getSelectedItem();
+ currentChannel = channel;
+ setCurrent(channel);
+ // setAsCurrent(channel);
+ // update search
+ if (channel.getAudio().isEmpty() == false) {
+ audio.setText(Utils.formatAudio(this, channel.getAudio()));
+ } else {
+ audio.setText("");
+ }
+
+ startEpgQuery(false);
+ }
+
+ @Override
+ public void onNothingSelected(final AdapterView<?> arg0) {
+ // startTimeEpgQuery(((EpgTimeSpinnerValue)timeSpinner.getAdapter().getItem(0)).getValue());
+ }
+
+ @Override
+ public void clearCache() {
+ getCache().clear();
+ EpgCache.CACHE.remove(currentChannel.getId());
+ EpgCache.NEXT_REFRESH.remove(currentChannel.getId());
+ }
+
+ private boolean useCache() {
+
+ if (currentChannel == null) {
+ return false;
+ }
+
+ final ArrayList<Epg> cachedChannel = EpgCache.CACHE.get(currentChannel
+ .getId());
+
+ if (cachedChannel == null) {
+ return false;
+ }
+
+ final Date nextForceCache = EpgCache.NEXT_REFRESH.get(currentChannel
+ .getId());
+
+ if (nextForceCache == null) {
+ return false;
+ }
+ final Date now = new Date();
+ if (nextForceCache.before(now)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onClick(final View view) {
+
+ if (view == switcher) {
+ final Intent intent = new Intent();
+ intent.setClass(this, TimeEpgListActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ startActivity(intent);
+ } else if (view == channelInfo) {
+ Utils.stream(this, currentChannel);
+ } else {
+ super.onClick(view);
+ }
+ }
+
+ private void startEpgQuery(final boolean force) {
+ if (useCache() && !force) {
+ fillAdapter();
+ return;
+ }
+
+ if (checkInternetConnection() == false) {
+ return;
+ }
+
+ // clearCache();
+
+ final EpgClient epgClient = new EpgClient(currentChannel,
+ getCertificateProblemDialog());
+
+ // remove old listeners
+ // epgClient.clearSvdrpListener();
+
+ // create background task
+ final SvdrpAsyncTask<Epg, SvdrpClient<Epg>> task = new SvdrpAsyncTask<Epg, SvdrpClient<Epg>>(
+ epgClient);
+
+ // create progress
+
+ addListener(task);
+
+ // start task
+ task.run();
+ }
+
+ private static final ArrayList<Epg> EMPTY = new ArrayList<Epg>(0);
+
+ private ArrayList<Epg> getCache() {
+ if (currentChannel == null) {
+ return EMPTY;
+ }
+ final ArrayList<Epg> arrayList = EpgCache.CACHE.get(currentChannel
+ .getId());
+ if (arrayList == null) {
+ return EMPTY;
+ }
+ return arrayList;
+ }
+
+ @Override
+ protected void fillAdapter() {
+
+ adapter.clear();
+
+ final ArrayList<Epg> cache = getCache();
+
+ if (cache.isEmpty()) {
+ return;
+ }
+
+ sort();
+
+ final Calendar cal = Calendar.getInstance();
+ int day = -1;
+ for (final Event e : cache) {
+ cal.setTime(e.getStart());
+ final int eday = cal.get(Calendar.DAY_OF_YEAR);
+ if (eday != day) {
+ day = eday;
+ adapter.add(new EventListItem(new DateFormatter(cal)
+ .getDailyHeader()));
+ }
+ adapter.add(new EventListItem(e));
+ }
+
+ adapter.notifyDataSetChanged();
+
+ }
+
+ /*
+ * (non-Javadoc) TODO this method also should be used in startEpgQuery on
+ * cache hit
+ *
+ * @see de.bjusystems.vdrmanager.gui.BaseEpgListActivity#finishedSuccess()
+ */
+ @Override
+ protected boolean finishedSuccessImpl(final List<Epg> results) {
+ // adapter.clear();
+ // CACHE.clear();
+
+ clearCache();
+
+ if (results.isEmpty()) {
+ return false;
+ }
+
+ final Date now = new Date();
+
+ EpgCache.NEXT_REFRESH.put(currentChannel.getId(), FUTURE);
+
+ Date nextForceCache = FUTURE;
+
+ // Calendar cal = Calendar.getInstance();
+ // int day = -1;
+ // sortItemsByTime(results);
+ final ArrayList<Epg> cache = new ArrayList<Epg>();
+ for (final Epg e : results) {
+ cache.add(e);
+ // cal.setTime(e.getStart());
+ // int eday = cal.get(Calendar.DAY_OF_YEAR);
+ // if (eday != day) {
+ // day = eday;
+ // adapter.add(new EventListItem(new DateFormatter(cal)
+ // .getDailyHeader()));
+ // }
+ // adapter.add(new EventListItem((Epg) e));
+ if (e.getStop().before(nextForceCache) && e.getStop().after(now)) {
+ nextForceCache = e.getStop();
+ }
+ }
+
+ EpgCache.NEXT_REFRESH.put(currentChannel.getId(), nextForceCache);
+ EpgCache.CACHE.put(currentChannel.getId(), cache);
+
+ fillAdapter();
+ listView.setSelectionAfterHeaderView();
+ return results.isEmpty() == false;
+
+ // ///////////////
+
+ // // get spinner value
+ // final EpgSearchTimeValue selection = (EpgSearchTimeValue) timeSpinner
+ // .getSelectedItem();
+ // nextForceCache = FUTURE;
+ // cachedTime = selection.getValue();
+ // Date now = new Date();
+ //
+ // //adapter.add(new EventListItem(new DateFormatter(results.get(0)
+ // // .getStart()).getDailyHeader()));
+ //
+ // for (Event e : results) {
+ // CACHE.add(e);
+ // if (e.getStop().before(nextForceCache) && e.getStop().after(now)) {
+ // nextForceCache = e.getStop();
+ // }
+ // }
+ //
+
+ }
+
+// @Override
+// protected int prepareDetailsViewData(final EventListItem item, int position) {
+// final VdrManagerApp app = (VdrManagerApp) getApplication();
+// app.setCurrentEvent(item.getEvent());
+// ArrayList<Epg> cache = getCache();
+// app.setCurrentEpgList(cache);
+// for (int i = 0; i < position; ++i) {
+// if (cache.get(i) == item.getEvent()) {
+// return i;
+// }
+// }
+//
+// return 0;
+// }
+
+ @Override
+ protected int getMainLayout() {
+ return R.layout.event_epg_list;
+ }
+
+ @Override
+ protected void refresh() {
+ startEpgQuery(true);
+ }
+
+ @Override
+ protected void retry() {
+ refresh();
+ }
+
+ @Override
+ protected String getWindowTitle() {
+ return getString(R.string.epg_by_channel);
+ }
+
+ private void nextEvent() {
+ final int pos = channelSpinner.getSelectedItemPosition();
+ if (pos + 1 >= channelSpinnerAdapter.getCount()) {
+ Toast.makeText(this, R.string.navigae_at_the_end,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ channelSpinner.setSelection(pos + 1, true);
+ }
+
+ private void prevEvent() {
+ final int pos = channelSpinner.getSelectedItemPosition();
+ if (pos <= 0) {
+ say(R.string.navigae_at_the_start);
+ return;
+ }
+ channelSpinner.setSelection(pos - 1, true);
+ }
+
+ @Override
+ public void onSwipe(final int direction) {
+ switch (direction) {
+ case SimpleGestureFilter.SWIPE_RIGHT:
+ prevEvent();
+ break;
+ case SimpleGestureFilter.SWIPE_LEFT:
+ nextEvent();
+ break;
+ }
+ }
+
+ @Override
+ protected int getListNavigationIndex() {
+ return LIST_NAVIGATION_EPG_BY_CHANNEL;
+ }
+
+ @Override
+ protected List<Epg> getCACHE() {
+ return getCache();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.epg_event_list_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.epg_list_stream) {
+ Utils.stream(this, currentChannel);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventListItemHolder.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventListItemHolder.java
new file mode 100644
index 0000000..eaf70bb
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/EventListItemHolder.java
@@ -0,0 +1,19 @@
+package de.bjusystems.vdrmanager.gui;
+
+
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+class EventListItemHolder {
+ public ImageView state;
+ public ImageView other;
+ public TextView title;
+ public TextView date;
+ public TextView time;
+ public TextView channel;
+ public TextView shortText;
+ public ProgressBar progress;
+ public TextView duration;
+ public TextView description;
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/PreferencesActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/PreferencesActivity.java
new file mode 100644
index 0000000..e8ae690
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/PreferencesActivity.java
@@ -0,0 +1,144 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceManager;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.backup.BackupSettingsActivity;
+import de.bjusystems.vdrmanager.data.Preferences;
+
+public class PreferencesActivity extends BasePreferencesActivity implements
+ OnSharedPreferenceChangeListener, OnPreferenceChangeListener,
+ OnPreferenceClickListener {
+//
+// Preference somePreference = findPreference(SOME_PREFERENCE_KEY);
+// PreferenceScreen preferenceScreen = getPreferenceScreen();
+// preferenceScreen.removePreference(somePreference);
+//
+// you can later call:
+//
+// preferenceScreen.addPreference(somePreference);
+ private static final String TAG = "PreferencesActivity";
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ this.getPreferenceManager().setSharedPreferencesName(
+ Preferences.getPreferenceFile(this));
+ this.addPreferencesFromResource(R.xml.preferences);
+
+
+ Preference backupPreference = findPreference(getString(R.string.settings_backup_key));
+ backupPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(PreferencesActivity.this, BackupSettingsActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ return true;
+ }
+ });
+
+ updateChildPreferences();
+ }
+
+ // /** Return a properly configured SharedPreferences instance */
+ public static SharedPreferences getSharedPreferences(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // Unregister the listener whenever a key changes
+ Preferences.getSharedPreferences(this)
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // Set up a listener whenever a key changes
+ Preferences.getSharedPreferences(this)
+ .registerOnSharedPreferenceChangeListener(this);
+
+ }
+
+
+
+ private void updateChildPreferences() {
+ SharedPreferences sp = Preferences.getSharedPreferences(this);
+
+ for (String key : sp.getAll().keySet()) {
+ Preference p = findPreference(key);
+ updateSummary(p);
+ }
+
+
+ }
+
+ @Override
+ public void onBackPressed() {
+ //Preferences.getSharedPreferences(this)
+ //.registerOnSharedPreferenceChangeListener(this);
+
+ // finish this activity
+ //Preferences.init(this);
+ // restart main activity because
+ // the buttons needs refreshing
+ Intent intent = new Intent(this, VdrManagerActivity.class);
+ int flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP;
+ intent.setFlags(flags);
+ startActivity(intent);
+ finish();
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences arg0, String key) {
+ updateChildPreferences();
+ Preference p = findPreference(key);
+ updateSummary(p);
+ //Preferences.reset();
+ Preferences.init(this);
+ //Preferences.setLocale(getBaseContext());
+ }
+
+ public boolean onPreferenceChange(Preference arg0, Object arg1) {
+ return false;
+ }
+
+ public boolean onPreferenceClick(Preference arg0) {
+ // if (arg0.getKey().equals(getString(R.string.wakeup_wol_mac_key))) {
+ // // if(arg0.)
+ // String host = Preferences.getPreferences().getSvdrpHost();
+ //
+ // if(host == null || host.length() == 0){
+ // return true;
+ // }
+ //
+ // try {
+ // Socket echoSocket = new Socket(host, 7);
+ // InetAddress ia = echoSocket.getInetAddress();
+ //
+ // } catch (Exception ex){
+ // Log.w(TAG,ex);
+ // }
+ //
+ // String mac = WakeOnLan.getMacFromArpCache(host);
+ // System.err.println("mac");
+ // }
+
+ return true;
+ }
+
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingAdapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingAdapter.java
new file mode 100644
index 0000000..b664276
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingAdapter.java
@@ -0,0 +1,137 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.Recording;
+import de.bjusystems.vdrmanager.data.RecordingListItem;
+
+class RecordingAdapter extends BaseEventAdapter<EventListItem> {
+
+ protected final static int TYPE_FOLDER = 2;
+
+ public RecordingAdapter(final Context context) {
+ super(context, R.layout.epg_event_item);
+ hideChannelName = false;
+ }
+
+ @Override
+ protected EventFormatter getEventFormatter(Event event) {
+ return new EventFormatter(event, true);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 3;
+ }
+
+ @Override
+ protected boolean isHeader(EventListItem item) {
+ if (item instanceof RecordingListItem == false) {
+ return item.isHeader();
+ }
+
+ if (((RecordingListItem) item).isFolder()) {
+ return false;
+ }
+
+ return item.isHeader();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+
+ // get item
+ final RecordingListItem item = (RecordingListItem) getItem(position);
+
+ if (item.isHeader()) {
+ return TYPE_HEADER;
+ } else if (item.isFolder()) {
+ return TYPE_FOLDER;
+ }
+ return TYPE_ITEM;
+ }
+
+ class EventListItemFolderHolder {
+ public TextView folder;
+ public TextView count;
+ }
+
+ protected EventListItemFolderHolder getFolderViewHolder(EventListItem item,
+ View view) {
+ EventListItemFolderHolder itemHolder = new EventListItemFolderHolder();
+ itemHolder.folder = (TextView) view.findViewById(R.id.header_item);
+ itemHolder.count = (TextView) view.findViewById(R.id.count);
+ return itemHolder;
+ }
+
+ @Override
+ public View getView(final int position, View convertView,
+ final ViewGroup parent) {
+
+ // get item
+ final RecordingListItem item = (RecordingListItem) getItem(position);
+
+ if (item.isFolder() == false) {
+ return super.getView(position, convertView, parent);
+ }
+
+ EventListItemFolderHolder holder = null;
+ if (convertView == null
+ || (convertView != null && convertView.getTag() instanceof EventListItemFolderHolder) == false) {
+ convertView = inflater.inflate(R.layout.folder_item, null);
+ holder = getFolderViewHolder(item, convertView);
+ convertView.setTag(holder);
+ } else {
+ holder = (EventListItemFolderHolder) convertView.getTag();
+ }
+
+ holder.folder
+ .setText(Utils.highlight(item.folder.getName(), highlight));
+ holder.count.setText(String.valueOf(item.folder.size()));
+ return convertView;
+ }
+
+ @Override
+ public RecordingListItem getItem(int position) {
+ return (RecordingListItem) super.getItem(position);
+ }
+
+ //
+ // protected void addSuper(RecordingListItem item) {
+ // super.addSuper(item);
+ // }
+ //
+ // protected void clearSuper() {
+ // super.clear();
+ // }
+ protected void handleState(EventListItemHolder itemHolder,
+ EventListItem item) {
+
+ Recording r = (Recording) item.getEvent();
+ if (r.getTimerStopTime() != null) {
+ itemHolder.state.setImageResource(R.drawable.timer_recording);
+ } else {
+ itemHolder.state.setImageResource(R.drawable.timer_none);
+ itemHolder.other.setVisibility(View.GONE);
+ if (r.isNeww() == true) {
+ itemHolder.state.setImageResource(R.drawable.newrecording);
+ if (r.isCut()) {
+ itemHolder.other.setVisibility(View.VISIBLE);
+ itemHolder.other.setImageResource(R.drawable.schere);
+ } else {
+ itemHolder.other.setVisibility(View.GONE);
+ }
+ } else if (r.isCut()) {
+ itemHolder.state.setImageResource(R.drawable.schere);
+ }
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingDir.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingDir.java
new file mode 100644
index 0000000..ce7b995
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingDir.java
@@ -0,0 +1,71 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+import de.bjusystems.vdrmanager.data.Recording;
+
+public class RecordingDir {
+
+ public RecordingDir() {
+
+ }
+
+ public String name;
+
+ public RecordingDir parent;
+
+ public TreeMap<String, RecordingDir> dirs = new TreeMap<String, RecordingDir>();
+
+ public List<Recording> recordings = new ArrayList<Recording>();
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[").append(name).append(" => {");
+ String sep = "";
+ for (RecordingDir e : dirs.values()) {
+ sb.append(sep).append(e.toString());
+ sep = ", ";
+ }
+
+ sb.append("}\n-").append("\n");
+
+ sb.append("{").append("");
+
+ sep = "";
+ for (Recording r : recordings) {
+ sb.append(sep).append(r.toString());
+ }
+ sb.append("}]");
+ return sb.toString();
+ }
+
+ public void clear() {
+ for (RecordingDir dir : dirs.values()) {
+ dir.clear();
+ }
+ recordings.clear();
+ }
+
+ public int size() {
+ int sum = 0;
+ for (RecordingDir d : dirs.values()) {
+ sum += d.size();
+ }
+ sum += recordings.size();
+ return sum;
+ }
+
+ public String getPath(){
+ if(parent == null){
+ return "/";
+ }
+ return parent.getPath() + name + "/";
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingListActivity.java
new file mode 100644
index 0000000..50f0971
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/RecordingListActivity.java
@@ -0,0 +1,568 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Stack;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Recording;
+import de.bjusystems.vdrmanager.data.RecordingListItem;
+import de.bjusystems.vdrmanager.tasks.DeleteRecordingTask;
+import de.bjusystems.vdrmanager.utils.date.DateFormatter;
+import de.bjusystems.vdrmanager.utils.svdrp.RecordingClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpStartListener;
+
+/**
+ * This class is used for showing what's current running on all channels
+ *
+ * @author bju
+ */
+public class RecordingListActivity extends BaseEventListActivity<Recording>
+ implements OnItemLongClickListener, SvdrpStartListener {
+
+ // RecordingClient recordingClient;
+
+ // public static final int MENU_GROUP_CHANNEL = 2;
+
+ public static final int ASC = 0;
+
+ public static final int DESC = 1;
+
+ // protected static ArrayList<Recording> CACHE = new ArrayList<Recording>();
+
+ // private static Map<String, List<Recording>> CACHE = new TreeMap<String,
+ // List<Recording>>();
+
+ // public static final Map<String, Set<String>> FOLDERS = new
+ // TreeMap<String, Set<String>>();
+
+ private RecordingDir currentFolder; // Recording.ROOT_FOLDER;
+
+ private final int ASC_DESC = ASC;
+
+ private static final List<Recording> EMPTY = new ArrayList<Recording>(0);
+
+ private final Stack<String> stack = new Stack<String>();
+
+ private TextView folderInfo;
+
+ private TextView currentCount;
+
+ private TextView driverInfo;
+
+ private View driverInfoContainer;
+
+ private ProgressBar drive_info_pb;
+
+ private int totalMB = -1;
+
+ private int freeMB = -1;
+
+ private int percent = -1;
+
+ private RecordingDir ROOT = new RecordingDir();
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ adapter = new RecordingAdapter(this);
+ currentFolder = ROOT;
+ // attach adapter to ListView
+ listView = (ListView) findViewById(R.id.recording_list);
+ folderInfo = (TextView) findViewById(R.id.folder_info);
+ driverInfoContainer = findViewById(R.id.driver_info_container);
+ driverInfo = (TextView) driverInfoContainer
+ .findViewById(R.id.drive_info);
+ drive_info_pb = (ProgressBar) driverInfoContainer
+ .findViewById(R.id.drive_info_pb);
+ currentCount = (TextView) findViewById(R.id.current_count);
+ listView.setAdapter(adapter);
+
+ // set click listener
+ listView.setOnItemLongClickListener(this);
+ // register EPG item click
+ listView.setOnItemClickListener(this);
+ // context menu wanted
+ registerForContextMenu(listView);
+ listView.setFastScrollEnabled(true);
+ listView.setTextFilterEnabled(true);
+ // start query
+ startRecordingQuery();
+ }
+
+ @Override
+ protected int getAvailableSortByEntries() {
+ return R.array.recordings_group_by;
+ };
+
+ // AlertDialog groupByDialog = null;
+
+ // @Override
+ // public boolean onOptionsItemSelected(
+ // final com.actionbarsherlock.view.MenuItem item) {
+ //
+ // switch (item.getItemId()) {
+ // case R.id.menu_groupby:
+ // // case MENU_PROVIDER:
+ // // case MENU_NAME:
+ // if (groupByDialog == null) {
+ // groupByDialog = new AlertDialog.Builder(this)
+ // .setTitle(R.string.menu_groupby)
+ // .setIcon(android.R.drawable.ic_menu_sort_alphabetically)
+ // .setSingleChoiceItems(getAvailableGroupByEntries(),
+ // groupBy, new DialogInterface.OnClickListener() {
+ // public void onClick(DialogInterface dialog,
+ // int which) {
+ // if (groupBy == which) {
+ // ASC_DESC = ASC_DESC == ASC ? DESC
+ // : ASC;
+ // } else {
+ // groupBy = which;
+ // ASC_DESC = ASC;
+ // }
+ // // fillAdapter();
+ // groupByDialog.dismiss();
+ // say("Comming soon...");
+ // }
+ // }).create();
+ // }
+ //
+ // groupByDialog.show();
+ //
+ // return true;
+ // default:
+ // return super.onOptionsItemSelected(item);
+ // }
+ // }
+
+ @Override
+ public void onItemClick(final AdapterView<?> parent, final View view,
+ final int position, final long id) {
+
+ final RecordingListItem item = (RecordingListItem) adapter
+ .getItem(position);
+ if (item.isFolder()) {
+ currentFolder = item.folder;
+ // if (currentFolder.equals(Recording.ROOT_FOLDER)) {
+ // currentFolder = item.folder;
+ // } else {
+ // currentFolder = currentFolder + Recording.FOLDERDELIMCHAR
+ // + item.folder;
+ // }
+ // stack.push(currentFolder);
+ fillAdapter();
+ } else {
+ super.onItemClick(parent, view, position, id);
+ }
+ }
+
+ private void updateCurrentFolderInfo(int size) {
+ folderInfo.setText(currentFolder.getPath());
+ currentCount.setText(String.valueOf(size));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * de.bjusystems.vdrmanager.gui.BaseActivity#onCreateOptionsMenu(android
+ * .view.Menu)
+ */
+ @Override
+ public boolean onCreateOptionsMenu(
+ final Menu menu) {
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.recording_list_menu, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+// @Override
+// protected int prepareDetailsViewData(final EventListItem event, int position) {
+// getApp().setCurrentEvent(event.getEvent());
+// List<Recording> cachEget = CACHEget(currentFolder);
+// getApp().setCurrentEpgList(cachEget);
+//
+// for(int i = 0; i < position; ++i){
+// if(cachEget.get(i) == event.getEvent()){
+// return i;
+// }
+// }
+//
+// return 0;
+// }
+
+ @Override
+ public void onCreateContextMenu(final ContextMenu menu, final View v,
+ final ContextMenuInfo menuInfo) {
+
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ final EventListItem item = adapter.getItem(info.position);
+ if (item.isHeader()) {
+ return;
+ }
+
+ if (v.getId() == R.id.recording_list) {
+ final MenuInflater inflater = getMenuInflater();
+ // set menu title
+ final EventFormatter formatter = new EventFormatter(item);
+ menu.setHeaderTitle(formatter.getTitle());
+
+ inflater.inflate(R.menu.recording_list_item_menu, menu);
+ if (Preferences.get().isEnableRecStream() == false) {
+ menu.removeItem(R.id.recording_item_menu_stream);
+ }
+
+ }
+
+ super.onCreateContextMenu(menu, v, menuInfo);
+ //
+ // http://projects.vdr-developer.org/issues/863
+ // if (Utils.isLive(item)) {
+ menu.removeItem(R.id.epg_item_menu_live_tv);
+ // }
+ }
+
+ @Override
+ public boolean onContextItemSelected(final MenuItem item) {
+
+ final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
+ .getMenuInfo();
+ final EventListItem event = adapter.getItem(info.position);
+ final Recording rec = (Recording) event.getEvent();
+ switch (item.getItemId()) {
+ case R.id.recording_item_menu_delete: {
+ final DeleteRecordingTask drt = new DeleteRecordingTask(this, rec) {
+ @Override
+ public void finished(final SvdrpEvent event) {
+ if (event == SvdrpEvent.FINISHED_SUCCESS) {
+ backupViewSelection();
+ refresh();
+ }
+ }
+ };
+ drt.start();
+ break;
+ }
+ case R.id.recording_item_menu_stream: {
+ Utils.streamRecording(this, rec);
+ // say("Sorry, not yet. It would be. File -> " + rec.getFileName());
+ break;
+ }
+
+ default:
+ return super.onContextItemSelected(item);
+ }
+ return true;
+ }
+
+ private void startRecordingQuery() {
+
+ if (checkInternetConnection() == false) {
+ return;
+ }
+
+ // get timer client
+ final RecordingClient recordingClient = new RecordingClient(
+ getCertificateProblemDialog());
+
+ recordingClient.addStartListener(this);
+
+ // create backgound task
+ final SvdrpAsyncTask<Recording, SvdrpClient<Recording>> task = new SvdrpAsyncTask<Recording, SvdrpClient<Recording>>(
+ recordingClient);
+
+ // create progress dialog
+
+ addListener(task);
+
+ // start task
+ task.run();
+ }
+
+ @Override
+ protected void retry() {
+ startRecordingQuery();
+ }
+
+ @Override
+ protected void refresh() {
+ startRecordingQuery();
+ }
+
+ @Override
+ protected int getMainLayout() {
+ return R.layout.recording_list;
+ }
+
+ @Override
+ protected String getWindowTitle() {
+ return getString(R.string.action_menu_recordings);
+ }
+
+ protected void sort() {
+ /* */
+ switch (sortBy) {
+ case MENU_GROUP_DEFAULT: {
+ sortItemsByTime(CACHEget(currentFolder), true);
+ break;
+ }
+ case MENU_GROUP_ALPHABET: {
+ Collections.sort(CACHEget(currentFolder), new TitleComparator());
+ break;
+ }
+ // case MENU_GROUP_CHANNEL: {
+ // sortItemsByChannel(results);
+ // }
+ }
+ }
+
+ private List<Recording> CACHEget(RecordingDir currentFolder) {
+ return currentFolder.recordings;
+ }
+
+
+ @Override
+ protected void fillAdapter() {
+
+ adapter.clear();
+
+ sort();
+
+ final Calendar cal = Calendar.getInstance();
+ int day = -1;
+
+ final Collection<RecordingDir> folders = currentFolder.dirs.values();
+ for (final RecordingDir d : folders) {
+ final RecordingListItem recordingListItem = new RecordingListItem(
+ d.name);
+ recordingListItem.folder = d;
+ // final String sf = currentFolder.length() > 0 ? currentFolder
+ // + Recording.FOLDERDELIMCHAR + f : f;
+ // final List<Recording> list2 = CACHE.get(sf);
+ // if (list2 != null) {
+ // recordingListItem.getCcount = getCountInFolder(currentFolder, f);
+ // }
+ adapter.add(recordingListItem);
+ }
+
+ updateCurrentFolderInfo(currentFolder.size());
+
+ for (final Event rec : CACHEget(currentFolder)) {
+ cal.setTime(rec.getStart());
+ final int eday = cal.get(Calendar.DAY_OF_YEAR);
+ if (eday != day) {
+ day = eday;
+ adapter.add(new RecordingListItem(new DateFormatter(cal)
+ .getDailyHeader()));
+ }
+ adapter.add(new RecordingListItem((Recording) rec));
+ }
+ adapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (currentFolder.parent == null) {
+ super.onBackPressed();
+ return;
+ }
+ currentFolder = currentFolder.parent;
+ fillAdapter();
+ }
+
+// private List<Recording> readDummy() {
+//
+// List<Recording> list = new ArrayList<Recording>();
+// try {
+// LineNumberReader r = new LineNumberReader(new FileReader(new File(
+// "/sdcard/vdrmanager.txt")));
+//
+// String line;
+//
+// while ((line = r.readLine()) != null) {
+// Recording rec = null;
+// try {
+// rec = new Recording(line);
+// } catch (Exception ex) {
+// ex.printStackTrace();
+// }
+// list.add(rec);
+// }
+//
+// } catch (Exception ex) {
+// ex.printStackTrace();
+// }
+//
+// return list;
+//
+// }
+
+ @Override
+ protected boolean finishedSuccessImpl(final List<Recording> results) {
+
+ // results.clear();
+ // results.addAll(readDummy());
+
+ clearCache();
+
+ for (final Recording r : results) {
+ final String folder = r.getFolder();
+
+ if (folder.length() == 0) {
+ ROOT.recordings.add(r);
+ } else {
+ final String[] split = folder.split(Recording.FOLDERDELIMCHAR);
+
+ RecordingDir parent = ROOT;
+ RecordingDir dir = null; // "a~b~c";
+ for (int i = 0; i < split.length; ++i) {
+ String dn = split[i];
+
+ dir = parent.dirs.get(dn);
+ if(dir == null) {
+ dir = new RecordingDir();
+ dir.parent = parent;
+ dir.name = dn;
+ parent.dirs.put(dn, dir);
+ }
+ parent = dir;
+ }
+ dir.recordings.add(r);
+
+ // StringBuilder sb = new StringBuilder();
+ // String sep = "";
+ // for(int i = 0; i < split.length - 1; ++i){
+ // sb.append(sep).append(split[i]);
+ // sep = Recording.FOLDERDELIMCHAR;
+ // }
+ // key = folder.subSequence(0,
+ // folder.length() - (value.length() + 1)).toString();
+
+ // Set<String> list = FOLDERS.get(key);
+ // if (list == null) {
+ // list = new TreeSet<String>(new Comparator<String>() {
+ // @Override
+ // public int compare(final String lhs, final String rhs) {
+ // return lhs.compareToIgnoreCase(rhs);
+ // }
+ // });
+ // FOLDERS.put(key, list);
+ // }
+ //
+ // list.add(value);
+ //
+ // if (key.equals(Recording.ROOT_FOLDER) == false) {
+ // Set<String> set = FOLDERS.get(Recording.ROOT_FOLDER);
+ // if (set == null) {
+ // set = new HashSet<String>();
+ // FOLDERS.put(key, set);
+ // }
+ // set.add(key);
+ // }
+
+ // a b
+ // a
+ // c
+ // a~b > k
+
+ }
+ // List<Recording> list = CACHE.get(folder);
+ // if (list == null) {
+ // list = new ArrayList<Recording>();
+ // CACHE.put(folder, list);
+ // }
+ // list.add(r);
+ }
+
+ pushResultCountToTitle();
+ fillAdapter();
+ return adapter.isEmpty() == false;
+ }
+
+ @Override
+ public boolean onItemLongClick(final AdapterView<?> arg0, final View arg1,
+ final int arg2, final long arg3) {
+
+ return false;
+ }
+
+ @Override
+ protected int getListNavigationIndex() {
+ return LIST_NAVIGATION_RECORDINGS;
+ }
+
+ @Override
+ public void clearCache() {
+ ROOT.clear();
+ // CACHE.clear();
+ // FOLDERS.clear();
+ }
+
+ @Override
+ protected List<Recording> getCACHE() {
+
+ return currentFolder.recordings;
+ }
+
+ @Override
+ public void start(final String meta) {
+ if (meta == null) {
+ return;
+ }
+ try {
+
+ runOnUiThread(new Runnable() {
+ public void run() {
+
+ // stuff that updates ui
+
+ String[] split = meta.split(":");
+ if (split.length != 3) {
+ Log.w(TAG, "Recoring list meta is wrong");
+ return;
+ }
+
+ totalMB = Integer.valueOf(split[0]);
+ freeMB = Integer.valueOf(split[1]);
+ percent = Integer.valueOf(split[2]);
+ driverInfoContainer.setVisibility(View.VISIBLE);
+ driverInfo.setText(getString(R.string.drive_info,
+ (freeMB) / 1024, totalMB / 1024, 100 - percent));
+ drive_info_pb.setProgress(percent);
+ }
+ });
+ } catch (Exception ex) {
+ Log.w(TAG, ex.getMessage(), ex);
+ }
+
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SimpleGestureFilter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SimpleGestureFilter.java
new file mode 100644
index 0000000..2afe23a
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SimpleGestureFilter.java
@@ -0,0 +1,170 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.app.Activity;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+
+public class SimpleGestureFilter extends SimpleOnGestureListener{
+
+ public final static int SWIPE_UP = 1;
+ public final static int SWIPE_DOWN = 2;
+ public final static int SWIPE_LEFT = 3;
+ public final static int SWIPE_RIGHT = 4;
+
+ public final static int MODE_TRANSPARENT = 0;
+ public final static int MODE_SOLID = 1;
+ public final static int MODE_DYNAMIC = 2;
+
+ private final static int ACTION_FAKE = -13; //just an unlikely number
+ private int swipe_Min_Distance = 100;
+ private int swipe_Max_Distance = 350;
+ private int swipe_Min_Velocity = 100;
+
+ private int mode = MODE_DYNAMIC;
+ private boolean running = true;
+ private boolean tapIndicator = false;
+
+ private Activity context;
+ private GestureDetector detector;
+ private SimpleGestureListener listener;
+
+
+ public SimpleGestureFilter(Activity context, SimpleGestureListener sgl) {
+
+ this.context = context;
+ this.detector = new GestureDetector(context, this);
+ this.listener = sgl;
+ }
+
+ public void onTouchEvent(MotionEvent event){
+
+ if(!this.running)
+ return;
+
+ boolean result = this.detector.onTouchEvent(event);
+
+ if(this.mode == MODE_SOLID)
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ else if (this.mode == MODE_DYNAMIC) {
+
+ if(event.getAction() == ACTION_FAKE)
+ event.setAction(MotionEvent.ACTION_UP);
+ else if (result)
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ else if(this.tapIndicator){
+ event.setAction(MotionEvent.ACTION_DOWN);
+ this.tapIndicator = false;
+ }
+
+ }
+ //else just do nothing, it's Transparent
+ }
+
+ public void setMode(int m){
+ this.mode = m;
+ }
+
+ public int getMode(){
+ return this.mode;
+ }
+
+ public void setEnabled(boolean status){
+ this.running = status;
+ }
+
+ public void setSwipeMaxDistance(int distance){
+ this.swipe_Max_Distance = distance;
+ }
+
+ public void setSwipeMinDistance(int distance){
+ this.swipe_Min_Distance = distance;
+ }
+
+ public void setSwipeMinVelocity(int distance){
+ this.swipe_Min_Velocity = distance;
+ }
+
+ public int getSwipeMaxDistance(){
+ return this.swipe_Max_Distance;
+ }
+
+ public int getSwipeMinDistance(){
+ return this.swipe_Min_Distance;
+ }
+
+ public int getSwipeMinVelocity(){
+ return this.swipe_Min_Velocity;
+ }
+
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+
+ final float xDistance = Math.abs(e1.getX() - e2.getX());
+ final float yDistance = Math.abs(e1.getY() - e2.getY());
+
+ if(xDistance > this.swipe_Max_Distance || yDistance > this.swipe_Max_Distance)
+ return false;
+
+ velocityX = Math.abs(velocityX);
+ velocityY = Math.abs(velocityY);
+ boolean result = false;
+
+ if(velocityX > this.swipe_Min_Velocity && xDistance > this.swipe_Min_Distance){
+ if(e1.getX() > e2.getX()) // right to left
+ this.listener.onSwipe(SWIPE_LEFT);
+ else
+ this.listener.onSwipe(SWIPE_RIGHT);
+
+ result = true;
+ }
+ /*
+ else if(velocityY > this.swipe_Min_Velocity && yDistance > this.swipe_Min_Distance){
+ if(e1.getY() > e2.getY()) // bottom to up
+ this.listener.onSwipe(SWIPE_UP);
+ else
+ this.listener.onSwipe(SWIPE_DOWN);
+
+ result = true;
+ }
+*/
+ return result;
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ this.tapIndicator = true;
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent arg0) {
+ this.listener.onDoubleTap();;
+ return true;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent arg0) {
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent arg0) {
+
+ if(this.mode == MODE_DYNAMIC){ // we owe an ACTION_UP, so we fake an
+ arg0.setAction(ACTION_FAKE); //action which will be converted to an ACTION_UP later.
+ this.context.dispatchTouchEvent(arg0);
+ }
+
+ return false;
+ }
+
+
+ static interface SimpleGestureListener{
+ void onSwipe(int direction);
+ void onDoubleTap();
+ }
+
+ } \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SvdrpProgressDialog.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SvdrpProgressDialog.java
new file mode 100644
index 0000000..74cd502
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/SvdrpProgressDialog.java
@@ -0,0 +1,69 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpException;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpExceptionListener;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpListener;
+
+public class SvdrpProgressDialog<T> extends ProgressDialog implements
+ SvdrpExceptionListener, SvdrpListener, DialogInterface.OnCancelListener {
+
+ ProgressDialog progress;
+
+ SvdrpClient<? extends Object> client;
+
+ public SvdrpProgressDialog(final Context context,
+ final SvdrpClient<T> client) {
+ super(context);
+
+ this.client = client;
+ progress = new ProgressDialog(context);
+ progress.setOnCancelListener(this);
+ progress.setCancelable(true);
+ progress.setCanceledOnTouchOutside(false);
+ }
+
+ public void svdrpEvent(final SvdrpEvent sevent) {
+ switch (sevent) {
+ case ABORTED:
+ case CONNECTION_TIMEOUT:
+ case CONNECT_ERROR:
+ case ERROR:
+ case LOGIN_ERROR:
+ case FINISHED_ABNORMALY:
+ case FINISHED_SUCCESS:
+ case CACHE_HIT:
+ progress.dismiss();
+ break;
+ case DISCONNECTED:
+ break;
+ }
+ }
+
+ public void svdrpException(final SvdrpException exception) {
+
+ }
+
+ private void abort() {
+ client.abort();
+ dismiss();
+ }
+
+ public void dismiss() {
+ progress.dismiss();
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ abort();
+ }
+
+ @Override
+ public void svdrpEvent(SvdrpEvent event, Throwable t) {
+ progress.dismiss();
+ Utils.say(getContext(), t.getLocalizedMessage());
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEpgListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEpgListActivity.java
new file mode 100644
index 0000000..c9c6a57
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEpgListActivity.java
@@ -0,0 +1,428 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import android.app.TimePickerDialog;
+import android.app.TimePickerDialog.OnTimeSetListener;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TimePicker;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.Epg;
+import de.bjusystems.vdrmanager.data.EpgSearchTimeValue;
+import de.bjusystems.vdrmanager.data.EpgSearchTimeValues;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.utils.date.DateFormatter;
+import de.bjusystems.vdrmanager.utils.svdrp.EpgClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+
+/**
+ * This class is used for showing what's current running on all channels
+ *
+ * @author bju
+ */
+public class TimeEpgListActivity extends BaseTimerEditActivity<Epg> implements
+ OnItemClickListener, OnItemSelectedListener, OnTimeSetListener {
+
+ protected Spinner timeSpinner;
+
+ protected View switcher;
+
+ protected View clock;
+
+ ArrayAdapter<EpgSearchTimeValue> timeSpinnerAdapter;
+
+ protected static Date nextForceCache = null;
+
+ private static String cachedTime = null;
+
+ int selectedIndex = 0;
+
+ protected static ArrayList<Epg> CACHE = new ArrayList<Epg>();
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_whatson_loading;
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // create adapter for time spinner
+ timeSpinnerAdapter = new ArrayAdapter<EpgSearchTimeValue>(this,
+ android.R.layout.simple_spinner_item);
+ timeSpinnerAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ timeSpinner = (Spinner) findViewById(R.id.epg_list_time_spinner);
+ timeSpinner.setAdapter(timeSpinnerAdapter);
+
+ switcher = findViewById(R.id.switch_epg_view);
+ switcher.setOnClickListener(this);
+
+ clock = findViewById(R.id.epg_list_times);
+ clock.setOnClickListener(this);
+
+ // update gui
+ adapter = new TimeEventAdapter(this);
+ // searchLabel.setVisibility(View.GONE);
+ timeSpinner.setOnItemSelectedListener(this);
+
+ fillTimeSpinnerValues();
+
+ // Create adapter for EPG list
+ listView = (ListView) findViewById(R.id.whatson_list);
+ listView.setFastScrollEnabled(true);
+ listView.setTextFilterEnabled(true);
+
+ listView.setAdapter(adapter);
+ registerForContextMenu(listView);
+
+ // register EPG item click
+ listView.setOnItemClickListener(this);
+
+ }
+
+ private void fillTimeSpinnerValues() {
+ final EpgSearchTimeValues values = new EpgSearchTimeValues(this);
+ timeSpinnerAdapter.clear();
+ for (final EpgSearchTimeValue value : values.getValues()) {
+ timeSpinnerAdapter.add(value);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ }
+
+ @Override
+ public void onClick(final View view) {
+ if (view == switcher) {
+ final Intent intent = new Intent();
+ intent.setClass(this, EventEpgListActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ startActivity(intent);
+ finish();
+ } else if (view == clock) {
+ final Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.setClass(this, EpgSearchTimesListActivity.class);
+ startActivity(intent);
+ } else {
+ super.onClick(view);
+ }
+ }
+
+ @Override
+ public void onTimeSet(final TimePicker view, final int hourOfDay,
+ final int minute) {
+ final String tm = String.format("%02d:%02d", hourOfDay, minute);
+
+ // timeSpinnerAdapter.add(time);
+ final EpgSearchTimeValues values = new EpgSearchTimeValues(this);
+ final List<EpgSearchTimeValue> vs = values.getValues();
+ final EpgSearchTimeValue time = new EpgSearchTimeValue(3, tm);
+ vs.add(vs.size() - 1, time);
+ timeSpinnerAdapter.clear();
+ int select = -1;
+ int counter = 0;
+ for (final EpgSearchTimeValue value : vs) {
+ timeSpinnerAdapter.add(value);
+ if (select == -1 && value.getText().equals(tm)) {
+ select = counter;
+ }
+ counter++;
+ }
+ timeSpinner.setSelection(select);
+ setTitle(resolveWindowTitle());
+ // update search
+ startEpgQuery(time.getValue(), false);
+ }
+
+ private String resolveWindowTitle() {
+ if (timeSpinner == null) {
+ return getString(R.string.epg_by_time);
+ }
+ final EpgSearchTimeValue v = (EpgSearchTimeValue) timeSpinner
+ .getSelectedItem();
+ if (v == null) {
+ return getString(R.string.epg_by_time);
+ }
+ return getString(R.string.epg_by_time_args, v.getText());
+ }
+
+ @Override
+ public void onItemSelected(final AdapterView<?> parent, final View view,
+ final int position, final long id) {
+
+ // get spinner value
+ final EpgSearchTimeValue selection = (EpgSearchTimeValue) timeSpinner
+ .getSelectedItem();
+
+ if (selection.getValue().equals("adhoc")) {
+ final Calendar cal = Calendar.getInstance(TimeZone.getDefault());
+ // show time selection
+ final TimePickerDialog dialog = new TimePickerDialog(this, this,
+ cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE),
+ Preferences.get().isUse24hFormat());
+ // dialog.setOnDismissListener(new OnDismissListener() {
+
+ // @Override
+ // public void onDismiss(DialogInterface dialog) {
+
+ // }
+ // });
+
+ dialog.show();
+ } else {
+ // update search
+ setTitle(getString(R.string.epg_by_time_args, selection.getText()));
+ startEpgQuery(selection.getValue(), false);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(final AdapterView<?> arg0) {
+ // startTimeEpgQuery(((EpgTimeSpinnerValue)timeSpinner.getAdapter().getItem(0)).getValue());
+ }
+
+ @Override
+ public void clearCache() {
+ super.clearCache();
+ cachedTime = null;
+ }
+
+ private boolean useCache(final String time) {
+
+ if (cachedTime == null) {
+ return false;
+ }
+
+ if (cachedTime.equals(time) == false) {
+ return false;
+ }
+
+ if (nextForceCache == null) {
+ return false;
+ }
+ final Date now = new Date();
+ if (nextForceCache.before(now)) {
+ return false;
+ }
+ return true;
+ }
+
+ private void startEpgQuery(final String time, final boolean force) {
+
+ if (useCache(time) && !force) {
+ fillAdapter();
+ return;
+ }
+
+ if (checkInternetConnection() == false) {
+ return;
+ }
+
+ final EpgClient epgClient = new EpgClient(time,
+ getCertificateProblemDialog());
+
+ // remove old listeners
+ // epgClient.clearSvdrpListener();
+
+ // create background task
+ final SvdrpAsyncTask<Epg, SvdrpClient<Epg>> task = new SvdrpAsyncTask<Epg, SvdrpClient<Epg>>(
+ epgClient);
+
+ // create progress
+ addListener(task);
+
+ // start task
+ task.run();
+ }
+
+ @Override
+ protected synchronized void fillAdapter() {
+
+ adapter.clear();
+
+ if (CACHE.isEmpty()) {
+ return;
+ }
+
+ sort();
+ listView.setFastScrollEnabled(false);
+ adapter.add(new EventListItem(
+ new DateFormatter(CACHE.get(0).getStart()).getDailyHeader()));
+
+ for (final Event e : CACHE) {
+ adapter.add(new EventListItem(e));
+ }
+ adapter.notifyDataSetChanged();
+ listView.setFastScrollEnabled(true);
+ }
+
+ void sort() {
+ if (sortBy == BaseEventListActivity.MENU_GROUP_ALPHABET) {
+ Collections.sort(CACHE, new TitleComparator());
+ } else if (sortBy == BaseEventListActivity.MENU_GROUP_DEFAULT) {
+ Collections.sort(CACHE, new ChannelComparator());
+ }
+ }
+
+ @Override
+ protected int getAvailableSortByEntries() {
+ return R.array.epg_sort_by_channels_alpha;
+ }
+
+ @Override
+ protected String getViewID() {
+ return TimeEpgListActivity.class.getSimpleName();
+ }
+
+ @Override
+ protected boolean finishedSuccessImpl(final List<Epg> results) {
+ clearCache();
+
+ if (results.isEmpty()) {
+ return false;
+ }
+
+ // get spinner value
+ final EpgSearchTimeValue selection = (EpgSearchTimeValue) timeSpinner
+ .getSelectedItem();
+ nextForceCache = FUTURE;
+ cachedTime = selection.getValue();
+ final Date now = new Date();
+
+ // adapter.add(new EventListItem(new DateFormatter(results.get(0)
+ // .getStart()).getDailyHeader()));
+
+ for (final Epg e : results) {
+ CACHE.add(e);
+ if (e.getStop().before(nextForceCache) && e.getStop().after(now)) {
+ nextForceCache = e.getStop();
+ }
+ }
+ fillAdapter();
+ pushResultCountToTitle();
+ listView.setSelectionAfterHeaderView();
+ return results.isEmpty() == false;
+
+ }
+
+// @Override
+// protected int prepareDetailsViewData(final EventListItem item, int position) {
+// final VdrManagerApp app = (VdrManagerApp) getApplication();
+// // remember event for details view and timer things
+// app.setCurrentEvent(item.getEvent());
+// ArrayList<EventListItem> current = new ArrayList<EventListItem>();
+// for (int i = 0; i < adapter.getCount(); ++i) {
+// current.add(adapter.getItem(i));
+// }
+// app.setCurrentEpgList(current);
+// for (int i = 0; i < position; ++i) {
+// if (current.get(i) == item.getEvent()) {
+// return i;
+// }
+// }
+//
+// return 0;
+// }
+
+ @Override
+ protected int getMainLayout() {
+ return R.layout.time_epg_list;
+ }
+
+ @Override
+ protected void refresh() {
+ // get spi
+ final EpgSearchTimeValue selection = (EpgSearchTimeValue) timeSpinner
+ .getSelectedItem();
+ // update search
+ startEpgQuery(selection.getValue(), true);
+ }
+
+ @Override
+ protected void retry() {
+ refresh();
+ }
+
+ @Override
+ protected String getWindowTitle() {
+ return resolveWindowTitle();
+ }
+
+ private void nextEvent() {
+ final int pos = timeSpinner.getSelectedItemPosition();
+ if (pos + 1 >= timeSpinnerAdapter.getCount()) {
+ say(R.string.navigae_at_the_end);
+ return;
+ }
+ timeSpinner.setSelection(pos + 1, true);
+ }
+
+ private void prevEvent() {
+ final int pos = timeSpinner.getSelectedItemPosition();
+ if (pos <= 0) {
+ say(R.string.navigae_at_the_start);
+ return;
+ }
+ timeSpinner.setSelection(pos - 1, true);
+ }
+
+ @Override
+ public void onSwipe(final int direction) {
+ switch (direction) {
+ case SimpleGestureFilter.SWIPE_RIGHT:
+ prevEvent();
+ break;
+ case SimpleGestureFilter.SWIPE_LEFT:
+ nextEvent();
+ break;
+ }
+ }
+
+ @Override
+ protected int getListNavigationIndex() {
+ return LIST_NAVIGATION_EPG_BY_TIME;
+ }
+
+ @Override
+ protected List<Epg> getCACHE() {
+ return CACHE;
+ }
+
+ @Override
+ protected void timerModified(final Timer timer) {
+ clearCache();
+ super.timerModified(timer);
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEventAdapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEventAdapter.java
new file mode 100644
index 0000000..53bc690
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimeEventAdapter.java
@@ -0,0 +1,19 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.content.Context;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.EventListItem;
+
+public class TimeEventAdapter extends BaseEventAdapter<EventListItem> {
+
+ public TimeEventAdapter(final Context context) {
+ super(context, R.layout.epg_event_item);
+ }
+
+ @Override
+ protected EventFormatter getEventFormatter(Event event) {
+ return new EventFormatter(event, true);
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerDetailsActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerDetailsActivity.java
new file mode 100644
index 0000000..c6ce4a6
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerDetailsActivity.java
@@ -0,0 +1,578 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.text.DateFormatSymbols;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DatePickerDialog;
+import android.app.DatePickerDialog.OnDateSetListener;
+import android.app.TimePickerDialog;
+import android.app.TimePickerDialog.OnTimeSetListener;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TimePicker;
+import android.widget.Toast;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.Intents;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.EpgCache;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.tasks.CreateTimerTask;
+import de.bjusystems.vdrmanager.tasks.ModifyTimerTask;
+import de.bjusystems.vdrmanager.utils.date.DateFormatter;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient.TimerOperation;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+
+public class TimerDetailsActivity extends Activity implements OnClickListener,
+ OnDateSetListener, OnTimeSetListener {
+
+ public static final int REQUEST_CODE_TIMER_MODIFIED = 34;
+
+ public static final int REQUEST_CODE_TIMER_EDIT = 35;
+
+ public static final int REQUEST_CODE_TIMER_ADD = 36;
+
+ private CharSequence prevStart;
+
+ private CharSequence prevEnd;
+
+ private CharSequence prevDate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = getLayoutInflater().inflate(R.layout.timer_detail, null);
+ tView = new EditTimerViewHolder();
+ tView.view = view;
+ tView.title = (TextView) view.findViewById(R.id.timer_detail_title);
+ tView.channel = (TextView) view.findViewById(R.id.timer_detail_channel);
+ tView.dateField = (Button) view.findViewById(R.id.timer_detail_day);
+ tView.startField = (Button) view.findViewById(R.id.timer_detail_start);
+ tView.endField = (Button) view.findViewById(R.id.timer_detail_end);
+ tView.saveButton = (Button) view.findViewById(R.id.timer_details_save);
+ tView.modifyButton = (Button) view
+ .findViewById(R.id.timer_details_modify);
+
+ tView.repeat = (Button) view.findViewById(R.id.timer_detail_repeat);
+
+ tView.vps = (CheckBox) view.findViewById(R.id.timer_detail_vps);
+
+ tView.priority = (EditText) view
+ .findViewById(R.id.timer_detail_priority);
+
+ tView.lifecycle = (EditText) view
+ .findViewById(R.id.timer_detail_lifetime);
+
+ view.findViewById(R.id.timer_details_cancel).setOnClickListener(this);
+ tView.dateField.setOnClickListener(this);
+ tView.startField.setOnClickListener(this);
+ tView.endField.setOnClickListener(this);
+ tView.saveButton.setOnClickListener(this);
+ tView.modifyButton.setOnClickListener(this);
+ tView.repeat.setOnClickListener(this);
+ setContentView(view);
+ timer = getApp().getCurrentTimer().copy();
+ original = getApp().getCurrentTimer().copy();
+
+ int op = getIntent().getExtras().getInt(Intents.TIMER_OP);
+ switch (op) {
+ case Intents.ADD_TIMER:
+ setTitle(R.string.timer_details_add_title);
+ add();
+ break;
+ case Intents.EDIT_TIMER:
+ setTitle(R.string.timer_details_modify_title);
+ modify();
+ break;
+
+ default:
+ finish();
+ }
+
+ if (timer.isVps() == false && timer.hasVPS() == false) {
+ findViewById(R.id.timer_block).setVisibility(View.GONE);
+ } else {
+ tView.vps
+ .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ if (isChecked == true) {
+ vpsChecked(false);
+ } else {
+ vpsUnchecked();
+ }
+ }
+ });
+ }
+ }
+
+ private void vpsChecked(boolean initial) {
+
+ if (initial == false) {
+ prevStart = tView.startField.getText();
+ prevEnd = tView.endField.getText();
+ prevDate = tView.dateField.getText();
+ }
+
+ DateFormatter formatter = new DateFormatter(original.getStart());
+ String date = formatter.getDateString();
+ tView.startField.setEnabled(false);
+ tView.startField.setText(formatter.getTimeString());
+ timer.setStart(original.getStart());
+
+ DateFormatter stopF = new DateFormatter(original.getStop());
+
+ tView.endField.setEnabled(false);
+ tView.endField.setText(stopF.getTimeString());
+ timer.setStop(original.getStop());
+
+ tView.dateField.setEnabled(false);
+ tView.dateField.setText(date);
+
+ }
+
+ private void vpsUnchecked() {
+ if (prevStart != null) {
+ tView.startField.setText(prevStart);
+ }
+ tView.startField.setEnabled(true);
+
+ if (prevEnd != null) {
+ tView.endField.setText(prevEnd);
+ }
+ tView.endField.setEnabled(true);
+
+ if (prevDate != null) {
+ tView.dateField.setText(prevDate);
+ }
+ tView.dateField.setEnabled(true);
+ }
+
+ public class EditTimerViewHolder {
+ View view;
+ TextView title;
+ TextView channel;
+ Button dateField;
+ Button startField;
+ Button endField;
+ Button saveButton;
+ Button modifyButton;
+ CheckBox vps;
+ Button repeat;
+ EditText priority;
+ EditText lifecycle;
+ }
+
+ EditTimerViewHolder tView = null;
+
+ boolean editStart;
+
+ // SetTimerClient setTimerClient;
+
+ Timer timer;
+
+ Timer original;
+
+ private void updateDates(Date start, Date stop) {
+ DateFormatter startF = new DateFormatter(start);
+ DateFormatter endF = new DateFormatter(stop);
+ tView.startField.setText(startF.getTimeString());
+ tView.endField.setText(endF.getTimeString());
+ tView.dateField.setText(startF.getDateString());
+ }
+
+ private void updateDisplay(TimerOperation op) {
+ updateDisplay();
+
+ switch (op) {
+ case CREATE:
+ tView.modifyButton.setVisibility(View.GONE);
+ tView.saveButton.setVisibility(View.VISIBLE);
+ tView.saveButton.setText(R.string.timer_details_create_title);
+ Preferences prefs = Preferences.get();
+ tView.priority.setText(String.valueOf(prefs
+ .getTimerDefaultPriority()));
+ tView.lifecycle.setText(String.valueOf(prefs
+ .getTimerDefaultLifetime()));
+
+ Date start = new Date(timer.getStart().getTime()
+ - prefs.getTimerPreMargin() * 60000);
+ timer.setStart(start);
+
+ Date end = new Date(timer.getStop().getTime()
+ + prefs.getTimerPostMargin() * 60000);
+ timer.setStop(end);
+
+ updateDates(start, end);
+ break;
+ case MODIFY:
+ tView.saveButton.setVisibility(View.GONE);
+ tView.modifyButton.setVisibility(View.VISIBLE);
+ tView.saveButton.setText(R.string.timer_details_save_title);
+ tView.priority.setText(String.valueOf(timer.getPriority()));
+ tView.lifecycle.setText(String.valueOf(timer.getLifetime()));
+ if (timer.isVps()) {
+ vpsChecked(true);
+ } else {
+ updateDates(timer.getStart(), timer.getStop());
+ }
+
+ break;
+ default:
+ throw new RuntimeException("Unknown Operation: " + op);
+ }
+
+ }
+
+ private void updateDisplay() {
+ tView.channel.setText(timer.getChannelNumber() + " "
+ + timer.getChannelName());
+ // tView.title.setText(timer.isVps() ?
+ // getString(R.string.timer_detail_title_vps, f.getTitle()) :
+ // f.getTitle());
+ EventFormatter f = new EventFormatter(timer, true);
+ tView.title.setText(f.getTitle());
+ tView.repeat.setText(getSelectedItems().toString(this, true));
+ EpgCache.CACHE.remove(timer.getChannelId());
+ EpgCache.NEXT_REFRESH.remove(timer.getChannelId());
+ tView.vps.setChecked(timer.isVps());
+
+ }
+
+ protected VdrManagerApp getApp() {
+ final VdrManagerApp app = (VdrManagerApp) getApplication();
+ return app;
+ }
+
+ public void add() {
+ updateDisplay(TimerOperation.CREATE);
+ }
+
+ public void modify() {
+ updateDisplay(TimerOperation.MODIFY);
+ }
+
+ public void onClick(final View view) {
+ switch (view.getId()) {
+ case R.id.timer_detail_day: {
+ final Calendar cal = new GregorianCalendar();
+ cal.setTime(timer.getStart());
+ final DatePickerDialog dialog = new DatePickerDialog(this, this,
+ cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
+ cal.get(Calendar.DAY_OF_MONTH));
+ dialog.show();
+ break;
+ }
+ case R.id.timer_detail_start: {
+ final Calendar cal = new GregorianCalendar();
+ cal.setTime(timer.getStart());
+ editStart = true;
+ final TimePickerDialog dialog = new TimePickerDialog(this, this,
+ cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE),
+ true);
+ dialog.show();
+ break;
+ }
+ case R.id.timer_detail_end: {
+ final Calendar cal = new GregorianCalendar();
+ cal.setTime(timer.getStop());
+ editStart = false;
+ final TimePickerDialog dialog = new TimePickerDialog(this, this,
+ cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE),
+ true);
+ dialog.show();
+ break;
+ }
+ case R.id.timer_details_cancel: {
+ // finishActivity(REQUEST_CODE_TIMER_EDIT);
+ finish();
+ break;
+ }
+ case R.id.timer_details_modify:
+ timer.setTitle(tView.title.getText().toString());
+ timer.setVps(tView.vps.isChecked());
+ timer.setPriority(getIntOr0(tView.priority));
+ timer.setLifetime(getIntOr0(tView.lifecycle));
+
+ modifyTimer(timer);
+ // say(R.string.done);
+ break;
+
+ case R.id.timer_details_save: {
+ timer.setTitle(tView.title.getText().toString());
+ timer.setVps(tView.vps.isChecked());
+ timer.setPriority(getIntOr0(tView.priority));
+ timer.setLifetime(getIntOr0(tView.lifecycle));
+
+ createTimer(timer);
+ // say(R.string.done);
+ break;
+ }
+
+ case R.id.timer_detail_repeat: {
+
+ String[] weekdays = new DateFormatSymbols().getWeekdays();
+ String[] values = new String[] { weekdays[Calendar.MONDAY],
+ weekdays[Calendar.TUESDAY], weekdays[Calendar.WEDNESDAY],
+ weekdays[Calendar.THURSDAY], weekdays[Calendar.FRIDAY],
+ weekdays[Calendar.SATURDAY], weekdays[Calendar.SUNDAY], };
+
+ final DaysOfWeek mNewDaysOfWeek = new DaysOfWeek(
+ getSelectedItems().mDays);
+
+ final AlertDialog b = new AlertDialog.Builder(this)
+ .setMultiChoiceItems(values,
+ getSelectedItems().getBooleanArray(),
+ new DialogInterface.OnMultiChoiceClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which, boolean isChecked) {
+ mNewDaysOfWeek.set(which, isChecked);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ StringBuilder sb = new StringBuilder(7);
+ sb.append(mNewDaysOfWeek.isSet(0) ? 'M'
+ : '-');
+ sb.append(mNewDaysOfWeek.isSet(1) ? 'T'
+ : '-');
+ sb.append(mNewDaysOfWeek.isSet(2) ? 'W'
+ : '-');
+ sb.append(mNewDaysOfWeek.isSet(3) ? 'T'
+ : '-');
+ sb.append(mNewDaysOfWeek.isSet(4) ? 'F'
+ : '-');
+ sb.append(mNewDaysOfWeek.isSet(5) ? 'S'
+ : '-');
+ sb.append(mNewDaysOfWeek.isSet(6) ? 'S'
+ : '-');
+ timer.setWeekdays(sb.toString());
+ tView.repeat.setText(mNewDaysOfWeek
+ .toString(
+ TimerDetailsActivity.this,
+ true));
+ }
+ }).create();
+
+ b.show();
+ }
+ }
+ }
+
+ DaysOfWeek getSelectedItems() {
+ String str = timer.getWeekdays();
+
+ DaysOfWeek dow = new DaysOfWeek(0);
+ if (str.length() != 7) {
+ return dow;
+ }
+
+ dow.set(0, str.charAt(0) == 'M');
+ dow.set(1, str.charAt(1) == 'T');
+ dow.set(2, str.charAt(2) == 'W');
+ dow.set(3, str.charAt(3) == 'T');
+ dow.set(4, str.charAt(4) == 'F');
+ dow.set(5, str.charAt(5) == 'S');
+ dow.set(6, str.charAt(6) == 'S');
+
+ return dow;
+ }
+
+ private int getIntOr0(EditText text) {
+ if (TextUtils.isEmpty(text.getText().toString())) {
+ return 0;
+ }
+ return Integer.valueOf(text.getText().toString());
+ }
+
+ protected void say(int res) {
+ Toast.makeText(this, res, Toast.LENGTH_SHORT).show();
+ }
+
+ public void onTimeSet(final TimePicker view, final int hourOfDay,
+ final int minute) {
+ if (editStart) {
+ timer.setStart(calculateTime(timer.getStart(), hourOfDay, minute,
+ null));
+ } else {
+ timer.setStop(calculateTime(timer.getStop(), hourOfDay, minute,
+ timer.getStart()));
+ }
+ updateDates(timer.getStart(), timer.getStop());
+ }
+
+ public void onDateSet(final DatePicker view, final int year,
+ final int monthOfYear, final int dayOfMonth) {
+ timer.setStart(calculateDate(timer.getStart(), year, monthOfYear,
+ dayOfMonth));
+ updateDates(timer.getStart(), timer.getStop());
+ }
+
+ private Date calculateDate(final Date oldDate, final int year,
+ final int monthOfYear, final int dayOfMonth) {
+
+ final Calendar cal = new GregorianCalendar();
+ cal.setTime(oldDate);
+ cal.set(Calendar.YEAR, year);
+ cal.set(Calendar.MONTH, monthOfYear);
+ cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+
+ return cal.getTime();
+ }
+
+ private Date calculateTime(final Date oldTime, final int hourOfDay,
+ final int minute, final Date startTime) {
+
+ // set hour and minute
+ final Calendar cal = new GregorianCalendar();
+ cal.setTime(oldTime);
+ cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ cal.set(Calendar.MINUTE, minute);
+
+ // go to the next day if end time before start time
+ if (startTime != null) {
+ if (cal.getTime().before(startTime)) {
+ cal.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ }
+
+ return cal.getTime();
+
+ }
+
+ private void createTimer(Timer timer) {
+ final CreateTimerTask task = new CreateTimerTask(this, timer) {
+ @Override
+ public void finished(SvdrpEvent event) {
+ done();
+ }
+ };
+ task.start();
+ }
+
+ public void done() {
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ private void modifyTimer(Timer timer) {
+ final ModifyTimerTask task = new ModifyTimerTask(this, timer, original) {
+ @Override
+ public void finished(SvdrpEvent event) {
+ done();
+ }
+ };
+ task.start();
+ }
+
+ /*
+ * Days of week code as a single int. 0x00: no day 0x01: Monday 0x02:
+ * Tuesday 0x04: Wednesday 0x08: Thursday 0x10: Friday 0x20: Saturday 0x40:
+ * Sunday
+ */
+ static final class DaysOfWeek {
+
+ private static int[] DAY_MAP = new int[] { Calendar.MONDAY,
+ Calendar.TUESDAY, Calendar.WEDNESDAY, Calendar.THURSDAY,
+ Calendar.FRIDAY, Calendar.SATURDAY, Calendar.SUNDAY, };
+
+ // Bitmask of all repeating days
+ private int mDays;
+
+ DaysOfWeek(int days) {
+ mDays = days;
+ }
+
+ public String toString(Context context, boolean showNever) {
+ StringBuilder ret = new StringBuilder();
+
+ // no days
+ if (mDays == 0) {
+ return showNever ? context.getText(R.string.never).toString()
+ : "";
+ }
+
+ // every day
+ if (mDays == 0x7f) {
+ return context.getText(R.string.every_day).toString();
+ }
+
+ // count selected days
+ int dayCount = 0, days = mDays;
+ while (days > 0) {
+ if ((days & 1) == 1)
+ dayCount++;
+ days >>= 1;
+ }
+
+ // short or long form?
+ DateFormatSymbols dfs = new DateFormatSymbols();
+ String[] dayList = (dayCount > 1) ? dfs.getShortWeekdays() : dfs
+ .getWeekdays();
+
+ // selected days
+ for (int i = 0; i < 7; i++) {
+ if ((mDays & (1 << i)) != 0) {
+ ret.append(dayList[DAY_MAP[i]]);
+ dayCount -= 1;
+ if (dayCount > 0)
+ ret.append(context.getText(R.string.day_concat));
+ }
+ }
+ return ret.toString();
+ }
+
+ private boolean isSet(int day) {
+ return ((mDays & (1 << day)) > 0);
+ }
+
+ public void set(int day, boolean set) {
+ if (set) {
+ mDays |= (1 << day);
+ } else {
+ mDays &= ~(1 << day);
+ }
+ }
+
+ public void set(DaysOfWeek dow) {
+ mDays = dow.mDays;
+ }
+
+ public int getCoded() {
+ return mDays;
+ }
+
+ // Returns days of week encoded in an array of booleans.
+ public boolean[] getBooleanArray() {
+ boolean[] ret = new boolean[7];
+ for (int i = 0; i < 7; i++) {
+ ret[i] = isSet(i);
+ }
+ return ret;
+ }
+
+ public boolean isRepeatSet() {
+ return mDays != 0;
+ }
+
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerEventAdapter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerEventAdapter.java
new file mode 100644
index 0000000..0c39f81
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerEventAdapter.java
@@ -0,0 +1,31 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.content.Context;
+import android.view.View;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.Timer;
+
+public class TimerEventAdapter extends TimeEventAdapter {
+
+ public TimerEventAdapter(final Context context) {
+ super(context);
+ }
+
+ public void sortItems() {
+ // sortItemsByChannel();
+ }
+
+ @Override
+ protected void handleState(EventListItemHolder itemHolder,
+ EventListItem item) {
+ super.handleState(itemHolder, item);
+ Timer r = (Timer) item.getEvent();
+ if (r.isVps()) {
+ itemHolder.other.setVisibility(View.VISIBLE);
+ itemHolder.other.setImageResource(R.drawable.timer_vps);
+ } else {
+ itemHolder.other.setVisibility(View.GONE);
+ }
+ }
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerListActivity.java
new file mode 100644
index 0000000..ae07e1c
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/TimerListActivity.java
@@ -0,0 +1,291 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.EventListItem;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.utils.date.DateFormatter;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+import de.bjusystems.vdrmanager.utils.svdrp.TimerClient;
+
+/**
+ * This class is used for showing all the existing timers
+ *
+ * @author bju
+ */
+public class TimerListActivity extends BaseTimerEditActivity<Timer> implements
+OnItemClickListener {
+
+ private static final int MENU_NEW_TIMER = 2;
+
+ private static final int MENU_GROUP_NEW_TIMER = 2;
+
+ protected static ArrayList<Timer> CACHE = new ArrayList<Timer>();
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * de.bjusystems.vdrmanager.gui.BaseEventListActivity#onCreate(android.os
+ * .Bundle)
+ */
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Attach view
+ // setContentView(getMainLayout());
+
+ // create an adapter
+ adapter = new TimerEventAdapter(this);
+
+ // attach adapter to ListView
+ listView = (ListView) findViewById(R.id.timer_list);
+ listView.setAdapter(adapter);
+ listView.setFastScrollEnabled(true);
+ listView.setTextFilterEnabled(true);
+
+ // set click listener
+ listView.setOnItemClickListener(this);
+
+ // context menu wanted
+ registerForContextMenu(listView);
+
+ // start query
+ startTimerQuery();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ private void startTimerQuery() {
+
+ if (checkInternetConnection() == false) {
+ return;
+ }
+
+ // get timer client
+ final TimerClient timerClient = new TimerClient(getCertificateProblemDialog());
+
+ // create backgound task
+ final SvdrpAsyncTask<Timer, SvdrpClient<Timer>> task = new SvdrpAsyncTask<Timer, SvdrpClient<Timer>>(
+ timerClient);
+
+ // create progress dialog
+ // progress = new SvdrpProgressDialog(this, timerClient);
+
+ // attach listener
+ // task.addListener(progress);
+ addListener(task);
+
+ // start task
+ task.run();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * de.bjusystems.vdrmanager.gui.BaseTimerEditActivity#getTimer(de.bjusystems
+ * .vdrmanager.data.EventListItem)
+ */
+ @Override
+ protected Timer getTimer(final EventListItem item) {
+ return (Timer) item.getEvent();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * de.bjusystems.vdrmanager.gui.BaseEventListActivity#prepareTimer(de.bjusystems
+ * .vdrmanager.data.EventListItem)
+ */
+// @Override
+// protected int prepareDetailsViewData(final EventListItem item, int position) {
+// final VdrManagerApp app = (VdrManagerApp) getApplication();
+// // remember event for details view and timer things
+// app.setCurrentEvent(item.getEvent());
+// app.setCurrentEpgList(CACHE);
+// for(int i = 0; i < position; ++i){
+// if(CACHE.get(i) == item.getEvent()){
+// return i;
+// }
+// }
+//
+// return 0;
+// }
+
+ protected Comparator<Timer> getTimeComparator(final boolean reverse) {
+ return new Comparator<Timer>() {
+ TimeAndChannelComparator c = new TimeAndChannelComparator();
+ @Override
+ public int compare(final Timer item1, final Timer item2) {
+ if (item1.isRecurring()) {
+ if(item2.isRecurring()){
+ return 0;
+ }
+ return 1;
+ }
+ if (item2.isRecurring()) {
+ return -1;
+ }
+ return c.compare(item1, item2);
+ }
+
+ };
+ }
+
+
+
+ @Override
+ protected boolean finishedSuccessImpl(final List<Timer> results) {
+ clearCache();
+ for(final Timer r :results){
+ CACHE.add(r);
+ }
+ pushResultCountToTitle();
+ fillAdapter();
+ return adapter.isEmpty() == false;
+
+ }
+
+
+ protected void sort() {
+ /* */
+ switch (sortBy) {
+ case MENU_GROUP_DEFAULT: {
+ Collections.sort(CACHE, getTimeComparator(false));
+ break;
+ }
+ case MENU_GROUP_ALPHABET: {
+ Collections.sort(CACHE, new TitleComparator());
+ break;
+ }
+ //case MENU_GROUP_CHANNEL: {
+ //sortItemsByChannel(results);
+ //}
+ }
+ }
+
+
+ @Override
+ protected void fillAdapter() {
+
+ adapter.clear();
+
+ if (CACHE.isEmpty()) {
+ return;
+ }
+
+ sort();
+
+ int day = -1;
+ final Calendar cal = Calendar.getInstance();
+
+ for (final Timer e : CACHE) {
+ if (e.isRecurring()) {
+ adapter.add(new EventListItem(e.getWeekdays()));
+ } else {
+ cal.setTime(e.getStart());
+ final int eday = cal.get(Calendar.DAY_OF_YEAR);
+ if (eday != day) {
+ day = eday;
+ adapter.add(new EventListItem(new DateFormatter(cal)
+ .getDailyHeader()));
+ }
+ }
+ adapter.add(new EventListItem(e));
+ }
+ adapter.notifyDataSetChanged();
+ }
+
+ // protected boolean finishedSuccessImpl() {
+ // adapter.clear();
+ // sortItemsByTime(results);
+ // int day = -1;
+ // Calendar cal = Calendar.getInstance();
+ // for (Timer e : results) {
+ // if (e.isRecurring()) {
+ // adapter.add(new EventListItem(e.getWeekdays()));
+ // } else {
+ // cal.setTime(e.getStart());
+ // int eday = cal.get(Calendar.DAY_OF_YEAR);
+ // if (eday != day) {
+ // day = eday;
+ // adapter.add(new EventListItem(new DateFormatter(cal)
+ // .getDailyHeader()));
+ // }
+ // }
+ // adapter.add(new EventListItem(e));
+ // }
+ // listView.setSelectionAfterHeaderView();
+ // return adapter.isEmpty() == false;
+ // }
+
+ @Override
+ protected boolean notifyDataSetChangedOnResume() {
+ return true;
+ }
+
+ @Override
+ protected String getWindowTitle() {
+ return getString(R.string.action_menu_timers);
+ }
+
+ @Override
+ protected int getMainLayout() {
+ return R.layout.timer_list;
+ }
+
+ @Override
+ protected void refresh() {
+ startTimerQuery();
+ }
+
+ @Override
+ protected void retry() {
+ refresh();
+ }
+
+ @Override
+ protected int getAvailableSortByEntries() {
+ return R.array.epg_sort_by_time_alpha;
+ }
+
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ // MenuItem item;
+ // item = menu.add(MENU_GROUP_NEW_TIMER, MENU_NEW_TIMER, 0,
+ // R.string.new_timer);
+ // item.setIcon(android.R.drawable.ic_menu_add);;
+ // /item.setAlphabeticShortcut('r');
+
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.timer_list_menu, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ protected int getListNavigationIndex() {
+ return LIST_NAVIGATION_TIMERS;
+ }
+
+ @Override
+ protected List<Timer> getCACHE() {
+ return CACHE;
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/Utils.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/Utils.java
new file mode 100644
index 0000000..1116736
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/Utils.java
@@ -0,0 +1,772 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.text.style.ForegroundColorSpan;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Gravity;
+import android.widget.Toast;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.StringUtils;
+import de.bjusystems.vdrmanager.app.C;
+import de.bjusystems.vdrmanager.data.AudioTrack;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Event;
+import de.bjusystems.vdrmanager.data.EventContentGroup;
+import de.bjusystems.vdrmanager.data.EventFormatter;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Recording;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.data.TimerMatch;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpException;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpListener;
+import de.bjusystems.vdrmanager.utils.svdrp.SwitchChannelClient;
+
+public class Utils {
+
+ public static final String TAG = Utils.class.getName();
+
+ public static final List EMPTY_LIST = new ArrayList(0);
+ public static final String[] EMPTY = new String[] {};
+ public static final ForegroundColorSpan HIGHLIGHT_TEXT = new ForegroundColorSpan(
+
+ Color.RED);
+
+ public static CharSequence highlight(final String where, String what) {
+ if (TextUtils.isEmpty(what)) {
+ return where;
+ }
+
+ final String str = where.toLowerCase();
+ what = what.toLowerCase();
+ final int idx = str.indexOf(what);
+ if (idx == -1) {
+ return where;
+ }
+ final SpannableString ss = new SpannableString(where);
+ ss.setSpan(HIGHLIGHT_TEXT, idx, idx + what.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return ss;
+ }
+
+ public static Pair<Boolean, CharSequence> highlight2(final String where,
+ String what) {
+ if (TextUtils.isEmpty(what)) {
+ return Pair.create(Boolean.FALSE, (CharSequence) where);
+ }
+
+ final String str = where.toLowerCase();
+ what = what.toLowerCase();
+ final int idx = str.indexOf(what);
+ if (idx == -1) {
+ return Pair.create(Boolean.FALSE, (CharSequence) where);
+ }
+ final SpannableString ss = new SpannableString(where);
+ ss.setSpan(HIGHLIGHT_TEXT, idx, idx + what.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return Pair.create(Boolean.TRUE, (CharSequence) ss);
+ }
+
+ public static int getProgress(final Date start, final Date stop) {
+ final long now = System.currentTimeMillis();
+ return getProgress(now, start.getTime(), stop.getTime());
+ }
+
+ public static int getProgress(final Event e) {
+ if (e instanceof Recording == false) {
+ return getProgress(e.getStart(), e.getStop());
+ }
+ final Recording r = ((Recording) e);
+ if (r.getTimerStopTime() == null) {
+ return getProgress(e.getStart(), e.getStop());
+ }
+ return getProgress(r.getStart(), r.getTimerStopTime());
+
+ }
+
+ /**
+ * @param now
+ * @param time
+ * @param time2
+ * @return -1, is not not between start stop,
+ */
+ private static int getProgress(final long now, final long start,
+ final long stop) {
+ if (now >= start && now <= stop) {
+ final long dura = stop - start;
+ final long prog = now - start;
+ return (int) (prog * 100 / dura);
+ }
+ return -1;
+ }
+
+ public static boolean isLive(final Event event) {
+ final long now = System.currentTimeMillis();
+ return now >= event.getStart().getTime()
+ && now <= event.getStop().getTime();
+ }
+
+ private static String trimToEmpty(final String str) {
+ if (str == null) {
+ return "";
+ }
+ if (TextUtils.isEmpty(str)) {
+ return "";
+ }
+ return str;
+ }
+
+ private static String getBaseUrl() {
+ final StringBuilder sb = new StringBuilder();
+ final Preferences p = Preferences.getPreferences();
+ String auth = trimToEmpty(p.getStreamingUsername()) + ":"
+ + trimToEmpty(p.getStreamingPassword());
+ if (auth.length() == 1) {
+ auth = "";
+ } else {
+ auth += "@";
+ }
+
+ sb.append("http://").append(auth).append(p.getHost()).append(":")
+ .append(p.getStreamPort());
+ return sb.toString();
+ }
+
+ private static String getStreamUrl(final String chn) {
+ // "http://192.168.1.119:3000/TS/"
+ final Preferences p = Preferences.getPreferences();
+ final StringBuilder sb = new StringBuilder();
+ sb.append(getBaseUrl()).append("/").append(p.getStreamFormat())
+ .append("/").append(chn);
+
+ return sb.toString();
+ }
+
+ private static String getRemuxStreamUrl(final String chn) {
+ // "http://192.168.1.119:3000/TS/"
+ final StringBuilder sb = new StringBuilder();
+ final Preferences p = Preferences.getPreferences();
+ sb.append(getBaseUrl()).append("/").append(p.getRemuxCommand())
+ .append(";").append(p.getRemuxParameter()).append("/")
+ .append(chn);
+ return sb.toString();
+ }
+
+ public static void stream(final Activity activity, final Event event) {
+ stream(activity, event.getStreamId());
+ }
+
+ public static void stream(final Activity activity, final Channel channel) {
+ stream(activity, channel.getId());
+ }
+
+ public static void stream(final Activity activity, final String idornr) {
+
+ if (Preferences.get().isEnableRemux() == false) {
+ final String url = getStreamUrl(idornr);
+ startStream(activity, url);
+ return;
+ }
+
+ final String sf = Preferences.get().getStreamFormat();
+ final String ext = activity.getString(R.string.remux_title);
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.stream_via_as)
+ //
+ .setItems(
+ new String[] {
+ activity.getString(R.string.stream_as, sf),
+ activity.getString(R.string.stream_via, ext) },// TODO
+ // add
+ // here
+ // what
+ // will
+ // be
+ // used
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog,
+ final int which) {
+ String url = null;
+ switch (which) {
+ case 0:
+ url = getStreamUrl(idornr);
+ break;
+ case 1:
+ url = getRemuxStreamUrl(idornr);
+ break;
+ }
+ startStream(activity, url);
+ }
+ }).create().show();
+ }
+
+ public static void startStream(final Activity activity, final String url) {
+ try {
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse(url.toString()), "video/*");
+ activity.startActivityForResult(intent, 1);
+ } catch (final ActivityNotFoundException anfe) {
+ Log.w(TAG, anfe);
+ Toast.makeText(activity, anfe.getLocalizedMessage(),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public static final String md5(final String s) {
+ try {
+ // Create MD5 Hash
+ final MessageDigest digest = java.security.MessageDigest
+ .getInstance("MD5");
+ digest.update(s.getBytes());
+ final byte messageDigest[] = digest.digest();
+
+ // Create Hex String
+ final StringBuffer hexString = new StringBuffer();
+ for (int i = 0; i < messageDigest.length; i++) {
+ String h = Integer.toHexString(0xFF & messageDigest[i]);
+ while (h.length() < 2) {
+ h = "0" + h;
+ }
+ hexString.append(h);
+ }
+ return hexString.toString();
+
+ } catch (final NoSuchAlgorithmException e) {
+ Log.w(TAG, e);
+ }
+ return "";
+ }
+
+ public static int getDuration(final Event event) {
+ final long millis = event.getDuration();
+ final int minuts = (int) (millis / 1000 / 60);
+ return minuts;
+ }
+
+ public static void shareEvent(final Activity activity, final Event event) {
+ final Intent share = new Intent(android.content.Intent.ACTION_SEND);
+ share.setType("text/plain");
+ StringBuilder sb = new StringBuilder();
+ sb.append(event.getTitle());
+ sb.append("\n");
+ final EventFormatter ef = new EventFormatter(event, false);
+ sb.append(ef.getDate()).append(" ").append(ef.getTime());
+ final String title = sb.toString();
+ share.putExtra(android.content.Intent.EXTRA_SUBJECT, sb.toString());
+ sb = new StringBuilder();
+ sb.append(title).append("\n\n");
+ sb.append(event.getChannelNumber() + " " + event.getChannelName());
+ sb.append("\n\n");
+ sb.append(ef.getShortText());
+ sb.append("\n\n");
+ sb.append(ef.getDescription());
+ sb.append("\n");
+ share.putExtra(android.content.Intent.EXTRA_TEXT, sb.toString());
+ activity.startActivity(Intent.createChooser(share,
+ activity.getString(R.string.share_chooser)));
+ }
+
+ public static void addCalendarEvent(final Activity activity,
+ final Event event) {
+ final Intent intent = new Intent(Intent.ACTION_EDIT);
+ intent.setType("vnd.android.cursor.item/event");
+ intent.putExtra("title", event.getTitle());
+ intent.putExtra("description", event.getShortText());
+ intent.putExtra("beginTime", event.getStart().getTime());
+ intent.putExtra("endTime", event.getStop().getTime());
+ activity.startActivity(intent);
+ }
+
+ public static String mapSpecialChars(final String src) {
+ if (src == null) {
+ return "";
+ }
+ return src.replace("|##", C.DATA_SEPARATOR).replace("||#", "\n");
+ }
+
+ public static String unMapSpecialChars(final String src) {
+ if (src == null) {
+ return "";
+ }
+ return src.replace(C.DATA_SEPARATOR, "|##").replace("\n", "||#");
+ }
+
+ public static PackageInfo getPackageInfo(final Context ctx) {
+ PackageInfo pi = null;
+ try {
+ pi = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(),
+ PackageManager.GET_ACTIVITIES);
+ } catch (final PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ return pi;
+ }
+
+ public static boolean checkInternetConnection(final Context ctx) {
+ final ConnectivityManager cm = (ConnectivityManager) ctx
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ // test for connection
+ if (cm.getActiveNetworkInfo() != null
+ && cm.getActiveNetworkInfo().isConnectedOrConnecting()) {
+ return true;
+ }
+ return false;
+ }
+
+ private static String getRecordingStream(final Activity ctx,
+ final Recording rec) {
+
+ final String m = Preferences.get().getRecStreamMethod();
+
+ final StringBuilder url = new StringBuilder();
+
+ if (StringUtils.equals(m, "vdr-live")) {
+ url.append("http://")
+ .append(Preferences.get().getHost())
+ //
+ .append(":")
+ .append(Integer.valueOf(Preferences.get().getLivePort()))
+ //
+ .append("/recstream.html?recid=recording_")
+ .append(Utils.md5(rec.getFileName()));
+ // http://192.168.1.119:8008/b0cdedeed2d36508dfd924f0876a851b
+ final String urlstring = url.toString();
+ return urlstring;
+ } else if (StringUtils.equals(m, "vdr-streamdev")) {
+ url.append("http://").append(Preferences.get().getHost())
+ //
+ .append(":")
+ .append(Integer.valueOf(Preferences.get().getStreamPort()))
+ //
+ .append("/").append(rec.getDevInode());
+ } else if (StringUtils.equals(m, "vdr-smarttvweb")) {
+
+ String type = Preferences.get().getSmarttvewebType();
+
+ url.append("http://")
+ .append(Preferences.get().getHost())
+ //
+ .append(":")
+ .append(Integer.valueOf(Preferences.get()
+ .getSmarttvewebPort()))
+ //
+ .append(Utils.encodeUrlPath(rec.getFileName()));
+ if (StringUtils.equals(type, "has") == true) {
+ url.append("/manifest-seg.mpd");
+ } else if (StringUtils.equals(type, "hls")) {
+ url.append("/manifest-seg.m3u8");
+ }
+ }
+ return url.toString();
+ }
+
+ public static void streamRecording(final Activity ctx, final Recording rec) {
+ final String urlstring = getRecordingStream(ctx, rec);
+ Log.d(TAG, "try stream: " + urlstring);
+ Utils.startStream(ctx, urlstring);
+ }
+
+ public static void switchTo(final Activity activity, final Channel channel) {
+ switchTo(activity, channel.getId(), channel.getName());
+ }
+
+ /**
+ * @param ctx
+ * @param id
+ * @param name
+ * Optional für die Anzeige
+ */
+ public static void switchTo(final Activity activity, final String id,
+ final String name) {
+
+ final SwitchChannelClient scc = new SwitchChannelClient(id,
+ new CertificateProblemDialog(activity));
+ final SvdrpAsyncTask<String, SwitchChannelClient> task = new SvdrpAsyncTask<String, SwitchChannelClient>(
+ scc);
+ task.addSvdrpListener(new SvdrpListener() {
+ @Override
+ public void svdrpEvent(final SvdrpEvent event) {
+ if (event == SvdrpEvent.FINISHED_SUCCESS) {
+ Utils.say(activity, activity.getString(
+ R.string.switching_success, (name != null ? name
+ : id)));
+ } else if (event == SvdrpEvent.CONNECT_ERROR
+ || event == SvdrpEvent.FINISHED_ABNORMALY
+ || event == SvdrpEvent.ABORTED
+ || event == SvdrpEvent.ERROR
+ || event == SvdrpEvent.CACHE_HIT) {
+ Utils.say(activity, activity.getString(
+ R.string.switching_failed, (name != null ? name
+ : id), event.name()));
+ }
+ }
+
+ public void svdrpException(final SvdrpException e) {
+ Log.w(TAG, e.getMessage(), e);
+ Utils.say(activity, e.getMessage());
+ }
+ });
+ task.run();
+ }
+
+ public static void say(final Context ctx, final String msg) {
+ final Toast t = Toast.makeText(ctx, msg, Toast.LENGTH_SHORT);
+ t.setGravity(Gravity.CENTER, 0, 0);
+ t.show();
+ }
+ public static String encodeUrlPath(String path){
+ return path.replaceAll("%", "%25");
+ }
+
+ public static void say(final Context ctx, final int msg) {
+ final Toast t = Toast.makeText(ctx, msg, Toast.LENGTH_SHORT);
+ t.setGravity(Gravity.CENTER, 0, 0);
+ t.show();
+ }
+
+ /**
+ * Formats the date and time based on user's phone date/time preferences.
+ *
+ * @param context
+ * the context
+ * @param time
+ * the time in milliseconds
+ */
+
+ public static String formatDateTime(final Context context, final long time) {
+ return android.text.format.DateFormat.getDateFormat(context).format(
+ time)
+ + " "
+ + DateUtils.formatDateTime(context, time,
+ DateUtils.FORMAT_SHOW_TIME).toString();
+ }
+
+ public static int getTimerStateDrawable(final TimerMatch match,
+ final int full, final int begin, final int end, final int conflict) {
+
+ switch (match) {
+ case Begin:
+ return begin;
+ case End:
+ return end;
+ case Conflict:
+ return conflict;
+ default:
+ return full;
+ }
+ }
+
+ public static String formatAudio(final Context context,
+ final List<AudioTrack> tracks) {
+
+ final StringBuilder sb = new StringBuilder();
+
+ String sep = "";
+ for (final AudioTrack a : tracks) {
+ sb.append(sep).append(a.display);
+ if (a.type.equals("d")) {
+ sb.append(" (")
+ .append(context.getString(R.string.audio_track_dolby))
+ .append(")");
+ }
+ sep = ", ";
+ }
+ return sb.toString();
+
+ }
+
+ public static TimerMatch getTimerMatch(Event event, Timer timer) {
+ if (timer == null) {
+ return null;
+ }
+ TimerMatch timerMatch = null;
+ Date start = event.getStart();
+ Date stop = event.getStop();
+ if (start.before(timer.getStart())) {
+ timerMatch = TimerMatch.End;
+ } else if (stop.after(timer.getStop())) {
+ timerMatch = TimerMatch.Begin;
+ } else {
+ timerMatch = TimerMatch.Full;
+ }
+ return timerMatch;
+ }
+
+ public static int contentToString(int c) {
+ ;
+ switch (c & 0xF0) {
+ case EventContentGroup.MovieDrama:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Movie__Drama;
+ case 0x01:
+ return R.string.Content$Detective__Thriller;
+ case 0x02:
+ return R.string.Content$Adventure__Western__War;
+ case 0x03:
+ return R.string.Content$Science_Fiction__Fantasy__Horror;
+ case 0x04:
+ return R.string.Content$Comedy;
+ case 0x05:
+ return R.string.Content$Soap__Melodrama__Folkloric;
+ case 0x06:
+ return R.string.Content$Romance;
+ case 0x07:
+ return R.string.Content$Serious__Classical__Religious__Historical_Movie__Drama;
+ case 0x08:
+ return R.string.Content$Adult_Movie__Drama;
+ }
+ case EventContentGroup.NewsCurrentAffairs:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$News__Current_Affairs;
+ case 0x01:
+ return R.string.Content$News__Weather_Report;
+ case 0x02:
+ return R.string.Content$News_Magazine;
+ case 0x03:
+ return R.string.Content$Documentary;
+ case 0x04:
+ return R.string.Content$Discussion__Inverview__Debate;
+ }
+
+ case EventContentGroup.Show:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Show__Game_Show;
+ case 0x01:
+ return R.string.Content$Game_Show__Quiz__Contest;
+ case 0x02:
+ return R.string.Content$Variety_Show;
+ case 0x03:
+ return R.string.Content$Talk_Show;
+ }
+
+ case EventContentGroup.Sports:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Sports;
+ case 0x01:
+ return R.string.Content$Special_Event;
+ case 0x02:
+ return R.string.Content$Sport_Magazine;
+ case 0x03:
+ return R.string.Content$Football__Soccer;
+ case 0x04:
+ return R.string.Content$Tennis__Squash;
+ case 0x05:
+ return R.string.Content$Team_Sports;
+ case 0x06:
+ return R.string.Content$Athletics;
+ case 0x07:
+ return R.string.Content$Motor_Sport;
+ case 0x08:
+ return R.string.Content$Water_Sport;
+ case 0x09:
+ return R.string.Content$Winter_Sports;
+ case 0x0A:
+ return R.string.Content$Equestrian;
+ case 0x0B:
+ return R.string.Content$Martial_Sports;
+ }
+
+ case EventContentGroup.ChildrenYouth:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Childrens__Youth_Programme;
+ case 0x01:
+ return R.string.Content$Preschool_Childrens_Programme;
+ case 0x02:
+ return R.string.Content$Entertainment_Programme_for_6_to_14;
+ case 0x03:
+ return R.string.Content$Entertainment_Programme_for_10_to_16;
+ case 0x04:
+ return R.string.Content$Informational__Educational__School_Programme;
+ case 0x05:
+ return R.string.Content$Cartoons__Puppets;
+ }
+
+ case EventContentGroup.MusicBalletDance:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Music__Ballet__Dance;
+ case 0x01:
+ return R.string.Content$Rock__Pop;
+ case 0x02:
+ return R.string.Content$Serious__Classical_Music;
+ case 0x03:
+ return R.string.Content$Folk__Tradional_Music;
+ case 0x04:
+ return R.string.Content$Jazz;
+ case 0x05:
+ return R.string.Content$Musical__Opera;
+ case 0x06:
+ return R.string.Content$Ballet;
+ }
+
+ case EventContentGroup.ArtsCulture:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Arts__Culture;
+ case 0x01:
+ return R.string.Content$Performing_Arts;
+ case 0x02:
+ return R.string.Content$Fine_Arts;
+ case 0x03:
+ return R.string.Content$Religion;
+ case 0x04:
+ return R.string.Content$Popular_Culture__Traditional_Arts;
+ case 0x05:
+ return R.string.Content$Literature;
+ case 0x06:
+ return R.string.Content$Film__Cinema;
+ case 0x07:
+ return R.string.Content$Experimental_Film__Video;
+ case 0x08:
+ return R.string.Content$Broadcasting__Press;
+ case 0x09:
+ return R.string.Content$New_Media;
+ case 0x0A:
+ return R.string.Content$Arts__Culture_Magazine;
+ case 0x0B:
+ return R.string.Content$Fashion;
+ }
+
+ case EventContentGroup.SocialPoliticalEconomics:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Social__Political__Economics;
+ case 0x01:
+ return R.string.Content$Magazine__Report__Documentary;
+ case 0x02:
+ return R.string.Content$Economics__Social_Advisory;
+ case 0x03:
+ return R.string.Content$Remarkable_People;
+ }
+
+ case EventContentGroup.EducationalScience:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Education__Science__Factual;
+ case 0x01:
+ return R.string.Content$Nature__Animals__Environment;
+ case 0x02:
+ return R.string.Content$Technology__Natural_Sciences;
+ case 0x03:
+ return R.string.Content$Medicine__Physiology__Psychology;
+ case 0x04:
+ return R.string.Content$Foreign_Countries__Expeditions;
+ case 0x05:
+ return R.string.Content$Social__Spiritual_Sciences;
+ case 0x06:
+ return R.string.Content$Further_Education;
+ case 0x07:
+ return R.string.Content$Languages;
+ }
+
+ case EventContentGroup.LeisureHobbies:
+ switch (c & 0x0F) {
+ default:
+ case 0x00:
+ return R.string.Content$Leisure__Hobbies;
+ case 0x01:
+ return R.string.Content$Tourism__Travel;
+ case 0x02:
+ return R.string.Content$Handicraft;
+ case 0x03:
+ return R.string.Content$Motoring;
+ case 0x04:
+ return R.string.Content$Fitness_and_Health;
+ case 0x05:
+ return R.string.Content$Cooking;
+ case 0x06:
+ return R.string.Content$Advertisement__Shopping;
+ case 0x07:
+ return R.string.Content$Gardening;
+ }
+ case EventContentGroup.Special:
+ switch (c & 0x0F) {
+ case 0x00:
+ return R.string.Content$Original_Language;
+ case 0x01:
+ return R.string.Content$Black_and_White;
+ case 0x02:
+ return R.string.Content$Unpublished;
+ case 0x03:
+ return R.string.Content$Live_Broadcast;
+ default:
+ ;
+ }
+ break;
+ default:
+ ;
+ }
+ return R.string.Content$Unknown;
+ }
+
+ public static String getContenString(Context ctx, int[] contents) {
+
+ if (contents.length == 0) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ String sep = "";
+
+ for (int content : contents) {
+
+ sb.append(sep).append(ctx.getString(contentToString(content)));
+ sep = ", ";
+ }
+
+ return sb.toString();
+ }
+
+ public static boolean isSerie(int[] contents) {
+ if (contents.length == 0) {
+ return false;
+ }
+
+ for (int c : contents) {
+ if (c == 21) {
+ return true;
+
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrListActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrListActivity.java
new file mode 100644
index 0000000..a499d00
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrListActivity.java
@@ -0,0 +1,287 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.style.UnderlineSpan;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.Intents;
+import de.bjusystems.vdrmanager.backup.BackupSettingsActivity;
+import de.bjusystems.vdrmanager.backup.IntentUtils;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Vdr;
+import de.bjusystems.vdrmanager.data.db.DBAccess;
+
+public class VdrListActivity extends ListActivity implements
+ OnItemClickListener, OnItemLongClickListener {
+
+ private static final String TAG = VdrListActivity.class.getName();
+
+ List<Vdr> list = new ArrayList<Vdr>();
+
+ ArrayAdapter<Vdr> adapter = null;
+
+ // Cursor cursor;
+
+ String[] listItems = {};
+
+ private boolean emptyConfig = false;
+
+ // private void initCursor() {
+ //
+ // //if (cursor != null) {
+ // //if (!cursor.isClosed()) {
+ // //cursor.close();
+ // // }
+ // //}
+ // try {
+ // cursor = getHelper().getVdrCursor();
+ // //startManagingCursor(cursor);
+ // } catch (Exception ex) {
+ // Log.w(TAG,ex);
+ // }
+ // }
+
+ private void populateIntent() {
+ emptyConfig = getIntent().getBooleanExtra(Intents.EMPTY_CONFIG,
+ Boolean.FALSE);
+ }
+
+ static class Holder {
+ public TextView text1;
+ public TextView text2;
+ }
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ populateIntent();
+
+ setContentView(R.layout.vdr_list_add_delete);
+
+ // initCursor();
+ final Vdr cur = Preferences.get().getCurrentVdr();
+ adapter = new ArrayAdapter<Vdr>(this,
+ android.R.layout.simple_list_item_2, list) {
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // recycle view?
+ Holder holder = null;
+ View view = convertView;
+ if (view == null) {
+ view = getLayoutInflater().inflate(
+ android.R.layout.simple_list_item_2, null);
+ holder = new Holder();
+
+ holder.text1 = (TextView) view
+ .findViewById(android.R.id.text1);
+ holder.text2 = (TextView) view
+ .findViewById(android.R.id.text2);
+ view.setTag(holder);
+ } else {
+ holder = (Holder) view.getTag();
+ }
+
+ Vdr vdr = getItem(position);
+ String name = (vdr.getName() != null ? vdr.getName() : "");
+ String host = vdr.getHost();
+ holder.text2.setText(host);
+
+ if (cur != null && cur.getId() == vdr.getId()) {
+ SpannableString content = new SpannableString(name);
+ content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
+ holder.text1.setText(content);
+ // text1.setText(text1.getText());
+ } else {
+ holder.text1.setTypeface(Typeface.DEFAULT);
+ holder.text1.setText(name);
+ }
+ return view;
+ }
+
+ };
+
+ // adapter = new ArrayAdapter<Vdr>(
+ // "name", "host" }, new int[] { android.R.id.text1,
+ // android.R.id.text2}) {
+ // @Override
+ // public void bindView(View view, Context context,
+ // Cursor cursor) {
+ //
+ // TextView text1 = (TextView)view.findViewById(android.R.id.text1);
+ // TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+ // int id = cursor.getInt(cursor.getColumnIndex("_id"));
+ // String name = cursor.getString(cursor.getColumnIndex("name"));
+ // String host = cursor.getString(cursor.getColumnIndex("host"));
+ // text2.setText(host);
+ //
+ // if(cur != null && cur.getId() == id) {
+ // SpannableString content = new SpannableString(name);
+ // content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
+ // text1.setText(content);
+ // //text1.setText(text1.getText());
+ // } else {
+ // text1.setTypeface(Typeface.DEFAULT);
+ // text1.setText(name);
+ // }
+ //
+ //
+ // }
+ //
+ // };
+ setListAdapter(adapter);
+ registerForContextMenu(getListView());
+ getListView().setOnItemClickListener(this);
+ getListView().setOnItemLongClickListener(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget
+ * .AdapterView, android.view.View, int, long)
+ */
+ public void onItemClick(AdapterView<?> parent, View view, int position,
+ long id) {
+ editVdr(adapter.getItem(position).getId());
+ }
+
+ /**
+ * Start {@link VdrPreferencesActivity} to create or edit a vdr
+ *
+ * @param id
+ * may be null. Then a new vdr is created
+ */
+ private void editVdr(Integer id) {
+ Intent intent = new Intent(this, VdrPreferencesActivity.class);
+ intent.putExtra(Intents.VDR_ID, id);
+ startActivityForResult(intent, Intents.EDIT_VDR);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see android.app.Activity#onActivityResult(int, int,
+ * android.content.Intent)
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+
+ if (resultCode != RESULT_OK) {
+ return;
+ }
+ if (requestCode == Intents.EDIT_VDR) {
+ refresh();
+ return;
+ }
+
+ }
+
+ @Override
+ protected void onResume() {
+ refresh();
+ super.onResume();
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (list.isEmpty()) {
+ finish();
+ return;
+ }
+ if (emptyConfig) {
+ Intent intent = new Intent(this, VdrManagerActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ finish();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ /**
+ * Refresh the list
+ */
+ private void refresh() {
+ list.clear();
+ list.addAll(DBAccess.get(this).getVdrDAO().queryForAll());
+ adapter.notifyDataSetChanged();
+ }
+
+ public boolean onItemLongClick(AdapterView<?> parent, View view,
+ final int position, final long id) {
+
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.vdr_device_delete_qeustion)//
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ if (DBAccess
+ .get(VdrListActivity.this)
+ .getVdrDAO()
+ .deleteById(
+ adapter.getItem(position)
+ .getId()) > 0) {
+ if (Preferences.get().getCurrentVdrContext(
+ VdrListActivity.this) == id) {
+ Preferences.setCurrentVdr(
+ VdrListActivity.this, null);
+ }
+ refresh();
+ }
+
+ }
+ })//
+ .setNegativeButton(android.R.string.cancel, null)//
+ .create()//
+ .show();
+ return false;
+ }
+
+ @Override
+ public final boolean onCreateOptionsMenu(final Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.vdrlist, menu);
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.main_menu_vdrlist_restore) {
+
+ Intent intent = IntentUtils.newIntent(this,
+ BackupSettingsActivity.class);
+ startActivity(intent);
+ return true;
+ } else if (item.getItemId() == R.id.main_menu_vdrlist_add) {
+ editVdr(null);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrManagerActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrManagerActivity.java
new file mode 100644
index 0000000..729d75c
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrManagerActivity.java
@@ -0,0 +1,341 @@
+package de.bjusystems.vdrmanager.gui;
+
+import android.app.AlertDialog;
+import android.app.SearchManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.SearchRecentSuggestions;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.SearchView;
+import android.support.v7.widget.SearchView.OnQueryTextListener;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.j256.ormlite.android.AndroidDatabaseResults;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.app.Intents;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Vdr;
+import de.bjusystems.vdrmanager.data.db.DBAccess;
+import de.bjusystems.vdrmanager.data.db.EPGSearchSuggestionsProvider;
+import de.bjusystems.vdrmanager.remote.RemoteActivity;
+import de.bjusystems.vdrmanager.utils.wakeup.AsyncWakeupTask;
+
+public class VdrManagerActivity extends ActionBarActivity implements
+ OnClickListener, OnQueryTextListener {
+
+ public static final String TAG = "VdrManagerActivity";
+
+ public static final String VDR_PORTAL = "http://www.vdr-portal.de";
+
+ private SearchView search;
+
+ private View actionMenuWakup;
+ private View actionMenuRemote;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Preferences.initVDR(this);
+
+ // if(Preferences.get().getCurrentVdr() == null){
+ // finish();
+ // return;
+ // } android.support.v7.appcompat.R
+
+ if (Preferences.initVDR(this) == false) {
+ final Intent intent = new Intent();
+ intent.setClass(this, VdrListActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intents.EMPTY_CONFIG, Boolean.TRUE);
+ startActivity(intent);
+ Toast.makeText(this, R.string.no_vdr, Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
+ Preferences.setLocale(this);
+
+ // this.getActionBar().setDisplayShowCustomEnabled(true);
+ // this.getActionBar().setDisplayShowTitleEnabled(false);
+ // setTitle(getString(R.string.app_name));
+ // attach view
+ setContentView(R.layout.vdrmanager);
+
+ // Preferences.loadPreferences(this);
+
+ findViewById(R.id.action_menu_channels).setOnClickListener(this);
+ findViewById(R.id.action_menu_recordings).setOnClickListener(this);
+ findViewById(R.id.action_menu_timers).setOnClickListener(this);
+ findViewById(R.id.action_menu_epg).setOnClickListener(this);
+ findViewById(R.id.action_menu_remote).setOnClickListener(this);
+// View v = findViewById(R.id.action_menu_search);
+// if (v != null) {
+// v.setOnClickListener(this);
+// }
+ //findViewById(R.id.main_logo).setOnClickListener(this);
+ actionMenuWakup = findViewById(R.id.action_menu_wakeup);
+ actionMenuRemote = findViewById(R.id.action_menu_remote);
+ // add and register buttons
+ // createButtons();
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main_menu, menu);
+
+ // search = new SearchView(getSupportActionBar().getThemedContext());
+ search = (SearchView) MenuItemCompat.getActionView( menu.findItem(R.id.menu_search));
+
+ // search = (SearchView)
+ // .getActionView();
+ //
+ // Object o = menu.findItem(R.id.menu_search);
+
+ SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ search.setSearchableInfo(searchManager
+ .getSearchableInfo(getComponentName()));
+
+ //search.setOnQueryTextListener(this);
+ return true;
+ }
+
+ @Override
+ protected void onResume() {
+ Preferences.setLocale(this);
+ if (Preferences.get().isWakeupEnabled() == false) {
+ actionMenuWakup.setVisibility(View.GONE);
+ actionMenuWakup.setOnClickListener(null);
+ } else {
+ actionMenuWakup.setVisibility(View.VISIBLE);
+ actionMenuWakup.setOnClickListener(this);
+ }
+
+ if(Preferences.get().isRemoteEnabled() == false){
+ actionMenuRemote.setVisibility(View.GONE);
+ actionMenuRemote.setOnClickListener(null);
+ } else {
+ actionMenuRemote.setVisibility(View.VISIBLE);
+ actionMenuRemote.setOnClickListener(this);
+ }
+
+
+ super.onResume();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(
+ final MenuItem item) {
+
+ switch (item.getItemId()) {
+ case R.id.main_menu_preferences: {
+ Intent intent = new Intent(this, PreferencesActivity.class);
+ int flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP;
+ intent.setFlags(flags);
+ startActivity(intent);
+ finish();
+ break;
+ }
+ case R.id.main_menu_info: {
+ if(isFinishing()){
+ break;
+ }
+ About.show(this);
+ break;
+ }
+ case R.id.main_menu_exit: {
+ finish();
+ break;
+ }
+
+ case R.id.main_menu_clear_search: {
+ SearchRecentSuggestions suggestions = new SearchRecentSuggestions(
+ this, EPGSearchSuggestionsProvider.AUTHORITY,
+ EPGSearchSuggestionsProvider.MODE);
+ suggestions.clearHistory();
+ break;
+ }
+
+ // case R.id.menu_search: {
+ // if(Build.VERSION.SDK_INT <11){
+ // onSearchRequested();
+ // }
+ // break;
+ // }
+ case R.id.main_menu_goto: {
+ try {
+ final Cursor cursor = ((AndroidDatabaseResults) DBAccess
+ .get(this).getVdrDAO().iterator().getRawResults())
+ .getRawCursor();
+ startManagingCursor(cursor);
+ final AlertDialog ad = new AlertDialog.Builder(this)
+ .setSingleChoiceItems(cursor, findVdrCursor(cursor),
+ "name", new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog,
+ int which) {
+ cursor.moveToPosition(which);
+ int id = cursor.getInt(cursor
+ .getColumnIndex("_id"));
+ Vdr vdr = DBAccess
+ .get(VdrManagerActivity.this)
+ .getVdrDAO().queryForId(id);
+ if (vdr == null) {
+ Toast.makeText(
+ VdrManagerActivity.this,
+ R.string.main_menu_goto_no_vdr,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Preferences.setCurrentVdr(
+ VdrManagerActivity.this,
+ vdr);
+ Toast.makeText(
+ VdrManagerActivity.this,
+ getString(
+ R.string.main_menu_switched_to,
+ vdr.getName()),
+ Toast.LENGTH_SHORT).show();
+ Intent intent = getIntent();
+ overridePendingTransition(0, 0);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ finish();
+
+ overridePendingTransition(0, 0);
+ startActivity(intent);
+ }
+ dialog.dismiss();
+ }
+ })//
+ .setTitle(R.string.main_menu_goto_title)//
+ .create();
+ ad.show();
+
+ } catch (Exception ex) {
+ Log.w(TAG, ex);
+ }
+
+ break;
+ }
+ }
+ return true;
+ }
+
+ private int findVdrCursor(Cursor c) {
+ if (Preferences.get().getCurrentVdr() == null) {
+ return -1;
+ }
+
+ int cid = Preferences.get().getCurrentVdr().getId();
+
+ int position = 0;
+ c.moveToPosition(-1);
+ while (c.moveToNext()) {
+ if (c.getInt(c.getColumnIndex("_id")) == cid) {
+ break;
+ }
+ position++;
+ }
+ return position;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (Preferences.get().isQuiteOnBackButton()) {
+ finish();
+ } else {
+ super.onBackPressed();
+ }
+
+ try {
+ // reassign a new and empty key store
+ ((VdrManagerApp)getApplication()).initSessionKeyStore();
+ } catch (final Exception e) {
+ Log.e(getClass().getName(), "Can't clear session key store");
+ }
+
+ }
+
+ public void startActivity(Class<?> clazz) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setClass(this, clazz);
+ startActivity(intent);
+ }
+
+ public void onClick(View v) {
+ int id = v.getId();
+
+ switch (id) {
+ case R.id.action_menu_channels:
+ startActivity(ChannelListActivity.class);
+ break;
+ case R.id.action_menu_recordings:
+ startActivity(RecordingListActivity.class);
+ break;
+ case R.id.action_menu_timers:
+ startActivity(TimerListActivity.class);
+ break;
+ case R.id.action_menu_epg:
+ startActivity(TimeEpgListActivity.class);
+ break;
+// case R.id.action_menu_search:
+// onSearchRequested();
+// break;
+ case R.id.action_menu_wakeup:
+ final AsyncWakeupTask wakeupTask = new AsyncWakeupTask(this);
+ wakeupTask.execute();
+ break;
+ case R.id.main_logo:
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setData(Uri.parse(VDR_PORTAL));
+ startActivity(i);
+ break;
+
+ case R.id.action_menu_remote:
+ startActivity(RemoteActivity.class);
+ break;
+
+ }
+
+ }
+
+ protected void startSearchManager() {
+ Bundle appData = new Bundle();
+ startSearch(null, false, appData, false);
+ }
+
+ @Override
+ public boolean onSearchRequested() {
+ search.setVisibility(View.VISIBLE);
+ // Bundle appData = new Bundle();
+ // appData.putBoolean(SearchableActivity.JARGON, true);
+ // startSearch(null, false, appData, false);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrPreferencesActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrPreferencesActivity.java
new file mode 100644
index 0000000..0d01737
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/gui/VdrPreferencesActivity.java
@@ -0,0 +1,508 @@
+package de.bjusystems.vdrmanager.gui;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.fueri.reeldroid.network.DeviceManager;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.view.View;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.VdrSharedPreferencesImpl;
+import de.bjusystems.vdrmanager.ZonePicker;
+import de.bjusystems.vdrmanager.app.Intents;
+import de.bjusystems.vdrmanager.data.FetchEditTextPreference;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Vdr;
+import de.bjusystems.vdrmanager.data.db.DBAccess;
+import de.bjusystems.vdrmanager.tasks.VoidAsyncTask;
+
+public class VdrPreferencesActivity extends BasePreferencesActivity implements
+ OnSharedPreferenceChangeListener, OnPreferenceClickListener {
+
+ public static final int REQUEST_CODE_PICK_A_TIME_ZONE = 1;
+
+ Vdr vdr;
+
+ VdrSharedPreferencesImpl pref;
+
+ int id = -1;
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ return this.pref;
+ }
+
+ @Override
+ public Preference findPreference(CharSequence key) {
+ return super.findPreference(key);
+ }
+
+ @Override
+ protected void updateSummary(Preference ep) {
+ if (ep.getKey().equals("key_timezone")) {
+ String text = vdr.getServerTimeZone();
+ if (text == null) {
+ return;
+ }
+ setSummary(text, ep);
+ return;
+ }
+ super.updateSummary(ep);
+ }
+
+ private boolean isNew = false;
+
+ private boolean hasChanged = false;
+
+ private void initVDRInstance() {
+ id = getIntent().getIntExtra(Intents.VDR_ID, -1);
+ if (id == -1) {// new vdr
+ vdr = new Vdr();
+ isNew = true;
+ } else {// edit
+ Vdr v = DBAccess.get(this).getVdrDAO().queryForId(id);
+ if (v != null) {
+ vdr = v;
+ } else {
+ vdr = new Vdr();
+ id = -1;
+ isNew = true;
+ }
+ }
+ pref = new VdrSharedPreferencesImpl(vdr, DBAccess.get(this).getVdrDAO());
+ }
+
+ public static String ARP_CACHE = "/proc/net/arp";
+
+ /**
+ * return mac address as a string.
+ *
+ * @param ip
+ * @return
+ */
+ public static String getMacFromArpCache(String ip) {
+
+ if (ip == null) {
+ return null;
+ }
+
+ BufferedReader br = null;
+
+ try {
+ br = new BufferedReader(new FileReader(ARP_CACHE));
+
+ String line;
+
+ while ((line = br.readLine()) != null) {
+ String[] values = line.split("\\s+");
+ if (values != null && values.length >= 4
+ && ip.equals(values[0])) {
+ // format check
+ String mac = values[3];
+ if (mac.matches("..:..:..:..:..:..")) {
+ return mac;
+ } else {
+ return null;
+ }
+ }
+ }
+ } catch (Exception e) {
+
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e) {
+
+ }
+ }
+ return null;
+ }
+
+ // private String getIp() throws Exception {
+ // final Preferences prefs = Preferences.get();
+ // String host = prefs.getSvdrpHost();
+ // return InetAddress.getByName(host).getHostAddress();
+ // }
+
+ private void ping(String ip, int port) throws Exception {
+ Socket socket = new Socket();
+ socket.connect(new InetSocketAddress(ip, port), 5 * 1000);
+ socket.setSoTimeout(5 * 1000);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ initVDRInstance();
+
+ this.addPreferencesFromResource(R.xml.vdr_prefs);
+
+ // this.getPreferenceManager().setSharedPreferencesName(Preferences.getPreferenceFile(this));
+
+ pref.registerOnSharedPreferenceChangeListener(this);
+
+ String recstream = pref.getString("key_recstream_method", "vdr-live");
+
+ if (recstream.equals("vdr-live") == false) {
+ Preference p = findPreference("key_live_port");
+ p.setEnabled(false);
+ // PreferenceCategory cat = (PreferenceCategory)
+ // findPreference("key_streaming_category");
+ // cat.removePreference(p);
+ }
+
+ if (recstream.equals("vdr-smarttvweb") == false) {
+ Preference p = findPreference("key_smarttvweb_port");
+ p.setEnabled(false);
+ p = findPreference("key_smarttvweb_recstream_method");
+ p.setEnabled(false);
+
+ }
+
+ // create background task
+
+ // start task
+
+ final FetchEditTextPreference macedit = (FetchEditTextPreference) findPreference(getString(R.string.wakeup_wol_mac_key));
+ String mac = vdr.getMac();
+ if (mac == null) {
+ mac = "";
+ }
+ macedit.setText(mac);
+ macedit.setCompoundButtonListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String host = vdr.getHost();
+ if (host == null) {
+ Utils.say(VdrPreferencesActivity.this,
+ getString(R.string.vdr_host_not_defined));
+ return;
+ }
+
+ new VoidAsyncTask() {
+
+ ProgressDialog pd;
+
+ private String mac;
+
+ String message;
+
+ protected void onPreExecute() {
+ pd = new ProgressDialog(VdrPreferencesActivity.this);
+ pd.setMessage(getString(R.string.processing));
+ pd.show();
+ };
+
+ protected void onPostExecute(Void result) {
+ pd.dismiss();
+ if (message != null) {
+ Utils.say(VdrPreferencesActivity.this, message);
+ return;
+ }
+ macedit.setEditText(mac);
+ };
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ String ip = InetAddress.getByName(host)
+ .getHostAddress();
+ ping(ip, vdr.getPort());
+ mac = getMacFromArpCache(ip);
+ } catch (Exception ex) {
+ message = ex.getLocalizedMessage();
+ }
+
+ return null;
+ }
+ }.execute();
+ }
+ });
+
+ final FetchEditTextPreference ipEdit = (FetchEditTextPreference) findPreference(getString(R.string.vdr_host_key));
+ String ip = vdr.getHost();
+ ipEdit.setText(ip);
+ ipEdit.setCompoundButtonListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ new AsyncTask<Void, String, List<String>>() {
+
+ ProgressDialog pd;
+
+ String message;
+
+ protected void onPreExecute() {
+ pd = new ProgressDialog(VdrPreferencesActivity.this);
+ pd.setMessage(getString(R.string.processing));
+ pd.show();
+ };
+
+ protected void onPostExecute(final List<String> ips) {
+ pd.dismiss();
+ if (message != null) {
+ Utils.say(VdrPreferencesActivity.this, message);
+ return;
+ }
+
+ if (ips.isEmpty()) {
+ Utils.say(VdrPreferencesActivity.this,
+ R.string.no_results);
+ return;
+ }
+ if (ips.size() == 1) {
+ ipEdit.setEditText(ips.get(0).toString());
+ } else {
+ new AlertDialog.Builder(VdrPreferencesActivity.this)
+ .setItems(
+ ips.toArray(new CharSequence[] {}),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(
+ DialogInterface dialog,
+ int which) {
+ String ip = ips.get(which);
+ ipEdit.setEditText(ip);
+ }
+ }).show();
+ }
+
+ }
+
+ protected void onProgressUpdate(String... values) {
+ pd.setMessage(getString(R.string.probing, values[0]));
+ };
+
+ @Override
+ protected List<String> doInBackground(Void... params) {
+ try {
+
+ final int port = vdr.getPort();
+ return DeviceManager.findVDRHosts(
+ VdrPreferencesActivity.this, port,
+ new DeviceManager.ProgressListener() {
+
+ @Override
+ public void publish(String currentIP) {
+ publishProgress(currentIP);
+ }
+ });
+
+ } catch (Exception ex) {
+ message = ex.getLocalizedMessage();
+ }
+ return new ArrayList<String>(0);
+ }
+ }.execute();
+
+ }
+ });
+
+ updateChildPreferences();
+
+ findPreference(getString(R.string.timezone_key))
+ .setOnPreferenceClickListener(this);
+
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences arg0, String key) {
+ hasChanged = true;
+ updateChildPreferences();
+ Preference p = findPreference(key);
+ updateSummary(p);
+
+ if (key != null && key.equals("key_recstream_method")) {
+ String recstream = pref.getString("key_recstream_method",
+ "vdr-live");
+ Preference pk = findPreference("key_live_port");
+ if (recstream.equals("vdr-live") == false) {
+
+ pk.setEnabled(false);
+ // PreferenceCategory cat = (PreferenceCategory)
+ // findPreference("key_streaming_category");
+ // cat.removePreference(p);
+ } else {
+ pk.setEnabled(true);
+ }
+
+ if (recstream.equals("vdr-smarttvweb") == false) {
+ p = findPreference("key_smarttvweb_port");
+ p.setEnabled(false);
+ p = findPreference("key_smarttvweb_recstream_method");
+ p.setEnabled(false);
+
+ } else {
+ p = findPreference("key_smarttvweb_port");
+ p.setEnabled(true);
+ p = findPreference("key_smarttvweb_recstream_method");
+ p.setEnabled(true);
+
+ }
+
+ // if(pk)
+ // cat.addPreference(pk);
+ // } else {
+ // cat.removePreference(pk);
+ // }
+ }
+
+ Preferences.reloadVDR(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ pref.registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // Unregister the listener whenever a key changes
+ pref.unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ private void enableWolPreferences() {
+ Preference p = findPreference(getString(R.string.wakeup_wol_mac_key));
+ if (p != null)
+
+ p.setEnabled(true);
+ p = findPreference(getString(R.string.wakeup_wol_custom_broadcast_key));
+ if (p != null) {
+ p.setEnabled(true);
+ }
+ }
+
+ private void disableWolPreferences() {
+ Preference p = findPreference(getString(R.string.wakeup_wol_mac_key));
+ if (p != null)
+ p.setEnabled(false);
+
+ p = findPreference(getString(R.string.wakeup_wol_custom_broadcast_key));
+ if (p != null)
+ p.setEnabled(false);
+
+ }
+
+ private void disableWakeupUrlPreferences() {
+ Preference p = findPreference(getString(R.string.wakeup_url_key));
+ if (p != null) {
+ p.setEnabled(false);
+ }
+ p = findPreference(getString(R.string.wakeup_password_key));
+ if (p != null) {
+ p.setEnabled(false);
+ }
+
+ p = findPreference(getString(R.string.wakeup_user_key));
+ if (p != null) {
+ p.setEnabled(false);
+ }
+ }
+
+ private void enableWakeupUrlPrefenreces() {
+ Preference p = findPreference(getString(R.string.wakeup_url_key));
+ if (p != null) {
+ p.setEnabled(true);
+ }
+
+ p = findPreference(getString(R.string.wakeup_password_key));
+ if (p != null) {
+ p.setEnabled(true);
+ }
+ p = findPreference(getString(R.string.wakeup_user_key));
+ if (p != null) {
+ p.setEnabled(true);
+ }
+ }
+
+ private void updateChildPreferences() {
+ String wakup = pref.getString(getString(R.string.wakeup_method_key),
+ "wol");
+
+ if (wakup.equals("url")) {
+ disableWolPreferences();
+ enableWakeupUrlPrefenreces();
+ } else {// remote url
+ disableWakeupUrlPreferences();
+ enableWolPreferences();
+ }
+
+ for (String key : pref.getAll().keySet()) {
+ Preference p = findPreference(key);
+ updateSummary(p);
+ }
+
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (id != -1) {// no new devices
+ setResult(RESULT_OK);
+ finish();
+ return;
+ }
+ if (isNew == true && hasChanged == false) {
+ // if (pref.commits < 2) {// user has not changed anything
+ DBAccess.get(this).getVdrDAO().delete(pref.getVdr());
+ finish();
+ return;
+ }
+ super.onBackPressed();
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+
+ String timezone = vdr.getServerTimeZone();
+
+ Intent intent = new Intent(this, ZonePicker.class);
+ intent.putExtra("current_tz", timezone);
+ startActivityForResult(intent, REQUEST_CODE_PICK_A_TIME_ZONE);
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ super.onActivityResult(requestCode, resultCode, data);
+ return;
+ }
+
+ // never mind, onResume also registers this, but the listenres are held
+ // in a map (as key) so that no double occurance
+ pref.registerOnSharedPreferenceChangeListener(this);
+
+ if (requestCode == REQUEST_CODE_PICK_A_TIME_ZONE) {
+ String ntz = data.getStringExtra("new_tz");
+ if (ntz != null) {
+ vdr.setServerTimeZone(ntz);
+ Editor editor = findPreference("key_timezone").getEditor();
+ editor.putString("key_timezone", ntz);
+ editor.commit();
+ // setSummary(ntz, );
+ }
+ }
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/HITK.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/HITK.java
new file mode 100644
index 0000000..5d0c596
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/HITK.java
@@ -0,0 +1,77 @@
+package de.bjusystems.vdrmanager.remote;
+
+/**
+ * Created by lado on 09.05.15.
+ */
+public enum HITK {
+ Up,
+ Down,
+ Menu,
+ Ok,
+ Back,
+ Left,
+ Right,
+ Red,
+ Green,
+ Yellow,
+ Blue,
+ Zero("0"),
+ One("1"),
+ Two("2"),
+ Three("3"),
+ Four("4"),
+ Five("5"),
+ Six("6"),
+ Seven("7"),
+ Eight("8"),
+ Nine("9"),
+ Info,
+ Play_Pause("Play/Pause"),
+ Play,
+ Pause,
+ Stop,
+ Record,
+ FastFwd,
+ FastRew,
+ Next,
+ Prev,
+ Power,
+ ChannelUp("Channel+"),
+ ChannelDown("Channel-"),
+ PrevChannel,
+ VolumeUp("Volume+"),
+ VolumeDown("Volume-"),
+ Mute,
+ Audio,
+ Subtitles,
+ Schedule,
+ Channels,
+ Timers,
+ Recordings,
+ Setup,
+ Commands,
+ User0,
+ User1,
+ User2,
+ User3,
+ User4,
+ User5,
+ User6,
+ User7,
+ User8,
+ User9,;
+
+ String value;
+
+ private HITK() {
+ this.value = this.name();
+ }
+
+ private HITK(String value) {
+ this.value = value;
+ }
+
+ public String getValue(){
+ return this.value;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/RemoteActivity.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/RemoteActivity.java
new file mode 100644
index 0000000..57f76d4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/remote/RemoteActivity.java
@@ -0,0 +1,395 @@
+package de.bjusystems.vdrmanager.remote;
+
+
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.view.*;
+import de.bjusystems.vdrmanager.gui.Utils;
+import org.hampelratte.svdrp.Connection;
+import org.hampelratte.svdrp.Response;
+import org.hampelratte.svdrp.commands.HITK;
+
+import de.androvdr.widget.AnimatedTextView;
+import de.androvdr.widget.FontAwesome;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.gui.ColoredButton;
+import de.bjusystems.vdrmanager.tasks.VoidAsyncTask;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View.OnClickListener;
+import android.view.animation.AlphaAnimation;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+public class RemoteActivity extends Activity implements OnClickListener, View.OnLongClickListener {
+
+
+ private Connection connection;
+
+ private AnimatedTextView result;
+ private AlphaAnimation out;
+ private AlphaAnimation in;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ //Remove notification bar
+ this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ View view = getLayoutInflater().inflate(R.layout.remote, null);
+
+ setContentView(view);
+
+ view.setOnLongClickListener(this);
+
+
+ ViewGroup viewGroup = (ViewGroup) view.findViewById(R.id.root);
+
+ Button button = (Button) viewGroup.findViewById(R.id.red);
+ //button.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
+
+ setAllButtonListener(viewGroup);
+
+ result = (AnimatedTextView) findViewById(R.id.result);
+ //Animation in = AnimationUtils.loadAnimation(this,android.R.anim.fade_in);
+ //Animation out = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
+/* result.setInAnimation(in);
+ result.setOutAnimation(out);
+ result.setFactory(this);*/
+ out = new AlphaAnimation(1.0f, 0.0f);
+ out.setDuration(100);
+
+ in = new AlphaAnimation(0.0f, 1.0f);
+ in.setDuration(100);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.remote_menu, menu);
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if(isBackKeyRemapped() == false) {
+ super.onBackPressed();
+ } else {
+ new HitkAsyncTask() {
+ }.execute("Back");
+
+ }
+ }
+
+
+ private void resetOverrides(){
+ SharedPreferences sharedPref = getSharedPreferences("remote_" + Preferences.get().getCurrentVdr().getId(), Context.MODE_PRIVATE);
+ sharedPref
+ .edit()
+ .clear()
+ .commit();
+ restart();
+ }
+
+ private void setBackKeyRemapped(boolean value){
+ SharedPreferences sharedPref = getSharedPreferences("misc_" + Preferences.get().getCurrentVdr().getId(), Context.MODE_PRIVATE);
+ sharedPref//
+ .edit()//
+ .putBoolean("backishitk", value)//
+ .commit();
+ }
+
+ private boolean isBackKeyRemapped(){
+ SharedPreferences sharedPref = getSharedPreferences("misc_" + Preferences.get().getCurrentVdr().getId(), Context.MODE_PRIVATE);
+ return sharedPref.getBoolean("backishitk", false);
+ }
+
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if(isBackKeyRemapped() == false){
+ return super.onKeyLongPress(keyCode, event);
+ }
+ super.onBackPressed();
+ return true;
+ }
+ return super.onKeyLongPress(keyCode, event);
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem item = menu.findItem(R.id.remapback);
+ item.setChecked(isBackKeyRemapped());
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+
+ switch (item.getItemId()) {
+ case R.id.reset: {
+ resetOverrides();
+ return true;
+ }
+ case R.id.exprt: {
+ Utils.say(this, R.string.not_yet_implemented);
+ return true;
+ }
+ case R.id.imprt: {
+ Utils.say(this, R.string.not_yet_implemented);
+ return true;
+ }
+ case R.id.remapback: {
+ if(item.isChecked()){
+ setBackKeyRemapped(false);
+ } else {
+ setBackKeyRemapped(true);
+ Utils.say(this, R.string.remapback_hint);
+ }
+ }
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (connection != null) {
+ new VoidAsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ connection.close();
+ connection = null;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ }.execute();
+
+ }
+ }
+
+
+ public void setAllButtonListener(ViewGroup viewGroup) {
+
+ //
+ SharedPreferences sharedPref = getSharedPreferences("remote_" + Preferences.get().getCurrentVdr().getId(), Context.MODE_PRIVATE);
+ View v;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ v = viewGroup.getChildAt(i);
+ if (v instanceof ViewGroup) {
+ setAllButtonListener((ViewGroup) v);
+ } else if (v instanceof Button) {
+ if (v.getTag() == null) {
+ continue;
+ }
+ ((Button) v).setOnClickListener(this);
+ ((Button) v).setOnLongClickListener(this);
+
+ String hitk = sharedPref.getString("key_" + String.valueOf(v.getTag()), null);
+ setOverrideTag(v, hitk);
+ String label = sharedPref.getString("label_" + String.valueOf(v.getTag()), null);
+ setOverrideLabel((Button)v, label);
+ }
+ }
+ }
+
+ private void setOverrideLabel(Button b, CharSequence label) {
+ if(label == null){
+ return;
+ }
+ b.setText(label);
+ }
+
+
+ private void restart() {
+
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ recreate();
+ } else {
+ Intent intent = getIntent();
+ overridePendingTransition(0, 0);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ finish();
+
+ overridePendingTransition(0, 0);
+ startActivity(intent);
+ }
+ }
+
+
+ class HitkAsyncTask extends AsyncTask<String, Void, Void>{
+
+ Response send;
+
+ Exception ex;
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ result.setText("");
+ result.fadeIn();
+ if (send != null) {
+ result.setText(String.valueOf(send.getMessage()));
+ result.fadeOut();
+ //Utils.say(getBaseContext(), send.toString());
+ } else if (ex != null) {
+ result.setText(ex.getMessage());
+ }
+ }
+
+ @Override
+ protected Void doInBackground(String... hitk) {
+ try {
+
+ if (connection == null) {
+ connection = new Connection(Preferences.get().getHost(), Preferences.get().getSvdrpPort());
+ }
+
+ send = connection.send(new HITK(hitk[0]));
+ } catch (Exception ex) {
+ this.ex = ex;
+ }
+
+ return null;
+ }
+
+
+
+ };
+
+ @Override
+ public void onClick(final View v) {
+ new HitkAsyncTask() {
+ }.execute(getCurrentTag(v));
+ }
+
+ @Override
+ public boolean onLongClick(final View v) {
+
+
+ if (v.getId() == R.id.root) {
+ openOptionsMenu();
+ return true;
+ }
+
+ CharSequence text = ((Button) v).getText();
+ final String tag = (String) v.getTag();
+ final String override = (String) v.getTag(-100);
+ String current = override != null ? override : tag;
+
+ View view = getLayoutInflater().inflate(R.layout.edit_remote_key, null);
+
+
+ final EditText hitk = (EditText) view.findViewById(R.id.hitk);
+ TextView hitkLabel = (TextView) view.findViewById(R.id.hitkLabel);
+ if (v instanceof ColoredButton) {
+ hitk.setVisibility(View.GONE);
+ hitkLabel.setVisibility(View.GONE);
+ } else {
+ hitk.setVisibility(View.VISIBLE);
+ hitkLabel.setVisibility(View.VISIBLE);
+ hitk.setTypeface(
+ FontAwesome.getFontAwesome(this)
+ );
+ }
+ hitk.setText(text);
+ final Spinner hitkspinner = (Spinner) view.findViewById(R.id.hitkSpinner);
+
+ final ArrayList<String> keys = new ArrayList<>();
+ for (de.bjusystems.vdrmanager.remote.HITK hk : de.bjusystems.vdrmanager.remote.HITK.values()) {
+ keys.add(hk.getValue());
+ }
+ ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_dropdown_item, keys.toArray(new String[]{}));
+// Specify the layout to use when the list of choices appears
+ // hitkspinner.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+// Apply the adapter to the spinner
+ hitkspinner.setAdapter(adapter);
+ int selected = -1;
+ for (int i = 0; i < keys.size(); ++i) {
+ String k = keys.get(i);
+ if (k.equals(current)) {
+ selected = i;
+ break;
+ }
+ }
+ hitkspinner.setSelection(selected);
+ hitkspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+ });
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ CharSequence nhk = hitk.getText();
+ ((Button) v).setText(nhk);
+ String hitk = (String) hitkspinner.getSelectedItem();
+ putVdrKey("key_" + tag, hitk);
+ putVdrKey("label_" + tag, nhk);
+ setOverrideTag(v, hitk);
+
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ }).setView(view);
+
+ builder.create().show();
+
+
+ return false;
+ }
+
+ private void putVdrKey(String key, CharSequence value) {
+ SharedPreferences sharedPref = getSharedPreferences("remote_" + Preferences.get().getCurrentVdr().getId(), Context.MODE_PRIVATE);
+ SharedPreferences.Editor edit = sharedPref.edit();
+ edit.putString(key, String.valueOf(value));
+ edit.commit();
+ }
+
+ private String getCurrentTag(View view) {
+ Object tag = view.getTag();
+ if (tag instanceof String == false) {
+ return null;
+ }
+
+ Object otag = view.getTag(-100);
+ if (otag == null) {
+ return (String) tag;
+ }
+ return (String) otag;
+ }
+
+ private void setOverrideTag(View view, String hitk) {
+ view.setTag(-100, hitk);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/AsyncProgressTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/AsyncProgressTask.java
new file mode 100644
index 0000000..daeb2c7
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/AsyncProgressTask.java
@@ -0,0 +1,63 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.app.Activity;
+import de.bjusystems.vdrmanager.gui.SvdrpProgressDialog;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpAsyncTask;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpEvent;
+
+public abstract class AsyncProgressTask<Result> {
+
+ class AsyncProgress extends SvdrpProgressDialog<Result> {
+
+ public AsyncProgress(final Activity activity,
+ final SvdrpClient<Result> client) {
+ super(activity, client);
+ }
+
+ @Override
+ public void svdrpEvent(final SvdrpEvent event) {
+ super.svdrpEvent(event);
+ switch (event) {
+ case ABORTED:
+ case CONNECT_ERROR:
+ case CONNECTION_TIMEOUT:
+ case ERROR:
+ case LOGIN_ERROR:
+ case FINISHED_ABNORMALY:
+ case FINISHED_SUCCESS:
+ case CACHE_HIT:
+ AsyncProgressTask.this.finished(event);
+ break;
+ }
+ }
+ }
+
+ Activity activity;
+ SvdrpClient<Result> client;
+
+ public AsyncProgressTask(final Activity activity,
+ final SvdrpClient<Result> client) {
+ this.activity = activity;
+ this.client = client;
+ }
+
+ public void start() {
+
+ // delete timer
+ /*
+ * final SetTimerClient client = new SetTimerClient(timer, true) {
+ *
+ * @Override public int getProgressTextId() { return
+ * R.string.progress_timer_delete; } };
+ */
+ final SvdrpAsyncTask<Result, SvdrpClient<Result>> task = new SvdrpAsyncTask<Result, SvdrpClient<Result>>(
+ client);
+ final AsyncProgress progress = new AsyncProgress(activity, client);
+ task.addSvdrpListener(progress);
+ task.addSvdrpExceptionListener(progress);
+ task.run();
+ }
+
+ public abstract void finished(SvdrpEvent event);
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ChannelsTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ChannelsTask.java
new file mode 100644
index 0000000..1623575
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ChannelsTask.java
@@ -0,0 +1,11 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.app.Activity;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.utils.svdrp.ChannelClient;
+
+public abstract class ChannelsTask extends AsyncProgressTask<Channel> {
+ public ChannelsTask(final Activity activity, final ChannelClient client) {
+ super(activity, client);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/CreateTimerTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/CreateTimerTask.java
new file mode 100644
index 0000000..fa7dc43
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/CreateTimerTask.java
@@ -0,0 +1,20 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.app.Activity;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.gui.CertificateProblemDialog;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient.TimerOperation;
+
+public abstract class CreateTimerTask extends AsyncProgressTask<Timer> {
+
+ public CreateTimerTask(final Activity activity, final Timer timer) {
+ super(activity, new SetTimerClient(timer, TimerOperation.CREATE, new CertificateProblemDialog(activity)) {
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_timer_save;
+ }
+ });
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteRecordingTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteRecordingTask.java
new file mode 100644
index 0000000..b9f5f75
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteRecordingTask.java
@@ -0,0 +1,18 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.app.Activity;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Recording;
+import de.bjusystems.vdrmanager.gui.CertificateProblemDialog;
+import de.bjusystems.vdrmanager.utils.svdrp.DelRecordingClient;
+
+public abstract class DeleteRecordingTask extends AsyncProgressTask<Recording> {
+ public DeleteRecordingTask(final Activity activity, final Recording r) {
+ super(activity, new DelRecordingClient(r, new CertificateProblemDialog(activity)) {
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_recording_delete;
+ }
+ });
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteTimerTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteTimerTask.java
new file mode 100644
index 0000000..b3314d2
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/DeleteTimerTask.java
@@ -0,0 +1,20 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.app.Activity;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.gui.CertificateProblemDialog;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient.TimerOperation;
+
+public abstract class DeleteTimerTask extends AsyncProgressTask<Timer> {
+
+ public DeleteTimerTask(final Activity activity, final Timer timer) {
+ super(activity, new SetTimerClient(timer, TimerOperation.DELETE, new CertificateProblemDialog(activity)) {
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_timer_delete;
+ }
+ });
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ModifyTimerTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ModifyTimerTask.java
new file mode 100644
index 0000000..80aae53
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ModifyTimerTask.java
@@ -0,0 +1,24 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.app.Activity;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.gui.CertificateProblemDialog;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient.TimerOperation;
+
+public abstract class ModifyTimerTask extends AsyncProgressTask<Timer> {
+
+ public ModifyTimerTask(final Activity activity, final Timer newTimer, final Timer oldTimer) {
+ super(activity, new SetTimerClient(newTimer, oldTimer, TimerOperation.MODIFY, new CertificateProblemDialog(activity)) {
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_timer_modify;
+ }
+
+
+ });
+
+
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ToggleTimerTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ToggleTimerTask.java
new file mode 100644
index 0000000..82daae4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/ToggleTimerTask.java
@@ -0,0 +1,27 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.app.Activity;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Timer;
+import de.bjusystems.vdrmanager.gui.CertificateProblemDialog;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient;
+import de.bjusystems.vdrmanager.utils.svdrp.SetTimerClient.TimerOperation;
+
+public abstract class ToggleTimerTask extends AsyncProgressTask<Timer> {
+
+ public ToggleTimerTask(final Activity activity, final Timer timer) {
+ super(activity, new SetTimerClient(timer, TimerOperation.TOGGLE, new CertificateProblemDialog(activity)) {
+ boolean enabled = timer.isEnabled();
+
+ @Override
+ public int getProgressTextId() {
+ if (enabled) {
+ return R.string.progress_timer_disable;
+ } else {
+ return R.string.progress_timer_enable;
+ }
+ }
+ });
+ timer.setEnabled(!timer.isEnabled());
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/VoidAsyncTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/VoidAsyncTask.java
new file mode 100644
index 0000000..1d9f9f7
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/tasks/VoidAsyncTask.java
@@ -0,0 +1,7 @@
+package de.bjusystems.vdrmanager.tasks;
+
+import android.os.AsyncTask;
+
+public abstract class VoidAsyncTask extends AsyncTask<Void, Void, Void>{
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/date/DateFormatter.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/date/DateFormatter.java
new file mode 100644
index 0000000..95f99db
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/date/DateFormatter.java
@@ -0,0 +1,49 @@
+package de.bjusystems.vdrmanager.utils.date;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import de.bjusystems.vdrmanager.data.Preferences;
+
+
+/**
+ * Class for formatting date and time values
+ * @author bju
+ *
+ */
+public class DateFormatter {
+
+ private final String timeString;
+ private final String dateString;
+ private final String dailyHeader;
+
+ public DateFormatter(final Date date) {
+ SimpleDateFormat sdf = new SimpleDateFormat(Preferences.get().getTimeFormat());
+ timeString = sdf.format(date);
+ sdf = new SimpleDateFormat("EEE dd.MM.yy");
+ dateString = sdf.format(date);
+ dailyHeader = DateFormat.getDateInstance(DateFormat.FULL).format(date);
+ }
+
+ public DateFormatter(final long seconds) {
+ this(new Date(seconds * 1000));
+ }
+
+ public DateFormatter(final Calendar cal) {
+ this(cal.getTime());
+ }
+
+ public String getDateString() {
+ return dateString;
+ }
+
+ public String getTimeString() {
+ return timeString;
+ }
+
+ public String getDailyHeader() {
+ return dailyHeader;
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/http/HttpHelper.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/http/HttpHelper.java
new file mode 100644
index 0000000..0812f73
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/http/HttpHelper.java
@@ -0,0 +1,263 @@
+package de.bjusystems.vdrmanager.utils.http;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpVersion;
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.HttpEntityWrapper;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Apache HttpClient helper class for performing HTTP requests.
+ *
+ * This class is intentionally *not* bound to any Android classes so that it is easier
+ * to develop and test. Use calls to this class inside Android AsyncTask implementations
+ * (or manual Thread-Handlers) to make HTTP requests asynchronous and not block the UI Thread.
+ *
+ * TODO cookies
+ * TODO multi-part binary data
+ * TODO follow 302s?
+ * TODO shutdown connection mgr? - client.getConnectionManager().shutdown();
+ *
+ * @author ccollins
+ *
+ */
+public class HttpHelper {
+
+ private static final String CONTENT_TYPE = "Content-Type";
+ private static final int POST_TYPE = 1;
+ private static final int GET_TYPE = 2;
+ private static final String GZIP = "gzip";
+ private static final String ACCEPT_ENCODING = "Accept-Encoding";
+
+ public static final String MIME_FORM_ENCODED = "application/x-www-form-urlencoded";
+ public static final String MIME_TEXT_PLAIN = "text/plain";
+ public static final String HTTP_RESPONSE = "HTTP_RESPONSE";
+ public static final String HTTP_RESPONSE_ERROR = "HTTP_RESPONSE_ERROR";
+
+ private final DefaultHttpClient client;
+ private final ResponseHandler<String> responseHandler;
+
+ /**
+ * Constructor.
+ *
+ */
+ public HttpHelper() {
+
+ final HttpParams params = new BasicHttpParams();
+ params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
+ params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, HTTP.UTF_8);
+ params.setParameter(CoreProtocolPNames.USER_AGENT, "Apache-HttpClient/Android");
+ params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 15000);
+ params.setParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false);
+ final SchemeRegistry schemeRegistry = new SchemeRegistry();
+ schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+ schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
+ final ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
+ client = new DefaultHttpClient(cm, params);
+
+ // add gzip decompressor to handle gzipped content in responses
+ // (default we *do* always send accept encoding gzip header in request)
+ client.addResponseInterceptor(new HttpResponseInterceptor() {
+ public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException {
+ final HttpEntity entity = response.getEntity();
+ final Header contentEncodingHeader = entity.getContentEncoding();
+ if (contentEncodingHeader != null) {
+ final HeaderElement[] codecs = contentEncodingHeader.getElements();
+ for (int i = 0; i < codecs.length; i++) {
+ if (codecs[i].getName().equalsIgnoreCase(HttpHelper.GZIP)) {
+ response.setEntity(new GzipDecompressingEntity(response.getEntity()));
+ return;
+ }
+ }
+ }
+ }
+ });
+
+ responseHandler = new BasicResponseHandler();
+ }
+
+ /**
+ * Perform a simple HTTP GET operation.
+ *
+ */
+ public String performGet(final String url) {
+ return performRequest(null, url, null, null, null, null, HttpHelper.GET_TYPE);
+ }
+
+ /**
+ * Perform an HTTP GET operation with user/pass and headers.
+ *
+ */
+ public String performGet(final String url, final String user, final String pass,
+ final Map<String, String> additionalHeaders) {
+ return performRequest(null, url, user, pass, additionalHeaders, null, HttpHelper.GET_TYPE);
+ }
+
+ /**
+ * Perform a simplified HTTP POST operation.
+ *
+ */
+ public String performPost(final String url, final Map<String, String> params) {
+ return performRequest(HttpHelper.MIME_FORM_ENCODED, url, null, null, null, params, HttpHelper.POST_TYPE);
+ }
+
+ /**
+ * Perform an HTTP POST operation with user/pass, headers, request
+ parameters,
+ * and a default content-type of "application/x-www-form-urlencoded."
+ *
+ */
+ public String performPost(final String url, final String user, final String pass,
+ final Map<String, String> additionalHeaders, final Map<String, String> params) {
+ return performRequest(HttpHelper.MIME_FORM_ENCODED, url, user, pass, additionalHeaders, params,
+ HttpHelper.POST_TYPE);
+ }
+
+ /**
+ * Perform an HTTP POST operation with flexible parameters (the
+ complicated/flexible version of the method).
+ *
+ */
+ public String performPost(final String contentType, final String url, final String user, final String pass,
+ final Map<String, String> additionalHeaders, final Map<String, String> params) {
+ return performRequest(contentType, url, user, pass, additionalHeaders, params, HttpHelper.POST_TYPE);
+ }
+
+ //
+ // private methods
+ //
+ private String performRequest(final String contentType, final String url, final String user, final String pass,
+ final Map<String, String> headers, final Map<String, String> params, final int requestType) {
+
+ // add user and pass to client credentials if present
+ if ((user != null) && (pass != null)) {
+ client.getCredentialsProvider().setCredentials(AuthScope.ANY,
+ new UsernamePasswordCredentials(user, pass));
+ }
+
+ // process headers using request interceptor
+ final Map<String, String> sendHeaders = new HashMap<String, String>();
+ // add encoding header for gzip if not present
+ if (!sendHeaders.containsKey(HttpHelper.ACCEPT_ENCODING)) {
+ sendHeaders.put(HttpHelper.ACCEPT_ENCODING, HttpHelper.GZIP);
+ }
+ if ((headers != null) && (headers.size() > 0)) {
+ sendHeaders.putAll(headers);
+ }
+ if (requestType == HttpHelper.POST_TYPE) {
+ sendHeaders.put(HttpHelper.CONTENT_TYPE, contentType);
+ }
+ if (sendHeaders.size() > 0) {
+ client.addRequestInterceptor(new HttpRequestInterceptor() {
+ public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
+ for (final String key : sendHeaders.keySet()) {
+ if (!request.containsHeader(key)) {
+ request.addHeader(key, sendHeaders.get(key));
+ }
+ }
+ }
+ });
+ }
+
+ // handle POST or GET request respectively
+ HttpRequestBase method = null;
+ if (requestType == HttpHelper.POST_TYPE) {
+ method = new HttpPost(url);
+ // data - name/value params
+ List<NameValuePair> nvps = null;
+ if ((params != null) && (params.size() > 0)) {
+ nvps = new ArrayList<NameValuePair>();
+ for (final Map.Entry<String, String> entry : params.entrySet()) {
+ nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
+ }
+ }
+ if (nvps != null) {
+ try {
+ final HttpPost methodPost = (HttpPost) method;
+ methodPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Error peforming HTTP request: " + e.getMessage(), e);
+ }
+ }
+ } else if (requestType == HttpHelper.GET_TYPE) {
+ method = new HttpGet(url);
+ }
+
+
+ // execute request
+ return execute(method);
+ }
+
+ private synchronized String execute(final HttpRequestBase method) {
+ String response = null;
+ // execute method returns?!? (rather than async) - do it here sync, and wrap async elsewhere
+ try {
+ response = client.execute(method, responseHandler);
+ } catch (final ClientProtocolException e) {
+ response = HttpHelper.HTTP_RESPONSE_ERROR + " - " + e.getClass().getSimpleName() + " " + e.getMessage();
+ //e.printStackTrace();
+ } catch (final IOException e) {
+ response = HttpHelper.HTTP_RESPONSE_ERROR + " - " + e.getClass().getSimpleName() + " " + e.getMessage();
+ //e.printStackTrace();
+ }
+ return response;
+ }
+
+ static class GzipDecompressingEntity extends HttpEntityWrapper {
+ public GzipDecompressingEntity(final HttpEntity entity) {
+ super(entity);
+ }
+
+ @Override
+ public InputStream getContent() throws IOException, IllegalStateException {
+ // the wrapped entity's getContent() decides about repeatability
+ final InputStream wrappedin = wrappedEntity.getContent();
+ return new GZIPInputStream(wrappedin);
+ }
+
+ @Override
+ public long getContentLength() {
+ // length of ungzipped content is not known
+ return -1;
+ }
+ }
+}
+
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/AliveClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/AliveClient.java
new file mode 100644
index 0000000..0d5dca9
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/AliveClient.java
@@ -0,0 +1,47 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import de.bjusystems.vdrmanager.data.AliveState;
+
+/**
+ * Class for retrieving informations about the running program
+ * @author bju
+ *
+ */
+public class AliveClient extends SvdrpClient<AliveState> {
+
+ /**
+ * Constructor
+ * @param certificateProblemListener
+ */
+ public AliveClient(final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ }
+
+ /**
+ * Starts the EPG request
+ * @param parameter parameter for lste
+ */
+ @Override
+ public void run() {
+ runCommand("aliv");
+ }
+
+ @Override
+ public AliveState parseAnswer(final String line) {
+
+ if (line.startsWith("200")) {
+ return AliveState.ALIVE;
+ }
+ if (line.startsWith("400")) {
+ return AliveState.DEAD;
+ }
+ return AliveState.UNKNOWN;
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return 0;
+ }
+
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/CertificateProblemListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/CertificateProblemListener.java
new file mode 100644
index 0000000..ac0ac1e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/CertificateProblemListener.java
@@ -0,0 +1,39 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import java.security.cert.X509Certificate;
+
+import android.app.Activity;
+
+/**
+ * Interface for reporting problems with the SSL certificate
+ * @author bju
+ *
+ */
+public interface CertificateProblemListener {
+
+ /**
+ * Possible user decisions on certificate problems
+ */
+ public enum CertificateProblemAction {
+
+ /** Abort the connection */
+ ABORT,
+ /** Accept the certificate this time */
+ ACCEPT_ONCE,
+ /** Accept the certificate forever */
+ ACCEPT_FOREVER
+ }
+
+ /**
+ * Reports the certificate problem and waits for a user decision
+ * @param chain Certificate trust chain
+ * @param authType authentication type
+ */
+ CertificateProblemAction reportProblem(final X509Certificate[] chain, final String authType);
+
+ /**
+ * Gets the current activity
+ * @return activity
+ */
+ Activity getCurrentActivity();
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/ChannelClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/ChannelClient.java
new file mode 100644
index 0000000..c3761c4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/ChannelClient.java
@@ -0,0 +1,184 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Preferences;
+
+/**
+ * Class for retrieving informations about the running program
+ *
+ * @author bju
+ *
+ */
+public class ChannelClient extends SvdrpClient<Channel> implements
+SvdrpListener, SvdrpResultListener<Channel> {
+
+ private static final ArrayList<String> channelGroups = new ArrayList<String>();
+
+ private static final ArrayList<String> channelSources = new ArrayList<String>();
+
+ private static LinkedHashMap<String, ArrayList<Channel>> groupChannels = new LinkedHashMap<String, ArrayList<Channel>>();
+
+ private static TreeMap<String, ArrayList<Channel>> providerChannels = new TreeMap<String, ArrayList<Channel>>();
+
+ private static TreeMap<String, ArrayList<Channel>> sourceChannels = new TreeMap<String, ArrayList<Channel>>();
+
+ private static ArrayList<Channel> channels = new ArrayList<Channel>();
+
+ private static Map<String, Channel> idChannels = new HashMap<String, Channel>();
+
+ public static Map<String, Channel> getIdChannels() {
+ return idChannels;
+ }
+
+ private static boolean inited = false;
+
+ public ChannelClient(final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ // if (useCache == false) {
+ // clearCache();
+ // }
+ addSvdrpListener(this);
+ addSvdrpResultListener(this);
+ }
+
+ public static void clearCache() {
+ channelSources.clear();
+ sourceChannels.clear();
+ channelGroups.clear();
+ groupChannels.clear();
+ providerChannels.clear();
+ channels.clear();
+ idChannels.clear();
+ inited = false;
+ }
+
+ public static ArrayList<String> getChannelGroups() {
+ return channelGroups;
+ }
+
+ public static ArrayList<String> getChannelSources() {
+ return channelSources;
+ }
+
+
+ public static HashMap<String, ArrayList<Channel>> getGroupChannels() {
+ return groupChannels;
+ }
+
+ public static TreeMap<String, ArrayList<Channel>> getProviderChannels() {
+ return providerChannels;
+ }
+
+ public static TreeMap<String, ArrayList<Channel>> getSourceChannels() {
+ return sourceChannels;
+ }
+
+ public static ArrayList<Channel> getChannels() {
+ return channels;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param host
+ * host
+ * @param port
+ * port
+ * @param ssl
+ * use ssl
+ */
+ // public ChannelClient() {
+ // this(true);
+ //
+ // }
+
+ /**
+ * Starts the EPG request
+ *
+ * @param parameter
+ * parameter for lste
+ */
+ @Override
+ synchronized public void run() {
+ if (inited == true) {
+ informListener(SvdrpEvent.CACHE_HIT);
+ } else {
+ runCommand("channels " + Preferences.get().getChannels());
+ }
+ }
+
+ @Override
+ public Channel parseAnswer(final String line) {
+ return new Channel(line);
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_channels_loading;
+ }
+
+ ArrayList<Channel> currentChannels = new ArrayList<Channel>();
+
+ String currentGroup;
+
+ private void addSourceChannel(final Channel c){
+ ArrayList<Channel> channels = sourceChannels.get(c.getSource());
+
+ if(channels == null){
+ channels = new ArrayList<Channel>();
+ sourceChannels.put(c.getSource(), channels);
+ channelSources.add(c.getSource());
+ }
+ channels.add(c);
+ }
+
+ private void received(final Channel c) {
+
+ if (c.isGroupSeparator()) {
+ currentGroup = c.getName();
+ channelGroups.add(currentGroup);
+ currentChannels = new ArrayList<Channel>();
+ groupChannels.put(c.getName(), currentChannels);
+ } else {
+
+ addSourceChannel(c);
+
+ if (channelGroups.isEmpty()) {// receiver channel with no previous
+ // group
+ channelGroups.add("");
+ groupChannels.put("", currentChannels);
+ }
+
+ c.setGroup(currentGroup);
+ channels.add(c);
+ idChannels.put(c.getId(), c);
+ currentChannels.add(c);
+ final String provider = c.getProvider();
+ ArrayList<Channel> pchannels = providerChannels.get(provider);
+ if (pchannels == null) {
+ pchannels = new ArrayList<Channel>();
+ providerChannels.put(provider, pchannels);
+ }
+ pchannels.add(c);
+ }
+ }
+
+ @Override
+ public void svdrpEvent(final Channel c) {
+ received(c);
+ }
+
+ @Override
+ public void svdrpEvent(final SvdrpEvent event) {
+ if (event == SvdrpEvent.FINISHED_SUCCESS) {
+ inited = true;
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/DelRecordingClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/DelRecordingClient.java
new file mode 100644
index 0000000..522e285
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/DelRecordingClient.java
@@ -0,0 +1,49 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Recording;
+
+/**
+ * Class for deleting a record
+ * @author lado
+ *
+ */
+public class DelRecordingClient extends SvdrpClient<Recording> {
+
+ /** current recording */
+ Recording recording;
+
+ /**
+ * Constructor
+ * Recording
+ */
+ public DelRecordingClient(final Recording recording, final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ this.recording = recording;
+ }
+
+ /**
+ * Starts the request
+ */
+ @Override
+ public void run() {
+
+ final StringBuilder command = new StringBuilder();
+
+ command.append("drecording ");
+ command.append(recording.toCommandLine());
+ runCommand(command.toString());
+ }
+
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_timer_save;
+ }
+
+ @Override
+ protected Recording parseAnswer(final String line) {
+ return null;
+ }
+}
+
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/EpgClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/EpgClient.java
new file mode 100644
index 0000000..a3d5b7e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/EpgClient.java
@@ -0,0 +1,92 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Epg;
+import de.bjusystems.vdrmanager.data.EpgSearchParams;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.data.Timer;
+
+/**
+ * Class for retrieving informations about the running program
+ * @author bju
+ *
+ */
+public class EpgClient extends SvdrpClient<Epg> {
+
+ /** Time to retrieve EPG for */
+ private String time;
+ /** Channel to retrieve EPG for */
+ private Channel channel;
+ /** Search parameters to retrieve EPG for */
+ private EpgSearchParams search;
+ /** Last read EPG */
+ private Epg lastEpg;
+
+ /**
+ * Constructor
+ */
+ private EpgClient(final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ this.time = null;
+ this.channel = null;
+ this.search = null;
+ }
+
+ /**
+ * Constructor
+ * @param time time to search for epg events
+ */
+ public EpgClient(final String time, final CertificateProblemListener certificateProblemListener) {
+ this(certificateProblemListener);
+ this.time = time;
+ }
+
+ /**
+ * Constructor
+ * @param channel channel to search for epg events
+ */
+ public EpgClient(final Channel channel, final CertificateProblemListener certificateProblemListener) {
+ this(certificateProblemListener);
+ this.channel = channel;
+ }
+
+ public EpgClient(final EpgSearchParams search, final CertificateProblemListener certificateProblemListener) {
+ this(certificateProblemListener);
+ this.search = search;
+ }
+
+ /**
+ * Starts the EPG request
+ * @param parameter parameter for lste
+ */
+ @Override
+ public void run() {
+ if (time != null) {
+ runCommand(String.format("tevents %s %s", time, Preferences.getPreferences().getChannels()));
+ } else if (channel != null) {
+ runCommand(String.format("cevents %s", channel.getNumber()));
+ } else if (search != null) {
+ runCommand(String.format("search %s:%s", Preferences.get().getChannels(), search.toCommandLine()));
+ }
+ }
+
+ @Override
+ public Epg parseAnswer(final String line) {
+
+ if (line.startsWith("E")) {
+ lastEpg = new Epg(line);
+ return lastEpg;
+ } else if (line.startsWith("T")) {
+ lastEpg.setTimer(new Timer(line));
+ }
+ return null;
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_whatson_loading;
+ }
+
+}
+
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/MySSLSocketFactory.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/MySSLSocketFactory.java
new file mode 100644
index 0000000..6c87faa
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/MySSLSocketFactory.java
@@ -0,0 +1,253 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+import de.bjusystems.vdrmanager.app.VdrManagerApp;
+
+
+/**
+ * SSLSocketFactory
+ * @author bju
+ */
+public class MySSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory {
+
+ /** The key store file */
+ private final String keyStoreFile = "KeyStore";
+
+ /** the key store */
+ private KeyStore appKeyStore;
+
+ /** the real socket factory */
+ private final SSLSocketFactory sslFactory;
+
+ /** the trust managers */
+ private X509TrustManager[] trustManagers;
+
+ /** the current activity */
+ private final Activity activity;
+
+ public MySSLSocketFactory(final boolean acceptAllCertificates, final CertificateProblemListener certProblemListener)
+ throws KeyManagementException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException {
+
+ super(null);
+
+ // save context
+ this.activity = certProblemListener.getCurrentActivity();
+
+ // accept all host names
+ this.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ // load the key store
+ initKeyStore();
+
+ // initialize the trust managers
+ if (acceptAllCertificates) {
+ initInsecureTrustManagers();
+ } else {
+ initSecureTrustManagers(certProblemListener);
+ }
+
+ // create SSL context
+ final SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, trustManagers, new SecureRandom());
+
+ // create the real factory
+ sslFactory = context.getSocketFactory();
+
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ return sslFactory.createSocket();
+ }
+
+ /**
+ * initialize the key store
+ * @return KeyStore
+ * @throws KeyStoreException
+ */
+ private void initKeyStore() throws KeyStoreException {
+
+ try {
+ appKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+
+ try {
+ final InputStream stream = activity.openFileInput(keyStoreFile);
+ appKeyStore.load(stream, null);
+ } catch (final FileNotFoundException e) {
+ appKeyStore.load(null);
+ }
+ } catch (final Exception e) {
+ throw new KeyStoreException(e);
+ }
+ }
+
+ /**
+ * initialize the trust managers validating certificates
+ * @param acceptAllCertificates accept all certificates
+ * @param certProblemListener listener to inform about certificate problems
+ * @throws NoSuchAlgorithmException
+ * @throws KeyStoreException
+ */
+ private void initSecureTrustManagers(final CertificateProblemListener certProblemListener) throws NoSuchAlgorithmException, KeyStoreException {
+
+ final List<X509TrustManager> trustManagerList = new ArrayList<X509TrustManager>();
+
+ // initialize the trust manager accepting certificates contained in the session key store
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
+ trustManagerFactory.init(((VdrManagerApp)activity.getApplication()).getSessionKeyStore());
+ X509TrustManager trustManager = getTrustManager(trustManagerFactory);
+ if (trustManager != null) {
+ trustManagerList.add(trustManager);
+ }
+
+ // initialize the trust manager accepting certificates contained in the permanent key store
+ trustManagerFactory = TrustManagerFactory.getInstance("X509");
+ trustManagerFactory.init(appKeyStore);
+ trustManager = getTrustManager(trustManagerFactory);
+ if (trustManager != null) {
+ trustManagerList.add(trustManager);
+ }
+
+ // initialize the trust manager accepting certificates accepted from the system
+ trustManagerFactory = TrustManagerFactory.getInstance("X509");
+ trustManagerFactory.init((KeyStore)null);
+ trustManager = getTrustManager(trustManagerFactory);
+ if (trustManager != null) {
+ trustManagerList.add(trustManager);
+ }
+
+ trustManagers = new X509TrustManager[] {
+ new X509TrustManager() {
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ @Override
+ public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
+ CertificateException lastException = null;
+ for(final X509TrustManager tm : trustManagerList) {
+ try {
+ tm.checkServerTrusted(chain, authType);
+ return;
+ } catch (final CertificateException e) {
+ lastException = e;
+ }
+ }
+
+ switch (certProblemListener.reportProblem(chain, authType)) {
+ case ACCEPT_ONCE:
+ saveCertificate(chain, authType, false);
+ return;
+ case ACCEPT_FOREVER:
+ saveCertificate(chain, authType, true);
+ return;
+ default:
+ if (lastException != null) {
+ throw lastException;
+ }
+ break;
+ }
+
+
+ throw new CertificateException("Certificate not validated");
+ }
+
+ @Override
+ public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
+ // NOP
+ }
+ }
+ };
+ }
+
+ /**
+ * initializes the trust managers validating nothing
+ */
+ private void initInsecureTrustManagers() {
+
+ trustManagers = new X509TrustManager[] {
+ new X509TrustManager() {
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ @Override
+ public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
+ }
+ }
+ };
+ }
+
+ /**
+ * finds the X509 trust manager
+ * @param trustManagerFactory TrustManager factory
+ * @return X509 trust manager
+ */
+ private X509TrustManager getTrustManager(final TrustManagerFactory trustManagerFactory) {
+
+ for(final TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
+ if (trustManager instanceof X509TrustManager) {
+ return (X509TrustManager) trustManager;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Saves the certificate
+ * @param chain certificate chain
+ * @param authType authentication type
+ */
+ private void saveCertificate(final X509Certificate[] chain, final String authType, final boolean permanently) {
+
+ // get the certificate alias
+ final String alias = chain[0].getSubjectDN().toString();
+
+ // key store to use
+ final KeyStore saveKeyStore = permanently ? appKeyStore : ((VdrManagerApp)activity.getApplication()).getSessionKeyStore();
+
+ // store the certificate for this alias
+ try {
+ saveKeyStore.setCertificateEntry(alias, chain[0]);
+
+ // the session key store is not saved
+ if (permanently) {
+ final FileOutputStream stream = activity.openFileOutput(keyStoreFile, Context.MODE_PRIVATE);
+ saveKeyStore.store(stream, null);
+ }
+ } catch (final Exception e) {
+ Log.e(getClass().getName(), "Can't save certificate for ' " + alias + "' as trusted");
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/RecordingClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/RecordingClient.java
new file mode 100644
index 0000000..f201ed9
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/RecordingClient.java
@@ -0,0 +1,31 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Recording;
+
+public class RecordingClient extends SvdrpClient<Recording> {
+
+ /**
+ * Constructor
+ * @param certificateProblemListener
+ */
+ public RecordingClient(final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ }
+
+ @Override
+ protected Recording parseAnswer(final String line) {
+ return new Recording(line);
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_recordings_loading;
+ }
+
+ @Override
+ public synchronized void run() {
+ runCommand("recordings");
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SetTimerClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SetTimerClient.java
new file mode 100644
index 0000000..8daab75
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SetTimerClient.java
@@ -0,0 +1,90 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Timer;
+
+/**
+ * Class for retrieving informations about the running program
+ * @author bju
+ *
+ */
+public class SetTimerClient extends SvdrpClient<Timer> {
+
+ public enum TimerOperation {
+ CREATE("C"),//
+ DELETE("D"),//
+ MODIFY("M"),//
+ TOGGLE("T"),//
+ ;
+ private String command;
+ private TimerOperation(final String command){
+ this.command = command;
+ }
+ public String getCommand(){
+ return this.command;
+ }
+
+ }
+
+ /** channel names for timer */
+ Timer newTimer;
+
+ Timer oldTimer;
+
+ /** timer should be deleted */
+ private final TimerOperation timerOperation;
+
+ /**
+ * @param newTimer Das was modifiziert angelegt wird
+ */
+ public SetTimerClient(final Timer newTimer, final TimerOperation op, final CertificateProblemListener certificateProblemListener) {
+ this(newTimer, null, op, certificateProblemListener);
+ }
+
+ /**
+ * @param newTimer
+ * @param oldTimer this is original Timer, if any (modify)
+ * @param op
+ */
+ public SetTimerClient(final Timer newTimer, final Timer oldTimer, final TimerOperation op, final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ this.newTimer = newTimer;
+ this.oldTimer = oldTimer;
+ this.timerOperation = op;
+ }
+
+
+ /**
+ * Starts the request
+ */
+ @Override
+ public void run() {
+
+ final StringBuilder command = new StringBuilder();
+
+ command.append("timer ");
+ command.append(timerOperation.getCommand());
+ //command.append(oldTimer.getNumber());
+ command.append(" ");
+ command.append(newTimer.toCommandLine());
+ if(timerOperation == TimerOperation.MODIFY){
+ command.append("#|#|#").append(oldTimer.toCommandLine());
+ }
+ //timer D 1:1:2011-11-11:1513:1710:50:99:Mirrors 2
+ //timer C 1:1:2011-11-11:2223:2250:50:99:Zapping
+ //timer T 0:1:2011-11-11:2013:2230:50:99:So spielt das Leben
+ //timer M
+ runCommand(command.toString());
+ }
+
+ @Override
+ public Timer parseAnswer(final String line) {
+ return new Timer(line);
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_timer_save;
+ }
+}
+
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpAsyncTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpAsyncTask.java
new file mode 100644
index 0000000..505b084
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpAsyncTask.java
@@ -0,0 +1,165 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.os.AsyncTask;
+
+public class SvdrpAsyncTask<Result, Client extends SvdrpClient<Result>> extends
+AsyncTask<Void, Object, Void> implements SvdrpListener,
+SvdrpExceptionListener, SvdrpResultListener<Result> {
+
+ Client client;
+
+ Throwable ex;
+
+ SvdrpEvent event;
+
+ List<SvdrpListener> eventListeners = new ArrayList<SvdrpListener>();
+
+ List<SvdrpExceptionListener> exceptionListeners = new ArrayList<SvdrpExceptionListener>();
+
+ List<SvdrpFinishedListener<Result>> finishedListeners = new ArrayList<SvdrpFinishedListener<Result>>();
+
+ //CertificateProblemListener certificateProblemListener = null;
+
+ //CertificateProblemAction certificateProblemAction;
+
+ public SvdrpAsyncTask(final Client client) {
+ this.client = client;
+ this.client.addSvdrpListener(this);
+ this.client.addSvdrpExceptionListener(this);
+ this.client.addSvdrpResultListener(this);
+ }
+
+ protected List<Result> results = new ArrayList<Result>();
+
+
+ public List<Result> getResults() {
+ return results;
+ }
+
+
+ /**
+ * Adds the listener to the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void addSvdrpListener(final SvdrpListener listener) {
+ // client.addSvdrpListener(listener);
+ eventListeners.add(listener);
+ }
+
+ /**
+ * Adds the listener to the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void addSvdrpResultListener(
+ final SvdrpResultListener<Result> listener) {
+ client.addSvdrpResultListener(listener);
+ }
+
+ public void addSvdrpFinishedListener(final SvdrpFinishedListener<Result> liste) {
+ finishedListeners.add(liste);
+ }
+
+ /**
+ * Adds the listener to the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void addSvdrpExceptionListener(final SvdrpExceptionListener listener) {
+ // client.addSvdrpExceptionListener(listener);
+ exceptionListeners.add(listener);
+ }
+
+ public void run() {
+ execute();
+ }
+
+ @Override
+ protected Void doInBackground(final Void... params) {
+ client.run();
+ return null;
+ }
+
+ @Override
+ protected void onProgressUpdate(final Object... values) {
+
+ if (values.length == 1) {
+
+ if (List.class.isAssignableFrom(values[0].getClass())) {
+ for (final SvdrpFinishedListener<Result> listener : finishedListeners) {
+ listener.finished((List<Result>) values[0]);
+ }
+ return;
+ }
+
+ for (final SvdrpListener listener : eventListeners) {
+ listener.svdrpEvent((SvdrpEvent) values[0]);
+ }
+
+ } else if (values.length == 2) {
+ for (final SvdrpExceptionListener listener : exceptionListeners) {
+ listener.svdrpEvent((SvdrpEvent) values[0], (Throwable) values[1]);
+ }
+ }
+
+ /*
+ * switch (event) { case CONNECTING: {
+ * setMessage(R.string.progress_connect); progress.show(); break; }
+ *
+ * case LOGGED_IN: { setMessage(R.string.progress_login); break; }
+ *
+ * case COMMAND_SENT: { setMessage(client.getProgressTextId()); break; }
+ *
+ * case DISCONNECTING: { setMessage(R.string.progress_disconnect);
+ * break; }
+ *
+ * case ERROR: case CONNECTION_TIMEOUT: case CONNECT_ERROR: case
+ * FINISHED_ABNORMALY: case CACHE_HIT: case FINISHED_SUCCESS: case
+ * LOGIN_ERROR: { progress.dismiss(); }
+ *
+ * }
+ */
+ }
+
+ // @Override
+ // protected void onPostExecute(SvdrpException exception) {
+ // for (SvdrpExceptionListener l : exceptionListeners) {
+ // l.svdrpEvent(exception.getEvent(), ex);
+ // }
+ // }
+
+ @Override
+ public void svdrpEvent(final SvdrpEvent event) {
+ publishProgress(event);;
+ if(event == SvdrpEvent.FINISHED_SUCCESS){
+ publishProgress(results);
+ }
+ }
+
+ // @Override
+ // public void finished(ListResult> results) {
+ // publishProgress(results);
+ // }
+
+ @Override
+ public void svdrpEvent(final SvdrpEvent event, final Throwable t) {
+ publishProgress(event, t);
+ }
+
+
+ @Override
+ public void svdrpEvent(final Result result) {
+ results.add(result);
+ }
+
+ public void abort(){
+ client.abort();
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java
new file mode 100644
index 0000000..0198bb0
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpClient.java
@@ -0,0 +1,533 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+
+import android.sax.StartElementListener;
+import android.util.Log;
+import de.bjusystems.vdrmanager.app.C;
+import de.bjusystems.vdrmanager.data.Preferences;
+
+/**
+ * Class for SVDRP communication
+ *
+ * @author bju
+ *
+ */
+public abstract class SvdrpClient<Result> {
+
+ private final String TAG = getClass().getName();
+
+ /** Socket for connection to SVDRP */
+ private Socket socket;
+ /** Output stream for sending commands */
+ private OutputStream outputStream;
+ /** Input stream for reading answer lines */
+ private InputStream inputStream;
+ /** flag for stopping the current request */
+ private boolean abort;
+ /** listener for events */
+ private final List<SvdrpListener> svdrpListeners = new ArrayList<SvdrpListener>();
+ /** Listener for start */
+ private final List<SvdrpStartListener> startListeners = new ArrayList<SvdrpStartListener>();
+ /** listeners for results */
+ private final List<SvdrpResultListener<Result>> svdrpResultListeners = new ArrayList<SvdrpResultListener<Result>>();
+ /** listeners for exceptions */
+ private final List<SvdrpExceptionListener> svdrpExceptionListeners = new ArrayList<SvdrpExceptionListener>();
+ /** listeners for finished job */
+ private final List<SvdrpFinishedListener<Result>> svdrpFinishedListeners = new ArrayList<SvdrpFinishedListener<Result>>();
+ /** listener for certificate problems set by caller */
+ private final CertificateProblemListener certificateProblemListener;
+
+ /** list of results */
+ // private final List<Result> results = new ArrayList<Result>();
+ /** should the listener be informed about each received result */
+ // private boolean resultInfoEnabled = false;
+ /**
+ * @return true if the client has result
+ */
+ // public boolean hasResults(){
+ // return results.isEmpty() == false;
+ // }
+
+ private final Timer watchDog = new Timer();
+
+ private String encoding;
+
+
+ // private NativeDES crypt = new NativeDES();
+
+ public boolean isConnected() {
+ if (socket == null) {
+ return false;
+ }
+ return socket.isConnected();
+ }
+
+ /**
+ * Parse received answer line
+ *
+ * @param line
+ * line
+ * @return received data object or null if not completed yet
+ */
+ protected abstract Result parseAnswer(String line);
+
+ public abstract int getProgressTextId();
+
+ public abstract void run();
+
+ /**
+ * Constructor
+ *
+ * @param prefs
+ * Preferences
+ */
+ protected SvdrpClient(
+ final CertificateProblemListener certificateProblemListener) {
+ // results.clear();
+ this.certificateProblemListener = certificateProblemListener;
+ encoding = Preferences.get().getEncoding();
+ }
+
+ /**
+ * Remove all listeners
+ */
+ public void clearListener() {
+ svdrpExceptionListeners.clear();
+ svdrpListeners.clear();
+ svdrpResultListeners.clear();
+ }
+
+ /**
+ * Adds the listener to the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void addSvdrpListener(final SvdrpListener listener) {
+ svdrpListeners.add(listener);
+ }
+
+ public void addStartListener(final SvdrpStartListener listener) {
+ startListeners.add(listener);
+ }
+
+ /**
+ * Adds the listener to the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void addSvdrpResultListener(
+ final SvdrpResultListener<Result> listener) {
+ svdrpResultListeners.add(listener);
+ }
+
+ /**
+ * Adds the listener to the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void addSvdrpFinishedListener(
+ final SvdrpFinishedListener<Result> listener) {
+ svdrpFinishedListeners.add(listener);
+ }
+
+ /**
+ * Adds the listener to the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void addSvdrpExceptionListener(final SvdrpExceptionListener listener) {
+ svdrpExceptionListeners.add(listener);
+ }
+
+ /**
+ * Removes the listener from the list of listeners
+ *
+ * @param listener
+ * listener
+ */
+ public void removeSvdrpListener(final SvdrpListener listener) {
+ svdrpListeners.remove(listener);
+ }
+
+ public void remoeStartListener(final SvdrpStartListener listener) {
+ startListeners.remove(listener);
+ }
+
+ public void removeSvdrpResultListener(
+ final SvdrpResultListener<Result> listener) {
+ svdrpResultListeners.remove(listener);
+ }
+
+ public void removeSvdrpExceptionListener(
+ final SvdrpExceptionListener listener) {
+ svdrpExceptionListeners.remove(listener);
+ }
+
+ /**
+ * Cancel the current request
+ */
+ public void abort() {
+ abort = true;
+ try {
+ if (isConnected()) {
+ socket.shutdownInput();
+ socket.shutdownOutput();
+ socket.close();
+ }
+ } catch (final Exception ex) {
+ Log.w(TAG, ex);
+ }
+
+ }
+
+ // /**
+ // * Gets the list of results
+ // *
+ // * @return results
+ // */
+ // public List<Result> getResults() {
+ // return results;
+ // }
+
+ /**
+ * Connect to SVDRP
+ *
+ * @param host
+ * host
+ * @param port
+ * port
+ * @param ssl
+ * use SSL
+ * @throws IOException
+ * on errors
+ */
+ protected boolean connect() throws IOException {
+
+ final Preferences prefs = Preferences.get();
+ try {
+ // connect
+ informListener(SvdrpEvent.CONNECTING);
+
+ if (Preferences.get().isSecure()) {
+ socket = new MySSLSocketFactory(false,
+ certificateProblemListener).createSocket();
+ } else {
+ socket = new Socket();
+ }
+
+ socket.connect(
+ new InetSocketAddress(prefs.getHost(), prefs
+ .getPort()),
+ prefs.getConnectionTimeout() * 1000);// 8 secs for connect
+ if (abort) {
+ informListener(SvdrpEvent.ABORTED);
+ }
+ //
+ socket.setSoTimeout(prefs.getReadTimeout() * 1000);// 15 sec for
+ // each read
+ final long delay = C.ONE_MINUTE_IN_MILLIS * prefs.getTimeout() * 60; // in
+ // 3
+ // minutes
+ // we
+ // abort
+ // the
+ // communication
+ watchDog.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ Log.w(TAG, "Aborted after " + delay + " ms");
+ abort = true;
+ }
+ }, delay);
+ informListener(SvdrpEvent.CONNECTED);
+
+ // create streams
+ outputStream = socket.getOutputStream();
+ inputStream = socket.getInputStream();
+
+ } catch (final SocketTimeoutException sote) {
+ Log.w(TAG, sote);
+ if (abort) {
+ informListener(SvdrpEvent.ABORTED);
+ } else {
+ informListener(SvdrpEvent.CONNECTION_TIMEOUT);
+ }
+ return false;
+ } catch (final Exception e) {
+
+ Log.w(TAG, e);
+ if (abort) {
+ informListener(SvdrpEvent.ABORTED);
+ } else {
+ informListener(SvdrpEvent.CONNECT_ERROR);
+ }
+ return false;
+ }
+
+ // password needed?
+ informListener(SvdrpEvent.LOGIN);
+ writeLine("passwd " + prefs.getPassword());
+ if (!readLine().startsWith("!OK")) {
+ informListener(SvdrpEvent.LOGIN_ERROR);
+ disconnect();
+ return false;
+ } else {
+ informListener(SvdrpEvent.LOGGED_IN);
+ }
+ return true;
+ }
+
+ /**
+ * Disconnect from SVDRP if connected
+ *
+ * @throws IOException
+ * on errors
+ */
+ protected void disconnect() throws IOException {
+ informListener(SvdrpEvent.DISCONNECTING);
+ if (socket != null && socket.isConnected()) {
+ writeLine("quit");
+ socket.close();
+ socket = null;
+ }
+ informListener(SvdrpEvent.DISCONNECTED);
+ }
+
+ /**
+ * Sends one line to SVDRP
+ *
+ * @param line
+ * line of text
+ * @throws IOException
+ * on errors
+ */
+ protected void writeLine(final String line) throws IOException {
+
+ final String command = line + "\r\n";
+ // if (false && Preferences.get().isSecure()) {
+ // command = crypt.encrypt(command, Preferences.get().getPassword());
+ // }
+ final byte[] bytes = command.getBytes("utf-8");
+ outputStream.write(bytes);
+ outputStream.flush();
+ }
+
+ /**
+ * Reads one line from SVDRP
+ *
+ * @return line read
+ * @throws IOException
+ * on errors
+ */
+ protected String readLine() throws IOException {
+
+ // handle not gzipped input
+ final ByteArrayOutputStream lineBytes = new ByteArrayOutputStream();
+
+ for (;;) {
+
+ // read next char
+ final int d = inputStream.read();
+ if (d < 0) {
+ break;
+ }
+ final char c = (char) d;
+
+ // skip '\r'
+ if (c == '\r') {
+ continue;
+ }
+
+ // with '\n' the line is completed
+ if (c == '\n') {
+ break;
+ }
+
+ // remember char
+ lineBytes.write(c);
+ }
+
+ String line = null;
+ try {
+ line = lineBytes.toString(encoding);
+ lineBytes.close();
+ } catch (final UnsupportedEncodingException usex) {
+ Log.w(TAG, usex);
+ line = lineBytes.toString();
+ }
+ // if (false && Preferences.get().isSecure()) {
+ // line = crypt.decrypt(line, Preferences.get().getPassword());
+ // }
+ return line;
+ }
+
+ public void runCommand(final String command) {
+
+ try {
+
+ // reset cancel flag
+ abort = false;
+
+ // clear results
+ // results.clear();
+
+ // connect
+ final boolean connected = connect();
+ if (!connected) {
+ return;
+ }
+
+ // activate compression
+ Log.i(TAG, "Activate compression");
+ writeLine("compress");
+
+ // send command
+ informListener(SvdrpEvent.COMMAND_SENDING);
+ writeLine(command);
+ informListener(SvdrpEvent.COMMAND_SENT);
+ Log.i(TAG, SvdrpEvent.COMMAND_SENT + ":" + command);
+
+ // get the answer for the compress command or
+ // the first line of the answer for the command
+ String line = readLine();
+ if (line.startsWith("!OK")) {
+ final String[] words = line.split(" ");
+ if (words.length > 1) {
+ final String mode = words[1].toUpperCase(Locale.getDefault());
+ if (mode.equals("ZLIB")) {
+ Log.i(TAG, "ZLIB compression activated");
+ inputStream = new InflaterInputStream(inputStream);
+ } else if (mode.equals("GZIP")) {
+ Log.i(TAG, "GZIP compression activated");
+ inputStream = new GZIPInputStream(inputStream);
+ } else {
+ Log.i(TAG, "NO compression activated");
+ }
+ }
+ line = readLine();
+ } else {
+ Log.i(TAG, "NO compression activated");
+ }
+
+ // correct answer?
+ if (!line.startsWith("START")) {
+ Log.w(TAG, line);
+ throw new IOException("Answer not wellformed: " + line);
+ }
+
+ if (line.startsWith("START|")) {
+ informStartListener(line.substring(6));
+ }
+
+ // read answer lines
+ for (; !abort;) {
+
+ // get next line
+ line = readLine();
+ if (line.length() == 0) {
+ break;
+ }
+
+ // last line?
+ if (line.startsWith("END")) {
+ break;
+ }
+
+ // error?
+ if (line.startsWith("!ERROR")) {
+ Log.w(TAG, line);
+ String msg;
+ if (line.startsWith("!ERROR:")) {
+ msg = line.substring(7);
+ } else {
+ msg = line;
+ }
+ disconnect();
+ informListener(SvdrpEvent.ERROR, new SvdrpException(msg));
+ break;
+ }
+
+ // delegate analysis
+ Result result = null;
+ try {
+ result = parseAnswer(line);
+
+ } catch (final Exception ex) {
+ Log.w(TAG, ex);
+ disconnect();
+ Log.w(TAG, "line: " + line);
+ informListener(SvdrpEvent.ERROR, ex);
+ return;
+ }
+ if (result != null) {
+ informListener(result);
+ // results.add(result);
+ // if (resultInfoEnabled) {
+
+ // }
+ }
+
+ }
+
+ // disconnect
+ disconnect();
+
+ if (abort) {
+ informListener(SvdrpEvent.ABORTED);
+ } else {
+ informListener(SvdrpEvent.FINISHED_SUCCESS);
+ }
+
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ informListener(SvdrpEvent.FINISHED_ABNORMALY, e);
+ }
+ }
+
+ // public void setResultInfoEnabled(final boolean resultInfoEnabled) {
+ // this.resultInfoEnabled = resultInfoEnabled;
+ // }
+
+ protected void informListener(final SvdrpEvent event, final Throwable e) {
+ for (final SvdrpExceptionListener listener : svdrpExceptionListeners) {
+ listener.svdrpEvent(event, e);
+ }
+ }
+
+ protected void informListener(final SvdrpEvent event) {
+ for (final SvdrpListener listener : svdrpListeners) {
+ listener.svdrpEvent(event);
+ }
+ }
+
+ protected void informListener(final Result result) {
+ for (final SvdrpResultListener<Result> listener : svdrpResultListeners) {
+ listener.svdrpEvent(result);
+ }
+ }
+
+ protected void informStartListener(final String result) {
+ for (final SvdrpStartListener l : startListeners) {
+ l.start(result);
+ }
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpEvent.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpEvent.java
new file mode 100644
index 0000000..a64f905
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpEvent.java
@@ -0,0 +1,24 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+public enum SvdrpEvent {
+ CONNECTING,
+ CONNECTED,
+ CONNECT_ERROR,
+ LOGIN,
+ LOGGED_IN,
+ LOGIN_ERROR,
+ COMMAND_SENDING,
+ COMMAND_SENT,
+ //RESULT_RECEIVED,
+ DISCONNECTING,
+ DISCONNECTED,
+ FINISHED_ABNORMALY,
+ FINISHED_SUCCESS,
+ ABORTED,
+ ERROR,
+ CACHE_HIT,
+ CONNECTION_TIMEOUT
+ //
+ ;
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpException.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpException.java
new file mode 100644
index 0000000..fe0824e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpException.java
@@ -0,0 +1,35 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+/**
+ * Class for exception caused by SVDRP errors
+ * @author bju
+ *
+ */
+@SuppressWarnings("serial")
+public class SvdrpException extends Exception {
+
+ SvdrpEvent event;
+
+ public SvdrpEvent getEvent() {
+ return event;
+ }
+
+ public void setEvent(SvdrpEvent event) {
+ this.event = event;
+ }
+
+ public SvdrpException(SvdrpEvent event, String text) {
+ super(text);
+ }
+ public SvdrpException(String text) {
+ this(null,text);
+ }
+
+ public SvdrpException(String text, Throwable cause) {
+ super(text, cause);
+ }
+
+ public SvdrpException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpExceptionListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpExceptionListener.java
new file mode 100644
index 0000000..1483e6d
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpExceptionListener.java
@@ -0,0 +1,5 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+public interface SvdrpExceptionListener {
+ void svdrpEvent(SvdrpEvent event, Throwable t);
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpFinishedListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpFinishedListener.java
new file mode 100644
index 0000000..7fba637
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpFinishedListener.java
@@ -0,0 +1,9 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import java.util.List;
+
+public interface SvdrpFinishedListener<Result> {
+
+ public void finished(List<Result> results);
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpListener.java
new file mode 100644
index 0000000..88ea35e
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpListener.java
@@ -0,0 +1,9 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+
+public interface SvdrpListener{
+
+
+ void svdrpEvent(SvdrpEvent event);
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpResultListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpResultListener.java
new file mode 100644
index 0000000..fbea3c3
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpResultListener.java
@@ -0,0 +1,6 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+public interface SvdrpResultListener<Result> {
+
+ void svdrpEvent(Result result);
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpStartListener.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpStartListener.java
new file mode 100644
index 0000000..e7c6700
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SvdrpStartListener.java
@@ -0,0 +1,15 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+public interface SvdrpStartListener {
+ /**
+ *
+ * START is read
+ *
+ * The Start line may contain addition information separated by '|'
+ *
+ * So START|some meta info
+ * @param meta may be null
+ */
+ void start(String meta);
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SwitchChannelClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SwitchChannelClient.java
new file mode 100644
index 0000000..65a4d59
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/SwitchChannelClient.java
@@ -0,0 +1,57 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import de.bjusystems.vdrmanager.R;
+
+
+/**
+ * Class for switching a channel by SETCHANNEL <NR|CHID>
+ *
+ * @author lado
+ *
+ */
+public class SwitchChannelClient extends SvdrpClient<String> {
+
+ private Integer nr;
+
+ private String chid;
+
+ public SwitchChannelClient(final Integer nr, final CertificateProblemListener certificateProblemListener){
+ this(certificateProblemListener);
+ this.nr = nr;
+ }
+
+ public SwitchChannelClient(final String chid, final CertificateProblemListener certificateProblemListener){
+ this(certificateProblemListener);
+ this.chid = chid;
+ }
+
+ /**
+ * Constructor
+ */
+ public SwitchChannelClient(final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ }
+
+ /**
+ * Starts the wakeup request
+ */
+ @Override
+ public void run() {
+ if(nr != null){
+ runCommand("SETCHANNEL " + String.valueOf(nr));
+ } else {
+ runCommand("SETCHANNEL " + chid);
+ }
+ }
+
+ @Override
+ public String parseAnswer(final String line) {
+ return line;
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_switching;
+ }
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/TimerClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/TimerClient.java
new file mode 100644
index 0000000..1f55750
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/svdrp/TimerClient.java
@@ -0,0 +1,47 @@
+package de.bjusystems.vdrmanager.utils.svdrp;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Channel;
+import de.bjusystems.vdrmanager.data.Timer;
+
+/**
+ * Class for retrieving informations about the running program
+ * @author bju
+ *
+ */
+public class TimerClient extends SvdrpClient<Timer> {
+
+ /** channel names for timer */
+ Map<String, Channel> channels;
+
+ /**
+ * Constructor
+ * @param certificateProblemListener CertificateProblemListener
+ */
+ public TimerClient(final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ this.channels = new HashMap<String, Channel>();
+ }
+
+ /**
+ * Starts the EPG request
+ */
+ @Override
+ public synchronized void run() {
+ runCommand("timers conflicts");
+ }
+
+ @Override
+ public Timer parseAnswer(final String line) {
+ return new Timer(line);
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return R.string.progress_timers_loading;
+ }
+}
+
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/AsyncWakeupTask.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/AsyncWakeupTask.java
new file mode 100644
index 0000000..5268eb4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/AsyncWakeupTask.java
@@ -0,0 +1,114 @@
+package de.bjusystems.vdrmanager.utils.wakeup;
+
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.widget.Toast;
+import de.bjusystems.vdrmanager.R;
+import de.bjusystems.vdrmanager.data.Preferences;
+import de.bjusystems.vdrmanager.utils.http.HttpHelper;
+
+public class AsyncWakeupTask extends AsyncTask<Object, WakeupProgress, Void> {
+
+ private static final String TAG = "AsyncWakeupTask";
+ /** Context */
+ private final Context context;
+ /** Progress dialog */
+ private ProgressDialog progressDialog;
+
+ public AsyncWakeupTask(final Context context) {
+ this.context = context;
+ }
+
+ Wakeuper getWakeuper() {
+ // Preferences
+ final Preferences prefs = Preferences.getPreferences();
+
+ if (Preferences.getPreferences().getWakeupMethod().equals("url")) {
+ return new Wakeuper() {
+ public void wakeup(Context context) {
+ // wakeup by http request
+ final HttpHelper httpHelper = new HttpHelper();
+ httpHelper.performGet(prefs.getWakeupUrl(),
+ prefs.getWakeupUser(), prefs.getWakeupPassword(),
+ null);
+ // request sent
+
+ }
+ };
+ } else {
+ return new Wakeuper() {
+ public void wakeup(Context context) throws Exception {
+ WakeOnLanClient.wake(prefs.getVdrMac());
+ }
+ };
+ }
+ }
+
+ @Override
+ protected Void doInBackground(final Object... params) {
+
+ // open progress dialog
+ publishProgress(new WakeupProgress(WakeupProgressType.WAKEUP_STARTED));
+
+ boolean ok = false;
+ String msg = null;
+ try {
+ getWakeuper().wakeup(context);
+ ok = true;
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ msg = e.getMessage();
+ }
+
+ // close progress
+ publishProgress(new WakeupProgress(WakeupProgressType.WAKEUP_FINISHED));
+ if (ok) {
+ publishProgress(new WakeupProgress(WakeupProgressType.WAKEUP_OK));
+ } else {
+ publishProgress(new WakeupProgress(WakeupProgressType.WAKEUP_ERROR,
+ msg));
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onProgressUpdate(final WakeupProgress... values) {
+ super.onProgressUpdate(values);
+
+ WakeupProgress t = values[0];
+
+ switch (t.getState()) {
+ case WAKEUP_STARTED:
+ final CharSequence message = context
+ .getText(R.string.progress_wakeup_sending);
+ progressDialog = ProgressDialog.show(context, "", message);
+ break;
+ case WAKEUP_FINISHED:
+ progressDialog.dismiss();
+ break;
+ case WAKEUP_OK:
+ showToast(R.string.progress_wakeup_sent);
+ break;
+ case WAKEUP_ERROR:
+ showToast(R.string.progress_wakeup_error, t.getInfo());
+ break;
+ }
+ }
+
+ private void showToast(final int textId, Object... args) {
+
+ final CharSequence text;
+ if (args.length > 1) {
+ text = context.getString(textId);
+ } else {
+ text = context.getString(textId, args);
+ }
+ final int duration = Toast.LENGTH_SHORT;
+ final Toast toast = Toast.makeText(context, text, duration);
+ toast.show();
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeOnLanClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeOnLanClient.java
new file mode 100644
index 0000000..737195a
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeOnLanClient.java
@@ -0,0 +1,140 @@
+/*
+ MythDroid: Android MythTV Remote
+ Copyright (C) 2009-2010 foobum@gmail.com
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.bjusystems.vdrmanager.utils.wakeup;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+
+import de.bjusystems.vdrmanager.data.Preferences;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+/** Send WakeOnLan packets */
+public class WakeOnLanClient {
+
+ static private byte[] addr;
+ static private byte[] buf = new byte[17 * 6];
+
+ /**
+ * Send a wake on lan packet
+ *
+ * @param hwaddr
+ * String containing the MAC address of the target
+ */
+ public static void wake(String hwaddr) throws Exception {
+ addr = parseAddr(hwaddr);
+ for (int i = 0; i < 6; i++)
+ buf[i] = (byte) 0xff;
+ for (int i = 6; i < buf.length; i += 6)
+ System.arraycopy(addr, 0, buf, i, 6);
+ // if (.debug)
+ Log.d("WakeOnLAN", //$NON-NLS-1$
+ "Sending WOL packets to 255.255.255.255 " + //$NON-NLS-1$
+ "ports 7, 9 for MAC address " + hwaddr //$NON-NLS-1$
+ );
+
+ String broadcast = Preferences.get().getWolCustomBroadcast();
+ if (TextUtils.isEmpty(broadcast) == true) {
+ broadcast = "255.255.255.255";//$NON-NLS-1$
+ }
+
+ InetAddress address = InetAddress.getByName(broadcast);
+ DatagramPacket dgram = new DatagramPacket(buf, buf.length, address, 9);
+ DatagramSocket sock = new DatagramSocket();
+ sock.setBroadcast(true);
+ sock.send(dgram);
+ dgram.setPort(7);
+ sock.send(dgram);
+ sock.close();
+ }
+
+ /**
+ * Try to extract a hardware MAC address from a given IP address using the
+ * ARP cache (/proc/net/arp).<br>
+ * <br>
+ * We assume that the file has this structure:<br>
+ * <br>
+ * IP address HW type Flags HW address Mask Device 192.168.18.11 0x1 0x2
+ * 00:04:20:06:55:1a * eth0 192.168.18.36 0x1 0x2 00:22:43:ab:2a:5b * eth0
+ *
+ * @param ip
+ * @return the MAC from the ARP cache
+ */
+ public static String getMacFromArpCache(String ip) {
+ if (ip == null)
+ return null;
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader("/proc/net/arp"));
+ String line;
+ while ((line = br.readLine()) != null) {
+ String[] splitted = line.split(" +");
+ if (splitted != null && splitted.length >= 4
+ && ip.equals(splitted[0])) {
+ // Basic sanity check
+ String mac = splitted[3];
+ if (mac.matches("..:..:..:..:..:..")) {
+ return mac;
+ } else {
+ return null;
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * Parse a MAC Addresse
+ *
+ * @param addr
+ * @return
+ * @throws IllegalArgumentException
+ */
+ public static byte[] parseAddr(String addr) throws IllegalArgumentException {
+ byte[] bytes = new byte[6];
+ String[] hex = addr.split(":"); //$NON-NLS-1$
+ if (hex.length != 6)
+ throw new IllegalArgumentException("Invalid MAC address"); //$NON-NLS-1$
+ try {
+ for (int i = 0; i < 6; i++)
+ bytes[i] = (byte) Integer.parseInt(hex[i], 16);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(
+ "Invalid hex digit in MAC address" //$NON-NLS-1$
+ );
+ }
+ return bytes;
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgress.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgress.java
new file mode 100644
index 0000000..f86608a
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgress.java
@@ -0,0 +1,26 @@
+package de.bjusystems.vdrmanager.utils.wakeup;
+
+public class WakeupProgress {
+
+ public WakeupProgress(WakeupProgressType state) {
+ this(state, null);
+ }
+
+ public WakeupProgress(WakeupProgressType state, String info) {
+ this.state = state;
+ this.info = info;
+ }
+
+ private WakeupProgressType state;
+
+ private String info;
+
+ public WakeupProgressType getState() {
+ return state;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgressType.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgressType.java
new file mode 100644
index 0000000..28dcf38
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupProgressType.java
@@ -0,0 +1,8 @@
+package de.bjusystems.vdrmanager.utils.wakeup;
+
+public enum WakeupProgressType {
+ WAKEUP_STARTED,
+ WAKEUP_OK,
+ WAKEUP_ERROR,
+ WAKEUP_FINISHED
+}
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupUrlClient.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupUrlClient.java
new file mode 100644
index 0000000..231554a
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/WakeupUrlClient.java
@@ -0,0 +1,53 @@
+package de.bjusystems.vdrmanager.utils.wakeup;
+
+import de.bjusystems.vdrmanager.data.WakeupState;
+import de.bjusystems.vdrmanager.utils.svdrp.CertificateProblemListener;
+import de.bjusystems.vdrmanager.utils.svdrp.SvdrpClient;
+
+/**
+ * Class for retrieving informations about the running program
+ * @author bju
+ *
+ */
+public class WakeupUrlClient extends SvdrpClient<WakeupState> {
+
+ private WakeupState state;
+
+ /**
+ * Constructor
+ */
+ public WakeupUrlClient(final CertificateProblemListener certificateProblemListener) {
+ super(certificateProblemListener);
+ }
+
+ /**
+ * Starts the wakeup request
+ */
+ @Override
+ public void run() {
+ runCommand("wake");
+ }
+
+ @Override
+ public WakeupState parseAnswer(final String line) {
+
+ if (line.startsWith("200")) {
+ state = WakeupState.OK;
+ } else if (line.startsWith("400")) {
+ state = WakeupState.FAILED;
+ } else {
+ state = WakeupState.ERROR;
+ }
+ return state;
+ }
+
+ @Override
+ public int getProgressTextId() {
+ return 0;
+ }
+
+ public WakeupState getState() {
+ return state;
+ }
+
+} \ No newline at end of file
diff --git a/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/Wakeuper.java b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/Wakeuper.java
new file mode 100644
index 0000000..5b149e4
--- /dev/null
+++ b/vdrmanager/app/src/main/java/de/bjusystems/vdrmanager/utils/wakeup/Wakeuper.java
@@ -0,0 +1,20 @@
+package de.bjusystems.vdrmanager.utils.wakeup;
+
+import android.content.Context;
+
+/**
+ * @author lado
+ * Interface to implement several wakeup methods
+ */
+public interface Wakeuper {
+
+ /**
+ *
+ *
+ *
+ * @param context
+ * @throws Exception if the wakeup process is not ended ok. Please note, on a.e. WOL, there can
+ * not be no guarantee, that the host has been woken up.
+ */
+ void wakeup(Context context) throws Exception;
+}
diff --git a/vdrmanager/app/src/main/java/org/fueri/reeldroid/network/DeviceManager.java b/vdrmanager/app/src/main/java/org/fueri/reeldroid/network/DeviceManager.java
new file mode 100644
index 0000000..427ff35
--- /dev/null
+++ b/vdrmanager/app/src/main/java/org/fueri/reeldroid/network/DeviceManager.java
@@ -0,0 +1,150 @@
+package org.fueri.reeldroid.network;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+
+/***
+ *
+ * DeviceManager singleton
+ *
+ * @author Patrick Fürlinger fueri@fueri.ch
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ *
+ */
+public class DeviceManager {
+
+ private static final int MAX_PING_TIMEOUT_MS = 40;
+
+ public static interface ProgressListener {
+ void publish(String currentIP);
+ }
+
+ /**
+ *
+ * @param defaultPort
+ * @return
+ */
+ public static List<String> findVDRHosts(Context context,
+ Integer defaultPort, ProgressListener listener) {
+
+ if (defaultPort == null) {
+ defaultPort = 6420;
+ }
+
+ List<String> list = new ArrayList<String>();
+ ConnectivityManager conMgr = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ WifiManager wifiManager = (WifiManager) context
+ .getSystemService(Context.WIFI_SERVICE);
+
+ boolean netStatus = conMgr
+ .getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()
+ || conMgr.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET)
+ .isConnected();
+
+ if (netStatus == false) {
+ return list;
+ }
+
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ String baseIp = intToBaseIp(wifiInfo.getIpAddress());
+
+ for (Integer i = 1; i <= 254; i++) {
+ InetAddress ia;
+ try {
+ String ipHost = baseIp + i.toString();
+ if (listener != null) {
+ listener.publish(ipHost);
+ }
+ ia = InetAddress.getByName(ipHost);
+ if (findHost(ia, defaultPort, MAX_PING_TIMEOUT_MS) == false) {
+ continue;
+ }
+ list.add(ipHost);
+ } catch (UnknownHostException e) {
+ }
+
+ }
+
+ return list;
+ }
+
+ /**
+ *
+ * @param ip
+ * @param port
+ * @return
+ */
+ public static boolean findHost(String ip, int port, int pingTimeout) {
+ InetAddress ia = null;
+ try {
+ ia = InetAddress.getByName(ip);
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ return findHost(ia, port, pingTimeout);
+ }
+
+ /**
+ *
+ * @param ip
+ * @param port
+ * @return
+ */
+ public static boolean findHost(InetAddress ip, int port, int pingTimeout) {
+
+ try {
+ InetAddress address = ip;
+
+ boolean reachable = address.isReachable(pingTimeout);
+
+ if (reachable == false) {
+ return false;
+ }
+
+ Socket socket = new Socket();
+ socket.connect(new InetSocketAddress(ip, port), 1000);
+ socket.close();
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * return base ip as a string.
+ *
+ * @param ip_address
+ * @return String
+ */
+ public static String intToBaseIp(int ip_address) {
+ return (ip_address & 0xFF) + "." + ((ip_address >> 8) & 0xFF) + "."
+ + ((ip_address >> 16) & 0xFF) + ".";
+
+ }
+
+}