diff options
102 files changed, 5853 insertions, 1499 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c14d4bfe7..cce892747 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -45,28 +45,12 @@ <activity android:name=".activity.MainActivity" android:configChanges="keyboardHidden|orientation" + android:launchMode="singleTask" android:label="@string/app_name"> - <meta-data - android:name="android.app.default_searchable" - android:value="de.danoeh.antennapod.activity.SearchActivity"/> - <meta-data - android:name="android.app.searchable" - android:resource="@xml/searchable"/> - <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> - </activity> - <activity - android:name="de.danoeh.antennapod.activity.AddFeedActivity" - android:configChanges="keyboardHidden|orientation" - android:label="@string/add_new_feed_label" - android:windowSoftInputMode="adjustResize"> - <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value="de.danoeh.antennapod.activity.MainActivity"/> - <intent-filter> <action android:name="android.intent.action.VIEW"/> @@ -114,6 +98,18 @@ <data android:mimeType="text/plain"/> </intent-filter> + + </activity> + <activity + android:name="de.danoeh.antennapod.activity.AddFeedActivity" + android:configChanges="keyboardHidden|orientation" + android:label="@string/add_new_feed_label" + android:windowSoftInputMode="adjustResize"> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value="de.danoeh.antennapod.activity.MainActivity"/> + + </activity> <activity android:name="de.danoeh.antennapod.activity.FeedItemlistActivity" @@ -121,12 +117,6 @@ <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="de.danoeh.antennapod.activity.MainActivity"/> - <meta-data - android:name="android.app.default_searchable" - android:value="de.danoeh.antennapod.activity.SearchActivity"/> - <meta-data - android:name="android.app.searchable" - android:resource="@xml/searchable"/> </activity> <activity android:name="de.danoeh.antennapod.activity.ItemviewActivity" @@ -296,19 +286,6 @@ android:label="@string/opml_import_label"> </activity> <activity - android:name=".activity.SearchActivity" - android:configChanges="keyboardHidden|orientation" - android:label="@string/search_results_label" - android:launchMode="singleTop"> - <intent-filter> - <action android:name="android.intent.action.SEARCH"/> - </intent-filter> - - <meta-data - android:name="android.app.searchable" - android:resource="@xml/searchable"/> - </activity> - <activity android:name=".activity.MiroGuideMainActivity" android:label="@string/miro_guide_label"> <meta-data diff --git a/assets/Roboto-Light.ttf b/assets/Roboto-Light.ttf Binary files differnew file mode 100644 index 000000000..13bf13af0 --- /dev/null +++ b/assets/Roboto-Light.ttf diff --git a/assets/Roboto.ttf b/assets/Roboto.ttf Binary files differnew file mode 100644 index 000000000..0ba95c98c --- /dev/null +++ b/assets/Roboto.ttf diff --git a/res/drawable-hdpi/ic_action_overflow.png b/res/drawable-hdpi/ic_action_overflow.png Binary files differnew file mode 100644 index 000000000..002fc4bfb --- /dev/null +++ b/res/drawable-hdpi/ic_action_overflow.png diff --git a/res/drawable-hdpi/ic_action_overflow_dark.png b/res/drawable-hdpi/ic_action_overflow_dark.png Binary files differnew file mode 100644 index 000000000..c8792cbe2 --- /dev/null +++ b/res/drawable-hdpi/ic_action_overflow_dark.png diff --git a/res/drawable-hdpi/ic_drag_handle.png b/res/drawable-hdpi/ic_drag_handle.png Binary files differnew file mode 100755 index 000000000..38ec201de --- /dev/null +++ b/res/drawable-hdpi/ic_drag_handle.png diff --git a/res/drawable-hdpi/ic_drag_handle_dark.png b/res/drawable-hdpi/ic_drag_handle_dark.png Binary files differnew file mode 100755 index 000000000..e96d23252 --- /dev/null +++ b/res/drawable-hdpi/ic_drag_handle_dark.png diff --git a/res/drawable-hdpi/ic_drawer.png b/res/drawable-hdpi/ic_drawer.png Binary files differnew file mode 100644 index 000000000..c59f601ca --- /dev/null +++ b/res/drawable-hdpi/ic_drawer.png diff --git a/res/drawable-hdpi/ic_drawer_dark.png b/res/drawable-hdpi/ic_drawer_dark.png Binary files differnew file mode 100644 index 000000000..6614ea4f4 --- /dev/null +++ b/res/drawable-hdpi/ic_drawer_dark.png diff --git a/res/drawable-mdpi/ic_action_overflow.png b/res/drawable-mdpi/ic_action_overflow.png Binary files differnew file mode 100644 index 000000000..6f0fb23f4 --- /dev/null +++ b/res/drawable-mdpi/ic_action_overflow.png diff --git a/res/drawable-mdpi/ic_action_overflow_dark.png b/res/drawable-mdpi/ic_action_overflow_dark.png Binary files differnew file mode 100644 index 000000000..b4a4a221f --- /dev/null +++ b/res/drawable-mdpi/ic_action_overflow_dark.png diff --git a/res/drawable-mdpi/ic_drag_handle.png b/res/drawable-mdpi/ic_drag_handle.png Binary files differnew file mode 100755 index 000000000..4afbdc67d --- /dev/null +++ b/res/drawable-mdpi/ic_drag_handle.png diff --git a/res/drawable-mdpi/ic_drag_handle_dark.png b/res/drawable-mdpi/ic_drag_handle_dark.png Binary files differnew file mode 100755 index 000000000..2b25c4101 --- /dev/null +++ b/res/drawable-mdpi/ic_drag_handle_dark.png diff --git a/res/drawable-mdpi/ic_drawer.png b/res/drawable-mdpi/ic_drawer.png Binary files differnew file mode 100644 index 000000000..1ed2c56ee --- /dev/null +++ b/res/drawable-mdpi/ic_drawer.png diff --git a/res/drawable-mdpi/ic_drawer_dark.png b/res/drawable-mdpi/ic_drawer_dark.png Binary files differnew file mode 100644 index 000000000..b05c026c1 --- /dev/null +++ b/res/drawable-mdpi/ic_drawer_dark.png diff --git a/res/drawable-xhdpi/ic_action_overflow.png b/res/drawable-xhdpi/ic_action_overflow.png Binary files differnew file mode 100644 index 000000000..7ba4e10ea --- /dev/null +++ b/res/drawable-xhdpi/ic_action_overflow.png diff --git a/res/drawable-xhdpi/ic_action_overflow_dark.png b/res/drawable-xhdpi/ic_action_overflow_dark.png Binary files differnew file mode 100644 index 000000000..5d8af5d63 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_overflow_dark.png diff --git a/res/drawable-xhdpi/ic_drag_handle.png b/res/drawable-xhdpi/ic_drag_handle.png Binary files differnew file mode 100755 index 000000000..5bdcac342 --- /dev/null +++ b/res/drawable-xhdpi/ic_drag_handle.png diff --git a/res/drawable-xhdpi/ic_drag_handle_dark.png b/res/drawable-xhdpi/ic_drag_handle_dark.png Binary files differnew file mode 100755 index 000000000..d341c7c82 --- /dev/null +++ b/res/drawable-xhdpi/ic_drag_handle_dark.png diff --git a/res/drawable-xhdpi/ic_drawer.png b/res/drawable-xhdpi/ic_drawer.png Binary files differnew file mode 100644 index 000000000..a5fa74def --- /dev/null +++ b/res/drawable-xhdpi/ic_drawer.png diff --git a/res/drawable-xhdpi/ic_drawer_dark.png b/res/drawable-xhdpi/ic_drawer_dark.png Binary files differnew file mode 100644 index 000000000..bcf49dd73 --- /dev/null +++ b/res/drawable-xhdpi/ic_drawer_dark.png diff --git a/res/drawable-xxhdpi/ic_action_overflow.png b/res/drawable-xxhdpi/ic_action_overflow.png Binary files differnew file mode 100644 index 000000000..5a603b6bc --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_overflow.png diff --git a/res/drawable-xxhdpi/ic_action_overflow_dark.png b/res/drawable-xxhdpi/ic_action_overflow_dark.png Binary files differnew file mode 100644 index 000000000..e22049b1e --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_overflow_dark.png diff --git a/res/drawable-xxhdpi/ic_drag_handle.png b/res/drawable-xxhdpi/ic_drag_handle.png Binary files differnew file mode 100755 index 000000000..f834699c6 --- /dev/null +++ b/res/drawable-xxhdpi/ic_drag_handle.png diff --git a/res/drawable-xxhdpi/ic_drag_handle_dark.png b/res/drawable-xxhdpi/ic_drag_handle_dark.png Binary files differnew file mode 100755 index 000000000..a9408bc9d --- /dev/null +++ b/res/drawable-xxhdpi/ic_drag_handle_dark.png diff --git a/res/drawable-xxhdpi/ic_drawer.png b/res/drawable-xxhdpi/ic_drawer.png Binary files differnew file mode 100644 index 000000000..9c4685d6e --- /dev/null +++ b/res/drawable-xxhdpi/ic_drawer.png diff --git a/res/drawable-xxhdpi/ic_drawer_dark.png b/res/drawable-xxhdpi/ic_drawer_dark.png Binary files differnew file mode 100644 index 000000000..f7e3b3079 --- /dev/null +++ b/res/drawable-xxhdpi/ic_drawer_dark.png diff --git a/res/layout-land/audioplayer_activity.xml b/res/layout-land/audioplayer_activity.xml index 7900e1ced..8f8fdbee3 100644 --- a/res/layout-land/audioplayer_activity.xml +++ b/res/layout-land/audioplayer_activity.xml @@ -1,178 +1,197 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<android.support.v4.widget.DrawerLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/drawer_layout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal" > + android:layout_height="match_parent"> - <FrameLayout - android:id="@+id/contentView" - android:layout_width="0dp" + <LinearLayout + android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="0.5" > - </FrameLayout> + android:orientation="horizontal"> - <RelativeLayout - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="0.5" - android:background="?attr/non_transparent_background" - android:orientation="vertical" > + <FrameLayout + android:id="@+id/contentView" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="0.5"> + </FrameLayout> <RelativeLayout - android:id="@+id/navBar" - android:layout_width="fill_parent" - android:layout_height="60dp" - android:layout_alignParentTop="true" > - - <ImageButton - android:id="@+id/butNavLeft" - android:contentDescription="@string/show_shownotes_label" - android:layout_width="60dp" - android:layout_height="match_parent" - android:layout_alignParentLeft="true" - android:background="?attr/borderless_button" - android:padding="4dp" /> - - <ImageButton - android:id="@+id/butNavRight" - android:contentDescription="@string/show_chapters_label" - android:layout_width="60dp" - android:layout_height="match_parent" - android:layout_alignParentRight="true" - android:background="?attr/borderless_button" - android:padding="4dp" /> - - <TextView - android:id="@+id/txtvTitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginTop="8dp" - android:layout_toLeftOf="@id/butNavRight" - android:layout_toRightOf="@id/butNavLeft" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:maxLines="1" - android:textColor="?android:attr/textColorPrimary" - android:textSize="@dimen/text_size_medium" - android:textStyle="bold" /> - - <TextView - android:id="@+id/txtvFeed" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/txtvTitle" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_toLeftOf="@id/butNavRight" - android:layout_toRightOf="@id/butNavLeft" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:maxLines="1" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_small" /> - </RelativeLayout> - - <View - android:id="@+id/navBarDivider" - android:layout_width="match_parent" - android:layout_height="1dp" - android:layout_below="@id/navBar" - android:background="@color/bright_blue" /> - - <RelativeLayout - android:id="@+id/player_control" - android:layout_width="match_parent" - android:layout_height="80dp" - android:layout_alignParentBottom="true" - android:background="?attr/overlay_background" > - - <ImageButton - android:id="@+id/butPlay" - android:contentDescription="@string/pause_label" - android:layout_width="80dp" - android:layout_height="match_parent" - android:layout_centerHorizontal="true" - android:background="?attr/borderless_button" - android:src="?attr/av_pause" /> - - <ImageButton - android:id="@+id/butRev" - android:contentDescription="@string/rewind_label" - android:layout_width="60dp" - android:layout_height="match_parent" - android:layout_toLeftOf="@id/butPlay" - android:background="?attr/borderless_button" - android:src="?attr/av_rewind" /> - - <ImageButton - android:id="@+id/butFF" - android:contentDescription="@string/fast_forward_label" - android:layout_width="60dp" - android:layout_height="match_parent" - android:layout_toRightOf="@id/butPlay" - android:background="?attr/borderless_button" - android:src="?attr/av_fast_forward" /> - - <Button - android:id="@+id/butPlaybackSpeed" - android:layout_width="60dp" - android:layout_height="match_parent" - android:layout_toRightOf="@id/butFF" - android:background="?attr/borderless_button" - android:src="?attr/av_fast_forward" - android:textColor="@color/gray" - android:textSize="@dimen/text_size_medium" - android:visibility="gone" /> - </RelativeLayout> - - <RelativeLayout - android:id="@+id/playtime_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_above="@id/player_control" - android:layout_alignParentLeft="true" - android:background="?attr/overlay_drawable" > - - <TextView - android:id="@+id/txtvPosition" - android:layout_width="wrap_content" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="0.5" + android:background="?attr/non_transparent_background" + android:orientation="vertical"> + + <RelativeLayout + android:id="@+id/navBar" + android:layout_width="fill_parent" + android:layout_height="60dp" + android:layout_alignParentTop="true"> + + <ImageButton + android:id="@+id/butNavLeft" + android:contentDescription="@string/show_shownotes_label" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:background="?attr/borderless_button" + android:padding="4dp"/> + + <ImageButton + android:id="@+id/butNavRight" + android:contentDescription="@string/show_chapters_label" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_alignParentRight="true" + android:background="?attr/borderless_button" + android:padding="4dp"/> + + <TextView + android:id="@+id/txtvTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_marginTop="8dp" + android:layout_toLeftOf="@id/butNavRight" + android:layout_toRightOf="@id/butNavLeft" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:maxLines="1" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_medium" + android:textStyle="bold"/> + + <TextView + android:id="@+id/txtvFeed" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/txtvTitle" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_toLeftOf="@id/butNavRight" + android:layout_toRightOf="@id/butNavLeft" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:maxLines="1" + android:textColor="?android:attr/textColorSecondary" + android:textSize="@dimen/text_size_small"/> + </RelativeLayout> + + <View + android:id="@+id/navBarDivider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_below="@id/navBar" + android:background="@color/bright_blue"/> + + <RelativeLayout + android:id="@+id/player_control" + android:layout_width="match_parent" + android:layout_height="80dp" + android:layout_alignParentBottom="true" + android:background="?attr/overlay_background"> + + <ImageButton + android:id="@+id/butPlay" + android:contentDescription="@string/pause_label" + android:layout_width="80dp" + android:layout_height="match_parent" + android:layout_centerHorizontal="true" + android:background="?attr/borderless_button" + android:src="?attr/av_pause"/> + + <ImageButton + android:id="@+id/butRev" + android:contentDescription="@string/rewind_label" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_toLeftOf="@id/butPlay" + android:background="?attr/borderless_button" + android:src="?attr/av_rewind"/> + + <ImageButton + android:id="@+id/butFF" + android:contentDescription="@string/fast_forward_label" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_toRightOf="@id/butPlay" + android:background="?attr/borderless_button" + android:src="?attr/av_fast_forward"/> + + <Button + android:id="@+id/butPlaybackSpeed" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_toRightOf="@id/butFF" + android:background="?attr/borderless_button" + android:src="?attr/av_fast_forward" + android:textColor="@color/gray" + android:textSize="@dimen/text_size_medium" + android:visibility="gone"/> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/playtime_layout" + android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_above="@id/player_control" android:layout_alignParentLeft="true" - android:layout_centerVertical="true" - android:layout_marginLeft="8dp" - android:layout_marginTop="16dp" - android:text="@string/position_default_label" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_micro" /> - - <TextView - android:id="@+id/txtvLength" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_centerVertical="true" - android:layout_marginRight="8dp" - android:layout_marginTop="16dp" - android:text="@string/position_default_label" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_micro" /> - - <SeekBar - android:id="@+id/sbPosition" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginTop="16dp" - android:layout_toLeftOf="@id/txtvLength" - android:layout_toRightOf="@id/txtvPosition" - android:max="500" /> + android:background="?attr/overlay_drawable"> + + <TextView + android:id="@+id/txtvPosition" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:layout_marginLeft="8dp" + android:layout_marginTop="16dp" + android:text="@string/position_default_label" + android:textColor="?android:attr/textColorSecondary" + android:textSize="@dimen/text_size_micro"/> + + <TextView + android:id="@+id/txtvLength" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_centerVertical="true" + android:layout_marginRight="8dp" + android:layout_marginTop="16dp" + android:text="@string/position_default_label" + android:textColor="?android:attr/textColorSecondary" + android:textSize="@dimen/text_size_micro"/> + + <SeekBar + android:id="@+id/sbPosition" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_marginTop="16dp" + android:layout_toLeftOf="@id/txtvLength" + android:layout_toRightOf="@id/txtvPosition" + android:max="500"/> + </RelativeLayout> </RelativeLayout> - </RelativeLayout> -</LinearLayout>
\ No newline at end of file + </LinearLayout> + + <ListView + android:id="@+id/nav_list" + android:layout_width="@dimen/drawer_width" + android:layout_height="match_parent" + android:layout_gravity="start" + android:choiceMode="singleChoice" + android:background="?attr/nav_drawer_background" + android:scrollbarStyle="outsideOverlay" + android:paddingLeft="8dp" + android:paddingRight="8dp"/> + +</android.support.v4.widget.DrawerLayout>
\ No newline at end of file diff --git a/res/layout/addfeed.xml b/res/layout/addfeed.xml index bb0842c0f..48c89f802 100644 --- a/res/layout/addfeed.xml +++ b/res/layout/addfeed.xml @@ -20,9 +20,7 @@ android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_margin="16dp" - android:textSize="@dimen/text_size_large" - android:textColor="@color/bright_blue" - android:textStyle="italic" + style="@style/AntennaPod.TextView.Heading" android:text="@string/txtvfeedurl_label"/> <EditText @@ -48,37 +46,34 @@ android:layout_height="wrap_content" android:layout_below="@id/butConfirm" android:layout_margin="8dp" - android:textSize="@dimen/text_size_large" - android:textColor="@color/bright_blue" - android:textStyle="italic" + style="@style/AntennaPod.TextView.Heading" android:text="@string/podcastdirectories_label"/> - <Button - android:id="@+id/butBrowseGpoddernet" + <TextView + android:id="@+id/txtvPodcastDirectoriesDescr" android:layout_width="match_parent" android:layout_height="wrap_content" + android:text="@string/podcastdirectories_descr" + android:textSize="@dimen/text_size_medium" android:layout_below="@id/txtvPodcastDirectories" - android:layout_margin="8dp" - android:text="@string/gpodnet_main_label"/> + android:layout_margin="8dp"/> <Button - android:id="@+id/butBrowseMiroguide" + android:id="@+id/butBrowseGpoddernet" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/butBrowseGpoddernet" + android:layout_below="@id/txtvPodcastDirectoriesDescr" android:layout_margin="8dp" - android:text="@string/miro_guide_label"/> + android:text="@string/browse_gpoddernet_label"/> <TextView android:id="@+id/txtvOpmlImport" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@id/butBrowseMiroguide" + android:layout_below="@id/butBrowseGpoddernet" android:layout_margin="8dp" - android:textSize="@dimen/text_size_large" - android:textColor="@color/bright_blue" - android:textStyle="italic" + style="@style/AntennaPod.TextView.Heading" android:text="@string/opml_import_label"/> <TextView @@ -87,6 +82,7 @@ android:layout_height="wrap_content" android:layout_below="@id/txtvOpmlImport" android:layout_margin="8dp" + android:textSize="@dimen/text_size_medium" android:text="@string/opml_import_txtv_button_lable"/> <Button diff --git a/res/layout/audioplayer_activity.xml b/res/layout/audioplayer_activity.xml index 9b501fbdb..a879aad55 100644 --- a/res/layout/audioplayer_activity.xml +++ b/res/layout/audioplayer_activity.xml @@ -1,173 +1,182 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<android.support.v4.widget.DrawerLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/drawer_layout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="?attr/non_transparent_background" - android:orientation="vertical" > - - <RelativeLayout - android:id="@+id/navBar" - android:layout_width="fill_parent" - android:layout_height="60dp" - android:layout_alignParentTop="true" > - - <ImageButton - android:id="@+id/butNavLeft" - android:contentDescription="@string/show_shownotes_label" - android:layout_width="60dp" - android:layout_height="match_parent" - android:layout_alignParentLeft="true" - android:background="?attr/borderless_button" - android:padding="4dp" /> - - <ImageButton - android:id="@+id/butNavRight" - android:contentDescription="@string/show_chapters_label" - android:layout_width="60dp" - android:layout_height="match_parent" - android:layout_alignParentRight="true" - android:background="?attr/borderless_button" - android:padding="4dp" /> - - <TextView - android:id="@+id/txtvTitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginTop="8dp" - android:layout_toLeftOf="@id/butNavRight" - android:layout_toRightOf="@id/butNavLeft" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:maxLines="1" - android:textColor="?android:attr/textColorPrimary" - android:textSize="@dimen/text_size_small" - android:textStyle="bold" /> - - <TextView - android:id="@+id/txtvFeed" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/txtvTitle" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_toLeftOf="@id/butNavRight" - android:layout_toRightOf="@id/butNavLeft" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" - android:maxLines="1" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_small" /> - </RelativeLayout> - - <View - android:id="@+id/navBarDivider" - android:layout_width="match_parent" - android:layout_height="1dp" - android:layout_below="@id/navBar" - android:background="@color/bright_blue" /> - - <RelativeLayout - android:id="@+id/player_control" - android:layout_width="match_parent" - android:layout_height="80dp" - android:layout_alignParentBottom="true" - android:background="?attr/overlay_background" > - - <ImageButton - android:id="@+id/butPlay" - android:contentDescription="@string/pause_label" - android:layout_width="80dp" - android:layout_height="match_parent" - android:layout_centerHorizontal="true" - android:background="?attr/borderless_button" - android:src="?attr/av_pause" /> - - <ImageButton - android:id="@+id/butRev" - android:contentDescription="@string/rewind_label" - android:layout_width="80dp" - android:layout_height="match_parent" - android:layout_toLeftOf="@id/butPlay" - android:background="?attr/borderless_button" - android:src="?attr/av_rewind" /> - - <ImageButton - android:id="@+id/butFF" - android:contentDescription="@string/fast_forward_label" - android:layout_width="80dp" - android:layout_height="match_parent" - android:layout_toRightOf="@id/butPlay" - android:background="?attr/borderless_button" - android:src="?attr/av_fast_forward" /> - - <Button - android:id="@+id/butPlaybackSpeed" - android:contentDescription="@string/set_playback_speed_label" - android:layout_width="80dp" - android:layout_height="match_parent" - android:layout_toRightOf="@id/butFF" - android:background="?attr/borderless_button" - android:src="?attr/av_fast_forward" - android:textColor="@color/gray" - android:textSize="@dimen/text_size_medium" - android:visibility="gone" /> - </RelativeLayout> - - <RelativeLayout - android:id="@+id/playtime_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_above="@id/player_control" - android:layout_alignParentLeft="true" - android:background="?attr/overlay_drawable" > - - <TextView - android:id="@+id/txtvPosition" - android:layout_width="wrap_content" + android:layout_height="match_parent"> + + <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/non_transparent_background" + android:orientation="vertical"> + + <RelativeLayout + android:id="@+id/navBar" + android:layout_width="fill_parent" + android:layout_height="60dp" + android:layout_alignParentTop="true"> + + <ImageButton + android:id="@+id/butNavLeft" + android:contentDescription="@string/show_shownotes_label" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:background="?attr/borderless_button" + android:padding="4dp"/> + + <ImageButton + android:id="@+id/butNavRight" + android:contentDescription="@string/show_chapters_label" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_alignParentRight="true" + android:background="?attr/borderless_button" + android:padding="4dp"/> + + <TextView + android:id="@+id/txtvTitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:layout_alignParentTop="true" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_marginTop="8dp" + android:layout_toLeftOf="@id/butNavRight" + android:layout_toRightOf="@id/butNavLeft" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:maxLines="2" + android:textColor="?android:attr/textColorPrimary" + android:textSize="16sp" + android:fontFamily="sans-serif-light" + /> + </RelativeLayout> + + <View + android:id="@+id/navBarDivider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_below="@id/navBar" + android:background="@color/bright_blue"/> + + <RelativeLayout + android:id="@+id/player_control" + android:layout_width="match_parent" + android:layout_height="80dp" + android:layout_alignParentBottom="true" + android:background="?attr/overlay_background"> + + <ImageButton + android:id="@+id/butPlay" + android:contentDescription="@string/pause_label" + android:layout_width="80dp" + android:layout_height="match_parent" + android:layout_centerHorizontal="true" + android:background="?attr/borderless_button" + android:src="?attr/av_pause"/> + + <ImageButton + android:id="@+id/butRev" + android:contentDescription="@string/rewind_label" + android:layout_width="80dp" + android:layout_height="match_parent" + android:layout_toLeftOf="@id/butPlay" + android:background="?attr/borderless_button" + android:src="?attr/av_rewind"/> + + <ImageButton + android:id="@+id/butFF" + android:contentDescription="@string/fast_forward_label" + android:layout_width="80dp" + android:layout_height="match_parent" + android:layout_toRightOf="@id/butPlay" + android:background="?attr/borderless_button" + android:src="?attr/av_fast_forward"/> + + <Button + android:id="@+id/butPlaybackSpeed" + android:contentDescription="@string/set_playback_speed_label" + android:layout_width="80dp" + android:layout_height="match_parent" + android:layout_toRightOf="@id/butFF" + android:background="?attr/borderless_button" + android:src="?attr/av_fast_forward" + android:textColor="@color/gray" + android:textSize="@dimen/text_size_medium" + android:visibility="gone"/> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/playtime_layout" + android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_above="@id/player_control" android:layout_alignParentLeft="true" - android:layout_centerVertical="true" - android:layout_marginLeft="8dp" - android:layout_marginTop="16dp" - android:text="@string/position_default_label" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_micro" /> - - <TextView - android:id="@+id/txtvLength" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_centerVertical="true" - android:layout_marginRight="8dp" - android:layout_marginTop="16dp" - android:text="@string/position_default_label" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_micro" /> - - <SeekBar - android:id="@+id/sbPosition" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginTop="16dp" - android:layout_toLeftOf="@id/txtvLength" - android:layout_toRightOf="@id/txtvPosition" - android:max="500" /> - </RelativeLayout> + android:background="?attr/overlay_drawable"> + + <TextView + android:id="@+id/txtvPosition" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:layout_marginLeft="8dp" + android:layout_marginTop="16dp" + android:text="@string/position_default_label" + android:textColor="?android:attr/textColorSecondary" + android:fontFamily="sans-serif-light" + android:textSize="@dimen/text_size_micro"/> + + <TextView + android:id="@+id/txtvLength" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_centerVertical="true" + android:layout_marginRight="8dp" + android:layout_marginTop="16dp" + android:text="@string/position_default_label" + android:textColor="?android:attr/textColorSecondary" + android:fontFamily="sans-serif-light" + android:textSize="@dimen/text_size_micro"/> + + <SeekBar + android:id="@+id/sbPosition" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_marginTop="16dp" + android:layout_toLeftOf="@id/txtvLength" + android:layout_toRightOf="@id/txtvPosition" + android:max="500"/> + </RelativeLayout> + + <FrameLayout + android:id="@+id/contentView" + android:layout_width="match_parent" + android:layout_height="0px" + android:layout_above="@id/playtime_layout" + android:layout_below="@id/navBarDivider"> + </FrameLayout> - <FrameLayout - android:id="@+id/contentView" - android:layout_width="match_parent" - android:layout_height="0px" - android:layout_above="@id/playtime_layout" - android:layout_below="@id/navBarDivider" > - </FrameLayout> + </RelativeLayout> -</RelativeLayout>
\ No newline at end of file + <ListView + android:id="@+id/nav_list" + android:layout_width="@dimen/drawer_width" + android:layout_height="match_parent" + android:layout_gravity="start" + android:choiceMode="singleChoice" + android:background="?attr/nav_drawer_background" + android:scrollbarStyle="outsideOverlay" + android:paddingLeft="8dp" + android:paddingRight="8dp"/> + +</android.support.v4.widget.DrawerLayout>
\ No newline at end of file diff --git a/res/layout/downloaded_episodeslist_item.xml b/res/layout/downloaded_episodeslist_item.xml new file mode 100644 index 000000000..35b3dbf9a --- /dev/null +++ b/res/layout/downloaded_episodeslist_item.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:orientation="horizontal" + android:layout_height="match_parent"> + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:layout_marginRight="16dp"> + + <ImageView + android:id="@+id/imgvImage" + android:contentDescription="@string/cover_label" + android:layout_width="@dimen/thumbnail_length_itemlist" + android:layout_height="@dimen/thumbnail_length_itemlist" + android:layout_alignParentLeft="true" + android:scaleType="centerCrop"/> + + <ImageView + android:id="@+id/statusPlaying" + android:contentDescription="@string/status_playing_label" + android:layout_width="@dimen/status_indicator_width" + android:layout_height="18dp" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:layout_marginLeft="8dp" + android:background="@color/status_playing" + android:gravity="center" + android:padding="2dp" + android:src="@drawable/av_play_dark"/> + + <TextView + android:id="@+id/txtvPublished" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentTop="true" + android:layout_toRightOf="@id/imgvImage" + android:layout_toLeftOf="@id/statusPlaying" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> + + <TextView + android:id="@+id/txtvTitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_below="@id/txtvPublished" + android:layout_marginLeft="8dp" + android:layout_marginRight="4dp" + android:layout_marginTop="2dp" + android:layout_toRightOf="@id/imgvImage" + android:layout_toLeftOf="@id/statusPlaying" + android:ellipsize="end" + android:lines="2" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_small"/> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:layout_toRightOf="@id/imgvImage" + android:orientation="vertical"> + + <RelativeLayout + android:id="@+id/bottom_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/txtvSize" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentLeft="true" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> + </RelativeLayout> + </LinearLayout> + </RelativeLayout> + + <View + android:layout_width="1dp" + android:layout_height="match_parent" + android:background="@drawable/vertical_divider" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"/> + + <ImageButton + android:id="@+id/butSecondaryAction" + android:contentDescription="@string/remove_episode_lable" + android:focusable="false" + android:clickable="false" + android:focusableInTouchMode="false" + android:layout_width="@dimen/listview_secondary_button_width" + android:layout_height="match_parent" + android:background="?attr/borderless_button" + android:src="?attr/content_discard" + /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/downloadlist_item.xml b/res/layout/downloadlist_item.xml index 688a64876..a0a589175 100644 --- a/res/layout/downloadlist_item.xml +++ b/res/layout/downloadlist_item.xml @@ -1,46 +1,73 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" > - - <TextView - android:id="@+id/txtvTitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:layout_marginTop="8dp" - android:textStyle="bold" /> - - <TextView - android:id="@+id/txtvMessage" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="8dp" /> - - <ProgressBar - android:id="@+id/progProgress" - style="?android:attr/progressBarStyleHorizontal" - android:layout_width="match_parent" - android:layout_height="16dp" - android:layout_margin="8dp" /> - - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="8dp" > + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="match_parent" + android:orientation="vertical"> <TextView - android:id="@+id/txtvDownloaded" - android:layout_width="wrap_content" + android:id="@+id/txtvTitle" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentLeft="true" /> + android:layout_marginLeft="8dp" + android:layout_marginTop="8dp" + android:textStyle="bold"/> <TextView - android:id="@+id/txtvPercent" - android:layout_width="wrap_content" + android:id="@+id/txtvMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="8dp"/> + + <ProgressBar + android:id="@+id/progProgress" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="match_parent" + android:layout_height="16dp" + android:layout_margin="8dp"/> + + <RelativeLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentRight="true" /> - </RelativeLayout> + android:layout_margin="8dp"> + + <TextView + android:id="@+id/txtvDownloaded" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true"/> + + <TextView + android:id="@+id/txtvPercent" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true"/> + </RelativeLayout> + + </LinearLayout> + + + <View + android:layout_width="1dp" + android:layout_height="match_parent" + android:background="@drawable/vertical_divider" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"/> + <ImageButton + android:id="@+id/butSecondaryAction" + android:contentDescription="@string/cancel_download_label" + android:focusable="false" + android:clickable="false" + android:focusableInTouchMode="false" + android:layout_width="@dimen/listview_secondary_button_width" + android:layout_height="match_parent" + android:background="?attr/borderless_button" + android:src="?attr/navigation_cancel" + /> </LinearLayout>
\ No newline at end of file diff --git a/res/layout/external_player_fragment.xml b/res/layout/external_player_fragment.xml index d619d7a77..f084ccac1 100644 --- a/res/layout/external_player_fragment.xml +++ b/res/layout/external_player_fragment.xml @@ -1,26 +1,26 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/fragmentLayout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:visibility="gone" > + android:id="@+id/fragmentLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone"> <View android:layout_width="match_parent" - android:layout_height="1.5dp" - android:background="#AAAAAA" /> + android:layout_height="2dp" + android:background="@color/bright_blue"/> <LinearLayout android:layout_width="match_parent" - android:layout_height="wrap_content" > + android:layout_height="wrap_content"> <RelativeLayout android:id="@+id/layoutInfo" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:background="?attr/borderless_button" > + android:background="?attr/borderless_button"> <ImageView android:id="@+id/imgvCover" @@ -28,9 +28,10 @@ android:layout_width="@dimen/external_player_height" android:layout_height="@dimen/external_player_height" android:layout_alignParentLeft="true" + android:padding="4dp" android:adjustViewBounds="true" android:cropToPadding="true" - android:scaleType="fitXY" /> + android:scaleType="fitXY"/> <TextView android:id="@+id/txtvTitle" @@ -42,37 +43,9 @@ android:layout_marginTop="8dp" android:layout_toRightOf="@id/imgvCover" android:ellipsize="end" - android:maxLines="1" - android:textStyle="bold" /> - - <TextView - android:id="@+id/txtvPosition" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/txtvTitle" - android:layout_marginBottom="8dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginTop="4dp" - android:layout_toRightOf="@id/imgvCover" - android:maxLines="1" - android:textSize="@dimen/text_size_micro" /> - - <TextView - android:id="@+id/txtvStatus" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/txtvTitle" - android:layout_marginBottom="8dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:layout_marginTop="4dp" - android:layout_toRightOf="@id/txtvPosition" - android:ellipsize="end" - android:maxLines="1" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_micro" /> + android:maxLines="2" + android:textSize="18sp" + android:fontFamily="sans-serif-light"/> </RelativeLayout> <ImageButton @@ -80,7 +53,7 @@ android:contentDescription="@string/pause_label" android:layout_width="@dimen/external_player_height" android:layout_height="@dimen/external_player_height" - android:background="?attr/borderless_button" /> + android:background="?attr/borderless_button"/> </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/res/layout/feedinfo.xml b/res/layout/feedinfo.xml index d975ef549..8c907c132 100644 --- a/res/layout/feedinfo.xml +++ b/res/layout/feedinfo.xml @@ -26,9 +26,7 @@ android:layout_centerVertical="true" android:layout_margin="4dp" android:layout_toRightOf="@id/imgvCover" - android:textSize="@dimen/text_size_large" - android:textStyle="bold" - android:textColor="?android:attr/textColorPrimary"/> + style="@style/AntennaPod.TextView.Heading"/> <View android:id="@+id/divider" @@ -99,12 +97,11 @@ <TextView android:id="@+id/txtvSettings" + style="@style/AntennaPod.TextView.Heading" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" android:text="@string/podcast_settings_label" - android:textSize="@dimen/text_size_medium" - android:textColor="?android:attr/textColorPrimary" android:layout_marginLeft="8dp" android:layout_marginBottom="8dp" android:layout_marginTop="24dp"/> @@ -119,13 +116,12 @@ android:textColor="?android:attr/textColorPrimary"/> <TextView + style="@style/AntennaPod.TextView.Heading" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginBottom="8dp" android:layout_marginTop="24dp" - android:textSize="@dimen/text_size_medium" - android:textColor="?android:attr/textColorPrimary" android:text="@string/description_label"/> <TextView diff --git a/res/layout/feeditem_dialog.xml b/res/layout/feeditem_dialog.xml new file mode 100644 index 000000000..4241ef9c8 --- /dev/null +++ b/res/layout/feeditem_dialog.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:id="@+id/header" + android:orientation="horizontal" + android:layout_alignParentTop="true" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageButton + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_weight="1" + android:id="@+id/butAction1" + android:background="?attr/borderless_button" + tools:ignore="ContentDescription"/> + + <ImageButton + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_weight="1" + android:id="@+id/butAction2" + android:background="?attr/borderless_button" + tools:ignore="ContentDescription"/> + + <ImageButton + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_weight="1" + android:id="@+id/butMoreActions" + android:background="?attr/borderless_button" + android:src="?attr/ic_action_overflow" + android:contentDescription="@string/butAction_label"/> + </LinearLayout> + + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="3dp" + android:layout_below="@id/header" + android:background="@color/bright_blue"/> + + <WebView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_below="@id/divider" + android:layout_alignParentBottom="true" + android:id="@+id/webview"/> + +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/feeditemlist_header.xml b/res/layout/feeditemlist_header.xml index 560013abd..d2ce7556f 100644 --- a/res/layout/feeditemlist_header.xml +++ b/res/layout/feeditemlist_header.xml @@ -1,56 +1,53 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="?attr/borderless_button" - android:orientation="vertical" > - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="match_parent" > +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="de.danoeh.antennapod.activity.MainActivity"> + + <ImageView + android:id="@+id/imgvCover" + android:contentDescription="@string/cover_label" + android:layout_width="@dimen/thumbnail_length_onlinefeedview" + android:layout_height="@dimen/thumbnail_length_onlinefeedview" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:layout_margin="4dp"/> - <TextView - android:id="@+id/txtvHeaderTitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_centerVertical="true" - android:layout_marginBottom="32dp" - android:layout_marginLeft="28dp" - android:layout_marginRight="16dp" - android:layout_marginTop="32dp" - android:paddingLeft="8dp" - android:textAllCaps="true" - android:textColor="@color/dark_blue" - android:textSize="@dimen/text_size_large" - android:typeface="sans" /> + <TextView + android:id="@+id/txtvTitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:gravity="center_vertical" + android:layout_alignTop="@id/imgvCover" + android:layout_toRightOf="@id/imgvCover" + android:layout_alignParentRight="true" + android:lines="1" + style="@style/AntennaPod.TextView.Heading" + android:layout_margin="4dp"/> - <ImageButton - android:id="@+id/butAction" - android:contentDescription="@string/butAction_label" - android:layout_width="48dp" - android:layout_height="match_parent" - android:layout_alignParentBottom="true" - android:layout_alignParentRight="true" - android:background="?attr/borderless_button" - android:clickable="false" - android:focusable="false" - android:focusableInTouchMode="false" - android:paddingLeft="24dp" - android:paddingRight="8dp" - android:paddingTop="16dp" - android:scaleType="fitEnd" - android:src="?attr/spinner_button" /> + <TextView + android:id="@+id/txtvAuthor" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="4dp" + android:layout_below="@id/txtvTitle" + android:layout_toRightOf="@id/imgvCover" + android:lines="1" + android:ellipsize="end" + android:textColor="?android:attr/textColorSecondary" + android:textSize="@dimen/text_size_small"/> - <TextView - android:id="@+id/txtvNumItems" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_centerVertical="true" - android:layout_toLeftOf="@id/butAction" - android:textColor="@color/dark_blue" - android:textSize="@dimen/text_size_large" - android:textStyle="normal" /> - </RelativeLayout> + <TextView + android:id="@+id/txtvLink" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:lines="1" + android:ellipsize="end" + android:layout_below="@id/txtvAuthor" + android:layout_toRightOf="@id/imgvCover" + android:textSize="@dimen/text_size_small"/> -</LinearLayout>
\ No newline at end of file +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/feeditemlist_item.xml b/res/layout/feeditemlist_item.xml index e2898b601..c887d3a4f 100644 --- a/res/layout/feeditemlist_item.xml +++ b/res/layout/feeditemlist_item.xml @@ -1,155 +1,133 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="0dip" - android:layout_height="match_parent" - android:layout_weight="1" - android:paddingLeft="4dp" > - <TextView - android:id="@+id/txtvItemname" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="4dp" - android:layout_marginRight="4dp" - android:layout_marginTop="4dp" - android:layout_toLeftOf="@+id/butAction" - android:ellipsize="end" - android:lines="2" - android:textColor="?android:attr/textColorPrimary" - android:textSize="@dimen/text_size_medium" /> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:orientation="horizontal" + android:layout_height="match_parent"> - <TextView - android:id="@+id/txtvFeedname" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/txtvItemname" - android:layout_marginBottom="4dp" - android:layout_toLeftOf="@id/butAction" - android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_micro" - android:visibility="gone" /> + <RelativeLayout + android:layout_margin="8dp" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:paddingLeft="4dp"> - <TextView - android:id="@+id/txtvPublished" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/txtvFeedname" - android:layout_marginBottom="4dp" - android:layout_toLeftOf="@id/butAction" - android:textColor="?android:attr/textColorTertiary" - android:textSize="@dimen/text_size_micro" /> + <TextView + android:id="@+id/txtvPublished" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_marginBottom="4dp" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> - <ImageView - android:id="@+id/imgvType" - android:layout_width="@dimen/enc_icons_size" - android:layout_height="@dimen/enc_icons_size" - android:layout_below="@id/txtvPublished" - android:layout_toLeftOf="@+id/imgvInPlaylist" - android:padding="2dp" - tools:ignore="ContentDescription"/> + <TextView + android:id="@+id/txtvItemname" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/txtvPublished" + android:layout_marginBottom="4dp" + android:layout_marginRight="4dp" + android:layout_marginTop="4dp" + android:ellipsize="end" + android:lines="2" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_medium"/> - <ImageView - android:id="@id/imgvInPlaylist" - android:contentDescription="@string/in_queue_label" - android:layout_width="@dimen/enc_icons_size" - android:layout_height="@dimen/enc_icons_size" - android:layout_below="@id/txtvPublished" - android:layout_toLeftOf="@+id/imgvDownloaded" - android:padding="2dp" - android:src="?attr/stat_playlist" - android:visibility="visible" /> - <ImageView - android:id="@id/imgvDownloaded" - android:contentDescription="@string/status_downloaded_label" - android:layout_width="@dimen/enc_icons_size" - android:layout_height="@dimen/enc_icons_size" - android:layout_below="@id/txtvPublished" - android:layout_toLeftOf="@+id/imgvDownloading" - android:padding="2dp" - android:src="?attr/av_download" - android:visibility="visible" /> + <ImageView + android:id="@+id/imgvInPlaylist" + android:contentDescription="@string/in_queue_label" + android:layout_width="@dimen/enc_icons_size" + android:layout_height="@dimen/enc_icons_size" + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:padding="2dp" + android:src="?attr/stat_playlist" + android:visibility="visible"/> - <ImageView - android:id="@id/imgvDownloading" - android:contentDescription="@string/downloading_label" - android:layout_width="@dimen/enc_icons_size" - android:layout_height="@dimen/enc_icons_size" - android:layout_below="@id/txtvPublished" - android:layout_toLeftOf="@id/butAction" - android:padding="2dp" - android:src="?attr/navigation_refresh" - android:visibility="visible" /> + <ImageView + android:id="@+id/imgvType" + android:layout_width="@dimen/enc_icons_size" + android:layout_height="@dimen/enc_icons_size" + android:layout_alignParentBottom="true" + android:layout_toLeftOf="@+id/imgvInPlaylist" + android:padding="2dp" + tools:ignore="ContentDescription"/> - <TextView - android:id="@+id/txtvLenSize" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_below="@id/txtvPublished" - android:maxLines="2" - android:textColor="?android:attr/textColorTertiary" - android:textSize="@dimen/text_size_micro" /> + <TextView + android:id="@+id/txtvLenSize" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@id/txtvItemname" + android:maxLines="2" + android:layout_marginBottom="2dp" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> - <ProgressBar - android:id="@+id/pbar_episode_progress" - style="?android:attr/progressBarStyleHorizontal" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_below="@id/txtvPublished" - android:layout_marginBottom="4dp" - android:layout_marginLeft="4dp" - android:layout_marginRight="4dp" - android:layout_marginTop="2dp" - android:layout_toLeftOf="@id/imgvType" - android:layout_toRightOf="@id/txtvLenSize" /> + <ProgressBar + android:id="@+id/pbar_episode_progress" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_below="@id/txtvItemname" + android:layout_marginLeft="4dp" + android:layout_marginRight="4dp" + android:layout_marginTop="2dp" + android:layout_toLeftOf="@id/imgvType" + android:layout_toRightOf="@id/txtvLenSize"/> - <ImageButton - android:id="@id/butAction" - android:contentDescription="@string/butAction_label" - android:layout_width="48dp" + <TextView + android:id="@+id/statusUnread" + android:contentDescription="@string/status_unread_label" + android:layout_width="wrap_content" + android:layout_height="18dp" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_marginLeft="8dp" + android:layout_marginBottom="8dp" + android:background="@color/status_unread" + android:gravity="center" + android:minWidth="@dimen/status_indicator_width" + android:text="@string/new_label" + android:textAlignment="center" + android:textColor="@color/white" + android:textSize="@dimen/text_size_micro" + android:textStyle="bold"/> + + <ImageView + android:id="@+id/statusPlaying" + android:contentDescription="@string/status_playing_label" + android:layout_width="@dimen/status_indicator_width" + android:layout_height="18dp" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_marginLeft="8dp" + android:layout_marginBottom="8dp" + android:background="@color/status_playing" + android:gravity="center" + android:padding="2dp" + android:src="@drawable/av_play_dark"/> + + </RelativeLayout> + + <View + android:layout_width="1dp" android:layout_height="match_parent" - android:layout_alignParentBottom="true" - android:layout_alignParentRight="true" - android:background="?attr/borderless_button" - android:clickable="false" + android:background="@drawable/vertical_divider" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"/> + + <ImageButton + android:id="@+id/butSecondaryAction" android:focusable="false" + android:clickable="false" android:focusableInTouchMode="false" - android:paddingLeft="24dp" - android:paddingRight="8dp" - android:paddingTop="16dp" - android:scaleType="fitEnd" - android:src="?attr/spinner_button" /> - - <TextView - android:id="@+id/statusUnread" - android:contentDescription="@string/status_unread_label" - android:layout_width="wrap_content" - android:layout_height="18dp" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_margin="8dp" - android:background="@color/status_unread" - android:gravity="center" - android:minWidth="@dimen/status_indicator_width" - android:text="@string/new_label" - android:textAlignment="center" - android:textColor="@color/white" - android:textSize="@dimen/text_size_micro" - android:textStyle="bold" /> - - <ImageView - android:id="@+id/statusPlaying" - android:contentDescription="@string/status_playing_label" - android:layout_width="@dimen/status_indicator_width" - android:layout_height="18dp" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:layout_margin="8dp" - android:background="@color/status_playing" - android:gravity="center" - android:padding="2dp" - android:src="@drawable/av_play_dark" /> + android:layout_width="@dimen/listview_secondary_button_width" + android:layout_height="match_parent" + android:background="?attr/borderless_button" + tools:ignore="ContentDescription"/> -</RelativeLayout>
\ No newline at end of file +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/gpodnetauth_credentials.xml b/res/layout/gpodnetauth_credentials.xml index b66fc9414..3e3c4e54f 100644 --- a/res/layout/gpodnetauth_credentials.xml +++ b/res/layout/gpodnetauth_credentials.xml @@ -10,10 +10,8 @@ android:layout_height="wrap_content" android:text="@string/gpodnetauth_login_title" android:layout_alignParentTop="true" - android:textSize="@dimen/text_size_large" android:layout_margin="16dp" - android:textColor="@color/bright_blue" - android:textStyle="italic"/> + style="@style/AntennaPod.TextView.Heading"/> <TextView android:id="@id/txtvDescription" diff --git a/res/layout/gpodnetauth_device.xml b/res/layout/gpodnetauth_device.xml index e7bfdc425..33d3d2718 100644 --- a/res/layout/gpodnetauth_device.xml +++ b/res/layout/gpodnetauth_device.xml @@ -10,10 +10,8 @@ android:layout_height="wrap_content" android:text="@string/gpodnetauth_device_title" android:layout_alignParentTop="true" - android:textSize="@dimen/text_size_large" android:layout_margin="16dp" - android:textColor="@color/bright_blue" - android:textStyle="italic"/> + style="@style/AntennaPod.TextView.Heading"/> <TextView android:id="@+id/txtvDescription" diff --git a/res/layout/gpodnetauth_finish.xml b/res/layout/gpodnetauth_finish.xml index 3b0b10d04..71873201a 100644 --- a/res/layout/gpodnetauth_finish.xml +++ b/res/layout/gpodnetauth_finish.xml @@ -10,10 +10,8 @@ android:layout_height="wrap_content" android:text="@string/gpodnetauth_finish_title" android:layout_alignParentTop="true" - android:textSize="@dimen/text_size_large" android:layout_margin="16dp" - android:textColor="@color/bright_blue" - android:textStyle="italic"/> + style="@style/AntennaPod.TextView.Heading"/> <TextView android:id="@+id/txtvDescription" diff --git a/res/layout/main.xml b/res/layout/main.xml index 2917db164..cfd59b87c 100644 --- a/res/layout/main.xml +++ b/res/layout/main.xml @@ -1,21 +1,40 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/main_view" +<android.support.v4.widget.DrawerLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/drawer_layout" android:layout_width="match_parent" - android:layout_height="match_parent" > + android:layout_height="match_parent"> - <FrameLayout - android:id="@+id/playerFragment" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_alignParentBottom="true" /> + <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/content" + android:layout_width="match_parent" + android:layout_height="match_parent"> - <android.support.v4.view.ViewPager - android:id="@+id/viewpager" - android:layout_width="match_parent" - android:layout_height="0px" - android:layout_above="@id/playerFragment" - android:layout_alignParentTop="true" > - </android.support.v4.view.ViewPager> -</RelativeLayout>
\ No newline at end of file + <FrameLayout + android:id="@+id/playerFragment" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true"/> + + <FrameLayout + android:id="@+id/main_view" + android:layout_width="match_parent" + android:layout_height="0px" + android:layout_alignParentTop="true" + android:layout_above="@id/playerFragment"/> + + </RelativeLayout> + + <ListView + android:id="@+id/nav_list" + android:layout_width="@dimen/drawer_width" + android:layout_height="match_parent" + android:layout_gravity="start" + android:choiceMode="singleChoice" + android:background="?attr/nav_drawer_background" + android:scrollbarStyle="outsideOverlay" + android:paddingLeft="8dp" + android:paddingRight="8dp"/> + +</android.support.v4.widget.DrawerLayout>
\ No newline at end of file diff --git a/res/layout/nav_feedlistitem.xml b/res/layout/nav_feedlistitem.xml new file mode 100644 index 000000000..e01087077 --- /dev/null +++ b/res/layout/nav_feedlistitem.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + + <ImageView + android:id="@+id/imgvCover" + android:contentDescription="@string/cover_label" + android:layout_width="@dimen/thumbnail_length_navlist" + android:layout_height="@dimen/thumbnail_length_navlist" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:adjustViewBounds="true" + android:cropToPadding="true" + android:scaleType="fitXY" + android:layout_marginTop="6dp" + android:layout_marginBottom="6dp"/> + + + <TextView + android:id="@+id/txtvTitle" + android:lines="1" + android:ellipsize="end" + android:layout_centerVertical="true" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_navdrawer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="12dp" + android:layout_marginTop="14dp" + android:layout_marginBottom="14dp" + android:layout_marginRight="48dp" + android:layout_toRightOf="@id/imgvCover" + /> +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/nav_listitem.xml b/res/layout/nav_listitem.xml new file mode 100644 index 000000000..f1b585189 --- /dev/null +++ b/res/layout/nav_listitem.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/txtvTitle" + android:lines="1" + android:ellipsize="end" + android:layout_centerVertical="true" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_navdrawer" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:layout_marginTop="28dp" + android:layout_marginBottom="28dp"/> +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/nav_section_item.xml b/res/layout/nav_section_item.xml new file mode 100644 index 000000000..1f2fc7e3e --- /dev/null +++ b/res/layout/nav_section_item.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent"> + + <TextView + android:id="@+id/txtvTitle" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_small" + android:typeface="sans" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:layout_alignParentBottom="true" + android:layout_marginTop="16dp" + android:paddingBottom="4dp"/> + + <View + android:layout_width="match_parent" + android:layout_height="2dp" + android:layout_alignParentBottom="true" + android:background="@color/gray"/> +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/new_episodes_fragment.xml b/res/layout/new_episodes_fragment.xml new file mode 100644 index 000000000..460fe73d0 --- /dev/null +++ b/res/layout/new_episodes_fragment.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:dslv="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.mobeta.android.dslv.DragSortListView + android:id="@android:id/list" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:scrollbarStyle="outsideOverlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + dslv:collapsed_height="2dp" + dslv:drag_enabled="false" + dslv:drag_scroll_start="0.33" + dslv:float_alpha="0.6" + dslv:max_drag_scroll_speed="0.5" + dslv:remove_enabled="true" + dslv:remove_mode="flingRemove" + dslv:slide_shuffle_speed="0.3" + dslv:sort_enabled="false" + dslv:track_drag_sort="false" + dslv:float_background_color="?attr/dragview_float_background" + dslv:use_default_controller="true"/> + + <TextView + android:id="@id/android:empty" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:gravity="center" + android:text="@string/no_items_label"/> + + <ProgressBar + android:id="@+id/progLoading" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:indeterminateOnly="true" + android:visibility="gone"/> + +</FrameLayout>
\ No newline at end of file diff --git a/res/layout/new_episodes_listdivider.xml b/res/layout/new_episodes_listdivider.xml new file mode 100644 index 000000000..6b4f55d61 --- /dev/null +++ b/res/layout/new_episodes_listdivider.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:padding="12dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAllCaps="true" + style="@style/AntennaPod.TextView.Heading" + android:text="@string/recently_published_episodes_label"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/new_episodes_listitem.xml b/res/layout/new_episodes_listitem.xml new file mode 100644 index 000000000..721b88071 --- /dev/null +++ b/res/layout/new_episodes_listitem.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:orientation="horizontal" + android:layout_height="match_parent"> + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:layout_marginRight="16dp"> + + <ImageView + android:id="@+id/imgvImage" + android:contentDescription="@string/cover_label" + android:layout_width="@dimen/thumbnail_length_itemlist" + android:layout_height="@dimen/thumbnail_length_itemlist" + android:layout_alignParentLeft="true" + android:scaleType="centerCrop"/> + + <ImageView + android:id="@+id/statusPlaying" + android:contentDescription="@string/status_playing_label" + android:layout_width="@dimen/status_indicator_width" + android:layout_height="18dp" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:layout_marginLeft="8dp" + android:background="@color/status_playing" + android:gravity="center" + android:padding="2dp" + android:src="@drawable/av_play_dark"/> + + <TextView + android:id="@+id/txtvPublished" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentTop="true" + android:layout_toRightOf="@id/imgvImage" + android:layout_toLeftOf="@id/statusPlaying" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> + + <TextView + android:id="@+id/txtvTitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_below="@id/txtvPublished" + android:layout_marginLeft="8dp" + android:layout_marginRight="4dp" + android:layout_marginTop="2dp" + android:layout_toRightOf="@id/imgvImage" + android:layout_toLeftOf="@id/statusPlaying" + android:ellipsize="end" + android:lines="2" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_small"/> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:layout_toRightOf="@id/imgvImage" + android:orientation="vertical"> + + <RelativeLayout + android:id="@+id/bottom_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@id/imgvInPlaylist" + android:contentDescription="@string/in_queue_label" + android:layout_width="@dimen/enc_icons_size" + android:layout_height="@dimen/enc_icons_size" + android:layout_alignParentRight="true" + android:src="?attr/stat_playlist"/> + + <ProgressBar + android:id="@+id/pbar_download_progress" + style="?android:attr/progressBarStyleHorizontal" + android:max="100" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_toLeftOf="@id/imgvInPlaylist" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentLeft="true"/> + + <TextView + android:id="@+id/txtvDuration" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_toLeftOf="@id/imgvInPlaylist" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentLeft="true" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> + </RelativeLayout> + </LinearLayout> + </RelativeLayout> + + <View + android:layout_width="1dp" + android:layout_height="match_parent" + android:background="@drawable/vertical_divider" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"/> + + <ImageButton + android:id="@+id/butSecondaryAction" + android:focusable="false" + android:clickable="false" + android:focusableInTouchMode="false" + android:layout_width="@dimen/listview_secondary_button_width" + android:layout_height="match_parent" + android:background="?attr/borderless_button" + tools:ignore="ContentDescription"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/onlinefeedview_header.xml b/res/layout/onlinefeedview_header.xml index 441df6174..6cec28e92 100644 --- a/res/layout/onlinefeedview_header.xml +++ b/res/layout/onlinefeedview_header.xml @@ -23,8 +23,7 @@ android:layout_toRightOf="@id/imgvCover" android:layout_alignParentRight="true" android:lines="1" - android:textColor="?android:attr/textColorPrimary" - android:textSize="@dimen/text_size_medium" + style="@style/AntennaPod.TextView.Heading" android:layout_margin="4dp"/> <TextView diff --git a/res/layout/pager_fragment.xml b/res/layout/pager_fragment.xml new file mode 100644 index 000000000..cb7ae0151 --- /dev/null +++ b/res/layout/pager_fragment.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.v4.view.ViewPager + android:id="@+id/pager" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/queue_fragment.xml b/res/layout/queue_fragment.xml new file mode 100644 index 000000000..1c48768a2 --- /dev/null +++ b/res/layout/queue_fragment.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:dslv="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.mobeta.android.dslv.DragSortListView + android:id="@android:id/list" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:scrollbarStyle="outsideOverlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + dslv:collapsed_height="2dp" + dslv:drag_enabled="true" + dslv:drag_handle_id="@id/drag_handle" + dslv:drag_scroll_start="0.33" + dslv:float_alpha="0.6" + dslv:max_drag_scroll_speed="0.5" + dslv:remove_enabled="true" + dslv:remove_mode="flingRemove" + dslv:slide_shuffle_speed="0.3" + dslv:sort_enabled="true" + dslv:track_drag_sort="true" + dslv:float_background_color="?attr/dragview_float_background" + dslv:use_default_controller="true"/> + + <TextView + android:id="@id/android:empty" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:gravity="center" + android:text="@string/no_items_label"/> + + <ProgressBar + android:id="@+id/progLoading" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:indeterminateOnly="true" + android:visibility="gone"/> + + <LinearLayout + android:id="@+id/undobar" + style="@style/UndoBar"> + + <TextView + android:id="@+id/undobar_message" + style="@style/UndoBarMessage"/> + + <Button + android:id="@+id/undobar_button" + style="@style/UndoBarButton"/> + </LinearLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/res/layout/queue_listitem.xml b/res/layout/queue_listitem.xml new file mode 100644 index 000000000..5bac726f3 --- /dev/null +++ b/res/layout/queue_listitem.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:orientation="horizontal" + android:layout_height="match_parent"> + + <ImageView + android:layout_width="24dp" + android:layout_height="match_parent" + android:id="@+id/drag_handle" + android:src="?attr/dragview_background" + android:scaleType="center" + android:layout_margin="8dp" + android:contentDescription="@string/drag_handle_content_description"/> + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:layout_marginRight="16dp"> + + <ImageView + android:id="@+id/imgvImage" + android:contentDescription="@string/cover_label" + android:layout_width="@dimen/thumbnail_length_itemlist" + android:layout_height="@dimen/thumbnail_length_itemlist" + android:layout_alignParentLeft="true" + android:scaleType="centerCrop"/> + + <ImageView + android:id="@+id/statusPlaying" + android:contentDescription="@string/status_playing_label" + android:layout_width="@dimen/status_indicator_width" + android:layout_height="18dp" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp" + android:background="@color/status_playing" + android:gravity="center" + android:padding="2dp" + android:src="@drawable/av_play_dark"/> + + <TextView + android:id="@+id/txtvPublished" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentTop="true" + android:layout_toRightOf="@id/imgvImage" + android:layout_toLeftOf="@id/statusPlaying" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> + + <TextView + android:id="@+id/txtvTitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_below="@id/txtvPublished" + android:layout_marginLeft="8dp" + android:layout_marginRight="4dp" + android:layout_marginTop="2dp" + android:layout_toRightOf="@id/imgvImage" + android:layout_toLeftOf="@id/statusPlaying" + android:ellipsize="end" + android:lines="2" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_small"/> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:layout_toRightOf="@id/imgvImage" + android:orientation="vertical"> + + <RelativeLayout + android:id="@+id/bottom_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ProgressBar + android:id="@+id/pbar_download_progress" + style="?android:attr/progressBarStyleHorizontal" + android:max="100" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentLeft="true"/> + + <TextView + android:id="@+id/txtvDuration" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_alignParentLeft="true" + android:layout_alignParentRight="true" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_micro"/> + </RelativeLayout> + </LinearLayout> + </RelativeLayout> + + <View + android:layout_width="1dp" + android:layout_height="match_parent" + android:background="@drawable/vertical_divider" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"/> + + <ImageButton + android:id="@+id/butSecondaryAction" + android:focusable="false" + android:clickable="false" + android:focusableInTouchMode="false" + android:layout_width="@dimen/listview_secondary_button_width" + android:layout_height="match_parent" + android:background="?attr/borderless_button" + tools:ignore="ContentDescription"/> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/searchlist_item.xml b/res/layout/searchlist_item.xml index 889f40eef..c83f34482 100644 --- a/res/layout/searchlist_item.xml +++ b/res/layout/searchlist_item.xml @@ -1,19 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:padding="8dp" > + android:layout_width="match_parent" + android:layout_height="match_parent"> <ImageView android:id="@+id/imgvFeedimage" android:contentDescription="@string/cover_label" - android:layout_width="55dip" - android:layout_height="55dip" + android:layout_width="@dimen/thumbnail_length_itemlist" + android:layout_height="@dimen/thumbnail_length_itemlist" android:layout_alignParentLeft="true" android:layout_centerVertical="true" - android:layout_marginLeft="1dip" - android:layout_marginRight="4dip" - android:cropToPadding="true" /> + android:scaleType="centerCrop"/> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" @@ -21,20 +18,27 @@ android:layout_height="match_parent" android:layout_centerVertical="true" android:layout_toRightOf="@id/imgvFeedimage" - android:orientation="vertical" > + android:orientation="vertical"> <TextView android:id="@+id/txtvTitle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:maxLines="2" - android:textStyle="bold" /> + android:layout_margin="4dp" + android:lines="2" + android:ellipsize="end" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/text_size_small"/> <TextView android:id="@+id/txtvSubtitle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:textColor="?android:attr/textColorSecondary" /> + android:layout_margin="4dp" + android:lines="1" + android:ellipsize="end" + android:textColor="?android:attr/textColorTertiary" + android:textSize="@dimen/text_size_small"/> </LinearLayout> </RelativeLayout>
\ No newline at end of file diff --git a/res/menu/feeditem_dialog.xml b/res/menu/feeditem_dialog.xml new file mode 100644 index 000000000..f33b7502a --- /dev/null +++ b/res/menu/feeditem_dialog.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res-auto"> + + <item + android:id="@id/skip_episode_item" + android:title="@string/skip_episode_label" + custom:showAsAction="collapseActionView"> + </item> + + <item + android:id="@+id/mark_read_item" + custom:showAsAction="collapseActionView" + android:title="@string/mark_read_label"> + </item> + <item + android:id="@+id/mark_unread_item" + custom:showAsAction="collapseActionView" + android:title="@string/mark_unread_label"> + </item> + <item + android:id="@+id/add_to_queue_item" + custom:showAsAction="collapseActionView" + android:title="@string/add_to_queue_label"> + </item> + <item + android:id="@+id/remove_from_queue_item" + custom:showAsAction="collapseActionView" + android:title="@string/remove_from_queue_label"> + </item> + <item + android:id="@+id/share_link_item" + custom:showAsAction="collapseActionView" + android:title="@string/share_link_label"> + </item> + <item + android:id="@+id/visit_website_item" + android:icon="?attr/location_web_site" + custom:showAsAction="ifRoom|collapseActionView" + android:title="@string/visit_website_label"> + </item> + <item + android:id="@+id/support_item" + custom:showAsAction="collapseActionView" + android:title="@string/support_label"> + </item> + +</menu>
\ No newline at end of file diff --git a/res/menu/feedlist.xml b/res/menu/feedlist.xml index 74f4fc923..28df69f2d 100644 --- a/res/menu/feedlist.xml +++ b/res/menu/feedlist.xml @@ -5,39 +5,37 @@ <item android:id="@+id/refresh_item" android:icon="?attr/navigation_refresh" + android:menuCategory="container" custom:showAsAction="ifRoom|collapseActionView" android:title="@string/refresh_label"> </item> <item android:id="@+id/mark_all_read_item" + android:menuCategory="container" custom:showAsAction="collapseActionView" android:title="@string/mark_all_read_label"> </item> <item android:id="@+id/show_info_item" + android:menuCategory="container" android:icon="?attr/action_about" custom:showAsAction="collapseActionView" android:title="@string/show_info_label"> </item> <item - android:id="@+id/remove_item" - android:icon="?attr/content_discard" + android:id="@+id/support_item" + android:menuCategory="container" custom:showAsAction="collapseActionView" - android:title="@string/remove_feed_label" - android:visible="true"> + android:title="@string/support_label" + android:visible="false"> </item> <item - android:id="@+id/visit_website_item" - android:icon="?attr/location_web_site" + android:id="@+id/remove_item" + android:menuCategory="container" + android:icon="?attr/content_discard" custom:showAsAction="collapseActionView" - android:title="@string/visit_website_label" + android:title="@string/remove_feed_label" android:visible="true"> </item> - <item - android:id="@+id/support_item" - custom:showAsAction="collapseActionView" - android:title="@string/support_label" - android:visible="false"> - </item> </menu>
\ No newline at end of file diff --git a/res/menu/main.xml b/res/menu/main.xml index 45047fc46..c5b069b40 100644 --- a/res/menu/main.xml +++ b/res/menu/main.xml @@ -1,42 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res-auto"> - <item - android:id="@+id/all_feed_refresh" - android:title="@string/refresh_label" - android:icon="?attr/navigation_refresh" - custom:showAsAction="ifRoom|collapseActionView"> - </item> - <item - android:id="@+id/add_feed" - android:title="@string/add_feed_label" - android:icon="?attr/content_new" - custom:showAsAction="ifRoom|collapseActionView"> - </item> - <item - android:id="@id/search_item" - android:icon="?attr/action_search" - android:title="@string/search_label" - custom:showAsAction="ifRoom|collapseActionView" - android:actionViewClass="android.support.v7.widget.SearchView"/> - <item - android:id="@+id/show_player" - android:title="@string/show_player_label" - android:icon="@drawable/av_play" - custom:showAsAction="collapseActionView"/> - <item - android:id="@+id/show_playback_history" - android:title="@string/playback_history_label" - custom:showAsAction="collapseActionView"/> - <item - android:id="@+id/show_downloads" - android:title="@string/downloads_label" - android:icon="@drawable/av_download" - custom:showAsAction="collapseActionView"> - </item> + <item android:id="@+id/show_preferences" android:title="@string/settings_label" + android:menuCategory="system" android:icon="?attr/action_settings" custom:showAsAction="collapseActionView"/> diff --git a/res/menu/new_episodes.xml b/res/menu/new_episodes.xml new file mode 100644 index 000000000..00ff3bc5a --- /dev/null +++ b/res/menu/new_episodes.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res-auto"> + + <item + android:id="@+id/refresh_item" + android:title="@string/refresh_label" + android:menuCategory="container" + custom:showAsAction="ifRoom|collapseActionView" + android:icon="?attr/navigation_refresh"/> + + <item + android:id="@+id/mark_all_read_item" + android:title="@string/mark_all_read_label" + android:menuCategory="container" + custom:showAsAction="ifRoom|collapseActionView" + android:icon="?attr/navigation_accept"/> + +</menu>
\ No newline at end of file diff --git a/res/values-v16/styles.xml b/res/values-v16/styles.xml new file mode 100644 index 000000000..947e43f38 --- /dev/null +++ b/res/values-v16/styles.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <style name="AntennaPod.TextView.Heading" parent="@android:style/TextAppearance.Medium"> + <item name="android:textSize">@dimen/text_size_large</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:fontFamily">sans-serif-light</item> + </style> +</resources>
\ No newline at end of file diff --git a/res/values-v19/colors.xml b/res/values-v19/colors.xml new file mode 100644 index 000000000..16c065d75 --- /dev/null +++ b/res/values-v19/colors.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="selection_background_color_dark">#484B4D</color> + <color name="selection_background_color_light">#E3E3E3</color> +</resources>
\ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index e8334cd60..751134a50 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -1,39 +1,42 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <attr name="action_about" format="reference" /> - <attr name="action_search" format="reference" /> - <attr name="action_settings" format="reference" /> - <attr name="action_stream" format="reference" /> - <attr name="av_download" format="reference" /> - <attr name="av_fast_forward" format="reference" /> - <attr name="av_pause" format="reference" /> - <attr name="av_play" format="reference" /> - <attr name="av_rewind" format="reference" /> - <attr name="content_discard" format="reference" /> - <attr name="content_new" format="reference" /> - <attr name="default_cover" format="reference" /> - <attr name="device_access_time" format="reference" /> - <attr name="location_web_site" format="reference" /> - <attr name="navigation_accept" format="reference" /> - <attr name="navigation_cancel" format="reference" /> - <attr name="navigation_expand" format="reference" /> - <attr name="navigation_collapse" format="reference" /> - <attr name="navigation_refresh" format="reference" /> - <attr name="navigation_up" format="reference" /> - <attr name="navigation_shownotes" format="reference" /> - <attr name="navigation_chapters" format="reference" /> - <attr name="social_share" format="reference" /> - <attr name="stat_playlist" format="reference" /> - <attr name="type_audio" format="reference" /> - <attr name="type_video" format="reference" /> - <attr name="borderless_button" format="reference" /> - <attr name="spinner_button" format="reference" /> - <attr name="overlay_drawable" format="reference" /> - <attr name="dragview_background" format="reference" /> - <attr name="dragview_float_background" format="reference" /> + <attr name="action_about" format="reference"/> + <attr name="action_search" format="reference"/> + <attr name="action_settings" format="reference"/> + <attr name="action_stream" format="reference"/> + <attr name="av_download" format="reference"/> + <attr name="av_fast_forward" format="reference"/> + <attr name="av_pause" format="reference"/> + <attr name="av_play" format="reference"/> + <attr name="av_rewind" format="reference"/> + <attr name="content_discard" format="reference"/> + <attr name="content_new" format="reference"/> + <attr name="default_cover" format="reference"/> + <attr name="device_access_time" format="reference"/> + <attr name="location_web_site" format="reference"/> + <attr name="navigation_accept" format="reference"/> + <attr name="navigation_cancel" format="reference"/> + <attr name="navigation_expand" format="reference"/> + <attr name="navigation_collapse" format="reference"/> + <attr name="navigation_refresh" format="reference"/> + <attr name="navigation_up" format="reference"/> + <attr name="navigation_shownotes" format="reference"/> + <attr name="navigation_chapters" format="reference"/> + <attr name="social_share" format="reference"/> + <attr name="stat_playlist" format="reference"/> + <attr name="type_audio" format="reference"/> + <attr name="type_video" format="reference"/> + <attr name="borderless_button" format="reference"/> + <attr name="spinner_button" format="reference"/> + <attr name="overlay_drawable" format="reference"/> + <attr name="dragview_background" format="reference"/> + <attr name="dragview_float_background" format="reference"/> + <attr name="ic_action_overflow" format="reference"/> <!-- Used in itemdescription --> - <attr name="non_transparent_background" format="reference" /> - <attr name="overlay_background" format="color" /> + <attr name="non_transparent_background" format="reference"/> + <attr name="overlay_background" format="color"/> + <attr name="nav_drawer_background" format="color"/> + <attr name="nav_drawer_toggle" format="reference"/> </resources>
\ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 8dea65a5b..f90b25a3a 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -4,13 +4,17 @@ <dimen name="widget_margin">8dp</dimen> <dimen name="thumbnail_length">70dp</dimen> <dimen name="dragview_length">54dp</dimen> - <dimen name="external_player_height">55dp</dimen> + <dimen name="external_player_height">70dp</dimen> <dimen name="enc_icons_size">20dp</dimen> <dimen name="text_size_micro">12sp</dimen> <dimen name="text_size_small">14sp</dimen> + <dimen name="text_size_navdrawer">16sp</dimen> <dimen name="text_size_medium">18sp</dimen> <dimen name="text_size_large">22sp</dimen> <dimen name="status_indicator_width">36dp</dimen> <dimen name="thumbnail_length_itemlist">80dp</dimen> <dimen name="thumbnail_length_onlinefeedview">110dp</dimen> + <dimen name="thumbnail_length_navlist">42dp</dimen> + <dimen name="listview_secondary_button_width">48dp</dimen> + <dimen name="drawer_width">280dp</dimen> </resources>
\ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 6d36a5c07..cf3b02148 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4,22 +4,33 @@ tools:ignore="MissingTranslation" > - <!-- Activitiy titles --> + <!-- Activitiy and fragment titles --> <string name="app_name">AntennaPod</string> <string name="feeds_label">Feeds</string> <string name="podcasts_label">PODCASTS</string> <string name="episodes_label">EPISODES</string> + <string name="new_episodes_label">New episodes</string> <string name="new_label">New</string> <string name="waiting_list_label">Waiting list</string> <string name="settings_label">Settings</string> <string name="add_new_feed_label">Add podcast</string> <string name="downloads_label">Downloads</string> + <string name="downloads_running_label">Running</string> + <string name="downloads_completed_label">Completed</string> + <string name="downloads_log_label">Log</string> <string name="cancel_download_label">Cancel Download</string> <string name="download_log_label">Download log</string> <string name="playback_history_label">Playback history</string> <string name="gpodnet_main_label">gpodder.net</string> <string name="gpodnet_auth_label">gpodder.net login</string> + <!-- New episodes fragment --> + <string name="recently_published_episodes_label">Recently published</string> + + <!-- Main activity --> + <string name="drawer_open">Open menu</string> + <string name="drawer_close">Close menu</string> + <!-- Webview actions --> <string name="open_in_browser_label">Open in browser</string> <string name="copy_url_label">Copy URL</string> @@ -59,12 +70,15 @@ <!-- 'Add Feed' Activity labels --> <string name="feedurl_label">Feed URL</string> <string name="txtvfeedurl_label">Add Podcast by URL</string> - <string name="podcastdirectories_label">Podcast directories</string> + <string name="podcastdirectories_label">Find podcast in directory</string> + <string name="podcastdirectories_descr">You can search for new podcasts by name, category or popularity in the gpodder.net directory.</string> + <string name="browse_gpoddernet_label">Browse gpodder.net</string> <!-- Actions on feeds --> <string name="mark_all_read_label">Mark all as read</string> + <string name="mark_all_read_msg">Marked all episodes as read</string> <string name="show_info_label">Show information</string> - <string name="remove_feed_label">Remove Feed</string> + <string name="remove_feed_label">Remove podcast</string> <string name="share_link_label">Share website link</string> <string name="share_source_label">Share feed link</string> <string name="feed_delete_confirmation_msg">Please confirm that you want to delete this feed and ALL episodes of this feed that you have downloaded.</string> @@ -76,6 +90,7 @@ <string name="pause_label">Pause</string> <string name="stream_label">Stream</string> <string name="remove_label">Remove</string> + <string name="remove_episode_lable">Remove episode</string> <string name="mark_read_label">Mark as read</string> <string name="mark_unread_label">Mark as unread</string> <string name="add_to_queue_label">Add to Queue</string> @@ -349,10 +364,12 @@ <string name="status_playing_label">Episode is being played</string> <string name="status_downloading_label">Episode is being downloaded</string> <string name="status_downloaded_label">Episode is downloaded</string> + <string name="status_not_downloaded_label">Episode has not been downloaded yet</string> <string name="status_unread_label">Item is new</string> <string name="in_queue_label">Episode is in the queue</string> <string name="new_episodes_count_label">Number of new episodes</string> <string name="in_progress_episodes_count_label">Number of episodes you have started listening to</string> + <string name="drag_handle_content_description">Drag to change the position of this item</string> <!-- OPML backup --> <string name="backup_restored">"Restored feed subscriptions from backup"</string> diff --git a/res/values/styles.xml b/res/values/styles.xml index d08b8060f..d6dccb426 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -33,8 +33,11 @@ <item name="attr/overlay_background">@color/overlay_light</item> <item name="attr/spinner_button">@drawable/spinner_button</item> <item name="attr/overlay_drawable">@drawable/overlay_drawable</item> - <item name="attr/dragview_background">@drawable/dragview_background</item> + <item name="attr/dragview_background">@drawable/ic_drag_handle</item> <item name="attr/dragview_float_background">@color/white</item> + <item name="attr/nav_drawer_background">@color/white</item> + <item name="attr/nav_drawer_toggle">@drawable/ic_drawer</item> + <item name="attr/ic_action_overflow">@drawable/ic_action_overflow</item> </style> <style name="Theme.AntennaPod.Dark" parent="@style/Theme.AppCompat"> @@ -69,43 +72,55 @@ <item name="attr/overlay_background">@color/overlay_dark</item> <item name="attr/spinner_button">@drawable/spinner_button_dark</item> <item name="attr/overlay_drawable">@drawable/overlay_drawable_dark</item> - <item name="attr/dragview_background">@drawable/dragview_background_dark</item> + <item name="attr/dragview_background">@drawable/ic_drag_handle_dark</item> <item name="attr/dragview_float_background">@color/black</item> + <item name="attr/nav_drawer_background">#3B3B3B</item> + <item name="attr/nav_drawer_toggle">@drawable/ic_drawer_dark</item> + <item name="attr/ic_action_overflow">@drawable/ic_action_overflow_dark</item> + </style> <style name="UndoBar"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">48dp</item> - <item name="android:layout_gravity">bottom</item> - <item name="android:layout_marginLeft">8dp</item> - <item name="android:layout_marginRight">8dp</item> - <item name="android:layout_marginBottom">16dp</item> - <item name="android:orientation">horizontal</item> - <item name="android:background">@drawable/undobar</item> - <item name="android:clickable">true</item> - <item name="android:divider">@drawable/undobar_divider</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">48dp</item> + <item name="android:layout_gravity">bottom</item> + <item name="android:layout_marginLeft">8dp</item> + <item name="android:layout_marginRight">8dp</item> + <item name="android:layout_marginBottom">16dp</item> + <item name="android:orientation">horizontal</item> + <item name="android:background">@drawable/undobar</item> + <item name="android:clickable">true</item> + <item name="android:divider">@drawable/undobar_divider</item> </style> + <style name="UndoBarMessage"> - <item name="android:layout_width">0dp</item> - <item name="android:layout_weight">1</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginLeft">16dp</item> - <item name="android:layout_gravity">center_vertical</item> - <item name="android:layout_marginRight">16dp</item> - <item name="android:textAppearance">?android:textAppearanceMedium</item> - <item name="android:textColor">#fff</item> + <item name="android:layout_width">0dp</item> + <item name="android:layout_weight">1</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">16dp</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_marginRight">16dp</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:textColor">#fff</item> </style> + <style name="UndoBarButton"> - <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">match_parent</item> - <item name="android:paddingLeft">16dp</item> - <item name="android:paddingRight">16dp</item> - <item name="android:background">@drawable/undobar_button</item> - <item name="android:drawableLeft">@drawable/ic_undobar_undo</item> - <item name="android:drawablePadding">12dp</item> - <item name="android:textAppearance">?android:textAppearanceSmall</item> - <item name="android:textStyle">bold</item> - <item name="android:textColor">#fff</item> - <item name="android:text">@string/undo</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">match_parent</item> + <item name="android:paddingLeft">16dp</item> + <item name="android:paddingRight">16dp</item> + <item name="android:background">@drawable/undobar_button</item> + <item name="android:drawableLeft">@drawable/ic_undobar_undo</item> + <item name="android:drawablePadding">12dp</item> + <item name="android:textAppearance">?android:textAppearanceSmall</item> + <item name="android:textStyle">bold</item> + <item name="android:textColor">#fff</item> + <item name="android:text">@string/undo</item> + </style> + + <style name="AntennaPod.TextView.Heading" parent="@android:style/TextAppearance.Medium"> + <item name="android:textSize">@dimen/text_size_large</item> + <item name="android:textStyle">italic</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> </style> </resources> diff --git a/src/de/danoeh/antennapod/activity/AddFeedActivity.java b/src/de/danoeh/antennapod/activity/AddFeedActivity.java index a77689ddb..54eff51c5 100644 --- a/src/de/danoeh/antennapod/activity/AddFeedActivity.java +++ b/src/de/danoeh/antennapod/activity/AddFeedActivity.java @@ -50,7 +50,6 @@ public class AddFeedActivity extends ActionBarActivity { etxtFeedurl.setText(getIntent().getDataString()); } - butBrowseMiroGuide = (Button) findViewById(R.id.butBrowseMiroguide); butBrowserGpoddernet = (Button) findViewById(R.id.butBrowseGpoddernet); butOpmlImport = (Button) findViewById(R.id.butOpmlImport); butConfirm = (Button) findViewById(R.id.butConfirm); diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index de989fa46..7a4429705 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -4,11 +4,16 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.TypedArray; +import android.os.AsyncTask; import android.os.Bundle; +import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.ListFragment; +import android.support.v4.widget.DrawerLayout; import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -18,18 +23,20 @@ import android.widget.ImageView.ScaleType; import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.ChapterListAdapter; +import de.danoeh.antennapod.adapter.NavListAdapter; import de.danoeh.antennapod.asynctask.ImageLoader; import de.danoeh.antennapod.dialog.VariableSpeedDialog; -import de.danoeh.antennapod.feed.Chapter; -import de.danoeh.antennapod.feed.MediaType; -import de.danoeh.antennapod.feed.SimpleChapter; +import de.danoeh.antennapod.feed.*; import de.danoeh.antennapod.fragment.CoverFragment; import de.danoeh.antennapod.fragment.ItemDescriptionFragment; import de.danoeh.antennapod.preferences.UserPreferences; import de.danoeh.antennapod.service.playback.PlaybackService; +import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.util.playback.ExternalMedia; import de.danoeh.antennapod.util.playback.Playable; +import java.util.List; + /** * Activity for playing audio files. */ @@ -44,6 +51,11 @@ public class AudioplayerActivity extends MediaplayerActivity { private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition"; private static final String PREF_PLAYABLE_ID = "playableId"; + private DrawerLayout drawerLayout; + private NavListAdapter navAdapter; + private ListView navList; + private ActionBarDrawerToggle drawerToggle; + private Fragment[] detachedFragments; private CoverFragment coverFragment; @@ -58,7 +70,6 @@ public class AudioplayerActivity extends MediaplayerActivity { private int savedPosition = -1; private TextView txtvTitle; - private TextView txtvFeed; private Button butPlaybackSpeed; private ImageButton butNavLeft; private ImageButton butNavRight; @@ -108,6 +119,8 @@ public class AudioplayerActivity extends MediaplayerActivity { super.onStop(); if (BuildConfig.DEBUG) Log.d(TAG, "onStop"); + cancelLoadTask(); + EventDistributor.getInstance().unregister(contentUpdate); } @@ -142,6 +155,7 @@ public class AudioplayerActivity extends MediaplayerActivity { @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + drawerToggle.onConfigurationChanged(newConfig); } @Override @@ -149,6 +163,7 @@ public class AudioplayerActivity extends MediaplayerActivity { // super.onSaveInstanceState(outState); would cause crash if (BuildConfig.DEBUG) Log.d(TAG, "onSaveInstanceState"); + } @Override @@ -223,6 +238,8 @@ public class AudioplayerActivity extends MediaplayerActivity { switchToFragment(savedPosition); } + EventDistributor.getInstance().register(contentUpdate); + loadData(); } @Override @@ -382,12 +399,52 @@ public class AudioplayerActivity extends MediaplayerActivity { protected void setupGUI() { super.setupGUI(); resetFragmentView(); + drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + navList = (ListView) findViewById(R.id.nav_list); txtvTitle = (TextView) findViewById(R.id.txtvTitle); - txtvFeed = (TextView) findViewById(R.id.txtvFeed); butNavLeft = (ImageButton) findViewById(R.id.butNavLeft); butNavRight = (ImageButton) findViewById(R.id.butNavRight); butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); + TypedArray typedArray = obtainStyledAttributes(new int[]{R.attr.nav_drawer_toggle}); + drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, typedArray.getResourceId(0,0), R.string.drawer_open, R.string.drawer_close) { + String currentTitle = getSupportActionBar().getTitle().toString(); + @Override + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + currentTitle = getSupportActionBar().getTitle().toString(); + getSupportActionBar().setTitle(R.string.app_name); + supportInvalidateOptionsMenu(); + } + + @Override + public void onDrawerClosed(View drawerView) { + super.onDrawerClosed(drawerView); + getSupportActionBar().setTitle(currentTitle); + supportInvalidateOptionsMenu(); + } + }; + typedArray.recycle(); + drawerToggle.setDrawerIndicatorEnabled(false); + + navAdapter = new NavListAdapter(itemAccess, this); + navList.setAdapter(navAdapter); + navList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + int viewType = parent.getAdapter().getItemViewType(position); + if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) { + int relPos = (viewType == NavListAdapter.VIEW_TYPE_NAV) ? position : position - NavListAdapter.SUBSCRIPTION_OFFSET; + Intent intent = new Intent(AudioplayerActivity.this, MainActivity.class); + intent.putExtra(MainActivity.EXTRA_NAV_TYPE, viewType); + intent.putExtra(MainActivity.EXTRA_NAV_INDEX, relPos); + startActivity(intent); + } + drawerLayout.closeDrawer(navList); + } + }); + drawerToggle.syncState(); + butNavLeft.setOnClickListener(new OnClickListener() { @Override @@ -491,7 +548,6 @@ public class AudioplayerActivity extends MediaplayerActivity { return false; } txtvTitle.setText(media.getEpisodeTitle()); - txtvFeed.setText(media.getFeedTitle()); if (media.getChapters() != null) { butNavRight.setVisibility(View.VISIBLE); } else { @@ -551,4 +607,78 @@ public class AudioplayerActivity extends MediaplayerActivity { return R.layout.audioplayer_activity; } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (drawerToggle.onOptionsItemSelected(item)) { + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + private List<Feed> feeds; + private AsyncTask<Void, Void, List<Feed>> loadTask; + + private void loadData() { + loadTask = new AsyncTask<Void, Void, List<Feed>>() { + @Override + protected List<Feed> doInBackground(Void... params) { + return DBReader.getFeedList(AudioplayerActivity.this); + } + + @Override + protected void onPostExecute(List<Feed> result) { + super.onPostExecute(result); + feeds = result; + if (navAdapter != null) { + navAdapter.notifyDataSetChanged(); + } + } + }; + loadTask.execute(); + } + + private void cancelLoadTask() { + if (loadTask != null) { + loadTask.cancel(true); + } + } + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((EventDistributor.FEED_LIST_UPDATE & arg) != 0) { + if (BuildConfig.DEBUG) + Log.d(TAG, "Received contentUpdate Intent."); + loadData(); + } + } + }; + + private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { + @Override + public int getCount() { + if (feeds != null) { + return feeds.size(); + } else { + return 0; + } + } + + @Override + public Feed getItem(int position) { + if (feeds != null && position < feeds.size()) { + return feeds.get(position); + } else { + return null; + } + } + + @Override + public int getSelectedItemIndex() { + return -1; + } + }; } diff --git a/src/de/danoeh/antennapod/activity/DownloadActivity.java b/src/de/danoeh/antennapod/activity/DownloadActivity.java index 996929cdb..416de73a4 100644 --- a/src/de/danoeh/antennapod/activity/DownloadActivity.java +++ b/src/de/danoeh/antennapod/activity/DownloadActivity.java @@ -180,8 +180,8 @@ public class DownloadActivity extends ActionBarActivity implements @Override public void onDownloadDataAvailable(List<Downloader> downloaderList) { - dla = new DownloadlistAdapter(DownloadActivity.this, 0, - downloaderList); + //dla = new DownloadlistAdapter(DownloadActivity.this, 0, + // downloaderList); listview.setAdapter(dla); dla.notifyDataSetChanged(); } diff --git a/src/de/danoeh/antennapod/activity/FeedItemlistActivity.java b/src/de/danoeh/antennapod/activity/FeedItemlistActivity.java index d0305eada..95ccb859f 100644 --- a/src/de/danoeh/antennapod/activity/FeedItemlistActivity.java +++ b/src/de/danoeh/antennapod/activity/FeedItemlistActivity.java @@ -166,7 +166,7 @@ public class FeedItemlistActivity extends ActionBarActivity { public boolean onOptionsItemSelected(MenuItem item) { try { if (FeedMenuHandler.onOptionsItemClicked(this, item, feed)) { - filf.getListAdapter().notifyDataSetChanged(); + // filf.getListAdapter().notifyDataSetChanged(); } else { switch (item.getItemId()) { case R.id.remove_item: diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java index 1a49e63d0..5fcd45289 100644 --- a/src/de/danoeh/antennapod/activity/MainActivity.java +++ b/src/de/danoeh/antennapod/activity/MainActivity.java @@ -1,164 +1,295 @@ package de.danoeh.antennapod.activity; -import android.app.SearchManager; -import android.app.SearchableInfo; -import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.TypedArray; import android.media.AudioManager; +import android.os.AsyncTask; import android.os.Bundle; +import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.support.v4.view.MenuItemCompat; -import android.support.v4.view.ViewPager; +import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; -import android.support.v7.widget.SearchView; import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.Window; +import android.view.*; +import android.widget.AdapterView; +import android.widget.ListView; import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.NavListAdapter; import de.danoeh.antennapod.feed.EventDistributor; -import de.danoeh.antennapod.fragment.EpisodesFragment; -import de.danoeh.antennapod.fragment.ExternalPlayerFragment; -import de.danoeh.antennapod.fragment.FeedlistFragment; +import de.danoeh.antennapod.feed.Feed; +import de.danoeh.antennapod.fragment.*; import de.danoeh.antennapod.preferences.UserPreferences; -import de.danoeh.antennapod.service.download.DownloadService; -import de.danoeh.antennapod.service.playback.PlaybackService; import de.danoeh.antennapod.storage.DBReader; -import de.danoeh.antennapod.storage.DBTasks; -import de.danoeh.antennapod.storage.DownloadRequester; import de.danoeh.antennapod.util.StorageUtils; +import org.apache.commons.lang3.StringUtils; -import java.util.ArrayList; +import java.util.List; /** * The activity that is shown when the user launches the app. */ public class MainActivity extends ActionBarActivity { private static final String TAG = "MainActivity"; - private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED - | EventDistributor.DOWNLOAD_QUEUED; + | EventDistributor.DOWNLOAD_QUEUED + | EventDistributor.FEED_LIST_UPDATE + | EventDistributor.UNREAD_ITEMS_UPDATE; + + public static final String EXTRA_NAV_INDEX = "nav_index"; + public static final String EXTRA_NAV_TYPE = "nav_type"; + public static final String EXTRA_FRAGMENT_ARGS = "fragment_args"; + + public static final int POS_NEW = 0, + POS_QUEUE = 1, + POS_DOWNLOADS = 2, + POS_HISTORY = 3, + POS_ADD = 4; - private ViewPager viewpager; - private TabsAdapter pagerAdapter; private ExternalPlayerFragment externalPlayerFragment; + private DrawerLayout drawerLayout; + + private ListView navList; + private NavListAdapter navAdapter; + + private ActionBarDrawerToggle drawerToogle; + + private CharSequence drawerTitle; + private CharSequence currentTitle; - private static boolean appLaunched = false; @Override public void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getTheme()); super.onCreate(savedInstanceState); - StorageUtils.checkStorageAvailability(this); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + StorageUtils.checkStorageAvailability(this); setContentView(R.layout.main); setVolumeControlStream(AudioManager.STREAM_MUSIC); - getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + drawerTitle = currentTitle = getTitle(); - viewpager = (ViewPager) findViewById(R.id.viewpager); - pagerAdapter = new TabsAdapter(this, viewpager); + drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + navList = (ListView) findViewById(R.id.nav_list); - viewpager.setAdapter(pagerAdapter); + TypedArray typedArray = obtainStyledAttributes(new int[]{R.attr.nav_drawer_toggle}); + drawerToogle = new ActionBarDrawerToggle(this, drawerLayout, typedArray.getResourceId(0, 0), R.string.drawer_open, R.string.drawer_close) { + @Override + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + currentTitle = getSupportActionBar().getTitle(); + getSupportActionBar().setTitle(drawerTitle); + supportInvalidateOptionsMenu(); + } + + @Override + public void onDrawerClosed(View drawerView) { + super.onDrawerClosed(drawerView); + getSupportActionBar().setTitle(currentTitle); + supportInvalidateOptionsMenu(); + + } + }; + typedArray.recycle(); - ActionBar.Tab feedsTab = getSupportActionBar().newTab(); - feedsTab.setText(R.string.podcasts_label); - ActionBar.Tab episodesTab = getSupportActionBar().newTab(); - episodesTab.setText(R.string.episodes_label); + drawerLayout.setDrawerListener(drawerToogle); + FragmentManager fm = getSupportFragmentManager(); - pagerAdapter.addTab(feedsTab, FeedlistFragment.class, null); - pagerAdapter.addTab(episodesTab, EpisodesFragment.class, null); + FragmentTransaction transaction = fm.beginTransaction(); + + Fragment mainFragment = fm.findFragmentByTag("main"); + if (mainFragment != null) { + transaction.replace(R.id.main_view, mainFragment); + } else { + loadFragment(NavListAdapter.VIEW_TYPE_NAV, POS_NEW, null); + } - FragmentTransaction transaction = getSupportFragmentManager() - .beginTransaction(); externalPlayerFragment = new ExternalPlayerFragment(); transaction.replace(R.id.playerFragment, externalPlayerFragment); transaction.commit(); - // executed on application start - if (!appLaunched && getIntent().getAction() != null - && getIntent().getAction().equals(Intent.ACTION_MAIN)) { - appLaunched = true; - if (DBReader.getNumberOfUnreadItems(this) > 0) { - // select 'episodes' tab - getSupportActionBar().setSelectedNavigationItem(1); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + navAdapter = new NavListAdapter(itemAccess, this); + navList.setAdapter(navAdapter); + navList.setOnItemClickListener(navListClickListener); + + } + + public ActionBar getMainActivtyActionBar() { + return getSupportActionBar(); + } + + public List<Feed> getFeeds() { + return feeds; + } + + private void loadFragment(int viewType, int relPos, Bundle args) { + FragmentManager fragmentManager = getSupportFragmentManager(); + // clear back stack + for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) { + fragmentManager.popBackStack(); + } + + FragmentTransaction fT = fragmentManager.beginTransaction(); + Fragment fragment = null; + if (viewType == NavListAdapter.VIEW_TYPE_NAV) { + switch (relPos) { + case POS_NEW: + fragment = new NewEpisodesFragment(); + break; + case POS_QUEUE: + fragment = new QueueFragment(); + break; + case POS_DOWNLOADS: + fragment = new DownloadsFragment(); + break; + case POS_HISTORY: + fragment = new PlaybackHistoryFragment(); + break; + case POS_ADD: + fragment = new AddFeedFragment(); + break; + + } + currentTitle = getString(NavListAdapter.NAV_TITLES[relPos]); + selectedNavListIndex = relPos; + + } else if (viewType == NavListAdapter.VIEW_TYPE_SUBSCRIPTION) { + Feed feed = itemAccess.getItem(relPos); + currentTitle = ""; + fragment = ItemlistFragment.newInstance(feed.getId()); + selectedNavListIndex = NavListAdapter.SUBSCRIPTION_OFFSET + relPos; + + } + if (fragment != null) { + if (args != null) { + fragment.setArguments(args); } + fT.replace(R.id.main_view, fragment, "main"); + fragmentManager.popBackStack(); } + fT.commit(); + getSupportActionBar().setTitle(currentTitle); + if (navAdapter != null) { + navAdapter.notifyDataSetChanged(); + } + } + + public void loadNavFragment(int position, Bundle args) { + loadFragment(NavListAdapter.VIEW_TYPE_NAV, position, args); + } + + public void loadFeedFragment(long feedID) { + if (feeds != null) { + for (int i = 0; i < feeds.size(); i++) { + if (feeds.get(i).getId() == feedID) { + loadFragment(NavListAdapter.VIEW_TYPE_SUBSCRIPTION, i, null); + break; + } + } + } + } + + public void loadChildFragment(Fragment fragment) { + if (fragment == null) throw new IllegalArgumentException("fragment = null"); + FragmentManager fm = getSupportFragmentManager(); + fm.beginTransaction() + .replace(R.id.main_view, fragment, "main") + .addToBackStack(null) + .commit(); + } + + private AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + int viewType = parent.getAdapter().getItemViewType(position); + if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER && position != selectedNavListIndex) { + int relPos = (viewType == NavListAdapter.VIEW_TYPE_NAV) ? position : position - NavListAdapter.SUBSCRIPTION_OFFSET; + loadFragment(viewType, relPos, null); + selectedNavListIndex = position; + navAdapter.notifyDataSetChanged(); + } + drawerLayout.closeDrawer(navList); + } + }; + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + drawerToogle.syncState(); if (savedInstanceState != null) { - getSupportActionBar().setSelectedNavigationItem( - savedInstanceState.getInt("tab", 0)); + currentTitle = savedInstanceState.getString("title"); + if (!drawerLayout.isDrawerOpen(navList)) { + getSupportActionBar().setTitle(currentTitle); + } + selectedNavListIndex = savedInstanceState.getInt("selectedNavIndex"); } } @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + drawerToogle.onConfigurationChanged(newConfig); + } + + @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putInt("tab", getSupportActionBar() - .getSelectedNavigationIndex()); + outState.putString("title", getSupportActionBar().getTitle().toString()); + outState.putInt("selectedNavIndex", selectedNavListIndex); + } @Override protected void onPause() { super.onPause(); - EventDistributor.getInstance().unregister(contentUpdate); } @Override protected void onResume() { super.onResume(); StorageUtils.checkStorageAvailability(this); - updateProgressBarVisibility(); EventDistributor.getInstance().register(contentUpdate); - } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((EVENTS & arg) != 0) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Received contentUpdate Intent."); - updateProgressBarVisibility(); + Intent intent = getIntent(); + if (StringUtils.equals(intent.getAction(), Intent.ACTION_SEND)) { + String extra = intent.getStringExtra(Intent.EXTRA_TEXT); + if (extra != null) { + Bundle args = new Bundle(); + args.putString(AddFeedFragment.ARG_FEED_URL, extra); + loadFragment(NavListAdapter.VIEW_TYPE_NAV, POS_ADD, args); + selectedNavListIndex = POS_ADD; + navAdapter.notifyDataSetChanged(); } + } else if (feeds != null && intent.hasExtra(EXTRA_NAV_INDEX) && intent.hasExtra(EXTRA_NAV_TYPE)) { + handleNavIntent(); } - }; - private void updateProgressBarVisibility() { - if (DownloadService.isRunning - && DownloadRequester.getInstance().isDownloadingFeeds()) { - setSupportProgressBarIndeterminateVisibility(true); - } else { - setSupportProgressBarIndeterminateVisibility(false); - } - supportInvalidateOptionsMenu(); + loadData(); + } + + @Override + protected void onStop() { + super.onStop(); + cancelLoadTask(); + EventDistributor.getInstance().unregister(contentUpdate); } @Override public boolean onOptionsItemSelected(MenuItem item) { + if (drawerToogle.onOptionsItemSelected(item)) { + return true; + } switch (item.getItemId()) { - case R.id.add_feed: - startActivity(new Intent(this, AddFeedActivity.class)); - return true; - case R.id.all_feed_refresh: - DBTasks.refreshAllFeeds(this, null); - return true; - case R.id.show_downloads: - startActivity(new Intent(this, DownloadActivity.class)); - return true; case R.id.show_preferences: startActivity(new Intent(this, PreferenceActivity.class)); return true; - case R.id.show_player: - startActivity(PlaybackService.getPlayerActivityIntent(this)); - return true; - case R.id.show_playback_history: - startActivity(new Intent(this, PlaybackHistoryActivity.class)); - return true; default: return super.onOptionsItemSelected(item); } @@ -167,13 +298,7 @@ public class MainActivity extends ActionBarActivity { @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - MenuItem refreshAll = menu.findItem(R.id.all_feed_refresh); - if (DownloadService.isRunning - && DownloadRequester.getInstance().isDownloadingFeeds()) { - refreshAll.setVisible(false); - } else { - refreshAll.setVisible(true); - } + return true; } @@ -182,104 +307,96 @@ public class MainActivity extends ActionBarActivity { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); - - SearchManager searchManager = - (SearchManager) getSystemService(Context.SEARCH_SERVICE); - MenuItem searchItem = menu.findItem(R.id.search_item); - SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); - if (searchView == null) { - MenuItemCompat.setActionView(searchItem, new SearchView(this)); - searchView = (SearchView) MenuItemCompat.getActionView(searchItem); - } - searchView.setIconifiedByDefault(true); - - SearchableInfo info = searchManager.getSearchableInfo(getComponentName()); - searchView.setSearchableInfo( - searchManager.getSearchableInfo(getComponentName())); - - return true; } - public static class TabsAdapter extends FragmentPagerAdapter implements - ActionBar.TabListener, ViewPager.OnPageChangeListener { - private final Context mContext; - private final ActionBar mActionBar; - private final ViewPager mViewPager; - private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); - - static final class TabInfo { - private final Class<?> clss; - private final Bundle args; - - TabInfo(Class<?> _class, Bundle _args) { - clss = _class; - args = _args; - } - } - - public TabsAdapter(MainActivity activity, ViewPager pager) { - super(activity.getSupportFragmentManager()); - mContext = activity; - mActionBar = activity.getSupportActionBar(); - mViewPager = pager; - mViewPager.setAdapter(this); - mViewPager.setOnPageChangeListener(this); - } - - public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) { - TabInfo info = new TabInfo(clss, args); - tab.setTag(info); - tab.setTabListener(this); - mTabs.add(info); - mActionBar.addTab(tab); - notifyDataSetChanged(); - } + private List<Feed> feeds; + private AsyncTask<Void, Void, List<Feed>> loadTask; + private int selectedNavListIndex = 0; + private NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { @Override public int getCount() { - return mTabs.size(); + if (feeds != null) { + return feeds.size(); + } else { + return 0; + } } @Override - public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), - info.args); + public Feed getItem(int position) { + if (feeds != null && position < feeds.size()) { + return feeds.get(position); + } else { + return null; + } } @Override - public void onPageScrolled(int position, float positionOffset, - int positionOffsetPixels) { + public int getSelectedItemIndex() { + return selectedNavListIndex; } - @Override - public void onPageSelected(int position) { - mActionBar.setSelectedNavigationItem(position); - } - @Override - public void onPageScrollStateChanged(int state) { - } + }; - @Override - public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { - Object tag = tab.getTag(); - for (int i = 0; i < mTabs.size(); i++) { - if (mTabs.get(i) == tag) { - mViewPager.setCurrentItem(i); + private void loadData() { + cancelLoadTask(); + loadTask = new AsyncTask<Void, Void, List<Feed>>() { + @Override + protected List<Feed> doInBackground(Void... params) { + return DBReader.getFeedList(MainActivity.this); + } + + @Override + protected void onPostExecute(List<Feed> result) { + super.onPostExecute(result); + boolean handleIntent = (feeds == null); + + feeds = result; + navAdapter.notifyDataSetChanged(); + + if (handleIntent) { + handleNavIntent(); } } + }; + loadTask.execute(); + } + + private void cancelLoadTask() { + if (loadTask != null) { + loadTask.cancel(true); } + } - @Override - public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((EVENTS & arg) != 0) { + if (BuildConfig.DEBUG) + Log.d(TAG, "Received contentUpdate Intent."); + loadData(); + } } + }; - @Override - public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { + private void handleNavIntent() { + Intent intent = getIntent(); + if (intent.hasExtra(EXTRA_NAV_INDEX) && intent.hasExtra(EXTRA_NAV_TYPE)) { + int index = intent.getIntExtra(EXTRA_NAV_INDEX, 0); + int type = intent.getIntExtra(EXTRA_NAV_TYPE, NavListAdapter.VIEW_TYPE_NAV); + Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS); + loadFragment(type, index, args); } + setIntent(new Intent(MainActivity.this, MainActivity.class)); // to avoid handling the intent twice when the configuration changes } + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + setIntent(intent); + } } diff --git a/src/de/danoeh/antennapod/activity/SearchActivity.java b/src/de/danoeh/antennapod/activity/SearchActivity.java index f330aeb7d..02c0838b3 100644 --- a/src/de/danoeh/antennapod/activity/SearchActivity.java +++ b/src/de/danoeh/antennapod/activity/SearchActivity.java @@ -58,7 +58,7 @@ public class SearchActivity extends ActionBarActivity implements AdapterView.OnI txtvStatus = (TextView) findViewById(android.R.id.empty); listView.setOnItemClickListener(this); - searchAdapter = new SearchlistAdapter(this, 0, new ArrayList<SearchResult>()); + //searchAdapter = new SearchlistAdapter(this, 0, new ArrayList<SearchResult>()); listView.setAdapter(searchAdapter); listView.setEmptyView(txtvStatus); } @@ -131,7 +131,7 @@ public class SearchActivity extends ActionBarActivity implements AdapterView.OnI @SuppressLint({"NewApi", "NewApi"}) private void handleSearchRequest(final String query) { if (searchAdapter != null) { - searchAdapter.clear(); + // searchAdapter.clear(); searchAdapter.notifyDataSetChanged(); } txtvStatus.setText(R.string.search_status_searching); @@ -154,9 +154,9 @@ public class SearchActivity extends ActionBarActivity implements AdapterView.OnI Log.d(TAG, "Found " + result.size() + " results"); - searchAdapter.clear(); + // searchAdapter.clear(); for (SearchResult s : result) { - searchAdapter.add(s); + // searchAdapter.add(s); } searchAdapter.notifyDataSetChanged(); txtvStatus diff --git a/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java new file mode 100644 index 000000000..78d62a8de --- /dev/null +++ b/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java @@ -0,0 +1,63 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.content.res.TypedArray; +import android.view.View; +import android.widget.ImageButton; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.storage.DownloadRequester; + +/** + * Utility methods for the action button that is displayed on the right hand side + * of a listitem. + */ +public class ActionButtonUtils { + + private final int[] labels; + private final TypedArray drawables; + private final Context context; + + public ActionButtonUtils(Context context) { + if (context == null) throw new IllegalArgumentException("context = null"); + this.context = context; + drawables = context.obtainStyledAttributes(new int[]{ + R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download}); + labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label}; + } + + /** + * Sets the displayed bitmap and content description of the given + * action button so that it matches the state of the FeedItem. + */ + public void configureActionButton(ImageButton butSecondary, FeedItem item) { + if (butSecondary == null || item == null) throw new IllegalArgumentException("butSecondary or item was null"); + final FeedMedia media = item.getMedia(); + if (media != null) { + final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); + if (!media.isDownloaded()) { + if (isDownloadingMedia) { + // item is being downloaded + butSecondary.setVisibility(View.VISIBLE); + butSecondary.setImageDrawable(drawables + .getDrawable(1)); + butSecondary.setContentDescription(context.getString(labels[1])); + } else { + // item is not downloaded and not being downloaded + butSecondary.setVisibility(View.VISIBLE); + butSecondary.setImageDrawable(drawables.getDrawable(2)); + butSecondary.setContentDescription(context.getString(labels[2])); + } + } else { + // item is not being downloaded + butSecondary.setVisibility(View.VISIBLE); + butSecondary + .setImageDrawable(drawables.getDrawable(0)); + butSecondary.setContentDescription(context.getString(labels[0])); + } + } else { + butSecondary.setVisibility(View.INVISIBLE); + } + } +} diff --git a/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java new file mode 100644 index 000000000..3acd587af --- /dev/null +++ b/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java @@ -0,0 +1,49 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.widget.Toast; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.storage.DBTasks; +import de.danoeh.antennapod.storage.DownloadRequestException; +import de.danoeh.antennapod.storage.DownloadRequester; + +/** + * Default implementation of an ActionButtonCallback + */ +public class DefaultActionButtonCallback implements ActionButtonCallback { + private static final String TAG = "DefaultActionButtonCallback"; + + private final Context context; + + public DefaultActionButtonCallback(Context context) { + if (context == null) throw new IllegalArgumentException("context = null"); + this.context = context; + } + + @Override + public void onActionButtonPressed(final FeedItem item) { + final FeedMedia media = item.getMedia(); + if (media == null) { + return; + } + + boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media); + if (!isDownloading && !media.isDownloaded()) { + try { + DBTasks.downloadFeedItems(context, item); + Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show(); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage()); + } + } else if (isDownloading) { + DownloadRequester.getInstance().cancelDownload(context, media); + Toast.makeText(context, R.string.download_cancelled_msg, Toast.LENGTH_SHORT).show(); + } else { // media is downloaded + DBTasks.playMedia(context, media, true, true, false); + } + } +} diff --git a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java new file mode 100644 index 000000000..e873a9e5a --- /dev/null +++ b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java @@ -0,0 +1,124 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.asynctask.ImageLoader; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.util.Converter; + +/** + * Shows a list of downloaded episodes + */ +public class DownloadedEpisodesListAdapter extends BaseAdapter { + + private final Context context; + private final ItemAccess itemAccess; + + public DownloadedEpisodesListAdapter(Context context, ItemAccess itemAccess) { + super(); + this.context = context; + this.itemAccess = itemAccess; + } + + @Override + public int getCount() { + return itemAccess.getCount(); + } + + @Override + public Object getItem(int position) { + return itemAccess.getItem(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Holder holder; + final FeedItem item = (FeedItem) getItem(position); + if (item == null) return null; + + if (convertView == null) { + holder = new Holder(); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = inflater.inflate(R.layout.downloaded_episodeslist_item, + null); + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.pubDate = (TextView) convertView + .findViewById(R.id.txtvPublished); + holder.butSecondary = (ImageButton) convertView + .findViewById(R.id.butSecondaryAction); + holder.statusPlaying = (ImageView) convertView + .findViewById(R.id.statusPlaying); + holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); + holder.txtvSize = (TextView) convertView.findViewById(R.id.txtvSize); + convertView.setTag(holder); + } else { + holder = (Holder) convertView.getTag(); + } + + holder.title.setText(item.getTitle()); + holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_SHOW_DATE)); + holder.txtvSize.setText(Converter.byteToString(item.getMedia().getSize())); + FeedItem.State state = item.getState(); + + if (state == FeedItem.State.PLAYING) { + holder.statusPlaying.setVisibility(View.VISIBLE); + holder.butSecondary.setEnabled(false); + } else { + holder.butSecondary.setEnabled(true); + holder.statusPlaying.setVisibility(View.INVISIBLE); + } + + holder.butSecondary.setFocusable(false); + holder.butSecondary.setTag(item); + holder.butSecondary.setOnClickListener(secondaryActionListener); + + + ImageLoader.getInstance().loadThumbnailBitmap( + item, + holder.imageView, + (int) convertView.getResources().getDimension( + R.dimen.thumbnail_length) + ); + return convertView; + } + + private View.OnClickListener secondaryActionListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + FeedItem item = (FeedItem) v.getTag(); + itemAccess.onFeedItemSecondaryAction(item); + } + }; + + + static class Holder { + TextView title; + TextView pubDate; + ImageView imageView; + ImageView statusPlaying; + TextView txtvSize; + ImageButton butSecondary; + } + + public interface ItemAccess { + int getCount(); + + FeedItem getItem(int position); + + void onFeedItemSecondaryAction(FeedItem item); + } +} diff --git a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java index 2739d2f27..fa2e5a0a7 100644 --- a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java +++ b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java @@ -4,7 +4,8 @@ import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; +import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.TextView; import de.danoeh.antennapod.R; @@ -14,86 +15,128 @@ import de.danoeh.antennapod.service.download.Downloader; import de.danoeh.antennapod.util.Converter; import de.danoeh.antennapod.util.ThemeUtils; -import java.util.List; - -public class DownloadlistAdapter extends ArrayAdapter<Downloader> { - private int selectedItemIndex; - - public static final int SELECTION_NONE = -1; - - public DownloadlistAdapter(Context context, int textViewResourceId, - List<Downloader> objects) { - super(context, textViewResourceId, objects); - this.selectedItemIndex = SELECTION_NONE; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Holder holder; - DownloadRequest request = getItem(position).getDownloadRequest(); - // Inflate layout - if (convertView == null) { - holder = new Holder(); - LayoutInflater inflater = (LayoutInflater) getContext() - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = inflater.inflate(R.layout.downloadlist_item, null); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.message = (TextView) convertView - .findViewById(R.id.txtvMessage); - holder.downloaded = (TextView) convertView - .findViewById(R.id.txtvDownloaded); - holder.percent = (TextView) convertView - .findViewById(R.id.txtvPercent); - holder.progbar = (ProgressBar) convertView - .findViewById(R.id.progProgress); - - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - } - - if (position == selectedItemIndex) { - convertView.setBackgroundColor(convertView.getResources().getColor( - ThemeUtils.getSelectionBackgroundColor())); - } else { - convertView.setBackgroundResource(0); - } - - holder.title.setText(request.getTitle()); - if (request.getStatusMsg() != 0) { - holder.message.setText(request.getStatusMsg()); - } - String strDownloaded = Converter.byteToString(request.getSoFar()); - if (request.getSize() != DownloadStatus.SIZE_UNKNOWN) { - strDownloaded += " / " + Converter.byteToString(request.getSize()); - holder.percent.setText(request.getProgressPercent() + "%"); - holder.progbar.setProgress(request.getProgressPercent()); - holder.percent.setVisibility(View.VISIBLE); - } else { - holder.progbar.setProgress(0); - holder.percent.setVisibility(View.INVISIBLE); - } - - holder.downloaded.setText(strDownloaded); - - return convertView; - } - - static class Holder { - TextView title; - TextView message; - TextView downloaded; - TextView percent; - ProgressBar progbar; - } - - public int getSelectedItemIndex() { - return selectedItemIndex; - } - - public void setSelectedItemIndex(int selectedItemIndex) { - this.selectedItemIndex = selectedItemIndex; - notifyDataSetChanged(); - } +public class DownloadlistAdapter extends BaseAdapter { + + public static final int SELECTION_NONE = -1; + + private int selectedItemIndex; + private ItemAccess itemAccess; + private Context context; + + public DownloadlistAdapter(Context context, + ItemAccess itemAccess) { + super(); + this.selectedItemIndex = SELECTION_NONE; + this.context = context; + this.itemAccess = itemAccess; + } + + @Override + public int getCount() { + return itemAccess.getCount(); + } + + @Override + public Downloader getItem(int position) { + return itemAccess.getItem(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Holder holder; + Downloader downloader = getItem(position); + DownloadRequest request = downloader.getDownloadRequest(); + // Inflate layout + if (convertView == null) { + holder = new Holder(); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = inflater.inflate(R.layout.downloadlist_item, null); + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.message = (TextView) convertView + .findViewById(R.id.txtvMessage); + holder.downloaded = (TextView) convertView + .findViewById(R.id.txtvDownloaded); + holder.percent = (TextView) convertView + .findViewById(R.id.txtvPercent); + holder.progbar = (ProgressBar) convertView + .findViewById(R.id.progProgress); + holder.butSecondary = (ImageButton) convertView + .findViewById(R.id.butSecondaryAction); + + convertView.setTag(holder); + } else { + holder = (Holder) convertView.getTag(); + } + + if (position == selectedItemIndex) { + convertView.setBackgroundColor(convertView.getResources().getColor( + ThemeUtils.getSelectionBackgroundColor())); + } else { + convertView.setBackgroundResource(0); + } + + holder.title.setText(request.getTitle()); + if (request.getStatusMsg() != 0) { + holder.message.setText(request.getStatusMsg()); + } + String strDownloaded = Converter.byteToString(request.getSoFar()); + if (request.getSize() != DownloadStatus.SIZE_UNKNOWN) { + strDownloaded += " / " + Converter.byteToString(request.getSize()); + holder.percent.setText(request.getProgressPercent() + "%"); + holder.progbar.setProgress(request.getProgressPercent()); + holder.percent.setVisibility(View.VISIBLE); + } else { + holder.progbar.setProgress(0); + holder.percent.setVisibility(View.INVISIBLE); + } + + holder.downloaded.setText(strDownloaded); + + holder.butSecondary.setFocusable(false); + holder.butSecondary.setTag(downloader); + holder.butSecondary.setOnClickListener(butSecondaryListener); + + return convertView; + } + + private View.OnClickListener butSecondaryListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + Downloader downloader = (Downloader) v.getTag(); + itemAccess.onSecondaryActionClick(downloader); + } + }; + + static class Holder { + TextView title; + TextView message; + TextView downloaded; + TextView percent; + ProgressBar progbar; + ImageButton butSecondary; + } + + public int getSelectedItemIndex() { + return selectedItemIndex; + } + + public void setSelectedItemIndex(int selectedItemIndex) { + this.selectedItemIndex = selectedItemIndex; + notifyDataSetChanged(); + } + + public interface ItemAccess { + public int getCount(); + + public Downloader getItem(int position); + + public void onSecondaryActionClick(Downloader downloader); + } } diff --git a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java index aa724f991..9a7b607aa 100644 --- a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java @@ -227,10 +227,10 @@ public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.feeditemlist_header, null); TextView headerTitle = (TextView) convertView - .findViewById(R.id.txtvHeaderTitle); + .findViewById(0); ImageButton actionButton = (ImageButton) convertView .findViewById(R.id.butAction); - TextView numItems = (TextView) convertView.findViewById(R.id.txtvNumItems); + TextView numItems = (TextView) convertView.findViewById(0); String headerString = null; int childrenCount = 0; diff --git a/src/de/danoeh/antennapod/adapter/InternalFeedItemlistAdapter.java b/src/de/danoeh/antennapod/adapter/InternalFeedItemlistAdapter.java index 4681284f5..356d75d99 100644 --- a/src/de/danoeh/antennapod/adapter/InternalFeedItemlistAdapter.java +++ b/src/de/danoeh/antennapod/adapter/InternalFeedItemlistAdapter.java @@ -12,6 +12,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.feed.FeedItem; import de.danoeh.antennapod.feed.FeedMedia; import de.danoeh.antennapod.feed.MediaType; +import de.danoeh.antennapod.service.playback.PlayerStatus; import de.danoeh.antennapod.storage.DownloadRequester; import de.danoeh.antennapod.util.Converter; import de.danoeh.antennapod.util.ThemeUtils; @@ -22,6 +23,7 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { private ActionButtonCallback callback; private boolean showFeedtitle; private int selectedItemIndex; + private final ActionButtonUtils actionButtonUtils; public static final int SELECTION_NONE = -1; @@ -32,6 +34,7 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { this.callback = callback; this.showFeedtitle = showFeedtitle; this.selectedItemIndex = SELECTION_NONE; + this.actionButtonUtils = new ActionButtonUtils(context); } @Override @@ -49,20 +52,12 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { holder.lenSize = (TextView) convertView .findViewById(R.id.txtvLenSize); holder.butAction = (ImageButton) convertView - .findViewById(R.id.butAction); + .findViewById(R.id.butSecondaryAction); holder.published = (TextView) convertView .findViewById(R.id.txtvPublished); holder.inPlaylist = (ImageView) convertView .findViewById(R.id.imgvInPlaylist); - holder.downloaded = (ImageView) convertView - .findViewById(R.id.imgvDownloaded); holder.type = (ImageView) convertView.findViewById(R.id.imgvType); - holder.downloading = (ImageView) convertView - .findViewById(R.id.imgvDownloading); - if (showFeedtitle) { - holder.feedtitle = (TextView) convertView - .findViewById(R.id.txtvFeedname); - } holder.statusPlaying = (View) convertView .findViewById(R.id.statusPlaying); holder.statusUnread = (View) convertView @@ -83,11 +78,13 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { convertView.setBackgroundResource(0); } - holder.title.setText(item.getTitle()); - if (showFeedtitle) { - holder.feedtitle.setVisibility(View.VISIBLE); - holder.feedtitle.setText(item.getFeed().getTitle()); - } + StringBuilder buffer = new StringBuilder(item.getTitle()); + if (showFeedtitle) { + buffer.append("("); + buffer.append(item.getFeed().getTitle()); + buffer.append(")"); + } + holder.title.setText(buffer.toString()); FeedItem.State state = item.getState(); switch (state) { @@ -104,12 +101,10 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { case NEW: holder.statusPlaying.setVisibility(View.GONE); holder.statusUnread.setVisibility(View.VISIBLE); - holder.episodeProgress.setVisibility(View.GONE); break; default: holder.statusPlaying.setVisibility(View.GONE); holder.statusUnread.setVisibility(View.GONE); - holder.episodeProgress.setVisibility(View.GONE); break; } @@ -121,11 +116,10 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { FeedMedia media = item.getMedia(); if (media == null) { - holder.downloaded.setVisibility(View.GONE); - holder.downloading.setVisibility(View.GONE); - holder.inPlaylist.setVisibility(View.GONE); - holder.type.setVisibility(View.GONE); - holder.lenSize.setVisibility(View.GONE); + holder.episodeProgress.setVisibility(View.GONE); + holder.inPlaylist.setVisibility(View.INVISIBLE); + holder.type.setVisibility(View.INVISIBLE); + holder.lenSize.setVisibility(View.INVISIBLE); } else { if (state == FeedItem.State.PLAYING @@ -153,19 +147,16 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { if (((ItemAccess) itemAccess).isInQueue(item)) { holder.inPlaylist.setVisibility(View.VISIBLE); } else { - holder.inPlaylist.setVisibility(View.GONE); - } - if (item.getMedia().isDownloaded()) { - holder.downloaded.setVisibility(View.VISIBLE); - } else { - holder.downloaded.setVisibility(View.GONE); + holder.inPlaylist.setVisibility(View.INVISIBLE); } if (DownloadRequester.getInstance().isDownloadingFile( item.getMedia())) { - holder.downloading.setVisibility(View.VISIBLE); - } else { - holder.downloading.setVisibility(View.GONE); + holder.episodeProgress.setVisibility(View.VISIBLE); + holder.episodeProgress.setProgress(((ItemAccess) itemAccess).getItemDownloadProgressPercent(item)); + } else if (!(state == FeedItem.State.IN_PROGRESS + || state == FeedItem.State.PLAYING)) { + holder.episodeProgress.setVisibility(View.GONE); } TypedArray typeDrawables = getContext().obtainStyledAttributes( @@ -187,14 +178,10 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { } } + actionButtonUtils.configureActionButton(holder.butAction, item); holder.butAction.setFocusable(false); - holder.butAction.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - callback.onActionButtonPressed(item); - } - }); + holder.butAction.setTag(item); + holder.butAction.setOnClickListener(butActionListener); } else { convertView.setVisibility(View.GONE); @@ -203,11 +190,16 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { } + private final OnClickListener butActionListener = new OnClickListener() { + @Override + public void onClick(View v) { + FeedItem item = (FeedItem) v.getTag(); + callback.onActionButtonPressed(item); + } + }; + static class Holder extends DefaultFeedItemlistAdapter.Holder { - TextView feedtitle; ImageView inPlaylist; - ImageView downloaded; - ImageView downloading; ImageButton butAction; View statusUnread; View statusPlaying; @@ -225,6 +217,8 @@ public class InternalFeedItemlistAdapter extends DefaultFeedItemlistAdapter { public static interface ItemAccess extends DefaultFeedItemlistAdapter.ItemAccess { public boolean isInQueue(FeedItem item); + + int getItemDownloadProgressPercent(FeedItem item); } } diff --git a/src/de/danoeh/antennapod/adapter/NavListAdapter.java b/src/de/danoeh/antennapod/adapter/NavListAdapter.java new file mode 100644 index 000000000..084f3609f --- /dev/null +++ b/src/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -0,0 +1,186 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.graphics.Typeface; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.asynctask.ImageLoader; +import de.danoeh.antennapod.feed.Feed; + +/** + * BaseAdapter for the navigation drawer + */ +public class NavListAdapter extends BaseAdapter { + public static final int VIEW_TYPE_COUNT = 3; + public static final int VIEW_TYPE_NAV = 0; + public static final int VIEW_TYPE_SECTION_DIVIDER = 1; + public static final int VIEW_TYPE_SUBSCRIPTION = 2; + + public static final int[] NAV_TITLES = {R.string.new_episodes_label, R.string.queue_label, R.string.downloads_label, R.string.playback_history_label, R.string.add_feed_label}; + + + public static final int SUBSCRIPTION_OFFSET = 1 + NAV_TITLES.length; + + private ItemAccess itemAccess; + private Context context; + + public NavListAdapter(ItemAccess itemAccess, Context context) { + this.itemAccess = itemAccess; + this.context = context; + } + + @Override + public int getCount() { + return NAV_TITLES.length + 1 + itemAccess.getCount(); + } + + @Override + public Object getItem(int position) { + int viewType = getItemViewType(position); + if (viewType == VIEW_TYPE_NAV) { + return context.getString(NAV_TITLES[position]); + } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) { + return context.getString(R.string.podcasts_label); + } else { + return itemAccess.getItem(position); + } + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemViewType(int position) { + if (0 <= position && position < NAV_TITLES.length) { + return VIEW_TYPE_NAV; + } else if (position < NAV_TITLES.length + 1) { + return VIEW_TYPE_SECTION_DIVIDER; + } else { + return VIEW_TYPE_SUBSCRIPTION; + } + } + + @Override + public int getViewTypeCount() { + return VIEW_TYPE_COUNT; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + int viewType = getItemViewType(position); + View v = null; + if (viewType == VIEW_TYPE_NAV) { + v = getNavView((String) getItem(position), position, convertView, parent); + } else if (viewType == VIEW_TYPE_SECTION_DIVIDER) { + v = getSectionDividerView((String) getItem(position), position, convertView, parent); + } else { + v = getFeedView(position - SUBSCRIPTION_OFFSET, convertView, parent); + } + if (v != null) { + TextView txtvTitle = (TextView) v.findViewById(R.id.txtvTitle); + if (position == itemAccess.getSelectedItemIndex()) { + txtvTitle.setTypeface(null, Typeface.BOLD); + } else { + txtvTitle.setTypeface(null, Typeface.NORMAL); + } + } + return v; + } + + private View getNavView(String title, int position, View convertView, ViewGroup parent) { + NavHolder holder; + if (convertView == null) { + holder = new NavHolder(); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + convertView = inflater.inflate(R.layout.nav_listitem, null); + + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + convertView.setTag(holder); + } else { + holder = (NavHolder) convertView.getTag(); + } + + holder.title.setText(title); + + return convertView; + } + + private View getSectionDividerView(String title, int position, View convertView, ViewGroup parent) { + SectionHolder holder; + if (convertView == null) { + holder = new SectionHolder(); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + convertView = inflater.inflate(R.layout.nav_section_item, null); + + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + convertView.setTag(holder); + } else { + holder = (SectionHolder) convertView.getTag(); + } + + holder.title.setText(title); + + convertView.setEnabled(false); + convertView.setOnClickListener(null); + + return convertView; + } + + private View getFeedView(int feedPos, View convertView, ViewGroup parent) { + FeedHolder holder; + Feed feed = itemAccess.getItem(feedPos); + + if (convertView == null) { + holder = new FeedHolder(); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + convertView = inflater.inflate(R.layout.nav_feedlistitem, null); + + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.image = (ImageView) convertView.findViewById(R.id.imgvCover); + convertView.setTag(holder); + } else { + holder = (FeedHolder) convertView.getTag(); + } + + holder.title.setText(feed.getTitle()); + ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), holder.image, (int) context.getResources().getDimension(R.dimen.thumbnail_length_navlist)); + + return convertView; + } + + static class NavHolder { + TextView title; + } + + static class SectionHolder { + TextView title; + } + + static class FeedHolder { + TextView title; + ImageView image; + } + + + public interface ItemAccess { + public int getCount(); + + public Feed getItem(int position); + + public int getSelectedItemIndex(); + } + +} diff --git a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java new file mode 100644 index 000000000..d13314b0b --- /dev/null +++ b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java @@ -0,0 +1,217 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.*; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.asynctask.ImageLoader; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.storage.DownloadRequester; +import de.danoeh.antennapod.util.Converter; + +/** + * List adapter for the list of new episodes + */ +public class NewEpisodesListAdapter extends BaseAdapter { + + private static final int VIEW_TYPE_FEEDITEM = 0; + private static final int VIEW_TYPE_DIVIDER = 1; + + private final Context context; + private final ItemAccess itemAccess; + private final ActionButtonCallback actionButtonCallback; + private final ActionButtonUtils actionButtonUtils; + + public NewEpisodesListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) { + super(); + this.context = context; + this.itemAccess = itemAccess; + this.actionButtonUtils = new ActionButtonUtils(context); + this.actionButtonCallback = actionButtonCallback; + } + + @Override + public int getCount() { + int unreadItems = itemAccess.getUnreadItemsCount(); + int recentItems = itemAccess.getRecentItemsCount(); + return unreadItems + recentItems + 1; + } + + @Override + public Object getItem(int position) { + int unreadItems = itemAccess.getUnreadItemsCount(); + + if (position == unreadItems) { + return null; + } + if (position < unreadItems && unreadItems > 0) { + return itemAccess.getUnreadItem(position); + } + return itemAccess.getRecentItem(position - unreadItems - 1); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemViewType(int position) { + int unreadItems = itemAccess.getUnreadItemsCount(); + if (position == unreadItems) { + return VIEW_TYPE_DIVIDER; + } else { + return VIEW_TYPE_FEEDITEM; + } + } + + @Override + public int getViewTypeCount() { + return 2; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + int viewType = getItemViewType(position); + if (viewType == VIEW_TYPE_FEEDITEM) { + return getFeedItemView(position, convertView, parent); + } else { + return getDividerView(position, convertView, parent); + } + } + + public View getDividerView(int position, View convertView, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = inflater.inflate(R.layout.new_episodes_listdivider, + null); + convertView.setOnClickListener(null); + convertView.setEnabled(false); + return convertView; + } + + public View getFeedItemView(int position, View convertView, ViewGroup parent) { + Holder holder; + final FeedItem item = (FeedItem) getItem(position); + if (item == null) return null; + + if (convertView == null) { + holder = new Holder(); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = inflater.inflate(R.layout.new_episodes_listitem, + null); + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.pubDate = (TextView) convertView + .findViewById(R.id.txtvPublished); + holder.butSecondary = (ImageButton) convertView + .findViewById(R.id.butSecondaryAction); + holder.queueStatus = (ImageView) convertView + .findViewById(R.id.imgvInPlaylist); + holder.statusPlaying = (ImageView) convertView + .findViewById(R.id.statusPlaying); + holder.downloadProgress = (ProgressBar) convertView + .findViewById(R.id.pbar_download_progress); + holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); + holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration); + convertView.setTag(holder); + } else { + holder = (Holder) convertView.getTag(); + } + + holder.title.setText(item.getTitle()); + holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_SHOW_DATE)); + FeedItem.State state = item.getState(); + + if (state == FeedItem.State.PLAYING) { + holder.statusPlaying.setVisibility(View.VISIBLE); + } else { + holder.statusPlaying.setVisibility(View.INVISIBLE); + } + + FeedMedia media = item.getMedia(); + if (media != null) { + final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); + + if (media.getDuration() > 0) { + holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration())); + } else { + holder.txtvDuration.setText(""); + } + + if (isDownloadingMedia) { + holder.downloadProgress.setVisibility(View.VISIBLE); + holder.txtvDuration.setVisibility(View.GONE); + } else { + holder.txtvDuration.setVisibility(View.VISIBLE); + holder.downloadProgress.setVisibility(View.GONE); + } + + if (!media.isDownloaded()) { + if (isDownloadingMedia) { + // item is being downloaded + holder.downloadProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); + } + } + } + if (itemAccess.isInQueue(item)) { + holder.queueStatus.setVisibility(View.VISIBLE); + } else { + holder.queueStatus.setVisibility(View.INVISIBLE); + } + + actionButtonUtils.configureActionButton(holder.butSecondary, item); + holder.butSecondary.setFocusable(false); + holder.butSecondary.setTag(item); + holder.butSecondary.setOnClickListener(secondaryActionListener); + + + + ImageLoader.getInstance().loadThumbnailBitmap( + item, + holder.imageView, + (int) convertView.getResources().getDimension( + R.dimen.thumbnail_length) + ); + return convertView; + } + + private View.OnClickListener secondaryActionListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + FeedItem item = (FeedItem) v.getTag(); + actionButtonCallback.onActionButtonPressed(item); + } + }; + + + static class Holder { + TextView title; + TextView pubDate; + ImageView queueStatus; + ImageView imageView; + ImageView statusPlaying; + ProgressBar downloadProgress; + TextView txtvDuration; + ImageButton butSecondary; + } + + public interface ItemAccess { + int getUnreadItemsCount(); + + int getRecentItemsCount(); + + FeedItem getUnreadItem(int position); + + FeedItem getRecentItem(int position); + + int getItemDownloadProgressPercent(FeedItem item); + + boolean isInQueue(FeedItem item); + } +} diff --git a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java new file mode 100644 index 000000000..c671f4a5c --- /dev/null +++ b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java @@ -0,0 +1,155 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.*; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.asynctask.ImageLoader; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.storage.DownloadRequester; +import de.danoeh.antennapod.util.Converter; + +/** + * List adapter for the queue. + */ +public class QueueListAdapter extends BaseAdapter { + + + private final Context context; + private final ItemAccess itemAccess; + private final ActionButtonCallback actionButtonCallback; + private final ActionButtonUtils actionButtonUtils; + + public QueueListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) { + super(); + this.context = context; + this.itemAccess = itemAccess; + this.actionButtonUtils = new ActionButtonUtils(context); + this.actionButtonCallback = actionButtonCallback; + + } + + @Override + public int getCount() { + return itemAccess.getCount(); + } + + @Override + public Object getItem(int position) { + return itemAccess.getItem(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Holder holder; + final FeedItem item = (FeedItem) getItem(position); + if (item == null) return null; + + if (convertView == null) { + holder = new Holder(); + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = inflater.inflate(R.layout.queue_listitem, + null); + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.pubDate = (TextView) convertView + .findViewById(R.id.txtvPublished); + holder.butSecondary = (ImageButton) convertView + .findViewById(R.id.butSecondaryAction); + holder.statusPlaying = (ImageView) convertView + .findViewById(R.id.statusPlaying); + holder.downloadProgress = (ProgressBar) convertView + .findViewById(R.id.pbar_download_progress); + holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); + holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration); + convertView.setTag(holder); + } else { + holder = (Holder) convertView.getTag(); + } + + holder.title.setText(item.getTitle()); + holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_SHOW_DATE)); + FeedItem.State state = item.getState(); + + if (state == FeedItem.State.PLAYING) { + holder.statusPlaying.setVisibility(View.VISIBLE); + } else { + holder.statusPlaying.setVisibility(View.INVISIBLE); + } + + FeedMedia media = item.getMedia(); + if (media != null) { + final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); + + if (media.getDuration() > 0) { + holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration())); + } else { + holder.txtvDuration.setText(""); + } + + if (isDownloadingMedia) { + holder.downloadProgress.setVisibility(View.VISIBLE); + holder.txtvDuration.setVisibility(View.GONE); + } else { + holder.txtvDuration.setVisibility(View.VISIBLE); + holder.downloadProgress.setVisibility(View.GONE); + } + if (!media.isDownloaded()) { + if (isDownloadingMedia) { + // item is being downloaded + holder.downloadProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); + } + } + } + + actionButtonUtils.configureActionButton(holder.butSecondary, item); + holder.butSecondary.setFocusable(false); + holder.butSecondary.setTag(item); + holder.butSecondary.setOnClickListener(secondaryActionListener); + + + ImageLoader.getInstance().loadThumbnailBitmap( + item, + holder.imageView, + (int) convertView.getResources().getDimension( + R.dimen.thumbnail_length) + ); + return convertView; + } + + private View.OnClickListener secondaryActionListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + FeedItem item = (FeedItem) v.getTag(); + actionButtonCallback.onActionButtonPressed(item); + } + }; + + + static class Holder { + TextView title; + TextView pubDate; + ImageView imageView; + ImageView statusPlaying; + ProgressBar downloadProgress; + TextView txtvDuration; + ImageButton butSecondary; + } + + public interface ItemAccess { + int getCount(); + + FeedItem getItem(int position); + + int getItemDownloadProgressPercent(FeedItem item); + } +} diff --git a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java index 926a5a5ad..5c6af3943 100644 --- a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java +++ b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java @@ -5,6 +5,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import de.danoeh.antennapod.R; @@ -17,14 +18,32 @@ import de.danoeh.antennapod.feed.SearchResult; import java.util.List; /** List adapter for search activity. */ -public class SearchlistAdapter extends ArrayAdapter<SearchResult> { +public class SearchlistAdapter extends BaseAdapter { - public SearchlistAdapter(Context context, int textViewResourceId, - List<SearchResult> objects) { - super(context, textViewResourceId, objects); - } + private final Context context; + private final ItemAccess itemAccess; + + public SearchlistAdapter(Context context, ItemAccess itemAccess) { + this.context = context; + this.itemAccess = itemAccess; + } + + @Override + public int getCount() { + return itemAccess.getCount(); + } - @Override + @Override + public SearchResult getItem(int position) { + return itemAccess.getItem(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override public View getView(int position, View convertView, ViewGroup parent) { final Holder holder; SearchResult result = getItem(position); @@ -33,7 +52,7 @@ public class SearchlistAdapter extends ArrayAdapter<SearchResult> { // Inflate Layout if (convertView == null) { holder = new Holder(); - LayoutInflater inflater = (LayoutInflater) getContext() + LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.searchlist_item, null); @@ -78,4 +97,9 @@ public class SearchlistAdapter extends ArrayAdapter<SearchResult> { TextView subtitle; } + public static interface ItemAccess { + int getCount(); + SearchResult getItem(int position); + } + } diff --git a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java index 40388cde5..1c5003ab3 100644 --- a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java +++ b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java @@ -23,9 +23,9 @@ public class DownloadObserver { /** * Time period between update notifications. */ - public static final int WAITING_INTERVAL_MS = 1000; + public static final int WAITING_INTERVAL_MS = 3000; - private final Activity activity; + private volatile Activity activity; private final Handler handler; private final Callback callback; @@ -57,19 +57,31 @@ public class DownloadObserver { public void onResume() { if (BuildConfig.DEBUG) Log.d(TAG, "DownloadObserver resumed"); activity.registerReceiver(contentChangedReceiver, new IntentFilter(DownloadService.ACTION_DOWNLOADS_CONTENT_CHANGED)); - activity.bindService(new Intent(activity, DownloadService.class), mConnection, 0); + connectToDownloadService(); } public void onPause() { if (BuildConfig.DEBUG) Log.d(TAG, "DownloadObserver paused"); - activity.unregisterReceiver(contentChangedReceiver); - activity.unbindService(mConnection); + try { + activity.unregisterReceiver(contentChangedReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + try { + activity.unbindService(mConnection); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } stopRefresher(); } private BroadcastReceiver contentChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + // reconnect to DownloadService if connection has been closed + if (downloadService == null) { + connectToDownloadService(); + } callback.onContentChanged(); startRefresher(); } @@ -81,6 +93,10 @@ public class DownloadObserver { void onDownloadDataAvailable(List<Downloader> downloaderList); } + private void connectToDownloadService() { + activity.bindService(new Intent(activity, DownloadService.class), mConnection, 0); + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName className) { downloadService = null; @@ -138,13 +154,21 @@ public class DownloadObserver { @Override public void run() { callback.onContentChanged(); - List<Downloader> downloaderList = downloadService.getDownloads(); - if (downloaderList == null || downloaderList.isEmpty()) { - Thread.currentThread().interrupt(); + if (downloadService != null) { + List<Downloader> downloaderList = downloadService.getDownloads(); + if (downloaderList == null || downloaderList.isEmpty()) { + Thread.currentThread().interrupt(); + } } } }); } } + public void setActivity(Activity activity) { + if (activity == null) throw new IllegalArgumentException("activity = null"); + this.activity = activity; + } + } + diff --git a/src/de/danoeh/antennapod/dialog/FeedItemDialog.java b/src/de/danoeh/antennapod/dialog/FeedItemDialog.java new file mode 100644 index 000000000..932f9aec9 --- /dev/null +++ b/src/de/danoeh/antennapod/dialog/FeedItemDialog.java @@ -0,0 +1,335 @@ +package de.danoeh.antennapod.dialog; + +import android.app.Dialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.widget.PopupMenu; +import android.util.Log; +import android.util.TypedValue; +import android.view.MenuItem; +import android.view.View; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ImageButton; +import android.widget.Toast; +import de.danoeh.antennapod.BuildConfig; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.preferences.UserPreferences; +import de.danoeh.antennapod.storage.DBTasks; +import de.danoeh.antennapod.storage.DBWriter; +import de.danoeh.antennapod.storage.DownloadRequestException; +import de.danoeh.antennapod.storage.DownloadRequester; +import de.danoeh.antennapod.util.QueueAccess; +import de.danoeh.antennapod.util.ShownotesProvider; +import de.danoeh.antennapod.util.menuhandler.FeedItemMenuHandler; +import org.apache.commons.lang3.StringEscapeUtils; +import org.shredzone.flattr4j.model.User; + +import java.util.Collection; +import java.util.concurrent.Callable; + +/** + * Shows information about a specific FeedItem and provides actions like playing, downloading, etc. + */ +public class FeedItemDialog extends Dialog { + private static final String TAG = "FeedItemDialog"; + + private FeedItem item; + private QueueAccess queue; + + private View header; + private WebView webvDescription; + private ImageButton butAction1; + private ImageButton butAction2; + private ImageButton butMore; + private PopupMenu popupMenu; + + public static FeedItemDialog newInstace(Context context, FeedItem item, QueueAccess queue) { + if (useDarkThemeWorkAround()) { + return new FeedItemDialog(context, R.style.Theme_AntennaPod_Dark, item, queue); + } else { + return new FeedItemDialog(context, item, queue); + } + } + + public FeedItemDialog(Context context, int theme, FeedItem item, QueueAccess queue) { + super(context, theme); + if (item == null) throw new IllegalArgumentException("item = null"); + if (queue == null) throw new IllegalArgumentException("queue = null"); + this.item = item; + this.queue = queue; + } + + private FeedItemDialog(Context context, FeedItem item, QueueAccess queue) { + this(context, 0, item, queue); + } + + /** + * Returns true if the dialog should use a dark theme. This has to be done on Gingerbread devices + * because dialogs are only available in a dark theme. + */ + private static boolean useDarkThemeWorkAround() { + return Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1 + && UserPreferences.getTheme() != R.style.Theme_AntennaPod_Dark; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.feeditem_dialog); + + header = findViewById(R.id.header); + webvDescription = (WebView) findViewById(R.id.webview); + butAction1 = (ImageButton) findViewById(R.id.butAction1); + butAction2 = (ImageButton) findViewById(R.id.butAction2); + butMore = (ImageButton) findViewById(R.id.butMoreActions); + popupMenu = new PopupMenu(getContext(), butMore); + + webvDescription.setWebViewClient(new WebViewClient()); + setTitle(item.getTitle()); + + if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { + if (Build.VERSION.SDK_INT >= 11 + && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } + webvDescription.setBackgroundColor(getContext().getResources().getColor( + R.color.black)); + } + webvDescription.getSettings().setUseWideViewPort(false); + webvDescription.getSettings().setLayoutAlgorithm( + WebSettings.LayoutAlgorithm.NARROW_COLUMNS); + webvDescription.getSettings().setLoadWithOverviewMode(true); + webvDescription.setWebViewClient(new WebViewClient() { + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + try { + getContext().startActivity(intent); + } catch (ActivityNotFoundException e) { + e.printStackTrace(); + return false; + } + return true; + } + }); + + loadDescriptionWebview(item); + + butAction1.setOnClickListener(new View.OnClickListener() { + DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getContext()); + + @Override + + public void onClick(View v) { + FeedMedia media = item.getMedia(); + if (media == null) { + return; + } + actionButtonCallback.onActionButtonPressed(item); + + } + } + ); + + butAction2.setOnClickListener(new View.OnClickListener() + + { + @Override + public void onClick(View v) { + FeedMedia media = item.getMedia(); + if (media == null) { + return; + } + + if (!media.isDownloaded()) { + DBTasks.playMedia(getContext(), media, true, true, true); + dismiss(); + } else { + DBWriter.deleteFeedMediaOfItem(getContext(), media.getId()); + } + } + } + ); + + butMore.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + popupMenu.getMenu().clear(); + popupMenu.inflate(R.menu.feeditem_dialog); + FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue); + popupMenu.show(); + } + } + ); + + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + + try { + return FeedItemMenuHandler.onMenuItemClicked(getContext(), menuItem.getItemId(), item); + } catch (DownloadRequestException e) { + e.printStackTrace(); + Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show(); + return true; + } + } + } + ); + + updateMenuAppearance(); + } + + + + private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() { + @Override + public void setItemVisibility(int id, boolean visible) { + MenuItem item = popupMenu.getMenu().findItem(id); + if (item != null) { + item.setVisible(visible); + } + } + }; + + public void updateMenuAppearance() { + if (item == null || queue == null) { + Log.w(TAG, "UpdateMenuAppearance called while item or queue was null"); + return; + } + FeedMedia media = item.getMedia(); + if (media == null) { + header.setVisibility(View.GONE); + } else { + header.setVisibility(View.VISIBLE); + boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media); + TypedArray drawables = getContext().obtainStyledAttributes(new int[]{R.attr.av_play, + R.attr.av_download, R.attr.action_stream, R.attr.content_discard, R.attr.navigation_cancel}); + + if (!media.isDownloaded()) { + butAction2.setImageDrawable(drawables.getDrawable(2)); + butAction2.setContentDescription(getContext().getString(R.string.stream_label)); + } else { + butAction2.setImageDrawable(drawables.getDrawable(3)); + butAction2.setContentDescription(getContext().getString(R.string.remove_episode_lable)); + } + + if (isDownloading) { + butAction1.setImageDrawable(drawables.getDrawable(4)); + butAction1.setContentDescription(getContext().getString(R.string.cancel_download_label)); + } else if (media.isDownloaded()) { + butAction1.setImageDrawable(drawables.getDrawable(0)); + butAction1.setContentDescription(getContext().getString(R.string.play_label)); + } else { + butAction1.setImageDrawable(drawables.getDrawable(1)); + butAction1.setContentDescription(getContext().getString(R.string.download_label)); + } + + drawables.recycle(); + } + } + + + private void loadDescriptionWebview(final ShownotesProvider shownotesProvider) { + AsyncTask<Void, Void, Void> loadTask = new AsyncTask<Void, Void, Void>() { + String data; + + + private String applyWebviewStyle(String textColor, String data) { + final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> @font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>"; + final int pageMargin = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 8, getContext().getResources() + .getDisplayMetrics() + ); + return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin, + pageMargin, pageMargin, pageMargin, data); + } + + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + // /webvDescription.loadData(url, "text/html", "utf-8"); + if (FeedItemDialog.this.isShowing() && webvDescription != null) { + webvDescription.loadDataWithBaseURL(null, data, "text/html", + "utf-8", "about:blank"); + if (BuildConfig.DEBUG) + Log.d(TAG, "Webview loaded"); + } + } + + + @Override + protected Void doInBackground(Void... params) { + if (BuildConfig.DEBUG) + Log.d(TAG, "Loading Webview"); + try { + Callable<String> shownotesLoadTask = shownotesProvider.loadShownotes(); + final String shownotes = shownotesLoadTask.call(); + + data = StringEscapeUtils.unescapeHtml4(shownotes); + TypedArray res = getContext() + .getTheme() + .obtainStyledAttributes( + new int[]{android.R.attr.textColorPrimary}); + int colorResource; + if (useDarkThemeWorkAround()) { + colorResource = getContext().getResources().getColor(R.color.black); + } else { + colorResource = res.getColor(0, 0); + } + String colorString = String.format("#%06X", + 0xFFFFFF & colorResource); + Log.i(TAG, "text color: " + colorString); + res.recycle(); + data = applyWebviewStyle(colorString, data); + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + }; + loadTask.execute(); + } + + public void setItem(FeedItem item) { + if (item == null) throw new IllegalArgumentException("item = null"); + this.item = item; + } + + public void setItemFromCollection(Collection<FeedItem> items) { + for (FeedItem item : items) { + if (item.getId() == this.item.getId()) { + setItem(item); + break; + } + } + } + + public void setQueue(QueueAccess queue) { + if (queue == null) throw new IllegalArgumentException("queue = null"); + this.queue = queue; + } + + public FeedItem getItem() { + return item; + } + + public QueueAccess getQueue() { + return queue; + } +} diff --git a/src/de/danoeh/antennapod/fragment/AddFeedFragment.java b/src/de/danoeh/antennapod/fragment/AddFeedFragment.java new file mode 100644 index 000000000..f5ae5a777 --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -0,0 +1,76 @@ +package de.danoeh.antennapod.fragment; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.DefaultOnlineFeedViewActivity; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.activity.OnlineFeedViewActivity; +import de.danoeh.antennapod.activity.OpmlImportFromPathActivity; +import de.danoeh.antennapod.fragment.gpodnet.GpodnetMainFragment; + +/** + * Provides actions for adding new podcast subscriptions + */ +public class AddFeedFragment extends Fragment { + private static final String TAG = "AddFeedFragment"; + + /** + * Preset value for url text field. + */ + public static final String ARG_FEED_URL = "feedurl"; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + View root = inflater.inflate(R.layout.addfeed, container, false); + + final EditText etxtFeedurl = (EditText) root.findViewById(R.id.etxtFeedurl); + + Bundle args = getArguments(); + if (args != null && args.getString(ARG_FEED_URL) != null) { + etxtFeedurl.setText(args.getString(ARG_FEED_URL)); + } + + Button butBrowserGpoddernet = (Button) root.findViewById(R.id.butBrowseGpoddernet); + Button butOpmlImport = (Button) root.findViewById(R.id.butOpmlImport); + Button butConfirm = (Button) root.findViewById(R.id.butConfirm); + + final MainActivity activity = (MainActivity) getActivity(); + activity.getMainActivtyActionBar().setTitle(R.string.add_feed_label); + + butBrowserGpoddernet.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + activity.loadChildFragment(new GpodnetMainFragment()); + } + }); + + butOpmlImport.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + startActivity(new Intent(getActivity(), + OpmlImportFromPathActivity.class)); + } + }); + + butConfirm.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(getActivity(), DefaultOnlineFeedViewActivity.class); + intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, etxtFeedurl.getText().toString()); + intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label)); + startActivity(intent); + } + }); + + return root; + } +} diff --git a/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java new file mode 100644 index 000000000..51c92e234 --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -0,0 +1,164 @@ +package de.danoeh.antennapod.fragment; + +import android.app.Activity; +import android.content.Context; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.view.View; +import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter; +import de.danoeh.antennapod.feed.EventDistributor; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.storage.DBReader; +import de.danoeh.antennapod.storage.DBWriter; + +import java.util.List; + +/** + * Displays all running downloads and provides a button to delete them + */ +public class CompletedDownloadsFragment extends ListFragment { + private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | + EventDistributor.DOWNLOADLOG_UPDATE | + EventDistributor.QUEUE_UPDATE | + EventDistributor.UNREAD_ITEMS_UPDATE; + + private List<FeedItem> items; + private DownloadedEpisodesListAdapter listAdapter; + + private boolean viewCreated = false; + private boolean itemsLoaded = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + startItemLoader(); + } + + @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + } + + @Override + public void onStop() { + super.onStop(); + EventDistributor.getInstance().unregister(contentUpdate); + stopItemLoader(); + } + + @Override + public void onDetach() { + super.onDetach(); + stopItemLoader(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + listAdapter = null; + viewCreated = false; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + if (viewCreated && itemsLoaded) { + onFragmentLoaded(); + } + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + viewCreated = true; + if (itemsLoaded && getActivity() != null) { + onFragmentLoaded(); + } + } + + private void onFragmentLoaded() { + if (listAdapter == null) { + listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess); + setListAdapter(listAdapter); + } + listAdapter.notifyDataSetChanged(); + } + + private DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() { + @Override + public int getCount() { + return (items != null) ? items.size() : 0; + } + + @Override + public FeedItem getItem(int position) { + return (items != null) ? items.get(position) : null; + } + + @Override + public void onFeedItemSecondaryAction(FeedItem item) { + DBWriter.deleteFeedMediaOfItem(getActivity(), item.getMedia().getId()); + } + }; + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((arg & EVENTS) != 0) { + startItemLoader(); + } + } + }; + + private ItemLoader itemLoader; + + private void startItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + itemLoader = new ItemLoader(); + itemLoader.execute(); + } + + private void stopItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + } + + private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> { + + @Override + protected void onPreExecute() { + super.onPreExecute(); + if (!itemsLoaded && viewCreated) { + setListShown(false); + } + } + + @Override + protected void onPostExecute(List<FeedItem> feedItems) { + super.onPostExecute(feedItems); + setListShown(true); + if (feedItems != null) { + items = feedItems; + itemsLoaded = true; + if (viewCreated && getActivity() != null) { + onFragmentLoaded(); + } + } + } + + @Override + protected List<FeedItem> doInBackground(Void... params) { + Context context = getActivity(); + if (context != null) { + return DBReader.getDownloadedItems(context); + } + return null; + } + } +} diff --git a/src/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/src/de/danoeh/antennapod/fragment/DownloadLogFragment.java new file mode 100644 index 000000000..d81ba4b86 --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -0,0 +1,121 @@ +package de.danoeh.antennapod.fragment; + +import android.content.Context; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.view.View; +import de.danoeh.antennapod.adapter.DownloadLogAdapter; +import de.danoeh.antennapod.feed.EventDistributor; +import de.danoeh.antennapod.service.download.DownloadStatus; +import de.danoeh.antennapod.storage.DBReader; + +import java.util.List; + +/** + * Shows the download log + */ +public class DownloadLogFragment extends ListFragment { + + private List<DownloadStatus> downloadLog; + private DownloadLogAdapter adapter; + + private boolean viewsCreated = false; + private boolean itemsLoaded = false; + + @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + startItemLoader(); + } + + @Override + public void onStop() { + super.onStop(); + EventDistributor.getInstance().unregister(contentUpdate); + stopItemLoader(); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + viewsCreated = true; + if (itemsLoaded) { + onFragmentLoaded(); + } + } + + private void onFragmentLoaded() { + if (adapter == null) { + adapter = new DownloadLogAdapter(getActivity(), itemAccess); + setListAdapter(adapter); + } + setListShown(true); + adapter.notifyDataSetChanged(); + + } + + private DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() { + + @Override + public int getCount() { + return (downloadLog != null) ? downloadLog.size() : 0; + } + + @Override + public DownloadStatus getItem(int position) { + return (downloadLog != null) ? downloadLog.get(position) : null; + } + }; + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((arg & EventDistributor.DOWNLOADLOG_UPDATE) != 0) { + startItemLoader(); + } + } + }; + + private ItemLoader itemLoader; + + private void startItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + itemLoader = new ItemLoader(); + itemLoader.execute(); + } + + private void stopItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + } + + private class ItemLoader extends AsyncTask<Void, Void, List<DownloadStatus>> { + + @Override + protected void onPostExecute(List<DownloadStatus> downloadStatuses) { + super.onPostExecute(downloadStatuses); + if (downloadStatuses != null) { + downloadLog = downloadStatuses; + itemsLoaded = true; + if (viewsCreated) { + onFragmentLoaded(); + } + } + } + + @Override + protected List<DownloadStatus> doInBackground(Void... params) { + Context context = getActivity(); + if (context != null) { + return DBReader.getDownloadLog(context); + } + return null; + } + } +} diff --git a/src/de/danoeh/antennapod/fragment/DownloadsFragment.java b/src/de/danoeh/antennapod/fragment/DownloadsFragment.java new file mode 100644 index 000000000..5a71cb36b --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -0,0 +1,145 @@ +package de.danoeh.antennapod.fragment; + +import android.app.Activity; +import android.content.res.Resources; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; + +/** + * Shows the CompletedDownloadsFragment and the RunningDownloadsFragment + */ +public class DownloadsFragment extends Fragment { + + public static final String ARG_SELECTED_TAB = "selected_tab"; + + public static final int POS_RUNNING = 0; + public static final int POS_COMPLETED = 1; + public static final int POS_LOG = 2; + + private ViewPager pager; + private MainActivity activity; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + View root = inflater.inflate(R.layout.pager_fragment, container, false); + pager = (ViewPager) root.findViewById(R.id.pager); + DownloadsPagerAdapter pagerAdapter = new DownloadsPagerAdapter(getChildFragmentManager(), getResources()); + pager.setAdapter(pagerAdapter); + final ActionBar actionBar = activity.getMainActivtyActionBar(); + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + ActionBar.TabListener tabListener = new ActionBar.TabListener() { + @Override + public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + pager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + + } + + @Override + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + + } + }; + actionBar.removeAllTabs(); + actionBar.addTab(actionBar.newTab() + .setText(R.string.downloads_running_label) + .setTabListener(tabListener)); + actionBar.addTab(actionBar.newTab() + .setText(R.string.downloads_completed_label) + .setTabListener(tabListener)); + actionBar.addTab(actionBar.newTab() + .setText(R.string.downloads_log_label) + .setTabListener(tabListener)); + + pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + actionBar.setSelectedNavigationItem(position); + } + }); + return root; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + if (getArguments() != null) { + int tab = getArguments().getInt(ARG_SELECTED_TAB); + pager.setCurrentItem(tab, false); + } + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + this.activity = (MainActivity) activity; + } + + @Override + public void onDetach() { + super.onDetach(); + activity.getMainActivtyActionBar().removeAllTabs(); + activity.getMainActivtyActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + } + + public class DownloadsPagerAdapter extends FragmentPagerAdapter { + + + + + Resources resources; + + public DownloadsPagerAdapter(FragmentManager fm, Resources resources) { + super(fm); + this.resources = resources; + } + + @Override + public Fragment getItem(int position) { + switch (position) { + case POS_RUNNING: + return new RunningDownloadsFragment(); + case POS_COMPLETED: + return new CompletedDownloadsFragment(); + case POS_LOG: + return new DownloadLogFragment(); + default: + return null; + } + } + + @Override + public int getCount() { + return 3; + } + + @Override + public CharSequence getPageTitle(int position) { + switch (position) { + case POS_RUNNING: + return resources.getString(R.string.downloads_running_label); + case POS_COMPLETED: + return resources.getString(R.string.downloads_completed_label); + case POS_LOG: + return resources.getString(R.string.downloads_log_label); + default: + return super.getPageTitle(position); + } + } + } +} diff --git a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index 47cd3f244..db47cd8a4 100644 --- a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -29,8 +29,6 @@ public class ExternalPlayerFragment extends Fragment { private ImageView imgvCover; private ViewGroup layoutInfo; private TextView txtvTitle; - private TextView txtvPosition; - private TextView txtvStatus; private ImageButton butPlay; private PlaybackController controller; @@ -48,9 +46,7 @@ public class ExternalPlayerFragment extends Fragment { imgvCover = (ImageView) root.findViewById(R.id.imgvCover); layoutInfo = (ViewGroup) root.findViewById(R.id.layoutInfo); txtvTitle = (TextView) root.findViewById(R.id.txtvTitle); - txtvPosition = (TextView) root.findViewById(R.id.txtvPosition); butPlay = (ImageButton) root.findViewById(R.id.butPlay); - txtvStatus = (TextView) root.findViewById(R.id.txtvStatus); layoutInfo.setOnClickListener(new OnClickListener() { @@ -84,12 +80,6 @@ public class ExternalPlayerFragment extends Fragment { @Override public void onPositionObserverUpdate() { - int duration = controller.getDuration(); - int position = controller.getPosition(); - if (duration != PlaybackController.INVALID_TIME - && position != PlaybackController.INVALID_TIME) { - txtvPosition.setText(getPositionString(position, duration)); - } } @Override @@ -127,12 +117,10 @@ public class ExternalPlayerFragment extends Fragment { @Override public void postStatusMsg(int msg) { - txtvStatus.setText(msg); } @Override public void clearStatusMsg() { - txtvStatus.setText(""); } @Override @@ -223,8 +211,6 @@ public class ExternalPlayerFragment extends Fragment { (int) getActivity().getResources().getDimension( R.dimen.external_player_height)); - txtvPosition.setText(getPositionString(media.getPosition(), - media.getDuration())); fragmentLayout.setVisibility(View.VISIBLE); if (controller.isPlayingVideo()) { butPlay.setVisibility(View.GONE); diff --git a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index 48c544457..bf6974982 100644 --- a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -236,7 +236,7 @@ public class ItemDescriptionFragment extends Fragment { * value is inserted directly into the CSS String. */ private String applyWebviewStyle(String textColor, String data) { - final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> * { color: %s; font-family: Helvetica; line-height: 1.5em; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>"; + final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> @font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>"; final int pageMargin = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 8, getResources() .getDisplayMetrics()); diff --git a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java index e5845dd76..82fe13c32 100644 --- a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -2,326 +2,409 @@ package de.danoeh.antennapod.fragment; import android.annotation.SuppressLint; import android.content.Context; -import android.content.Intent; +import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.support.v4.app.ListFragment; import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; +import android.text.util.Linkify; import android.util.Log; import android.view.*; -import android.view.ContextMenu.ContextMenuInfo; +import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.ItemviewActivity; -import de.danoeh.antennapod.adapter.ActionButtonCallback; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.InternalFeedItemlistAdapter; +import de.danoeh.antennapod.asynctask.DownloadObserver; +import de.danoeh.antennapod.asynctask.FeedRemover; +import de.danoeh.antennapod.asynctask.ImageLoader; +import de.danoeh.antennapod.dialog.ConfirmationDialog; import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator; +import de.danoeh.antennapod.dialog.FeedItemDialog; import de.danoeh.antennapod.feed.EventDistributor; import de.danoeh.antennapod.feed.Feed; import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; import de.danoeh.antennapod.service.download.DownloadService; +import de.danoeh.antennapod.service.download.Downloader; import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.storage.DownloadRequestException; import de.danoeh.antennapod.storage.DownloadRequester; import de.danoeh.antennapod.util.QueueAccess; import de.danoeh.antennapod.util.menuhandler.FeedItemMenuHandler; +import de.danoeh.antennapod.util.menuhandler.FeedMenuHandler; +import de.danoeh.antennapod.util.menuhandler.MenuItemUtils; import java.util.List; -/** Displays a list of FeedItems. */ +/** + * Displays a list of FeedItems. + */ @SuppressLint("ValidFragment") public class ItemlistFragment extends ListFragment { - private static final String TAG = "ItemlistFragment"; - - private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED - | EventDistributor.DOWNLOAD_QUEUED - | EventDistributor.QUEUE_UPDATE - | EventDistributor.UNREAD_ITEMS_UPDATE; - - public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem"; - public static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id"; - protected InternalFeedItemlistAdapter fila; - - private Feed feed; - protected List<Long> queue; - - protected FeedItem selectedItem = null; - protected boolean contextMenuClosed = true; - - /** Argument for FeeditemlistAdapter */ - protected boolean showFeedtitle; - - private AsyncTask<Long, Void, Feed> currentLoadTask; - - public ItemlistFragment(boolean showFeedtitle) { - super(); - this.showFeedtitle = showFeedtitle; - } - - public ItemlistFragment() { - } - - /** - * Creates new ItemlistFragment which shows the Feeditems of a specific - * feed. Sets 'showFeedtitle' to false - * - * @param feedId - * The id of the feed to show - * @return the newly created instance of an ItemlistFragment - */ - public static ItemlistFragment newInstance(long feedId) { - ItemlistFragment i = new ItemlistFragment(); - i.showFeedtitle = false; - Bundle b = new Bundle(); - b.putLong(ARGUMENT_FEED_ID, feedId); - i.setArguments(b); - return i; - } - - private InternalFeedItemlistAdapter.ItemAccess itemAccessRef; - protected InternalFeedItemlistAdapter.ItemAccess itemAccess() { - if (itemAccessRef == null) { - itemAccessRef = new InternalFeedItemlistAdapter.ItemAccess() { - - @Override - public FeedItem getItem(int position) { - return (feed != null) ? feed.getItemAtIndex(true, position) : null; - } + private static final String TAG = "ItemlistFragment"; - @Override - public int getCount() { - return (feed != null) ? feed.getNumOfItems(true) : 0; - } + private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED + | EventDistributor.DOWNLOAD_QUEUED + | EventDistributor.QUEUE_UPDATE + | EventDistributor.UNREAD_ITEMS_UPDATE; - @Override - public boolean isInQueue(FeedItem item) { - return (queue != null) && queue.contains(item.getId()); - } - }; - } - return itemAccessRef; + public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem"; + public static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id"; + + protected InternalFeedItemlistAdapter adapter; + + private long feedID; + private Feed feed; + protected QueueAccess queue; + + private boolean itemsLoaded = false; + private boolean viewsCreated = false; + + private DownloadObserver downloadObserver; + private List<Downloader> downloaderList; + + private FeedItemDialog feedItemDialog; + + + /** + * Creates new ItemlistFragment which shows the Feeditems of a specific + * feed. Sets 'showFeedtitle' to false + * + * @param feedId The id of the feed to show + * @return the newly created instance of an ItemlistFragment + */ + public static ItemlistFragment newInstance(long feedId) { + ItemlistFragment i = new ItemlistFragment(); + Bundle b = new Bundle(); + b.putLong(ARGUMENT_FEED_ID, feedId); + i.setArguments(b); + return i; } - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.feeditemlist, container, false); - } + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + setHasOptionsMenu(true); + + Bundle args = getArguments(); + if (args == null) throw new IllegalArgumentException("args invalid"); + feedID = args.getLong(ARGUMENT_FEED_ID); + } @Override public void onStart() { super.onStart(); EventDistributor.getInstance().register(contentUpdate); - loadData(); + if (downloadObserver != null) { + downloadObserver.setActivity(getActivity()); + downloadObserver.onResume(); + } + if (viewsCreated && itemsLoaded) { + onFragmentLoaded(); + } } @Override public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - if (currentLoadTask != null) { - currentLoadTask.cancel(true); - } + stopItemLoader(); } - protected synchronized void loadData() { - final long feedId; - if (feed == null) { - feedId = getArguments().getLong(ARGUMENT_FEED_ID); - } else { - feedId = feed.getId(); - } - if (currentLoadTask != null) { - currentLoadTask.cancel(true); + @Override + public void onResume() { + super.onResume(); + updateProgressBarVisibility(); + startItemLoader(); + } + + @Override + public void onDetach() { + super.onDetach(); + stopItemLoader(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + resetViewState(); + } + + private void resetViewState() { + adapter = null; + viewsCreated = false; + if (downloadObserver != null) { + downloadObserver.onPause(); } - AsyncTask<Long, Void, Feed> loadTask = new AsyncTask<Long, Void, Feed>(){ - private volatile List<Long> queueRef; + feedItemDialog = null; + } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + FeedMenuHandler.onCreateOptionsMenu(inflater, menu); + + final SearchView sv = new SearchView(getActivity()); + MenuItemUtils.addSearchItem(menu, sv); + sv.setQueryHint(getString(R.string.search_hint)); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override - protected Feed doInBackground(Long... longs) { - Context context = ItemlistFragment.this.getActivity(); - if (context != null) { - Feed result = DBReader.getFeed(context, longs[0]); - if (result != null) { - result.setItems(DBReader.getFeedItemList(context, result)); - queueRef = DBReader.getQueueIDList(context); - return result; - } + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + if (itemsLoaded) { + ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId())); } - return null; + return true; } @Override - protected void onPostExecute(Feed result) { - super.onPostExecute(result); - if (result != null && result.getItems() != null) { - feed = result; - if (queueRef != null) { - queue = queueRef; - } else { - Log.e(TAG, "Could not load queue"); - } - setEmptyViewIfListIsEmpty(); - if (fila != null) { - fila.notifyDataSetChanged(); + public boolean onQueryTextChange(String s) { + return false; + } + }); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + FeedMenuHandler.onPrepareOptionsMenu(menu, feed); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (!super.onOptionsItemSelected(item)) { + try { + if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) { + switch (item.getItemId()) { + case R.id.remove_item: + final FeedRemover remover = new FeedRemover( + getActivity(), feed) { + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + ((MainActivity)getActivity()).loadNavFragment(MainActivity.POS_NEW, null); + } + }; + ConfirmationDialog conDialog = new ConfirmationDialog(getActivity(), + R.string.remove_feed_label, + R.string.feed_delete_confirmation_msg) { + + @Override + public void onConfirmButtonPressed( + DialogInterface dialog) { + dialog.dismiss(); + remover.executeAsync(); + } + }; + conDialog.createNewDialog().show(); + return true; + default: + return false; + } } else { - if (result == null) { - Log.e(TAG, "Could not load feed with id " + feedId); - } else if (result.getItems() == null) { - Log.e(TAG, "Could not load feed items"); - } + return true; } + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage()); + return true; } - }; - currentLoadTask = loadTask; - loadTask.execute(feedId); + } else { + return true; + } + } - private void setEmptyViewIfListIsEmpty() { - if (getListView() != null && feed != null && feed.getItems() != null) { - if (feed.getItems().isEmpty()) { - ((TextView) getActivity().findViewById(android.R.id.empty)).setText(R.string.no_items_label); - } + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(""); + + viewsCreated = true; + if (itemsLoaded) { + onFragmentLoaded(); } } - protected InternalFeedItemlistAdapter createListAdapter() { - return new InternalFeedItemlistAdapter(getActivity(), itemAccess(), - adapterCallback, showFeedtitle); - } - - @Override - public void onResume() { - super.onResume(); - getActivity().runOnUiThread(new Runnable() { - - @Override - public void run() { - fila.notifyDataSetChanged(); - } - }); - updateProgressBarVisibility(); - } - - @Override - public void onListItemClick(ListView l, View v, int position, long id) { - FeedItem selection = fila.getItem(position - l.getHeaderViewsCount()); - Intent showItem = new Intent(getActivity(), ItemviewActivity.class); - showItem.putExtra(FeedlistFragment.EXTRA_SELECTED_FEED, selection - .getFeed().getId()); - showItem.putExtra(EXTRA_SELECTED_FEEDITEM, selection.getId()); - - startActivity(showItem); - } - - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((EVENTS & arg) != 0) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Received contentUpdate Intent."); - if ((EventDistributor.DOWNLOAD_QUEUED & arg) != 0) { - updateProgressBarVisibility(); - } else { - if (feed != null) { - loadData(); - } - updateProgressBarVisibility(); - } - } - } - }; - - private void updateProgressBarVisibility() { - if (feed != null) { - if (DownloadService.isRunning - && DownloadRequester.getInstance().isDownloadingFile(feed)) { + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + FeedItem selection = adapter.getItem(position - l.getHeaderViewsCount()); + feedItemDialog = FeedItemDialog.newInstace(getActivity(), selection, queue); + feedItemDialog.show(); + } + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((EVENTS & arg) != 0) { + if (BuildConfig.DEBUG) + Log.d(TAG, "Received contentUpdate Intent."); + if ((EventDistributor.DOWNLOAD_QUEUED & arg) != 0) { + updateProgressBarVisibility(); + } else { + startItemLoader(); + updateProgressBarVisibility(); + } + } + } + }; + + private void updateProgressBarVisibility() { + if (feed != null) { + if (DownloadService.isRunning + && DownloadRequester.getInstance().isDownloadingFile(feed)) { ((ActionBarActivity) getActivity()) - .setSupportProgressBarIndeterminateVisibility(true); - } else { + .setSupportProgressBarIndeterminateVisibility(true); + } else { ((ActionBarActivity) getActivity()) - .setSupportProgressBarIndeterminateVisibility(false); - } + .setSupportProgressBarIndeterminateVisibility(false); + } getActivity().supportInvalidateOptionsMenu(); - } - } - - protected ActionButtonCallback adapterCallback = new ActionButtonCallback() { - - @Override - public void onActionButtonPressed(FeedItem item) { - selectedItem = item; - contextMenuClosed = true; - getListView().showContextMenu(); - } - }; - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - fila = createListAdapter(); - setListAdapter(fila); - this.getListView().setItemsCanFocus(true); - getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); - registerForContextMenu(getListView()); - getListView().setOnItemLongClickListener(null); - } - - @Override - public void onCreateContextMenu(final ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - if (!contextMenuClosed) { // true if context menu was cancelled before - selectedItem = null; - } - contextMenuClosed = false; - getListView().setOnItemLongClickListener(null); - if (selectedItem != null) { - new MenuInflater(ItemlistFragment.this.getActivity()).inflate( - R.menu.feeditem, menu); - - menu.setHeaderTitle(selectedItem.getTitle()); - FeedItemMenuHandler.onPrepareMenu( - new FeedItemMenuHandler.MenuInterface() { - - @Override - public void setItemVisibility(int id, boolean visible) { - menu.findItem(id).setVisible(visible); - } - }, selectedItem, false, QueueAccess.IDListAccess(queue)); - - } - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - boolean handled = false; - - if (selectedItem != null) { - - try { - handled = FeedItemMenuHandler.onMenuItemClicked( - getActivity(), item.getItemId(), selectedItem); - } catch (DownloadRequestException e) { - e.printStackTrace(); - DownloadRequestErrorDialogCreator.newRequestErrorDialog( - getActivity(), e.getMessage()); - } - if (handled) { - fila.notifyDataSetChanged(); - } - - } - selectedItem = null; - contextMenuClosed = true; - return handled; - } - - public InternalFeedItemlistAdapter getListAdapter() { - return fila; - } + } + } + + private void onFragmentLoaded() { + if (adapter == null) { + getListView().setAdapter(null); + setupHeaderView(); + adapter = new InternalFeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false); + setListAdapter(adapter); + downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback); + downloadObserver.onResume(); + } + setListShown(true); + adapter.notifyDataSetChanged(); + + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.setItemFromCollection(feed.getItems()); + feedItemDialog.setQueue(queue); + feedItemDialog.updateMenuAppearance(); + } + getActivity().supportInvalidateOptionsMenu(); + } + private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { + @Override + public void onContentChanged() { + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.updateMenuAppearance(); + } + } + + @Override + public void onDownloadDataAvailable(List<Downloader> downloaderList) { + ItemlistFragment.this.downloaderList = downloaderList; + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } + }; + + private void setupHeaderView() { + if (getListView() == null || feed == null) { + Log.e(TAG, "Unable to setup listview: listView = null or feed = null"); + return; + } + LayoutInflater inflater = (LayoutInflater) + getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View header = inflater.inflate(R.layout.feeditemlist_header, null); + getListView().addHeaderView(header); + + TextView txtvTitle = (TextView) header.findViewById(R.id.txtvTitle); + TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor); + TextView txtvLink = (TextView) header.findViewById(R.id.txtvLink); + ImageView imgvCover = (ImageView) header.findViewById(R.id.imgvCover); + + txtvTitle.setText(feed.getTitle()); + txtvAuthor.setText(feed.getAuthor()); + txtvLink.setText(feed.getLink()); + Linkify.addLinks(txtvLink, Linkify.WEB_URLS); + ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), imgvCover, + (int) getResources().getDimension(R.dimen.thumbnail_length_onlinefeedview)); + } + + private InternalFeedItemlistAdapter.ItemAccess itemAccess = new InternalFeedItemlistAdapter.ItemAccess() { + + @Override + public FeedItem getItem(int position) { + return (feed != null) ? feed.getItemAtIndex(true, position) : null; + } + + @Override + public int getCount() { + return (feed != null) ? feed.getNumOfItems(true) : 0; + } + + @Override + public boolean isInQueue(FeedItem item) { + return (queue != null) && queue.contains(item.getId()); + } + + @Override + public int getItemDownloadProgressPercent(FeedItem item) { + if (downloaderList != null) { + for (Downloader downloader : downloaderList) { + if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA + && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) { + return downloader.getDownloadRequest().getProgressPercent(); + } + } + } + return 0; + } + }; + + private ItemLoader itemLoader; + + private void startItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + itemLoader = new ItemLoader(); + itemLoader.execute(feedID); + } + + private void stopItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + } + + private class ItemLoader extends AsyncTask<Long, Void, Object[]> { + @Override + protected Object[] doInBackground(Long... params) { + long feedID = params[0]; + Context context = getActivity(); + if (context != null) { + return new Object[]{DBReader.getFeed(context, feedID), + QueueAccess.IDListAccess(DBReader.getQueueIDList(context))}; + } else { + return null; + } + } + + @Override + protected void onPostExecute(Object[] res) { + super.onPostExecute(res); + if (res != null) { + feed = (Feed) res[0]; + queue = (QueueAccess) res[1]; + itemsLoaded = true; + if (viewsCreated) { + onFragmentLoaded(); + } + } + } + } } diff --git a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java new file mode 100644 index 000000000..3e67599f0 --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -0,0 +1,381 @@ +package de.danoeh.antennapod.fragment; + +import android.app.Activity; +import android.content.Context; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; +import android.view.*; +import android.widget.AdapterView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; +import com.mobeta.android.dslv.DragSortListView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; +import de.danoeh.antennapod.adapter.NewEpisodesListAdapter; +import de.danoeh.antennapod.asynctask.DownloadObserver; +import de.danoeh.antennapod.dialog.FeedItemDialog; +import de.danoeh.antennapod.feed.EventDistributor; +import de.danoeh.antennapod.feed.Feed; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.service.download.DownloadService; +import de.danoeh.antennapod.service.download.Downloader; +import de.danoeh.antennapod.storage.DBReader; +import de.danoeh.antennapod.storage.DBTasks; +import de.danoeh.antennapod.storage.DBWriter; +import de.danoeh.antennapod.storage.DownloadRequester; +import de.danoeh.antennapod.util.QueueAccess; +import de.danoeh.antennapod.util.menuhandler.MenuItemUtils; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Shows unread or recently published episodes + */ +public class NewEpisodesFragment extends Fragment { + private static final String TAG = "NewEpisodesFragment"; + private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | + EventDistributor.DOWNLOAD_QUEUED | + EventDistributor.QUEUE_UPDATE | + EventDistributor.UNREAD_ITEMS_UPDATE; + + private static final int RECENT_EPISODES_LIMIT = 150; + + private DragSortListView listView; + private NewEpisodesListAdapter listAdapter; + private TextView txtvEmpty; + private ProgressBar progLoading; + + private List<FeedItem> unreadItems; + private List<FeedItem> recentItems; + private QueueAccess queueAccess; + private List<Downloader> downloaderList; + + private boolean itemsLoaded = false; + private boolean viewsCreated = false; + + private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>(); + + private DownloadObserver downloadObserver = null; + + private FeedItemDialog feedItemDialog; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + setHasOptionsMenu(true); + } + + @Override + public void onResume() { + super.onResume(); + startItemLoader(); + } + + @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + this.activity.set((MainActivity) getActivity()); + if (downloadObserver != null) { + downloadObserver.setActivity(getActivity()); + downloadObserver.onResume(); + } + if (viewsCreated && itemsLoaded) { + onFragmentLoaded(); + } + } + + @Override + public void onStop() { + super.onStop(); + EventDistributor.getInstance().unregister(contentUpdate); + stopItemLoader(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + this.activity.set((MainActivity) getActivity()); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + resetViewState(); + } + + private void resetViewState() { + listAdapter = null; + activity.set(null); + viewsCreated = false; + if (downloadObserver != null) { + downloadObserver.onPause(); + } + feedItemDialog = null; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.new_episodes, menu); + + final SearchView sv = new SearchView(getActivity()); + MenuItemUtils.addSearchItem(menu, sv); + sv.setQueryHint(getString(R.string.search_hint)); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s)); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + return false; + } + }); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.mark_all_read_item).setVisible(unreadItems != null && !unreadItems.isEmpty()); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (!super.onOptionsItemSelected(item)) { + switch (item.getItemId()) { + case R.id.refresh_item: + List<Feed> feeds = ((MainActivity) getActivity()).getFeeds(); + if (feeds != null) { + DBTasks.refreshAllFeeds(getActivity(), feeds); + } + return true; + case R.id.mark_all_read_item: + DBWriter.markAllItemsRead(getActivity()); + Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show(); + return true; + default: + return false; + } + } else { + return true; + } + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.new_episodes_label); + + View root = inflater.inflate(R.layout.new_episodes_fragment, container, false); + listView = (DragSortListView) root.findViewById(android.R.id.list); + txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + progLoading = (ProgressBar) root.findViewById(R.id.progLoading); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount()); + if (item != null) { + feedItemDialog = FeedItemDialog.newInstace(activity.get(), item, queueAccess); + feedItemDialog.show(); + } + + } + }); + + if (!itemsLoaded) { + progLoading.setVisibility(View.VISIBLE); + txtvEmpty.setVisibility(View.GONE); + } + + viewsCreated = true; + + if (itemsLoaded && activity.get() != null) { + onFragmentLoaded(); + } + + return root; + } + + private void onFragmentLoaded() { + if (listAdapter == null) { + listAdapter = new NewEpisodesListAdapter(activity.get(), itemAccess, new DefaultActionButtonCallback(activity.get())); + listView.setAdapter(listAdapter); + listView.setEmptyView(txtvEmpty); + downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); + downloadObserver.onResume(); + } + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.setQueue(queueAccess); + feedItemDialog.setItemFromCollection(unreadItems); + feedItemDialog.setItemFromCollection(recentItems); + feedItemDialog.updateMenuAppearance(); + } + listAdapter.notifyDataSetChanged(); + getActivity().supportInvalidateOptionsMenu(); + updateProgressBarVisibility(); + } + + private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { + @Override + public void onContentChanged() { + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.updateMenuAppearance(); + } + } + + @Override + public void onDownloadDataAvailable(List<Downloader> downloaderList) { + NewEpisodesFragment.this.downloaderList = downloaderList; + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + }; + + private NewEpisodesListAdapter.ItemAccess itemAccess = new NewEpisodesListAdapter.ItemAccess() { + + + @Override + public int getUnreadItemsCount() { + return (itemsLoaded) ? unreadItems.size() : 0; + } + + @Override + public int getRecentItemsCount() { + return (itemsLoaded) ? recentItems.size() : 0; + } + + @Override + public FeedItem getUnreadItem(int position) { + return (itemsLoaded) ? unreadItems.get(position) : null; + } + + @Override + public FeedItem getRecentItem(int position) { + return (itemsLoaded) ? recentItems.get(position) : null; + } + + @Override + public int getItemDownloadProgressPercent(FeedItem item) { + if (downloaderList != null) { + for (Downloader downloader : downloaderList) { + if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA + && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) { + return downloader.getDownloadRequest().getProgressPercent(); + } + } + } + return 0; + } + + @Override + public boolean isInQueue(FeedItem item) { + if (itemsLoaded) { + return queueAccess.contains(item.getId()); + } else { + return false; + } + } + + + }; + + private void updateProgressBarVisibility() { + if (DownloadService.isRunning + && DownloadRequester.getInstance().isDownloadingFeeds()) { + ((ActionBarActivity) getActivity()) + .setSupportProgressBarIndeterminateVisibility(true); + } else { + ((ActionBarActivity) getActivity()) + .setSupportProgressBarIndeterminateVisibility(false); + } + getActivity().supportInvalidateOptionsMenu(); + + } + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((arg & EVENTS) != 0) { + startItemLoader(); + updateProgressBarVisibility(); + } + } + }; + + private ItemLoader itemLoader; + + private void startItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + itemLoader = new ItemLoader(); + itemLoader.execute(); + } + + private void stopItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + } + + private class ItemLoader extends AsyncTask<Void, Void, Object[]> { + + @Override + protected void onPreExecute() { + super.onPreExecute(); + if (viewsCreated && !itemsLoaded) { + listView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progLoading.setVisibility(View.VISIBLE); + } + } + + @Override + protected Object[] doInBackground(Void... params) { + Context context = activity.get(); + if (context != null) { + return new Object[]{DBReader.getUnreadItemsList(context), + DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT), + QueueAccess.IDListAccess(DBReader.getQueueIDList(context))}; + } else { + return null; + } + } + + @Override + protected void onPostExecute(Object[] lists) { + super.onPostExecute(lists); + listView.setVisibility(View.VISIBLE); + progLoading.setVisibility(View.GONE); + + if (lists != null) { + unreadItems = (List<FeedItem>) lists[0]; + recentItems = (List<FeedItem>) lists[1]; + queueAccess = (QueueAccess) lists[2]; + itemsLoaded = true; + if (viewsCreated && activity.get() != null) { + onFragmentLoaded(); + } + } + } + } +} diff --git a/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index d6524853f..6369a51c1 100644 --- a/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -1,112 +1,279 @@ package de.danoeh.antennapod.fragment; +import android.app.Activity; import android.content.Context; +import android.content.res.TypedArray; import android.os.AsyncTask; import android.os.Bundle; -import android.util.Log; -import de.danoeh.antennapod.BuildConfig; +import android.os.Handler; +import android.support.v4.app.ListFragment; +import android.support.v4.view.MenuItemCompat; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.ListView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.ActionButtonCallback; +import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.InternalFeedItemlistAdapter; +import de.danoeh.antennapod.asynctask.DownloadObserver; +import de.danoeh.antennapod.dialog.FeedItemDialog; import de.danoeh.antennapod.feed.EventDistributor; import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.service.download.Downloader; import de.danoeh.antennapod.storage.DBReader; +import de.danoeh.antennapod.storage.DBWriter; +import de.danoeh.antennapod.util.QueueAccess; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; -public class PlaybackHistoryFragment extends ItemlistFragment { - private static final String TAG = "PlaybackHistoryFragment"; +public class PlaybackHistoryFragment extends ListFragment { + private static final String TAG = "PlaybackHistoryFragment"; private List<FeedItem> playbackHistory; + private QueueAccess queue; + private InternalFeedItemlistAdapter adapter; - public PlaybackHistoryFragment() { - super(true); - } + private boolean itemsLoaded = false; + private boolean viewsCreated = false; + + private AtomicReference<Activity> activity = new AtomicReference<Activity>(); + + private DownloadObserver downloadObserver; + private List<Downloader> downloaderList; + + private FeedItemDialog feedItemDialog; - InternalFeedItemlistAdapter.ItemAccess itemAccessRef; @Override - protected InternalFeedItemlistAdapter.ItemAccess itemAccess() { - if (itemAccessRef == null) { - itemAccessRef = new InternalFeedItemlistAdapter.ItemAccess() { + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + setHasOptionsMenu(true); + } - @Override - public FeedItem getItem(int position) { - return (playbackHistory != null) ? playbackHistory.get(position) : null; - } + @Override + public void onResume() { + super.onResume(); + startItemLoader(); + } - @Override - public int getCount() { - return (playbackHistory != null) ? playbackHistory.size() : 0; - } + @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + } - @Override - public boolean isInQueue(FeedItem item) { - return (queue != null) ? queue.contains(item.getId()) : false; - } - }; + @Override + public void onStop() { + super.onStop(); + EventDistributor.getInstance().unregister(contentUpdate); + stopItemLoader(); + } + + @Override + public void onDetach() { + super.onDetach(); + stopItemLoader(); + activity.set(null); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + this.activity.set(activity); + if (downloadObserver != null) { + downloadObserver.setActivity(activity); + downloadObserver.onResume(); + } + if (viewsCreated && itemsLoaded) { + onFragmentLoaded(); } - return itemAccessRef; } @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - EventDistributor.getInstance().register(historyUpdate); - } - - @Override - public void onDestroy() { - super.onDestroy(); - EventDistributor.getInstance().unregister(historyUpdate); - } - - private EventDistributor.EventListener historyUpdate = new EventDistributor.EventListener() { - - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((EventDistributor.PLAYBACK_HISTORY_UPDATE & arg) != 0) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Received content update"); - loadData(); - } - - } - }; + public void onDestroyView() { + super.onDestroyView(); + adapter = null; + viewsCreated = false; + if (downloadObserver != null) { + downloadObserver.onPause(); + } + feedItemDialog = null; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + viewsCreated = true; + if (itemsLoaded) { + onFragmentLoaded(); + } + } @Override - protected void loadData() { - AsyncTask<Void, Void, Void> loadTask = new AsyncTask<Void, Void, Void>() { - private volatile List<FeedItem> phRef; - private volatile List<Long> queueRef; - - @Override - protected Void doInBackground(Void... voids) { - Context context = PlaybackHistoryFragment.this.getActivity(); - if (context != null) { - queueRef = DBReader.getQueueIDList(context); - phRef = DBReader.getPlaybackHistory(context); + public void onListItemClick(ListView l, View v, int position, long id) { + super.onListItemClick(l, v, position, id); + FeedItem item = adapter.getItem(position - l.getHeaderViewsCount()); + if (item != null) { + feedItemDialog = FeedItemDialog.newInstace(activity.get(), item, queue); + feedItemDialog.show(); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label); + MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); + TypedArray drawables = getActivity().obtainStyledAttributes(new int[] {R.attr.content_discard}); + clearHistory.setIcon(drawables.getDrawable(0)); + drawables.recycle(); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.clear_history_item).setVisible(playbackHistory != null && !playbackHistory.isEmpty()); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (!super.onOptionsItemSelected(item)) { + switch(item.getItemId()) { + case R.id.clear_history_item: + DBWriter.clearPlaybackHistory(getActivity()); + return true; + default: + return false; + } + } else { + return true; + } + } + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((arg & EventDistributor.PLAYBACK_HISTORY_UPDATE) != 0) { + startItemLoader(); + getActivity().supportInvalidateOptionsMenu(); + } + } + }; + + private void onFragmentLoaded() { + if (adapter == null) { + adapter = new InternalFeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true); + setListAdapter(adapter); + downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); + downloadObserver.onResume(); + } + setListShown(true); + adapter.notifyDataSetChanged(); + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.setItemFromCollection(playbackHistory); + feedItemDialog.setQueue(queue); + feedItemDialog.updateMenuAppearance(); + } + getActivity().supportInvalidateOptionsMenu(); + } + + private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { + @Override + public void onContentChanged() { + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.updateMenuAppearance(); + } + } + + @Override + public void onDownloadDataAvailable(List<Downloader> downloaderList) { + PlaybackHistoryFragment.this.downloaderList = downloaderList; + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } + }; + + private InternalFeedItemlistAdapter.ItemAccess itemAccess = new InternalFeedItemlistAdapter.ItemAccess() { + @Override + public boolean isInQueue(FeedItem item) { + return (queue != null) ? queue.contains(item.getId()) : false; + } + + @Override + public int getItemDownloadProgressPercent(FeedItem item) { + if (downloaderList != null) { + for (Downloader downloader : downloaderList) { + if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA + && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) { + return downloader.getDownloadRequest().getProgressPercent(); + } } + } + return 0; + } + + @Override + public int getCount() { + return (playbackHistory != null) ? playbackHistory.size() : 0; + } + + @Override + public FeedItem getItem(int position) { + return (playbackHistory != null) ? playbackHistory.get(position) : null; + } + }; + + private ItemLoader itemLoader; + + private void startItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + itemLoader = new ItemLoader(); + itemLoader.execute(); + } + + private void stopItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + } + + private class ItemLoader extends AsyncTask<Void, Void, Object[]> { + + @Override + protected Object[] doInBackground(Void... params) { + Context context = activity.get(); + if (context != null) { + List<FeedItem> ph = DBReader.getPlaybackHistory(context); + DBReader.loadFeedDataOfFeedItemlist(context, ph); + return new Object[]{ph, + QueueAccess.IDListAccess(DBReader.getQueueIDList(context))}; + } else { return null; } + } - @Override - protected void onPostExecute(Void aVoid) { - super.onPostExecute(aVoid); - if (queueRef != null && phRef != null) { - queue = queueRef; - playbackHistory = phRef; - Log.i(TAG, "Number of items in playback history: " + playbackHistory.size()); - if (fila != null) { - fila.notifyDataSetChanged(); - } - } else { - if (queueRef == null) { - Log.e(TAG, "Could not load queue"); - } - if (phRef == null) { - Log.e(TAG, "Could not load playback history"); - } + @Override + protected void onPostExecute(Object[] res) { + super.onPostExecute(res); + if (res != null) { + playbackHistory = (List<FeedItem>) res[0]; + queue = (QueueAccess) res[1]; + itemsLoaded = true; + if (viewsCreated) { + onFragmentLoaded(); } } - }; - loadTask.execute(); + } } } diff --git a/src/de/danoeh/antennapod/fragment/QueueFragment.java b/src/de/danoeh/antennapod/fragment/QueueFragment.java new file mode 100644 index 000000000..086e8f062 --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/QueueFragment.java @@ -0,0 +1,349 @@ +package de.danoeh.antennapod.fragment; + +import android.app.Activity; +import android.content.Context; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcelable; +import android.support.v4.app.Fragment; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.SearchView; +import android.util.Log; +import android.view.*; +import android.widget.AdapterView; +import android.widget.ProgressBar; +import android.widget.TextView; +import com.mobeta.android.dslv.DragSortListView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; +import de.danoeh.antennapod.adapter.QueueListAdapter; +import de.danoeh.antennapod.asynctask.DownloadObserver; +import de.danoeh.antennapod.dialog.FeedItemDialog; +import de.danoeh.antennapod.feed.EventDistributor; +import de.danoeh.antennapod.feed.FeedItem; +import de.danoeh.antennapod.feed.FeedMedia; +import de.danoeh.antennapod.service.download.Downloader; +import de.danoeh.antennapod.storage.DBReader; +import de.danoeh.antennapod.storage.DBWriter; +import de.danoeh.antennapod.util.QueueAccess; +import de.danoeh.antennapod.util.UndoBarController; +import de.danoeh.antennapod.util.gui.FeedItemUndoToken; +import de.danoeh.antennapod.util.menuhandler.MenuItemUtils; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Shows all items in the queue + */ +public class QueueFragment extends Fragment { + private static final String TAG = "QueueFragment"; + private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | + EventDistributor.DOWNLOAD_QUEUED | + EventDistributor.QUEUE_UPDATE; + + private DragSortListView listView; + private QueueListAdapter listAdapter; + private TextView txtvEmpty; + private ProgressBar progLoading; + private UndoBarController undoBarController; + + private List<FeedItem> queue; + private List<Downloader> downloaderList; + + private boolean itemsLoaded = false; + private boolean viewsCreated = false; + + private AtomicReference<Activity> activity = new AtomicReference<Activity>(); + + private DownloadObserver downloadObserver = null; + + private FeedItemDialog feedItemDialog; + + /** + * Download observer updates won't result in an upate of the list adapter if this is true. + */ + private boolean blockDownloadObserverUpdate = false; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + setHasOptionsMenu(true); + } + + @Override + public void onResume() { + super.onResume(); + startItemLoader(); + } + + @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + this.activity.set((MainActivity) getActivity()); + if (downloadObserver != null) { + downloadObserver.setActivity(getActivity()); + downloadObserver.onResume(); + } + if (viewsCreated && itemsLoaded) { + onFragmentLoaded(); + } + } + + @Override + public void onStop() { + super.onStop(); + EventDistributor.getInstance().unregister(contentUpdate); + stopItemLoader(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + this.activity.set((MainActivity) activity); + } + + private void resetViewState() { + listAdapter = null; + undoBarController = null; + activity.set(null); + viewsCreated = false; + blockDownloadObserverUpdate = false; + if (downloadObserver != null) { + downloadObserver.onPause(); + } + feedItemDialog = null; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + resetViewState(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + final SearchView sv = new SearchView(getActivity()); + MenuItemUtils.addSearchItem(menu, sv); + sv.setQueryHint(getString(R.string.search_hint)); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s)); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + return false; + } + }); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.queue_label); + + View root = inflater.inflate(R.layout.queue_fragment, container, false); + listView = (DragSortListView) root.findViewById(android.R.id.list); + txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + progLoading = (ProgressBar) root.findViewById(R.id.progLoading); + listView.setEmptyView(txtvEmpty); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount()); + if (item != null) { + feedItemDialog = FeedItemDialog.newInstace(activity.get(), item, QueueAccess.ItemListAccess(queue)); + feedItemDialog.show(); + } + } + }); + + undoBarController = new UndoBarController(root.findViewById(R.id.undobar), new UndoBarController.UndoListener() { + @Override + public void onUndo(Parcelable token) { + // Perform the undo + FeedItemUndoToken undoToken = (FeedItemUndoToken) token; + if (token != null) { + long itemId = undoToken.getFeedItemId(); + int position = undoToken.getPosition(); + DBWriter.addQueueItemAt(getActivity(), itemId, position, false); + } + } + }); + + listView.setDragSortListener(new DragSortListView.DragSortListener() { + @Override + public void drag(int from, int to) { + Log.d(TAG, "drag"); + blockDownloadObserverUpdate = true; + } + + @Override + public void drop(int from, int to) { + Log.d(TAG, "drop"); + blockDownloadObserverUpdate = false; + stopItemLoader(); + final FeedItem item = queue.remove(from); + queue.add(to, item); + listAdapter.notifyDataSetChanged(); + DBWriter.moveQueueItem(getActivity(), from, to, true); + } + + @Override + public void remove(int which) { + stopItemLoader(); + FeedItem item = (FeedItem) listView.getAdapter().getItem(which); + DBWriter.removeQueueItem(getActivity(), item.getId(), true); + undoBarController.showUndoBar(false, + getString(R.string.removed_from_queue), new FeedItemUndoToken(item, + which) + ); + } + }); + + if (!itemsLoaded) { + progLoading.setVisibility(View.VISIBLE); + txtvEmpty.setVisibility(View.GONE); + } + + viewsCreated = true; + + if (itemsLoaded && activity.get() != null) { + onFragmentLoaded(); + } + + return root; + } + + private void onFragmentLoaded() { + if (listAdapter == null) { + listAdapter = new QueueListAdapter(activity.get(), itemAccess, new DefaultActionButtonCallback(activity.get())); + listView.setAdapter(listAdapter); + downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); + downloadObserver.onResume(); + } + listAdapter.notifyDataSetChanged(); + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.setQueue(QueueAccess.ItemListAccess(queue)); + feedItemDialog.setItemFromCollection(queue); + feedItemDialog.updateMenuAppearance(); + } + } + + private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { + @Override + public void onContentChanged() { + if (listAdapter != null && !blockDownloadObserverUpdate) { + listAdapter.notifyDataSetChanged(); + } + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.updateMenuAppearance(); + } + } + + @Override + public void onDownloadDataAvailable(List<Downloader> downloaderList) { + QueueFragment.this.downloaderList = downloaderList; + if (listAdapter != null && !blockDownloadObserverUpdate) { + listAdapter.notifyDataSetChanged(); + } + } + }; + + private QueueListAdapter.ItemAccess itemAccess = new QueueListAdapter.ItemAccess() { + @Override + public int getCount() { + return (itemsLoaded) ? queue.size() : 0; + } + + @Override + public FeedItem getItem(int position) { + return (itemsLoaded) ? queue.get(position) : null; + } + + @Override + public int getItemDownloadProgressPercent(FeedItem item) { + if (downloaderList != null) { + for (Downloader downloader : downloaderList) { + if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA + && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) { + return downloader.getDownloadRequest().getProgressPercent(); + } + } + } + return 0; + } + }; + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((arg & EVENTS) != 0) { + startItemLoader(); + } + } + }; + + private ItemLoader itemLoader; + + private void startItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + itemLoader = new ItemLoader(); + itemLoader.execute(); + } + + private void stopItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + } + + private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> { + @Override + protected void onPreExecute() { + super.onPreExecute(); + if (viewsCreated && !itemsLoaded) { + listView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progLoading.setVisibility(View.VISIBLE); + } + } + + @Override + protected void onPostExecute(List<FeedItem> feedItems) { + super.onPostExecute(feedItems); + listView.setVisibility(View.VISIBLE); + progLoading.setVisibility(View.GONE); + + if (feedItems != null) { + queue = feedItems; + itemsLoaded = true; + if (viewsCreated && activity.get() != null) { + onFragmentLoaded(); + } + } + } + + @Override + protected List<FeedItem> doInBackground(Void... params) { + Context context = activity.get(); + if (context != null) { + return DBReader.getQueue(context); + } + return null; + } + } +} diff --git a/src/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/src/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java new file mode 100644 index 000000000..89c30e34b --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java @@ -0,0 +1,69 @@ +package de.danoeh.antennapod.fragment; + +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.ListFragment; +import android.view.View; +import de.danoeh.antennapod.adapter.DownloadlistAdapter; +import de.danoeh.antennapod.asynctask.DownloadObserver; +import de.danoeh.antennapod.service.download.Downloader; +import de.danoeh.antennapod.storage.DownloadRequester; + +import java.util.List; + +/** + * Displays all running downloads and provides actions to cancel them + */ +public class RunningDownloadsFragment extends ListFragment { + private static final String TAG = "RunningDownloadsFragment"; + + private DownloadObserver downloadObserver; + private List<Downloader> downloaderList; + + + @Override + public void onDetach() { + super.onDetach(); + if (downloadObserver != null) { + downloadObserver.onPause(); + } + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + final DownloadlistAdapter downloadlistAdapter = new DownloadlistAdapter(getActivity(), itemAccess); + setListAdapter(downloadlistAdapter); + + downloadObserver = new DownloadObserver(getActivity(), new Handler(), new DownloadObserver.Callback() { + @Override + public void onContentChanged() { + downloadlistAdapter.notifyDataSetChanged(); + } + + @Override + public void onDownloadDataAvailable(List<Downloader> downloaderList) { + RunningDownloadsFragment.this.downloaderList = downloaderList; + downloadlistAdapter.notifyDataSetChanged(); + } + }); + downloadObserver.onResume(); + } + + private DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() { + @Override + public int getCount() { + return (downloaderList != null) ? downloaderList.size() : 0; + } + + @Override + public Downloader getItem(int position) { + return (downloaderList != null) ? downloaderList.get(position) : null; + } + + @Override + public void onSecondaryActionClick(Downloader downloader) { + DownloadRequester.getInstance().cancelDownload(getActivity(), downloader.getDownloadRequest().getSource()); + } + }; +} diff --git a/src/de/danoeh/antennapod/fragment/SearchFragment.java b/src/de/danoeh/antennapod/fragment/SearchFragment.java new file mode 100644 index 000000000..566695465 --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/SearchFragment.java @@ -0,0 +1,247 @@ +package de.danoeh.antennapod.fragment; + +import android.content.Context; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.ListView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.SearchlistAdapter; +import de.danoeh.antennapod.dialog.FeedItemDialog; +import de.danoeh.antennapod.feed.*; +import de.danoeh.antennapod.storage.DBReader; +import de.danoeh.antennapod.storage.FeedSearcher; +import de.danoeh.antennapod.util.QueueAccess; + +import java.util.List; + +/** + * Performs a search operation on all feeds or one specific feed and displays the search result. + */ +public class SearchFragment extends ListFragment { + private static final String TAG = "SearchFragment"; + + private static final String ARG_QUERY = "query"; + private static final String ARG_FEED = "feed"; + + private SearchlistAdapter searchAdapter; + private List<SearchResult> searchResults; + + private boolean viewCreated = false; + private boolean itemsLoaded = false; + + private QueueAccess queue; + + private FeedItemDialog feedItemDialog; + + /** + * Create a new SearchFragment that searches all feeds. + */ + public static SearchFragment newInstance(String query) { + if (query == null) query = ""; + SearchFragment fragment = new SearchFragment(); + Bundle args = new Bundle(); + args.putString(ARG_QUERY, query); + args.putLong(ARG_FEED, 0); + fragment.setArguments(args); + return fragment; + } + + /** + * Create a new SearchFragment that searches one specific feed. + */ + public static SearchFragment newInstance(String query, long feed) { + SearchFragment fragment = newInstance(query); + fragment.getArguments().putLong(ARG_FEED, feed); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + setHasOptionsMenu(true); + startSearchTask(); + } + + @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + } + + @Override + public void onStop() { + super.onStop(); + stopSearchTask(); + EventDistributor.getInstance().unregister(contentUpdate); + } + + @Override + public void onDetach() { + super.onDetach(); + stopSearchTask(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + searchAdapter = null; + viewCreated = false; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label); + viewCreated = true; + if (itemsLoaded) { + onFragmentLoaded(); + } + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + super.onListItemClick(l, v, position, id); + SearchResult result = (SearchResult) l.getAdapter().getItem(position); + FeedComponent comp = result.getComponent(); + if (comp.getClass() == Feed.class) { + ((MainActivity)getActivity()).loadFeedFragment(comp.getId()); + } else { + if (comp.getClass() == FeedItem.class) { + feedItemDialog = FeedItemDialog.newInstace(getActivity(), (FeedItem) comp, queue); + feedItemDialog.show(); + } + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label); + MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); + final SearchView sv = new SearchView(getActivity()); + sv.setQueryHint(getString(R.string.search_hint)); + sv.setQuery(getArguments().getString(ARG_QUERY), false); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + getArguments().putString(ARG_QUERY, s); + itemsLoaded = false; + startSearchTask(); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + return false; + } + }); + MenuItemCompat.setActionView(item, sv); + } + + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((arg & (EventDistributor.DOWNLOAD_QUEUED)) != 0) { + feedItemDialog.updateMenuAppearance(); + } + if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE + | EventDistributor.DOWNLOAD_HANDLED + | EventDistributor.QUEUE_UPDATE)) != 0) { + startSearchTask(); + } + } + }; + + private void onFragmentLoaded() { + if (searchAdapter == null) { + searchAdapter = new SearchlistAdapter(getActivity(), itemAccess); + setListAdapter(searchAdapter); + } + searchAdapter.notifyDataSetChanged(); + setListShown(true); + if (feedItemDialog != null && feedItemDialog.isShowing()) { + feedItemDialog.setQueue(queue); + for (SearchResult result : searchResults) { + FeedComponent comp = result.getComponent(); + if (comp.getClass() == FeedItem.class && ((FeedItem) comp).getId() == feedItemDialog.getItem().getId()) { + feedItemDialog.setItem((FeedItem) comp); + } + } + feedItemDialog.updateMenuAppearance(); + } + } + + private final SearchlistAdapter.ItemAccess itemAccess = new SearchlistAdapter.ItemAccess() { + @Override + public int getCount() { + return (searchResults != null) ? searchResults.size() : 0; + } + + @Override + public SearchResult getItem(int position) { + return (searchResults != null) ? searchResults.get(position) : null; + } + }; + + private SearchTask searchTask; + + private void startSearchTask() { + if (searchTask != null) { + searchTask.cancel(true); + } + searchTask = new SearchTask(); + searchTask.execute(getArguments()); + } + + private void stopSearchTask() { + if (searchTask != null) { + searchTask.cancel(true); + } + } + + private class SearchTask extends AsyncTask<Bundle, Void, Object[]> { + @Override + protected Object[] doInBackground(Bundle... params) { + String query = params[0].getString(ARG_QUERY); + long feed = params[0].getLong(ARG_FEED); + Context context = getActivity(); + if (context != null) { + return new Object[]{FeedSearcher.performSearch(context, query, feed), + QueueAccess.IDListAccess(DBReader.getQueueIDList(context))}; + } else { + return null; + } + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + if (viewCreated && !itemsLoaded) { + setListShown(false); + } + } + + @Override + protected void onPostExecute(Object[] results) { + super.onPostExecute(results); + if (results != null) { + itemsLoaded = true; + searchResults = (List<SearchResult>) results[0]; + queue = (QueueAccess) results[1]; + if (viewCreated) { + onFragmentLoaded(); + } + } + } + } +} diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java new file mode 100644 index 000000000..ec8f69368 --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java @@ -0,0 +1,131 @@ +package de.danoeh.antennapod.fragment.gpodnet; + +import android.app.Activity; +import android.content.res.Resources; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; + +/** + * Main navigation hub for gpodder.net podcast directory + */ +public class GpodnetMainFragment extends Fragment { + + private ViewPager pager; + private MainActivity activity; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + View root = inflater.inflate(R.layout.pager_fragment, container, false); + pager = (ViewPager) root.findViewById(R.id.pager); + GpodnetPagerAdapter pagerAdapter = new GpodnetPagerAdapter(getChildFragmentManager(), getResources()); + pager.setAdapter(pagerAdapter); + final ActionBar actionBar = activity.getMainActivtyActionBar(); + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + ActionBar.TabListener tabListener = new ActionBar.TabListener() { + @Override + public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + pager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + + } + + @Override + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + + } + }; + actionBar.removeAllTabs(); + actionBar.addTab(actionBar.newTab() + .setText(R.string.gpodnet_taglist_header) + .setTabListener(tabListener)); + actionBar.addTab(actionBar.newTab() + .setText(R.string.gpodnet_toplist_header) + .setTabListener(tabListener)); + actionBar.setTitle(R.string.gpodnet_main_label); + + pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + actionBar.setSelectedNavigationItem(position); + } + }); + return root; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + activity.getMainActivtyActionBar().removeAllTabs(); + activity.getMainActivtyActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + this.activity = (MainActivity) activity; + } + + public class GpodnetPagerAdapter extends FragmentPagerAdapter { + + + private static final int NUM_PAGES = 2; + private static final int POS_TAGS = 0; + private static final int POS_TOPLIST = 1; + private static final int POS_SUGGESTIONS = 2; + + Resources resources; + + public GpodnetPagerAdapter(FragmentManager fm, Resources resources) { + super(fm); + this.resources = resources; + } + + @Override + public Fragment getItem(int i) { + switch (i) { + case POS_TAGS: + return new TagListFragment(); + case POS_TOPLIST: + return new PodcastTopListFragment(); + case POS_SUGGESTIONS: + return new SuggestionListFragment(); + default: + return null; + } + } + + @Override + public CharSequence getPageTitle(int position) { + switch (position) { + case POS_TAGS: + return getString(R.string.gpodnet_taglist_header); + case POS_TOPLIST: + return getString(R.string.gpodnet_toplist_header); + case POS_SUGGESTIONS: + return getString(R.string.gpodnet_suggestions_header); + default: + return super.getPageTitle(position); + } + } + + @Override + public int getCount() { + return NUM_PAGES; + } + } +} diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index 4164429b2..837df0594 100644 --- a/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -5,19 +5,22 @@ import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.support.v7.widget.*; import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.*; +import android.widget.SearchView; import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.DefaultOnlineFeedViewActivity; +import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.gpodnet.PodcastListAdapter; +import de.danoeh.antennapod.fragment.SearchFragment; import de.danoeh.antennapod.gpoddernet.GpodnetService; import de.danoeh.antennapod.gpoddernet.GpodnetServiceException; import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast; +import de.danoeh.antennapod.util.menuhandler.MenuItemUtils; import java.util.List; @@ -33,8 +36,34 @@ public abstract class PodcastListFragment extends Fragment { private Button butRetry; @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + final android.support.v7.widget.SearchView sv = new android.support.v7.widget.SearchView(getActivity()); + MenuItemUtils.addSearchItem(menu, sv); + sv.setQueryHint(getString(R.string.gpodnet_search_hint)); + sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s)); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + return false; + } + }); + } + + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - setRetainInstance(true); View root = inflater.inflate(R.layout.gpodnet_podcast_list, container, false); gridView = (GridView) root.findViewById(R.id.gridView); diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java index 322d13097..79d0c5d6f 100644 --- a/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java +++ b/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java @@ -1,14 +1,21 @@ package de.danoeh.antennapod.fragment.gpodnet; import android.os.Bundle; +import android.support.v7.widget.SearchView; +import android.view.Menu; +import android.view.MenuInflater; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.fragment.SearchFragment; import de.danoeh.antennapod.gpoddernet.GpodnetService; import de.danoeh.antennapod.gpoddernet.GpodnetServiceException; import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast; +import de.danoeh.antennapod.util.menuhandler.MenuItemUtils; import java.util.List; /** - * Created by daniel on 23.08.13. + * Performs a search on the gpodder.net directory and displays the results. */ public class SearchListFragment extends PodcastListFragment { private static final String ARG_QUERY = "query"; @@ -26,6 +33,7 @@ public class SearchListFragment extends PodcastListFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (getArguments() != null && getArguments().containsKey(ARG_QUERY)) { this.query = getArguments().getString(ARG_QUERY); } else { @@ -34,6 +42,27 @@ public class SearchListFragment extends PodcastListFragment { } @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + final SearchView sv = new SearchView(getActivity()); + MenuItemUtils.addSearchItem(menu, sv); + sv.setQueryHint(getString(R.string.gpodnet_search_hint)); + sv.setQuery(query, false); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + changeQuery(s); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + return false; + } + }); + } + + @Override protected List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException { return service.searchPodcasts(query, 0); } diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java new file mode 100644 index 000000000..f016290bf --- /dev/null +++ b/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java @@ -0,0 +1,47 @@ +package de.danoeh.antennapod.fragment.gpodnet; + +import android.os.Bundle; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.gpoddernet.GpodnetService; +import de.danoeh.antennapod.gpoddernet.GpodnetServiceException; +import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast; +import de.danoeh.antennapod.gpoddernet.model.GpodnetTag; + +import java.util.List; + +/** + * Shows all podcasts from gpodder.net that belong to a specific tag. + * Use the newInstance method of this class to create a new TagFragment. + */ +public class TagFragment extends PodcastListFragment { + + private static final String TAG = "TagFragment"; + private static final int PODCAST_COUNT = 50; + + private GpodnetTag tag; + + public static TagFragment newInstance(String tagName) { + if (tagName == null) throw new IllegalArgumentException("tagName = null"); + TagFragment fragment = new TagFragment(); + Bundle args = new Bundle(); + args.putString("tag", tagName); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + if (args == null || args.getString("tag") == null) throw new IllegalArgumentException("args invalid"); + + tag = new GpodnetTag(args.getString("tag")); + ((MainActivity) getActivity()).getMainActivtyActionBar().setTitle(tag.getName()); + } + + @Override + protected List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException { + return service.getPodcastsForTag(tag, PODCAST_COUNT); + } +} diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java index fcb9d01c5..80e896c0f 100644 --- a/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java +++ b/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java @@ -1,18 +1,23 @@ package de.danoeh.antennapod.fragment.gpodnet; import android.content.Context; -import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.support.v7.widget.SearchView; +import android.view.Menu; +import android.view.MenuInflater; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.TextView; -import de.danoeh.antennapod.activity.gpoddernet.GpodnetTagActivity; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.fragment.SearchFragment; import de.danoeh.antennapod.gpoddernet.GpodnetService; import de.danoeh.antennapod.gpoddernet.GpodnetServiceException; import de.danoeh.antennapod.gpoddernet.model.GpodnetTag; +import de.danoeh.antennapod.util.menuhandler.MenuItemUtils; import java.util.ArrayList; import java.util.List; @@ -22,17 +27,42 @@ public class TagListFragment extends ListFragment { private static final int COUNT = 50; @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + final SearchView sv = new SearchView(getActivity()); + MenuItemUtils.addSearchItem(menu, sv); + sv.setQueryHint(getString(R.string.search_hint)); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s)); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + return false; + } + }); + } + + @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - setRetainInstance(true); getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String selectedTag = (String) getListAdapter().getItem(position); - Intent intent = new Intent(getActivity(), GpodnetTagActivity.class); - intent.putExtra(GpodnetTagActivity.ARG_TAGNAME, selectedTag); - startActivity(intent); + MainActivity activity = (MainActivity) getActivity(); + activity.loadChildFragment(TagFragment.newInstance(selectedTag)); } }); diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index f5883babc..c7f3b8573 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -13,6 +13,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaMetadataRetriever; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.support.v4.app.NotificationCompat; @@ -23,7 +24,10 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.DownloadActivity; import de.danoeh.antennapod.activity.DownloadAuthenticationActivity; import de.danoeh.antennapod.activity.DownloadLogActivity; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.NavListAdapter; import de.danoeh.antennapod.feed.*; +import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.storage.*; import de.danoeh.antennapod.syndication.handler.FeedHandler; import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException; @@ -274,11 +278,18 @@ public class DownloadService extends Service { @SuppressLint("NewApi") private void setupNotificationBuilders() { - PendingIntent pIntent = PendingIntent.getActivity(this, 0, new Intent( - this, DownloadActivity.class), + Intent intent = new Intent(this, MainActivity.class); + intent.putExtra(MainActivity.EXTRA_NAV_TYPE, NavListAdapter.VIEW_TYPE_NAV); + intent.putExtra(MainActivity.EXTRA_NAV_INDEX, MainActivity.POS_DOWNLOADS); + Bundle args = new Bundle(); + args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_RUNNING); + intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args); + + PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT ); + Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.stat_notify_sync); @@ -490,6 +501,13 @@ public class DownloadService extends Service { if (createReport) { if (BuildConfig.DEBUG) Log.d(TAG, "Creating report"); + Intent intent = new Intent(this, MainActivity.class); + intent.putExtra(MainActivity.EXTRA_NAV_TYPE, NavListAdapter.VIEW_TYPE_NAV); + intent.putExtra(MainActivity.EXTRA_NAV_INDEX, MainActivity.POS_DOWNLOADS); + Bundle args = new Bundle(); + args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG); + intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args); + // create notification object Notification notification = new NotificationCompat.Builder(this) .setTicker( @@ -507,8 +525,7 @@ public class DownloadService extends Service { R.drawable.stat_notify_sync) ) .setContentIntent( - PendingIntent.getActivity(this, 0, new Intent(this, - DownloadLogActivity.class), 0) + PendingIntent.getActivity(this, 0, intent, 0) ) .setAutoCancel(true).getNotification(); NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/src/de/danoeh/antennapod/storage/DBReader.java b/src/de/danoeh/antennapod/storage/DBReader.java index 8d4785bd4..859ff2473 100644 --- a/src/de/danoeh/antennapod/storage/DBReader.java +++ b/src/de/danoeh/antennapod/storage/DBReader.java @@ -504,6 +504,32 @@ public final class DBReader { return itemIds; } + + /** + * Loads a list of FeedItems sorted by pubDate in descending order. + * + * @param context A context that is used for opening a database connection. + * @param limit The maximum number of episodes that should be loaded. + * */ + public static List<FeedItem> getRecentlyPublishedEpisodes(Context context, int limit) { + if (BuildConfig.DEBUG) + Log.d(TAG, "Extracting recently published items list"); + + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + + Cursor itemlistCursor = adapter.getRecentlyPublishedItemsCursor(limit); + List<FeedItem> items = extractItemlistFromCursor(adapter, + itemlistCursor); + itemlistCursor.close(); + + loadFeedDataOfFeedItemlist(context, items); + + adapter.close(); + + return items; + } + /** * Loads the playback history from the database. A FeedItem is in the playback history if playback of the correpsonding episode * has been completed at least once. diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java index 8e2d10711..40a71a75d 100644 --- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java +++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java @@ -999,6 +999,11 @@ public class PodDBAdapter { } + public final Cursor getRecentlyPublishedItemsCursor(int limit) { + Cursor c = db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, null, null, null, null, KEY_PUBDATE + " DESC LIMIT " + limit); + return c; + } + public Cursor getDownloadedItemsCursor() { final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON " diff --git a/src/de/danoeh/antennapod/util/gui/FeedItemUndoToken.java b/src/de/danoeh/antennapod/util/gui/FeedItemUndoToken.java new file mode 100644 index 000000000..b920559db --- /dev/null +++ b/src/de/danoeh/antennapod/util/gui/FeedItemUndoToken.java @@ -0,0 +1,55 @@ +package de.danoeh.antennapod.util.gui; + +import android.os.Parcel; +import android.os.Parcelable; +import de.danoeh.antennapod.feed.FeedItem; + +/** + * Used by an UndoBarController for saving a removed FeedItem + */ +public class FeedItemUndoToken implements Parcelable { + private long itemId; + private long feedId; + private int position; + + public FeedItemUndoToken(FeedItem item, int position) { + this.itemId = item.getId(); + this.feedId = item.getFeed().getId(); + this.position = position; + } + + private FeedItemUndoToken(Parcel in) { + itemId = in.readLong(); + feedId = in.readLong(); + position = in.readInt(); + } + + public static final Parcelable.Creator<FeedItemUndoToken> CREATOR = new Parcelable.Creator<FeedItemUndoToken>() { + public FeedItemUndoToken createFromParcel(Parcel in) { + return new FeedItemUndoToken(in); + } + + public FeedItemUndoToken[] newArray(int size) { + return new FeedItemUndoToken[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeLong(itemId); + out.writeLong(feedId); + out.writeInt(position); + } + + public long getFeedItemId() { + return itemId; + } + + public int getPosition() { + return position; + } +} + diff --git a/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java b/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java new file mode 100644 index 000000000..e75fa394a --- /dev/null +++ b/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java @@ -0,0 +1,20 @@ +package de.danoeh.antennapod.util.menuhandler; + +import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.SearchView; +import android.view.Menu; +import android.view.MenuItem; +import de.danoeh.antennapod.R; + +/** + * Utilities for menu items + */ +public class MenuItemUtils { + + public static MenuItem addSearchItem(Menu menu, SearchView searchView) { + MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label); + MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); + MenuItemCompat.setActionView(item, searchView); + return item; + } +} |