summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2012-09-19 23:49:03 +0400
committerAndrew Dolgov <[email protected]>2012-09-19 23:49:03 +0400
commitd361b0fec497911de1e8616cb4d0768601e07171 (patch)
treef5f92112ca8451d255e765b9f5d1670a79b6850a
parent71fb6880f565ff417ccd5ac387263dfb8bd05826 (diff)
parent45992f20f25bc94f2f81df2392df26b6c6f8cea7 (diff)
merge separate-activities
-rw-r--r--AndroidManifest.xml50
-rw-r--r--libs/android-support-v4.jarbin247894 -> 349252 bytes
-rw-r--r--res/layout-port/headlines_row.xml6
-rw-r--r--res/layout-port/headlines_row_selected.xml6
-rw-r--r--res/layout-port/headlines_row_unread.xml6
-rw-r--r--res/layout-sw600dp-port/headlines.xml48
-rw-r--r--res/layout-sw600dp-port/main.xml61
-rw-r--r--res/layout-sw600dp/feeds.xml (renamed from res/layout-sw600dp/main.xml)103
-rw-r--r--res/layout-sw600dp/headlines.xml49
-rw-r--r--res/layout/article_fragment.xml24
-rw-r--r--res/layout/feeds.xml (renamed from res/layout/main.xml)2
-rw-r--r--res/layout/headlines.xml27
-rw-r--r--res/layout/headlines_row_loadmore.xml13
-rw-r--r--res/layout/login.xml14
-rw-r--r--res/menu/category_menu.xml4
-rw-r--r--res/menu/feed_menu.xml8
-rw-r--r--res/menu/headlines_context_menu.xml (renamed from res/menu/headlines_menu.xml)0
-rw-r--r--res/menu/main_menu.xml63
-rw-r--r--res/menu/offline_menu.xml14
-rw-r--r--res/values-v11/style.xml2
-rw-r--r--res/values/arrays.xml10
-rw-r--r--res/values/strings.xml11
-rw-r--r--res/xml/preferences.xml49
-rw-r--r--src/org/fox/ttrss/ApiRequest.java238
-rw-r--r--src/org/fox/ttrss/ArticleFragment.java90
-rw-r--r--src/org/fox/ttrss/ArticlePager.java182
-rw-r--r--src/org/fox/ttrss/CommonActivity.java64
-rw-r--r--src/org/fox/ttrss/FeedCategoriesFragment.java119
-rw-r--r--src/org/fox/ttrss/FeedsActivity.java300
-rw-r--r--src/org/fox/ttrss/FeedsFragment.java245
-rw-r--r--src/org/fox/ttrss/GlobalState.java31
-rw-r--r--src/org/fox/ttrss/HeadlinesActivity.java187
-rw-r--r--src/org/fox/ttrss/HeadlinesEventListener.java11
-rw-r--r--src/org/fox/ttrss/HeadlinesFragment.java391
-rw-r--r--src/org/fox/ttrss/MainActivity.java2313
-rw-r--r--src/org/fox/ttrss/OnlineActivity.java1283
-rw-r--r--src/org/fox/ttrss/OnlineServices.java32
-rw-r--r--src/org/fox/ttrss/PreferencesActivity.java10
-rw-r--r--src/org/fox/ttrss/offline/OfflineActivity.java2112
-rw-r--r--src/org/fox/ttrss/offline/OfflineArticleFragment.java79
-rw-r--r--src/org/fox/ttrss/offline/OfflineArticlePager.java158
-rw-r--r--src/org/fox/ttrss/offline/OfflineDownloadService.java118
-rw-r--r--src/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java59
-rw-r--r--src/org/fox/ttrss/offline/OfflineFeedsActivity.java253
-rw-r--r--src/org/fox/ttrss/offline/OfflineFeedsFragment.java42
-rw-r--r--src/org/fox/ttrss/offline/OfflineHeadlinesActivity.java142
-rw-r--r--src/org/fox/ttrss/offline/OfflineHeadlinesEventListener.java7
-rw-r--r--src/org/fox/ttrss/offline/OfflineHeadlinesFragment.java222
-rw-r--r--src/org/fox/ttrss/offline/OfflineServices.java22
-rw-r--r--src/org/fox/ttrss/offline/OfflineUploadService.java5
-rw-r--r--src/org/fox/ttrss/types/ArticleList.java8
-rw-r--r--src/org/fox/ttrss/types/Feed.java10
-rw-r--r--src/org/fox/ttrss/util/Base64.java582
-rw-r--r--src/org/fox/ttrss/util/Base64DecoderException.java32
-rw-r--r--src/org/fox/ttrss/util/EasySSLSocketFactory.java120
-rw-r--r--src/org/fox/ttrss/util/EasyX509TrustManager.java26
-rw-r--r--src/org/fox/ttrss/util/HeadlinesRequest.java82
-rw-r--r--src/org/fox/ttrss/util/ImageCacheService.java6
58 files changed, 4783 insertions, 5368 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bc4254c1..1f72589d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.fox.ttrss"
- android:versionCode="98"
- android:versionName="0.7.6" >
+ android:versionCode="100"
+ android:versionName="0.8.0" >
<uses-sdk
android:minSdkVersion="8"
@@ -13,21 +13,14 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
+ android:name=".GlobalState"
android:allowBackup="true"
android:backupAgent=".util.PrefsBackupAgent"
android:hardwareAccelerated="true"
android:icon="@drawable/icon"
android:label="@string/app_name" >
<activity
- android:name=".LoginActivity"
- android:label="@string/app_name" >
- </activity>
- <activity
- android:name=".offline.OfflineActivity"
- android:label="@string/app_name" >
- </activity>
- <activity
- android:name=".MainActivity"
+ android:name=".OnlineActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -40,6 +33,41 @@
android:label="@string/preferences" >
</activity>
+ <activity
+ android:name=".FeedsActivity"
+ android:label="@string/app_name" >
+ </activity>
+
+ <activity
+ android:name=".HeadlinesActivity"
+ android:label="@string/app_name" >
+ </activity>
+
+ <activity
+ android:name=".CommonActivity"
+ android:label="@string/app_name" >
+ </activity>
+
+ <activity
+ android:name=".ArticleActivity"
+ android:label="@string/app_name" >
+ </activity>
+
+ <activity
+ android:name=".offline.OfflineActivity"
+ android:label="@string/app_name" >
+ </activity>
+
+ <activity
+ android:name=".offline.OfflineFeedsActivity"
+ android:label="@string/app_name" >
+ </activity>
+
+ <activity
+ android:name=".offline.OfflineHeadlinesActivity"
+ android:label="@string/app_name" >
+ </activity>
+
<service
android:name=".offline.OfflineDownloadService"
android:enabled="true" />
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
index d006198e..feaf44f8 100644
--- a/libs/android-support-v4.jar
+++ b/libs/android-support-v4.jar
Binary files differ
diff --git a/res/layout-port/headlines_row.xml b/res/layout-port/headlines_row.xml
index 8f65f7ec..4a6679bc 100644
--- a/res/layout-port/headlines_row.xml
+++ b/res/layout-port/headlines_row.xml
@@ -7,11 +7,6 @@
android:gravity="center_vertical"
android:orientation="vertical" >
- <LinearLayout
- android:id="@+id/linearLayout7"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
@@ -100,7 +95,6 @@
android:src="@drawable/ic_rss_bw" />
</LinearLayout>
</LinearLayout>
- </LinearLayout>
<TextView
android:id="@+id/content"
diff --git a/res/layout-port/headlines_row_selected.xml b/res/layout-port/headlines_row_selected.xml
index 8c5af7f9..ad5ab33f 100644
--- a/res/layout-port/headlines_row_selected.xml
+++ b/res/layout-port/headlines_row_selected.xml
@@ -7,11 +7,6 @@
android:gravity="center_vertical"
android:orientation="vertical" >
- <LinearLayout
- android:id="@+id/linearLayout7"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
@@ -100,7 +95,6 @@
android:src="@drawable/ic_rss_bw" />
</LinearLayout>
</LinearLayout>
- </LinearLayout>
<TextView
android:id="@+id/content"
diff --git a/res/layout-port/headlines_row_unread.xml b/res/layout-port/headlines_row_unread.xml
index 35b2dead..ce0cf9a3 100644
--- a/res/layout-port/headlines_row_unread.xml
+++ b/res/layout-port/headlines_row_unread.xml
@@ -7,11 +7,6 @@
android:gravity="center_vertical"
android:orientation="vertical" >
- <LinearLayout
- android:id="@+id/linearLayout7"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
@@ -100,7 +95,6 @@
android:src="@drawable/ic_rss_bw" />
</LinearLayout>
</LinearLayout>
- </LinearLayout>
<TextView
android:id="@+id/content"
diff --git a/res/layout-sw600dp-port/headlines.xml b/res/layout-sw600dp-port/headlines.xml
new file mode 100644
index 00000000..84d8d654
--- /dev/null
+++ b/res/layout-sw600dp-port/headlines.xml
@@ -0,0 +1,48 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/loading_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?loadingBackground"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="visible" >
+
+ <TextView
+ android:id="@+id/loading_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/loading_message"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <FrameLayout
+ android:id="@+id/headlines_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.3"
+ android:background="?headlinesBackgroundSolid" >
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/article_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.7"
+ android:background="?articleBackground" >
+ </FrameLayout>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout-sw600dp-port/main.xml b/res/layout-sw600dp-port/main.xml
deleted file mode 100644
index 02732ba4..00000000
--- a/res/layout-sw600dp-port/main.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/main"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
-
- <LinearLayout
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal" >
-
- <FrameLayout
- android:id="@+id/feeds_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="0.6"
- android:background="?feedlistBackground" >
- </FrameLayout>
-
- <LinearLayout
- android:id="@+id/vertical_fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="0.4"
- android:orientation="vertical" >
-
- <FrameLayout
- android:id="@+id/headlines_fragment"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.30"
- android:background="?headlinesBackgroundSolid" >
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/article_fragment"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:background="?articleBackground"
- android:layout_weight="0.70" >
- </FrameLayout>
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/loading_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:background="?loadingBackground"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/loading_message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:text="@string/loading_message"
- android:textAppearance="?android:attr/textAppearanceLarge" />
- </LinearLayout>
-</FrameLayout> \ No newline at end of file
diff --git a/res/layout-sw600dp/main.xml b/res/layout-sw600dp/feeds.xml
index 9df4e048..3e75a37c 100644
--- a/res/layout-sw600dp/main.xml
+++ b/res/layout-sw600dp/feeds.xml
@@ -1,55 +1,48 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/main"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
-
- <LinearLayout
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal" >
-
- <FrameLayout
- android:id="@+id/feeds_fragment"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="0.3"
- android:background="?feedlistBackground" >
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/headlines_fragment"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="0.4"
- android:background="?headlinesBackground" >
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/article_fragment"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="0.6"
- android:background="?articleBackground" >
- </FrameLayout>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/loading_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?loadingBackground"
- android:gravity="center"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/loading_message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:text="@string/loading_message"
- android:textAppearance="?android:attr/textAppearanceLarge" />
- </LinearLayout>
-
-</FrameLayout> \ No newline at end of file
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/loading_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?loadingBackground"
+ android:gravity="center"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/loading_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/loading_message"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:baselineAligned="false"
+ android:orientation="horizontal" >
+
+ <FrameLayout
+ android:id="@+id/feeds_fragment"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.4"
+ android:background="?feedlistBackground" >
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/headlines_fragment"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.6"
+ android:background="?headlinesBackground" >
+ </FrameLayout>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout-sw600dp/headlines.xml b/res/layout-sw600dp/headlines.xml
new file mode 100644
index 00000000..54eabd01
--- /dev/null
+++ b/res/layout-sw600dp/headlines.xml
@@ -0,0 +1,49 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/headlines"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/loading_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?loadingBackground"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="visible" >
+
+ <TextView
+ android:id="@+id/loading_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/loading_message"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:baselineAligned="false"
+ android:orientation="horizontal" >
+
+ <FrameLayout
+ android:id="@+id/headlines_fragment"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.4"
+ android:background="?headlinesBackground" >
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/article_fragment"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0.6"
+ android:background="?articleBackground" >
+ </FrameLayout>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/article_fragment.xml b/res/layout/article_fragment.xml
index 94e0a42e..0b847017 100644
--- a/res/layout/article_fragment.xml
+++ b/res/layout/article_fragment.xml
@@ -1,18 +1,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/article_fragment"
android:layout_width="fill_parent"
+ android:padding="5sp"
android:layout_height="fill_parent"
- android:orientation="horizontal" >
-
- <LinearLayout
- xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/article_holder"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="vertical"
- android:padding="5sp" >
+ android:orientation="vertical" >
<LinearLayout
android:id="@+id/article_header"
@@ -24,6 +15,7 @@
<TextView
android:id="@+id/title"
+ android:textColor="?linkColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -92,12 +84,18 @@
android:text="@string/attachment_view" />
<Button
+ android:id="@+id/attachment_share"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/attachment_share" />
+
+ <Button
android:id="@+id/attachment_copy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/attachment_copy" />
</LinearLayout>
- </LinearLayout>
-
+
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/main.xml b/res/layout/feeds.xml
index eb06058c..c3f7be59 100644
--- a/res/layout/main.xml
+++ b/res/layout/feeds.xml
@@ -19,7 +19,7 @@
</LinearLayout>
<FrameLayout
- android:id="@+id/fragment_container"
+ android:id="@+id/feeds_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
diff --git a/res/layout/headlines.xml b/res/layout/headlines.xml
new file mode 100644
index 00000000..c3027217
--- /dev/null
+++ b/res/layout/headlines.xml
@@ -0,0 +1,27 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/headlines"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <LinearLayout
+ android:id="@+id/loading_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/loading_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/loading_message" />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/headlines_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ </FrameLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/headlines_row_loadmore.xml b/res/layout/headlines_row_loadmore.xml
index d2eda7a6..ab7adc87 100644
--- a/res/layout/headlines_row_loadmore.xml
+++ b/res/layout/headlines_row_loadmore.xml
@@ -4,18 +4,10 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?headlineNormalBackground"
- android:gravity="center_vertical"
+ android:gravity="center"
+ android:padding="5dp"
android:orientation="horizontal" >
- <LinearLayout
- android:id="@+id/loadmore_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:layout_weight="1"
- android:orientation="horizontal"
- android:padding="10dp" >
-
<ProgressBar
android:id="@+id/loadmore_progress"
style="?android:attr/progressBarStyleSmall"
@@ -29,6 +21,5 @@
android:layout_height="wrap_content"
android:textColor="?headlineTextColor"
android:text="@string/loading_message" />
- </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/login.xml b/res/layout/login.xml
new file mode 100644
index 00000000..1ed26afa
--- /dev/null
+++ b/res/layout/login.xml
@@ -0,0 +1,14 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/loading_container"
+ android:layout_width="fill_parent"
+ android:gravity="center"
+ android:layout_height="fill_parent" >
+
+ <TextView
+ android:id="@+id/loading_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/loading_message" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/menu/category_menu.xml b/res/menu/category_menu.xml
index a1e5f81b..69f26554 100644
--- a/res/menu/category_menu.xml
+++ b/res/menu/category_menu.xml
@@ -1,6 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
+ android:id="@+id/browse_headlines"
+ android:title="@string/category_browse_headlines"/>
+
+ <item
android:id="@+id/browse_articles"
android:title="@string/category_browse_articles"/>
diff --git a/res/menu/feed_menu.xml b/res/menu/feed_menu.xml
index ff58a881..df8e4862 100644
--- a/res/menu/feed_menu.xml
+++ b/res/menu/feed_menu.xml
@@ -1,6 +1,14 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
+ android:id="@+id/browse_headlines"
+ android:title="@string/category_browse_headlines"/>
+
+ <item
+ android:id="@+id/browse_articles"
+ android:title="@string/category_browse_articles"/>
+
+ <item
android:id="@+id/catchup_feed"
android:title="@string/catchup"/>
diff --git a/res/menu/headlines_menu.xml b/res/menu/headlines_context_menu.xml
index 073ecb37..073ecb37 100644
--- a/res/menu/headlines_menu.xml
+++ b/res/menu/headlines_context_menu.xml
diff --git a/res/menu/main_menu.xml b/res/menu/main_menu.xml
index c6e49511..a4d168a5 100644
--- a/res/menu/main_menu.xml
+++ b/res/menu/main_menu.xml
@@ -1,7 +1,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<group android:id="@+id/menu_group_logged_in" >
-
<group android:id="@+id/menu_group_feeds" >
<!--
@@ -17,136 +16,125 @@
android:icon="@android:drawable/ic_menu_agenda"
android:showAsAction=""
android:title="@string/menu_all_feeds"/>
-
<item
android:id="@+id/update_feeds"
android:icon="@android:drawable/ic_menu_rotate"
android:showAsAction="ifRoom"
android:title="@string/update_feeds"/>
-
<item
android:id="@+id/go_offline"
android:icon="@drawable/ic_menu_cloud"
android:showAsAction=""
android:title="@string/go_offline"/>
-
<item
android:id="@+id/logout"
android:icon="@drawable/ic_menu_exit"
android:showAsAction=""
android:title="@string/logout"/>
</group>
-
<group android:id="@+id/menu_group_headlines" >
-
<item
- android:id="@+id/go_offline"
- android:icon="@drawable/ic_menu_cloud"
- android:title="@string/go_offline"/>
-
+ android:id="@+id/update_headlines"
+ android:icon="@android:drawable/ic_menu_rotate"
+ android:showAsAction="ifRoom"
+ android:title="@string/update_headlines"/>
<item
android:id="@+id/search"
android:actionViewClass="android.widget.SearchView"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom|collapseActionView"
android:title="@string/search"/>
-
<item
android:id="@+id/headlines_mark_as_read"
android:icon="@drawable/ic_menu_tick"
android:showAsAction=""
android:title="@string/headlines_mark_as_read"/>
-
<item
android:id="@+id/headlines_select"
android:icon="@drawable/ic_menu_database"
- android:showAsAction="ifRoom"
+ android:showAsAction=""
android:title="@string/headlines_select"/>
-
- <item
+
+ <!--
+ <item
android:id="@+id/close_feed"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction=""
android:title="@string/close_feed"/>
-
- </group>
+ -->
+ </group>
<group android:id="@+id/menu_group_headlines_selection" >
-
<item
android:id="@+id/selection_toggle_unread"
android:icon="@drawable/ic_menu_tick"
android:showAsAction="ifRoom"
android:title="@string/selection_toggle_unread"/>
-
<item
android:id="@+id/selection_toggle_marked"
android:icon="@drawable/ic_menu_marked"
android:showAsAction="ifRoom"
android:title="@string/selection_toggle_marked"/>
-
<item
android:id="@+id/selection_toggle_published"
android:icon="@drawable/ic_menu_rss"
android:showAsAction="ifRoom"
android:title="@string/selection_toggle_published"/>
-
<item
android:id="@+id/selection_select_none"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction=""
android:title="@string/selection_select_none"/>
</group>
-
<group android:id="@+id/menu_group_article" >
-
<item
android:id="@+id/toggle_marked"
android:icon="@drawable/ic_menu_marked"
android:showAsAction="ifRoom"
android:title="@string/article_toggle_marked"/>
-
<item
android:id="@+id/toggle_published"
android:icon="@drawable/ic_menu_rss"
android:showAsAction="ifRoom"
android:title="@string/article_toggle_published"/>
-
<item
android:id="@+id/share_article"
+ android:actionProviderClass="android.widget.ShareActionProvider"
android:icon="@android:drawable/ic_menu_share"
android:showAsAction=""
- android:actionProviderClass="android.widget.ShareActionProvider"
android:title="@string/share_article"/>
-
<item
android:id="@+id/set_unread"
android:icon="@android:drawable/ic_menu_recent_history"
android:showAsAction=""
android:title="@string/article_set_unread"/>
-
<item
android:id="@+id/catchup_above"
android:icon="@drawable/ic_menu_tick"
android:title="@string/article_mark_read_above"/>
-
<item
android:id="@+id/set_labels"
android:icon="@drawable/ic_menu_marked"
android:title="@string/article_set_labels"/>
-
- <item
- android:id="@+id/article_set_note"
- android:showAsAction=""
- android:title="@string/article_set_note"/>
-
<item
+ android:id="@+id/article_set_note"
+ android:showAsAction=""
+ android:title="@string/article_set_note"/>
+
+ <!--
+ <item
android:id="@+id/close_article"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction=""
android:title="@string/close_article"/>
-
+ -->
+
</group>
+
+ <item
+ android:id="@+id/donate"
+ android:showAsAction=""
+ android:title="@string/donate"/>
</group>
<item
@@ -154,9 +142,8 @@
android:icon="@android:drawable/ic_menu_preferences"
android:showAsAction=""
android:title="@string/preferences"/>
-
- <group android:id="@+id/menu_group_logged_out" >
+ <group android:id="@+id/menu_group_logged_out" >
<item
android:id="@+id/login"
android:icon="@android:drawable/ic_menu_rotate"
diff --git a/res/menu/offline_menu.xml b/res/menu/offline_menu.xml
index 26444ef8..137184d4 100644
--- a/res/menu/offline_menu.xml
+++ b/res/menu/offline_menu.xml
@@ -23,11 +23,11 @@
android:title="@string/menu_all_feeds"/>
</group>
<group android:id="@+id/menu_group_headlines" >
- <item
+ <!-- <item
android:id="@+id/go_online"
android:icon="@drawable/ic_menu_cloud"
android:title="@string/go_online"
- android:visible="false"/>
+ android:visible="false"/> -->
<item
android:id="@+id/search"
android:actionViewClass="android.widget.SearchView"
@@ -41,13 +41,13 @@
<item
android:id="@+id/headlines_select"
android:icon="@drawable/ic_menu_database"
- android:showAsAction="ifRoom"
+ android:showAsAction=""
android:title="@string/headlines_select"/>
- <item
+ <!-- <item
android:id="@+id/close_feed"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction=""
- android:title="@string/close_feed"/>
+ android:title="@string/close_feed"/> -->
</group>
<group android:id="@+id/menu_group_headlines_selection" >
<item
@@ -97,11 +97,11 @@
android:id="@+id/catchup_above"
android:icon="@drawable/ic_menu_tick"
android:title="@string/article_mark_read_above"/>
- <item
+ <!-- <item
android:id="@+id/close_article"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction=""
- android:title="@string/close_article"/>
+ android:title="@string/close_article"/> -->
</group>
<item
diff --git a/res/values-v11/style.xml b/res/values-v11/style.xml
index 2d49b8c4..6b3d9e2c 100644
--- a/res/values-v11/style.xml
+++ b/res/values-v11/style.xml
@@ -28,7 +28,7 @@
<item name="feedlistBackground">@drawable/ics_divider_vertical</item>
<item name="unreadCounterColor">#303030</item>
<item name="headlinesBackground">@drawable/headlines_dark</item>
- <item name="headlinesBackgroundSolid">@android:color/transparent</item>
+ <item name="headlinesBackgroundSolid">@drawable/headlines_dark</item>
<item name="articleBackground">@android:color/black</item>
<item name="headlineSelectedBackground">@drawable/headline_row_selected_dark</item>
<item name="headlineSelectedBackgroundSolid">@color/ics_cyan</item>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index ef479753..961bd955 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -17,5 +17,13 @@
<item>1</item>
<item>2</item>
</string-array>
-
+ <string-array name="pref_view_mode_names">
+ <item>@string/category_browse_headlines</item>
+ <item>@string/category_browse_articles</item>
+ </string-array>
+ <string-array name="pref_view_mode_values">
+ <item>HEADLINES</item>
+ <item>ARTICLES</item>
+ </string-array>
+
</resources> \ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3a3fc913..2c766d85 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -24,7 +24,7 @@
<string name="loading_message">Loading, please wait…</string>
<string name="menu_unread_feeds">Show unread feeds</string>
<string name="menu_all_feeds">Show all feeds</string>
- <string name="update_feeds">Refresh feeds</string>
+ <string name="update_feeds">Refresh</string>
<string name="share_article">Share article</string>
<string name="catchup">Mark read</string>
<string name="sort_feeds_by_unread">Sort feeds by unread count</string>
@@ -128,6 +128,11 @@
<string name="notify_article_published">Article published</string>
<string name="notify_article_unpublished">Article unpublished</string>
<string name="notify_article_note_set">Article note saved</string>
- <string name="force_small_tablet_ui">Force compact interface</string>
- <string name="prefs_for_tablets">Tablets</string>
+ <string name="update_headlines">Refresh</string>
+ <string name="attachment_share">Share</string>
+ <string name="error_network_unavailable">Error: network unavailable</string>
+ <string name="category_browse_headlines">Browse headlines</string>
+ <string name="pref_default_view_mode">Default feed view</string>
+ <string name="pref_default_view_mode_long">Which feed view to open by default on smartphones</string>
+ <string name="donate_thanks">Donation found, thank you for support!</string>
</resources> \ No newline at end of file
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 6bc4c427..d6b81026 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -2,18 +2,21 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="@string/connection" >
+
<EditTextPreference
android:key="login"
android:singleLine="true"
android:summary="@string/login_summary"
android:title="@string/login" >
</EditTextPreference>
+
<EditTextPreference
android:key="password"
android:password="true"
android:singleLine="true"
android:title="@string/password" >
</EditTextPreference>
+
<EditTextPreference
android:hint="@string/default_url"
android:inputType="textUri"
@@ -28,13 +31,16 @@
android:key="ssl_trust_any"
android:title="@string/ssl_trust_any" />
</PreferenceCategory>
+
<PreferenceCategory android:title="@string/http_authentication" >
+
<EditTextPreference
android:key="http_login"
android:singleLine="true"
android:summary="@string/http_login_summary"
android:title="@string/login" >
</EditTextPreference>
+
<EditTextPreference
android:key="http_password"
android:password="true"
@@ -42,9 +48,9 @@
android:title="@string/password" >
</EditTextPreference>
</PreferenceCategory>
- <PreferenceCategory
- android:key="category_look_and_feel"
- android:title="@string/look_and_feel" >
+
+ <PreferenceCategory android:key="category_look_and_feel" android:title="@string/look_and_feel" >
+
<ListPreference
android:defaultValue="THEME_DARK"
android:entries="@array/pref_theme_names"
@@ -52,6 +58,7 @@
android:key="theme"
android:summary="@string/pref_theme_long"
android:title="@string/pref_theme" />
+
<ListPreference
android:defaultValue="0"
android:entries="@array/pref_font_size_names"
@@ -63,50 +70,56 @@
android:defaultValue="false"
android:key="sort_feeds_by_unread"
android:title="@string/sort_feeds_by_unread" />
+
<CheckBoxPreference
android:defaultValue="false"
android:key="download_feed_icons"
android:title="@string/download_feed_icons" />
+
<CheckBoxPreference
android:defaultValue="false"
android:key="enable_cats"
android:title="@string/enable_cats" />
+
<CheckBoxPreference
android:defaultValue="false"
- android:dependency="enable_cats"
android:key="browse_cats_like_feeds"
+ android:dependency="enable_cats"
android:summary="@string/browse_cats_like_feeds_summary"
android:title="@string/browse_cats_like_feeds" />
- <CheckBoxPreference
+
+ <!-- <CheckBoxPreference
android:defaultValue="false"
android:key="combined_mode"
android:summary="@string/combined_mode_summary"
- android:title="@string/combined_mode" />
+ android:title="@string/combined_mode" /> -->
+
<CheckBoxPreference
android:defaultValue="true"
android:key="justify_article_text"
android:title="@string/justify_article_text" />
+
+ <ListPreference
+ android:defaultValue="HEADLINES"
+ android:entries="@array/pref_view_mode_names"
+ android:entryValues="@array/pref_view_mode_values"
+ android:key="default_view_mode"
+ android:summary="@string/pref_default_view_mode_long"
+ android:title="@string/pref_default_view_mode" />
+
</PreferenceCategory>
- <PreferenceCategory
- android:key="category_tablets"
- android:title="@string/prefs_for_tablets" >
- <CheckBoxPreference
- android:defaultValue="false"
- android:key="tablet_article_swipe"
- android:title="@string/tablet_article_swipe" />
- <CheckBoxPreference
- android:defaultValue="false"
- android:key="force_small_tablet_ui"
- android:title="@string/force_small_tablet_ui" />
- </PreferenceCategory>
+
<PreferenceCategory android:title="@string/offline_mode" >
+
<CheckBoxPreference
android:defaultValue="false"
android:key="offline_image_cache_enabled"
android:summary="@string/offline_image_cache_enabled_summary"
android:title="@string/offline_image_cache_enabled" />
</PreferenceCategory>
+
<PreferenceCategory android:title="@string/debugging" >
+
<CheckBoxPreference
android:defaultValue="false"
android:key="transport_debugging"
diff --git a/src/org/fox/ttrss/ApiRequest.java b/src/org/fox/ttrss/ApiRequest.java
index d067e394..57c4699c 100644
--- a/src/org/fox/ttrss/ApiRequest.java
+++ b/src/org/fox/ttrss/ApiRequest.java
@@ -4,29 +4,31 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.net.MalformedURLException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
import java.net.URL;
+import java.nio.CharBuffer;
+import java.security.cert.CertificateException;
import java.util.HashMap;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.protocol.ClientContext;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.protocol.HttpContext;
-import org.fox.ttrss.util.EasySSLSocketFactory;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.util.CharArrayBuffer;
+
+import java.security.cert.X509Certificate;
import android.content.Context;
import android.content.SharedPreferences;
-import android.net.http.AndroidHttpClient;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.AsyncTask;
+import android.os.Build;
import android.preference.PreferenceManager;
+import android.util.Base64;
import android.util.Log;
import com.google.gson.Gson;
@@ -38,7 +40,8 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
private final String TAG = this.getClass().getSimpleName();
public enum ApiError { NO_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND,
- HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, API_UNKNOWN, LOGIN_FAILED, INVALID_URL, INCORRECT_USAGE };
+ HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED,
+ API_UNKNOWN, LOGIN_FAILED, INVALID_URL, INCORRECT_USAGE, NETWORK_UNAVAILABLE };
public static final int API_STATUS_OK = 0;
public static final int API_STATUS_ERR = 1;
@@ -46,8 +49,10 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
private String m_api;
private boolean m_trustAny = false;
private boolean m_transportDebugging = false;
- protected int m_httpStatusCode = 0;
+ protected int m_responseCode = 0;
+ protected String m_responseMessage;
protected int m_apiStatusCode = 0;
+ protected boolean m_canUseProgress = false;
protected Context m_context;
private SharedPreferences m_prefs;
@@ -97,6 +102,8 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
return R.string.error_invalid_api_url;
case INCORRECT_USAGE:
return R.string.error_api_incorrect_usage;
+ case NETWORK_UNAVAILABLE:
+ return R.string.error_network_unavailable;
default:
Log.d(TAG, "getErrorMessage: unknown error code=" + m_lastError);
return R.string.error_unknown;
@@ -106,94 +113,94 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
@Override
protected JsonElement doInBackground(HashMap<String, String>... params) {
+ if (!isNetworkAvailable()) {
+ m_lastError = ApiError.NETWORK_UNAVAILABLE;
+ return null;
+ }
+
Gson gson = new Gson();
String requestStr = gson.toJson(new HashMap<String,String>(params[0]));
+ byte[] postData = null;
+
+ try {
+ postData = requestStr.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ m_lastError = ApiError.OTHER_ERROR;
+ e.printStackTrace();
+ return null;
+ }
+
+ disableConnectionReuseIfNecessary();
if (m_transportDebugging) Log.d(TAG, ">>> (" + requestStr + ") " + m_api);
- AndroidHttpClient client = AndroidHttpClient.newInstance("Tiny Tiny RSS");
+ if (m_trustAny) trustAllHosts();
+
+ URL url;
- if (m_trustAny) {
- client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", new EasySSLSocketFactory(), 443));
- }
-
try {
-
- HttpPost httpPost;
-
- try {
- httpPost = new HttpPost(m_api + "/api/");
- } catch (IllegalArgumentException e) {
- m_lastError = ApiError.INVALID_URL;
- e.printStackTrace();
- client.close();
- return null;
- } catch (Exception e) {
- m_lastError = ApiError.OTHER_ERROR;
- e.printStackTrace();
- client.close();
- return null;
- }
-
- HttpContext context = null;
-
+ url = new URL(m_api + "/api/");
+ } catch (Exception e) {
+ m_lastError = ApiError.INVALID_URL;
+ e.printStackTrace();
+ return null;
+ }
+
+ try {
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+
String httpLogin = m_prefs.getString("http_login", "").trim();
String httpPassword = m_prefs.getString("http_password", "").trim();
if (httpLogin.length() > 0) {
if (m_transportDebugging) Log.d(TAG, "Using HTTP Basic authentication.");
-
- URL targetUrl;
- try {
- targetUrl = new URL(m_api);
- } catch (MalformedURLException e) {
- m_lastError = ApiError.INVALID_URL;
- e.printStackTrace();
- client.close();
- return null;
- }
-
- HttpHost targetHost = new HttpHost(targetUrl.getHost(), targetUrl.getPort(), targetUrl.getProtocol());
- CredentialsProvider cp = new BasicCredentialsProvider();
- context = new BasicHttpContext();
- cp.setCredentials(
- new AuthScope(targetHost.getHostName(), targetHost.getPort()),
- new UsernamePasswordCredentials(httpLogin, httpPassword));
-
- context.setAttribute(ClientContext.CREDS_PROVIDER, cp);
+ conn.setRequestProperty("Authorization", "Basic " +
+ Base64.encode((httpLogin + ":" + httpPassword).getBytes("UTF-8"), Base64.NO_WRAP));
}
- httpPost.setEntity(new StringEntity(requestStr, "utf-8"));
- HttpResponse execute = client.execute(httpPost, context);
-
- m_httpStatusCode = execute.getStatusLine().getStatusCode();
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+ conn.setUseCaches(false);
+ conn.setRequestMethod("POST");
+ conn.setRequestProperty("Content-Length", Integer.toString(postData.length));
- switch (m_httpStatusCode) {
- case 200:
- InputStream content = execute.getEntity().getContent();
-
- BufferedReader buffer = new BufferedReader(
- new InputStreamReader(content), 8192);
-
- String s = "";
- String response = "";
-
- while ((s = buffer.readLine()) != null) {
- response += s;
+ OutputStream out = conn.getOutputStream();
+ out.write(postData);
+ out.close();
+
+ m_responseCode = conn.getResponseCode();
+ m_responseMessage = conn.getResponseMessage();
+
+ switch (m_responseCode) {
+ case HttpURLConnection.HTTP_OK:
+ StringBuffer response = new StringBuffer();
+ InputStreamReader in = new InputStreamReader(conn.getInputStream(), "UTF-8");
+ char[] buf = new char[256];
+ int read = 0;
+ int total = 0;
+
+ int contentLength = conn.getHeaderFieldInt("Api-Content-Length", -1);
+
+ m_canUseProgress = (contentLength != -1);
+
+ while ((read = in.read(buf)) >= 0) {
+ response.append(buf, 0, read);
+ total += read;
+ publishProgress(Integer.valueOf(total), Integer.valueOf(contentLength));
}
-
+
if (m_transportDebugging) Log.d(TAG, "<<< " + response);
JsonParser parser = new JsonParser();
- JsonElement result = parser.parse(response);
+ JsonElement result = parser.parse(response.toString());
JsonObject resultObj = result.getAsJsonObject();
m_apiStatusCode = resultObj.get("status").getAsInt();
- client.close();
+ conn.disconnect();
switch (m_apiStatusCode) {
case API_STATUS_OK:
@@ -217,25 +224,25 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
}
return null;
- case 401:
+ case HttpURLConnection.HTTP_UNAUTHORIZED:
m_lastError = ApiError.HTTP_UNAUTHORIZED;
break;
- case 403:
+ case HttpURLConnection.HTTP_FORBIDDEN:
m_lastError = ApiError.HTTP_FORBIDDEN;
break;
- case 404:
+ case HttpURLConnection.HTTP_NOT_FOUND:
m_lastError = ApiError.HTTP_NOT_FOUND;
break;
- case 500:
+ case HttpURLConnection.HTTP_INTERNAL_ERROR:
m_lastError = ApiError.HTTP_SERVER_ERROR;
break;
default:
m_lastError = ApiError.HTTP_OTHER_ERROR;
break;
}
-
- client.close();
- return null;
+
+ conn.disconnect();
+ return null;
} catch (javax.net.ssl.SSLPeerUnverifiedException e) {
m_lastError = ApiError.SSL_REJECTED;
e.printStackTrace();
@@ -250,7 +257,64 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
e.printStackTrace();
}
- client.close();
return null;
}
+
+ private static void trustAllHosts() {
+ X509TrustManager easyTrustManager = new X509TrustManager() {
+
+ public void checkClientTrusted(
+ X509Certificate[] chain,
+ String authType) throws CertificateException {
+ // Oh, I am easy!
+ }
+
+ public void checkServerTrusted(
+ X509Certificate[] chain,
+ String authType) throws CertificateException {
+ // Oh, I am easy!
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ };
+
+ // Create a trust manager that does not validate certificate chains
+ TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
+
+ // Install the all-trusting trust manager
+ try {
+ SSLContext sc = SSLContext.getInstance("TLS");
+
+ sc.init(null, trustAllCerts, new java.security.SecureRandom());
+
+ HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private static void disableConnectionReuseIfNecessary() {
+ // HTTP connection reuse which was buggy pre-froyo
+ if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
+ System.setProperty("http.keepAlive", "false");
+ }
+ }
+
+ protected boolean isNetworkAvailable() {
+ ConnectivityManager cm = (ConnectivityManager)
+ m_context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+
+ // if no network is available networkInfo will be null
+ // otherwise check if we are connected
+ if (networkInfo != null && networkInfo.isConnected()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/org/fox/ttrss/ArticleFragment.java b/src/org/fox/ttrss/ArticleFragment.java
index c3d83dd0..7df4349b 100644
--- a/src/org/fox/ttrss/ArticleFragment.java
+++ b/src/org/fox/ttrss/ArticleFragment.java
@@ -17,34 +17,38 @@ import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.text.Html;
import android.text.method.LinkMovementMethod;
+import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebView;
+import android.webkit.WebViewClient;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
public class ArticleFragment extends Fragment {
- @SuppressWarnings("unused")
private final String TAG = this.getClass().getSimpleName();
private SharedPreferences m_prefs;
private Article m_article;
- private OnlineServices m_onlineServices;
+ private OnlineActivity m_activity;
//private Article m_nextArticle;
//private Article m_prevArticle;
@@ -61,6 +65,28 @@ public class ArticleFragment extends Fragment {
private View.OnTouchListener m_gestureListener;
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ /* AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo(); */
+
+ switch (item.getItemId()) {
+ case R.id.article_link_share:
+ if (true) {
+ ((OnlineActivity) getActivity()).shareArticle(m_article);
+ }
+ return true;
+ case R.id.article_link_copy:
+ if (true) {
+ ((OnlineActivity) getActivity()).copyToClipboard(m_article.link);
+ }
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
@@ -75,6 +101,8 @@ public class ArticleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ m_activity.setProgressBarVisibility(true);
+
if (savedInstanceState != null) {
m_article = savedInstanceState.getParcelable("article");
}
@@ -94,14 +122,37 @@ public class ArticleFragment extends Fragment {
else
titleStr = m_article.title;
- title.setMovementMethod(LinkMovementMethod.getInstance());
- title.setText(Html.fromHtml("<a href=\""+m_article.link.trim().replace("\"", "\\\"")+"\">" + titleStr + "</a>"));
- registerForContextMenu(title);
+ title.setText(titleStr);
+ title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
+ title.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(m_article.link.trim()));
+ startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ m_activity.toast(R.string.error_other_error);
+ }
+ }
+ });
+
+ registerForContextMenu(title);
}
WebView web = (WebView)view.findViewById(R.id.content);
if (web != null) {
+ web.setWebChromeClient(new WebChromeClient() {
+ @Override
+ public void onProgressChanged(WebView view, int progress) {
+ m_activity.setProgress(Math.round(((float)progress / 100f) * 10000));
+ if (progress == 100) {
+ m_activity.setProgressBarVisibility(false);
+ }
+ }
+ });
String content;
String cssOverride = "";
@@ -186,9 +237,10 @@ public class ArticleFragment extends Fragment {
try {
URL url = new URL(a.content_url.trim());
+ String strUrl = url.toString().trim();
- if (a.content_type.indexOf("image") != -1) {
- content += "<br/><img src=\"" + url.toString().trim().replace("\"", "\\\"") + "\">";
+ if (a.content_type.indexOf("image") != -1 && !articleContent.contains(strUrl)) {
+ content += "<p><img src=\"" + strUrl.replace("\"", "\\\"") + "\"></p>";
}
spinnerArray.add(a);
@@ -226,11 +278,29 @@ public class ArticleFragment extends Fragment {
Attachment attachment = (Attachment) spinner.getSelectedItem();
if (attachment != null) {
- m_onlineServices.copyToClipboard(attachment.content_url);
+ m_activity.copyToClipboard(attachment.content_url);
}
}
});
+
+ Button attachmentsShare = (Button) view.findViewById(R.id.attachment_share);
+ if (!m_activity.isPortrait()) {
+ attachmentsShare.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Attachment attachment = (Attachment) spinner.getSelectedItem();
+
+ if (attachment != null) {
+ m_activity.shareText(attachment.content_url);
+ }
+ }
+ });
+ } else {
+ attachmentsShare.setVisibility(View.GONE);
+ }
+
} else {
view.findViewById(R.id.attachments_holder).setVisibility(View.GONE);
}
@@ -243,7 +313,7 @@ public class ArticleFragment extends Fragment {
e.printStackTrace();
}
- if (m_onlineServices.isSmallScreen())
+ if (m_activity.isSmallScreen())
web.setOnTouchListener(m_gestureListener);
}
@@ -296,7 +366,7 @@ public class ArticleFragment extends Fragment {
super.onAttach(activity);
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
- m_onlineServices = (OnlineServices)activity;
+ m_activity = (OnlineActivity)activity;
//m_article = m_onlineServices.getSelectedArticle();
}
diff --git a/src/org/fox/ttrss/ArticlePager.java b/src/org/fox/ttrss/ArticlePager.java
index 3027d61b..74c7255f 100644
--- a/src/org/fox/ttrss/ArticlePager.java
+++ b/src/org/fox/ttrss/ArticlePager.java
@@ -1,6 +1,11 @@
package org.fox.ttrss;
+import java.util.HashMap;
+
import org.fox.ttrss.types.Article;
+import org.fox.ttrss.types.ArticleList;
+import org.fox.ttrss.types.Feed;
+import org.fox.ttrss.util.HeadlinesRequest;
import android.app.Activity;
import android.os.Bundle;
@@ -8,17 +13,23 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import com.google.gson.JsonElement;
+
public class ArticlePager extends Fragment {
private final String TAG = "ArticlePager";
private PagerAdapter m_adapter;
- private OnlineServices m_onlineServices;
- private HeadlinesFragment m_hf;
+ private HeadlinesEventListener m_listener;
private Article m_article;
+ private ArticleList m_articles = GlobalState.getInstance().m_loadedArticles;
+ private OnlineActivity m_activity;
+ private String m_searchQuery = "";
+ private Feed m_feed;
private class PagerAdapter extends FragmentStatePagerAdapter {
@@ -28,7 +39,7 @@ public class ArticlePager extends Fragment {
@Override
public Fragment getItem(int position) {
- Article article = m_hf.getArticleAtPosition(position);
+ Article article = m_articles.get(position);
if (article != null) {
ArticleFragment af = new ArticleFragment(article);
@@ -39,7 +50,7 @@ public class ArticlePager extends Fragment {
@Override
public int getCount() {
- return m_hf.getAllArticles().size();
+ return m_articles.size();
}
}
@@ -48,21 +59,33 @@ public class ArticlePager extends Fragment {
super();
}
- public ArticlePager(Article article) {
+ public ArticlePager(Article article, Feed feed) {
super();
-
+
m_article = article;
+ m_feed = feed;
+ }
+
+ public void setSearchQuery(String searchQuery) {
+ m_searchQuery = searchQuery;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.article_pager, container, false);
+ if (savedInstanceState != null) {
+ m_article = savedInstanceState.getParcelable("article");
+ m_feed = savedInstanceState.getParcelable("feed");
+ }
+
m_adapter = new PagerAdapter(getActivity().getSupportFragmentManager());
ViewPager pager = (ViewPager) view.findViewById(R.id.article_pager);
- int position = m_hf.getArticlePosition(m_article);
+ int position = m_articles.indexOf(m_article);
+
+ m_listener.onArticleSelected(m_article, false);
pager.setAdapter(m_adapter);
pager.setCurrentItem(position);
@@ -78,20 +101,23 @@ public class ArticlePager extends Fragment {
@Override
public void onPageSelected(int position) {
- Article article = m_hf.getArticleAtPosition(position);
+ Article article = m_articles.get(position);
if (article != null) {
- if (article.unread) {
+ m_article = article;
+
+ /* if (article.unread) {
article.unread = false;
- m_onlineServices.saveArticleUnread(article);
- }
- m_onlineServices.setSelectedArticle(article);
+ m_activity.saveArticleUnread(article);
+ } */
+
+ m_listener.onArticleSelected(article, false);
//Log.d(TAG, "Page #" + position + "/" + m_adapter.getCount());
- if (position == m_adapter.getCount() - 5) {
- m_hf.refresh(true);
- m_adapter.notifyDataSetChanged();
+ if (m_activity.isSmallScreen() && position == m_adapter.getCount() - 5) {
+ Log.d(TAG, "loading more articles...");
+ refresh(true);
}
}
}
@@ -100,12 +126,134 @@ public class ArticlePager extends Fragment {
return view;
}
+ @SuppressWarnings({ "unchecked", "serial" })
+ private void refresh(boolean append) {
+ m_activity.setLoadingStatus(R.string.blank, true);
+
+ m_activity.setProgressBarVisibility(true);
+
+ if (!m_feed.equals(GlobalState.getInstance().m_activeFeed)) {
+ append = false;
+ }
+
+ HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext(), m_activity) {
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ m_activity.setProgress(progress[0] / progress[1] * 10000);
+ }
+
+ @Override
+ protected void onPostExecute(JsonElement result) {
+ m_activity.setProgressBarVisibility(false);
+
+ super.onPostExecute(result);
+
+ if (result != null) {
+ m_adapter.notifyDataSetChanged();
+
+ if (m_article.id == 0 || m_articles.indexOf(m_article) == -1) {
+ if (m_articles.size() > 0) {
+ m_article = m_articles.get(0);
+ m_listener.onArticleSelected(m_article, false);
+ }
+ }
+
+ } else {
+ if (m_lastError == ApiError.LOGIN_FAILED) {
+ m_activity.login();
+ } else {
+ m_activity.toast(getErrorMessage());
+ //setLoadingStatus(getErrorMessage(), false);
+ }
+ }
+ }
+ };
+
+ final Feed feed = m_feed;
+
+ final String sessionId = m_activity.getSessionId();
+ final boolean showUnread = m_activity.getUnreadArticlesOnly();
+ int skip = 0;
+
+ if (append) {
+ for (Article a : m_articles) {
+ if (a.unread) ++skip;
+ }
+
+ if (skip == 0) skip = m_articles.size();
+ }
+
+ final int fskip = skip;
+
+ req.setOffset(skip);
+
+ HashMap<String,String> map = new HashMap<String,String>() {
+ {
+ put("op", "getHeadlines");
+ put("sid", sessionId);
+ put("feed_id", String.valueOf(feed.id));
+ put("show_content", "true");
+ put("include_attachments", "true");
+ put("limit", String.valueOf(HeadlinesFragment.HEADLINES_REQUEST_SIZE));
+ put("offset", String.valueOf(0));
+ put("view_mode", showUnread ? "adaptive" : "all_articles");
+ put("skip", String.valueOf(fskip));
+ put("include_nested", "true");
+
+ if (feed.is_cat) put("is_cat", "true");
+
+ if (m_searchQuery != null && m_searchQuery.length() != 0) {
+ put("search", m_searchQuery);
+ put("search_mode", "");
+ put("match_on", "both");
+ }
+ }
+ };
+
+ req.execute(map);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putParcelable("article", m_article);
+ out.putParcelable("feed", m_feed);
+ }
+
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- m_hf = (HeadlinesFragment) getActivity().getSupportFragmentManager().findFragmentByTag(MainActivity.FRAG_HEADLINES);
- m_onlineServices = (OnlineServices)activity;
+ m_listener = (HeadlinesEventListener)activity;
+ m_activity = (OnlineActivity)activity;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (m_articles.size() == 0 || !m_feed.equals(GlobalState.getInstance().m_activeFeed)) {
+ refresh(false);
+ GlobalState.getInstance().m_activeFeed = m_feed;
+ }
+
+ m_activity.initMenu();
}
+ public Article getSelectedArticle() {
+ return m_article;
+ }
+
+ public void setActiveArticle(Article article) {
+ if (m_article != article) {
+ m_article = article;
+
+ int position = m_articles.indexOf(m_article);
+
+ ViewPager pager = (ViewPager) getView().findViewById(R.id.article_pager);
+
+ pager.setCurrentItem(position);
+ }
+ }
}
diff --git a/src/org/fox/ttrss/CommonActivity.java b/src/org/fox/ttrss/CommonActivity.java
index 3e254535..4b34ed62 100644
--- a/src/org/fox/ttrss/CommonActivity.java
+++ b/src/org/fox/ttrss/CommonActivity.java
@@ -2,15 +2,15 @@ package org.fox.ttrss;
import org.fox.ttrss.util.DatabaseHelper;
-import android.content.SharedPreferences;
+import android.annotation.SuppressLint;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
-import android.preference.PreferenceManager;
import android.support.v4.app.FragmentActivity;
import android.util.DisplayMetrics;
import android.util.FloatMath;
import android.util.Log;
import android.view.Display;
+import android.widget.TextView;
import android.widget.Toast;
public class CommonActivity extends FragmentActivity {
@@ -21,20 +21,43 @@ public class CommonActivity extends FragmentActivity {
public final static String FRAG_FEEDS = "feeds";
public final static String FRAG_CATS = "cats";
- private SharedPreferences m_prefs;
-
private SQLiteDatabase m_readableDb;
private SQLiteDatabase m_writableDb;
private boolean m_smallScreenMode = true;
private boolean m_compatMode = false;
- private boolean m_smallTablet = false;
protected void setSmallScreen(boolean smallScreen) {
Log.d(TAG, "m_smallScreenMode=" + smallScreen);
m_smallScreenMode = smallScreen;
}
+ public boolean getUnreadArticlesOnly() {
+ return GlobalState.getInstance().m_unreadArticlesOnly;
+ }
+
+ public boolean getUnreadOnly() {
+ return GlobalState.getInstance().m_unreadOnly;
+ }
+
+ public void setUnreadOnly(boolean unread) {
+ GlobalState.getInstance().m_unreadOnly = unread;
+ }
+
+ public void setUnreadArticlesOnly(boolean unread) {
+ GlobalState.getInstance().m_unreadArticlesOnly = unread;
+ }
+
+ public void setLoadingStatus(int status, boolean showProgress) {
+ TextView tv = (TextView) findViewById(R.id.loading_message);
+
+ if (tv != null) {
+ tv.setText(status);
+ }
+
+ setProgressBarIndeterminateVisibility(showProgress);
+ }
+
public void toast(int msgId) {
Toast toast = Toast.makeText(CommonActivity.this, msgId, Toast.LENGTH_SHORT);
toast.show();
@@ -45,23 +68,6 @@ public class CommonActivity extends FragmentActivity {
toast.show();
}
- protected void detectSmallTablet() {
-
- DisplayMetrics displayMetrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
-
- float inHeight = displayMetrics.heightPixels / displayMetrics.ydpi;
- float inWidth = displayMetrics.widthPixels / displayMetrics.xdpi;
-
- float inDiag = FloatMath.sqrt(inHeight * inHeight + inWidth * inWidth);
-
- if (inDiag < 9 || m_prefs.getBoolean("force_small_tablet_ui", false)) {
- m_smallTablet = true;
- }
-
- Log.d(TAG, "m_smallTabletMode=" + m_smallTablet + " " + inDiag);
- }
-
private void initDatabase() {
DatabaseHelper dh = new DatabaseHelper(getApplicationContext());
@@ -83,7 +89,6 @@ public class CommonActivity extends FragmentActivity {
m_readableDb.close();
m_writableDb.close();
-
}
@Override
@@ -92,13 +97,8 @@ public class CommonActivity extends FragmentActivity {
m_compatMode = android.os.Build.VERSION.SDK_INT <= 10;
- m_prefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
-
Log.d(TAG, "m_compatMode=" + m_compatMode);
- detectSmallTablet();
-
super.onCreate(savedInstanceState);
}
@@ -106,14 +106,11 @@ public class CommonActivity extends FragmentActivity {
return m_smallScreenMode;
}
- public boolean isSmallTablet() {
- return m_smallTablet;
- }
-
public boolean isCompatMode() {
return m_compatMode;
}
+ @SuppressWarnings("deprecation")
public boolean isPortrait() {
Display display = getWindowManager().getDefaultDisplay();
@@ -123,9 +120,10 @@ public class CommonActivity extends FragmentActivity {
return width < height;
}
+ @SuppressLint("NewApi")
+ @SuppressWarnings("deprecation")
public void copyToClipboard(String str) {
if (android.os.Build.VERSION.SDK_INT < 11) {
- @SuppressWarnings("deprecation")
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(str);
} else {
diff --git a/src/org/fox/ttrss/FeedCategoriesFragment.java b/src/org/fox/ttrss/FeedCategoriesFragment.java
index 055659ca..ac5c0046 100644
--- a/src/org/fox/ttrss/FeedCategoriesFragment.java
+++ b/src/org/fox/ttrss/FeedCategoriesFragment.java
@@ -7,6 +7,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
+import org.fox.ttrss.types.Feed;
import org.fox.ttrss.types.FeedCategory;
import org.fox.ttrss.types.FeedCategoryList;
@@ -17,11 +18,14 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
@@ -36,13 +40,12 @@ import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
public class FeedCategoriesFragment extends Fragment implements OnItemClickListener, OnSharedPreferenceChangeListener {
- @SuppressWarnings("unused")
private final String TAG = this.getClass().getSimpleName();
private SharedPreferences m_prefs;
private FeedCategoryListAdapter m_adapter;
private FeedCategoryList m_cats = new FeedCategoryList();
private FeedCategory m_selectedCat;
- private OnlineServices m_onlineServices;
+ private FeedsActivity m_activity;
class CatUnreadComparator implements Comparator<FeedCategory> {
@Override
@@ -83,10 +86,58 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
}
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+
+ switch (item.getItemId()) {
+ case R.id.browse_articles:
+ if (true) {
+ FeedCategory cat = getCategoryAtPosition(info.position);
+ if (cat != null) {
+ m_activity.openFeedArticles(new Feed(cat.id, cat.title, true));
+ //setSelectedCategory(cat);
+ }
+ }
+ return true;
+ case R.id.browse_headlines:
+ if (true) {
+ FeedCategory cat = getCategoryAtPosition(info.position);
+ if (cat != null) {
+ m_activity.onCatSelected(cat, true);
+ //setSelectedCategory(cat);
+ }
+ }
+ return true;
+ case R.id.browse_feeds:
+ if (true) {
+ FeedCategory cat = getCategoryAtPosition(info.position);
+ if (cat != null) {
+ m_activity.onCatSelected(cat, false);
+ //cf.setSelectedCategory(cat);
+ }
+ }
+ return true;
+ case R.id.catchup_category:
+ if (true) {
+ FeedCategory cat = getCategoryAtPosition(info.position);
+ if (cat != null) {
+ m_activity.catchupFeed(new Feed(cat.id, cat.title, true));
+ }
+ }
+ return true;
+
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- getActivity().getMenuInflater().inflate(R.menu.category_menu, menu);
+ m_activity.getMenuInflater().inflate(R.menu.category_menu, menu);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
FeedCategory cat = m_adapter.getItem(info.position);
@@ -117,11 +168,6 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
list.setOnItemClickListener(this);
registerForContextMenu(list);
- if (m_cats == null || m_cats.size() == 0)
- refresh(false);
- else
- getActivity().setProgressBarIndeterminateVisibility(false);
-
return view;
}
@@ -129,11 +175,20 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
public void onAttach(Activity activity) {
super.onAttach(activity);
- m_onlineServices = (OnlineServices)activity;
+ m_activity = (FeedsActivity)activity;
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
m_prefs.registerOnSharedPreferenceChangeListener(this);
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ refresh(false);
+ m_activity.initMenu();
}
@Override
@@ -153,31 +208,26 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
}
}
- if (getActivity() != null)
- getActivity().setProgressBarIndeterminateVisibility(showProgress);
+ m_activity.setProgressBarIndeterminateVisibility(showProgress);
}
@SuppressWarnings("unchecked")
public void refresh(boolean background) {
CatsRequest req = new CatsRequest(getActivity().getApplicationContext());
- final String sessionId = m_onlineServices.getSessionId();
- final boolean unreadOnly = m_onlineServices.getUnreadOnly();
+ final String sessionId = m_activity.getSessionId();
+ final boolean unreadOnly = m_activity.getUnreadOnly();
if (sessionId != null) {
-
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- setLoadingStatus(R.string.blank, true);
- }
- });
+ setLoadingStatus(R.string.blank, true);
+ m_activity.setProgressBarVisibility(true);
@SuppressWarnings("serial")
HashMap<String,String> map = new HashMap<String,String>() {
{
put("op", "getCategories");
put("sid", sessionId);
+ put("enable_nested", "true");
if (unreadOnly) {
put("unread_only", String.valueOf(unreadOnly));
}
@@ -185,7 +235,6 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
};
req.execute(map);
-
}
}
@@ -195,7 +244,15 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
super(context);
}
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ m_activity.setProgress(Math.round((((float)progress[0] / (float)progress[1]) * 10000)));
+ }
+
+ @Override
protected void onPostExecute(JsonElement result) {
+ m_activity.setProgressBarVisibility(false);
+
if (result != null) {
try {
JsonArray content = result.getAsJsonArray();
@@ -205,7 +262,7 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
m_cats.clear();
- int apiLevel = m_onlineServices.getApiLevel();
+ int apiLevel = m_activity.getApiLevel();
// virtual cats implemented in getCategories since api level 1
if (apiLevel == 0) {
@@ -233,7 +290,7 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
}
if (m_lastError == ApiError.LOGIN_FAILED) {
- m_onlineServices.login();
+ m_activity.login();
} else {
setLoadingStatus(getErrorMessage(), false);
}
@@ -247,7 +304,7 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
if (m_prefs.getBoolean("sort_feeds_by_unread", false)) {
cmp = new CatUnreadComparator();
} else {
- if (m_onlineServices.getApiLevel() >= 3) {
+ if (m_activity.getApiLevel() >= 3) {
cmp = new CatOrderComparator();
} else {
cmp = new CatTitleComparator();
@@ -280,7 +337,7 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
public int getItemViewType(int position) {
FeedCategory cat = items.get(position);
- if (!m_onlineServices.isSmallScreen() && m_selectedCat != null && cat.id == m_selectedCat.id) {
+ if (!m_activity.isSmallScreen() && m_selectedCat != null && cat.id == m_selectedCat.id) {
return VIEW_SELECTED;
} else {
return VIEW_NORMAL;
@@ -344,9 +401,17 @@ public class FeedCategoriesFragment extends Fragment implements OnItemClickListe
if (list != null) {
FeedCategory cat = (FeedCategory)list.getItemAtPosition(position);
- m_onlineServices.onCatSelected(cat);
- if (!m_onlineServices.isSmallScreen())
+ if ("ARTICLES".equals(m_prefs.getString("default_view_mode", "HEADLINES")) &&
+ m_prefs.getBoolean("browse_cats_like_feeds", false)) {
+
+ m_activity.openFeedArticles(new Feed(cat.id, cat.title, true));
+
+ } else {
+ m_activity.onCatSelected(cat);
+ }
+
+ if (!m_activity.isSmallScreen())
m_selectedCat = cat;
m_adapter.notifyDataSetChanged();
diff --git a/src/org/fox/ttrss/FeedsActivity.java b/src/org/fox/ttrss/FeedsActivity.java
new file mode 100644
index 00000000..e1126fe5
--- /dev/null
+++ b/src/org/fox/ttrss/FeedsActivity.java
@@ -0,0 +1,300 @@
+package org.fox.ttrss;
+
+import java.util.Date;
+
+import org.fox.ttrss.types.Article;
+import org.fox.ttrss.types.ArticleList;
+import org.fox.ttrss.types.Feed;
+import org.fox.ttrss.types.FeedCategory;
+import org.fox.ttrss.util.AppRater;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+
+public class FeedsActivity extends OnlineActivity implements HeadlinesEventListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ protected SharedPreferences m_prefs;
+ protected long m_lastRefresh = 0;
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ m_prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
+ setTheme(R.style.DarkTheme);
+ } else {
+ setTheme(R.style.LightTheme);
+ }
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.feeds);
+
+ setSmallScreen(findViewById(R.id.headlines_fragment) == null);
+
+ Intent intent = getIntent();
+
+ if (savedInstanceState == null) {
+
+ if (intent.getParcelableExtra("feed") != null || intent.getParcelableExtra("category") != null ||
+ intent.getParcelableExtra("article") != null) {
+
+ if (!isCompatMode()) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ Feed feed = (Feed) intent.getParcelableExtra("feed");
+ FeedCategory cat = (FeedCategory) intent.getParcelableExtra("category");
+ Article article = (Article) intent.getParcelableExtra("article");
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ if (article != null) {
+ Article original = GlobalState.getInstance().m_loadedArticles.findById(article.id);
+
+ ArticlePager ap = new ArticlePager(original != null ? original : article, feed);
+ ft.replace(R.id.feeds_fragment, ap, FRAG_ARTICLE);
+
+ ap.setSearchQuery(intent.getStringExtra("searchQuery"));
+
+ setTitle(feed.title);
+ } else {
+ if (feed != null) {
+ HeadlinesFragment hf = new HeadlinesFragment(feed);
+ ft.replace(R.id.feeds_fragment, hf, FRAG_HEADLINES);
+
+ setTitle(feed.title);
+ }
+
+ if (cat != null) {
+ FeedsFragment ff = new FeedsFragment(cat);
+ ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS);
+
+ setTitle(cat.title);
+ }
+ }
+
+ ft.commit();
+
+ } else {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ if (m_prefs.getBoolean("enable_cats", false)) {
+ ft.replace(R.id.feeds_fragment, new FeedCategoriesFragment(), FRAG_CATS);
+ } else {
+ ft.replace(R.id.feeds_fragment, new FeedsFragment(), FRAG_FEEDS);
+ }
+
+ ft.commit();
+
+ AppRater.appLaunched(this);
+ }
+ }
+ }
+
+ @Override
+ protected void initMenu() {
+ super.initMenu();
+
+ if (m_menu != null && getSessionId() != null) {
+ Fragment ff = getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
+ Fragment cf = getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
+ ArticlePager af = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+ HeadlinesFragment hf = (HeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ m_menu.setGroupVisible(R.id.menu_group_feeds, (ff != null && ff.isAdded()) || (cf != null && cf.isAdded()));
+
+ m_menu.setGroupVisible(R.id.menu_group_article, af != null && af.isAdded());
+
+ m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded() && hf.getSelectedArticles().size() == 0);
+ m_menu.setGroupVisible(R.id.menu_group_headlines_selection, hf != null && hf.isAdded() && hf.getSelectedArticles().size() != 0);
+
+ if (isSmallScreen()) {
+ m_menu.findItem(R.id.update_headlines).setVisible(hf != null && hf.isAdded());
+ } else {
+ m_menu.findItem(R.id.update_headlines).setVisible(false);
+ }
+
+ MenuItem item = m_menu.findItem(R.id.show_feeds);
+
+ if (getUnreadOnly()) {
+ item.setTitle(R.string.menu_all_feeds);
+ } else {
+ item.setTitle(R.string.menu_unread_feeds);
+ }
+ }
+ }
+
+ public void onFeedSelected(Feed feed) {
+ GlobalState.getInstance().m_loadedArticles.clear();
+
+ if (isSmallScreen()) {
+ Intent intent = new Intent(FeedsActivity.this, FeedsActivity.class);
+ intent.putExtra("feed", feed);
+
+ startActivityForResult(intent, 0);
+ } else {
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ HeadlinesFragment hf = new HeadlinesFragment(feed);
+ ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
+
+ ft.commit();
+
+ Date date = new Date();
+
+ if (date.getTime() - m_lastRefresh > 10000) {
+ m_lastRefresh = date.getTime();
+ refresh(false);
+ }
+ }
+ }
+
+ public void onCatSelected(FeedCategory cat, boolean openAsFeed) {
+
+ if (!openAsFeed) {
+
+ if (isSmallScreen()) {
+
+ Intent intent = new Intent(FeedsActivity.this, FeedsActivity.class);
+ intent.putExtra("category", cat);
+
+ startActivityForResult(intent, 0);
+
+ } else {
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ FeedsFragment ff = new FeedsFragment(cat);
+ ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS);
+
+ ft.addToBackStack(null);
+ ft.commit();
+ }
+ } else {
+ Feed feed = new Feed(cat.id, cat.title, true);
+ onFeedSelected(feed);
+ }
+ }
+
+ public void onCatSelected(FeedCategory cat) {
+ onCatSelected(cat, m_prefs.getBoolean("browse_cats_like_feeds", false));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.show_feeds:
+ setUnreadOnly(!getUnreadOnly());
+ initMenu();
+ refresh();
+ return true;
+ case R.id.update_feeds:
+ refresh();
+ return true;
+ default:
+ Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ protected void loginSuccess() {
+ setLoadingStatus(R.string.blank, false);
+ findViewById(R.id.loading_container).setVisibility(View.GONE);
+ initMenu();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ initMenu();
+ }
+
+ @Override
+ public void onArticleListSelectionChange(ArticleList m_selectedArticles) {
+ initMenu();
+ }
+
+ public void openFeedArticles(Feed feed) {
+ if (isSmallScreen()) {
+ Intent intent = new Intent(FeedsActivity.this, FeedsActivity.class);
+
+ GlobalState.getInstance().m_activeFeed = feed;
+ GlobalState.getInstance().m_loadedArticles.clear();
+
+ intent.putExtra("feed", feed);
+ intent.putExtra("article", new Article());
+ startActivityForResult(intent, 0);
+
+ } else {
+
+ }
+ }
+
+ public void onArticleSelected(Article article, boolean open) {
+ if (article.unread) {
+ article.unread = false;
+ saveArticleUnread(article);
+ }
+
+ if (open) {
+ HeadlinesFragment hf = (HeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ if (isSmallScreen()) {
+
+ Intent intent = new Intent(FeedsActivity.this, FeedsActivity.class);
+ intent.putExtra("feed", hf.getFeed());
+ intent.putExtra("article", article);
+ intent.putExtra("searchQuery", hf.getSearchQuery());
+
+ startActivityForResult(intent, 0);
+
+
+ } else {
+ Intent intent = new Intent(FeedsActivity.this, HeadlinesActivity.class);
+ intent.putExtra("feed", hf.getFeed());
+ intent.putExtra("article", article);
+ intent.putExtra("searchQuery", hf.getSearchQuery());
+
+ startActivityForResult(intent, 0);
+ }
+ } else {
+ initMenu();
+ }
+ }
+
+ @Override
+ public void onArticleSelected(Article article) {
+ onArticleSelected(article, true);
+ }
+
+ public void catchupFeed(final Feed feed) {
+ super.catchupFeed(feed);
+ refresh();
+ }
+
+ @Override
+ public void onHeadlinesLoaded(boolean appended) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/src/org/fox/ttrss/FeedsFragment.java b/src/org/fox/ttrss/FeedsFragment.java
index 030005fe..a8284c76 100644
--- a/src/org/fox/ttrss/FeedsFragment.java
+++ b/src/org/fox/ttrss/FeedsFragment.java
@@ -5,29 +5,25 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Type;
-import java.net.MalformedURLException;
+import java.net.HttpURLConnection;
import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpResponse;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.protocol.ClientContext;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.protocol.HttpContext;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.fox.ttrss.types.Article;
import org.fox.ttrss.types.Feed;
import org.fox.ttrss.types.FeedCategory;
import org.fox.ttrss.types.FeedList;
-import org.fox.ttrss.util.EasySSLSocketFactory;
import android.app.Activity;
import android.content.Context;
@@ -37,14 +33,17 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
+import android.util.Base64;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
@@ -65,7 +64,7 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
private SharedPreferences m_prefs;
private FeedListAdapter m_adapter;
private FeedList m_feeds = new FeedList();
- private OnlineServices m_onlineServices;
+ private FeedsActivity m_activity;
private Feed m_selectedFeed;
private FeedCategory m_activeCategory;
private static final String ICON_PATH = "/data/org.fox.ttrss/icons/";
@@ -98,10 +97,16 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
@Override
public int compare(Feed a, Feed b) {
- if (a.id >= 0 && b.id >= 0)
+ if (a.is_cat && b.is_cat)
+ return a.title.compareTo(b.title);
+ else if (a.is_cat && !b.is_cat)
+ return -1;
+ else if (!a.is_cat && b.is_cat)
+ return 1;
+ else if (a.id >= 0 && b.id >= 0)
return a.title.compareTo(b.title);
else
- return a.id - b.id;
+ return a.id - b.id;
}
}
@@ -109,9 +114,15 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
class FeedOrderComparator implements Comparator<Feed> {
@Override
- public int compare(Feed a, Feed b) {
+ public int compare(Feed a, Feed b) {
if (a.id >= 0 && b.id >= 0)
- if (a.order_id != 0 && b.order_id != 0)
+ if (a.is_cat && b.is_cat)
+ return a.title.compareTo(b.title);
+ else if (a.is_cat && !b.is_cat)
+ return -1;
+ else if (!a.is_cat && b.is_cat)
+ return 1;
+ else if (a.order_id != 0 && b.order_id != 0)
return a.order_id - b.order_id;
else
return a.title.compareTo(b.title);
@@ -120,6 +131,42 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
}
}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+ switch (item.getItemId()) {
+ case R.id.browse_articles:
+ if (true) {
+ Feed feed = getFeedAtPosition(info.position);
+ if (feed != null) {
+ m_activity.openFeedArticles(feed);
+ }
+ }
+ return true;
+ case R.id.browse_headlines:
+ if (true) {
+ Feed feed = getFeedAtPosition(info.position);
+ if (feed != null) {
+ m_activity.onFeedSelected(feed);
+ }
+ }
+ return true;
+ case R.id.catchup_feed:
+ if (true) {
+ Feed feed = getFeedAtPosition(info.position);
+ if (feed != null) {
+ m_activity.catchupFeed(feed);
+ }
+ }
+ return true;
+
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
@@ -133,6 +180,11 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
if (feed != null)
menu.setHeaderTitle(feed.title);
+ if (!m_activity.isSmallScreen()) {
+ menu.findItem(R.id.browse_headlines).setVisible(false);
+ menu.findItem(R.id.browse_feeds).setVisible(false);
+ }
+
super.onCreateContextMenu(menu, v, menuInfo);
}
@@ -159,11 +211,6 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
m_enableFeedIcons = m_prefs.getBoolean("download_feed_icons", false);
- if (m_feeds == null || m_feeds.size() == 0)
- refresh(false);
- else
- getActivity().setProgressBarIndeterminateVisibility(false);
-
return view;
}
@@ -179,12 +226,19 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
m_prefs.registerOnSharedPreferenceChangeListener(this);
- m_onlineServices = (OnlineServices)activity;
-
- //m_selectedFeed = m_onlineServices.getActiveFeed();
+ m_activity = (FeedsActivity)activity;
}
@Override
+ public void onResume() {
+ super.onResume();
+
+ refresh(false);
+
+ m_activity.initMenu();
+ }
+
+ @Override
public void onSaveInstanceState (Bundle out) {
super.onSaveInstanceState(out);
@@ -200,9 +254,22 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
if (list != null) {
Feed feed = (Feed)list.getItemAtPosition(position);
- m_onlineServices.onFeedSelected(feed);
- if (!m_onlineServices.isSmallScreen())
+ if (feed.is_cat) {
+ FeedCategory cat = new FeedCategory();
+ cat.id = feed.id;
+ cat.title = feed.title;
+
+ m_activity.onCatSelected(cat);
+ } else {
+ if ("ARTICLES".equals(m_prefs.getString("default_view_mode", "HEADLINES"))) {
+ m_activity.openFeedArticles(feed);
+ } else {
+ m_activity.onFeedSelected(feed);
+ }
+ }
+
+ if (!m_activity.isSmallScreen())
m_selectedFeed = feed;
m_adapter.notifyDataSetChanged();
@@ -213,26 +280,23 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
public void refresh(boolean background) {
//FeedCategory cat = m_onlineServices.getActiveCategory();
+ m_activity.setProgressBarVisibility(true);
+
final int catId = (m_activeCategory != null) ? m_activeCategory.id : -4;
- final String sessionId = m_onlineServices.getSessionId();
- final boolean unreadOnly = m_onlineServices.getUnreadOnly();
+ final String sessionId = m_activity.getSessionId();
+ final boolean unreadOnly = m_activity.getUnreadOnly();
FeedsRequest req = new FeedsRequest(getActivity().getApplicationContext(), catId);
if (sessionId != null) {
-
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- setLoadingStatus(R.string.blank, true);
- }
- });
+ setLoadingStatus(R.string.blank, true);
HashMap<String,String> map = new HashMap<String,String>() {
{
put("op", "getFeeds");
put("sid", sessionId);
+ put("include_nested", "true");
put("cat_id", String.valueOf(catId));
if (unreadOnly) {
put("unread_only", String.valueOf(unreadOnly));
@@ -292,7 +356,7 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
}
};
- final String sessionId = m_onlineServices.getSessionId();
+ final String sessionId = m_activity.getSessionId();
HashMap<String,String> map = new HashMap<String,String>() {
{
@@ -312,7 +376,15 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
m_catId = catId;
}
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ m_activity.setProgress(Math.round((((float)progress[0] / (float)progress[1]) * 10000)));
+ }
+
+ @Override
protected void onPostExecute(JsonElement result) {
+ m_activity.setProgressBarVisibility(false);
+
if (result != null) {
try {
JsonArray content = result.getAsJsonArray();
@@ -347,7 +419,7 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
}
if (m_lastError == ApiError.LOGIN_FAILED) {
- m_onlineServices.login();
+ m_activity.login();
} else {
setLoadingStatus(getErrorMessage(), false);
}
@@ -375,7 +447,7 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
public int getItemViewType(int position) {
Feed feed = items.get(position);
- if (!m_onlineServices.isSmallScreen() && m_selectedFeed != null && feed.id == m_selectedFeed.id) {
+ if (!m_activity.isSmallScreen() && m_selectedFeed != null && feed.id == m_selectedFeed.id) {
return VIEW_SELECTED;
} else {
return VIEW_NORMAL;
@@ -449,7 +521,7 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
if (m_prefs.getBoolean("sort_feeds_by_unread", false)) {
cmp = new FeedUnreadComparator();
} else {
- if (m_onlineServices.getApiLevel() >= 3) {
+ if (m_activity.getApiLevel() >= 3) {
cmp = new FeedOrderComparator();
} else {
cmp = new FeedTitleComparator();
@@ -478,7 +550,7 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
if (iconPath.exists()) {
for (Feed feed : params[0]) {
- if (feed.id > 0 && feed.has_icon) {
+ if (feed.id > 0 && feed.has_icon && !feed.is_cat) {
File outputFile = new File(iconPath.getAbsolutePath() + "/" + feed.id + ".ico");
String fetchUrl = m_baseUrl + "/" + feed.id + ".ico";
@@ -496,46 +568,73 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
return null;
}
+ private void trustAllHosts() {
+ X509TrustManager easyTrustManager = new X509TrustManager() {
+
+ public void checkClientTrusted(
+ X509Certificate[] chain,
+ String authType) throws CertificateException {
+ // Oh, I am easy!
+ }
+
+ public void checkServerTrusted(
+ X509Certificate[] chain,
+ String authType) throws CertificateException {
+ // Oh, I am easy!
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ };
+
+ // Create a trust manager that does not validate certificate chains
+ TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager};
+
+ // Install the all-trusting trust manager
+ try {
+ SSLContext sc = SSLContext.getInstance("TLS");
+
+ sc.init(null, trustAllCerts, new java.security.SecureRandom());
+
+ HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void disableConnectionReuseIfNecessary() {
+ // HTTP connection reuse which was buggy pre-froyo
+ if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
+ System.setProperty("http.keepAlive", "false");
+ }
+ }
+
protected void downloadFile(String fetchUrl, String outputFile) {
AndroidHttpClient client = AndroidHttpClient.newInstance("Tiny Tiny RSS");
+ disableConnectionReuseIfNecessary();
+
if (m_prefs.getBoolean("ssl_trust_any", false)) {
- client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", new EasySSLSocketFactory(), 443));
+ trustAllHosts();
}
- HttpGet httpGet = new HttpGet(fetchUrl);
- HttpContext context = null;
+ try {
+ URL url = new URL(fetchUrl);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- String httpLogin = m_prefs.getString("http_login", "");
- String httpPassword = m_prefs.getString("http_password", "");
-
- if (httpLogin.length() > 0) {
-
- URL targetUrl;
- try {
- targetUrl = new URL(fetchUrl);
- } catch (MalformedURLException e) {
- e.printStackTrace();
- client.close();
- return;
- }
-
- HttpHost targetHost = new HttpHost(targetUrl.getHost(), targetUrl.getPort(), targetUrl.getProtocol());
- CredentialsProvider cp = new BasicCredentialsProvider();
- context = new BasicHttpContext();
+ String httpLogin = m_prefs.getString("http_login", "");
+ String httpPassword = m_prefs.getString("http_password", "");
- cp.setCredentials(
- new AuthScope(targetHost.getHostName(), targetHost.getPort()),
- new UsernamePasswordCredentials(httpLogin, httpPassword));
-
- context.setAttribute(ClientContext.CREDS_PROVIDER, cp);
- }
-
+ if (httpLogin.length() > 0) {
+ conn.setRequestProperty("Authorization", "Basic " +
+ Base64.encode((httpLogin + ":" + httpPassword).getBytes("UTF-8"), Base64.NO_WRAP));
+ }
- try {
- HttpResponse execute = client.execute(httpGet, context);
-
- InputStream content = execute.getEntity().getContent();
+ InputStream content = conn.getInputStream();
BufferedInputStream is = new BufferedInputStream(content, 1024);
FileOutputStream fos = new FileOutputStream(outputFile);
diff --git a/src/org/fox/ttrss/GlobalState.java b/src/org/fox/ttrss/GlobalState.java
new file mode 100644
index 00000000..ba13e776
--- /dev/null
+++ b/src/org/fox/ttrss/GlobalState.java
@@ -0,0 +1,31 @@
+package org.fox.ttrss;
+
+import org.fox.ttrss.types.Article;
+import org.fox.ttrss.types.ArticleList;
+import org.fox.ttrss.types.Feed;
+
+import android.app.Application;
+
+public class GlobalState extends Application {
+ private static GlobalState m_singleton;
+
+ public ArticleList m_loadedArticles = new ArticleList();
+ public Feed m_activeFeed;
+ public Article m_activeArticle;
+ public int m_selectedArticleId;
+ public boolean m_unreadOnly = true;
+ public boolean m_unreadArticlesOnly = true;
+ public String m_sessionId;
+ public int m_apiLevel;
+ public boolean m_canUseProgress;
+
+ public static GlobalState getInstance(){
+ return m_singleton;
+ }
+
+ @Override
+ public final void onCreate() {
+ super.onCreate();
+ m_singleton = this;
+ }
+}
diff --git a/src/org/fox/ttrss/HeadlinesActivity.java b/src/org/fox/ttrss/HeadlinesActivity.java
new file mode 100644
index 00000000..6fad6c66
--- /dev/null
+++ b/src/org/fox/ttrss/HeadlinesActivity.java
@@ -0,0 +1,187 @@
+package org.fox.ttrss;
+
+import org.fox.ttrss.types.Article;
+import org.fox.ttrss.types.ArticleList;
+import org.fox.ttrss.types.Feed;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+
+public class HeadlinesActivity extends OnlineActivity implements HeadlinesEventListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ protected SharedPreferences m_prefs;
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ m_prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
+ setTheme(R.style.DarkTheme);
+ } else {
+ setTheme(R.style.LightTheme);
+ }
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.headlines);
+
+ if (!isCompatMode()) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ setSmallScreen(findViewById(R.id.headlines_fragment) == null);
+
+ if (savedInstanceState == null) {
+ Intent i = getIntent();
+
+ if (i.getExtras() != null) {
+ Feed feed = i.getParcelableExtra("feed");
+ Article article = i.getParcelableExtra("article");
+ String searchQuery = i.getStringExtra("searchQuery");
+
+ HeadlinesFragment hf = new HeadlinesFragment(feed, article);
+ ArticlePager af = new ArticlePager(hf.getArticleById(article.id), feed);
+
+ hf.setSearchQuery(searchQuery);
+ af.setSearchQuery(searchQuery);
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
+ ft.replace(R.id.article_fragment, af, FRAG_ARTICLE);
+
+ ft.commit();
+
+ setTitle(feed.title);
+ }
+ }
+ }
+
+ @Override
+ protected void refresh() {
+ super.refresh();
+
+
+ }
+
+ @Override
+ protected void loginSuccess() {
+ Log.d(TAG, "loginSuccess");
+
+ setLoadingStatus(R.string.blank, false);
+ findViewById(R.id.loading_container).setVisibility(View.GONE);
+
+ initMenu();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ default:
+ Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void initMenu() {
+ super.initMenu();
+
+ if (m_menu != null && getSessionId() != null) {
+ m_menu.setGroupVisible(R.id.menu_group_feeds, false);
+
+ HeadlinesFragment hf = (HeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.getSelectedArticles().size() == 0);
+ m_menu.setGroupVisible(R.id.menu_group_headlines_selection, hf != null && hf.getSelectedArticles().size() != 0);
+
+ Fragment af = getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+
+ m_menu.setGroupVisible(R.id.menu_group_article, af != null);
+
+ m_menu.findItem(R.id.search).setVisible(false);
+ }
+ }
+
+ @Override
+ public void onArticleListSelectionChange(ArticleList m_selectedArticles) {
+ initMenu();
+ }
+
+ @Override
+ public void onArticleSelected(Article article) {
+ onArticleSelected(article, true);
+ }
+
+ @Override
+ public void onArticleSelected(Article article, boolean open) {
+
+ if (article.unread) {
+ article.unread = false;
+ saveArticleUnread(article);
+ }
+
+ if (open) {
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ ArticlePager af = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+ af.setActiveArticle(article);
+
+ ft.commit();
+ } else {
+ HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+ hf.setActiveArticle(article);
+ }
+
+ GlobalState.getInstance().m_activeArticle = article;
+
+ initMenu();
+
+ }
+
+ @Override
+ public void onHeadlinesLoaded(boolean appended) {
+ HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ if (hf != null) {
+ Article article = hf.getActiveArticle();
+
+ if (article == null) {
+ article = hf.getAllArticles().get(0);
+
+ hf.setActiveArticle(article);
+
+ ArticlePager af = new ArticlePager(article, hf.getFeed());
+
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ ft.replace(R.id.article_fragment, af, FRAG_ARTICLE);
+
+ ft.commit();
+ }
+ }
+ }
+}
diff --git a/src/org/fox/ttrss/HeadlinesEventListener.java b/src/org/fox/ttrss/HeadlinesEventListener.java
new file mode 100644
index 00000000..08806c8b
--- /dev/null
+++ b/src/org/fox/ttrss/HeadlinesEventListener.java
@@ -0,0 +1,11 @@
+package org.fox.ttrss;
+
+import org.fox.ttrss.types.Article;
+import org.fox.ttrss.types.ArticleList;
+
+public interface HeadlinesEventListener {
+ void onArticleListSelectionChange(ArticleList m_selectedArticles);
+ void onArticleSelected(Article article);
+ void onArticleSelected(Article article, boolean open);
+ void onHeadlinesLoaded(boolean appended);
+}
diff --git a/src/org/fox/ttrss/HeadlinesFragment.java b/src/org/fox/ttrss/HeadlinesFragment.java
index 4cb77858..f2148003 100644
--- a/src/org/fox/ttrss/HeadlinesFragment.java
+++ b/src/org/fox/ttrss/HeadlinesFragment.java
@@ -1,6 +1,5 @@
package org.fox.ttrss;
-import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
@@ -8,13 +7,13 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
-import java.util.List;
import java.util.TimeZone;
import org.fox.ttrss.types.Article;
import org.fox.ttrss.types.ArticleList;
import org.fox.ttrss.types.Attachment;
import org.fox.ttrss.types.Feed;
+import org.fox.ttrss.util.HeadlinesRequest;
import org.jsoup.Jsoup;
import android.app.Activity;
@@ -34,6 +33,7 @@ import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -51,10 +51,7 @@ import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
-import com.google.gson.reflect.TypeToken;
public class HeadlinesFragment extends Fragment implements OnItemClickListener, OnScrollListener {
public static enum ArticlesSelection { ALL, NONE, UNREAD };
@@ -66,28 +63,27 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
private Feed m_feed;
private Article m_activeArticle;
- private boolean m_refreshInProgress = false;
- private boolean m_canLoadMore = false;
private boolean m_combinedMode = true;
private String m_searchQuery = "";
+ private boolean m_refreshInProgress = false;
private SharedPreferences m_prefs;
private ArticleListAdapter m_adapter;
- private ArticleList m_articles = new ArticleList();
+ private ArticleList m_articles = GlobalState.getInstance().m_loadedArticles;
private ArticleList m_selectedArticles = new ArticleList();
-
- private OnlineServices m_onlineServices;
+ private HeadlinesEventListener m_listener;
+ private OnlineActivity m_activity;
private ImageGetter m_dummyGetter = new ImageGetter() {
+ @SuppressWarnings("deprecation")
@Override
public Drawable getDrawable(String source) {
return new BitmapDrawable();
}
};
-
public ArticleList getSelectedArticles() {
return m_selectedArticles;
}
@@ -95,16 +91,152 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
public HeadlinesFragment(Feed feed) {
m_feed = feed;
}
-
+
+ public HeadlinesFragment(Feed feed, Article activeArticle) {
+ m_feed = feed;
+ m_activeArticle = getArticleById(activeArticle.id);
+ }
+
public HeadlinesFragment() {
//
}
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+
+ switch (item.getItemId()) {
+ case R.id.set_labels:
+ if (true) {
+ Article article = getArticleAtPosition(info.position);
+
+ if (article != null) {
+ m_activity.editArticleLabels(article);
+ }
+ }
+ return true;
+ case R.id.article_set_note:
+ if (true) {
+ Article article = getArticleAtPosition(info.position);
+
+ if (article != null) {
+ m_activity.editArticleNote(article);
+ }
+ }
+ return true;
+
+ case R.id.article_link_copy:
+ if (true) {
+ Article article = getArticleAtPosition(info.position);
+
+ if (article != null) {
+ m_activity.copyToClipboard(article.link);
+ }
+ }
+ return true;
+ case R.id.selection_toggle_marked:
+ if (true) {
+ ArticleList selected = getSelectedArticles();
+
+ if (selected.size() > 0) {
+ for (Article a : selected)
+ a.marked = !a.marked;
+
+ m_activity.toggleArticlesMarked(selected);
+ //updateHeadlines();
+ } else {
+ Article article = getArticleAtPosition(info.position);
+ if (article != null) {
+ article.marked = !article.marked;
+ m_activity.saveArticleMarked(article);
+ //updateHeadlines();
+ }
+ }
+ m_adapter.notifyDataSetChanged();
+ }
+ return true;
+ case R.id.selection_toggle_published:
+ if (true) {
+ ArticleList selected = getSelectedArticles();
+
+ if (selected.size() > 0) {
+ for (Article a : selected)
+ a.published = !a.published;
+
+ m_activity.toggleArticlesPublished(selected);
+ //updateHeadlines();
+ } else {
+ Article article = getArticleAtPosition(info.position);
+ if (article != null) {
+ article.published = !article.published;
+ m_activity.saveArticlePublished(article);
+ //updateHeadlines();
+ }
+ }
+ m_adapter.notifyDataSetChanged();
+ }
+ return true;
+ case R.id.selection_toggle_unread:
+ if (true) {
+ ArticleList selected = getSelectedArticles();
+
+ if (selected.size() > 0) {
+ for (Article a : selected)
+ a.unread = !a.unread;
+
+ m_activity.toggleArticlesUnread(selected);
+ //updateHeadlines();
+ } else {
+ Article article = getArticleAtPosition(info.position);
+ if (article != null) {
+ article.unread = !article.unread;
+ m_activity.saveArticleUnread(article);
+ //updateHeadlines();
+ }
+ }
+ m_adapter.notifyDataSetChanged();
+ }
+ return true;
+ case R.id.share_article:
+ if (true) {
+ Article article = getArticleAtPosition(info.position);
+ if (article != null)
+ m_activity.shareArticle(article);
+ }
+ return true;
+ case R.id.catchup_above:
+ if (true) {
+ Article article = getArticleAtPosition(info.position);
+ if (article != null) {
+ ArticleList articles = getAllArticles();
+ ArticleList tmp = new ArticleList();
+ for (Article a : articles) {
+ a.unread = false;
+ tmp.add(a);
+ if (article.id == a.id)
+ break;
+ }
+ if (tmp.size() > 0) {
+ m_activity.toggleArticlesUnread(tmp);
+ //updateHeadlines();
+ }
+ }
+ m_adapter.notifyDataSetChanged();
+ }
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+
+ @Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- getActivity().getMenuInflater().inflate(R.menu.headlines_menu, menu);
+ getActivity().getMenuInflater().inflate(R.menu.headlines_context_menu, menu);
if (m_selectedArticles.size() > 0) {
menu.setHeaderTitle(R.string.headline_context_multiple);
@@ -116,8 +248,8 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
menu.setGroupVisible(R.id.menu_group_single_article, true);
}
- menu.findItem(R.id.set_labels).setEnabled(m_onlineServices.getApiLevel() >= 1);
- menu.findItem(R.id.article_set_note).setEnabled(m_onlineServices.getApiLevel() >= 1);
+ menu.findItem(R.id.set_labels).setEnabled(m_activity.getApiLevel() >= 1);
+ menu.findItem(R.id.article_set_note).setEnabled(m_activity.getApiLevel() >= 1);
super.onCreateContextMenu(menu, v, menuInfo);
@@ -128,10 +260,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
if (savedInstanceState != null) {
m_feed = savedInstanceState.getParcelable("feed");
- m_articles = savedInstanceState.getParcelable("articles");
+ //m_articles = savedInstanceState.getParcelable("articles");
m_activeArticle = savedInstanceState.getParcelable("activeArticle");
m_selectedArticles = savedInstanceState.getParcelable("selectedArticles");
- m_canLoadMore = savedInstanceState.getBoolean("canLoadMore");
m_combinedMode = savedInstanceState.getBoolean("combinedMode");
m_searchQuery = (String) savedInstanceState.getCharSequence("searchQuery");
}
@@ -146,26 +277,45 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
//list.setEmptyView(view.findViewById(R.id.no_headlines));
registerForContextMenu(list);
- if (m_onlineServices.isSmallScreen() || m_onlineServices.isPortrait())
+ if (m_activity.isSmallScreen() || m_activity.isPortrait())
view.findViewById(R.id.headlines_fragment).setPadding(0, 0, 0, 0);
Log.d(TAG, "onCreateView, feed=" + m_feed);
- if (m_feed != null && (m_articles == null || m_articles.size() == 0))
- refresh(false);
- else
- getActivity().setProgressBarIndeterminateVisibility(false);
-
return view;
}
@Override
+ public void onResume() {
+ super.onResume();
+
+ if (GlobalState.getInstance().m_activeArticle != null) {
+ m_activeArticle = GlobalState.getInstance().m_activeArticle;
+ GlobalState.getInstance().m_activeArticle = null;
+ }
+
+ if (m_activeArticle != null) {
+ setActiveArticle(m_activeArticle);
+ }
+
+ if (m_articles.size() == 0 || !m_feed.equals(GlobalState.getInstance().m_activeFeed)) {
+ refresh(false);
+ GlobalState.getInstance().m_activeFeed = m_feed;
+ } else {
+ notifyUpdated();
+ }
+
+ m_activity.initMenu();
+ }
+
+ @Override
public void onAttach(Activity activity) {
super.onAttach(activity);
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
- m_onlineServices = (OnlineServices) activity;
+ m_activity = (OnlineActivity) activity;
+ m_listener = (HeadlinesEventListener) activity;
- m_combinedMode = m_prefs.getBoolean("combined_mode", false);
+ m_combinedMode = false; /* m_prefs.getBoolean("combined_mode", false); */
}
@Override
@@ -179,12 +329,16 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
if (article.id >= 0) {
if (m_combinedMode) {
article.unread = false;
- m_onlineServices.saveArticleUnread(article);
+ m_activity.saveArticleUnread(article);
} else {
- m_onlineServices.onArticleSelected(article);
+ m_listener.onArticleSelected(article);
+ }
+
+ // only set active article when it makes sense (in HeadlinesActivity)
+ if (getActivity().findViewById(R.id.article_fragment) != null) {
+ m_activeArticle = article;
}
- m_activeArticle = article;
m_adapter.notifyDataSetChanged();
}
}
@@ -192,63 +346,101 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
@SuppressWarnings({ "unchecked", "serial" })
public void refresh(boolean append) {
- m_refreshInProgress = true;
-
- HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext());
-
- final String sessionId = m_onlineServices.getSessionId();
- final boolean showUnread = m_onlineServices.getUnreadArticlesOnly();
- final boolean isCat = m_feed.is_cat;
- int skip = 0;
-
- if (append) {
- for (Article a : m_articles) {
- if (a.unread) ++skip;
+ if (m_activity != null) {
+ m_refreshInProgress = true;
+
+ m_activity.setProgressBarVisibility(true);
+
+ if (!m_feed.equals(GlobalState.getInstance().m_activeFeed)) {
+ append = false;
}
+
+ final boolean fappend = append;
+ final String sessionId = m_activity.getSessionId();
+ final boolean showUnread = m_activity.getUnreadArticlesOnly();
+ final boolean isCat = m_feed.is_cat;
- if (skip == 0) skip = m_articles.size();
- } else {
- setLoadingStatus(R.string.blank, true);
- }
-
- final int fskip = skip;
-
- req.setOffset(skip);
-
- HashMap<String,String> map = new HashMap<String,String>() {
- {
- put("op", "getHeadlines");
- put("sid", sessionId);
- put("feed_id", String.valueOf(m_feed.id));
- put("show_content", "true");
- put("include_attachments", "true");
- put("limit", String.valueOf(HEADLINES_REQUEST_SIZE));
- put("offset", String.valueOf(0));
- put("view_mode", showUnread ? "adaptive" : "all_articles");
- put("skip", String.valueOf(fskip));
-
- if (isCat) put("is_cat", "true");
-
- if (m_searchQuery.length() != 0) {
- put("search", m_searchQuery);
- put("search_mode", "");
- put("match_on", "both");
+ HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext(), m_activity) {
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ m_activity.setProgress(Math.round((((float)progress[0] / (float)progress[1]) * 10000)));
}
- }
- };
- req.execute(map);
- }
+ @Override
+ protected void onPostExecute(JsonElement result) {
+ m_activity.setProgressBarVisibility(false);
+
+ super.onPostExecute(result);
+
+ if (result != null) {
+ m_refreshInProgress = false;
+
+ if (m_articles.indexOf(m_activeArticle) == -1)
+ m_activeArticle = null;
+
+ m_adapter.notifyDataSetChanged();
+ m_listener.onHeadlinesLoaded(fappend);
+ } else {
+ if (m_lastError == ApiError.LOGIN_FAILED) {
+ m_activity.login();
+ } else {
+ setLoadingStatus(getErrorMessage(), false);
+ }
+ }
+ }
+ };
+
+ int skip = 0;
+
+ if (append) {
+ for (Article a : m_articles) {
+ if (a.unread) ++skip;
+ }
+
+ if (skip == 0) skip = m_articles.size();
+ } else {
+ setLoadingStatus(R.string.blank, true);
+ }
+
+ final int fskip = skip;
+
+ req.setOffset(skip);
+
+ HashMap<String,String> map = new HashMap<String,String>() {
+ {
+ put("op", "getHeadlines");
+ put("sid", sessionId);
+ put("feed_id", String.valueOf(m_feed.id));
+ put("show_content", "true");
+ put("include_attachments", "true");
+ put("limit", String.valueOf(HEADLINES_REQUEST_SIZE));
+ put("offset", String.valueOf(0));
+ put("view_mode", showUnread ? "adaptive" : "all_articles");
+ put("skip", String.valueOf(fskip));
+ put("include_nested", "true");
+
+ if (isCat) put("is_cat", "true");
+
+ if (m_searchQuery != null && m_searchQuery.length() != 0) {
+ put("search", m_searchQuery);
+ put("search_mode", "");
+ put("match_on", "both");
+ }
+ }
+ };
+
+ req.execute(map);
+ }
+ }
@Override
public void onSaveInstanceState (Bundle out) {
super.onSaveInstanceState(out);
out.putParcelable("feed", m_feed);
- out.putParcelable("articles", m_articles);
+ //out.putParcelable("articles", m_articles);
out.putParcelable("activeArticle", m_activeArticle);
out.putParcelable("selectedArticles", m_selectedArticles);
- out.putBoolean("canLoadMore", m_canLoadMore);
out.putBoolean("combinedMode", m_combinedMode);
out.putCharSequence("searchQuery", m_searchQuery);
}
@@ -266,7 +458,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
getActivity().setProgressBarIndeterminateVisibility(showProgress);
}
- private class HeadlinesRequest extends ApiRequest {
+ /* private class HeadlinesRequest extends ApiRequest {
int m_offset = 0;
public HeadlinesRequest(Context context) {
@@ -319,7 +511,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
}
if (m_lastError == ApiError.LOGIN_FAILED) {
- m_onlineServices.login();
+ m_activity.login();
} else {
setLoadingStatus(getErrorMessage(), false);
}
@@ -329,7 +521,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
public void setOffset(int skip) {
m_offset = skip;
}
- }
+ } */
private class ArticleListAdapter extends ArrayAdapter<Article> {
private ArrayList<Article> items;
@@ -409,7 +601,12 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
if (ft != null) {
if (article.feed_title != null && (m_feed.is_cat || m_feed.id < 0)) {
- ft.setText(article.feed_title);
+
+ if (article.feed_title.length() > 20)
+ ft.setText(article.feed_title.substring(0, 20) + "...");
+ else
+ ft.setText(article.feed_title);
+
} else {
ft.setVisibility(View.GONE);
}
@@ -428,7 +625,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
article.marked = !article.marked;
m_adapter.notifyDataSetChanged();
- m_onlineServices.saveArticleMarked(article);
+ m_activity.saveArticleMarked(article);
}
});
}
@@ -445,7 +642,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
article.published = !article.published;
m_adapter.notifyDataSetChanged();
- m_onlineServices.saveArticlePublished(article);
+ m_activity.saveArticlePublished(article);
}
});
}
@@ -533,7 +730,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
Attachment attachment = (Attachment) spinner.getSelectedItem();
if (attachment != null) {
- m_onlineServices.copyToClipboard(attachment.content_url);
+ m_activity.copyToClipboard(attachment.content_url);
}
}
});
@@ -590,7 +787,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
m_selectedArticles.remove(article);
}
- m_onlineServices.onArticleListSelectionChange(m_selectedArticles);
+ m_listener.onArticleListSelectionChange(m_selectedArticles);
Log.d(TAG, "num selected: " + m_selectedArticles.size());
}
@@ -615,27 +812,25 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
- /* public void notifyUpdated() {
+ public void notifyUpdated() {
m_adapter.notifyDataSetChanged();
-
- Article article = m_onlineServices.getSelectedArticle();
-
- setActiveArticle(article);
- } */
+ }
public ArticleList getAllArticles() {
return m_articles;
}
public void setActiveArticle(Article article) {
- m_activeArticle = article;
- m_adapter.notifyDataSetChanged();
+ if (article != m_activeArticle) {
+ m_activeArticle = article;
+ m_adapter.notifyDataSetChanged();
- ListView list = (ListView)getView().findViewById(R.id.headlines);
+ ListView list = (ListView)getView().findViewById(R.id.headlines);
- if (list != null && article != null) {
- int position = m_adapter.getPosition(article);
- list.setSelection(position);
+ if (list != null && article != null) {
+ int position = m_adapter.getPosition(article);
+ list.setSelection(position);
+ }
}
}
@@ -681,7 +876,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- if (!m_refreshInProgress && m_canLoadMore && firstVisibleItem + visibleItemCount == m_articles.size()) {
+ if (!m_refreshInProgress && m_articles.findById(-1) != null && firstVisibleItem + visibleItemCount == m_articles.size()) {
refresh(true);
}
}
@@ -703,6 +898,10 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
}
}
+ public String getSearchQuery() {
+ return m_searchQuery;
+ }
+
public void setSearchQuery(String query) {
if (!m_searchQuery.equals(query)) {
m_searchQuery = query;
@@ -710,5 +909,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
}
}
+ public Feed getFeed() {
+ return m_feed;
+ }
+
}
diff --git a/src/org/fox/ttrss/MainActivity.java b/src/org/fox/ttrss/MainActivity.java
deleted file mode 100644
index 1ac736e2..00000000
--- a/src/org/fox/ttrss/MainActivity.java
+++ /dev/null
@@ -1,2313 +0,0 @@
-package org.fox.ttrss;
-
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import org.fox.ttrss.offline.OfflineActivity;
-import org.fox.ttrss.offline.OfflineDownloadService;
-import org.fox.ttrss.offline.OfflineUploadService;
-import org.fox.ttrss.types.Article;
-import org.fox.ttrss.types.ArticleList;
-import org.fox.ttrss.types.Feed;
-import org.fox.ttrss.types.FeedCategory;
-import org.fox.ttrss.types.Label;
-import org.fox.ttrss.util.AppRater;
-
-import android.animation.LayoutTransition;
-import android.annotation.SuppressLint;
-import android.app.ActionBar;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.DialogInterface.OnMultiChoiceClickListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.util.Log;
-import android.view.ActionMode;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.SearchView;
-import android.widget.ShareActionProvider;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.reflect.TypeToken;
-
-public class MainActivity extends CommonActivity implements OnlineServices {
- private final String TAG = this.getClass().getSimpleName();
-
- private SharedPreferences m_prefs;
- private String m_themeName = "";
- private String m_sessionId;
- private Article m_selectedArticle;
- private Feed m_activeFeed;
- private FeedCategory m_activeCategory;
- private Timer m_refreshTimer;
- private RefreshTask m_refreshTask;
- private Menu m_menu;
- private boolean m_unreadOnly = true;
- private boolean m_unreadArticlesOnly = true;
- private boolean m_enableCats = false;
- private int m_apiLevel = 0;
- private boolean m_isLoggingIn = false;
- private boolean m_isOffline = false;
- private int m_offlineModeStatus = 0;
- private int m_selectedProduct = -1;
- private long m_lastRefresh = 0;
-
- private ActionMode m_headlinesActionMode;
- private HeadlinesActionModeCallback m_headlinesActionModeCallback;
- private NavigationListener m_navigationListener;
- private NavigationAdapter m_navigationAdapter;
- private ArrayList<NavigationEntry> m_navigationEntries = new ArrayList<NavigationEntry>();
-
- private class NavigationListener implements ActionBar.OnNavigationListener {
- @Override
- public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- Log.d(TAG, "onNavigationItemSelected: " + itemPosition);
-
- NavigationEntry entry = m_navigationAdapter.getItem(itemPosition);
- entry._onItemSelected(itemPosition, m_navigationAdapter.getCount()-1);
-
- return false;
- }
- }
-
- private class ArticleNavigationEntry extends NavigationEntry {
- public ArticleNavigationEntry(Article article) {
- super(article.title);
- }
-
- @Override
- public void onItemSelected() {
-
- }
- }
-
- private class RootNavigationEntry extends NavigationEntry {
- public RootNavigationEntry(String title) {
- super(title);
- }
-
- @Override
- public void onItemSelected() {
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- m_activeFeed = null;
- m_selectedArticle = null;
- m_activeCategory = null;
-
- if (isSmallScreen()) {
-
- if (m_enableCats) {
- ft.replace(R.id.fragment_container, new FeedCategoriesFragment(), FRAG_CATS);
- } else {
- ft.replace(R.id.fragment_container, new FeedsFragment(), FRAG_FEEDS);
- }
-
- Fragment hf = getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
- if (hf != null) ft.remove(hf);
-
- Fragment af = getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
- if (af != null) ft.remove(af);
-
- } else {
- if (m_enableCats) {
- ft.replace(R.id.feeds_fragment, new FeedCategoriesFragment(), FRAG_CATS);
- } else {
- ft.replace(R.id.feeds_fragment, new FeedsFragment(), FRAG_FEEDS);
- }
-
- findViewById(R.id.article_fragment).setVisibility(View.GONE);
- findViewById(R.id.feeds_fragment).setVisibility(View.VISIBLE);
-
- ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
- }
-
- ft.commit();
- initMainMenu();
- }
- }
-
- private class CategoryNavigationEntry extends NavigationEntry {
- FeedCategory m_category = null;
-
- public CategoryNavigationEntry(FeedCategory category) {
- super(category.title);
-
- m_category = category;
- }
-
- @Override
- public void onItemSelected() {
- m_selectedArticle = null;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (isSmallScreen()) {
-
- Fragment hf = getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
- if (hf != null) ft.remove(hf);
-
- Fragment af = getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
- if (af != null) ft.remove(af);
-
- if (m_activeFeed.is_cat) {
- FeedCategoriesFragment cats = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
-
- if (cats != null) {
- ft.show(cats);
- cats.setSelectedCategory(null);
- }
- } else {
- FeedsFragment feeds = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
-
- if (feeds != null) {
- ft.show(feeds);
- feeds.setSelectedFeed(null);
- }
- }
-
- } else {
- findViewById(R.id.article_fragment).setVisibility(View.GONE);
- findViewById(R.id.feeds_fragment).setVisibility(View.VISIBLE);
-
- updateHeadlines();
-
- //ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
- }
- ft.commit();
-
- m_activeFeed = null;
- refresh();
- initMainMenu();
- }
- }
-
- private class FeedNavigationEntry extends NavigationEntry {
- Feed m_feed = null;
-
- public FeedNavigationEntry(Feed feed) {
- super(feed.title);
-
- m_feed = feed;
- }
-
- @Override
- public void onItemSelected() {
-
- m_selectedArticle = null;
-
- if (!isSmallScreen())
- findViewById(R.id.article_fragment).setVisibility(View.GONE);
-
- viewFeed(m_feed, false);
- }
- }
-
- private abstract class NavigationEntry {
- private String title = null;
- private int timesCalled = 0;
-
- public void _onItemSelected(int position, int size) {
- Log.d(TAG, "_onItemSelected; TC=" + timesCalled + " P/S=" + position + "/" + size);
-
- if (position == size && timesCalled == 0) {
- ++timesCalled;
- } else {
- onItemSelected();
- }
- }
-
- public NavigationEntry(String title) {
- this.title = title;
- }
-
- public String toString() {
- return title;
- }
-
- public abstract void onItemSelected();
- }
-
- private class NavigationAdapter extends ArrayAdapter<NavigationEntry> {
- public NavigationAdapter(Context context, int textViewResourceId, ArrayList<NavigationEntry> items) {
- super(context, textViewResourceId, items);
- }
- }
-
- private class HeadlinesActionModeCallback implements ActionMode.Callback {
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- deselectAllArticles();
- m_headlinesActionMode = null;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.headlines_action_menu, menu);
-
- return true;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- onOptionsItemSelected(item);
- return false;
- }
- };
-
- private BroadcastReceiver m_broadcastReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context content, Intent intent) {
-
- if (intent.getAction().equals(OfflineDownloadService.INTENT_ACTION_SUCCESS)) {
-
- m_offlineModeStatus = 2;
-
- switchOffline();
-
- } else if (intent.getAction().equals(OfflineUploadService.INTENT_ACTION_SUCCESS)) {
- //Log.d(TAG, "offline upload service reports success");
-
- refresh();
-
- toast(R.string.offline_sync_success);
- }
-
- }
- };
-
- public void updateHeadlines() {
- HeadlinesFragment frag = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
- if (frag != null) {
- frag.setActiveArticle(m_selectedArticle);
- }
- }
-
- @Override
- public int getApiLevel() {
- return m_apiLevel;
- }
-
- private boolean hasPendingOfflineData() {
- try {
- Cursor c = getReadableDb().query("articles",
- new String[] { "COUNT(*)" }, "modified = 1", null, null, null,
- null);
- if (c.moveToFirst()) {
- int modified = c.getInt(0);
- c.close();
-
- return modified > 0;
- }
- } catch (IllegalStateException e) {
- // db is closed? ugh
- }
-
- return false;
- }
-
- private boolean hasOfflineData() {
- try {
- Cursor c = getReadableDb().query("articles",
- new String[] { "COUNT(*)" }, null, null, null, null, null);
- if (c.moveToFirst()) {
- int modified = c.getInt(0);
- c.close();
-
- return modified > 0;
- }
- } catch (IllegalStateException e) {
- // db is closed?
- }
-
- return false;
- }
-
- @SuppressWarnings({ "unchecked", "serial" })
- public void saveArticleUnread(final Article article) {
- ApiRequest req = new ApiRequest(getApplicationContext());
-
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", String.valueOf(article.id));
- put("mode", article.unread ? "1" : "0");
- put("field", "2");
- }
- };
-
- req.execute(map);
- }
-
- @SuppressWarnings({ "unchecked", "serial" })
- public void saveArticleMarked(final Article article) {
- ApiRequest req = new ApiRequest(getApplicationContext()) {
- protected void onPostExecute(JsonElement result) {
- toast(article.marked ? R.string.notify_article_marked : R.string.notify_article_unmarked);
- }
- };
-
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", String.valueOf(article.id));
- put("mode", article.marked ? "1" : "0");
- put("field", "0");
- }
- };
-
- req.execute(map);
- }
-
- @SuppressWarnings({ "unchecked", "serial" })
- public void saveArticlePublished(final Article article) {
-
- ApiRequest req = new ApiRequest(getApplicationContext()) {
- protected void onPostExecute(JsonElement result) {
- toast(article.published ? R.string.notify_article_published : R.string.notify_article_unpublished);
- }
- };
-
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", String.valueOf(article.id));
- put("mode", article.published ? "1" : "0");
- put("field", "1");
- }
- };
-
- req.execute(map);
- }
-
- @SuppressWarnings({ "unchecked", "serial" })
- public void saveArticleNote(final Article article, final String note) {
- ApiRequest req = new ApiRequest(getApplicationContext()) {
- protected void onPostExecute(JsonElement result) {
- toast(R.string.notify_article_note_set);
- }
- };
-
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", String.valueOf(article.id));
- put("mode", "1");
- put("data", note);
- put("field", "3");
- }
- };
-
- req.execute(map);
- }
-
- public static String articlesToIdString(ArticleList articles) {
- String tmp = "";
-
- for (Article a : articles)
- tmp += String.valueOf(a.id) + ",";
-
- return tmp.replaceAll(",$", "");
- }
-
- @SuppressWarnings("unchecked")
- public void catchupFeed(final Feed feed) {
- Log.d(TAG, "catchupFeed=" + feed);
-
- ApiRequest req = new ApiRequest(getApplicationContext()) {
- protected void onPostExecute(JsonElement result) {
- refresh();
- }
-
- };
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "catchupFeed");
- put("feed_id", String.valueOf(feed.id));
- if (feed.is_cat)
- put("is_cat", "1");
- }
- };
-
- req.execute(map);
- }
-
- @SuppressWarnings("unchecked")
- private void toggleArticlesMarked(final ArticleList articles) {
- ApiRequest req = new ApiRequest(getApplicationContext());
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", articlesToIdString(articles));
- put("mode", "2");
- put("field", "0");
- }
- };
-
- req.execute(map);
- }
-
- @SuppressWarnings("unchecked")
- private void toggleArticlesUnread(final ArticleList articles) {
- ApiRequest req = new ApiRequest(getApplicationContext());
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", articlesToIdString(articles));
- put("mode", "2");
- put("field", "2");
- }
- };
-
- req.execute(map);
- refresh();
- }
-
- @SuppressWarnings("unchecked")
- private void toggleArticlesPublished(final ArticleList articles) {
- ApiRequest req = new ApiRequest(getApplicationContext());
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", articlesToIdString(articles));
- put("mode", "2");
- put("field", "1");
- }
- };
-
- req.execute(map);
- }
-
- private class RefreshTask extends TimerTask {
-
- @Override
- public void run() {
- ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
-
- if (cm.getBackgroundDataSetting()) {
- NetworkInfo networkInfo = cm.getActiveNetworkInfo();
- if (networkInfo != null && networkInfo.isAvailable()
- && networkInfo.isConnected()) {
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- refresh();
- }
- });
-
- }
- }
- }
- }
-
- private synchronized void refresh() {
- Date date = new Date();
-
- if (m_sessionId != null && date.getTime() - m_lastRefresh > 5000) {
-
- FeedsFragment ff = (FeedsFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_FEEDS);
-
- if (ff != null) {
- Log.d(TAG, "Refreshing feeds/" + m_activeFeed);
- ff.refresh(true);
- }
-
- FeedCategoriesFragment cf = (FeedCategoriesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_CATS);
-
- if (cf != null) {
- Log.d(TAG, "Refreshing categories/" + m_activeCategory);
- cf.refresh(true);
- }
-
- m_lastRefresh = date.getTime();
- }
- }
-
- /* private synchronized void refreshHeadlines() {
- if (m_sessionId != null) {
- HeadlinesFragment frag = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- Log.d(TAG, "Refreshing headlines...");
-
- if (frag != null) {
- frag.refresh(true);
- }
- }
- } */
-
- private void setUnreadOnly(boolean unread) {
- m_unreadOnly = unread;
- m_lastRefresh = 0;
- refresh();
- }
-
- @Override
- public boolean getUnreadOnly() {
- return m_unreadOnly;
- }
-
- @Override
- public boolean getUnreadArticlesOnly() {
- return m_unreadArticlesOnly;
- }
-
- @Override
- public String getSessionId() {
- return m_sessionId;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- m_prefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
-
- if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
- setTheme(R.style.DarkTheme);
- } else {
- setTheme(R.style.LightTheme);
- }
-
- super.onCreate(savedInstanceState);
-
- if (OfflineDownloadService.INTENT_ACTION_CANCEL.equals(getIntent().getAction())) {
- cancelOfflineSync();
- }
-
- //Log.d(TAG, "started with intent action=" + intentAction);
-
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
- m_themeName = m_prefs.getString("theme", "THEME_DARK");
-
- if (savedInstanceState != null) {
- m_sessionId = savedInstanceState.getString("sessionId");
- m_unreadOnly = savedInstanceState.getBoolean("unreadOnly");
- m_activeFeed = savedInstanceState.getParcelable("activeFeed");
- m_selectedArticle = savedInstanceState
- .getParcelable("selectedArticle");
- m_unreadArticlesOnly = savedInstanceState
- .getBoolean("unreadArticlesOnly");
- m_activeCategory = savedInstanceState
- .getParcelable("activeCategory");
- m_apiLevel = savedInstanceState.getInt("apiLevel");
- m_offlineModeStatus = savedInstanceState.getInt("offlineModeStatus");
- }
-
- m_enableCats = m_prefs.getBoolean("enable_cats", false);
-
- setContentView(R.layout.main);
-
- setSmallScreen(findViewById(R.id.headlines_fragment) == null);
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(OfflineDownloadService.INTENT_ACTION_SUCCESS);
- filter.addAction(OfflineUploadService.INTENT_ACTION_SUCCESS);
- filter.addCategory(Intent.CATEGORY_DEFAULT);
-
- registerReceiver(m_broadcastReceiver, filter);
-
- SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE);
-
- m_isOffline = localPrefs.getBoolean("offline_mode_active", false);
-
- Log.d(TAG, "m_isOffline=" + m_isOffline);
-
- if (!isCompatMode()) {
-
- if (!isSmallScreen()) {
- findViewById(R.id.feeds_fragment).setVisibility(m_selectedArticle != null && (isPortrait() || isSmallTablet()) ? View.GONE : View.VISIBLE);
- findViewById(R.id.article_fragment).setVisibility(m_selectedArticle != null ? View.VISIBLE : View.GONE);
- }
-
- LayoutTransition transitioner = new LayoutTransition();
- ((ViewGroup) findViewById(R.id.fragment_container)).setLayoutTransition(transitioner);
-
- m_navigationAdapter = new NavigationAdapter(this, android.R.layout.simple_spinner_dropdown_item, m_navigationEntries);
-
- m_headlinesActionModeCallback = new HeadlinesActionModeCallback();
- m_navigationListener = new NavigationListener();
-
- getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- getActionBar().setListNavigationCallbacks(m_navigationAdapter, m_navigationListener);
- }
-
- if (isSmallScreen()) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- // temporary workaround against viewpager going a bit crazy when restoring after rotation
- if (m_selectedArticle != null) {
- ft.remove(getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE));
- m_selectedArticle = null;
- }
-
- if (m_activeFeed != null) {
- if (m_activeFeed.is_cat) {
- ft.hide(getSupportFragmentManager().findFragmentByTag(FRAG_CATS));
- } else {
- ft.hide(getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS));
- }
- }
- ft.commit();
- }
-
- if (m_isOffline) {
- Intent offline = new Intent(MainActivity.this,
- OfflineActivity.class);
- startActivity(offline);
- finish();
- } else {
- //AppRater.showRateDialog(this, null);
- AppRater.appLaunched(this);
-
- if (m_sessionId != null) {
- loginSuccess();
- } else {
- //login(); -- handled in onResume()
- }
- }
- }
-
- private void switchOffline() {
- if (m_offlineModeStatus == 2) {
-
- AlertDialog.Builder builder = new AlertDialog.Builder(
- MainActivity.this)
- .setMessage(R.string.dialog_offline_success)
- .setPositiveButton(R.string.dialog_offline_go,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
-
- m_offlineModeStatus = 0;
-
- SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = localPrefs.edit();
- editor.putBoolean("offline_mode_active", true);
- editor.commit();
-
- Intent refresh = new Intent(
- MainActivity.this,
- OfflineActivity.class);
- startActivity(refresh);
- finish();
- }
- })
- .setNegativeButton(R.string.dialog_cancel,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
-
- m_offlineModeStatus = 0;
-
- }
- });
-
- AlertDialog dlg = builder.create();
- dlg.show();
-
- } else if (m_offlineModeStatus == 0) {
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setMessage(R.string.dialog_offline_switch_prompt)
- .setPositiveButton(R.string.dialog_offline_go,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
-
- if (m_sessionId != null) {
- Log.d(TAG, "offline: starting");
-
- m_offlineModeStatus = 1;
-
- Intent intent = new Intent(
- MainActivity.this,
- OfflineDownloadService.class);
- intent.putExtra("sessionId", m_sessionId);
-
- startService(intent);
- }
- }
- })
- .setNegativeButton(R.string.dialog_cancel,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
- //
- }
- });
-
- AlertDialog dlg = builder.create();
- dlg.show();
- } else if (m_offlineModeStatus == 1) {
- cancelOfflineSync();
- }
- }
-
- private void cancelOfflineSync() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setMessage(R.string.dialog_offline_sync_in_progress)
- .setNegativeButton(R.string.dialog_offline_sync_stop,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
-
- if (m_sessionId != null) {
- Log.d(TAG, "offline: stopping");
-
- m_offlineModeStatus = 0;
-
- Intent intent = new Intent(
- MainActivity.this,
- OfflineDownloadService.class);
-
- stopService(intent);
-
- dialog.dismiss();
-
- restart();
- }
- }
- })
- .setPositiveButton(R.string.dialog_offline_sync_continue,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
-
- dialog.dismiss();
-
- restart();
- }
- });
-
- AlertDialog dlg = builder.create();
- dlg.show();
- }
-
- private void switchOfflineSuccess() {
- logout();
- // setLoadingStatus(R.string.blank, false);
-
- SharedPreferences.Editor editor = m_prefs.edit();
- editor.putBoolean("offline_mode_active", true);
- editor.commit();
-
- Intent offline = new Intent(MainActivity.this, OfflineActivity.class);
- startActivity(offline);
- finish();
-
- }
-
- private void setLoadingStatus(int status, boolean showProgress) {
- TextView tv = (TextView) findViewById(R.id.loading_message);
-
- if (tv != null) {
- tv.setText(status);
- }
-
- setProgressBarIndeterminateVisibility(showProgress);
- }
-
- @Override
- public void onSaveInstanceState(Bundle out) {
- super.onSaveInstanceState(out);
-
- out.putString("sessionId", m_sessionId);
- out.putBoolean("unreadOnly", m_unreadOnly);
- out.putParcelable("activeFeed", m_activeFeed);
- out.putParcelable("selectedArticle", m_selectedArticle);
- out.putBoolean("unreadArticlesOnly", m_unreadArticlesOnly);
- out.putParcelable("activeCategory", m_activeCategory);
- out.putInt("apiLevel", m_apiLevel);
- out.putInt("offlineModeStatus", m_offlineModeStatus);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- boolean needRefresh = !m_prefs.getString("theme", "THEME_DARK").equals(
- m_themeName)
- || m_prefs.getBoolean("enable_cats", false) != m_enableCats;
-
- if (needRefresh) {
- restart();
- } else if (m_sessionId != null) {
- m_refreshTask = new RefreshTask();
- m_refreshTimer = new Timer("Refresh");
-
- m_refreshTimer.schedule(m_refreshTask, 60 * 1000L, 120 * 1000L);
- } else {
- if (!m_isLoggingIn) {
- login();
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.main_menu, menu);
-
- m_menu = menu;
-
- initMainMenu();
-
- MenuItem item = menu.findItem(R.id.show_feeds);
-
- if (getUnreadOnly()) {
- item.setTitle(R.string.menu_all_feeds);
- } else {
- item.setTitle(R.string.menu_unread_feeds);
- }
-
- /*
- * item = menu.findItem(R.id.show_all_articles);
- *
- * if (getUnreadArticlesOnly()) {
- * item.setTitle(R.string.show_all_articles); } else {
- * item.setTitle(R.string.show_unread_articles); }
- */
-
- return true;
- }
-
- private void setMenuLabel(int id, int labelId) {
- MenuItem mi = m_menu.findItem(id);
-
- if (mi != null) {
- mi.setTitle(labelId);
- }
- }
-
- @Override
- public void onBackPressed() {
- goBack(true);
- }
-
- private void closeCategory() {
- m_activeCategory = null;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, new FeedCategoriesFragment(), FRAG_CATS);
- } else {
- ft.replace(R.id.feeds_fragment, new FeedCategoriesFragment(), FRAG_CATS);
- }
- ft.commit();
-
- initMainMenu();
- refresh();
- }
-
- private void deselectAllArticles() {
- HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (hf != null) {
- ArticleList selected = hf.getSelectedArticles();
- if (selected.size() > 0) {
- selected.clear();
- initMainMenu();
- updateHeadlines();
- }
- }
- }
-
- private void goBack(boolean allowQuit) {
- if (isSmallScreen()) {
- if (m_selectedArticle != null) {
- closeArticle();
- } else if (m_activeFeed != null) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- if (m_activeFeed.is_cat) {
-
- Fragment headlines = getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
- FeedCategoriesFragment cats = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
-
- ft.show(cats);
- ft.remove(headlines);
-
- } else {
- Fragment headlines = getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
- FeedsFragment feeds = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
-
- ft.show(feeds);
- ft.remove(headlines);
- }
- ft.commit();
-
- m_activeFeed = null;
-
- refresh();
-
- initMainMenu();
-
- } else if (m_activeCategory != null) {
- closeCategory();
- } else if (allowQuit) {
- finish();
- }
- } else {
- if (m_selectedArticle != null) {
- closeArticle();
- refresh();
- } else if (m_activeFeed != null) {
- closeFeed();
- } else if (m_activeCategory != null) {
- closeCategory();
- } else if (allowQuit) {
- finish();
- }
- }
- }
-
-
- private void closeFeed() {
- if (m_activeFeed != null) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
- ft.commit();
-
- if (m_activeFeed.is_cat) {
- FeedCategoriesFragment cats = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
- if (cats != null) {
- cats.setSelectedCategory(null);
- }
- } else {
- FeedsFragment feeds = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
- if (feeds != null) {
- feeds.setSelectedFeed(null);
- }
- }
-
- m_activeFeed = null;
-
- initMainMenu();
- }
- }
-
-
- @SuppressWarnings("unchecked")
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- final HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- switch (item.getItemId()) {
- case R.id.close_feed:
- if (m_activeFeed != null) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
- ft.commit();
-
- if (m_activeFeed.is_cat) {
- FeedCategoriesFragment cats = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
- cats.setSelectedCategory(null);
- } else {
- FeedsFragment feeds = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
- feeds.setSelectedFeed(null);
- }
-
- m_activeFeed = null;
-
- initMainMenu();
- }
- return true;
- case R.id.close_article:
- closeArticle();
- return true;
- case android.R.id.home:
- goBack(false);
- return true;
- case R.id.search:
- if (hf != null && isCompatMode()) {
- Dialog dialog = new Dialog(this);
-
- final EditText edit = new EditText(this);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.search)
- .setPositiveButton(getString(R.string.search),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
-
- String query = edit.getText().toString().trim();
-
- hf.setSearchQuery(query);
-
- }
- })
- .setNegativeButton(getString(R.string.cancel),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
-
- //
-
- }
- }).setView(edit);
-
- dialog = builder.create();
- dialog.show();
- }
-
- return true;
- case R.id.preferences:
- Intent intent = new Intent(MainActivity.this,
- PreferencesActivity.class);
- startActivityForResult(intent, 0);
- return true;
- case R.id.update_feeds:
- m_lastRefresh = 0;
- refresh();
- return true;
- case R.id.logout:
- logout();
- return true;
- case R.id.login:
- login();
- return true;
- case R.id.go_offline:
- switchOffline();
- return true;
- case R.id.article_set_note:
- if (m_selectedArticle != null) {
- editArticleNote(m_selectedArticle);
- }
- return true;
- case R.id.headlines_select:
- if (hf != null) {
- Dialog dialog = new Dialog(this);
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.headlines_select_dialog)
- .setSingleChoiceItems(
- new String[] {
- getString(R.string.headlines_select_all),
- getString(R.string.headlines_select_unread),
- getString(R.string.headlines_select_none) },
- 0, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- switch (which) {
- case 0:
- hf.setSelection(HeadlinesFragment.ArticlesSelection.ALL);
- break;
- case 1:
- hf.setSelection(HeadlinesFragment.ArticlesSelection.UNREAD);
- break;
- case 2:
- hf.setSelection(HeadlinesFragment.ArticlesSelection.NONE);
- break;
- }
- dialog.cancel();
- initMainMenu();
- }
- });
-
- dialog = builder.create();
- dialog.show();
- }
- return true;
- case R.id.headlines_mark_as_read:
- if (hf != null) {
- ArticleList articles = hf.getUnreadArticles();
-
- for (Article a : articles)
- a.unread = false;
-
- updateHeadlines();
-
- ApiRequest req = new ApiRequest(getApplicationContext());
-
- final String articleIds = articlesToIdString(articles);
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "updateArticle");
- put("article_ids", articleIds);
- put("mode", "0");
- put("field", "2");
- }
- };
-
- req.execute(map);
- refresh();
- }
- return true;
- case R.id.share_article:
- if (android.os.Build.VERSION.SDK_INT < 14) {
- shareArticle(m_selectedArticle);
- }
- return true;
- case R.id.toggle_marked:
- if (m_selectedArticle != null) {
- m_selectedArticle.marked = !m_selectedArticle.marked;
- saveArticleMarked(m_selectedArticle);
- updateHeadlines();
- }
- return true;
- case R.id.selection_select_none:
- deselectAllArticles();
- return true;
- case R.id.selection_toggle_unread:
- if (hf != null) {
- ArticleList selected = hf.getSelectedArticles();
-
- if (selected.size() > 0) {
- for (Article a : selected)
- a.unread = !a.unread;
-
- toggleArticlesUnread(selected);
- updateHeadlines();
- }
- refresh();
- }
- return true;
- case R.id.selection_toggle_marked:
- if (hf != null) {
- ArticleList selected = hf.getSelectedArticles();
-
- if (selected.size() > 0) {
- for (Article a : selected)
- a.marked = !a.marked;
-
- toggleArticlesMarked(selected);
- updateHeadlines();
- }
- }
- return true;
- case R.id.selection_toggle_published:
- if (hf != null) {
- ArticleList selected = hf.getSelectedArticles();
-
- if (selected.size() > 0) {
- for (Article a : selected)
- a.published = !a.published;
-
- toggleArticlesPublished(selected);
- updateHeadlines();
- }
- }
- return true;
- case R.id.toggle_published:
- if (m_selectedArticle != null) {
- m_selectedArticle.published = !m_selectedArticle.published;
- saveArticlePublished(m_selectedArticle);
- updateHeadlines();
- }
- return true;
- case R.id.catchup_above:
- if (hf != null) {
- if (m_selectedArticle != null) {
- ArticleList articles = hf.getAllArticles();
- ArticleList tmp = new ArticleList();
- for (Article a : articles) {
- a.unread = false;
- tmp.add(a);
- if (m_selectedArticle.id == a.id)
- break;
- }
- if (tmp.size() > 0) {
- toggleArticlesUnread(tmp);
- updateHeadlines();
- }
- }
- }
- return true;
- case R.id.set_unread:
- if (m_selectedArticle != null) {
- m_selectedArticle.unread = true;
- saveArticleUnread(m_selectedArticle);
- updateHeadlines();
- }
- return true;
- case R.id.show_feeds:
- setUnreadOnly(!getUnreadOnly());
-
- if (getUnreadOnly()) {
- item.setTitle(R.string.menu_all_feeds);
- } else {
- item.setTitle(R.string.menu_unread_feeds);
- }
-
- return true;
- case R.id.set_labels:
- if (m_selectedArticle != null) {
- editArticleLabels(m_selectedArticle);
- }
- return true;
- default:
- Log.d(TAG,
- "onOptionsItemSelected, unhandled id=" + item.getItemId());
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void editArticleNote(final Article article) {
- String note = "";
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(article.title);
- final EditText topicEdit = new EditText(this);
- topicEdit.setText(note);
- builder.setView(topicEdit);
-
- builder.setPositiveButton(R.string.article_set_note, new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- saveArticleNote(article, topicEdit.getText().toString().trim());
- article.published = true;
- saveArticlePublished(article);
- updateHeadlines();
- }
- });
-
- builder.setNegativeButton(R.string.dialog_cancel, new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- //
- }
- });
-
- AlertDialog dialog = builder.create();
- dialog.show();
- }
-
- private void editArticleLabels(Article article) {
- final int articleId = article.id;
-
- ApiRequest req = new ApiRequest(getApplicationContext()) {
- @Override
- protected void onPostExecute(JsonElement result) {
- if (result != null) {
- Type listType = new TypeToken<List<Label>>() {}.getType();
- final List<Label> labels = new Gson().fromJson(result, listType);
-
- CharSequence[] items = new CharSequence[labels.size()];
- final int[] itemIds = new int[labels.size()];
- boolean[] checkedItems = new boolean[labels.size()];
-
- for (int i = 0; i < labels.size(); i++) {
- items[i] = labels.get(i).caption;
- itemIds[i] = labels.get(i).id;
- checkedItems[i] = labels.get(i).checked;
- }
-
- Dialog dialog = new Dialog(MainActivity.this);
- AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
- .setTitle(R.string.article_set_labels)
- .setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which, final boolean isChecked) {
- final int labelId = itemIds[which];
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "setArticleLabel");
- put("label_id", String.valueOf(labelId));
- put("article_ids", String.valueOf(articleId));
- if (isChecked) put("assign", "true");
- }
- };
-
- ApiRequest req = new ApiRequest(m_context);
- req.execute(map);
-
- }
- }).setPositiveButton(R.string.dialog_close, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
-
- dialog = builder.create();
- dialog.show();
-
- }
- }
- };
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "getLabels");
- put("article_id", String.valueOf(articleId));
- }
- };
-
- req.execute(map);
- }
-
- private Intent getShareIntent(Article article) {
- Intent intent = new Intent(Intent.ACTION_SEND);
-
- intent.setType("text/plain");
- intent.putExtra(Intent.EXTRA_SUBJECT, article.title);
- intent.putExtra(Intent.EXTRA_TEXT, article.link);
-
- return intent;
- }
-
- private void shareArticle(Article article) {
- if (article != null) {
-
- Intent intent = getShareIntent(article);
-
- startActivity(Intent.createChooser(intent,
- getString(R.string.share_article)));
- }
- }
-
- private void closeArticle() {
- m_selectedArticle = null;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (isSmallScreen()) {
- ft.remove(getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE));
- ft.show(getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES));
- } else {
- findViewById(R.id.feeds_fragment).setVisibility(View.VISIBLE);
- findViewById(R.id.article_fragment).setVisibility(View.GONE);
- ft.replace(R.id.article_fragment, new DummyFragment(), FRAG_ARTICLE);
-
- updateHeadlines();
- }
- ft.commit();
-
- initMainMenu();
- }
-
- private void updateTitle() {
- if (!isCompatMode()) {
-
- m_navigationAdapter.clear();
-
- if (m_activeCategory != null || (m_activeFeed != null && (isSmallScreen() || isPortrait()))) {
- getActionBar().setDisplayShowTitleEnabled(false);
- getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
-
- m_navigationAdapter.add(new RootNavigationEntry(getString(R.string.app_name)));
-
- if (m_activeCategory != null)
- m_navigationAdapter.add(new CategoryNavigationEntry(m_activeCategory));
-
- if (m_activeFeed != null)
- m_navigationAdapter.add(new FeedNavigationEntry(m_activeFeed));
-
- //if (m_selectedArticle != null)
- // m_navigationAdapter.add(new ArticleNavigationEntry(m_selectedArticle));
-
- getActionBar().setSelectedNavigationItem(getActionBar().getNavigationItemCount());
-
- } else {
- getActionBar().setDisplayShowTitleEnabled(true);
- getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
- getActionBar().setTitle(R.string.app_name);
- }
-
- if (isSmallScreen()) {
- getActionBar().setDisplayHomeAsUpEnabled(m_selectedArticle != null || m_activeCategory != null || m_activeFeed != null);
- } else {
- getActionBar().setDisplayHomeAsUpEnabled(m_selectedArticle != null || m_activeCategory != null);
- }
-
- } else {
- if (m_activeFeed != null) {
- setTitle(m_activeFeed.title);
- } else if (m_activeCategory != null) {
- setTitle(m_activeCategory.title);
- } else {
- setTitle(R.string.app_name);
- }
- }
- }
-
- @SuppressLint({ "NewApi", "NewApi", "NewApi" })
- public void initMainMenu() {
- if (m_menu != null) {
-
- m_menu.setGroupVisible(R.id.menu_group_feeds, false);
- m_menu.setGroupVisible(R.id.menu_group_headlines, false);
- m_menu.setGroupVisible(R.id.menu_group_headlines_selection, false);
- m_menu.setGroupVisible(R.id.menu_group_article, false);
-
- if (m_sessionId != null) {
-
- m_menu.setGroupVisible(R.id.menu_group_logged_in, true);
- m_menu.setGroupVisible(R.id.menu_group_logged_out, false);
-
- HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- int numSelected = 0;
-
- if (hf != null)
- numSelected = hf.getSelectedArticles().size();
-
- if (numSelected != 0) {
- if (isCompatMode()) {
- m_menu.setGroupVisible(R.id.menu_group_headlines_selection, true);
- } else {
- if (m_headlinesActionMode == null)
- m_headlinesActionMode = startActionMode(m_headlinesActionModeCallback);
- }
-
- } else if (m_selectedArticle != null) {
- m_menu.setGroupVisible(R.id.menu_group_article, true);
- m_menu.findItem(R.id.close_article).setVisible(!isSmallScreen());
-
- if (android.os.Build.VERSION.SDK_INT >= 14) {
- ShareActionProvider shareProvider = (ShareActionProvider) m_menu.findItem(R.id.share_article).getActionProvider();
-
- if (m_selectedArticle != null) {
- Log.d(TAG, "setting up share provider");
- shareProvider.setShareIntent(getShareIntent(m_selectedArticle));
-
- if (!m_prefs.getBoolean("tablet_article_swipe", false) && !isSmallScreen()) {
- m_menu.findItem(R.id.share_article).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- }
- }
- }
-
- } else if (m_activeFeed != null) {
- m_menu.setGroupVisible(R.id.menu_group_headlines, true);
- m_menu.findItem(R.id.close_feed).setVisible(!isSmallScreen());
-
- MenuItem search = m_menu.findItem(R.id.search);
-
- search.setEnabled(m_apiLevel >= 2);
-
- if (!isCompatMode()) {
- SearchView searchView = (SearchView) search.getActionView();
- searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- private String query = "";
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- HeadlinesFragment frag = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (frag != null) {
- frag.setSearchQuery(query);
- this.query = query;
- }
-
- return false;
- }
-
- @Override
- public boolean onQueryTextChange(String newText) {
- if (newText.equals("") && !newText.equals(this.query)) {
- HeadlinesFragment frag = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (frag != null) {
- frag.setSearchQuery(newText);
- this.query = newText;
- }
- }
-
- return false;
- }
- });
- }
-
- } else {
- m_menu.setGroupVisible(R.id.menu_group_feeds, true);
- }
-
- if (numSelected == 0 && m_headlinesActionMode != null) {
- m_headlinesActionMode.finish();
- }
-
- //Log.d(TAG, "isCompatMode=" + isCompatMode());
-
-
- m_menu.findItem(R.id.set_labels).setEnabled(m_apiLevel >= 1);
- m_menu.findItem(R.id.article_set_note).setEnabled(m_apiLevel >= 1);
-
- } else {
- m_menu.setGroupVisible(R.id.menu_group_logged_in, false);
- m_menu.setGroupVisible(R.id.menu_group_logged_out, true);
- }
- }
-
- updateTitle();
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- if (m_refreshTask != null) {
- m_refreshTask.cancel();
- m_refreshTask = null;
- }
-
- if (m_refreshTimer != null) {
- m_refreshTimer.cancel();
- m_refreshTimer = null;
- }
-
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- unregisterReceiver(m_broadcastReceiver);
- }
-
- private void syncOfflineData() {
- Log.d(TAG, "offlineSync: starting");
-
- Intent intent = new Intent(
- MainActivity.this,
- OfflineUploadService.class);
-
- intent.putExtra("sessionId", m_sessionId);
-
- startService(intent);
- }
-
- private void loginSuccess() {
- findViewById(R.id.loading_container).setVisibility(View.GONE);
- setProgressBarIndeterminateVisibility(false);
-
- m_isOffline = false;
-
- initMainMenu();
-
- if (m_refreshTask != null) {
- m_refreshTask.cancel();
- m_refreshTask = null;
- }
-
- if (m_refreshTimer != null) {
- m_refreshTimer.cancel();
- m_refreshTimer = null;
- }
-
- m_refreshTask = new RefreshTask();
- m_refreshTimer = new Timer("Refresh");
-
- m_refreshTimer.schedule(m_refreshTask, 60 * 1000L, 120 * 1000L);
- }
-
-
- private class LoginRequest extends ApiRequest {
- public LoginRequest(Context context) {
- super(context);
- }
-
- @SuppressWarnings("unchecked")
- protected void onPostExecute(JsonElement result) {
- m_isLoggingIn = false;
-
- if (result != null) {
- try {
- JsonObject content = result.getAsJsonObject();
- if (content != null) {
- m_sessionId = content.get("session_id").getAsString();
-
- Log.d(TAG, "Authenticated!");
-
- ApiRequest req = new ApiRequest(m_context) {
- protected void onPostExecute(JsonElement result) {
- m_apiLevel = 0;
-
- if (result != null) {
- try {
- m_apiLevel = result.getAsJsonObject()
- .get("level").getAsInt();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- Log.d(TAG, "Received API level: " + m_apiLevel);
-
- if (hasPendingOfflineData())
- syncOfflineData();
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (m_enableCats) {
- FeedCategoriesFragment frag = new FeedCategoriesFragment();
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, frag, FRAG_CATS);
- } else {
- ft.replace(R.id.feeds_fragment, frag, FRAG_CATS);
- }
-
- } else {
- FeedsFragment frag = new FeedsFragment();
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, frag, FRAG_FEEDS);
- } else {
- ft.replace(R.id.feeds_fragment, frag, FRAG_FEEDS);
- }
- }
-
- try {
- ft.commit();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- }
-
- loginSuccess();
-
- }
- };
-
- @SuppressWarnings("serial")
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("sid", m_sessionId);
- put("op", "getApiLevel");
- }
- };
-
- req.execute(map);
-
- setLoadingStatus(R.string.loading_message, true);
-
- return;
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- m_sessionId = null;
-
- setLoadingStatus(getErrorMessage(), false);
-
- if (hasOfflineData()) {
-
- AlertDialog.Builder builder = new AlertDialog.Builder(
- MainActivity.this)
- .setMessage(R.string.dialog_offline_prompt)
- .setPositiveButton(R.string.dialog_offline_go,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
- switchOfflineSuccess();
- }
- })
- .setNegativeButton(R.string.dialog_cancel,
- new Dialog.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
- //
- }
- });
-
- AlertDialog dlg = builder.create();
- dlg.show();
- }
-
- // m_menu.findItem(R.id.login).setVisible(true);
- }
-
- }
-
- @Override
- public void onFeedSelected(Feed feed) {
- viewFeed(feed, false);
- }
-
- public void viewFeed(Feed feed, boolean append) {
- Log.d(TAG, "viewFeeed/" + feed.id);
-
- m_activeFeed = feed;
-
- if (!append) {
- //m_selectedArticle = null;
-
- if (m_menu != null) {
- MenuItem search = m_menu.findItem(R.id.search);
-
- if (search != null && !isCompatMode()) {
- SearchView sv = (SearchView) search.getActionView();
- sv.setQuery("", false);
- }
- }
-
- HeadlinesFragment hf = new HeadlinesFragment(feed);
-
- FragmentTransaction ft = getSupportFragmentManager()
- .beginTransaction();
-
- if (isSmallScreen()) {
- Fragment cats = getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
- if (cats != null) ft.hide(cats);
-
- Fragment feeds = getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
- if (feeds != null) ft.hide(feeds);
-
- ft.add(R.id.fragment_container, hf, FRAG_HEADLINES);
- } else {
- //findViewById(R.id.article_fragment).setVisibility(View.GONE);
- findViewById(R.id.headlines_fragment).setVisibility(View.VISIBLE);
- ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
- }
- ft.commit();
-
- } else {
- HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
- if (hf != null) {
- hf.refresh(true);
- }
- }
-
- initMainMenu();
- }
-
- public void viewCategory(FeedCategory cat, boolean openAsFeed) {
-
- Log.d(TAG, "viewCategory");
-
- if (!openAsFeed) {
- m_activeCategory = cat;
-
- FeedsFragment frag = new FeedsFragment(cat);
-
- FragmentTransaction ft = getSupportFragmentManager()
- .beginTransaction();
-
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, frag, FRAG_FEEDS);
- } else {
- ft.replace(R.id.feeds_fragment, frag, FRAG_FEEDS);
- }
- ft.commit();
-
- } else {
- Feed feed = new Feed(cat.id, cat.title, true);
-
- if (m_menu != null) {
- MenuItem search = m_menu.findItem(R.id.search);
-
- if (search != null && !isCompatMode()) {
- SearchView sv = (SearchView) search.getActionView();
- sv.setQuery("", false);
- }
- }
- viewFeed(feed, false);
- }
-
- initMainMenu();
- }
-
- @Override
- public void onArticleSelected(Article article) {
- openArticle(article);
- }
-
- public void openArticle(Article article) {
- m_selectedArticle = article;
-
- if (article.unread) {
- article.unread = false;
- saveArticleUnread(article);
- }
-
- initMainMenu();
-
- Fragment frag;
-
- if (isSmallScreen() || m_prefs.getBoolean("tablet_article_swipe", false)) {
- frag = new ArticlePager(article);
- } else {
- frag = new ArticleFragment(article);
- }
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- if (isSmallScreen()) {
- ft.hide(getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES));
- ft.add(R.id.fragment_container, frag, FRAG_ARTICLE);
- } else {
- findViewById(R.id.feeds_fragment).setVisibility(isPortrait() || isSmallTablet() ? View.GONE : View.VISIBLE);
- findViewById(R.id.article_fragment).setVisibility(View.VISIBLE);
- ft.replace(R.id.article_fragment, frag, FRAG_ARTICLE);
-
- if (!isPortrait()) refresh();
- }
- ft.commit();
- }
-
- /* private Feed getActiveFeed() {
- return m_activeFeed;
- }
-
- private FeedCategory getActiveCategory() {
- return m_activeCategory;
- } */
-
- private void logout() {
- if (m_refreshTask != null) {
- m_refreshTask.cancel();
- m_refreshTask = null;
- }
-
- if (m_refreshTimer != null) {
- m_refreshTimer.cancel();
- m_refreshTimer = null;
- }
-
- m_sessionId = null;
-
- findViewById(R.id.loading_container).setVisibility(View.VISIBLE);
-
- TextView tv = (TextView) findViewById(R.id.loading_message);
-
- if (tv != null) {
- tv.setText(R.string.login_ready);
- }
-
- initMainMenu();
- }
-
- @Override
- @SuppressWarnings({ "unchecked", "serial" })
- public void login() {
-
- logout();
-
- if (m_prefs.getString("ttrss_url", "").trim().length() == 0) {
-
- setLoadingStatus(R.string.login_need_configure, false);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setMessage(R.string.dialog_need_configure_prompt)
- .setCancelable(false)
- .setPositiveButton(R.string.dialog_open_preferences, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- // launch preferences
-
- Intent intent = new Intent(MainActivity.this,
- PreferencesActivity.class);
- startActivityForResult(intent, 0);
- }
- })
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- });
- AlertDialog alert = builder.create();
- alert.show();
-
- } else {
-
- LoginRequest ar = new LoginRequest(getApplicationContext());
-
- HashMap<String, String> map = new HashMap<String, String>() {
- {
- put("op", "login");
- put("user", m_prefs.getString("login", "").trim());
- put("password", m_prefs.getString("password", "").trim());
- }
- };
-
- ar.execute(map);
-
- setLoadingStatus(R.string.login_in_progress, true);
-
- m_isLoggingIn = true;
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
- .getMenuInfo();
-
- HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
- FeedsFragment ff = (FeedsFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_FEEDS);
- FeedCategoriesFragment cf = (FeedCategoriesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_CATS);
-
- switch (item.getItemId()) {
- case R.id.article_link_copy:
- if (true) {
- Article article = null;
-
- if (m_selectedArticle != null) {
- article = m_selectedArticle;
- } else if (info != null) {
- article = hf.getArticleAtPosition(info.position);
- }
-
- if (article != null) {
- copyToClipboard(article.link);
- }
- }
- return true;
- case R.id.article_link_share:
- if (m_selectedArticle != null) {
- shareArticle(m_selectedArticle);
- }
- return true;
- case R.id.set_labels:
- if (true) {
- Article article = null;
-
- if (m_selectedArticle != null) {
- article = m_selectedArticle;
- } else if (info != null) {
- article = hf.getArticleAtPosition(info.position);
- }
-
- if (article != null) {
- editArticleLabels(article);
- }
- }
- return true;
- case R.id.article_set_note:
- if (true) {
- Article article = null;
-
- if (m_selectedArticle != null) {
- article = m_selectedArticle;
- } else if (info != null) {
- article = hf.getArticleAtPosition(info.position);
- }
-
- if (article != null) {
- editArticleNote(article);
- }
- }
- return true;
- case R.id.browse_articles:
- if (cf != null) {
- FeedCategory cat = cf.getCategoryAtPosition(info.position);
- if (cat != null) {
- viewCategory(cat, true);
- cf.setSelectedCategory(cat);
- }
- }
- return true;
- case R.id.browse_feeds:
- if (cf != null) {
- FeedCategory cat = cf.getCategoryAtPosition(info.position);
- if (cat != null) {
- viewCategory(cat, false);
- cf.setSelectedCategory(cat);
- }
- }
- return true;
- case R.id.catchup_category:
- if (cf != null) {
- FeedCategory cat = cf.getCategoryAtPosition(info.position);
- if (cat != null) {
- catchupFeed(new Feed(cat.id, cat.title, true));
- }
- }
- return true;
- case R.id.catchup_feed:
- if (ff != null) {
- Feed feed = ff.getFeedAtPosition(info.position);
- if (feed != null) {
- catchupFeed(feed);
- }
- }
- return true;
- case R.id.selection_toggle_marked:
- if (hf != null) {
- ArticleList selected = hf.getSelectedArticles();
-
- if (selected.size() > 0) {
- for (Article a : selected)
- a.marked = !a.marked;
-
- toggleArticlesMarked(selected);
- updateHeadlines();
- } else {
- Article article = hf.getArticleAtPosition(info.position);
- if (article != null) {
- article.marked = !article.marked;
- saveArticleMarked(article);
- updateHeadlines();
- }
- }
- }
- return true;
- case R.id.selection_toggle_published:
- if (hf != null) {
- ArticleList selected = hf.getSelectedArticles();
-
- if (selected.size() > 0) {
- for (Article a : selected)
- a.published = !a.published;
-
- toggleArticlesPublished(selected);
- updateHeadlines();
- } else {
- Article article = hf.getArticleAtPosition(info.position);
- if (article != null) {
- article.published = !article.published;
- saveArticlePublished(article);
- updateHeadlines();
- }
- }
- }
- return true;
- case R.id.selection_toggle_unread:
- if (hf != null) {
- ArticleList selected = hf.getSelectedArticles();
-
- if (selected.size() > 0) {
- for (Article a : selected)
- a.unread = !a.unread;
-
- toggleArticlesUnread(selected);
- updateHeadlines();
- } else {
- Article article = hf.getArticleAtPosition(info.position);
- if (article != null) {
- article.unread = !article.unread;
- saveArticleUnread(article);
- updateHeadlines();
- }
- }
- }
- return true;
- case R.id.share_article:
- if (hf != null) {
- Article article = hf.getArticleAtPosition(info.position);
- if (article != null)
- shareArticle(article);
- }
- return true;
- case R.id.catchup_above:
- if (hf != null) {
- Article article = hf.getArticleAtPosition(info.position);
- if (article != null) {
- ArticleList articles = hf.getAllArticles();
- ArticleList tmp = new ArticleList();
- for (Article a : articles) {
- a.unread = false;
- tmp.add(a);
- if (article.id == a.id)
- break;
- }
- if (tmp.size() > 0) {
- toggleArticlesUnread(tmp);
- updateHeadlines();
- }
- }
- }
- return true;
- /*
- * case R.id.set_unread: if (hf != null) { Article article =
- * hf.getArticleAtPosition(info.position); if (article != null) {
- * article.unread = true; saveArticleUnread(article); } } break;
- */
- default:
- Log.d(TAG,
- "onContextItemSelected, unhandled id=" + item.getItemId());
- return super.onContextItemSelected(item);
- }
- }
-
- private Article getRelativeArticle(Article article, RelativeArticle ra) {
- HeadlinesFragment frag = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
- if (frag != null) {
- ArticleList articles = frag.getAllArticles();
- for (int i = 0; i < articles.size(); i++) {
- Article a = articles.get(i);
-
- if (a.id == article.id) {
- if (ra == RelativeArticle.AFTER) {
- try {
- return articles.get(i + 1);
- } catch (IndexOutOfBoundsException e) {
- return null;
- }
- } else {
- try {
- return articles.get(i - 1);
- } catch (IndexOutOfBoundsException e) {
- return null;
- }
- }
- }
- }
- }
- return null;
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- int action = event.getAction();
- int keyCode = event.getKeyCode();
- switch (keyCode) {
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- if (action == KeyEvent.ACTION_DOWN) {
- HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (hf != null && m_activeFeed != null) {
- Article base = hf.getActiveArticle();
-
- Article next = base != null ? getRelativeArticle(base,
- RelativeArticle.AFTER) : hf.getArticleAtPosition(0);
-
- if (next != null) {
- hf.setActiveArticle(next);
-
- boolean combinedMode = m_prefs.getBoolean(
- "combined_mode", false);
-
- if (combinedMode || m_selectedArticle == null) {
- next.unread = false;
- saveArticleUnread(next);
- } else {
- openArticle(next);
- }
- }
- }
- }
- return true;
- case KeyEvent.KEYCODE_VOLUME_UP:
- if (action == KeyEvent.ACTION_UP) {
- HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (hf != null && m_activeFeed != null) {
- Article base = hf.getActiveArticle();
-
- Article prev = base != null ? getRelativeArticle(base,
- RelativeArticle.BEFORE) : hf
- .getArticleAtPosition(0);
-
- if (prev != null) {
- hf.setActiveArticle(prev);
-
- boolean combinedMode = m_prefs.getBoolean(
- "combined_mode", false);
-
- if (combinedMode || m_selectedArticle == null) {
- prev.unread = false;
- saveArticleUnread(prev);
- } else {
- openArticle(prev);
- }
- }
- }
-
- }
- return true;
- default:
- return super.dispatchKeyEvent(event);
- }
- }
-
- @Override
- public void onCatSelected(FeedCategory cat) {
- Log.d(TAG, "onCatSelected");
- boolean browse = m_prefs.getBoolean("browse_cats_like_feeds", false);
-
- viewCategory(cat, browse && cat.id >= 0);
- }
-
- @Override
- public void setSelectedArticle(Article article) {
- m_selectedArticle = article;
- updateHeadlines();
- initMainMenu();
- }
-
- public void restart() {
- Intent refresh = new Intent(MainActivity.this, MainActivity.class);
- refresh.putExtra("sessionId", m_sessionId);
- startActivity(refresh);
- finish();
- }
-
- @Override
- public void onArticleListSelectionChange(ArticleList selection) {
- initMainMenu();
- }
-} \ No newline at end of file
diff --git a/src/org/fox/ttrss/OnlineActivity.java b/src/org/fox/ttrss/OnlineActivity.java
new file mode 100644
index 00000000..212c4510
--- /dev/null
+++ b/src/org/fox/ttrss/OnlineActivity.java
@@ -0,0 +1,1283 @@
+package org.fox.ttrss;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+
+import org.fox.ttrss.offline.OfflineActivity;
+import org.fox.ttrss.offline.OfflineDownloadService;
+import org.fox.ttrss.offline.OfflineUploadService;
+import org.fox.ttrss.types.Article;
+import org.fox.ttrss.types.ArticleList;
+import org.fox.ttrss.types.Feed;
+import org.fox.ttrss.types.Label;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnMultiChoiceClickListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.EditText;
+import android.widget.SearchView;
+import android.widget.ShareActionProvider;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+public class OnlineActivity extends CommonActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ protected SharedPreferences m_prefs;
+ protected Menu m_menu;
+
+ protected int m_offlineModeStatus = 0;
+
+ private ActionMode m_headlinesActionMode;
+ private HeadlinesActionModeCallback m_headlinesActionModeCallback;
+
+ private BroadcastReceiver m_broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context content, Intent intent) {
+
+ if (intent.getAction().equals(OfflineDownloadService.INTENT_ACTION_SUCCESS)) {
+
+ m_offlineModeStatus = 2;
+
+ switchOffline();
+
+ } else if (intent.getAction().equals(OfflineUploadService.INTENT_ACTION_SUCCESS)) {
+ Log.d(TAG, "offline upload service reports success");
+ toast(R.string.offline_sync_success);
+ }
+ }
+ };
+
+
+ @TargetApi(11)
+ private class HeadlinesActionModeCallback implements ActionMode.Callback {
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ if (hf != null) {
+ ArticleList selected = hf.getSelectedArticles();
+ if (selected.size() > 0) {
+ selected.clear();
+ initMenu();
+ hf.notifyUpdated();
+ }
+ }
+
+ m_headlinesActionMode = null;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.headlines_action_menu, menu);
+
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ onOptionsItemSelected(item);
+ return false;
+ }
+ };
+
+ protected String getSessionId() {
+ return GlobalState.getInstance().m_sessionId;
+ }
+
+ protected void setSessionId(String sessionId) {
+ GlobalState.getInstance().m_sessionId = sessionId;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ m_prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
+ setTheme(R.style.DarkTheme);
+ } else {
+ setTheme(R.style.LightTheme);
+ }
+
+ super.onCreate(savedInstanceState);
+
+ if (canUseProgress()) {
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+ } else {
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ }
+
+ setProgressBarVisibility(false);
+ setProgressBarIndeterminateVisibility(false);
+
+// SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE);
+
+ SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE);
+
+ boolean isOffline = localPrefs.getBoolean("offline_mode_active", false);
+
+ Log.d(TAG, "m_isOffline=" + isOffline);
+
+ setContentView(R.layout.login);
+
+ if (isOffline) {
+ switchOfflineSuccess();
+ } else {
+
+ /* if (getIntent().getExtras() != null) {
+ Intent i = getIntent();
+ } */
+
+ if (savedInstanceState != null) {
+ m_offlineModeStatus = savedInstanceState.getInt("offlineModeStatus");
+ }
+
+ if (!isCompatMode()) {
+ m_headlinesActionModeCallback = new HeadlinesActionModeCallback();
+ }
+ }
+ }
+
+ protected boolean canUseProgress() {
+ return GlobalState.getInstance().m_canUseProgress;
+ }
+
+ private void switchOffline() {
+ if (m_offlineModeStatus == 2) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ OnlineActivity.this)
+ .setMessage(R.string.dialog_offline_success)
+ .setPositiveButton(R.string.dialog_offline_go,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ m_offlineModeStatus = 0;
+
+ SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = localPrefs.edit();
+ editor.putBoolean("offline_mode_active", true);
+ editor.commit();
+
+ Intent offline = new Intent(
+ OnlineActivity.this,
+ OfflineActivity.class);
+ offline.putExtra("initial", true);
+ startActivity(offline);
+ finish();
+ }
+ })
+ .setNegativeButton(R.string.dialog_cancel,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ m_offlineModeStatus = 0;
+
+ }
+ });
+
+ AlertDialog dlg = builder.create();
+ dlg.show();
+
+ } else if (m_offlineModeStatus == 0) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setMessage(R.string.dialog_offline_switch_prompt)
+ .setPositiveButton(R.string.dialog_offline_go,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ if (getSessionId() != null) {
+ Log.d(TAG, "offline: starting");
+
+ m_offlineModeStatus = 1;
+
+ Intent intent = new Intent(
+ OnlineActivity.this,
+ OfflineDownloadService.class);
+ intent.putExtra("sessionId", getSessionId());
+
+ startService(intent);
+ }
+ }
+ })
+ .setNegativeButton(R.string.dialog_cancel,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ //
+ }
+ });
+
+ AlertDialog dlg = builder.create();
+ dlg.show();
+ } else if (m_offlineModeStatus == 1) {
+ cancelOfflineSync();
+ }
+ }
+
+ private boolean hasPendingOfflineData() {
+ try {
+ Cursor c = getReadableDb().query("articles",
+ new String[] { "COUNT(*)" }, "modified = 1", null, null, null,
+ null);
+ if (c.moveToFirst()) {
+ int modified = c.getInt(0);
+ c.close();
+
+ return modified > 0;
+ }
+ } catch (IllegalStateException e) {
+ // db is closed? ugh
+ }
+
+ return false;
+ }
+
+ private boolean hasOfflineData() {
+ try {
+ Cursor c = getReadableDb().query("articles",
+ new String[] { "COUNT(*)" }, null, null, null, null, null);
+ if (c.moveToFirst()) {
+ int modified = c.getInt(0);
+ c.close();
+
+ return modified > 0;
+ }
+ } catch (IllegalStateException e) {
+ // db is closed?
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ unregisterReceiver(m_broadcastReceiver);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ private void syncOfflineData() {
+ Log.d(TAG, "offlineSync: starting");
+
+ Intent intent = new Intent(
+ OnlineActivity.this,
+ OfflineUploadService.class);
+
+ intent.putExtra("sessionId", getSessionId());
+
+ startService(intent);
+ }
+
+ private void cancelOfflineSync() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setMessage(R.string.dialog_offline_sync_in_progress)
+ .setNegativeButton(R.string.dialog_offline_sync_stop,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ if (getSessionId() != null) {
+ Log.d(TAG, "offline: stopping");
+
+ m_offlineModeStatus = 0;
+
+ Intent intent = new Intent(
+ OnlineActivity.this,
+ OfflineDownloadService.class);
+
+ stopService(intent);
+
+ dialog.dismiss();
+
+ restart();
+ }
+ }
+ })
+ .setPositiveButton(R.string.dialog_offline_sync_continue,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ dialog.dismiss();
+
+ restart();
+ }
+ });
+
+ AlertDialog dlg = builder.create();
+ dlg.show();
+ }
+
+ public void restart() {
+ Intent refresh = new Intent(OnlineActivity.this, OnlineActivity.class);
+ startActivity(refresh);
+ finish();
+ }
+
+ private void switchOfflineSuccess() {
+ logout();
+ // setLoadingStatus(R.string.blank, false);
+
+ SharedPreferences.Editor editor = m_prefs.edit();
+ editor.putBoolean("offline_mode_active", true);
+ editor.commit();
+
+ Intent offline = new Intent(OnlineActivity.this, OfflineActivity.class);
+ offline.putExtra("initial", true);
+ offline.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+ startActivityForResult(offline, 0);
+
+ finish();
+
+ }
+
+ public void login() {
+ if (m_prefs.getString("ttrss_url", "").trim().length() == 0) {
+
+ setLoadingStatus(R.string.login_need_configure, false);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage(R.string.dialog_need_configure_prompt)
+ .setCancelable(false)
+ .setPositiveButton(R.string.dialog_open_preferences, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // launch preferences
+
+ Intent intent = new Intent(OnlineActivity.this,
+ PreferencesActivity.class);
+ startActivityForResult(intent, 0);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ } else {
+
+ LoginRequest ar = new LoginRequest(getApplicationContext());
+
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("op", "login");
+ put("user", m_prefs.getString("login", "").trim());
+ put("password", m_prefs.getString("password", "").trim());
+ }
+ };
+
+ ar.execute(map);
+
+ setLoadingStatus(R.string.login_in_progress, true);
+ }
+ }
+
+ protected void loginSuccess() {
+ setLoadingStatus(R.string.blank, false);
+ findViewById(R.id.loading_container).setVisibility(View.GONE);
+
+ initMenu();
+
+ Intent intent = new Intent(OnlineActivity.this, FeedsActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+ startActivityForResult(intent, 0);
+
+ if (hasPendingOfflineData())
+ syncOfflineData();
+
+ List<PackageInfo> pkgs = getPackageManager()
+ .getInstalledPackages(0);
+
+ for (PackageInfo p : pkgs) {
+ if ("org.fox.ttrss.key".equals(p.packageName)) {
+ toast(R.string.donate_thanks);
+ break;
+ }
+ }
+
+ finish();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+ final ArticlePager ap = (ArticlePager)getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ case R.id.donate:
+ if (true) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://details?id=org.fox.ttrss.key"));
+ startActivity(intent);
+ } catch (ActivityNotFoundException ae) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("https://play.google.com/store/apps/details?id=org.fox.ttrss.key"));
+ startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ toast(R.string.error_other_error);
+ }
+ }
+ }
+ return true;
+ case R.id.logout:
+ logout();
+ return true;
+ case R.id.login:
+ login();
+ return true;
+ case R.id.go_offline:
+ switchOffline();
+ return true;
+ case R.id.article_set_note:
+ if (ap != null && ap.getSelectedArticle() != null) {
+ editArticleNote(ap.getSelectedArticle());
+ }
+ return true;
+ case R.id.preferences:
+ Intent intent = new Intent(OnlineActivity.this,
+ PreferencesActivity.class);
+ startActivityForResult(intent, 0);
+ return true;
+ case R.id.search:
+ if (hf != null && isCompatMode()) {
+ Dialog dialog = new Dialog(this);
+
+ final EditText edit = new EditText(this);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.search)
+ .setPositiveButton(getString(R.string.search),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ String query = edit.getText().toString().trim();
+
+ hf.setSearchQuery(query);
+
+ }
+ })
+ .setNegativeButton(getString(R.string.cancel),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ //
+
+ }
+ }).setView(edit);
+
+ dialog = builder.create();
+ dialog.show();
+ }
+ return true;
+ case R.id.headlines_mark_as_read:
+ if (hf != null) {
+ ArticleList articles = hf.getUnreadArticles();
+
+ for (Article a : articles)
+ a.unread = false;
+
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ protected void onPostExecute(JsonElement result) {
+ hf.refresh(false);
+ }
+ };
+
+ final String articleIds = articlesToIdString(articles);
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", articleIds);
+ put("mode", "0");
+ put("field", "2");
+ }
+ };
+ req.execute(map);
+ }
+ return true;
+ case R.id.headlines_select:
+ if (hf != null) {
+ Dialog dialog = new Dialog(this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.headlines_select_dialog)
+ .setSingleChoiceItems(
+ new String[] {
+ getString(R.string.headlines_select_all),
+ getString(R.string.headlines_select_unread),
+ getString(R.string.headlines_select_none) },
+ 0, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ switch (which) {
+ case 0:
+ hf.setSelection(HeadlinesFragment.ArticlesSelection.ALL);
+ break;
+ case 1:
+ hf.setSelection(HeadlinesFragment.ArticlesSelection.UNREAD);
+ break;
+ case 2:
+ hf.setSelection(HeadlinesFragment.ArticlesSelection.NONE);
+ break;
+ }
+ dialog.cancel();
+ initMenu();
+ }
+ });
+
+ dialog = builder.create();
+ dialog.show();
+ }
+ return true;
+ case R.id.share_article:
+ if (android.os.Build.VERSION.SDK_INT < 14) {
+ if (ap != null) {
+ shareArticle(ap.getSelectedArticle());
+ }
+ }
+ return true;
+ case R.id.toggle_marked:
+ if (ap != null & ap.getSelectedArticle() != null) {
+ Article a = ap.getSelectedArticle();
+ a.marked = !a.marked;
+ saveArticleMarked(a);
+ if (hf != null) hf.notifyUpdated();
+ }
+ return true;
+ case R.id.selection_select_none:
+ if (hf != null) {
+ ArticleList selected = hf.getSelectedArticles();
+ if (selected.size() > 0) {
+ selected.clear();
+ initMenu();
+ hf.notifyUpdated();
+ }
+ }
+ return true;
+ case R.id.selection_toggle_unread:
+ if (hf != null) {
+ ArticleList selected = hf.getSelectedArticles();
+
+ if (selected.size() > 0) {
+ for (Article a : selected)
+ a.unread = !a.unread;
+
+ toggleArticlesUnread(selected);
+ hf.notifyUpdated();
+ }
+ }
+ return true;
+ case R.id.selection_toggle_marked:
+ if (hf != null) {
+ ArticleList selected = hf.getSelectedArticles();
+
+ if (selected.size() > 0) {
+ for (Article a : selected)
+ a.marked = !a.marked;
+
+ toggleArticlesMarked(selected);
+ hf.notifyUpdated();
+ }
+ }
+ return true;
+ case R.id.selection_toggle_published:
+ if (hf != null) {
+ ArticleList selected = hf.getSelectedArticles();
+
+ if (selected.size() > 0) {
+ for (Article a : selected)
+ a.published = !a.published;
+
+ toggleArticlesPublished(selected);
+ hf.notifyUpdated();
+ }
+ }
+ return true;
+ case R.id.toggle_published:
+ if (ap != null && ap.getSelectedArticle() != null) {
+ Article a = ap.getSelectedArticle();
+ a.published = !a.published;
+ saveArticlePublished(a);
+ if (hf != null) hf.notifyUpdated();
+ }
+ return true;
+ case R.id.catchup_above:
+ if (hf != null) {
+ if (ap != null && ap.getSelectedArticle() != null) {
+ Article article = ap.getSelectedArticle();
+
+ ArticleList articles = hf.getAllArticles();
+ ArticleList tmp = new ArticleList();
+ for (Article a : articles) {
+ a.unread = false;
+ tmp.add(a);
+ if (article.id == a.id)
+ break;
+ }
+ if (tmp.size() > 0) {
+ toggleArticlesUnread(tmp);
+ hf.notifyUpdated();
+ }
+ }
+ }
+ return true;
+ case R.id.set_unread:
+ if (ap != null && ap.getSelectedArticle() != null) {
+ Article a = ap.getSelectedArticle();
+ a.unread = true;
+ saveArticleUnread(a);
+ if (hf != null) hf.notifyUpdated();
+ }
+ return true;
+ case R.id.set_labels:
+ if (ap != null && ap.getSelectedArticle() != null) {
+ editArticleLabels(ap.getSelectedArticle());
+ }
+ return true;
+ case R.id.update_headlines:
+ refresh();
+ return true;
+ default:
+ Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ public void editArticleNote(final Article article) {
+ String note = "";
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(article.title);
+ final EditText topicEdit = new EditText(this);
+ topicEdit.setText(note);
+ builder.setView(topicEdit);
+
+ builder.setPositiveButton(R.string.article_set_note, new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ saveArticleNote(article, topicEdit.getText().toString().trim());
+ article.published = true;
+ saveArticlePublished(article);
+
+ HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+ if (hf != null) hf.notifyUpdated();
+ }
+ });
+
+ builder.setNegativeButton(R.string.dialog_cancel, new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ //
+ }
+ });
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ public void editArticleLabels(Article article) {
+ final int articleId = article.id;
+
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ @Override
+ protected void onPostExecute(JsonElement result) {
+ if (result != null) {
+ Type listType = new TypeToken<List<Label>>() {}.getType();
+ final List<Label> labels = new Gson().fromJson(result, listType);
+
+ CharSequence[] items = new CharSequence[labels.size()];
+ final int[] itemIds = new int[labels.size()];
+ boolean[] checkedItems = new boolean[labels.size()];
+
+ for (int i = 0; i < labels.size(); i++) {
+ items[i] = labels.get(i).caption;
+ itemIds[i] = labels.get(i).id;
+ checkedItems[i] = labels.get(i).checked;
+ }
+
+ Dialog dialog = new Dialog(OnlineActivity.this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(OnlineActivity.this)
+ .setTitle(R.string.article_set_labels)
+ .setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which, final boolean isChecked) {
+ final int labelId = itemIds[which];
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "setArticleLabel");
+ put("label_id", String.valueOf(labelId));
+ put("article_ids", String.valueOf(articleId));
+ if (isChecked) put("assign", "true");
+ }
+ };
+
+ ApiRequest req = new ApiRequest(m_context);
+ req.execute(map);
+
+ }
+ }).setPositiveButton(R.string.dialog_close, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ dialog = builder.create();
+ dialog.show();
+
+ }
+ }
+ };
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "getLabels");
+ put("article_id", String.valueOf(articleId));
+ }
+ };
+
+ req.execute(map);
+ }
+
+ protected void logout() {
+ setSessionId(null);
+
+ findViewById(R.id.loading_container).setVisibility(View.VISIBLE);
+ setLoadingStatus(R.string.login_ready, false);
+
+ initMenu();
+ }
+
+ protected void loginFailure() {
+ setSessionId(null);
+ initMenu();
+
+ if (hasOfflineData()) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ OnlineActivity.this)
+ .setMessage(R.string.dialog_offline_prompt)
+ .setPositiveButton(R.string.dialog_offline_go,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ switchOfflineSuccess();
+ }
+ })
+ .setNegativeButton(R.string.dialog_cancel,
+ new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ //
+ }
+ });
+
+ AlertDialog dlg = builder.create();
+ dlg.show();
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putInt("offlineModeStatus", m_offlineModeStatus);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(OfflineDownloadService.INTENT_ACTION_SUCCESS);
+ filter.addAction(OfflineUploadService.INTENT_ACTION_SUCCESS);
+ filter.addCategory(Intent.CATEGORY_DEFAULT);
+
+ registerReceiver(m_broadcastReceiver, filter);
+
+ if (getSessionId() == null) {
+ login();
+ } else {
+ loginSuccess();
+ }
+ }
+
+ public Menu getMenu() {
+ return m_menu;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main_menu, menu);
+
+ m_menu = menu;
+
+ initMenu();
+
+ List<PackageInfo> pkgs = getPackageManager()
+ .getInstalledPackages(0);
+
+ for (PackageInfo p : pkgs) {
+ if ("org.fox.ttrss.key".equals(p.packageName)) {
+ Log.d(TAG, "license apk found");
+ menu.findItem(R.id.donate).setVisible(false);
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ protected int getApiLevel() {
+ return GlobalState.getInstance().m_apiLevel;
+ }
+
+ protected void setApiLevel(int apiLevel) {
+ GlobalState.getInstance().m_apiLevel = apiLevel;
+ }
+
+ @SuppressWarnings({ "unchecked", "serial" })
+ public void saveArticleUnread(final Article article) {
+ ApiRequest req = new ApiRequest(getApplicationContext());
+
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", String.valueOf(article.id));
+ put("mode", article.unread ? "1" : "0");
+ put("field", "2");
+ }
+ };
+
+ req.execute(map);
+ }
+
+ @SuppressWarnings({ "unchecked", "serial" })
+ public void saveArticleMarked(final Article article) {
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ protected void onPostExecute(JsonElement result) {
+ toast(article.marked ? R.string.notify_article_marked : R.string.notify_article_unmarked);
+ }
+ };
+
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", String.valueOf(article.id));
+ put("mode", article.marked ? "1" : "0");
+ put("field", "0");
+ }
+ };
+
+ req.execute(map);
+ }
+
+ @SuppressWarnings({ "unchecked", "serial" })
+ public void saveArticlePublished(final Article article) {
+
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ protected void onPostExecute(JsonElement result) {
+ toast(article.published ? R.string.notify_article_published : R.string.notify_article_unpublished);
+ }
+ };
+
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", String.valueOf(article.id));
+ put("mode", article.published ? "1" : "0");
+ put("field", "1");
+ }
+ };
+
+ req.execute(map);
+ }
+
+ @SuppressWarnings({ "unchecked", "serial" })
+ public void saveArticleNote(final Article article, final String note) {
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ protected void onPostExecute(JsonElement result) {
+ toast(R.string.notify_article_note_set);
+ }
+ };
+
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", String.valueOf(article.id));
+ put("mode", "1");
+ put("data", note);
+ put("field", "3");
+ }
+ };
+
+ req.execute(map);
+ }
+
+ public static String articlesToIdString(ArticleList articles) {
+ String tmp = "";
+
+ for (Article a : articles)
+ tmp += String.valueOf(a.id) + ",";
+
+ return tmp.replaceAll(",$", "");
+ }
+
+ public void shareText(String text) {
+
+ Intent intent = new Intent(Intent.ACTION_SEND);
+
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_TEXT, text);
+
+ startActivity(Intent.createChooser(intent, text));
+ }
+
+ public void shareArticle(Article article) {
+ if (article != null) {
+
+ Intent intent = getShareIntent(article);
+
+ startActivity(Intent.createChooser(intent,
+ getString(R.string.share_article)));
+ }
+ }
+
+ protected Intent getShareIntent(Article article) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_SUBJECT, article.title);
+ intent.putExtra(Intent.EXTRA_TEXT, article.link);
+
+ return intent;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void catchupFeed(final Feed feed) {
+ Log.d(TAG, "catchupFeed=" + feed);
+
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ protected void onPostExecute(JsonElement result) {
+ // refresh?
+ }
+ };
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "catchupFeed");
+ put("feed_id", String.valueOf(feed.id));
+ if (feed.is_cat)
+ put("is_cat", "1");
+ }
+ };
+
+ req.execute(map);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void toggleArticlesMarked(final ArticleList articles) {
+ ApiRequest req = new ApiRequest(getApplicationContext());
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", articlesToIdString(articles));
+ put("mode", "2");
+ put("field", "0");
+ }
+ };
+
+ req.execute(map);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void toggleArticlesUnread(final ArticleList articles) {
+ ApiRequest req = new ApiRequest(getApplicationContext());
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", articlesToIdString(articles));
+ put("mode", "2");
+ put("field", "2");
+ }
+ };
+
+ req.execute(map);
+ //refresh();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void toggleArticlesPublished(final ArticleList articles) {
+ ApiRequest req = new ApiRequest(getApplicationContext());
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "updateArticle");
+ put("article_ids", articlesToIdString(articles));
+ put("mode", "2");
+ put("field", "1");
+ }
+ };
+
+ req.execute(map);
+ }
+
+
+ protected void initMenu() {
+ if (m_menu != null) {
+ if (getSessionId() != null) {
+ m_menu.setGroupVisible(R.id.menu_group_logged_in, true);
+ m_menu.setGroupVisible(R.id.menu_group_logged_out, false);
+ } else {
+ m_menu.setGroupVisible(R.id.menu_group_logged_in, false);
+ m_menu.setGroupVisible(R.id.menu_group_logged_out, true);
+ }
+
+ m_menu.setGroupVisible(R.id.menu_group_headlines, false);
+ m_menu.setGroupVisible(R.id.menu_group_headlines_selection, false);
+ m_menu.setGroupVisible(R.id.menu_group_article, false);
+ m_menu.setGroupVisible(R.id.menu_group_feeds, false);
+
+ m_menu.findItem(R.id.set_labels).setEnabled(getApiLevel() >= 1);
+ m_menu.findItem(R.id.article_set_note).setEnabled(getApiLevel() >= 1);
+
+ MenuItem search = m_menu.findItem(R.id.search);
+ search.setEnabled(getApiLevel() >= 2);
+
+ if (android.os.Build.VERSION.SDK_INT >= 14) {
+ ShareActionProvider shareProvider = (ShareActionProvider) m_menu.findItem(R.id.share_article).getActionProvider();
+
+ ArticlePager af = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+
+ if (af != null && af.getSelectedArticle() != null) {
+ shareProvider.setShareIntent(getShareIntent(af.getSelectedArticle()));
+
+ if (!isSmallScreen()) {
+ m_menu.findItem(R.id.share_article).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+ }
+ }
+
+ if (!isCompatMode()) {
+ HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ if (hf != null) {
+ if (hf.getSelectedArticles().size() > 0 && m_headlinesActionMode == null) {
+ m_headlinesActionMode = startActionMode(m_headlinesActionModeCallback);
+ } else if (hf.getSelectedArticles().size() == 0 && m_headlinesActionMode != null) {
+ m_headlinesActionMode.finish();
+ }
+ }
+
+ SearchView searchView = (SearchView) search.getActionView();
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ private String query = "";
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ HeadlinesFragment frag = (HeadlinesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_HEADLINES);
+
+ if (frag != null) {
+ frag.setSearchQuery(query);
+ this.query = query;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ if (newText.equals("") && !newText.equals(this.query)) {
+ HeadlinesFragment frag = (HeadlinesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_HEADLINES);
+
+ if (frag != null) {
+ frag.setSearchQuery(newText);
+ this.query = newText;
+ }
+ }
+
+ return false;
+ }
+ });
+ }
+ }
+ }
+
+ protected void refresh(boolean includeHeadlines) {
+ FeedCategoriesFragment cf = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
+
+ if (cf != null) {
+ cf.refresh(false);
+ }
+
+ FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
+
+ if (ff != null) {
+ ff.refresh(false);
+ }
+
+ if (includeHeadlines) {
+ HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ if (hf != null) {
+ hf.refresh(false);
+ }
+ }
+ }
+
+ protected void refresh() {
+ refresh(true);
+ }
+
+ private class LoginRequest extends ApiRequest {
+ public LoginRequest(Context context) {
+ super(context);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void onPostExecute(JsonElement result) {
+ if (result != null) {
+ try {
+ JsonObject content = result.getAsJsonObject();
+ if (content != null) {
+ setSessionId(content.get("session_id").getAsString());
+
+ GlobalState.getInstance().m_canUseProgress = m_canUseProgress;
+
+ Log.d(TAG, "Authenticated! canUseProgress=" + m_canUseProgress);
+
+ ApiRequest req = new ApiRequest(m_context) {
+ protected void onPostExecute(JsonElement result) {
+ setApiLevel(0);
+
+ if (result != null) {
+ try {
+ setApiLevel(result.getAsJsonObject().get("level").getAsInt());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ setLoadingStatus(getErrorMessage(), false);
+ loginFailure();
+ return;
+ }
+
+ Log.d(TAG, "Received API level: " + getApiLevel());
+
+ loginSuccess();
+ return;
+ }
+ };
+
+ @SuppressWarnings("serial")
+ HashMap<String, String> map = new HashMap<String, String>() {
+ {
+ put("sid", getSessionId());
+ put("op", "getApiLevel");
+ }
+ };
+
+ req.execute(map);
+
+ setLoadingStatus(R.string.loading_message, true);
+
+ return;
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ setSessionId(null);
+ setLoadingStatus(getErrorMessage(), false);
+
+ loginFailure();
+ }
+
+ }
+}
diff --git a/src/org/fox/ttrss/OnlineServices.java b/src/org/fox/ttrss/OnlineServices.java
deleted file mode 100644
index 112f9722..00000000
--- a/src/org/fox/ttrss/OnlineServices.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.fox.ttrss;
-
-import org.fox.ttrss.types.Article;
-import org.fox.ttrss.types.ArticleList;
-import org.fox.ttrss.types.Feed;
-import org.fox.ttrss.types.FeedCategory;
-
-public interface OnlineServices {
- public enum RelativeArticle { BEFORE, AFTER };
-
- public void saveArticleUnread(final Article article);
- public void saveArticleMarked(final Article article);
- public void saveArticlePublished(final Article article);
- public void setSelectedArticle(Article article);
- public boolean getUnreadArticlesOnly();
-
- public void onCatSelected(FeedCategory cat);
- public void onFeedSelected(Feed feed);
- public void onArticleSelected(Article article);
- public void onArticleListSelectionChange(ArticleList selection);
-
- //public void initMainMenu();
- public void login();
- public String getSessionId();
- public boolean isSmallScreen();
- public boolean getUnreadOnly();
- public int getApiLevel();
- public boolean isPortrait();
-
- public void copyToClipboard(String str);
-}
-
diff --git a/src/org/fox/ttrss/PreferencesActivity.java b/src/org/fox/ttrss/PreferencesActivity.java
index 017888f9..5a7a28fb 100644
--- a/src/org/fox/ttrss/PreferencesActivity.java
+++ b/src/org/fox/ttrss/PreferencesActivity.java
@@ -1,16 +1,14 @@
package org.fox.ttrss;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
-import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
public class PreferencesActivity extends PreferenceActivity {
+ @SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -19,15 +17,15 @@ public class PreferencesActivity extends PreferenceActivity {
.getDefaultSharedPreferences(getApplicationContext());
addPreferencesFromResource(R.xml.preferences);
-
+
findPreference("justify_article_text").setEnabled(!prefs.getBoolean("combined_mode", false));
- findPreference("combined_mode").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ /* findPreference("combined_mode").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
findPreference("justify_article_text").setEnabled(!newValue.toString().equals("true"));
return true;
}
- });
+ }); */
}
}
diff --git a/src/org/fox/ttrss/offline/OfflineActivity.java b/src/org/fox/ttrss/offline/OfflineActivity.java
index 4e48a4da..42f3effc 100644
--- a/src/org/fox/ttrss/offline/OfflineActivity.java
+++ b/src/org/fox/ttrss/offline/OfflineActivity.java
@@ -1,1523 +1,589 @@
-package org.fox.ttrss.offline;
-
-import java.util.ArrayList;
-
-import org.fox.ttrss.CommonActivity;
-import org.fox.ttrss.DummyFragment;
-import org.fox.ttrss.FeedCategoriesFragment;
-import org.fox.ttrss.FeedsFragment;
-import org.fox.ttrss.MainActivity;
-import org.fox.ttrss.OnlineServices;
-import org.fox.ttrss.OnlineServices.RelativeArticle;
-import org.fox.ttrss.PreferencesActivity;
-import org.fox.ttrss.R;
-
-import android.animation.LayoutTransition;
-import android.app.ActionBar;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteStatement;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.provider.BaseColumns;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.util.Log;
-import android.view.ActionMode;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.SearchView;
-import android.widget.ShareActionProvider;
-import android.widget.Toast;
-
-public class OfflineActivity extends CommonActivity implements
- OfflineServices {
- private final String TAG = this.getClass().getSimpleName();
-
- private SharedPreferences m_prefs;
- private String m_themeName = "";
- private Menu m_menu;
- private boolean m_unreadOnly = true;
- private boolean m_unreadArticlesOnly = true;
- private boolean m_enableCats = false;
-
- private int m_activeFeedId = 0;
- private boolean m_activeFeedIsCat = false;
- private int m_activeCatId = -1;
- private int m_selectedArticleId = 0;
-
- private ActionMode m_headlinesActionMode;
- private HeadlinesActionModeCallback m_headlinesActionModeCallback;
- private NavigationListener m_navigationListener;
- private NavigationAdapter m_navigationAdapter;
- private ArrayList<NavigationEntry> m_navigationEntries = new ArrayList<NavigationEntry>();
-
- private class RootNavigationEntry extends NavigationEntry {
- public RootNavigationEntry(String title) {
- super(title);
- }
-
- @Override
- public void onItemSelected() {
-
- m_activeFeedId = 0;
- m_selectedArticleId = 0;
- m_activeCatId = -1;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (isSmallScreen()) {
-
- if (m_enableCats) {
- ft.replace(R.id.fragment_container, new OfflineFeedCategoriesFragment(), FRAG_CATS);
- } else {
- ft.replace(R.id.fragment_container, new OfflineFeedsFragment(), FRAG_FEEDS);
- }
-
- Fragment hf = getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
- if (hf != null) ft.remove(hf);
-
- Fragment af = getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
- if (af != null) ft.remove(af);
-
- } else {
- if (m_enableCats) {
- ft.replace(R.id.feeds_fragment, new OfflineFeedCategoriesFragment(), FRAG_CATS);
- } else {
- ft.replace(R.id.feeds_fragment, new OfflineFeedsFragment(), FRAG_FEEDS);
- }
-
- ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
-
- findViewById(R.id.feeds_fragment).setVisibility(View.VISIBLE);
- //findViewById(R.id.article_fragment).setVisibility(View.GONE);
-
- ft.replace(R.id.article_fragment, new DummyFragment(), "");
- }
-
- ft.commit();
-
- initMainMenu();
- }
- }
-
- private class CategoryNavigationEntry extends NavigationEntry {
- int m_category = -1;
-
- public CategoryNavigationEntry(int category, String title) {
- super(title);
-
- m_category = category;
- }
-
- @Override
- public void onItemSelected() {
- m_selectedArticleId = 0;
- m_activeFeedId = 0;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (isSmallScreen()) {
-
- Fragment hf = getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
- if (hf != null) ft.remove(hf);
-
- Fragment af = getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
- if (af != null) ft.remove(af);
-
- if (m_activeFeedIsCat) {
- ft.replace(R.id.fragment_container, new OfflineFeedCategoriesFragment());
- } else {
- ft.replace(R.id.fragment_container, new OfflineFeedsFragment(m_category));
- }
-
- } else {
- ft.replace(R.id.article_fragment, new DummyFragment(), "");
-
- findViewById(R.id.feeds_fragment).setVisibility(View.VISIBLE);
- //findViewById(R.id.article_fragment).setVisibility(View.GONE);
-
- //ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
- }
-
- ft.commit();
-
- m_activeFeedId = 0;
- refreshViews();
- initMainMenu();
- }
- }
-
-
- private class FeedNavigationEntry extends NavigationEntry {
- int m_feed = 0;
-
- public FeedNavigationEntry(int feed, String title) {
- super(title);
-
- m_feed = feed;
- }
-
- @Override
- public void onItemSelected() {
-
- m_selectedArticleId = 0;
-
- if (!isSmallScreen())
- findViewById(R.id.article_fragment).setVisibility(View.GONE);
-
- viewFeed(m_feed, false);
- }
- }
-
- private abstract class NavigationEntry {
- private String title = null;
- private int timesCalled = 0;
-
- public void _onItemSelected(int position, int size) {
- Log.d(TAG, "_onItemSelected; TC=" + timesCalled + " P/S=" + position + "/" + size);
-
- if (position == size && timesCalled == 0) {
- ++timesCalled;
- } else {
- onItemSelected();
- }
- }
-
- public NavigationEntry(String title) {
- this.title = title;
- }
-
- public String toString() {
- return title;
- }
-
- public abstract void onItemSelected();
- }
-
- private class NavigationAdapter extends ArrayAdapter<NavigationEntry> {
- public NavigationAdapter(Context context, int textViewResourceId, ArrayList<NavigationEntry> items) {
- super(context, textViewResourceId, items);
- }
- }
-
- private class NavigationListener implements ActionBar.OnNavigationListener {
- @Override
- public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- Log.d(TAG, "onNavigationItemSelected: " + itemPosition);
-
- NavigationEntry entry = m_navigationAdapter.getItem(itemPosition);
- entry._onItemSelected(itemPosition, m_navigationAdapter.getCount()-1);
-
- return false;
- }
- }
-
- private class HeadlinesActionModeCallback implements ActionMode.Callback {
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- deselectAllArticles();
- m_headlinesActionMode = null;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.headlines_action_menu, menu);
-
- return true;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- onOptionsItemSelected(item);
- return false;
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- m_prefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
-
- if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
- setTheme(R.style.DarkTheme);
- } else {
- setTheme(R.style.LightTheme);
- }
-
- super.onCreate(savedInstanceState);
-
- //requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
- NotificationManager nmgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- nmgr.cancel(OfflineDownloadService.NOTIFY_DOWNLOADING);
-
- m_themeName = m_prefs.getString("theme", "THEME_DARK");
-
- if (savedInstanceState != null) {
- m_unreadOnly = savedInstanceState.getBoolean("unreadOnly");
- m_unreadArticlesOnly = savedInstanceState
- .getBoolean("unreadArticlesOnly");
- m_activeFeedId = savedInstanceState.getInt("offlineActiveFeedId");
- m_selectedArticleId = savedInstanceState.getInt("offlineArticleId");
- m_activeFeedIsCat = savedInstanceState.getBoolean("activeFeedIsCat");
- m_activeCatId = savedInstanceState.getInt("activeCatId");
- }
-
- m_enableCats = m_prefs.getBoolean("enable_cats", false);
-
- setContentView(R.layout.main);
-
- setSmallScreen(findViewById(R.id.headlines_fragment) == null);
-
- if (!isCompatMode()) {
- if (!isSmallScreen()) {
- findViewById(R.id.feeds_fragment).setVisibility(m_selectedArticleId != 0 && (isPortrait() || isSmallTablet()) ? View.GONE : View.VISIBLE);
- findViewById(R.id.article_fragment).setVisibility(m_selectedArticleId != 0 ? View.VISIBLE : View.GONE);
- }
-
- LayoutTransition transitioner = new LayoutTransition();
- ((ViewGroup) findViewById(R.id.fragment_container)).setLayoutTransition(transitioner);
-
- m_navigationAdapter = new NavigationAdapter(this, android.R.layout.simple_spinner_dropdown_item, m_navigationEntries);
-
- m_headlinesActionModeCallback = new HeadlinesActionModeCallback();
- m_navigationListener = new NavigationListener();
-
- getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- getActionBar().setListNavigationCallbacks(m_navigationAdapter, m_navigationListener);
-
- m_headlinesActionModeCallback = new HeadlinesActionModeCallback();
- }
-
- initMainMenu();
-
- findViewById(R.id.loading_container).setVisibility(View.GONE);
-
- if (isSmallScreen()) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- // temporary workaround against viewpager going a bit crazy when restoring after rotation
- if (m_selectedArticleId != 0) {
- ft.remove(getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE));
- m_selectedArticleId = 0;
- }
- ft.commit();
- }
-
- if (m_activeFeedId == 0 && !m_activeFeedIsCat) {
- FragmentTransaction ft = getSupportFragmentManager()
- .beginTransaction();
-
- Fragment frag = null;
- String tag = null;
-
- if (m_enableCats) {
- frag = new OfflineFeedCategoriesFragment();
- tag = FRAG_CATS;
- } else {
- frag = new OfflineFeedsFragment();
- tag = FRAG_FEEDS;
- }
-
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, frag, tag);
- } else {
- ft.replace(R.id.feeds_fragment, frag, tag);
- }
-
- ft.commit();
- }
-
-
- }
-
- private void switchOnline() {
- SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = localPrefs.edit();
- editor.putBoolean("offline_mode_active", false);
- editor.commit();
-
- Intent refresh = new Intent(this, MainActivity.class);
- startActivity(refresh);
- finish();
- }
-
- @Override
- public int getActiveFeedId() {
- return m_activeFeedId;
- }
-
- /* private void setLoadingStatus(int status, boolean showProgress) {
- TextView tv = (TextView) findViewById(R.id.loading_message);
-
- if (tv != null) {
- tv.setText(status);
- }
-
- setProgressBarIndeterminateVisibility(showProgress);
- } */
-
- @Override
- public void onSaveInstanceState(Bundle out) {
- super.onSaveInstanceState(out);
-
- out.putBoolean("unreadOnly", m_unreadOnly);
- out.putBoolean("unreadArticlesOnly", m_unreadArticlesOnly);
- out.putInt("offlineActiveFeedId", m_activeFeedId);
- out.putInt("offlineArticleId", m_selectedArticleId);
- out.putBoolean("activeFeedIsCat", m_activeFeedIsCat);
- out.putInt("activeCatId", m_activeCatId);
- }
-
- private void setUnreadOnly(boolean unread) {
- m_unreadOnly = unread;
-
- refreshViews();
-
- /*
- * if (!m_enableCats || m_activeCategory != null ) refreshFeeds(); else
- * refreshCategories();
- */
- }
-
- @Override
- public boolean getUnreadOnly() {
- return m_unreadOnly;
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- boolean needRefresh = !m_prefs.getString("theme", "THEME_DARK").equals(
- m_themeName)
- || m_prefs.getBoolean("enable_cats", false) != m_enableCats;
-
- if (needRefresh) {
- Intent refresh = new Intent(this, OfflineActivity.class);
- startActivity(refresh);
- finish();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.offline_menu, menu);
-
- m_menu = menu;
-
- initMainMenu();
-
- MenuItem item = menu.findItem(R.id.show_feeds);
-
- if (getUnreadOnly()) {
- item.setTitle(R.string.menu_all_feeds);
- } else {
- item.setTitle(R.string.menu_unread_feeds);
- }
-
- return true;
- }
-
- private void goBack(boolean allowQuit) {
- if (isSmallScreen()) {
- if (m_selectedArticleId != 0) {
- closeArticle();
- } else if (m_activeFeedId != 0) {
- m_activeFeedId = 0;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (m_activeFeedIsCat) {
- ft.replace(R.id.fragment_container, new OfflineFeedCategoriesFragment(), FRAG_CATS);
- } else {
- ft.replace(R.id.fragment_container, new OfflineFeedsFragment(m_activeCatId), FRAG_FEEDS);
- }
- ft.commit();
-
- refreshViews();
- initMainMenu();
- } else if (m_activeCatId != -1) {
- closeCategory();
- } else if (allowQuit) {
- finish();
- }
- } else {
- if (m_selectedArticleId != 0) {
- closeArticle();
- /* } else if (m_activeFeedId != 0) {
- m_activeFeedId = 0;
-
- OfflineFeedsFragment ff = (OfflineFeedsFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_FEEDS);
-
- OfflineFeedCategoriesFragment cf = (OfflineFeedCategoriesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_CATS);
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
- ft.commit();
-
- if (ff != null) {
- ff.setSelectedFeedId(0);
- }
-
- if (cf != null) {
- cf.setSelectedFeedId(-1);
- }
-
- refreshViews();
- initMainMenu(); */
- } else if (m_activeCatId != -1) {
- closeCategory();
- } else if (allowQuit) {
- finish();
- }
- }
- }
-
- @Override
- public void onBackPressed() {
- goBack(true);
- }
-
- /*
- * @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if
- * (keyCode == KeyEvent.KEYCODE_BACK) {
- *
- * if (isSmallScreen()) { if (m_selectedArticleId != 0) { closeArticle();
- * } else if (m_activeFeedId != 0) { if (isCompatMode()) {
- * findViewById(R.id.main).setAnimation(AnimationUtils.loadAnimation(this,
- * R.anim.slide_right)); }
- */
-
- /*
- * if (m_activeFeed != null && m_activeFeed.is_cat) {
- * findViewById(R.id.headlines_fragment).setVisibility(View.GONE);
- * findViewById(R.id.cats_fragment).setVisibility(View.VISIBLE);
- *
- * refreshCategories(); } else {
- *//*
- * findViewById(R.id.headlines_fragment).setVisibility(View.GONE);
- * findViewById(R.id.feeds_fragment).setVisibility(View.VISIBLE); //}
- * m_activeFeedId = 0; refreshViews(); initMainMenu();
- *
- * } else { finish(); } } else { if (m_selectedArticleId != 0) {
- * closeArticle(); } else { finish(); } }
- *
- * return false; } return super.onKeyDown(keyCode, event); }
- */
-
- private Cursor getArticleById(int articleId) {
- Cursor c = getReadableDb().query("articles", null,
- BaseColumns._ID + "=?",
- new String[] { String.valueOf(articleId) }, null, null, null);
-
- c.moveToFirst();
-
- return c;
- }
-
- private Cursor getFeedById(int feedId) {
- Cursor c = getReadableDb().query("feeds", null,
- BaseColumns._ID + "=?",
- new String[] { String.valueOf(feedId) }, null, null, null);
-
- c.moveToFirst();
-
- return c;
- }
-
- private Cursor getCatById(int catId) {
- Cursor c = getReadableDb().query("categories", null,
- BaseColumns._ID + "=?",
- new String[] { String.valueOf(catId) }, null, null, null);
-
- c.moveToFirst();
-
- return c;
- }
-
- private Intent getShareIntent(Cursor article) {
- String title = article.getString(article.getColumnIndex("title"));
- String link = article.getString(article.getColumnIndex("link"));
-
- Intent intent = new Intent(Intent.ACTION_SEND);
-
- intent.setType("text/plain");
- intent.putExtra(Intent.EXTRA_SUBJECT, title);
- intent.putExtra(Intent.EXTRA_TEXT, link);
-
- return intent;
- }
-
- private void shareArticle(int articleId) {
-
- Cursor article = getArticleById(articleId);
-
- if (article != null) {
- shareArticle(article);
- article.close();
- }
- }
-
- private void shareArticle(Cursor article) {
- if (article != null) {
- Intent intent = getShareIntent(article);
-
- startActivity(Intent.createChooser(intent,
- getString(R.id.share_article)));
- }
- }
-
- private void refreshHeadlines() {
- OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (ohf != null) {
- ohf.refresh();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- final OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- switch (item.getItemId()) {
- case R.id.close_feed:
- if (m_activeFeedId != 0 || m_activeFeedIsCat) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- ft.replace(R.id.headlines_fragment, new DummyFragment(), "");
- ft.commit();
-
- if (m_activeFeedIsCat) {
- OfflineFeedCategoriesFragment cats = (OfflineFeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
- cats.setSelectedFeedId(-1);
- } else {
- OfflineFeedsFragment feeds = (OfflineFeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
- feeds.setSelectedFeedId(0);
- }
-
- m_activeFeedId = 0;
-
- initMainMenu();
- }
- return true;
- case R.id.close_article:
- closeArticle();
- return true;
- case android.R.id.home:
- goBack(false);
- return true;
- case R.id.search:
- if (ohf != null && isCompatMode()) {
- Dialog dialog = new Dialog(this);
-
- final EditText edit = new EditText(this);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.search)
- .setPositiveButton(getString(R.string.search),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
-
- String query = edit.getText().toString().trim();
-
- ohf.setSearchQuery(query);
-
- }
- })
- .setNegativeButton(getString(R.string.cancel),
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
-
- //
-
- }
- }).setView(edit);
-
- dialog = builder.create();
- dialog.show();
- }
-
- return true;
- case R.id.preferences:
- Intent intent = new Intent(this, PreferencesActivity.class);
- startActivityForResult(intent, 0);
- return true;
- case R.id.go_online:
- switchOnline();
- return true;
- case R.id.headlines_select:
- if (ohf != null) {
- Dialog dialog = new Dialog(this);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.headlines_select_dialog);
-
- builder.setSingleChoiceItems(new String[] {
- getString(R.string.headlines_select_all),
- getString(R.string.headlines_select_unread),
- getString(R.string.headlines_select_none) }, 0,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- switch (which) {
- case 0:
- SQLiteStatement stmtSelectAll = getWritableDb()
- .compileStatement(
- "UPDATE articles SET selected = 1 WHERE feed_id = ?");
- stmtSelectAll.bindLong(1, m_activeFeedId);
- stmtSelectAll.execute();
- stmtSelectAll.close();
- break;
- case 1:
- SQLiteStatement stmtSelectUnread = getWritableDb()
- .compileStatement(
- "UPDATE articles SET selected = 1 WHERE feed_id = ? AND unread = 1");
- stmtSelectUnread
- .bindLong(1, m_activeFeedId);
- stmtSelectUnread.execute();
- stmtSelectUnread.close();
- break;
- case 2:
- deselectAllArticles();
- break;
- }
-
- refreshViews();
- initMainMenu();
-
- dialog.cancel();
- }
- });
-
- dialog = builder.create();
- dialog.show();
- }
- return true;
- case R.id.headlines_mark_as_read:
- if (m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = 0 WHERE feed_id = ?");
- stmt.bindLong(1, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.share_article:
- if (android.os.Build.VERSION.SDK_INT < 14) {
- shareArticle(m_selectedArticleId);
- }
- return true;
- case R.id.toggle_marked:
- if (m_selectedArticleId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET marked = NOT marked WHERE "
- + BaseColumns._ID + " = ?");
- stmt.bindLong(1, m_selectedArticleId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.selection_select_none:
- deselectAllArticles();
- return true;
- case R.id.selection_toggle_unread:
- if (getSelectedArticleCount() > 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET unread = NOT unread WHERE selected = 1 AND feed_id = ?");
- stmt.bindLong(1, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.selection_toggle_marked:
- if (getSelectedArticleCount() > 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET marked = NOT marked WHERE selected = 1 AND feed_id = ?");
- stmt.bindLong(1, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.selection_toggle_published:
- if (getSelectedArticleCount() > 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET published = NOT published WHERE selected = 1 AND feed_id = ?");
- stmt.bindLong(1, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.toggle_published:
- if (m_selectedArticleId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET published = NOT published WHERE "
- + BaseColumns._ID + " = ?");
- stmt.bindLong(1, m_selectedArticleId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.catchup_above:
- if (m_selectedArticleId != 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = 0 WHERE updated >= "
- + "(SELECT updated FROM articles WHERE "
- + BaseColumns._ID + " = ?) AND feed_id = ?");
- stmt.bindLong(1, m_selectedArticleId);
- stmt.bindLong(2, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.set_unread:
- if (m_selectedArticleId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = 1 WHERE "
- + BaseColumns._ID + " = ?");
- stmt.bindLong(1, m_selectedArticleId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.show_feeds:
- setUnreadOnly(!getUnreadOnly());
-
- if (getUnreadOnly()) {
- item.setTitle(R.string.menu_all_feeds);
- } else {
- item.setTitle(R.string.menu_unread_feeds);
- }
-
- return true;
- default:
- Log.d(TAG,
- "onOptionsItemSelected, unhandled id=" + item.getItemId());
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void refreshFeeds() {
- OfflineFeedsFragment frag = (OfflineFeedsFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_FEEDS);
-
- if (frag != null) {
- frag.refresh();
- }
- }
-
- private void refreshCats() {
- OfflineFeedCategoriesFragment frag = (OfflineFeedCategoriesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_CATS);
-
- if (frag != null) {
- frag.refresh();
- }
- }
-
- private void closeArticle() {
- m_selectedArticleId = 0;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (isSmallScreen()) {
- ft.remove(getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE));
- ft.show(getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES));
- } else {
- findViewById(R.id.feeds_fragment).setVisibility(View.VISIBLE);
- findViewById(R.id.article_fragment).setVisibility(View.GONE);
- ft.replace(R.id.article_fragment, new DummyFragment(), FRAG_ARTICLE);
- }
- ft.commit();
-
- initMainMenu();
-
- refreshViews();
- }
-
- private int getSelectedArticleCount() {
- Cursor c = getReadableDb().query("articles",
- new String[] { "COUNT(*)" }, "selected = 1", null, null, null,
- null);
- c.moveToFirst();
- int selected = c.getInt(0);
- c.close();
-
- return selected;
- }
-
- @Override
- public void initMainMenu() {
- if (m_menu != null) {
- int numSelected = getSelectedArticleCount();
-
- m_menu.setGroupVisible(R.id.menu_group_feeds, false);
- m_menu.setGroupVisible(R.id.menu_group_headlines, false);
- m_menu.setGroupVisible(R.id.menu_group_headlines_selection, false);
- m_menu.setGroupVisible(R.id.menu_group_article, false);
-
- if (numSelected != 0) {
- if (isCompatMode()) {
- m_menu.setGroupVisible(R.id.menu_group_headlines_selection, true);
- } else {
- if (m_headlinesActionMode == null)
- m_headlinesActionMode = startActionMode(m_headlinesActionModeCallback);
- }
- } else if (m_selectedArticleId != 0) {
- m_menu.setGroupVisible(R.id.menu_group_article, true);
- m_menu.findItem(R.id.close_article).setVisible(!isSmallScreen());
-
- if (android.os.Build.VERSION.SDK_INT >= 14) {
- ShareActionProvider shareProvider = (ShareActionProvider) m_menu.findItem(R.id.share_article).getActionProvider();
-
- if (m_selectedArticleId != 0) {
- Log.d(TAG, "setting up share provider");
- shareProvider.setShareIntent(getShareIntent(getArticleById(m_selectedArticleId)));
-
- if (!m_prefs.getBoolean("tablet_article_swipe", false) && !isSmallScreen()) {
- m_menu.findItem(R.id.share_article).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- }
- }
- }
- } else if (m_activeFeedId != 0) {
- m_menu.setGroupVisible(R.id.menu_group_headlines, true);
- m_menu.findItem(R.id.close_feed).setVisible(!isSmallScreen());
-
- MenuItem search = m_menu.findItem(R.id.search);
-
- if (!isCompatMode()) {
- SearchView searchView = (SearchView) search.getActionView();
- searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- private String query = "";
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- OfflineHeadlinesFragment frag = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (frag != null) {
- frag.setSearchQuery(query);
- this.query = query;
- }
-
- return false;
- }
-
- @Override
- public boolean onQueryTextChange(String newText) {
- if (newText.equals("") && !newText.equals(this.query)) {
- OfflineHeadlinesFragment frag = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (frag != null) {
- frag.setSearchQuery(newText);
- this.query = newText;
- }
- }
-
- return false;
- }
- });
- }
-
- } else {
- m_menu.setGroupVisible(R.id.menu_group_feeds, true);
- }
-
- if (numSelected == 0 && m_headlinesActionMode != null) {
- m_headlinesActionMode.finish();
- }
- }
-
- updateTitle();
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
- private void refreshViews() {
- refreshFeeds();
- refreshCats();
- refreshHeadlines();
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
- .getMenuInfo();
-
- OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
- OfflineFeedsFragment ff = (OfflineFeedsFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_FEEDS);
- OfflineFeedCategoriesFragment cf = (OfflineFeedCategoriesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_CATS);
-
- switch (item.getItemId()) {
- case R.id.article_link_copy:
- if (m_selectedArticleId != 0) {
- Cursor article = null;
-
- if (m_selectedArticleId != 0) {
- article = getArticleById(m_selectedArticleId);
- } else if (info != null) {
- article = hf.getArticleAtPosition(info.position);
- }
-
- if (article != null) {
- copyToClipboard(article.getString(article.getColumnIndex("link")));
- article.close();
- }
- }
- return true;
- case R.id.article_link_share:
- if (m_selectedArticleId != 0) {
- shareArticle(m_selectedArticleId);
- }
- return true;
-
- case R.id.browse_articles:
- if (cf != null) {
- int catId = cf.getCatIdAtPosition(info.position);
- viewFeed(catId, true);
- }
- return true;
- case R.id.browse_feeds:
- if (cf != null) {
- int catId = cf.getCatIdAtPosition(info.position);
- viewCategory(catId, false);
- }
- return true;
- case R.id.catchup_category:
- if (cf != null) {
- int catId = cf.getCatIdAtPosition(info.position);
-
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = 0 WHERE feed_id IN (SELECT "+
- BaseColumns._ID+" FROM feeds WHERE cat_id = ?)");
- stmt.bindLong(1, catId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- case R.id.catchup_feed:
- if (ff != null) {
- int feedId = ff.getFeedIdAtPosition(info.position);
-
- if (feedId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = 0 WHERE feed_id = ?");
- stmt.bindLong(1, feedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- }
- return true;
- case R.id.selection_toggle_unread:
- if (getSelectedArticleCount() > 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET unread = NOT unread WHERE selected = 1 AND feed_id = ?");
- stmt.bindLong(1, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- } else {
- int articleId = hf.getArticleIdAtPosition(info.position);
- if (articleId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = NOT unread WHERE "
- + BaseColumns._ID + " = ?");
- stmt.bindLong(1, articleId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- }
- return true;
- case R.id.selection_toggle_marked:
- if (getSelectedArticleCount() > 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET marked = NOT marked WHERE selected = 1 AND feed_id = ?");
- stmt.bindLong(1, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- } else {
- int articleId = hf.getArticleIdAtPosition(info.position);
- if (articleId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET marked = NOT marked WHERE "
- + BaseColumns._ID + " = ?");
- stmt.bindLong(1, articleId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- }
- return true;
- case R.id.selection_toggle_published:
- if (getSelectedArticleCount() > 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET published = NOT published WHERE selected = 1 AND feed_id = ?");
- stmt.bindLong(1, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- } else {
- int articleId = hf.getArticleIdAtPosition(info.position);
- if (articleId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET published = NOT published WHERE "
- + BaseColumns._ID + " = ?");
- stmt.bindLong(1, articleId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- }
- return true;
- case R.id.share_article:
- Cursor article = hf.getArticleAtPosition(info.position);
-
- if (article != null) {
- shareArticle(article);
- }
- return true;
- case R.id.catchup_above:
- int articleId = hf.getArticleIdAtPosition(info.position);
-
- if (articleId != 0 && m_activeFeedId != 0) {
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = 0 WHERE updated >= "
- + "(SELECT updated FROM articles WHERE "
- + BaseColumns._ID + " = ?) AND feed_id = ?");
- stmt.bindLong(1, articleId);
- stmt.bindLong(2, m_activeFeedId);
- stmt.execute();
- stmt.close();
- refreshViews();
- }
- return true;
- default:
- Log.d(TAG,
- "onContextItemSelected, unhandled id=" + item.getItemId());
- return super.onContextItemSelected(item);
- }
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- int action = event.getAction();
- int keyCode = event.getKeyCode();
- switch (keyCode) {
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- if (action == KeyEvent.ACTION_DOWN) {
-
- OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- int nextId = getRelativeArticleId(m_selectedArticleId,
- m_activeFeedId, RelativeArticle.AFTER);
-
- if (nextId != 0 && ohf != null) {
- if (m_prefs.getBoolean("combined_mode", false)) {
- ohf.setActiveArticleId(nextId);
-
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET unread = 0 "
- + "WHERE " + BaseColumns._ID
- + " = ?");
-
- stmt.bindLong(1, nextId);
- stmt.execute();
- stmt.close();
-
- } else {
- openArticle(nextId, 0);
- }
- }
- }
- return true;
- case KeyEvent.KEYCODE_VOLUME_UP:
- if (action == KeyEvent.ACTION_UP) {
-
- OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- int prevId = getRelativeArticleId(m_selectedArticleId,
- m_activeFeedId, RelativeArticle.BEFORE);
-
- if (prevId != 0 && ohf != null) {
- if (m_prefs.getBoolean("combined_mode", false)) {
- ohf.setActiveArticleId(prevId);
-
- SQLiteStatement stmt = getWritableDb()
- .compileStatement(
- "UPDATE articles SET unread = 0 "
- + "WHERE " + BaseColumns._ID
- + " = ?");
-
- stmt.bindLong(1, prevId);
- stmt.execute();
- stmt.close();
-
- } else {
- openArticle(prevId, 0);
- }
- }
- }
- return true;
- default:
- return super.dispatchKeyEvent(event);
- }
- }
-
- private void deselectAllArticles() {
- getWritableDb().execSQL("UPDATE articles SET selected = 0 ");
- }
-
- @Override
- public int getRelativeArticleId(int baseId, int feedId,
- OnlineServices.RelativeArticle mode) {
-
- Cursor c;
-
- /*
- * if (baseId == 0) { c = getReadableDb().query("articles", null,
- * "feed_id = ?", new String[] { String.valueOf(feedId) }, null, null,
- * "updated DESC LIMIT 1");
- *
- * if (c.moveToFirst()) { baseId = c.getInt(0); }
- *
- * c.close();
- *
- * return baseId; }
- */
-
- if (mode == RelativeArticle.BEFORE) {
- c = getReadableDb().query(
- "articles",
- null,
- "updated > (SELECT updated FROM articles WHERE "
- + BaseColumns._ID + " = ?) AND feed_id = ?",
- new String[] { String.valueOf(baseId),
- String.valueOf(feedId) }, null, null,
- "updated LIMIT 1");
-
- } else {
- c = getReadableDb().query(
- "articles",
- null,
- "updated < (SELECT updated FROM articles WHERE "
- + BaseColumns._ID + " = ?) AND feed_id = ?",
- new String[] { String.valueOf(baseId),
- String.valueOf(feedId) }, null, null,
- "updated DESC LIMIT 1");
- }
-
- int id = 0;
-
- if (c.moveToFirst()) {
- id = c.getInt(0);
- }
-
- c.close();
-
- return id;
- }
-
- public void onCatSelected(int catId) {
- Log.d(TAG, "onCatSelected");
- boolean browse = m_prefs.getBoolean("browse_cats_like_feeds", false);
-
- viewCategory(catId, browse);
- }
-
- public void viewCategory(int cat, boolean openAsFeed) {
-
- Log.d(TAG, "viewCategory");
-
- if (!openAsFeed) {
- OfflineFeedsFragment frag = new OfflineFeedsFragment(cat);
-
- FragmentTransaction ft = getSupportFragmentManager()
- .beginTransaction();
-
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, frag, FRAG_FEEDS);
- } else {
- ft.replace(R.id.feeds_fragment, frag, FRAG_FEEDS);
- }
- ft.commit();
-
- m_activeCatId = cat;
-
- } else {
- if (m_menu != null) {
- MenuItem search = m_menu.findItem(R.id.search);
-
- if (search != null && !isCompatMode()) {
- SearchView sv = (SearchView) search.getActionView();
- sv.setQuery("", false);
- }
- }
- viewFeed(cat, true);
- }
-
- initMainMenu();
- }
-
- @Override
- public void onFeedSelected(int feedId) {
- viewFeed(feedId);
- }
-
- public void viewFeed(int feedId) {
- viewFeed(feedId, false);
- }
-
- public void viewFeed(int feedId, boolean isCat) {
- m_activeFeedId = feedId;
- m_activeFeedIsCat = isCat;
-
- initMainMenu();
-
- deselectAllArticles();
-
- if (m_menu != null) {
- MenuItem search = m_menu.findItem(R.id.search);
-
- if (search != null && !isCompatMode()) {
- SearchView sv = (SearchView) search.getActionView();
- sv.setQuery("", false);
- }
- }
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- OfflineHeadlinesFragment frag = new OfflineHeadlinesFragment(feedId, isCat);
-
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, frag, FRAG_HEADLINES);
- } else {
- findViewById(R.id.headlines_fragment).setVisibility(View.VISIBLE);
- ft.replace(R.id.headlines_fragment, frag, FRAG_HEADLINES);
- }
- ft.commit();
-
- }
-
- @Override
- public void openArticle(int articleId, int compatAnimation) {
- m_selectedArticleId = articleId;
-
- initMainMenu();
-
- /* OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager()
- .findFragmentByTag(FRAG_HEADLINES);
-
- if (hf != null) {
- hf.setActiveArticleId(articleId);
- } */
-
- SQLiteStatement stmt = getWritableDb().compileStatement(
- "UPDATE articles SET unread = 0 " + "WHERE " + BaseColumns._ID
- + " = ?");
-
- stmt.bindLong(1, articleId);
- stmt.execute();
- stmt.close();
-
- Fragment frag;
-
- if (isSmallScreen() || m_prefs.getBoolean("tablet_article_swipe", false)) {
- frag = new OfflineArticlePager(articleId);
- } else {
- frag = new OfflineArticleFragment(articleId);
- }
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (isSmallScreen()) {
- ft.hide(getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES));
- ft.add(R.id.fragment_container, frag, FRAG_ARTICLE);
- } else {
- findViewById(R.id.feeds_fragment).setVisibility(isPortrait() || isSmallTablet() ? View.GONE : View.VISIBLE);
- findViewById(R.id.article_fragment).setVisibility(View.VISIBLE);
- ft.replace(R.id.article_fragment, frag, FRAG_ARTICLE);
-
- refreshViews();
- }
-
- ft.commit();
- }
-
- @Override
- public int getSelectedArticleId() {
- return m_selectedArticleId;
- }
-
- @Override
- public void setSelectedArticleId(int articleId) {
- m_selectedArticleId = articleId;
- initMainMenu();
- refreshViews();
- }
-
- private void closeCategory() {
- m_activeCatId = -1;
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- if (isSmallScreen()) {
- ft.replace(R.id.fragment_container, new OfflineFeedCategoriesFragment(), FRAG_CATS);
- } else {
- ft.replace(R.id.feeds_fragment, new OfflineFeedCategoriesFragment(), FRAG_CATS);
- }
- ft.commit();
-
- initMainMenu();
-
- refreshViews();
- }
-
- @Override
- public boolean activeFeedIsCat() {
- return m_activeFeedIsCat;
- }
-
- private void updateTitle() {
- if (!isCompatMode()) {
-
- m_navigationAdapter.clear();
-
- if (m_activeCatId != -1 || (m_activeFeedId != 0 && (isSmallScreen() || isPortrait()))) {
- getActionBar().setDisplayShowTitleEnabled(false);
- getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
-
- m_navigationAdapter.add(new RootNavigationEntry(getString(R.string.app_name)));
-
- if (m_activeCatId != -1) {
- Cursor cat = getCatById(m_activeCatId);
- String title = cat.getString(cat.getColumnIndex("title"));
- m_navigationAdapter.add(new CategoryNavigationEntry(m_activeCatId, title));
- cat.close();
- }
-
- if (m_activeFeedId != 0) {
- Cursor feed = null;
- if (m_activeFeedIsCat) {
- feed = getCatById(m_activeFeedId);
- } else {
- feed = getFeedById(m_activeFeedId);
- }
- String title = feed.getString(feed.getColumnIndex("title"));
- m_navigationAdapter.add(new FeedNavigationEntry(m_activeFeedId, title));
- feed.close();
- }
-
- //if (m_selectedArticle != null)
- // m_navigationAdapter.add(new ArticleNavigationEntry(m_selectedArticle));
-
- getActionBar().setSelectedNavigationItem(getActionBar().getNavigationItemCount());
-
- } else {
- getActionBar().setDisplayShowTitleEnabled(true);
- getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
- getActionBar().setTitle(R.string.app_name);
- }
-
- if (isSmallScreen()) {
- getActionBar().setDisplayHomeAsUpEnabled(m_selectedArticleId != 0 || m_activeFeedId != 0 || m_activeCatId != -1);
- } else {
- getActionBar().setDisplayHomeAsUpEnabled(m_selectedArticleId != 0 || m_activeCatId != -1);
- }
-
- } else {
- if (m_activeFeedId != 0) {
- if (!m_activeFeedIsCat) {
- Cursor feed = getFeedById(m_activeFeedId);
-
- if (feed != null) {
- setTitle(feed.getString(feed.getColumnIndex("title")));
- feed.close();
- }
- } else {
- Cursor cat = getCatById(m_activeFeedId);
-
- if (cat != null) {
- setTitle(cat.getString(cat.getColumnIndex("title")));
- cat.close();
- }
- }
- } else if (m_activeCatId != -1) {
- Cursor cat = getCatById(m_activeCatId);
-
- if (cat != null) {
- setTitle(cat.getString(cat.getColumnIndex("title")));
- cat.close();
- }
-
- } else {
- setTitle(R.string.app_name);
- }
-
- }
- }
-} \ No newline at end of file
+package org.fox.ttrss.offline;
+
+import org.fox.ttrss.CommonActivity;
+import org.fox.ttrss.PreferencesActivity;
+import org.fox.ttrss.R;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.EditText;
+import android.widget.SearchView;
+import android.widget.ShareActionProvider;
+
+public class OfflineActivity extends CommonActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ protected SharedPreferences m_prefs;
+ protected Menu m_menu;
+
+ private ActionMode m_headlinesActionMode;
+ private HeadlinesActionModeCallback m_headlinesActionModeCallback;
+
+ @SuppressLint("NewApi")
+ private class HeadlinesActionModeCallback implements ActionMode.Callback {
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ deselectAllArticles();
+ m_headlinesActionMode = null;
+ initMenu();
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.headlines_action_menu, menu);
+
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ onOptionsItemSelected(item);
+ return false;
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ m_prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
+ setTheme(R.style.DarkTheme);
+ } else {
+ setTheme(R.style.LightTheme);
+ }
+
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+
+ setProgressBarVisibility(false);
+
+ setContentView(R.layout.login);
+
+ setLoadingStatus(R.string.blank, false);
+ findViewById(R.id.loading_container).setVisibility(View.GONE);
+
+ initMenu();
+
+ Intent intent = getIntent();
+
+ if (intent.getExtras() != null) {
+ if (intent.getBooleanExtra("initial", false)) {
+ intent = new Intent(OfflineActivity.this, OfflineFeedsActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+ startActivityForResult(intent, 0);
+ finish();
+ }
+ }
+
+ /* if (savedInstanceState != null) {
+
+ } */
+
+ if (!isCompatMode()) {
+ m_headlinesActionModeCallback = new HeadlinesActionModeCallback();
+ }
+
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+ }
+
+ protected void selectArticles(int feedId, boolean isCat, int mode) {
+ switch (mode) {
+ case 0:
+ SQLiteStatement stmtSelectAll = null;
+
+ if (isCat) {
+ stmtSelectAll = getWritableDb().compileStatement(
+ "UPDATE articles SET selected = 1 WHERE feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)");
+ } else {
+ stmtSelectAll = getWritableDb().compileStatement(
+ "UPDATE articles SET selected = 1 WHERE feed_id = ?");
+ }
+
+ stmtSelectAll.bindLong(1, feedId);
+ stmtSelectAll.execute();
+ stmtSelectAll.close();
+
+ break;
+ case 1:
+
+ SQLiteStatement stmtSelectUnread = null;
+
+ if (isCat) {
+ stmtSelectUnread = getWritableDb().compileStatement(
+ "UPDATE articles SET selected = 1 WHERE feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?) AND unread = 1");
+ } else {
+ stmtSelectUnread = getWritableDb().compileStatement(
+ "UPDATE articles SET selected = 1 WHERE feed_id = ? AND unread = 1");
+ }
+
+ stmtSelectUnread.bindLong(1, feedId);
+ stmtSelectUnread.execute();
+ stmtSelectUnread.close();
+
+ break;
+ case 2:
+ deselectAllArticles();
+ break;
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_HEADLINES);
+
+ /* final OfflineFeedsFragment off = (OfflineFeedsFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_FEEDS); */
+
+ /* final OfflineFeedCategoriesFragment ocf = (OfflineFeedCategoriesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_CATS); */
+
+ final OfflineArticlePager oap = (OfflineArticlePager) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_ARTICLE);
+
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ case R.id.go_online:
+ switchOnline();
+ return true;
+ case R.id.search:
+ if (ohf != null && isCompatMode()) {
+ Dialog dialog = new Dialog(this);
+
+ final EditText edit = new EditText(this);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.search)
+ .setPositiveButton(getString(R.string.search),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ String query = edit.getText().toString().trim();
+
+ ohf.setSearchQuery(query);
+
+ }
+ })
+ .setNegativeButton(getString(R.string.cancel),
+ new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ //
+
+ }
+ }).setView(edit);
+
+ dialog = builder.create();
+ dialog.show();
+ }
+
+ return true;
+ case R.id.preferences:
+ Intent intent = new Intent(this, PreferencesActivity.class);
+ startActivityForResult(intent, 0);
+ return true;
+ case R.id.headlines_select:
+ if (ohf != null) {
+ Dialog dialog = new Dialog(this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.headlines_select_dialog);
+
+ builder.setSingleChoiceItems(new String[] {
+ getString(R.string.headlines_select_all),
+ getString(R.string.headlines_select_unread),
+ getString(R.string.headlines_select_none) }, 0,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ selectArticles(ohf.getFeedId(), ohf.getFeedIsCat(), which);
+ initMenu();
+ refresh();
+
+ dialog.cancel();
+ }
+ });
+
+ dialog = builder.create();
+ dialog.show();
+ }
+ return true;
+ case R.id.headlines_mark_as_read:
+ if (ohf != null) {
+ int feedId = ohf.getFeedId();
+ boolean isCat = ohf.getFeedIsCat();
+
+ SQLiteStatement stmt = null;
+
+ if (isCat) {
+ stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)");
+ } else {
+ stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE feed_id = ?");
+ }
+ stmt.bindLong(1, feedId);
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ case R.id.share_article:
+ if (android.os.Build.VERSION.SDK_INT < 14 && oap != null && android.os.Build.VERSION.SDK_INT < 14) {
+ int articleId = oap.getSelectedArticleId();
+
+ shareArticle(articleId);
+ }
+ return true;
+ case R.id.toggle_marked:
+ if (oap != null) {
+ int articleId = oap.getSelectedArticleId();
+
+ SQLiteStatement stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET marked = NOT marked WHERE "
+ + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ case R.id.selection_select_none:
+ deselectAllArticles();
+ return true;
+ case R.id.selection_toggle_unread:
+ if (getSelectedArticleCount() > 0) {
+ SQLiteStatement stmt = getWritableDb()
+ .compileStatement(
+ "UPDATE articles SET unread = NOT unread WHERE selected = 1");
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ case R.id.selection_toggle_marked:
+ if (getSelectedArticleCount() > 0) {
+ SQLiteStatement stmt = getWritableDb()
+ .compileStatement(
+ "UPDATE articles SET marked = NOT marked WHERE selected = 1");
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ case R.id.selection_toggle_published:
+ if (getSelectedArticleCount() > 0) {
+ SQLiteStatement stmt = getWritableDb()
+ .compileStatement(
+ "UPDATE articles SET published = NOT published WHERE selected = 1");
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ case R.id.toggle_published:
+ if (oap != null) {
+ int articleId = oap.getSelectedArticleId();
+
+ SQLiteStatement stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET published = NOT published WHERE "
+ + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ case R.id.catchup_above:
+ if (oap != null) {
+ int articleId = oap.getSelectedArticleId();
+ int feedId = oap.getFeedId();
+ boolean isCat = oap.getFeedIsCat();
+
+ SQLiteStatement stmt = null;
+
+ if (isCat) {
+ stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE " +
+ "updated >= (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " +
+ "AND feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)");
+ } else {
+ stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE " +
+ "updated >= (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " +
+ "AND feed_id = ?");
+ }
+
+ stmt.bindLong(1, articleId);
+ stmt.bindLong(2, feedId);
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ case R.id.set_unread:
+ if (oap != null) {
+ int articleId = oap.getSelectedArticleId();
+
+ SQLiteStatement stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 1 WHERE "
+ + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+
+ refresh();
+ }
+ return true;
+ default:
+ Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.offline_menu, menu);
+
+ m_menu = menu;
+
+ initMenu();
+
+ return true;
+ }
+
+ @SuppressLint("NewApi")
+ protected void initMenu() {
+ if (m_menu != null) {
+ m_menu.setGroupVisible(R.id.menu_group_headlines, false);
+ m_menu.setGroupVisible(R.id.menu_group_headlines_selection, false);
+ m_menu.setGroupVisible(R.id.menu_group_article, false);
+ m_menu.setGroupVisible(R.id.menu_group_feeds, false);
+
+ if (android.os.Build.VERSION.SDK_INT >= 14) {
+ ShareActionProvider shareProvider = (ShareActionProvider) m_menu.findItem(R.id.share_article).getActionProvider();
+
+ OfflineArticlePager af = (OfflineArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+
+ if (af != null) {
+ shareProvider.setShareIntent(getShareIntent(getArticleById(af.getSelectedArticleId())));
+
+ if (!isSmallScreen()) {
+ m_menu.findItem(R.id.share_article).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+ }
+ }
+
+ if (!isCompatMode()) {
+ MenuItem search = m_menu.findItem(R.id.search);
+
+ OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ if (hf != null) {
+ if (hf.getSelectedArticleCount() > 0 && m_headlinesActionMode == null) {
+ m_headlinesActionMode = startActionMode(m_headlinesActionModeCallback);
+ } else if (hf.getSelectedArticleCount() == 0 && m_headlinesActionMode != null) {
+ m_headlinesActionMode.finish();
+ }
+ }
+
+ SearchView searchView = (SearchView) search.getActionView();
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ private String query = "";
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ OfflineHeadlinesFragment frag = (OfflineHeadlinesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_HEADLINES);
+
+ if (frag != null) {
+ frag.setSearchQuery(query);
+ this.query = query;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ if (newText.equals("") && !newText.equals(this.query)) {
+ OfflineHeadlinesFragment frag = (OfflineHeadlinesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_HEADLINES);
+
+ if (frag != null) {
+ frag.setSearchQuery(newText);
+ this.query = newText;
+ }
+ }
+
+ return false;
+ }
+ });
+ }
+ }
+ }
+
+ private void switchOnline() {
+ SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = localPrefs.edit();
+ editor.putBoolean("offline_mode_active", false);
+ editor.commit();
+
+ Intent refresh = new Intent(this, org.fox.ttrss.OnlineActivity.class);
+ startActivity(refresh);
+ finish();
+ }
+
+ protected Cursor getArticleById(int articleId) {
+ Cursor c = getReadableDb().query("articles", null,
+ BaseColumns._ID + "=?",
+ new String[] { String.valueOf(articleId) }, null, null, null);
+
+ c.moveToFirst();
+
+ return c;
+ }
+
+ protected Cursor getFeedById(int feedId) {
+ Cursor c = getReadableDb().query("feeds", null,
+ BaseColumns._ID + "=?",
+ new String[] { String.valueOf(feedId) }, null, null, null);
+
+ c.moveToFirst();
+
+ return c;
+ }
+
+ protected Cursor getCatById(int catId) {
+ Cursor c = getReadableDb().query("categories", null,
+ BaseColumns._ID + "=?",
+ new String[] { String.valueOf(catId) }, null, null, null);
+
+ c.moveToFirst();
+
+ return c;
+ }
+
+ protected Intent getShareIntent(Cursor article) {
+ String title = article.getString(article.getColumnIndex("title"));
+ String link = article.getString(article.getColumnIndex("link"));
+
+ Intent intent = new Intent(Intent.ACTION_SEND);
+
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_SUBJECT, title);
+ intent.putExtra(Intent.EXTRA_TEXT, link);
+
+ return intent;
+ }
+
+ protected void shareArticle(int articleId) {
+
+ Cursor article = getArticleById(articleId);
+
+ if (article != null) {
+ shareArticle(article);
+ article.close();
+ }
+ }
+
+ private void shareArticle(Cursor article) {
+ if (article != null) {
+ Intent intent = getShareIntent(article);
+
+ startActivity(Intent.createChooser(intent,
+ getString(R.string.share_article)));
+ }
+ }
+
+ protected int getSelectedArticleCount() {
+ Cursor c = getReadableDb().query("articles",
+ new String[] { "COUNT(*)" }, "selected = 1", null, null, null,
+ null);
+ c.moveToFirst();
+ int selected = c.getInt(0);
+ c.close();
+
+ return selected;
+ }
+
+ protected void deselectAllArticles() {
+ getWritableDb().execSQL("UPDATE articles SET selected = 0 ");
+ refresh();
+ }
+
+ protected void refresh() {
+ OfflineFeedsFragment ff = (OfflineFeedsFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_FEEDS);
+
+ if (ff != null) {
+ ff.refresh();
+ }
+
+ OfflineFeedCategoriesFragment cf = (OfflineFeedCategoriesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_CATS);
+
+ if (cf != null) {
+ cf.refresh();
+ }
+
+ OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager()
+ .findFragmentByTag(FRAG_HEADLINES);
+
+ if (ohf != null) {
+ ohf.refresh();
+ }
+ }
+
+}
diff --git a/src/org/fox/ttrss/offline/OfflineArticleFragment.java b/src/org/fox/ttrss/offline/OfflineArticleFragment.java
index ebe11fc8..98ce59f4 100644
--- a/src/org/fox/ttrss/offline/OfflineArticleFragment.java
+++ b/src/org/fox/ttrss/offline/OfflineArticleFragment.java
@@ -10,34 +10,42 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
+import android.annotation.SuppressLint;
import android.app.Activity;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
+import android.graphics.Paint;
+import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
import android.text.Html;
import android.text.method.LinkMovementMethod;
+import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnClickListener;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
-import android.webkit.WebView;
import android.webkit.WebSettings.LayoutAlgorithm;
+import android.webkit.WebView;
import android.widget.TextView;
public class OfflineArticleFragment extends Fragment {
- @SuppressWarnings("unused")
private final String TAG = this.getClass().getSimpleName();
private SharedPreferences m_prefs;
private int m_articleId;
+ private boolean m_isCat = false; // FIXME use
private Cursor m_cursor;
- private OfflineServices m_offlineServices;
+ private OfflineActivity m_activity;
public OfflineArticleFragment() {
super();
@@ -48,6 +56,32 @@ public class OfflineArticleFragment extends Fragment {
m_articleId = articleId;
}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ /* AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo(); */
+
+ switch (item.getItemId()) {
+ case R.id.article_link_share:
+ m_activity.shareArticle(m_articleId);
+ return true;
+ case R.id.article_link_copy:
+ if (true) {
+ Cursor article = m_activity.getArticleById(m_articleId);
+
+ if (article != null) {
+ m_activity.copyToClipboard(article.getString(article.getColumnIndex("link")));
+ article.close();
+ }
+ }
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
@@ -59,6 +93,7 @@ public class OfflineArticleFragment extends Fragment {
}
+ @SuppressLint("NewApi")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -68,7 +103,7 @@ public class OfflineArticleFragment extends Fragment {
View view = inflater.inflate(R.layout.article_fragment, container, false);
- m_cursor = m_offlineServices.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
+ m_cursor = m_activity.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
new String[] { "articles.*", "feeds.title AS feed_title" }, "articles." + BaseColumns._ID + "=?",
new String[] { String.valueOf(m_articleId) }, null, null, null);
@@ -87,8 +122,24 @@ public class OfflineArticleFragment extends Fragment {
else
titleStr = m_cursor.getString(m_cursor.getColumnIndex("title"));
- title.setMovementMethod(LinkMovementMethod.getInstance());
- title.setText(Html.fromHtml("<a href=\""+m_cursor.getString(m_cursor.getColumnIndex("link")).trim().replace("\"", "\\\"")+"\">" + titleStr + "</a>"));
+ final String link = m_cursor.getString(m_cursor.getColumnIndex("link"));
+
+ title.setText(titleStr);
+ title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
+ title.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(link.trim()));
+ startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ m_activity.toast(R.string.error_other_error);
+ }
+ }
+ });
+
registerForContextMenu(title);
}
@@ -96,6 +147,16 @@ public class OfflineArticleFragment extends Fragment {
if (web != null) {
+ web.setWebChromeClient(new WebChromeClient() {
+ @Override
+ public void onProgressChanged(WebView view, int progress) {
+ m_activity.setProgress(Math.round(((float)progress / 100f) * 10000));
+ if (progress == 100) {
+ m_activity.setProgressBarVisibility(false);
+ }
+ }
+ });
+
String content;
String cssOverride = "";
@@ -199,7 +260,7 @@ public class OfflineArticleFragment extends Fragment {
if (tagv != null) {
int feedTitleIndex = m_cursor.getColumnIndex("feed_title");
- if (feedTitleIndex != -1 && m_offlineServices.activeFeedIsCat()) {
+ if (feedTitleIndex != -1 && m_isCat) {
tagv.setText(m_cursor.getString(feedTitleIndex));
} else {
String tagsStr = m_cursor.getString(m_cursor.getColumnIndex("tags"));
@@ -230,8 +291,8 @@ public class OfflineArticleFragment extends Fragment {
super.onAttach(activity);
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
-
- m_offlineServices = (OfflineServices)activity;
+
+ m_activity = (OfflineActivity) activity;
}
diff --git a/src/org/fox/ttrss/offline/OfflineArticlePager.java b/src/org/fox/ttrss/offline/OfflineArticlePager.java
index 104d9f4f..3faf3415 100644
--- a/src/org/fox/ttrss/offline/OfflineArticlePager.java
+++ b/src/org/fox/ttrss/offline/OfflineArticlePager.java
@@ -3,67 +3,126 @@ package org.fox.ttrss.offline;
import org.fox.ttrss.R;
import android.app.Activity;
-import android.database.sqlite.SQLiteStatement;
+import android.database.Cursor;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class OfflineArticlePager extends Fragment {
+ private final String TAG = this.getClass().getSimpleName();
private PagerAdapter m_adapter;
- private OfflineServices m_offlineServices;
- private OfflineHeadlinesFragment m_hf;
+ private OfflineActivity m_activity;
+ private OfflineHeadlinesEventListener m_listener;
+ private boolean m_isCat;
+ private int m_feedId;
private int m_articleId;
+ private String m_searchQuery = "";
+ private Cursor m_cursor;
- private class PagerAdapter extends FragmentStatePagerAdapter {
+ public int getFeedId() {
+ return m_feedId;
+ }
+
+ public boolean getFeedIsCat() {
+ return m_isCat;
+ }
+
+ public Cursor createCursor() {
+ String feedClause = null;
+
+ if (m_isCat) {
+ feedClause = "feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)";
+ } else {
+ feedClause = "feed_id = ?";
+ }
+ if (m_searchQuery == null || m_searchQuery.equals("")) {
+ return m_activity.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
+ new String[] { "articles."+BaseColumns._ID, "feeds.title AS feed_title" }, feedClause,
+ new String[] { String.valueOf(m_feedId) }, null, null, "updated DESC");
+ } else {
+ return m_activity.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
+ new String[] { "articles."+BaseColumns._ID },
+ feedClause + " AND (articles.title LIKE '%' || ? || '%' OR content LIKE '%' || ? || '%')",
+ new String[] { String.valueOf(m_feedId), m_searchQuery, m_searchQuery }, null, null, "updated DESC");
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close();
+ }
+
+ private class PagerAdapter extends FragmentStatePagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
- int articleId = m_hf.getArticleIdAtPosition(position);
+ Log.d(TAG, "getItem: " + position);
- if (articleId != 0) {
- return new OfflineArticleFragment(articleId);
+ if (m_cursor.moveToPosition(position)) {
+ return new OfflineArticleFragment(m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID)));
}
return null;
}
-
+
@Override
public int getCount() {
- return m_hf.getArticleCount();
+ return m_cursor.getCount();
}
-
}
public OfflineArticlePager() {
super();
}
- public OfflineArticlePager(int articleId) {
+ public OfflineArticlePager(int articleId, int feedId, boolean isCat) {
super();
+ m_feedId = feedId;
+ m_isCat = isCat;
m_articleId = articleId;
+
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.article_pager, container, false);
+ if (savedInstanceState != null) {
+ m_articleId = savedInstanceState.getInt("articleId", 0);
+ m_feedId = savedInstanceState.getInt("feedId", 0);
+ m_isCat = savedInstanceState.getBoolean("isCat", false);
+ }
+
m_adapter = new PagerAdapter(getActivity().getSupportFragmentManager());
- ViewPager pager = (ViewPager) view.findViewById(R.id.article_pager);
+ m_cursor.moveToFirst();
+
+ int position = 0;
- int position = m_hf.getArticleIdPosition(m_articleId);
+ while (!m_cursor.isLast()) {
+ if (m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID)) == m_articleId) {
+ position = m_cursor.getPosition();
+ break;
+ }
+ m_cursor.moveToNext();
+ }
+
+ ViewPager pager = (ViewPager) view.findViewById(R.id.article_pager);
pager.setAdapter(m_adapter);
pager.setCurrentItem(position);
@@ -79,22 +138,17 @@ public class OfflineArticlePager extends Fragment {
@Override
public void onPageSelected(int position) {
- int articleId = m_hf.getArticleIdAtPosition(position);
-
- if (articleId != 0) {
- m_offlineServices.setSelectedArticleId(articleId);
+ if (m_cursor.moveToPosition(position)) {
+ int articleId = m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID));
+
+ m_listener.onArticleSelected(articleId, false);
+
+ m_articleId = articleId;
- SQLiteStatement stmt = m_offlineServices.getWritableDb().compileStatement(
- "UPDATE articles SET unread = 0 " + "WHERE " + BaseColumns._ID
- + " = ?");
-
- stmt.bindLong(1, articleId);
- stmt.execute();
- stmt.close();
}
}
});
-
+
return view;
}
@@ -102,8 +156,58 @@ public class OfflineArticlePager extends Fragment {
public void onAttach(Activity activity) {
super.onAttach(activity);
- m_hf = (OfflineHeadlinesFragment) getActivity().getSupportFragmentManager().findFragmentByTag(OfflineActivity.FRAG_HEADLINES);
- m_offlineServices = (OfflineServices)activity;
+ m_activity = (OfflineActivity)activity;
+ m_listener = (OfflineHeadlinesEventListener)activity;
+ m_cursor = createCursor();
+
+ }
+
+ public void refresh() {
+ if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close();
+
+ m_cursor = createCursor();
+
+ if (m_cursor != null) {
+ m_adapter.notifyDataSetChanged();
+ }
+ }
+
+ public int getSelectedArticleId() {
+ return m_articleId;
}
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putInt("articleId", m_articleId);
+ out.putInt("feedId", m_feedId);
+ out.putBoolean("isCat", m_isCat);
+
+ }
+
+ public void setSearchQuery(String searchQuery) {
+ m_searchQuery = searchQuery;
+ }
+
+ public void setArticleId(int articleId) {
+ m_articleId = articleId;
+
+ m_cursor.moveToFirst();
+
+ int position = 0;
+
+ while (!m_cursor.isLast()) {
+ if (m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID)) == m_articleId) {
+ position = m_cursor.getPosition();
+ break;
+ }
+ m_cursor.moveToNext();
+ }
+
+ ViewPager pager = (ViewPager) getView().findViewById(R.id.article_pager);
+
+ pager.setCurrentItem(position);
+
+ }
}
diff --git a/src/org/fox/ttrss/offline/OfflineDownloadService.java b/src/org/fox/ttrss/offline/OfflineDownloadService.java
index b757082f..eb9a802e 100644
--- a/src/org/fox/ttrss/offline/OfflineDownloadService.java
+++ b/src/org/fox/ttrss/offline/OfflineDownloadService.java
@@ -5,7 +5,7 @@ import java.util.HashMap;
import java.util.List;
import org.fox.ttrss.ApiRequest;
-import org.fox.ttrss.MainActivity;
+import org.fox.ttrss.OnlineActivity;
import org.fox.ttrss.R;
import org.fox.ttrss.types.Article;
import org.fox.ttrss.types.Feed;
@@ -46,8 +46,8 @@ public class OfflineDownloadService extends Service {
public static final String INTENT_ACTION_SUCCESS = "org.fox.ttrss.intent.action.DownloadComplete";
public static final String INTENT_ACTION_CANCEL = "org.fox.ttrss.intent.action.Cancel";
- private static final int OFFLINE_SYNC_SEQ = 40;
- private static final int OFFLINE_SYNC_MAX = 500;
+ private static final int OFFLINE_SYNC_SEQ = 50;
+ private static final int OFFLINE_SYNC_MAX = OFFLINE_SYNC_SEQ * 10;
private SQLiteDatabase m_writableDb;
private SQLiteDatabase m_readableDb;
@@ -87,11 +87,12 @@ public class OfflineDownloadService extends Service {
initDatabase();
}
+ @SuppressWarnings("deprecation")
private void updateNotification(String msg) {
Notification notification = new Notification(R.drawable.icon,
getString(R.string.notify_downloading_title), System.currentTimeMillis());
- Intent intent = new Intent(this, MainActivity.class);
+ Intent intent = new Intent(this, OnlineActivity.class);
intent.setAction(INTENT_ACTION_CANCEL);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
@@ -158,9 +159,9 @@ public class OfflineDownloadService extends Service {
m_readableDb = dh.getReadableDatabase();
}
- private synchronized SQLiteDatabase getReadableDb() {
+ /* private synchronized SQLiteDatabase getReadableDb() {
return m_readableDb;
- }
+ } */
private synchronized SQLiteDatabase getWritableDb() {
return m_writableDb;
@@ -198,9 +199,11 @@ public class OfflineDownloadService extends Service {
ApiRequest req = new ApiRequest(getApplicationContext()) {
@Override
- protected void onPostExecute(JsonElement content) {
+ protected JsonElement doInBackground(HashMap<String, String>... params) {
+ JsonElement content = super.doInBackground(params);
+
if (content != null) {
-
+
try {
Type listType = new TypeToken<List<Feed>>() {}.getType();
List<Feed> feeds = new Gson().fromJson(content, listType);
@@ -215,29 +218,35 @@ public class OfflineDownloadService extends Service {
stmtInsert.bindString(3, feed.feed_url);
stmtInsert.bindLong(4, feed.has_icon ? 1 : 0);
stmtInsert.bindLong(5, feed.cat_id);
-
+
stmtInsert.execute();
}
-
+
stmtInsert.close();
-
+
Log.d(TAG, "offline: done downloading feeds");
m_articleOffset = 0;
getWritableDb().execSQL("DELETE FROM articles;");
-
- if (m_canProceed) {
- downloadArticles();
- } else {
- downloadFailed();
- }
} catch (Exception e) {
e.printStackTrace();
updateNotification(R.string.offline_switch_error);
downloadFailed();
}
+ }
+ return content;
+ }
+
+ @Override
+ protected void onPostExecute(JsonElement content) {
+ if (content != null) {
+ if (m_canProceed) {
+ downloadArticles();
+ } else {
+ downloadFailed();
+ }
} else {
updateNotification(getErrorMessage());
downloadFailed();
@@ -266,10 +275,10 @@ public class OfflineDownloadService extends Service {
getWritableDb().execSQL("DELETE FROM categories;");
ApiRequest req = new ApiRequest(getApplicationContext()) {
- @Override
- protected void onPostExecute(JsonElement content) {
+ protected JsonElement doInBackground(HashMap<String, String>... params) {
+ JsonElement content = super.doInBackground(params);
+
if (content != null) {
-
try {
Type listType = new TypeToken<List<FeedCategory>>() {}.getType();
List<FeedCategory> cats = new Gson().fromJson(content, listType);
@@ -289,17 +298,23 @@ public class OfflineDownloadService extends Service {
Log.d(TAG, "offline: done downloading categories");
- if (m_canProceed) {
- downloadFeeds();
- } else {
- downloadFailed();
- }
} catch (Exception e) {
e.printStackTrace();
updateNotification(R.string.offline_switch_error);
downloadFailed();
}
-
+ }
+
+ return content;
+ }
+ @Override
+ protected void onPostExecute(JsonElement content) {
+ if (content != null) {
+ if (m_canProceed) {
+ downloadFeeds();
+ } else {
+ downloadFailed();
+ }
} else {
updateNotification(getErrorMessage());
downloadFailed();
@@ -335,22 +350,27 @@ public class OfflineDownloadService extends Service {
}
public class OfflineArticlesRequest extends ApiRequest {
+ List<Article> m_articles;
+
public OfflineArticlesRequest(Context context) {
super(context);
}
@Override
- protected void onPostExecute(JsonElement content) {
+ protected JsonElement doInBackground(HashMap<String, String>... params) {
+ JsonElement content = super.doInBackground(params);
+
if (content != null) {
+
try {
Type listType = new TypeToken<List<Article>>() {}.getType();
- List<Article> articles = new Gson().fromJson(content, listType);
+ m_articles = new Gson().fromJson(content, listType);
SQLiteStatement stmtInsert = getWritableDb().compileStatement("INSERT INTO articles " +
"("+BaseColumns._ID+", unread, marked, published, updated, is_updated, title, link, feed_id, tags, content) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
- for (Article article : articles) {
+ for (Article article : m_articles) {
String tagsString = "";
@@ -402,25 +422,12 @@ public class OfflineDownloadService extends Service {
}
+ m_articleOffset += m_articles.size();
+
+ Log.d(TAG, "offline: received " + m_articles.size() + " articles; canProc=" + m_canProceed);
+
stmtInsert.close();
- //m_canGetMoreArticles = articles.size() == 30;
- m_articleOffset += articles.size();
-
- Log.d(TAG, "offline: received " + articles.size() + " articles; canProc=" + m_canProceed);
-
- if (m_canProceed) {
- if (articles.size() == OFFLINE_SYNC_SEQ && m_articleOffset < m_syncMax) {
- downloadArticles();
- } else {
- downloadComplete();
- }
- } else {
- downloadFailed();
- }
-
- return;
-
} catch (Exception e) {
updateNotification(R.string.offline_switch_error);
Log.d(TAG, "offline: failed: exception when loading articles");
@@ -428,6 +435,25 @@ public class OfflineDownloadService extends Service {
downloadFailed();
}
+ }
+
+ return content;
+ }
+
+ @Override
+ protected void onPostExecute(JsonElement content) {
+ if (content != null) {
+
+ if (m_canProceed && m_articles != null) {
+ if (m_articles.size() == OFFLINE_SYNC_SEQ && m_articleOffset < m_syncMax) {
+ downloadArticles();
+ } else {
+ downloadComplete();
+ }
+ } else {
+ downloadFailed();
+ }
+
} else {
Log.d(TAG, "offline: failed: " + getErrorMessage());
updateNotification(getErrorMessage());
diff --git a/src/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java b/src/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java
index f6ae2835..d46e5587 100644
--- a/src/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java
+++ b/src/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java
@@ -16,6 +16,7 @@ import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
@@ -31,7 +32,7 @@ public class OfflineFeedCategoriesFragment extends Fragment implements OnItemCli
private FeedCategoryListAdapter m_adapter;
private int m_selectedCatId;
private Cursor m_cursor;
- private OfflineServices m_offlineServices;
+ private OfflineFeedsActivity m_activity;
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
@@ -50,11 +51,11 @@ public class OfflineFeedCategoriesFragment extends Fragment implements OnItemCli
}
public Cursor createCursor() {
- String unreadOnly = BaseColumns._ID + "> 0 AND " + (m_offlineServices.getUnreadOnly() ? "unread > 0" : "1");
+ String unreadOnly = BaseColumns._ID + "> 0 AND " + (m_activity.getUnreadOnly() ? "unread > 0" : "1");
String order = m_prefs.getBoolean("sort_feeds_by_unread", false) ? "unread DESC, title" : "title";
- return m_offlineServices.getReadableDb().query("cats_unread",
+ return m_activity.getReadableDb().query("cats_unread",
null, unreadOnly, null, null, null, order);
}
@@ -70,6 +71,48 @@ public class OfflineFeedCategoriesFragment extends Fragment implements OnItemCli
}
@Override
+ public void onResume() {
+ super.onResume();
+ refresh();
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+
+ switch (item.getItemId()) {
+ case R.id.browse_articles:
+ if (true) {
+ int catId = getCatIdAtPosition(info.position);
+ if (catId != -10000) {
+ m_activity.onCatSelected(catId, true);
+ }
+ }
+ return true;
+ case R.id.browse_feeds:
+ if (true) {
+ int catId = getCatIdAtPosition(info.position);
+ if (catId != -10000) {
+ m_activity.onCatSelected(catId, false);
+ }
+ }
+ return true;
+ case R.id.catchup_category:
+ if (true) {
+ int catId = getCatIdAtPosition(info.position);
+ if (catId != -10000) {
+ m_activity.catchupFeed(catId, true);
+ }
+ }
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (savedInstanceState != null) {
@@ -106,7 +149,7 @@ public class OfflineFeedCategoriesFragment extends Fragment implements OnItemCli
public void onAttach(Activity activity) {
super.onAttach(activity);
- m_offlineServices = (OfflineServices)activity;
+ m_activity = (OfflineFeedsActivity)activity;
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
m_prefs.registerOnSharedPreferenceChangeListener(this);
@@ -131,9 +174,9 @@ public class OfflineFeedCategoriesFragment extends Fragment implements OnItemCli
int feedId = (int) cursor.getLong(0);
Log.d(TAG, "clicked on feed " + feedId);
- m_offlineServices.onCatSelected(feedId);
+ m_activity.onCatSelected(feedId);
- if (!m_offlineServices.isSmallScreen())
+ if (!m_activity.isSmallScreen())
m_selectedCatId = feedId;
m_adapter.notifyDataSetChanged();
@@ -175,7 +218,7 @@ public class OfflineFeedCategoriesFragment extends Fragment implements OnItemCli
public int getItemViewType(int position) {
Cursor cursor = (Cursor) this.getItem(position);
- if (!m_offlineServices.isSmallScreen() && cursor.getLong(0) == m_selectedCatId) {
+ if (!m_activity.isSmallScreen() && cursor.getLong(0) == m_selectedCatId) {
return VIEW_SELECTED;
} else {
return VIEW_NORMAL;
@@ -251,7 +294,7 @@ public class OfflineFeedCategoriesFragment extends Fragment implements OnItemCli
return catId;
}
- return 0;
+ return -10000;
}
public void setSelectedFeedId(int feedId) {
diff --git a/src/org/fox/ttrss/offline/OfflineFeedsActivity.java b/src/org/fox/ttrss/offline/OfflineFeedsActivity.java
new file mode 100644
index 00000000..35bf05b7
--- /dev/null
+++ b/src/org/fox/ttrss/offline/OfflineFeedsActivity.java
@@ -0,0 +1,253 @@
+package org.fox.ttrss.offline;
+
+import org.fox.ttrss.R;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+
+public class OfflineFeedsActivity extends OfflineActivity implements OfflineHeadlinesEventListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ m_prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
+ setTheme(R.style.DarkTheme);
+ } else {
+ setTheme(R.style.LightTheme);
+ }
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.feeds);
+
+ setSmallScreen(findViewById(R.id.headlines_fragment) == null);
+
+ if (savedInstanceState != null) {
+
+ } else {
+ Intent intent = getIntent();
+
+ if (intent.getIntExtra("feed", -10000) != -10000 || intent.getIntExtra("category", -10000) != -10000 ||
+ intent.getIntExtra("article", -10000) != -10000) {
+
+ if (!isCompatMode()) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ int feedId = intent.getIntExtra("feed", -10000);
+ int catId = intent.getIntExtra("category", -10000);
+ int articleId = intent.getIntExtra("article", -10000);
+ boolean isCat = intent.getBooleanExtra("isCat", false);
+
+ if (articleId != -10000) {
+ ft.replace(R.id.feeds_fragment, new OfflineArticlePager(articleId, feedId, isCat), FRAG_ARTICLE);
+ } else {
+ if (feedId != -10000) {
+ ft.replace(R.id.feeds_fragment, new OfflineHeadlinesFragment(feedId, isCat), FRAG_HEADLINES);
+ }
+
+ if (catId != -10000) {
+ ft.replace(R.id.feeds_fragment, new OfflineFeedsFragment(catId), FRAG_FEEDS);
+ }
+ }
+
+ ft.commit();
+ } else {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ if (m_prefs.getBoolean("enable_cats", false)) {
+ ft.replace(R.id.feeds_fragment, new OfflineFeedCategoriesFragment(), FRAG_CATS);
+ } else {
+ ft.replace(R.id.feeds_fragment, new OfflineFeedsFragment(), FRAG_FEEDS);
+ }
+
+ ft.commit();
+ }
+ }
+
+ setLoadingStatus(R.string.blank, false);
+ findViewById(R.id.loading_container).setVisibility(View.GONE);
+
+ initMenu();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.show_feeds:
+ setUnreadOnly(!getUnreadOnly());
+ initMenu();
+ refresh();
+ return true;
+ default:
+ Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ }
+
+ public void initMenu() {
+ super.initMenu();
+
+ if (m_menu != null) {
+ Fragment ff = getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
+ Fragment cf = getSupportFragmentManager().findFragmentByTag(FRAG_CATS);
+ OfflineArticlePager af = (OfflineArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+ OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ m_menu.setGroupVisible(R.id.menu_group_feeds, (ff != null && ff.isAdded()) || (cf != null && cf.isAdded()));
+
+ m_menu.setGroupVisible(R.id.menu_group_article, af != null && af.isAdded());
+
+ m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded() && getSelectedArticleCount() == 0);
+ m_menu.setGroupVisible(R.id.menu_group_headlines_selection, hf != null && hf.isAdded() && getSelectedArticleCount() != 0);
+
+ MenuItem item = m_menu.findItem(R.id.show_feeds);
+
+ if (getUnreadOnly()) {
+ item.setTitle(R.string.menu_all_feeds);
+ } else {
+ item.setTitle(R.string.menu_unread_feeds);
+ }
+ }
+ }
+
+ public void onCatSelected(int catId) {
+ onCatSelected(catId, m_prefs.getBoolean("browse_cats_like_feeds", false));
+ }
+
+ public void onCatSelected(int catId, boolean openAsFeed) {
+ if (openAsFeed) {
+ onFeedSelected(catId, true, true);
+ } else {
+ if (isSmallScreen()) {
+ Intent intent = new Intent(OfflineFeedsActivity.this, OfflineFeedsActivity.class);
+ intent.putExtra("category", catId);
+
+ startActivityForResult(intent, 0);
+ } else {
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ OfflineFeedsFragment ff = new OfflineFeedsFragment(catId);
+
+ ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS);
+ ft.addToBackStack(null);
+
+ ft.commit();
+ }
+ }
+ }
+
+ public void onFeedSelected(int feedId) {
+ onFeedSelected(feedId, false, true);
+ }
+
+ public void onFeedSelected(int feedId, boolean isCat, boolean open) {
+
+ if (open) {
+ if (isSmallScreen()) {
+
+ Intent intent = new Intent(OfflineFeedsActivity.this, OfflineFeedsActivity.class);
+ intent.putExtra("feed", feedId);
+ intent.putExtra("isCat", isCat);
+
+ startActivityForResult(intent, 0);
+
+ } else {
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ OfflineHeadlinesFragment hf = new OfflineHeadlinesFragment(feedId, isCat);
+ ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
+
+ ft.commit();
+ }
+ }
+ }
+
+ public void catchupFeed(int feedId, boolean isCat) {
+ if (isCat) {
+ SQLiteStatement stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE feed_id IN (SELECT "+
+ BaseColumns._ID+" FROM feeds WHERE cat_id = ?)");
+ stmt.bindLong(1, feedId);
+ stmt.execute();
+ stmt.close();
+ } else {
+ SQLiteStatement stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE feed_id = ?");
+ stmt.bindLong(1, feedId);
+ stmt.execute();
+ stmt.close();
+ }
+
+ refresh();
+ }
+
+ @Override
+ public void onArticleSelected(int articleId, boolean open) {
+ SQLiteStatement stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 " + "WHERE " + BaseColumns._ID
+ + " = ?");
+
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+
+ if (open) {
+ if (isSmallScreen()) {
+
+ OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ Intent intent = new Intent(OfflineFeedsActivity.this, OfflineFeedsActivity.class);
+ intent.putExtra("feed", hf.getFeedId());
+ intent.putExtra("isCat", hf.getFeedIsCat());
+ intent.putExtra("article", articleId);
+
+ startActivityForResult(intent, 0);
+
+ } else {
+
+ OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ Intent intent = new Intent(OfflineFeedsActivity.this, OfflineHeadlinesActivity.class);
+ intent.putExtra("feed", hf.getFeedId());
+ intent.putExtra("isCat", hf.getFeedIsCat());
+ intent.putExtra("article", articleId);
+
+ startActivityForResult(intent, 0);
+ }
+ } else {
+ refresh();
+ }
+
+ initMenu();
+
+ }
+
+ @Override
+ public void onArticleSelected(int articleId) {
+ onArticleSelected(articleId, true);
+ }
+}
diff --git a/src/org/fox/ttrss/offline/OfflineFeedsFragment.java b/src/org/fox/ttrss/offline/OfflineFeedsFragment.java
index 605f506c..87af187c 100644
--- a/src/org/fox/ttrss/offline/OfflineFeedsFragment.java
+++ b/src/org/fox/ttrss/offline/OfflineFeedsFragment.java
@@ -21,6 +21,7 @@ import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
@@ -39,7 +40,7 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
private int m_catId = -1;
private boolean m_enableFeedIcons;
private Cursor m_cursor;
- private OfflineServices m_offlineServices;
+ private OfflineFeedsActivity m_activity;
public OfflineFeedsFragment() {
//
@@ -48,8 +49,31 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
public OfflineFeedsFragment(int catId) {
m_catId = catId;
}
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refresh();
+ }
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+ switch (item.getItemId()) {
+ case R.id.catchup_feed:
+ int feedId = getFeedIdAtPosition(info.position);
+ if (feedId != -10000) {
+ m_activity.catchupFeed(feedId, false);
+ }
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
@@ -66,14 +90,14 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
}
public Cursor createCursor() {
- String unreadOnly = m_offlineServices.getUnreadOnly() ? "unread > 0" : "1";
+ String unreadOnly = m_activity.getUnreadOnly() ? "unread > 0" : "1";
String order = m_prefs.getBoolean("sort_feeds_by_unread", false) ? "unread DESC, title" : "title";
if (m_catId != -1) {
- return m_offlineServices.getReadableDb().query("feeds_unread",
+ return m_activity.getReadableDb().query("feeds_unread",
null, unreadOnly + " AND cat_id = ?", new String[] { String.valueOf(m_catId) }, null, null, order);
} else {
- return m_offlineServices.getReadableDb().query("feeds_unread",
+ return m_activity.getReadableDb().query("feeds_unread",
null, unreadOnly, null, null, null, order);
}
}
@@ -129,7 +153,7 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
public void onAttach(Activity activity) {
super.onAttach(activity);
- m_offlineServices = (OfflineServices)activity;
+ m_activity = (OfflineFeedsActivity)activity;
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
m_prefs.registerOnSharedPreferenceChangeListener(this);
@@ -155,9 +179,9 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
int feedId = (int) cursor.getLong(0);
Log.d(TAG, "clicked on feed " + feedId);
- m_offlineServices.onFeedSelected(feedId);
+ m_activity.onFeedSelected(feedId);
- if (!m_offlineServices.isSmallScreen())
+ if (!m_activity.isSmallScreen())
m_selectedFeedId = feedId;
m_adapter.notifyDataSetChanged();
@@ -199,7 +223,7 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
public int getItemViewType(int position) {
Cursor cursor = (Cursor) this.getItem(position);
- if (!m_offlineServices.isSmallScreen() && cursor.getLong(0) == m_selectedFeedId) {
+ if (!m_activity.isSmallScreen() && cursor.getLong(0) == m_selectedFeedId) {
return VIEW_SELECTED;
} else {
return VIEW_NORMAL;
@@ -295,7 +319,7 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
return feedId;
}
- return 0;
+ return -10000;
}
public void setSelectedFeedId(int feedId) {
diff --git a/src/org/fox/ttrss/offline/OfflineHeadlinesActivity.java b/src/org/fox/ttrss/offline/OfflineHeadlinesActivity.java
new file mode 100644
index 00000000..d9053f27
--- /dev/null
+++ b/src/org/fox/ttrss/offline/OfflineHeadlinesActivity.java
@@ -0,0 +1,142 @@
+package org.fox.ttrss.offline;
+
+import org.fox.ttrss.GlobalState;
+import org.fox.ttrss.R;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+
+public class OfflineHeadlinesActivity extends OfflineActivity implements OfflineHeadlinesEventListener {
+ @SuppressWarnings("unused")
+ private final String TAG = this.getClass().getSimpleName();
+
+ protected SharedPreferences m_prefs;
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ m_prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
+ setTheme(R.style.DarkTheme);
+ } else {
+ setTheme(R.style.LightTheme);
+ }
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.headlines);
+
+ if (!isCompatMode()) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ setSmallScreen(findViewById(R.id.headlines_fragment) == null);
+
+ if (savedInstanceState == null) {
+ Intent i = getIntent();
+
+ if (i.getExtras() != null) {
+ int feedId = i.getIntExtra("feed", 0);
+ boolean isCat = i.getBooleanExtra("isCat", false);
+ int articleId = i.getIntExtra("article", 0);
+ String searchQuery = i.getStringExtra("searchQuery");
+
+ OfflineHeadlinesFragment hf = new OfflineHeadlinesFragment(feedId, isCat);
+ OfflineArticlePager af = new OfflineArticlePager(articleId, feedId, isCat);
+
+ hf.setActiveArticleId(articleId);
+
+ hf.setSearchQuery(searchQuery);
+ af.setSearchQuery(searchQuery);
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
+ ft.replace(R.id.article_fragment, af, FRAG_ARTICLE);
+
+ ft.commit();
+
+ Cursor c;
+
+ if (isCat) {
+ c = getCatById(feedId);
+ } else {
+ c = getFeedById(feedId);
+ }
+
+ if (c != null) {
+ setTitle(c.getString(c.getColumnIndex("title")));
+ c.close();
+ }
+
+ }
+ }
+
+ setLoadingStatus(R.string.blank, false);
+ findViewById(R.id.loading_container).setVisibility(View.GONE);
+
+ initMenu();
+ }
+
+ @Override
+ public void onArticleSelected(int articleId, boolean open) {
+ SQLiteStatement stmt = getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 " + "WHERE " + BaseColumns._ID
+ + " = ?");
+
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+
+ if (open) {
+ OfflineArticlePager af = (OfflineArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+
+ af.setArticleId(articleId);
+ } else {
+ OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ hf.setActiveArticleId(articleId);
+ }
+
+ GlobalState.getInstance().m_selectedArticleId = articleId;
+
+ initMenu();
+ refresh();
+ }
+
+ @Override
+ protected void initMenu() {
+ super.initMenu();
+
+ if (m_menu != null) {
+ m_menu.setGroupVisible(R.id.menu_group_feeds, false);
+
+ OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.getSelectedArticleCount() == 0);
+ m_menu.setGroupVisible(R.id.menu_group_headlines_selection, hf != null && hf.getSelectedArticleCount() != 0);
+
+ Fragment af = getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE);
+
+ m_menu.setGroupVisible(R.id.menu_group_article, af != null);
+
+ m_menu.findItem(R.id.search).setVisible(false);
+ }
+ }
+
+ @Override
+ public void onArticleSelected(int articleId) {
+ onArticleSelected(articleId, true);
+ }
+}
diff --git a/src/org/fox/ttrss/offline/OfflineHeadlinesEventListener.java b/src/org/fox/ttrss/offline/OfflineHeadlinesEventListener.java
new file mode 100644
index 00000000..cdff5913
--- /dev/null
+++ b/src/org/fox/ttrss/offline/OfflineHeadlinesEventListener.java
@@ -0,0 +1,7 @@
+package org.fox.ttrss.offline;
+
+
+public interface OfflineHeadlinesEventListener {
+ void onArticleSelected(int articleId, boolean open);
+ void onArticleSelected(int articleId);
+}
diff --git a/src/org/fox/ttrss/offline/OfflineHeadlinesFragment.java b/src/org/fox/ttrss/offline/OfflineHeadlinesFragment.java
index e5428f59..158e36cd 100644
--- a/src/org/fox/ttrss/offline/OfflineHeadlinesFragment.java
+++ b/src/org/fox/ttrss/offline/OfflineHeadlinesFragment.java
@@ -5,6 +5,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
+import org.fox.ttrss.GlobalState;
import org.fox.ttrss.R;
import org.jsoup.Jsoup;
@@ -27,6 +28,7 @@ import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -55,10 +57,12 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
private Cursor m_cursor;
private ArticleListAdapter m_adapter;
- private OfflineServices m_offlineServices;
+ private OfflineHeadlinesEventListener m_listener;
+ private OfflineActivity m_activity;
private ImageGetter m_dummyGetter = new ImageGetter() {
+ @SuppressWarnings("deprecation")
@Override
public Drawable getDrawable(String source) {
return new BitmapDrawable();
@@ -83,7 +87,7 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
}
public int getSelectedArticleCount() {
- Cursor c = m_offlineServices.getReadableDb().query("articles",
+ Cursor c = m_activity.getReadableDb().query("articles",
new String[] { "COUNT(*)" }, "selected = 1", null, null, null, null);
c.moveToFirst();
int selected = c.getInt(0);
@@ -93,10 +97,122 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
}
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+
+ switch (item.getItemId()) {
+ case R.id.article_link_copy:
+ if (true) {
+ int articleId = getArticleIdAtPosition(info.position);
+
+ Cursor article = m_activity.getArticleById(articleId);
+
+ if (article != null) {
+ m_activity.copyToClipboard(article.getString(article.getColumnIndex("link")));
+ article.close();
+ }
+ }
+ return true;
+ case R.id.selection_toggle_marked:
+ if (getSelectedArticleCount() > 0) {
+ SQLiteStatement stmt = m_activity.getWritableDb()
+ .compileStatement(
+ "UPDATE articles SET marked = NOT marked WHERE selected = 1");
+ stmt.execute();
+ stmt.close();
+ } else {
+ int articleId = getArticleIdAtPosition(info.position);
+
+ SQLiteStatement stmt = m_activity.getWritableDb().compileStatement(
+ "UPDATE articles SET marked = NOT marked WHERE "
+ + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+ }
+ refresh();
+ return true;
+ case R.id.selection_toggle_published:
+ if (getSelectedArticleCount() > 0) {
+ SQLiteStatement stmt = m_activity.getWritableDb()
+ .compileStatement(
+ "UPDATE articles SET published = NOT published WHERE selected = 1");
+ stmt.execute();
+ stmt.close();
+ } else {
+ int articleId = getArticleIdAtPosition(info.position);
+
+ SQLiteStatement stmt = m_activity.getWritableDb().compileStatement(
+ "UPDATE articles SET published = NOT published WHERE "
+ + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+ }
+ refresh();
+ return true;
+ case R.id.selection_toggle_unread:
+ if (getSelectedArticleCount() > 0) {
+ SQLiteStatement stmt = m_activity.getWritableDb()
+ .compileStatement(
+ "UPDATE articles SET unread = NOT unread WHERE selected = 1");
+ stmt.execute();
+ stmt.close();
+ } else {
+ int articleId = getArticleIdAtPosition(info.position);
+
+ SQLiteStatement stmt = m_activity.getWritableDb().compileStatement(
+ "UPDATE articles SET unread = NOT unread WHERE "
+ + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
+ }
+ refresh();
+ return true;
+ case R.id.share_article:
+ if (true) {
+ int articleId = getArticleIdAtPosition(info.position);
+ m_activity.shareArticle(articleId);
+ }
+ return true;
+ case R.id.catchup_above:
+ if (true) {
+ int articleId = getArticleIdAtPosition(info.position);
+
+ SQLiteStatement stmt = null;
+
+ if (m_feedIsCat) {
+ stmt = m_activity.getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE " +
+ "updated >= (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " +
+ "AND feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)");
+ } else {
+ stmt = m_activity.getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 WHERE " +
+ "updated >= (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " +
+ "AND feed_id = ?");
+ }
+
+ stmt.bindLong(1, articleId);
+ stmt.bindLong(2, m_feedId);
+ stmt.execute();
+ stmt.close();
+ }
+ refresh();
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- getActivity().getMenuInflater().inflate(R.menu.headlines_menu, menu);
+ getActivity().getMenuInflater().inflate(R.menu.headlines_context_menu, menu);
if (getSelectedArticleCount() > 0) {
menu.setHeaderTitle(R.string.headline_context_multiple);
@@ -107,12 +223,31 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
menu.setHeaderTitle(c.getString(c.getColumnIndex("title")));
//c.close();
menu.setGroupVisible(R.id.menu_group_single_article, true);
+
+ menu.findItem(R.id.set_labels).setVisible(false);
+ menu.findItem(R.id.article_set_note).setVisible(false);
}
super.onCreateContextMenu(menu, v, menuInfo);
}
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (GlobalState.getInstance().m_selectedArticleId != 0) {
+ m_activeArticleId = GlobalState.getInstance().m_selectedArticleId;
+ GlobalState.getInstance().m_selectedArticleId = 0;
+ }
+
+ if (m_activeArticleId != 0) {
+ setActiveArticleId(m_activeArticleId);
+ }
+
+ refresh();
+ }
+
public void refresh() {
if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close();
@@ -120,7 +255,6 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
if (m_cursor != null) {
m_adapter.changeCursor(m_cursor);
- setActiveArticleId(m_offlineServices.getSelectedArticleId());
m_adapter.notifyDataSetChanged();
}
}
@@ -135,6 +269,8 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
m_combinedMode = savedInstanceState.getBoolean("combinedMode");
m_searchQuery = (String) savedInstanceState.getCharSequence("searchQuery");
m_feedIsCat = savedInstanceState.getBoolean("feedIsCat");
+ } else {
+ m_activity.getWritableDb().execSQL("UPDATE articles SET selected = 0 ");
}
View view = inflater.inflate(R.layout.headlines_fragment, container, false);
@@ -150,7 +286,7 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
list.setEmptyView(view.findViewById(R.id.no_headlines));
registerForContextMenu(list);
- if (m_offlineServices.isSmallScreen() || m_offlineServices.isPortrait())
+ if (m_activity.isSmallScreen() || m_activity.isPortrait())
view.findViewById(R.id.headlines_fragment).setPadding(0, 0, 0, 0);
getActivity().setProgressBarIndeterminateVisibility(false);
@@ -167,12 +303,12 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
feedClause = "feed_id = ?";
}
- if (m_searchQuery.equals("")) {
- return m_offlineServices.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
+ if (m_searchQuery == null || m_searchQuery.equals("")) {
+ return m_activity.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
new String[] { "articles.*", "feeds.title AS feed_title" }, feedClause,
new String[] { String.valueOf(m_feedId) }, null, null, "updated DESC");
} else {
- return m_offlineServices.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
+ return m_activity.getReadableDb().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")",
new String[] { "articles.*", "feeds.title AS feed_title" },
feedClause + " AND (articles.title LIKE '%' || ? || '%' OR content LIKE '%' || ? || '%')",
new String[] { String.valueOf(m_feedId), m_searchQuery, m_searchQuery }, null, null, "updated DESC");
@@ -182,10 +318,11 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- m_offlineServices = (OfflineServices)activity;
+ m_listener = (OfflineHeadlinesEventListener) activity;
+ m_activity = (OfflineActivity) activity;
m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
- m_combinedMode = m_prefs.getBoolean("combined_mode", false);
+ m_combinedMode = false; /* m_prefs.getBoolean("combined_mode", false); */
}
@Override
@@ -197,17 +334,22 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
if (list != null) {
Cursor cursor = (Cursor)list.getItemAtPosition(position);
- m_activeArticleId = cursor.getInt(0);
-
- SQLiteStatement stmtUpdate = m_offlineServices.getWritableDb().compileStatement("UPDATE articles SET unread = 0 " +
- "WHERE " + BaseColumns._ID + " = ?");
+ int articleId = cursor.getInt(0);
- stmtUpdate.bindLong(1, m_activeArticleId);
- stmtUpdate.execute();
- stmtUpdate.close();
+ if (getActivity().findViewById(R.id.article_fragment) != null) {
+ m_activeArticleId = articleId;
+ }
if (!m_combinedMode) {
- m_offlineServices.openArticle(m_activeArticleId, 0);
+ m_listener.onArticleSelected(articleId);
+ } else {
+ SQLiteStatement stmt = m_activity.getWritableDb().compileStatement(
+ "UPDATE articles SET unread = 0 " + "WHERE " + BaseColumns._ID
+ + " = ?");
+
+ stmt.bindLong(1, articleId);
+ stmt.execute();
+ stmt.close();
}
refresh();
@@ -321,7 +463,10 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
if (ft != null && feedTitleIndex != -1 && m_feedIsCat) {
String feedTitle = article.getString(feedTitleIndex);
- if (feedTitle != null) {
+ if (feedTitle.length() > 20)
+ feedTitle = feedTitle.substring(0, 20) + "...";
+
+ if (feedTitle.length() > 0) {
ft.setText(feedTitle);
} else {
ft.setVisibility(View.GONE);
@@ -339,7 +484,7 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
@Override
public void onClick(View v) {
- SQLiteStatement stmtUpdate = m_offlineServices.getWritableDb().compileStatement("UPDATE articles SET marked = NOT marked " +
+ SQLiteStatement stmtUpdate = m_activity.getWritableDb().compileStatement("UPDATE articles SET marked = NOT marked " +
"WHERE " + BaseColumns._ID + " = ?");
stmtUpdate.bindLong(1, articleId);
@@ -360,7 +505,7 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
@Override
public void onClick(View v) {
- SQLiteStatement stmtUpdate = m_offlineServices.getWritableDb().compileStatement("UPDATE articles SET published = NOT published " +
+ SQLiteStatement stmtUpdate = m_activity.getWritableDb().compileStatement("UPDATE articles SET published = NOT published " +
"WHERE " + BaseColumns._ID + " = ?");
stmtUpdate.bindLong(1, articleId);
@@ -438,7 +583,7 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
public void onClick(View view) {
CheckBox cb = (CheckBox)view;
- SQLiteStatement stmtUpdate = m_offlineServices.getWritableDb().compileStatement("UPDATE articles SET selected = ? " +
+ SQLiteStatement stmtUpdate = m_activity.getWritableDb().compileStatement("UPDATE articles SET selected = ? " +
"WHERE " + BaseColumns._ID + " = ?");
stmtUpdate.bindLong(1, cb.isChecked() ? 1 : 0);
@@ -448,7 +593,7 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
refresh();
- m_offlineServices.initMainMenu();
+ m_activity.initMenu();
}
});
@@ -476,13 +621,19 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
public void setActiveArticleId(int articleId) {
m_activeArticleId = articleId;
- // m_adapter.notifyDataSetChanged();
-
- ListView list = (ListView)getView().findViewById(R.id.headlines);
+ try {
+ m_adapter.notifyDataSetChanged();
+
+ ListView list = (ListView)getView().findViewById(R.id.headlines);
- if (list != null) {
- list.setSelection(getArticleIdPosition(articleId));
- }
+ Log.d(TAG, articleId + " position " + getArticleIdPosition(articleId));
+
+ if (list != null) {
+ list.setSelection(getArticleIdPosition(articleId));
+ }
+ } catch (NullPointerException e) {
+ // invoked before view is created, nvm
+ }
}
public Cursor getArticleAtPosition(int position) {
@@ -520,8 +671,19 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis
public void setSearchQuery(String query) {
if (!m_searchQuery.equals(query)) {
m_searchQuery = query;
- refresh();
}
}
+
+ public int getFeedId() {
+ return m_feedId;
+ }
+
+ public boolean getFeedIsCat() {
+ return m_feedIsCat;
+ }
+
+ public String getSearchQuery() {
+ return m_searchQuery;
+ }
}
diff --git a/src/org/fox/ttrss/offline/OfflineServices.java b/src/org/fox/ttrss/offline/OfflineServices.java
deleted file mode 100644
index 8483c97f..00000000
--- a/src/org/fox/ttrss/offline/OfflineServices.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.fox.ttrss.offline;
-
-import org.fox.ttrss.OnlineServices;
-
-import android.database.sqlite.SQLiteDatabase;
-
-public interface OfflineServices {
- public int getActiveFeedId();
- public SQLiteDatabase getReadableDb();
- public SQLiteDatabase getWritableDb();
- public int getRelativeArticleId(int baseId, int feedId, OnlineServices.RelativeArticle mode);
- public void onFeedSelected(int feedId);
- public void onCatSelected(int catId);
- public void openArticle(int articleId, int compatAnimation);
- public boolean getUnreadOnly();
- public int getSelectedArticleId();
- public void initMainMenu();
- public boolean isSmallScreen();
- public void setSelectedArticleId(int articleId);
- public boolean activeFeedIsCat();
- public boolean isPortrait();
-}
diff --git a/src/org/fox/ttrss/offline/OfflineUploadService.java b/src/org/fox/ttrss/offline/OfflineUploadService.java
index 84a7fc92..bacb1ede 100644
--- a/src/org/fox/ttrss/offline/OfflineUploadService.java
+++ b/src/org/fox/ttrss/offline/OfflineUploadService.java
@@ -3,7 +3,7 @@ package org.fox.ttrss.offline;
import java.util.HashMap;
import org.fox.ttrss.ApiRequest;
-import org.fox.ttrss.MainActivity;
+import org.fox.ttrss.OnlineActivity;
import org.fox.ttrss.R;
import org.fox.ttrss.util.DatabaseHelper;
@@ -48,12 +48,13 @@ public class OfflineUploadService extends IntentService {
m_nmgr.cancel(NOTIFY_UPLOADING);
}
+ @SuppressWarnings("deprecation")
private void updateNotification(String msg) {
Notification notification = new Notification(R.drawable.icon,
getString(R.string.notify_uploading_title), System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
- new Intent(this, MainActivity.class), 0);
+ new Intent(this, OnlineActivity.class), 0);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
diff --git a/src/org/fox/ttrss/types/ArticleList.java b/src/org/fox/ttrss/types/ArticleList.java
index a03546c3..b68b4362 100644
--- a/src/org/fox/ttrss/types/ArticleList.java
+++ b/src/org/fox/ttrss/types/ArticleList.java
@@ -21,6 +21,14 @@ public class ArticleList extends ArrayList<Article> implements Parcelable {
}
}
+ public Article findById(int id) {
+ for (Article a : this) {
+ if (a.id == id)
+ return a;
+ }
+ return null;
+ }
+
public void readFromParcel(Parcel in) {
int length = in.readInt();
diff --git a/src/org/fox/ttrss/types/Feed.java b/src/org/fox/ttrss/types/Feed.java
index 91d12308..c0649be3 100644
--- a/src/org/fox/ttrss/types/Feed.java
+++ b/src/org/fox/ttrss/types/Feed.java
@@ -28,6 +28,16 @@ public class Feed implements Comparable<Feed>, Parcelable {
}
+ public boolean equals(Feed feed) {
+ if (feed == this)
+ return true;
+
+ if (feed == null)
+ return false;
+
+ return feed.id == this.id && (this.title == null || this.title.equals(feed.title)) && this.is_cat == feed.is_cat;
+ }
+
@Override
public int compareTo(Feed feed) {
if (feed.unread != this.unread)
diff --git a/src/org/fox/ttrss/util/Base64.java b/src/org/fox/ttrss/util/Base64.java
deleted file mode 100644
index 5c8136db..00000000
--- a/src/org/fox/ttrss/util/Base64.java
+++ /dev/null
@@ -1,582 +0,0 @@
-// Portions copyright 2002, Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package org.fox.ttrss.util;
-
-// This code was converted from code at http://iharder.sourceforge.net/base64/
-// Lots of extraneous features were removed.
-/* The original code said:
- * <p>
- * I am placing this code in the Public Domain. Do with it as you will.
- * This software comes with no guarantees or warranties but with
- * plenty of well-wishing instead!
- * Please visit
- * <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>
- * periodically to check for updates or to contribute improvements.
- * </p>
- *
- * @author Robert Harder
- * @author [email protected]
- * @version 1.3
- */
-
-/**
- * Base64 converter class. This code is not a complete MIME encoder; it simply
- * converts binary data to base64 data and back.
- *
- * <p>
- * Note {@link CharBase64} is a GWT-compatible implementation of this class.
- */
-public class Base64 {
- /** Specify encoding (value is {@code true}). */
- public final static boolean ENCODE = true;
-
- /** Specify decoding (value is {@code false}). */
- public final static boolean DECODE = false;
-
- /** The equals sign (=) as a byte. */
- private final static byte EQUALS_SIGN = (byte) '=';
-
- /** The new line character (\n) as a byte. */
- private final static byte NEW_LINE = (byte) '\n';
-
- /**
- * The 64 valid Base64 values.
- */
- private final static byte[] ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
- (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
- (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
- (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
- (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
- (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
- (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
-
- /**
- * The 64 valid web safe Base64 values.
- */
- private final static byte[] WEBSAFE_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
- (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
- (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
- (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
- (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
- (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
- (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_' };
-
- /**
- * Translates a Base64 value to either its 6-bit reconstruction value or a
- * negative number indicating some other meaning.
- **/
- private final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
- // 0
- // -
- // 8
- -5, -5, // Whitespace: Tab and Linefeed
- -9, -9, // Decimal 11 - 12
- -5, // Whitespace: Carriage Return
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
- // 26
- -9, -9, -9, -9, -9, // Decimal 27 - 31
- -5, // Whitespace: Space
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
- 62, // Plus sign at decimal 43
- -9, -9, -9, // Decimal 44 - 46
- 63, // Slash at decimal 47
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
- -9, -9, -9, // Decimal 58 - 60
- -1, // Equals sign at decimal 61
- -9, -9, -9, // Decimal 62 - 64
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
- // 'N'
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
- // through 'Z'
- -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
- 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
- // through 'm'
- 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
- // through 'z'
- -9, -9, -9, -9, -9 // Decimal 123 - 127
- /*
- * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
- */
- };
-
- /** The web safe decodabet */
- private final static byte[] WEBSAFE_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
- // 0
- // -
- // 8
- -5, -5, // Whitespace: Tab and Linefeed
- -9, -9, // Decimal 11 - 12
- -5, // Whitespace: Carriage Return
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
- // 26
- -9, -9, -9, -9, -9, // Decimal 27 - 31
- -5, // Whitespace: Space
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
- 62, // Dash '-' sign at decimal 45
- -9, -9, // Decimal 46-47
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
- -9, -9, -9, // Decimal 58 - 60
- -1, // Equals sign at decimal 61
- -9, -9, -9, // Decimal 62 - 64
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
- // 'N'
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
- // through 'Z'
- -9, -9, -9, -9, // Decimal 91-94
- 63, // Underscore '_' at decimal 95
- -9, // Decimal 96
- 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
- // through 'm'
- 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
- // through 'z'
- -9, -9, -9, -9, -9 // Decimal 123 - 127
- /*
- * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
- * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
- */
- };
-
- // Indicates white space in encoding
- private final static byte WHITE_SPACE_ENC = -5;
- // Indicates equals sign in encoding
- private final static byte EQUALS_SIGN_ENC = -1;
-
- /** Defeats instantiation. */
- private Base64() {
- }
-
- /* ******** E N C O D I N G M E T H O D S ******** */
-
- /**
- * Encodes up to three bytes of the array <var>source</var> and writes the
- * resulting four Base64 bytes to <var>destination</var>. The source and
- * destination arrays can be manipulated anywhere along their length by
- * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
- * does not check to make sure your arrays are large enough to accommodate
- * <var>srcOffset</var> + 3 for the <var>source</var> array or
- * <var>destOffset</var> + 4 for the <var>destination</var> array. The
- * actual number of significant bytes in your array is given by
- * <var>numSigBytes</var>.
- *
- * @param source
- * the array to convert
- * @param srcOffset
- * the index where conversion begins
- * @param numSigBytes
- * the number of significant bytes in your array
- * @param destination
- * the array to hold the conversion
- * @param destOffset
- * the index where output will be put
- * @param alphabet
- * is the encoding alphabet
- * @return the <var>destination</var> array
- * @since 1.3
- */
- private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
- // 1 2 3
- // 01234567890123456789012345678901 Bit position
- // --------000000001111111122222222 Array position from threeBytes
- // --------| || || || | Six bit groups to index alphabet
- // >>18 >>12 >> 6 >> 0 Right shift necessary
- // 0x3f 0x3f 0x3f Additional AND
-
- // Create buffer with zero-padding if there are only one or two
- // significant bytes passed in the array.
- // We have to shift left 24 in order to flush out the 1's that appear
- // when Java treats a value as negative that is cast from a byte to an
- // int.
- int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
- | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
- | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
-
- switch (numSigBytes) {
- case 3:
- destination[destOffset] = alphabet[(inBuff >>> 18)];
- destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
- destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
- return destination;
- case 2:
- destination[destOffset] = alphabet[(inBuff >>> 18)];
- destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
- destination[destOffset + 3] = EQUALS_SIGN;
- return destination;
- case 1:
- destination[destOffset] = alphabet[(inBuff >>> 18)];
- destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- destination[destOffset + 2] = EQUALS_SIGN;
- destination[destOffset + 3] = EQUALS_SIGN;
- return destination;
- default:
- return destination;
- } // end switch
- } // end encode3to4
-
- /**
- * Encodes a byte array into Base64 notation. Equivalent to calling {@code
- * encodeBytes(source, 0, source.length)}
- *
- * @param source
- * The data to convert
- * @since 1.4
- */
- public static String encode(byte[] source) {
- return encode(source, 0, source.length, ALPHABET, true);
- }
-
- /**
- * Encodes a byte array into web safe Base64 notation.
- *
- * @param source
- * The data to convert
- * @param doPadding
- * is {@code true} to pad result with '=' chars if it does not
- * fall on 3 byte boundaries
- */
- public static String encodeWebSafe(byte[] source, boolean doPadding) {
- return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
- }
-
- /**
- * Encodes a byte array into Base64 notation.
- *
- * @param source
- * the data to convert
- * @param off
- * offset in array where conversion should begin
- * @param len
- * length of data to convert
- * @param alphabet
- * the encoding alphabet
- * @param doPadding
- * is {@code true} to pad result with '=' chars if it does not
- * fall on 3 byte boundaries
- * @since 1.4
- */
- public static String encode(byte[] source, int off, int len, byte[] alphabet, boolean doPadding) {
- byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
- int outLen = outBuff.length;
-
- // If doPadding is false, set length to truncate '='
- // padding characters
- while (doPadding == false && outLen > 0) {
- if (outBuff[outLen - 1] != '=') {
- break;
- }
- outLen -= 1;
- }
-
- return new String(outBuff, 0, outLen);
- }
-
- /**
- * Encodes a byte array into Base64 notation.
- *
- * @param source
- * the data to convert
- * @param off
- * offset in array where conversion should begin
- * @param len
- * length of data to convert
- * @param alphabet
- * is the encoding alphabet
- * @param maxLineLength
- * maximum length of one line.
- * @return the BASE64-encoded byte array
- */
- public static byte[] encode(byte[] source, int off, int len, byte[] alphabet, int maxLineLength) {
- int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
- int len43 = lenDiv3 * 4;
- byte[] outBuff = new byte[len43 // Main 4:3
- + (len43 / maxLineLength)]; // New lines
-
- int d = 0;
- int e = 0;
- int len2 = len - 2;
- int lineLength = 0;
- for (; d < len2; d += 3, e += 4) {
-
- // The following block of code is the same as
- // encode3to4( source, d + off, 3, outBuff, e, alphabet );
- // but inlined for faster encoding (~20% improvement)
- int inBuff = ((source[d + off] << 24) >>> 8) | ((source[d + 1 + off] << 24) >>> 16) | ((source[d + 2 + off] << 24) >>> 24);
- outBuff[e] = alphabet[(inBuff >>> 18)];
- outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
- outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
-
- lineLength += 4;
- if (lineLength == maxLineLength) {
- outBuff[e + 4] = NEW_LINE;
- e++;
- lineLength = 0;
- } // end if: end of line
- } // end for: each piece of array
-
- if (d < len) {
- encode3to4(source, d + off, len - d, outBuff, e, alphabet);
-
- lineLength += 4;
- if (lineLength == maxLineLength) {
- // Add a last newline
- outBuff[e + 4] = NEW_LINE;
- e++;
- }
- e += 4;
- }
-
- assert (e == outBuff.length);
- return outBuff;
- }
-
- /* ******** D E C O D I N G M E T H O D S ******** */
-
- /**
- * Decodes four bytes from array <var>source</var> and writes the resulting
- * bytes (up to three of them) to <var>destination</var>. The source and
- * destination arrays can be manipulated anywhere along their length by
- * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
- * does not check to make sure your arrays are large enough to accommodate
- * <var>srcOffset</var> + 4 for the <var>source</var> array or
- * <var>destOffset</var> + 3 for the <var>destination</var> array. This
- * method returns the actual number of bytes that were converted from the
- * Base64 encoding.
- *
- *
- * @param source
- * the array to convert
- * @param srcOffset
- * the index where conversion begins
- * @param destination
- * the array to hold the conversion
- * @param destOffset
- * the index where output will be put
- * @param decodabet
- * the decodabet for decoding Base64 content
- * @return the number of decoded bytes converted
- * @since 1.3
- */
- private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset, byte[] decodabet) {
- // Example: Dk==
- if (source[srcOffset + 2] == EQUALS_SIGN) {
- int outBuff = ((decodabet[source[srcOffset]] << 24) >>> 6) | ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
-
- destination[destOffset] = (byte) (outBuff >>> 16);
- return 1;
- } else if (source[srcOffset + 3] == EQUALS_SIGN) {
- // Example: DkL=
- int outBuff = ((decodabet[source[srcOffset]] << 24) >>> 6) | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
- | ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
-
- destination[destOffset] = (byte) (outBuff >>> 16);
- destination[destOffset + 1] = (byte) (outBuff >>> 8);
- return 2;
- } else {
- // Example: DkLE
- int outBuff = ((decodabet[source[srcOffset]] << 24) >>> 6) | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
- | ((decodabet[source[srcOffset + 2]] << 24) >>> 18) | ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
-
- destination[destOffset] = (byte) (outBuff >> 16);
- destination[destOffset + 1] = (byte) (outBuff >> 8);
- destination[destOffset + 2] = (byte) (outBuff);
- return 3;
- }
- } // end decodeToBytes
-
- /**
- * Decodes data from Base64 notation.
- *
- * @param s
- * the string to decode (decoded in default encoding)
- * @return the decoded data
- * @since 1.4
- */
- public static byte[] decode(String s) throws Base64DecoderException {
- byte[] bytes = s.getBytes();
- return decode(bytes, 0, bytes.length);
- }
-
- /**
- * Decodes data from web safe Base64 notation. Web safe encoding uses '-'
- * instead of '+', '_' instead of '/'
- *
- * @param s
- * the string to decode (decoded in default encoding)
- * @return the decoded data
- */
- public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
- byte[] bytes = s.getBytes();
- return decodeWebSafe(bytes, 0, bytes.length);
- }
-
- /**
- * Decodes Base64 content in byte array format and returns the decoded byte
- * array.
- *
- * @param source
- * The Base64 encoded data
- * @return decoded data
- * @since 1.3
- * @throws Base64DecoderException
- */
- public static byte[] decode(byte[] source) throws Base64DecoderException {
- return decode(source, 0, source.length);
- }
-
- /**
- * Decodes web safe Base64 content in byte array format and returns the
- * decoded data. Web safe encoding uses '-' instead of '+', '_' instead of
- * '/'
- *
- * @param source
- * the string to decode (decoded in default encoding)
- * @return the decoded data
- */
- public static byte[] decodeWebSafe(byte[] source) throws Base64DecoderException {
- return decodeWebSafe(source, 0, source.length);
- }
-
- /**
- * Decodes Base64 content in byte array format and returns the decoded byte
- * array.
- *
- * @param source
- * the Base64 encoded data
- * @param off
- * the offset of where to begin decoding
- * @param len
- * the length of characters to decode
- * @return decoded data
- * @since 1.3
- * @throws Base64DecoderException
- */
- public static byte[] decode(byte[] source, int off, int len) throws Base64DecoderException {
- return decode(source, off, len, DECODABET);
- }
-
- /**
- * Decodes web safe Base64 content in byte array format and returns the
- * decoded byte array. Web safe encoding uses '-' instead of '+', '_'
- * instead of '/'
- *
- * @param source
- * the Base64 encoded data
- * @param off
- * the offset of where to begin decoding
- * @param len
- * the length of characters to decode
- * @return decoded data
- */
- public static byte[] decodeWebSafe(byte[] source, int off, int len) throws Base64DecoderException {
- return decode(source, off, len, WEBSAFE_DECODABET);
- }
-
- /**
- * Decodes Base64 content using the supplied decodabet and returns the
- * decoded byte array.
- *
- * @param source
- * the Base64 encoded data
- * @param off
- * the offset of where to begin decoding
- * @param len
- * the length of characters to decode
- * @param decodabet
- * the decodabet for decoding Base64 content
- * @return decoded data
- */
- public static byte[] decode(byte[] source, int off, int len, byte[] decodabet) throws Base64DecoderException {
- int len34 = len * 3 / 4;
- byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
- int outBuffPosn = 0;
-
- byte[] b4 = new byte[4];
- int b4Posn = 0;
- int i = 0;
- byte sbiCrop = 0;
- byte sbiDecode = 0;
- for (i = 0; i < len; i++) {
- sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven
- // bits
- sbiDecode = decodabet[sbiCrop];
-
- if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or
- // better
- if (sbiDecode >= EQUALS_SIGN_ENC) {
- // An equals sign (for padding) must not occur at position 0
- // or 1
- // and must be the last byte[s] in the encoded value
- if (sbiCrop == EQUALS_SIGN) {
- int bytesLeft = len - i;
- byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
- if (b4Posn == 0 || b4Posn == 1) {
- throw new Base64DecoderException("invalid padding byte '=' at byte offset " + i);
- } else if ((b4Posn == 3 && bytesLeft > 2) || (b4Posn == 4 && bytesLeft > 1)) {
- throw new Base64DecoderException("padding byte '=' falsely signals end of encoded value " + "at offset " + i);
- } else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
- throw new Base64DecoderException("encoded value has invalid trailing byte");
- }
- break;
- }
-
- b4[b4Posn++] = sbiCrop;
- if (b4Posn == 4) {
- outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
- b4Posn = 0;
- }
- }
- } else {
- throw new Base64DecoderException("Bad Base64 input character at " + i + ": " + source[i + off] + "(decimal)");
- }
- }
-
- // Because web safe encoding allows non padding base64 encodes, we
- // need to pad the rest of the b4 buffer with equal signs when
- // b4Posn != 0. There can be at most 2 equal signs at the end of
- // four characters, so the b4 buffer must have two or three
- // characters. This also catches the case where the input is
- // padded with EQUALS_SIGN
- if (b4Posn != 0) {
- if (b4Posn == 1) {
- // Ensure you have set your public key
- throw new Base64DecoderException("single trailing character at offset " + (len - 1));
- }
- b4[b4Posn++] = EQUALS_SIGN;
- outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
- }
-
- byte[] out = new byte[outBuffPosn];
- System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
- return out;
- }
-}
diff --git a/src/org/fox/ttrss/util/Base64DecoderException.java b/src/org/fox/ttrss/util/Base64DecoderException.java
deleted file mode 100644
index 7b7be229..00000000
--- a/src/org/fox/ttrss/util/Base64DecoderException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2002, Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package org.fox.ttrss.util;;
-
-/**
- * Exception thrown when encountering an invalid Base64 input character.
- *
- * @author nelson
- */
-public class Base64DecoderException extends Exception {
- public Base64DecoderException() {
- super();
- }
-
- public Base64DecoderException(String s) {
- super(s);
- }
-
- private static final long serialVersionUID = 1L;
-}
diff --git a/src/org/fox/ttrss/util/EasySSLSocketFactory.java b/src/org/fox/ttrss/util/EasySSLSocketFactory.java
deleted file mode 100644
index f0c2d3ad..00000000
--- a/src/org/fox/ttrss/util/EasySSLSocketFactory.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package org.fox.ttrss.util;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-
-import org.apache.http.conn.ConnectTimeoutException;
-import org.apache.http.conn.scheme.LayeredSocketFactory;
-import org.apache.http.conn.scheme.SocketFactory;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-
-public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory
-{
- private SSLContext sslcontext = null;
-
- private static SSLContext createEasySSLContext() throws IOException
- {
- try
- {
- SSLContext context = SSLContext.getInstance("TLS");
- context.init(null, new TrustManager[] { new EasyX509TrustManager() }, null);
- return context;
- }
- catch (Exception e)
- {
- throw new IOException(e.getMessage());
- }
- }
-
- private SSLContext getSSLContext() throws IOException
- {
- if (this.sslcontext == null)
- {
- this.sslcontext = createEasySSLContext();
- }
- return this.sslcontext;
- }
-
- /**
- * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int,
- * java.net.InetAddress, int, org.apache.http.params.HttpParams)
- */
- public Socket connectSocket(Socket sock,
- String host,
- int port,
- InetAddress localAddress,
- int localPort,
- HttpParams params)
-
- throws IOException, UnknownHostException, ConnectTimeoutException
- {
- int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
- int soTimeout = HttpConnectionParams.getSoTimeout(params);
- InetSocketAddress remoteAddress = new InetSocketAddress(host, port);
- SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());
-
- if ((localAddress != null) || (localPort > 0))
- {
- // we need to bind explicitly
- if (localPort < 0)
- {
- localPort = 0; // indicates "any"
- }
- InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);
- sslsock.bind(isa);
- }
-
- sslsock.connect(remoteAddress, connTimeout);
- sslsock.setSoTimeout(soTimeout);
- return sslsock;
- }
-
- /**
- * @see org.apache.http.conn.scheme.SocketFactory#createSocket()
- */
- public Socket createSocket() throws IOException {
- return getSSLContext().getSocketFactory().createSocket();
- }
-
- /**
- * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket)
- */
- public boolean isSecure(Socket socket) throws IllegalArgumentException {
- return true;
- }
-
- /**
- * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int,
- * boolean)
- */
- public Socket createSocket(Socket socket,
- String host,
- int port,
- boolean autoClose) throws IOException,
- UnknownHostException
- {
- return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
- }
-
- // -------------------------------------------------------------------
- // javadoc in org.apache.http.conn.scheme.SocketFactory says :
- // Both Object.equals() and Object.hashCode() must be overridden
- // for the correct operation of some connection managers
- // -------------------------------------------------------------------
-
- public boolean equals(Object obj) {
- return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class));
- }
-
- public int hashCode() {
- return EasySSLSocketFactory.class.hashCode();
- }
-} \ No newline at end of file
diff --git a/src/org/fox/ttrss/util/EasyX509TrustManager.java b/src/org/fox/ttrss/util/EasyX509TrustManager.java
deleted file mode 100644
index 5ffc19bb..00000000
--- a/src/org/fox/ttrss/util/EasyX509TrustManager.java
+++ /dev/null
@@ -1,26 +0,0 @@
-
-package org.fox.ttrss.util;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.X509TrustManager;
-
-// http://stackoverflow.com/questions/6989116/httpget-not-working-due-to-not-trusted-server-certificate-but-it-works-with-ht
-
-public class EasyX509TrustManager implements X509TrustManager {
-
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType)
- throws CertificateException { }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType)
- throws CertificateException { }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
-
-}
diff --git a/src/org/fox/ttrss/util/HeadlinesRequest.java b/src/org/fox/ttrss/util/HeadlinesRequest.java
new file mode 100644
index 00000000..768abc7c
--- /dev/null
+++ b/src/org/fox/ttrss/util/HeadlinesRequest.java
@@ -0,0 +1,82 @@
+package org.fox.ttrss.util;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+import org.fox.ttrss.ApiRequest;
+import org.fox.ttrss.GlobalState;
+import org.fox.ttrss.OnlineActivity;
+import org.fox.ttrss.R;
+import org.fox.ttrss.types.Article;
+import org.fox.ttrss.types.ArticleList;
+
+import android.content.Context;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+public class HeadlinesRequest extends ApiRequest {
+ public static final int HEADLINES_REQUEST_SIZE = 30;
+ public static final int HEADLINES_BUFFER_MAX = 500;
+
+ private int m_offset = 0;
+ private OnlineActivity m_activity;
+ private ArticleList m_articles = GlobalState.getInstance().m_loadedArticles;
+
+ public HeadlinesRequest(Context context, OnlineActivity activity) {
+ super(context);
+
+ m_activity = activity;
+ }
+
+ protected void onPostExecute(JsonElement result) {
+ if (result != null) {
+ try {
+ JsonArray content = result.getAsJsonArray();
+ if (content != null) {
+ Type listType = new TypeToken<List<Article>>() {}.getType();
+ final List<Article> articles = new Gson().fromJson(content, listType);
+
+ while (m_articles.size() > HEADLINES_BUFFER_MAX)
+ m_articles.remove(0);
+
+ if (m_offset == 0)
+ m_articles.clear();
+ else
+ if (m_articles.get(m_articles.size()-1).id == -1)
+ m_articles.remove(m_articles.size()-1); // remove previous placeholder
+
+ for (Article f : articles)
+ m_articles.add(f);
+
+ if (articles.size() == HEADLINES_REQUEST_SIZE) {
+ Article placeholder = new Article(-1);
+ m_articles.add(placeholder);
+ }
+
+ if (m_articles.size() == 0)
+ m_activity.setLoadingStatus(R.string.no_headlines_to_display, false);
+ else
+ m_activity.setLoadingStatus(R.string.blank, false);
+
+ return;
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (m_lastError == ApiError.LOGIN_FAILED) {
+ m_activity.login();
+ } else {
+ m_activity.setLoadingStatus(getErrorMessage(), false);
+ }
+ }
+
+ public void setOffset(int skip) {
+ m_offset = skip;
+ }
+}
diff --git a/src/org/fox/ttrss/util/ImageCacheService.java b/src/org/fox/ttrss/util/ImageCacheService.java
index 25c4aff0..b699c569 100644
--- a/src/org/fox/ttrss/util/ImageCacheService.java
+++ b/src/org/fox/ttrss/util/ImageCacheService.java
@@ -10,7 +10,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
-import org.fox.ttrss.MainActivity;
+import org.fox.ttrss.OnlineActivity;
import org.fox.ttrss.R;
import org.fox.ttrss.offline.OfflineDownloadService;
@@ -25,6 +25,7 @@ import android.os.Environment;
public class ImageCacheService extends IntentService {
+ @SuppressWarnings("unused")
private final String TAG = this.getClass().getSimpleName();
public static final int NOTIFY_DOWNLOADING = 1;
@@ -123,12 +124,13 @@ public class ImageCacheService extends IntentService {
}
}
+ @SuppressWarnings("deprecation")
private void updateNotification(String msg) {
Notification notification = new Notification(R.drawable.icon,
getString(R.string.notify_downloading_title), System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
- new Intent(this, MainActivity.class), 0);
+ new Intent(this, OnlineActivity.class), 0);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;