diff --git a/.classpath b/.classpath
index ff3e5f766521..3a70dea9728d 100644
--- a/.classpath
+++ b/.classpath
@@ -1,15 +1,14 @@
-
-
-
+
-
+
+
diff --git a/.gitignore b/.gitignore
index a22392ade60e..f8f42ca8f94a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,6 @@
/bin
/gen
+local.properties
+res/raw/config.properties
+.jdbrc
+src/org/wordpress/android/Config.java
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4fbd5a95f32d..b67a29479506 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2,8 +2,8 @@
+ android:versionCode="71"
+ android:versionName="2.4" >
+
+
+
+
+
+
+
+
-
-
+
+
@@ -65,11 +75,13 @@
+ android:windowSoftInputMode="stateHidden"
+ android:theme="@style/Theme.Sherlock.Light" />
+ android:windowSoftInputMode="stateHidden"
+ android:theme="@style/Theme.Sherlock.Light" />
-
-
+
+
+
+
-
+
+
+
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/assets/licenses.html b/assets/licenses.html
index 5f784e218097..814f6cc01777 100644
--- a/assets/licenses.html
+++ b/assets/licenses.html
@@ -332,26 +332,6 @@
Notices for files:
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.
-
- Notices for files:
-
- CWAC-AdapterWrapper.jar
- CWAC-Bus.jar
- CWAC-Task.jar
-
- Copyright 2008-2009 CommonsWare, LLC
-
-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.
diff --git a/assets/licenses_bb.html b/assets/licenses_bb.html
index ad2a2be39efd..568921d36762 100644
--- a/assets/licenses_bb.html
+++ b/assets/licenses_bb.html
@@ -332,26 +332,6 @@ Notices for files:
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.
-
- Notices for files:
-
- CWAC-AdapterWrapper.jar
- CWAC-Bus.jar
- CWAC-Task.jar
-
- Copyright 2008-2009 CommonsWare, LLC
-
-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.
diff --git a/custom_rules.xml b/custom_rules.xml
new file mode 100644
index 000000000000..3ac1262fa133
--- /dev/null
+++ b/custom_rules.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/CWAC-AdapterWrapper.jar b/libs/CWAC-AdapterWrapper.jar
deleted file mode 100755
index ebbe0d9450f3..000000000000
Binary files a/libs/CWAC-AdapterWrapper.jar and /dev/null differ
diff --git a/libs/CWAC-Bus.jar b/libs/CWAC-Bus.jar
deleted file mode 100755
index c44b1f2f4cf2..000000000000
Binary files a/libs/CWAC-Bus.jar and /dev/null differ
diff --git a/libs/CWAC-Task.jar b/libs/CWAC-Task.jar
deleted file mode 100755
index 64479c9ab646..000000000000
Binary files a/libs/CWAC-Task.jar and /dev/null differ
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
new file mode 100644
index 000000000000..65ebaf8dc345
Binary files /dev/null and b/libs/android-support-v4.jar differ
diff --git a/libs/gcm.jar b/libs/gcm.jar
new file mode 100644
index 000000000000..ac109a830ebe
Binary files /dev/null and b/libs/gcm.jar differ
diff --git a/libs/volley.jar b/libs/volley.jar
new file mode 100644
index 000000000000..bffe8ca07e1c
Binary files /dev/null and b/libs/volley.jar differ
diff --git a/libs/wordpresscom-android-rest-v0.0.3.jar b/libs/wordpresscom-android-rest-v0.0.3.jar
new file mode 100644
index 000000000000..5299c104b106
Binary files /dev/null and b/libs/wordpresscom-android-rest-v0.0.3.jar differ
diff --git a/project.properties b/project.properties
index fef3618e9755..aeee57f8807f 100644
--- a/project.properties
+++ b/project.properties
@@ -18,3 +18,4 @@ apk-configurations=
android.library=false
android.library.reference.1=../ActionBarSherlock/library
android.library.reference.2=../../../Downloads/android-menudrawer-master/library
+
diff --git a/res/anim/blink.xml b/res/anim/blink.xml
new file mode 100644
index 000000000000..2d1035dfea37
--- /dev/null
+++ b/res/anim/blink.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/disappear.xml b/res/anim/disappear.xml
deleted file mode 100644
index 332fdd28f763..000000000000
--- a/res/anim/disappear.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/res/anim/do_nothing.xml b/res/anim/do_nothing.xml
new file mode 100644
index 000000000000..4c8f1d99264e
--- /dev/null
+++ b/res/anim/do_nothing.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/res/anim/fade_out.xml b/res/anim/fade_out.xml
new file mode 100644
index 000000000000..18050e8eb09a
--- /dev/null
+++ b/res/anim/fade_out.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/pop.xml b/res/anim/pop.xml
new file mode 100644
index 000000000000..9eb8a332b574
--- /dev/null
+++ b/res/anim/pop.xml
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/res/anim/rail.xml b/res/anim/rail.xml
deleted file mode 100644
index 2fdbe38302c0..000000000000
--- a/res/anim/rail.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/res/anim/rotate_zoom_in.xml b/res/anim/rotate_zoom_in.xml
new file mode 100644
index 000000000000..a3d1acb79d4a
--- /dev/null
+++ b/res/anim/rotate_zoom_in.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/rotate_zoom_out.xml b/res/anim/rotate_zoom_out.xml
new file mode 100644
index 000000000000..dc6b713e3d3b
--- /dev/null
+++ b/res/anim/rotate_zoom_out.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/show.xml b/res/anim/show.xml
deleted file mode 100644
index a99d4990708d..000000000000
--- a/res/anim/show.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/res/anim/slide_down.xml b/res/anim/slide_down.xml
new file mode 100644
index 000000000000..f94edfe0537b
--- /dev/null
+++ b/res/anim/slide_down.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/res/anim/slide_up.xml b/res/anim/slide_up.xml
new file mode 100644
index 000000000000..7c6821e29c91
--- /dev/null
+++ b/res/anim/slide_up.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/res/anim/zoom.xml b/res/anim/zoom.xml
new file mode 100644
index 000000000000..4f02af677a2f
--- /dev/null
+++ b/res/anim/zoom.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/res/drawable-hdpi/ab_icon_reply.png b/res/drawable-hdpi/ab_icon_reply.png
new file mode 100644
index 000000000000..835d96fc6c33
Binary files /dev/null and b/res/drawable-hdpi/ab_icon_reply.png differ
diff --git a/res/drawable-hdpi/ab_icon_send.png b/res/drawable-hdpi/ab_icon_send.png
new file mode 100644
index 000000000000..6bdd9585fcfe
Binary files /dev/null and b/res/drawable-hdpi/ab_icon_send.png differ
diff --git a/res/drawable-hdpi/bottom_shadow.9.png b/res/drawable-hdpi/bottom_shadow.9.png
new file mode 100644
index 000000000000..687ed33b7d62
Binary files /dev/null and b/res/drawable-hdpi/bottom_shadow.9.png differ
diff --git a/res/drawable-hdpi/comment_badge.png b/res/drawable-hdpi/comment_badge.png
deleted file mode 100644
index 30b384babadd..000000000000
Binary files a/res/drawable-hdpi/comment_badge.png and /dev/null differ
diff --git a/res/drawable-hdpi/dashboard_icon_notifications.png b/res/drawable-hdpi/dashboard_icon_notifications.png
index 1a485bdc4f27..0d103621ac7d 100644
Binary files a/res/drawable-hdpi/dashboard_icon_notifications.png and b/res/drawable-hdpi/dashboard_icon_notifications.png differ
diff --git a/res/drawable-hdpi/follow_minus.png b/res/drawable-hdpi/follow_minus.png
new file mode 100644
index 000000000000..276c6371b510
Binary files /dev/null and b/res/drawable-hdpi/follow_minus.png differ
diff --git a/res/drawable-hdpi/follow_plus.png b/res/drawable-hdpi/follow_plus.png
new file mode 100644
index 000000000000..f7cae6e5d7b1
Binary files /dev/null and b/res/drawable-hdpi/follow_plus.png differ
diff --git a/res/drawable-hdpi/ic_drawer.png b/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 000000000000..96cc65fafe80
Binary files /dev/null and b/res/drawable-hdpi/ic_drawer.png differ
diff --git a/res/drawable-hdpi/moderate_approve.png b/res/drawable-hdpi/moderate_approve.png
new file mode 100644
index 000000000000..6c6df33ecc49
Binary files /dev/null and b/res/drawable-hdpi/moderate_approve.png differ
diff --git a/res/drawable-hdpi/moderate_spam.png b/res/drawable-hdpi/moderate_spam.png
new file mode 100644
index 000000000000..866112a5d057
Binary files /dev/null and b/res/drawable-hdpi/moderate_spam.png differ
diff --git a/res/drawable-hdpi/moderate_trash.png b/res/drawable-hdpi/moderate_trash.png
new file mode 100644
index 000000000000..5fd916002e35
Binary files /dev/null and b/res/drawable-hdpi/moderate_trash.png differ
diff --git a/res/drawable-hdpi/moderate_unapprove.png b/res/drawable-hdpi/moderate_unapprove.png
new file mode 100644
index 000000000000..4172117d40ef
Binary files /dev/null and b/res/drawable-hdpi/moderate_unapprove.png differ
diff --git a/res/drawable-hdpi/note_icon_achievement.png b/res/drawable-hdpi/note_icon_achievement.png
new file mode 100644
index 000000000000..b51994925591
Binary files /dev/null and b/res/drawable-hdpi/note_icon_achievement.png differ
diff --git a/res/drawable-hdpi/note_icon_comment.png b/res/drawable-hdpi/note_icon_comment.png
new file mode 100644
index 000000000000..e91e6023426f
Binary files /dev/null and b/res/drawable-hdpi/note_icon_comment.png differ
diff --git a/res/drawable-hdpi/note_icon_follow.png b/res/drawable-hdpi/note_icon_follow.png
new file mode 100644
index 000000000000..a7689b0f4581
Binary files /dev/null and b/res/drawable-hdpi/note_icon_follow.png differ
diff --git a/res/drawable-hdpi/note_icon_like.png b/res/drawable-hdpi/note_icon_like.png
new file mode 100644
index 000000000000..f6c569b98f48
Binary files /dev/null and b/res/drawable-hdpi/note_icon_like.png differ
diff --git a/res/drawable-hdpi/note_icon_reblog.png b/res/drawable-hdpi/note_icon_reblog.png
new file mode 100644
index 000000000000..44938f8f2022
Binary files /dev/null and b/res/drawable-hdpi/note_icon_reblog.png differ
diff --git a/res/drawable-hdpi/note_icon_stats.png b/res/drawable-hdpi/note_icon_stats.png
new file mode 100644
index 000000000000..f02d8457b1c3
Binary files /dev/null and b/res/drawable-hdpi/note_icon_stats.png differ
diff --git a/res/drawable-hdpi/notification_multi.png b/res/drawable-hdpi/notification_multi.png
new file mode 100644
index 000000000000..ed981474562c
Binary files /dev/null and b/res/drawable-hdpi/notification_multi.png differ
diff --git a/res/drawable-hdpi/remote_failed.png b/res/drawable-hdpi/remote_failed.png
new file mode 100644
index 000000000000..072850a4338c
Binary files /dev/null and b/res/drawable-hdpi/remote_failed.png differ
diff --git a/res/drawable-hdpi/remote_image.png b/res/drawable-hdpi/remote_image.png
new file mode 100644
index 000000000000..a2cf1d68a92d
Binary files /dev/null and b/res/drawable-hdpi/remote_image.png differ
diff --git a/res/drawable-mdpi/ab_icon_reply.png b/res/drawable-mdpi/ab_icon_reply.png
new file mode 100644
index 000000000000..9e344659eedc
Binary files /dev/null and b/res/drawable-mdpi/ab_icon_reply.png differ
diff --git a/res/drawable-mdpi/ab_icon_send.png b/res/drawable-mdpi/ab_icon_send.png
new file mode 100644
index 000000000000..515668a6b6c3
Binary files /dev/null and b/res/drawable-mdpi/ab_icon_send.png differ
diff --git a/res/drawable/dashboard_icon_comments.png b/res/drawable-mdpi/dashboard_icon_comments.png
similarity index 100%
rename from res/drawable/dashboard_icon_comments.png
rename to res/drawable-mdpi/dashboard_icon_comments.png
diff --git a/res/drawable/dashboard_icon_new_page.png b/res/drawable-mdpi/dashboard_icon_new_page.png
similarity index 100%
rename from res/drawable/dashboard_icon_new_page.png
rename to res/drawable-mdpi/dashboard_icon_new_page.png
diff --git a/res/drawable/dashboard_icon_new_post.png b/res/drawable-mdpi/dashboard_icon_new_post.png
similarity index 100%
rename from res/drawable/dashboard_icon_new_post.png
rename to res/drawable-mdpi/dashboard_icon_new_post.png
diff --git a/res/drawable/dashboard_icon_notifications.png b/res/drawable-mdpi/dashboard_icon_notifications.png
similarity index 57%
rename from res/drawable/dashboard_icon_notifications.png
rename to res/drawable-mdpi/dashboard_icon_notifications.png
index 2c6a81747905..a4361802cd29 100644
Binary files a/res/drawable/dashboard_icon_notifications.png and b/res/drawable-mdpi/dashboard_icon_notifications.png differ
diff --git a/res/drawable/dashboard_icon_pages.png b/res/drawable-mdpi/dashboard_icon_pages.png
similarity index 100%
rename from res/drawable/dashboard_icon_pages.png
rename to res/drawable-mdpi/dashboard_icon_pages.png
diff --git a/res/drawable/dashboard_icon_photo.png b/res/drawable-mdpi/dashboard_icon_photo.png
similarity index 100%
rename from res/drawable/dashboard_icon_photo.png
rename to res/drawable-mdpi/dashboard_icon_photo.png
diff --git a/res/drawable/dashboard_icon_posts.png b/res/drawable-mdpi/dashboard_icon_posts.png
similarity index 100%
rename from res/drawable/dashboard_icon_posts.png
rename to res/drawable-mdpi/dashboard_icon_posts.png
diff --git a/res/drawable/dashboard_icon_settings.png b/res/drawable-mdpi/dashboard_icon_settings.png
similarity index 100%
rename from res/drawable/dashboard_icon_settings.png
rename to res/drawable-mdpi/dashboard_icon_settings.png
diff --git a/res/drawable/dashboard_icon_stats.png b/res/drawable-mdpi/dashboard_icon_stats.png
similarity index 100%
rename from res/drawable/dashboard_icon_stats.png
rename to res/drawable-mdpi/dashboard_icon_stats.png
diff --git a/res/drawable/dashboard_icon_subs.png b/res/drawable-mdpi/dashboard_icon_subs.png
similarity index 100%
rename from res/drawable/dashboard_icon_subs.png
rename to res/drawable-mdpi/dashboard_icon_subs.png
diff --git a/res/drawable/dashboard_icon_video.png b/res/drawable-mdpi/dashboard_icon_video.png
similarity index 100%
rename from res/drawable/dashboard_icon_video.png
rename to res/drawable-mdpi/dashboard_icon_video.png
diff --git a/res/drawable/dashboard_icon_view.png b/res/drawable-mdpi/dashboard_icon_view.png
similarity index 100%
rename from res/drawable/dashboard_icon_view.png
rename to res/drawable-mdpi/dashboard_icon_view.png
diff --git a/res/drawable/dashboard_icon_wp.png b/res/drawable-mdpi/dashboard_icon_wp.png
similarity index 100%
rename from res/drawable/dashboard_icon_wp.png
rename to res/drawable-mdpi/dashboard_icon_wp.png
diff --git a/res/drawable-mdpi/follow_minus.png b/res/drawable-mdpi/follow_minus.png
new file mode 100644
index 000000000000..deab5e2446cb
Binary files /dev/null and b/res/drawable-mdpi/follow_minus.png differ
diff --git a/res/drawable-mdpi/follow_plus.png b/res/drawable-mdpi/follow_plus.png
new file mode 100644
index 000000000000..552617968b6d
Binary files /dev/null and b/res/drawable-mdpi/follow_plus.png differ
diff --git a/res/drawable-mdpi/ic_drawer.png b/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 000000000000..58fe873bae1f
Binary files /dev/null and b/res/drawable-mdpi/ic_drawer.png differ
diff --git a/res/drawable-mdpi/moderate_approve.png b/res/drawable-mdpi/moderate_approve.png
new file mode 100644
index 000000000000..9785088db56d
Binary files /dev/null and b/res/drawable-mdpi/moderate_approve.png differ
diff --git a/res/drawable-mdpi/moderate_spam.png b/res/drawable-mdpi/moderate_spam.png
new file mode 100644
index 000000000000..dbb56dbceb95
Binary files /dev/null and b/res/drawable-mdpi/moderate_spam.png differ
diff --git a/res/drawable-mdpi/moderate_trash.png b/res/drawable-mdpi/moderate_trash.png
new file mode 100644
index 000000000000..27666c24091d
Binary files /dev/null and b/res/drawable-mdpi/moderate_trash.png differ
diff --git a/res/drawable-mdpi/moderate_unapprove.png b/res/drawable-mdpi/moderate_unapprove.png
new file mode 100644
index 000000000000..509a40dfd4d7
Binary files /dev/null and b/res/drawable-mdpi/moderate_unapprove.png differ
diff --git a/res/drawable-mdpi/note_icon_achievement.png b/res/drawable-mdpi/note_icon_achievement.png
new file mode 100644
index 000000000000..d85afbaa8c53
Binary files /dev/null and b/res/drawable-mdpi/note_icon_achievement.png differ
diff --git a/res/drawable-mdpi/note_icon_comment.png b/res/drawable-mdpi/note_icon_comment.png
new file mode 100644
index 000000000000..2a3ebddaed80
Binary files /dev/null and b/res/drawable-mdpi/note_icon_comment.png differ
diff --git a/res/drawable-mdpi/note_icon_follow.png b/res/drawable-mdpi/note_icon_follow.png
new file mode 100644
index 000000000000..6e751aed8fa6
Binary files /dev/null and b/res/drawable-mdpi/note_icon_follow.png differ
diff --git a/res/drawable-mdpi/note_icon_like.png b/res/drawable-mdpi/note_icon_like.png
new file mode 100644
index 000000000000..5321ca2653eb
Binary files /dev/null and b/res/drawable-mdpi/note_icon_like.png differ
diff --git a/res/drawable-mdpi/note_icon_reblog.png b/res/drawable-mdpi/note_icon_reblog.png
new file mode 100644
index 000000000000..c85c28447245
Binary files /dev/null and b/res/drawable-mdpi/note_icon_reblog.png differ
diff --git a/res/drawable-mdpi/note_icon_stats.png b/res/drawable-mdpi/note_icon_stats.png
new file mode 100644
index 000000000000..8b5a58aa3aa8
Binary files /dev/null and b/res/drawable-mdpi/note_icon_stats.png differ
diff --git a/res/drawable-mdpi/notification_multi.png b/res/drawable-mdpi/notification_multi.png
new file mode 100644
index 000000000000..ca85a3230e14
Binary files /dev/null and b/res/drawable-mdpi/notification_multi.png differ
diff --git a/res/drawable-mdpi/remote_failed.png b/res/drawable-mdpi/remote_failed.png
new file mode 100644
index 000000000000..a370d8bd06e1
Binary files /dev/null and b/res/drawable-mdpi/remote_failed.png differ
diff --git a/res/drawable-mdpi/remote_image.png b/res/drawable-mdpi/remote_image.png
new file mode 100644
index 000000000000..827fbdbc9ab5
Binary files /dev/null and b/res/drawable-mdpi/remote_image.png differ
diff --git a/res/drawable-xhdpi/ab_icon_reply.png b/res/drawable-xhdpi/ab_icon_reply.png
new file mode 100644
index 000000000000..4cc854ab8001
Binary files /dev/null and b/res/drawable-xhdpi/ab_icon_reply.png differ
diff --git a/res/drawable-xhdpi/ab_icon_send.png b/res/drawable-xhdpi/ab_icon_send.png
new file mode 100644
index 000000000000..0c870d2ce19d
Binary files /dev/null and b/res/drawable-xhdpi/ab_icon_send.png differ
diff --git a/res/drawable-xhdpi/dashboard_icon_notifications.png b/res/drawable-xhdpi/dashboard_icon_notifications.png
index 45997f4abe85..a494ea2ef344 100644
Binary files a/res/drawable-xhdpi/dashboard_icon_notifications.png and b/res/drawable-xhdpi/dashboard_icon_notifications.png differ
diff --git a/res/drawable-xhdpi/follow_minus.png b/res/drawable-xhdpi/follow_minus.png
new file mode 100644
index 000000000000..7e29a9018f37
Binary files /dev/null and b/res/drawable-xhdpi/follow_minus.png differ
diff --git a/res/drawable-xhdpi/follow_plus.png b/res/drawable-xhdpi/follow_plus.png
new file mode 100644
index 000000000000..6d24ee1da002
Binary files /dev/null and b/res/drawable-xhdpi/follow_plus.png differ
diff --git a/res/drawable-xhdpi/ic_drawer.png b/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 000000000000..17a06149490f
Binary files /dev/null and b/res/drawable-xhdpi/ic_drawer.png differ
diff --git a/res/drawable-xhdpi/moderate_approve.png b/res/drawable-xhdpi/moderate_approve.png
new file mode 100644
index 000000000000..875b6ab11e45
Binary files /dev/null and b/res/drawable-xhdpi/moderate_approve.png differ
diff --git a/res/drawable-xhdpi/moderate_spam.png b/res/drawable-xhdpi/moderate_spam.png
new file mode 100644
index 000000000000..8e17bbba7521
Binary files /dev/null and b/res/drawable-xhdpi/moderate_spam.png differ
diff --git a/res/drawable-xhdpi/moderate_trash.png b/res/drawable-xhdpi/moderate_trash.png
new file mode 100644
index 000000000000..b0dba043c859
Binary files /dev/null and b/res/drawable-xhdpi/moderate_trash.png differ
diff --git a/res/drawable-xhdpi/moderate_unapprove.png b/res/drawable-xhdpi/moderate_unapprove.png
new file mode 100644
index 000000000000..dc302526d565
Binary files /dev/null and b/res/drawable-xhdpi/moderate_unapprove.png differ
diff --git a/res/drawable-xhdpi/note_icon_achievement.png b/res/drawable-xhdpi/note_icon_achievement.png
new file mode 100644
index 000000000000..b288029734de
Binary files /dev/null and b/res/drawable-xhdpi/note_icon_achievement.png differ
diff --git a/res/drawable-xhdpi/note_icon_comment.png b/res/drawable-xhdpi/note_icon_comment.png
new file mode 100644
index 000000000000..bdd3581231cd
Binary files /dev/null and b/res/drawable-xhdpi/note_icon_comment.png differ
diff --git a/res/drawable-xhdpi/note_icon_follow.png b/res/drawable-xhdpi/note_icon_follow.png
new file mode 100644
index 000000000000..76bdcd0d6d0d
Binary files /dev/null and b/res/drawable-xhdpi/note_icon_follow.png differ
diff --git a/res/drawable-xhdpi/note_icon_like.png b/res/drawable-xhdpi/note_icon_like.png
new file mode 100644
index 000000000000..314448909d0b
Binary files /dev/null and b/res/drawable-xhdpi/note_icon_like.png differ
diff --git a/res/drawable-xhdpi/note_icon_reblog.png b/res/drawable-xhdpi/note_icon_reblog.png
new file mode 100644
index 000000000000..4fdf3d313973
Binary files /dev/null and b/res/drawable-xhdpi/note_icon_reblog.png differ
diff --git a/res/drawable-xhdpi/note_icon_stats.png b/res/drawable-xhdpi/note_icon_stats.png
new file mode 100644
index 000000000000..1fd9b859fb01
Binary files /dev/null and b/res/drawable-xhdpi/note_icon_stats.png differ
diff --git a/res/drawable-xhdpi/notification_multi.png b/res/drawable-xhdpi/notification_multi.png
new file mode 100644
index 000000000000..8357482d2121
Binary files /dev/null and b/res/drawable-xhdpi/notification_multi.png differ
diff --git a/res/drawable/follow_indicator.xml b/res/drawable/follow_indicator.xml
new file mode 100644
index 000000000000..0aa877003b48
--- /dev/null
+++ b/res/drawable/follow_indicator.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/res/drawable/follow_minus.png b/res/drawable/follow_minus.png
new file mode 100644
index 000000000000..d251f5b2e698
Binary files /dev/null and b/res/drawable/follow_minus.png differ
diff --git a/res/drawable/follow_plus.png b/res/drawable/follow_plus.png
new file mode 100644
index 000000000000..30f09a0a52dd
Binary files /dev/null and b/res/drawable/follow_plus.png differ
diff --git a/res/drawable/list_divider.xml b/res/drawable/list_divider.xml
index 649275d8fcd5..97797a5e2fd8 100644
--- a/res/drawable/list_divider.xml
+++ b/res/drawable/list_divider.xml
@@ -1,14 +1,7 @@
-
-
- -
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/remote_failed.png b/res/drawable/remote_failed.png
new file mode 100644
index 000000000000..c7fb3e50b5e5
Binary files /dev/null and b/res/drawable/remote_failed.png differ
diff --git a/res/drawable/remote_image.png b/res/drawable/remote_image.png
new file mode 100644
index 000000000000..55f18a95089e
Binary files /dev/null and b/res/drawable/remote_image.png differ
diff --git a/res/drawable/reply_row_background.xml b/res/drawable/reply_row_background.xml
new file mode 100644
index 000000000000..07b57c25abe6
--- /dev/null
+++ b/res/drawable/reply_row_background.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/res/drawable/reply_row_textcolor.xml b/res/drawable/reply_row_textcolor.xml
new file mode 100644
index 000000000000..473482d457d9
--- /dev/null
+++ b/res/drawable/reply_row_textcolor.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/res/drawable/throbber.xml b/res/drawable/throbber.xml
new file mode 100644
index 000000000000..6753f3a6204f
--- /dev/null
+++ b/res/drawable/throbber.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout-large-mdpi/notifications.xml b/res/layout-large-mdpi/notifications.xml
new file mode 100644
index 000000000000..2aa0596880f8
--- /dev/null
+++ b/res/layout-large-mdpi/notifications.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/res/layout-sw720dp/notifications.xml b/res/layout-sw720dp/notifications.xml
new file mode 100644
index 000000000000..2aa0596880f8
--- /dev/null
+++ b/res/layout-sw720dp/notifications.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/add_account.xml b/res/layout/add_account.xml
index aef17ed0b2f8..79fee1a4c38a 100644
--- a/res/layout/add_account.xml
+++ b/res/layout/add_account.xml
@@ -87,14 +87,6 @@
android:shadowDy="1"
android:shadowRadius="1"
android:text="@string/log_in" />
-
-
-
@@ -91,7 +91,8 @@
android:orientation="vertical"
android:paddingTop="4dp"
android:singleLine="false"
- android:textColor="#545454" />
+ android:textColor="#545454"
+ android:fontFamily="sans-serif-light" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notifications.xml b/res/layout/notifications.xml
new file mode 100644
index 000000000000..7a6ba6c4e5a3
--- /dev/null
+++ b/res/layout/notifications.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notifications_big_badge.xml b/res/layout/notifications_big_badge.xml
new file mode 100644
index 000000000000..810385cda49f
--- /dev/null
+++ b/res/layout/notifications_big_badge.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notifications_comment.xml b/res/layout/notifications_comment.xml
new file mode 100644
index 000000000000..86cc33569c00
--- /dev/null
+++ b/res/layout/notifications_comment.xml
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notifications_detail_header.xml b/res/layout/notifications_detail_header.xml
new file mode 100644
index 000000000000..87431695f4f5
--- /dev/null
+++ b/res/layout/notifications_detail_header.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notifications_enabled_dialog.xml b/res/layout/notifications_enabled_dialog.xml
new file mode 100644
index 000000000000..0da72af0cd85
--- /dev/null
+++ b/res/layout/notifications_enabled_dialog.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notifications_follow_row.xml b/res/layout/notifications_follow_row.xml
new file mode 100644
index 000000000000..cf548238ca1b
--- /dev/null
+++ b/res/layout/notifications_follow_row.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notifications_reply_row.xml b/res/layout/notifications_reply_row.xml
new file mode 100644
index 000000000000..5a60f5a567da
--- /dev/null
+++ b/res/layout/notifications_reply_row.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout/row_post_page.xml b/res/layout/row_post_page.xml
index 2edd2ed90996..ccc883a123ac 100644
--- a/res/layout/row_post_page.xml
+++ b/res/layout/row_post_page.xml
@@ -3,8 +3,8 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
- android:paddingLeft="18dp"
- android:paddingRight="18dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
style="@style/WordPressListRowBackground" >
diff --git a/res/layout/view_comment.xml b/res/layout/view_comment.xml
index 39a57178b4ee..b6097ff42e5d 100644
--- a/res/layout/view_comment.xml
+++ b/res/layout/view_comment.xml
@@ -13,11 +13,10 @@
android:paddingRight="10dp"
android:paddingTop="10dp" >
-
-
+ android:layout_height="86dp" />
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/notifications.xml b/res/menu/notifications.xml
new file mode 100644
index 000000000000..8b541c4502f9
--- /dev/null
+++ b/res/menu/notifications.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/posts.xml b/res/menu/posts.xml
index d758cd7aa3a9..609826d496f9 100644
--- a/res/menu/posts.xml
+++ b/res/menu/posts.xml
@@ -1,6 +1,6 @@
-
+
+
+
\ No newline at end of file
diff --git a/res/menu/reader.xml b/res/menu/reader.xml
new file mode 100644
index 000000000000..9de946136430
--- /dev/null
+++ b/res/menu/reader.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/refresh_only.xml b/res/menu/refresh_only.xml
deleted file mode 100644
index 9df8286ed195..000000000000
--- a/res/menu/refresh_only.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/res/menu/webview.xml b/res/menu/webview.xml
index e2deb064788c..1e13a5888815 100644
--- a/res/menu/webview.xml
+++ b/res/menu/webview.xml
@@ -16,4 +16,14 @@
android:icon="@drawable/ab_icon_refresh"
android:showAsAction="never"
android:title="@string/refresh"/>
+
+
\ No newline at end of file
diff --git a/res/raw/notification.mp3 b/res/raw/notification.mp3
new file mode 100644
index 000000000000..e7ca091fe10a
Binary files /dev/null and b/res/raw/notification.mp3 differ
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 7e4cc78b4fee..700c22619006 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -1,22 +1,63 @@
+ WordPress.com
+ Anmelden
+ Abmelden
+ Löschung bestätigen
+ Bist Du sicher, dass Du diesen Kommentar löschen möchtest?
+ Anmelden
+ Benachrichtigungen
+ Benachrichtigungen verwalten
+ Benachrichtigungen aktivieren
+ Einstellungen
+ Kommentar moderieren
+ Ausschalten
+ Anmelden ...
+ Bist Du sicher, dass Du dich abmelden möchtest?
+ Bist Du sicher, dass Du dich von WordPress.com abmelden möchtest?
+ Bitte erneut anmelden.
+ Ausschalten für 1 Stunde
+ Ausschalten für 8 Stunden
+ Antippen zum Antworten
+ Veröffentliche Antwort
+ Antwort veröffentlicht
+ %d neue Benachrichtigungen
+ und %d weitere.
+ Antippen zum Anschauen.
+ Folgen
+ Nicht mehr folgen
+ Diesem Blog folgen oder nicht mehr folgen
+ Tut mir Leid, ein Fehler ist aufgetreten, als versucht wurde dem Blog zu folgen. Bitte versuche es später noch einmal.
+ Typen
+ Kommentar hinzufügen
+ Kommentar eingeben:
+ Kommentar absenden
+ Erforderliches Feld
+ Kommentar erfolgreich hinzugefügt
+ Kommentar schreiben
+ Bitte gib etwas im Kommentarfeld ein.
+ Admin anzeigen
+ Versuche den Kommentar zu veröffentlichen
+ Kommentar bearbeiten
+ Seite teilen
+ Es tut mir wirklich Leid, aber es ist ein Fehler aufgetreten, als versucht wurde die App-Datenbank zu erstellen. Bitte installiere die App noch einmal neu.
+ Lädt...
Webseite anschauen
Open-Source-Lizenzen
- Beitragssignatur
- Eine Signatur zu neuen Beiträgen hinzufügen
HTTP-Benutzername
HTTP-Passwort
HTTP-Zugangsdaten (optional)
+ Beitragssignatur
+ Eine Signatur zu neuen Beiträgen hinzufügen
Blogs aktivieren
- Aktualisierungsintervall
Starte in Sekunden mit dem Bloggen.
Konnte keine Temp-Datei für den Medien-Upload erstellen. Bitte stelle sicher, dass genug freier Speicherplatz auf deinem Gerät verfügbar ist.
Beim Hochladen von Medien ist ein Fehler aufgetreten
Teilen-Link
- Inhalt (hier tippen um Text oder Medien hinzuzufügen)
Veröffentlichen
- Beitragsformat
Medien hinzufügen
+ Inhalt (hier tippen um Text oder Medien hinzuzufügen)
+ Beitragsformat
Falscher Benutzername oder falsches Passwort.
Leider ist beim Versuch %s zu Löschen ein Fehler aufgetreten.
Leider ist ein Fehler aufgetreten. Bitte versuche es später noch einmal.
@@ -25,20 +66,17 @@
Leider ist beim Hochladen von %s ein Fehler aufgetreten.
Du hast das Anmeldelimit überschritten. Bitte stelle sicher, dass deine Zugangsdaten richtig sind und versuche es in ein paar Minuten noch einmal.
©2013 Automattic, Inc
+ Kategoriename
+ Blog hinzufügen
Passwort
Benutzername
- Blog hinzufügen
- Kategoriename
- Kategorie-Titelform (optional)
- Kategorie-Beschreibung (optional)
- Anmelden
Blog-URL
+ Kategorie-Beschreibung (optional)
+ Kategorie-Titelform (optional)
Reader
- Nicht genügend Speicher verfügbar.
Die Mediendatei zum Hochladen konnte nicht gefunden werden. Wurde sie gelöscht oder verschoben?
+ Nicht genügend Speicher verfügbar.
Als Artikelbild nutzen
- Bild im Artikelinhalt verwenden
- Es konnten keine %s-Blogs für dieses Konto gefunden werden.
zusätzlich
Alle hinzufügen
Markierte hinzufügen
@@ -46,46 +84,48 @@
Kein Netzwerk verfügbar
Es ist kein Netzwerk verfügbar. Verbinde mit einem Netzwerk und probiere es noch einmal.
Tut mir Leid, %s konnte nicht aktualisiert werden. Bitte versuche es später noch einmal.
- Tut mir Leid. Es ist ein Fehler aufgetreten, beim dem Versuch einer Verbindung zu deinen Statistiken. Bitte versuche es später noch einmal.
Seiten
- Bildunterschrift (optional)
- Tut mir Leid, leider konnte keine Verbindung zur WordPress-Webseite hergestellt werden. Bitte versuche es später noch einmal.
Breite
Artikel
- Anonym
- Keine Blogs gefunden
Seite
- Falsche Anmeldedaten oder der Blog ist nicht mit diesem WordPress.com-Konto verbunden.
+ Keine Blogs gefunden
+ Anonym
+ Bildunterschrift (optional)
+ Bild im Artikelinhalt verwenden
Horizontale Ausrichtung
+ Es konnten keine %s-Blogs für dieses Konto gefunden werden.
+ Tut mir Leid. Es ist ein Fehler aufgetreten, beim dem Versuch einer Verbindung zu deinen Statistiken. Bitte versuche es später noch einmal.
+ Tut mir Leid, leider konnte keine Verbindung zur WordPress-Webseite hergestellt werden. Bitte versuche es später noch einmal.
+ Falsche Anmeldedaten oder der Blog ist nicht mit diesem WordPress.com-Konto verbunden.
Beitrag
- Veröffentlicht mit WordPress für BlackBerry
- blogusername
Spalte 1
Spalte 2
- WordPress-Blog
+ Veröffentlicht mit WordPress für BlackBerry
Bildeinstellungen
WordPress für BlackBerry
- Hier klicken um Text oder Medien hinzuzufügen
OK
+ WordPress-Blog
Bist Du sicher, dass Du die Bearbeitung des Kommentars abbrechen möchtest?
+ Hier klicken um Text oder Medien hinzuzufügen
+ blogusername
Verbindung konnte nicht hergestellt werden. Bitte gib den kompletten Pfad zur xmlrpc.php auf deiner Webseite an und probiere es noch einmal.
Du hast Änderungen an einem hochgeladenen Artikel vorgenommen, die noch nicht gespeichert wurden. Mit dem Aktualisieren weiter machen und die lokalen Änderungen überschreiben?
Lokale Änderungen
- Hochladen und zum skalierten Bild verlinken
- Bitte gib einen gültigen Wert für die skalierte Breite ein
Skalierte Bildbreite
- Kommentarinhalt
+ Bitte gib einen gültigen Wert für die skalierte Breite ein
+ Hochladen und zum skalierten Bild verlinken
Name
E-Mail
URL
Kommentar aktualisieren
Änderungen speichern
Der Kommentar-Inhalt ist erforderlich.
+ Kommentarinhalt
Themen
Aktualisieren
Entfernen
- Beim Versuch den Artikel zu laden trat leider ein Fehler auf. Bitte aktualisiere die Artikel und versuche es noch einmal.
Unbekannter Standort
+ Beim Versuch den Artikel zu laden trat leider ein Fehler auf. Bitte aktualisiere die Artikel und versuche es noch einmal.
Geplant
Beim Versuch diesen Blog zu erreichen trat leider ein Fehler auf.
Übersicht
@@ -96,7 +136,6 @@
Link-Text (optional)
URL
Veröffentlicht mit WordPress für Android
- Kommentar-Benachrichtigungen
Spam
Lädt hoch
Lade Medienobjekt # hoch
@@ -117,15 +156,14 @@
Quick Video
Tut mir Leid, aber das Medienobjekt konnte nicht aus der Galerie geladen werden.
Hochladen fehlgeschlagen
- Bitte überprüfe, ob die eingegebene Blog-URL gültig ist.
Im Browser anzeigen
- Bitte gib etwas im Antworten-Feld ein.
- Unbenannt
+ Bitte überprüfe, ob die eingegebene Blog-URL gültig ist.
Bitte füge etwas Inhalt ein oder hänge ein Medienobjekt an.
+ Unbenannt
Kein Inhalt gefunden
Bearbeiten
- Artikel
Seite
+ Artikel
Passwort (optional)
Sofort
Namen für Tastaturkürzel festlegen
@@ -137,10 +175,10 @@
Versuche URL aufzurufen
Blog für QuickPress Verknüpfung auswählen
Name des Tastaturkürzel darf nicht leer sein
- Veröffentlichen
Ausstehender Review
Entwurf
Privater Eintrag
+ Veröffentlichen
Hochladen und zu Vollbild verlinken
Falsche Blog URL
Bei WordPress.com gespeicherten Blog hinzufügen
@@ -152,49 +190,49 @@
Bitte fülle alle benötigten Felder aus, bevor du deine Daten übermittelst!
Neuen Blog bei WordPress.com beginnen
Kommentar von
- Passwort
- Nutzername
+ Tags (durch Kommas getrennt)
Benutzername und Password werden benötigt.
+ Nutzername
+ Passwort
Titel
- Tags (durch Kommas getrennt)
- Kommentare moderieren
Mehr laden
- Statistiken
Lösche Kommentare
+ Statistiken
+ Kommentare moderieren
Kommentare erfolgreich moderiert
Einstellungen
+ Bist Du sicher, dass diese Seite gelöscht werden soll
Eintrag erfolgreich gelöscht
Versuche Eintrag zu löschen
Bist du dir sicher, dass du diesen Eintrag löschen möchtest?
Eintrag löschen
Seite erfolgreich gelöscht
- Bist Du sicher, dass diese Seite gelöscht werden soll
Versuche Seite zu löschen
Seite löschen
Es ist ein Fehler beim Vorbereiten der aufgenommenen Dateien aufgetreten.
- Benachrichtigungslicht blinken
Benachrichtigunston abspielen
Vibration
+ Benachrichtigungslicht blinken
Ortungsdaten noch nicht verfügbar, bitte warten.
Status
- Eine eingebundene SD-Karte wird benötigt, um Medien hochzuladen.
Ortung
Ein neues Video drehen
SD-Karte benötigt
Wähle ein Video von der Galerie
+ Eine eingebundene SD-Karte wird benötigt, um Medien hochzuladen.
Seite ansehen
Artikel ansehen
Konto-Details
Media
Kein WordPress-Konto gefunden, bitte füge ein Konto hinzu und versuche es nochmal.
Wähle ein Blog
- Bitte überprüfe deine Einstellungen und versuche es nochmal.
Kategorie erfolgreich hinzugefügt.
Das Feld für den Kategorie-Namen ist benötigt.
- Kommentar wird gelöscht
+ Bitte überprüfe deine Einstellungen und versuche es nochmal.
Löschen
Genehmigen
Sperren
+ Kommentar wird gelöscht
Benötigtes Feld
Neue Kategorie hinzufügen
Versuche, die Kategorie hinzuzufügen
@@ -211,59 +249,57 @@
Konnte Blog nicht löschen.
Wähle ein Foto von der Galerie
Schieß ein Foto
- Fehler
- Ausgewählte Kategorien:
- Du hast noch kein Blog?
- Hol\' dir ein kostenloses Blog bei WordPress.com
- Abbrechen
- Speichern
- Hinzufügen
- Seite bearbeiten
- Artikel bearbeiten
- Aktualisiere Kategorien
- Versuche, die Kategorien zu aktualisieren
- Verbindungsfehler
Ein Verbindungsfehler ist aufgetreten. Bitte versuche es später erneut.
- Kategorien aktualsiert
- Fehler beim Aktualisieren der Kategorien
- Bearbeitung abbrechen
- Möchtest Du die Bearbeitung dieses Artikels wirklich abbrechen?
- Neuer Account
+ Entwurf abbrechen
Kommentare
- Artikel
- Seiten
- Möchtest Du die Bearbeitung dieser Seite wirklich abbrechen?
- Neue Seite
- Lokale Entwürfe
- Neuer Artikel
- Gib eine Antwort ein:
Antwort senden
Auf Kommentar antworten
- Benötigtes Feld
+ Bitte gib eine Antwort ein.
+ Gib eine Antwort ein:
+ Kommentar-Aktionen
+ Freigeben
+ Ablehnen
+ Antworten
Vorschau
- Lade...
zu
Freigegeben
Abgelehnt
Spam
- Moderiere Kommentar
- Antworte auf Kommentar
- Kommentar erfolgreich moderiert.
- Antwort erfolgreich hinzugefügt
+ Account einrichten
+ Möchtest Du die Bearbeitung dieser Seite wirklich abbrechen?
+ Neue Seite
+ Lokale Entwürfe
+ Neuer Artikel
+ Artikel-Aktionen
+ Seiten-Aktionen
+ Möchtest Du den Entwurf wirklich löschen?
Entwurf-Aktionen
Entwurf bearbeiten
Entwurf löschen
- Möchtest Du den Entwurf wirklich löschen?
- Seiten-Aktionen
- Artikel-Aktionen
- Kommentar-Aktionen
- Freigeben
- Ablehnen
- Antworten
- Account einrichten
- Versuche, den Account einzurichten.
- Entwurf abbrechen
+ Abbrechen
+ Speichern
+ Hinzufügen
+ Ausgewählte Kategorien:
Veröffentlichen?
+ Antworte auf Kommentar
+ Kommentar erfolgreich moderiert.
+ Antwort erfolgreich hinzugefügt
+ Neuer Account
+ Moderiere Kommentar
Ja
Nein
+ Fehler
+ Seite bearbeiten
+ Artikel bearbeiten
+ Artikel
+ Seiten
+ Hol\' dir ein kostenloses Blog bei WordPress.com
+ Du hast noch kein Blog?
+ Aktualisiere Kategorien
+ Versuche, die Kategorien zu aktualisieren
+ Verbindungsfehler
+ Bearbeitung abbrechen
+ Möchtest Du die Bearbeitung dieses Artikels wirklich abbrechen?
+ Kategorien aktualsiert
+ Fehler beim Aktualisieren der Kategorien
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 10f15b2bc1a8..51a82ead398c 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -1,5 +1,63 @@
+ Biztosan kijelentkezünk a WordPress.com-ról?
+ Lépjünk be újra.
+ WordPress.com
+ Bejelentkezés
+ Kijelentkezés
+ Törlés megerősítése
+ Biztosan töröljük ezt a hozzászólást?
+ Bejelentkezés...
+ Bejelentkezés
+ Értesítések
+ Értesítések kezelése
+ Értesítések engedélyezése
+ Típusok
+ Lehetőségek
+ Biztosan kijelentkezünk?
+ Kikapcsolás
+ Kikapcsolás egy órára
+ Kikapcsolás nyolc órára
+ Érintsük meg a válaszhoz
+ Válasz elküldése
+ Válasz elküldve
+ Követés
+ Követés törlése
+ %d új értesítés
+ és %d további.
+ Értinsük meg a megtekintéshez.
+ Hozzászólás moderálása
+ Kövessük, vagy töröljük a blog követését
+ Sajnos, hiba törtét, a blog követésének beállítása közben. Próbáljuk újra.
+ Kötelező mező
+ Hozzászólás írása
+ Hozzászólás küldése
+ Hozzászólás szövege:
+ Új hozzászólás
+ Kísérlet a hozzászólás elküldésére
+ Adminisztrációs rész megtekintése
+ A hozzászólás mező kitöltése kötelező.
+ A hozzászólást sikeresen hozzáadva.
+ Hozzászólás szerkesztése
+ Oldal megosztása
+ Sajnos, hiba történt az alkalmazás adatbázisának létrehozása közben. Próbáljuk meg újra telepíteni az alkalmazást.
+ Betöltés
+ Honlap megtekintése
+ Nyíltforrású licencek
+ Aláírás hozzáadása a bejegyzéshez
+ Bejegyzés aláírás
+ HTTP felhasználónév
+ HTTP jelszó
+ HTTP belépési adatok (nem kötelező)
+ Blogok engedélyezése
+ Kezdjünk blogolni másodperceken belül
+ Hiba történt a média feltöltése közben
+ Nem sikerült átmeneti fájlt létrehozni a média feltöltéshez. Bizonyosodjunk meg róla, hogy van elég hely az eszközünkön.
+ Hivatkozás megosztása
+ Közzététel
+ Bejegyzés formátum
+ Média hozzáadása
+ Tartalom (éritsük meg szöveg és média hozzáadásához)
Hibás felhasználónév vagy jelszó.
Elnézést, hiba történt a %s törlése közben.
Elnézést, valami hiba történt, a műveletet meg kell ismételni.
@@ -14,7 +72,6 @@
Kategória név
Kategória slug (lehetőség)
Kategória leírása (lehetőség)
- Bejelentkezés
Blog URL
Olvasó
Az eszközön elfogyott a memória.
@@ -77,7 +134,6 @@
URL
Hivatkozás szövege(nem kötelező)
Ez a bejegyzés WordPress for Android alkalmazással készült
- Hozzászólás értesítés
Spam
Feltöltés
Média feltöltése #
@@ -98,7 +154,6 @@
Főoldal
Sajnos, a média elemet nem lehet letölteni a Galériából.
A feltöltés nem sikerült
- Adjunk meg valamit a válasz mezőben.
Megtekintés böngészőben
Ellenőrizzük, hogy a megadott blog cím helyes-e.
Cím nélküli
@@ -219,8 +274,7 @@
Hozzászólás szövege:
Válasz küldése
Válasz a hozzászólásra
- Kötelező mezők
- Beolvasás....
+ Valami szöveget elvárnánk a hozzászólás mezőben!
-
Elfogadva
Elutasítva
@@ -240,10 +294,10 @@
Megjelölve mint nem elfogadott
Válasz
Felhasználói fiók beállítása
- Kísérlet a felhaszálói fiók konfigurálására
Vázlatkészítés megszakítása
Közzétegyük?
Igen
Nem
Helyi vázlatok
+ Előnézet
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 0ff7ad20188e..3394717c29bf 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -1,5 +1,46 @@
+ 無効化
+ 1時間無効化
+ 8時間無効化
+ ログイン
+ ログアウト
+ WordPress.com
+ もう一度ログインしてください。
+ ログイン
+ 設定
+ ログイン中...
+ 通知
+ WordPress.com から本当にログアウトしますか ?
+ 本当にログアウトしますか ?
+ このコメントを本当に削除しますか ?
+ このコメントを本当に削除しますか ?
+ 通知を有効化する
+ 通知の管理
+ タップして返信
+ 返信を公開しました
+ 返信を公開中です
+ タップして表示
+ フォロー
+ フォローを解除
+ このブログをフォローする又はフォローを解除する
+ あと%d件。
+ %d件の新しい通知
+ コメントの承認
+ ブログをフォローしようとしてエラーが発生しました。後ほどもう一度お試しください。
+ コメントを入力:
+ コメントの追加に成功しました
+ コメントを追加
+ コメントを送信
+ コメントを投稿
+ 入力必須欄
+ コメント欄に何か記入してください。
+ コメントを投稿しようとしています
+ 管理画面を表示
+ コメントを編集
+ ページを共有
+ アプリのデータベースを作成する際にエラーが発生しました。アプリを再インストールしてみてください。
+ 読込中…
サイトを表示
オープンソースライセンス
投稿の署名
@@ -8,7 +49,6 @@
HTTP パスワード
HTTP ログイン情報 (オプション)
ブログを有効化
- 更新間隔
すぐにブログを始められます。
メディアアップロードの際に一時ファイルを作成できませんでした。デバイス上に空き容量があるか確認してください。
メディアをアップロードする際にエラーが発生しました
@@ -20,10 +60,10 @@
ユーザー名またはパスワードが正しくありません。
%sを削除する際にエラーが発生しました。
エラーが発生しました。後ほどもう一度お試しください。
- 承認の際にエラーが発生しました。後ほどもう一度お試しください。
コメント編集の際にエラーが発生しました。後ほどもう一度お試しください。
%sをアップロードする際にエラーが発生しました。
ログイン失敗の上限回数を超えました。ログイン情報が正しいことをお確かめの上、数分後にもう一度お試しください。
+ コメントの処理の際にエラーが発生しました。後ほどもう一度お試しください。
©2013 Automattic, Inc
パスワード
ユーザー名
@@ -31,61 +71,60 @@
カテゴリー名
カテゴリースラッグ (オプション)
カテゴリーの説明 (オプション)
- ログイン
ブログの URL
購読ブログ
デバイスのメモリが足りません。
アップロードするメディアファイルが見つかりませんでした。削除または移動されたようです。
アイキャッチ画像として使用
投稿本文中に画像を含める
- ご指定のアカウントに %s ブログが見つかりませんでした。
- 追加
- すべて追加
- 選択したブログを追加
+ 統計情報への接続中にエラーが発生しました。後ほど再度お試しください。
+ %sの再読み込みに失敗しました。後ほど再度お試しください。
+ WordPress サイトに接続できませんでした。後ほど再度お試しください。
+ 利用可能なネットワークがありません。ネットワークに接続して再度お試しください。
ブログを選択
+ 選択したブログを追加
利用可能なネットワークがありません
- 利用可能なネットワークがありません。ネットワークに接続して再度お試しください。
- %sの再読み込みに失敗しました。後ほど再度お試しください。
- 統計情報への接続中にエラーが発生しました。後ほど再度お試しください。
ページ
キャプション (オプション)
- WordPress サイトに接続できませんでした。後ほど再度お試しください。
横幅
+ ページ
投稿
匿名
ブログが見つかりませんでした。
- ページ
- ログイン情報が間違っているか、この WordPress.com アカウントに紐付けられていないブログのようです。
横位置
投稿
+ ご指定のアカウントに %s ブログが見つかりませんでした。
+ 追加
+ すべて追加
+ ログイン情報が間違っているか、この WordPress.com アカウントに紐付けられていないブログのようです。
+ 画像の設定
WordPress for BlackBerry からの投稿
+ WordPress ブログ
+ WordPress for BlackBerry
+ このコメントの修正を中止しますか ?
blogusername
カラム 1
カラム 2
- WordPress ブログ
- 画像の設定
- WordPress for BlackBerry
タップしてテキストとメディアを追加
OK
- このコメントの修正を中止しますか ?
接続できませんでした。サイト上の xmlrpc.php へのフルパスを入力し、もう一度お試しください。
まだ保存されていないアップロード済みの投稿に変更が加えられています。再読み込みしてローカルの変更を上書きしますか ?
ローカルの変更
+ 変更後の画像幅
アップロードしてサイズを変更した画像にリンク
正しい画像幅を入力してください
- 変更後の画像幅
- コメント本文
名前
メールアドレス
URL
+ コメント本文
コメントを更新
- 変更を保存しています
コメント本文は必須項目です。
+ 変更を保存中
トピック
更新
+ 位置情報が見つかりません
削除
投稿の読み込み中にエラーが発生しました。リロードしてもう一度お試しください。
- 位置情報が見つかりません
予約済み
このブログにアクセスする際にエラーが発生しました。
ダッシュボード
@@ -96,7 +135,6 @@
リンクテキスト (オプション):
URL
WordPress for Android から投稿
- コメント通知
スパム
アップロード中
アップロード中: メディア #
@@ -117,12 +155,11 @@
クイックビデオ
ギャラリーからメディアを取得できませんでした。
アップロードに失敗しました。
- 記入したブログ URL が正しいかどうかご確認ください。\n
ブラウザで表示
- 返信欄に何か記入してください。\n
+ 記入したブログ URL が正しいかどうかご確認ください。\n
タイトル未設定
- 文章を入力するか、メディア項目を添付してください。
コンテンツがありません
+ 文章を入力するか、メディア項目を添付してください。
編集
投稿
固定ページ
@@ -134,12 +171,12 @@
地図を表示
この投稿はまだ公開されていません
このページはまだ公開されていません
- URLを取得しています
+ URL を取得中
QuickPress ショートカット用のブログを選択
ショートカット名を記入してください
公開
- レビュー待ち
下書き
+ レビュー待ち
非公開
アップロードしてフルサイズの画像にリンクする
無効なブログ URL
@@ -149,19 +186,19 @@
設定を読み込みますか ?
不正なログイン
もしくはサイトが見つかりません。
- 送信する前にすべての欄をご記入ください。
WordPress.com で新しいブログを作成
+ 送信する前にすべての欄をご記入ください。
コメント投稿者:
パスワード
ユーザー名
- ユーザー名とパスワードは必須です。
タイトル
タグ (コンマ区切り)
+ ユーザー名とパスワードは必須です。
コメント承認中
さらに読み込む
統計情報
コメント削除中
- コメント承認に成功しました
+ コメントの処理を完了しました
環境設定
投稿を削除しました
投稿を削除しようとしています
@@ -172,89 +209,89 @@
ページを削除しようとしています
ページを削除
キャプチャメディアの設定中にエラーが発生しました。
- 通知ライトを点滅
通知音を再生
+ 通知ライトを点滅
バイブ
位置データがまだ利用できません。少々お待ちください。
状態
- メディアをアップロードするには、SD カードが必要です。
場所
新しいビデオを撮る
SD カードが必要です
ギャラリーからビデオを選択する
+ メディアをアップロードするには、SD カードが必要です。
ページをプレビュー
投稿をプレビュー
- アカウント詳細
メディア
+ アカウント詳細
WordPress アカウントが見つかりません。アカウントを追加して、もう一度試してください。
ブログを選択
- 設定を確認して再度お試しください。
- カテゴリーを追加しました。
カテゴリー名は必須項目です。
- コメントを削除しています
+ カテゴリーを追加しました。
+ 設定を確認して再度お試しください。
削除
承認
承認しない
+ コメントを削除中
必須項目
- 新しいカテゴリーを追加
- 新しいカテゴリーを追加中
- カテゴリーを追加中
- カテゴリーの追加に失敗しました。
なし
+ カテゴリーの追加に失敗しました。
親カテゴリー (オプション):
+ カテゴリーを追加中
+ 新しいカテゴリーを追加
+ 新しいカテゴリーを追加中
必須項目
カテゴリーを選択
- ブログを取り除きました
ブログを取り除く
+ ブログを取り除きました
本当にこのブログを取り除きますか ?
ブログ
ブログを取り除くことができません。
ギャラリーから写真を選択
新しい写真を撮影
- エラー
- カテゴリ選択:
- キャンセル
- 保存
- 追加
- ページを編集
- 投稿を編集
- カテゴリーを再読み込みしています
- カテゴリーを再読み込みしようとしています
- 接続エラー
接続エラーが発生しました。のちほど試してみてください。
- カテゴリーの再読み込みエラー
- この投稿の修正を中止しますか ?
- 新規アカウント
+ 下書きを取り消す
コメント
- 投稿
- ページ
- このページの修正を中止しますか?
- 新規ページ
- 下書き
- 新規投稿
- 返信を入力する
返信を送信
コメントに返信
- 必須項目
+ 返信を入力してください。
+ 返信を入力する
+ コメントアクション
+ 承認されたものにマークをつける
+ 未承認にマークをつける
+ 返信する
プレビュー
- 読み込み中…
オン
承認済
未承認
スパム
- コメント返信
+ アカウント登録
+ このページの修正を中止しますか?
+ 新規ページ
+ 下書き
+ 新規投稿
+ 投稿アクション
+ ページアクション
+ 本当に下書きを削除しますか?
下書きを編集
下書きを削除
- 本当に下書きを削除しますか?
- ページアクション
- 投稿アクション
- コメントアクション
- 承認されたものにマークをつける
- 未承認にマークをつける
- 返信する
- アカウント登録
- 下書きを取り消す
+ キャンセル
+ 保存
+ 追加
+ カテゴリ選択:
公開しますか?
+ コメント返信
+ 新規アカウント
はい
いいえ
+ エラー
+ ページを編集
+ 投稿を編集
+ 投稿
+ ページ
+ 接続エラー
+ カテゴリーの再読み込みエラー
+ この投稿の修正を中止しますか ?
+ コメントの処理を完了しました
+ カテゴリーを再読み込み中
+ カテゴリーを再読み込み中
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 85107c283bf2..04f7e81693d0 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -1,5 +1,47 @@
+ 워드프레스닷컴에서 로그아웃 하시겠습니까?
+ 로그아웃 하시겠습니까?
+ 다시 로그인 해주세요.
+ 워드프레스닷컴
+ 로그인
+ 로그아웃
+ 삭제 확인
+ 이 댓글을 삭제하시겠습니까?
+ 로그인 중...
+ 로그인
+ 알림
+ 알림 관리
+ 알림 활성화
+ 형식
+ 옵션
+ 끄기
+ 1시간 동안 끄기
+ 8시간 동안 끄기
+ 답변하려면 탭하세요
+ 답변 발행하기
+ 답변이 발행됐습니다
+ 팔로우
+ 팔로우 해제
+ %d 새 알림
+ 그리고 %d 더 있습니다.
+ 보기는 탭하세요.
+ 댓글 검토
+ 이 블로그 팔로우 또는 팔로우 해제하기
+ 죄송합니다. 블로그를 팔로우 시도하던 중 에러가 발생했습니다. 나중에 다시 해주세요.
+ 댓글 추가
+ 댓글 입력:
+ 댓글 보내기
+ 댓글 쓰기
+ 필수 필드
+ 댓글 필드에 무언가 입력해주세요.
+ 댓글이 성공적으로 추가됐습니다
+ 댓글 올리는 중
+ 관리자 보기
+ 댓글 편집
+ 페이지 공유
+ 죄송합니다. 앱 데이터베이스 생성 중 에러가 발생했습니다. 앱 재설치를 해주세요.
+ 로딩 중...
사이트 보기
오픈 소스 라이센스
글 서명
@@ -8,7 +50,6 @@
HTTP 비밀번호
HTTP 인증서(선택사항)
블로그 활성화
- 공백기간 업데이트
잠시 후에 블로깅을 시작합니다.
미디어 업로드를 위한 임시 파일을 생성할 수 없습니다. 기기에 충분한 여유 공간이 있는지 확인하세요.
미디어를 업로드하는데 에러가 발생했습니다
@@ -31,11 +72,10 @@
카테고리 이름
카테고리 슬러그(선택)
카테고리 설명(선택)
- 로그인
블로그 URL
리더
- 기기 메모리가 부족합니다.
업로드을 하려는 미디어 파일을 찾을 수 없습니다. 파일이 삭제 또는 이동 되었습니까?
+ 기기 메모리가 부족합니다.
특성 이미지로 사용
글 컨텐트에 이미지 포함
해당 계정으로 %s 블로그를 찾을 수 없습니다.
@@ -46,7 +86,6 @@
네트워크가 가능하지 않습니다
가능한 네트워크가 없습니다. 네트워크에 연결하고 다시 해보세요.
죄송합니다. %s가 현재 새로고침 할 수가 없습니다. 나중에 다시 해보세요.
- 죄송합니다. 통계와 연결 시도중 에러가 발생했습니다. 다시 해보세요.
페이지
캡션(선택 사항)
죄송합니다. 워드프레스 사이트에 연결할 수 없습니다. 나중에 다시 해보세요.
@@ -58,29 +97,30 @@
올바른 로그인이 아닙니다. 또는 블로그가 WordPress.com 계정에 연결되지 않았습니다.
수평 정렬
글
+ 죄송합니다. 통계와 연결 시도중 에러가 발생했습니다. 다시 해보세요.
워드프레스에서 블랙베리로 글 게시됨
+ 텍스트와 미디어를 추가하려면 탭하세요
+ OK
+ 정말로 이 댓글 편집을 취소하시겠습니까?
블로그 사용자명
컬럼 1
컬럼 2
워드프레스 블로그
이미지 설정
블랙베리용 워드프레스
- 텍스트와 미디어를 추가하려면 탭하세요
- OK
- 정말로 이 댓글 편집을 취소하시겠습니까?
연결할 수 없습니다. 사이트에 있는 xmlrpc.php 전체 경로를 입력한 후 다시 시도하십시오.
아직 저장되지 않은 기존 게시물을 변경했습니다. 새로 고침을 실행하고 변경 사항을 덮어쓰시겠습니까?
로컬 변경내용
- 업로드 후 크기 변경된 이미지로 연결
올바른 변경 너비를 입력하십시오.
크기 변경 이미지 너비
- 댓글 내용
+ 업로드 후 크기 변경된 이미지로 연결
이름
이메일
URL
- 댓글 업데이트
변경 저장 중
댓글은 필수입니다.
+ 댓글 내용
+ 댓글 업데이트
주제
업데이트
삭제
@@ -96,7 +136,6 @@
링크 텍스트
URL
Wordpress안드로이드에서 올렸습니다.
- 댓글 알림
스팸
전송중
미디어 # 를 올리고 있습니다.
@@ -119,7 +158,6 @@
업로드 실패
입력된 블로그 URL이 정확한지 확인하세요.
브라우저에서 보기
- 응답란에 무엇이든 입력하세요.
무제
본문을 작성하거나 미디어를 첨부하십시요.\nPlease enter some post content or attach a media item.
내용이 없습니다.
@@ -147,21 +185,21 @@
독립 호스팅 워드프레스 블로그 추가
Geotag 글
설정을 불러오시겠습니까?
- 잘못된 로그인
또는 사이트가 없습니다.
+ 잘못된 로그인
제출하기 전에 모든 항목을 채워주십시요
Wordpress.com에서 새 블로그 시작
다음에서 온 댓글
- 비밀번호
- 사용자명
- 사용자명과 비밀번호는 필수 항목입니다.
제목
+ 사용자명과 비밀번호는 필수 항목입니다.
+ 사용자명
+ 비밀번호
태그(쉼표로 구분)
- 댓글 관리
- 더보기
- 통계
댓글 삭제
댓글 삭제완료
+ 통계
+ 댓글 관리
+ 더보기
환경 설정
게시글 삭제완료
게시글 삭제중
@@ -172,83 +210,88 @@
페이지 삭제중
페이지 삭제
캡쳐한 미디어를 설정하던 중 에러가 발생했습니다.
+ 진동
통지 표시 깜빡임
통지음 실행
- 진동
위치정보를 아직 사용할 수 없습니다. 잠시 기다려 주십시요
상태
- 미디어를 업러드 하려면 장착된 SD 카드가 필요합니다.
- 위치
- 동영상 촬영
SD 메모리카드 필요
겔러리에서 동영상을 선택하세요
+ 동영상 촬영
+ 위치
+ 미디어를 업러드 하려면 장착된 SD 카드가 필요합니다.
페이지 보기
포스트 보기
- 계정 상세정보
미디어
+ 계정 상세정보
WordPress 계정이 없습니다, 계정을 추하신 후 사용하세요.
블로그 선택
설정을 다시 확인하세요.
카테고리 추가완료
카테고리 이름 란은 필수입니다.
- 댓글 삭제
삭제
+ 댓글 삭제
승인
불승인
- 필수 항목
+ 없음
+ 카테고리 추가실패
+ 카테고리 추가중
신규 카테고리 추가
카테고리 추가중
- 카테고리 추가중
- 카테고리 추가실패
- 없음
+ 필수 항목
상위 카테고리(선택 사항)
필수 항목
카테고리 선택
블로그 제거완료
+ 블로그를 제거할 수 없습니다.
+ 블로그
블로그 제거
정말로 이 블로그를 제거하시겠습니까?
- 블로그
- 블로그를 제거할 수 없습니다.
겔러리에서 사진을 선택하세요
사진촬영
+ 아니오
+ 예
+ 게시하시겠습니까?
+ 답글
+ 계정설정
+ 입력취소
+ 댓글 관리
+ 댓글 승인
+ 댓글 검증중
오류
선택된 카테고리
- 아직 블로그가 없으세요?
- WordPress.com에서 무료 블로그를 받으세요.
취소
저장
추가
페이지 수정
글 수정
- 카테고리 새로고침
- 카테고리 새로고침 시도 중
연결 오류
- 연결오류가 발생하였습니다. 다시 시도하시기 바랍니다.
- 카테고리 새로고침완료
+ 카테고리 새로고침
카테고리 새로고침 오류
- 수정 취소
+ 카테고리 새로고침완료
+ 연결오류가 발생하였습니다. 다시 시도하시기 바랍니다.
수정한 글을 취소하시겠습니까?
- 신규계정
댓글
게시글
- 페이지
- 정말로 이 페이지 편집을 취소하시겠습니까?
- 새 페이지
- 로컬 임시글
- 새 글
댓글 입력
댓글 전송
댓글 답변
- 필수 항목
- 미리보기
- 불러오기중...
- 켜기
수락
검토중
스팸
+ 수정 취소
+ 신규계정
+ 로컬 임시글
+ 새 글
+ 켜기
댓글 관리하기
댓글 응답
댓글 관리 성공
+ 아직 블로그가 없으세요?
+ WordPress.com에서 무료 블로그를 받으세요.
+ 카테고리 새로고침 시도 중
+ 페이지
+ 새 페이지
응답이 성공적으로 추가됐습니다.
임시글 작업
임시글 편집
@@ -256,14 +299,7 @@
임시글을 정말로 삭제하시겠습니까?
페이지 작업
글 작업
- 댓글 관리
- 댓글 승인
- 댓글 검증중
- 답글
- 계정설정
- 계정을 구성중입니다.
- 입력취소
- 게시하시겠습니까?
- 예
- 아니오
+ 정말로 이 페이지 편집을 취소하시겠습니까?
+ 미리보기
+ 답변 필드에 무언가 입력해주세요.
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 4aae16b937a9..1c5b864e7db6 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -1,11 +1,87 @@
- Helaas, we hebben geen WordPress website gevonden voor deze URL.
+ Stuur Reactie
+ Voer reactie in
+ Reactie Succesvol Toegevoegd
+ Voeg Reactie toe
+ Schrijf Reactie
+ Verplicht Veld
+ Voer iets in het reactie veld in.
+ Proberen om reactie te plaatsen
+ Bekijk Admin
+ Bewerk reactie
+ Deel Pagina
+ Sorry, er kwam een foutmelding tijdens het creëren van de app database. Herinstalleer de app.
+ Laden...
+ Bekijk Site
+ Open Source Licentie
+ Bericht handtekening
+ Voeg een handtekening toe aan nieuwe berichten
+ HTTP gebruikersnaam
+ HTTP wachtwoord
+ HTTP inloggegevens (optioneel)
+ Activeer Blogs
+ Begin te bloggen binnen enkele seconden
+ Kon geen tijdelijk bestand aanmaken voor de media upload. Kijk of er genoeg ruimte is op je apparaat.
+ Er kwam een foutmelding tijdens het uploaden van media
+ Deel Link
+ Inhoud (klik om tekst en media toe te voegen)
+ Publiceer
+ Bericht Format
+ Media toevoegen
+ Incorrecte gebruikersnaam en wachtwoord.
+ Sorry, er kwam een foutmelding wanneer je %s probeerde te verwijderen.
+ Sorry, er kwam een foutmelding. Probeer het later nogmaals.
+ Sorry, er kwam een foutmelding tijdens het modereren. Probeer het later nogmaals.
+ Sorry, er kwam een foutmelding tijdens bewerken van een reactie. Probeer het later nogmaals.
+ Sorry, er kwam een foutmelding tijdens uploaden van %s.
+ Je hebt het login limiet overschreven. Controleer of the inloggegevens correct zijn en probeer het nogmaals over een paar minuten.
+ ©2013 Automattic, Inc
+ Wachtwoord
+ Gebruikersnaam
+ Blog Toevoegen
+ Categorie Naam
+ Categorie Slug (optioneel)
+ Category Omschrijving (optioneel)
+ Blog URL
+ Reader
+ Het toestel heeft onvoldoende geheugen.
+ Kan het bestand dat je wilt uploaden niet vinden. Misschien heb je het verwijderd of verplaatst?
+ Gebruiken als uitgelichte afbeelding
+ Plaats afbeelding in bericht
+ Geen %s blogs gevonden voor dit account.
+ additioneel
+ Alles toevoegen
+ Geselecteerde toevoegen
+ Selecteer Blogs
+ Geen netwerk beschikbaar
+ Er is geen netwerk beschikbaar. Maak verbinding met een netwerk en probeer het later opnieuw.
+ Fout bij het verversen van %s. Probeer het later opnieuw.
+ Er is een fout opgetreden bij het ophalen van de statistieken. Probeer het later opnieuw.
+ Pagina\'s
+ Onderschrift (optioneel)
+ Er is een fout opgetreden bij het verbinden naar je WordPress website. Probeer het later opnieuw.
+ Breedte
+ Berichten
+ Anoniem
+ Geen blogs gevonden
+ Pagina
+ Inloggegevens zijn fout, of je blog is niet gelinkt aan dit WordPress.com account.
+ Horizontaal uitlijnen
+ Bericht
+ Geplaatst met WordPress voor BlackBerry
+ blogusername
+ Kolom 1
+ Kolom 2
+ WordPress Blog
+ Afbeelding instellingen
+ WordPress voor BlackBerry
+ Tik hier om tekst en media toe te voegen
+ Oké
+ Weet je zeker dat je het wijzigen van de reactie wilt annuleren?
Kan geen verbinding maken. Vul het volledige pad in naar het bestand: xmlrpc.php en probeer het opnieuw.
- ©2012 Automattic, Inc
Er zijn wijzigingen aan dit bericht die nog niet zijn opgeslagen. Weet je zeker dat je de pagina wilt verversen? Alle niet opgeslagen wijzigingen gaan verloren!
Lokale wijzigingen
- URL delen
Upload en link naar afbeelding
Vul een geldig geschaalde breedte in
Breedte geschaalde afbeelding
@@ -15,270 +91,185 @@
URL
Reactie bijwerken
Wijzigingen opslaan
- De auteur naam is vereist.
- Het e-mail adres van de auteur is vereist.
Een reactie is vereist.
Onderwerpen
+ Update
+ Verwijderen
+ Er is een fout opgetreden bij het laden van het bericht. Herlaad je berichten en probeer het opnieuw.
+ Onbekende locatie
Gepland
Sorry, er is een fout opgetreden bij te toegang van het blog.
Dashboard
Gepubliceerd
- Voor deze functie is een WordPress.com account vereist.
Deel bericht
Hulp nodig?
Maak een link
- URL
Link tekst (optioneel)
+ URL
Geplaatst met WordPress voor Android
Spam
Uploaden
Uploaden van een media bestand #
Versie
Eindgebruikersovereenkomst
+ Privacy Policy
WordPress voor Android
Ontwikkelaar: Automattic, Inc
Over
- Status
Tags & categorieën
Lokaal concept
Meer
- Jammer genoeg bevind er zich geen camera op uw toestel.
- Begin met bloggen vanaf je tablet in seconden.
+ Standaard breedte afbeelding
Uitlijning
+ Home
Vernieuw
- Lees
Snelle Foto
Snelle Video
- Home
+ Het bestand kon niet worden opgehaald vanuit de media bibliotheek.
Upload mislukt
- Upload geslaagd
- Bekijk in je browser
- Er zijn geen reacties goedgekeurd voor dit bericht.
Gelieve de foute URL van het blog te verbeteren.
- Kies een blog:
- Schrijf in Volledig Scherm
- Verlaten zonder opslaan
- Opslaan en volledig scherm verlaten
- Jouw account
- .wordpress.com
- Wachten op de e-mail bevestiging
- WordPress.com Sign Up
- Blog Adres
- Mail
- Registreer
- Geen inhoud gevonden
+ Bekijk in je browser
Zonder titel
+ Voeg aub wat inhoud aan het bericht toe of voeg een media item toe.
+ Geen inhoud gevonden
Bewerk
- Publicatiedatum:
Bericht
Pagina
Wachtwoord (optioneel)
Direct
Geef een snelkoppelingsnaam
- Vandaag
- HTTP gebruikersnaam (optioneel)
Instellingen
Optionele instellingen
- HTTP wachtwoord (optioneel):
Bekijk kaart
- De URL aan het halen.
Bericht is nog niet gepubliceerd
Pagina is nog niet gepubliceerd
+ De URL aan het halen.
Selecteer een blog voor de QuickPress snelkoppeling
Snelkoppelingsnaam kan niet leeg zijn.
- Gebruikersovereenkomst
Publiceer
- Privé
Wachtend op review
Concept
+ Privé
Upload en link naar de volledige afbeelding
Foutieve blog URL
Voeg een blog gehost bij WordPress.com toe
Voeg een zelf gehoste WordPress blog toe
- HTML bewerker geactiveerd
- De permalink werd niet gevonden.
Geotag berichten
Je instellingen nu laden?
Foute log-in
of de site is niet gevonden.
+ Gelieve alle velden in te vullen voor je verder gaat.
Begin een nieuw blog op WordPress.com
Reactie van
- Gebruikersnaam
- Datum
- Bericht titel
Wachtwoord
+ Gebruikersnaam
+ Gebruikersnaam en het wachtwoord zijn verplicht om ingevuld te worden.
Titel
- Inhoud van de pagina
- Titel van het bericht
- Inhoud van het bericht
Tags (gescheiden door komma\'s)
- Kliks
- Categorieën
- Er is geen data voor de statistieken gevonden. Probeer aub opnieuw.
- Proberen om te statistieken te laden
- Ophalen van de Statistieken
- Statistieken login
- Jaar
- Verwijder reacties
Reacties beheren
- 7 Dagen
- 30 Dagen
- Video Speelt
Laad Meer
- Bekeken
- Zoek Termen
- Wijk
- Bericht Bezichtigen
- Verwijzers
Statistieken
+ Verwijder reacties
Reacties Succesvol Gemodereerd
- Sinds altijd
- Instellingen
Voorkeuren
- Verwijder pagina
+ Bericht Succesvol Verwijderd
+ Proberen om het bericht te verwijderen
+ Ben je zeker dat je het bericht wilt verwijderen?
Verwijder bericht
Pagina succesvol gewist
Ben je zeker dat je deze pagina wilt verwijderen
- Ben je zeker dat je het bericht wilt verwijderen?
- Bericht Succesvol Verwijderd
- Proberen om het bericht te verwijderen
Proberen om de pagina te verwijderen
- Vibreren
+ Verwijder pagina
+ Er was een fout tijdens het opzetten van de opgenomen media.
Flikker het notificatie licht
Speel het notificatie geluid
+ Vibreren
De locatie gegevens zijn niet beschikbaar, even geduld aub.
Status
+ Een werkende SD kaart is nodig om media te uploaden
Locatie
- Uploaden Inhoud
Maak een nieuwe video
SD kaart is vereist
Selecteer een video van je galerij
- Begin het bloggen via je gsm in enkele seconden tijd!
Bekijk pagina
Bekijk Bericht
- Bestandsfout
- Media
Account Details
- Blog succesvol verwijderd
+ Media
+ Geen WordPress account gevonden, gelieve een account toe te voegen en opnieuw te proberen
Selecteer een Blog
+ Controleer aub je instellingen en probeer opnieuw.
Categorie is succesvol toegevoegd.
Het veld van de Categorie Naam is vereist.
- Controleer aub je instellingen en probeer opnieuw.
+ Verwijder reactie
Verwijderen
Goedkeuren
Weigeren
- Verwijder reactie
+ Vereist Veld
+ Een Nieuwe Categorie Toevoegen
+ Proberen van een nieuwe categorie toe te voegen
Voeg categorie toe
Categorie toevoegen is mislukt.
- Categorie Beschrijving (optioneel):
- Vereist Veld
Geen
Categorie Hoofd (optioneel):
- Een Nieuwe Categorie Toevoegen
- Proberen van een nieuwe categorie toe te voegen
- Gedupliceerde categorie
- Vernieuw Categorieën
- Categorie Naam:
- Categorie Slug (optioneel):
Vereiste Velden
Selecteer categorieën
- Kon het blog niet verwijderen.
- Blogs
- Verwijder Blog
Blog succesvol verwijderd
- Maak een nieuwe foto
+ Verwijder Blog
+ Weet je zeker dat je dit blog wilt verwijderen?
+ Blogs
+ Kon het blog niet verwijderen.
Selecteer een foto van je galerij.
- Voeg Media Toe
- Nee
- Ja
- Voeg account toe
- Nieuwe reactie
- Publiceren?
- Gelukt!
- Aanvaarden
- uur
- uur
- dagelijks
- Daling
- Beantwoord
- Publiceer: Ja
- Publiceer: Nee
- minuten
- Nieuwe pagina
+ Maak een nieuwe foto
+ Fout
Selecteer categorieën
- Proberen om de pagina te uploaden
- Beheer je reacties
- Het bericht was succesvol bewerkt
- Proberen om de account aan te passen
- Bericht aan het halen
- Pagina bewerkt
- Annuleer bewerking
- Pagina was succesvol bewerkt
- Je hebt geen pagina\'s op je blog
- Vernieuw interval:
- Annuleer klad
- Account/Blog is succesvol toegevoegd
- Account Opstellen
- Account bestaat al
- Bericht Acties
- Proberen het proberen up te loaden
- Acties voor de reacties
- Markeer Aanvaard
- Markeer Niet Aanvaard
- Pagina acties
- Spam
- aan
- Laden
- Aanvaard
- Geweigerd
- Gebruikersnaam:
- Originele Grootte
- De instellingen zijn niet gevonden
- Bericht bewerkt
- Verplicht Veld
- Reactie Succesvol Gemodereerd
Heb je nog geen blog?
+ Maak een gratis blog op WordPress.com
+ Annuleer
+ Bewaar
Voeg toe
Bewerk Pagina
- WordPress
- Plaats een reactie:
- Bericht Toegevoegd
- Lokale klad berichten
- Berichten
- Geen Pagina\'s Gevonden
- Modereren reactie
Bericht Bewerken
- Geen tekst geselecteerd
- Reacties
- Wachtwoord:
- Fout
- Annuleer
- Bewaar
- Maak een gratis blog op WordPress.com
- Ophalen van pagina
Vernieuwen van Categorieën
Proberen om de categorieën te vernieuwen
Verbindingsfout
+ Er deed zich een fout voor met de verbinding. Probeer aub later opnieuw.
Categorieën vernieuwd
Categorie vernieuwen fout
- Pagina\'s
+ Annuleer bewerking
+ Weet je zeker dat je het bewerken van dit bericht wilt annuleren?
Nieuwe Account
+ Reacties
+ Berichten
+ Pagina\'s
+ Weet je zeker dat je het bewerken van deze pagina wilt annuleren?
+ Nieuwe pagina
+ Lokale klad berichten
Nieuw Bericht
+ Plaats een reactie:
Verzend Antwoord
- Reacties Bekijken
+ Reageer op een reactie
+ Voorbeeld
+ aan
+ Aanvaard
+ Geweigerd
+ Spam
+ Modereren reactie
Reageren op een reactie
+ Reactie Succesvol Gemodereerd
Antwoord Succesvol Toegevoegd
- Geen Berichten Gevonden
- Je hebt geen enkel bericht op je blog
- Proberen om de pagina te bewerken
- Proberen het bericht te bewerken
- Maak leeg
- Site instellingen
Concept acties
- Upload concept naar site
+ Bewerk concept
Verwijder concept
Weet je zeker dat je het concept wilt verwijderen?
- Bewaard naar lokale concept berichten
- Reageer op een reactie
- Bewerk concept
+ Pagina acties
+ Bericht Acties
+ Acties voor de reacties
+ Markeer Aanvaard
+ Markeer Niet Aanvaard
+ Beantwoord
+ Account Opstellen
+ Annuleer klad
+ Publiceren?
+ Ja
+ Nee
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index d0d3b84d13a7..a5031f25c0ac 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -1,77 +1,125 @@
+ WordPress.com
+ Пожалуйста, войдите снова.
+ Вы уверены, что хотите выйти?
+ Войти
+ Выйти
+ Подтвердите удаление
+ Вы уверены, что хотите удалить этот комментарий?
+ Войти
+ Уведомления
+ Включить уведомления
+ Типы
+ Подписаться
+ Отписаться
+ Подписаться или отписаться от этого блога
+ Вы уверены, что хотите выйти с WordPress.com?
+ Вход…
+ Выключить
+ Выключить на 1 час
+ Выключить на 8 часов
+ Щёлкните для ответа
+ Публикация ответа
+ Ответ опубликован
+ и %d ещё.
+ Щёлкните для просмотра.
+ Модерировать комментарий
+ Извините, при подписке на блог произошла ошибка. Пожалуйста, повторите попытку позже.
+ Параметры уведомлений
+ Настройки
+ Новые уведомления: %d
+ Отправить комментарий
+ Введите комментарий:
+ Комментарий успешно добавлен
+ Добавить комментарий
+ Написать комментарий
+ Обязательное поле
+ Пожалуйста, введите текст в поле комментария.
+ Попытка отправить комментарий
+ Панель управления
+ Редактировать комментарий
+ Извините, при попытке создать базу данных приложения произошла ошибка. Попробуйте переустановить приложение.
+ Поделиться
+ Загрузка…
Взглянуть на сайт
Открытые лицензии
Подпись для записи
Добавлять подпись к новым записям
Имя для HTTP-авторизации
Пароль для HTTP-авторизации
- HTTP авторизация (необязательно)
+ HTTP-авторизация (необязательно)
Разрешить блоги
- Интервал обновления
Начните писать через секунду
+ Произошла ошибка при загрузке медиафайла
+ Не удалось создать временный файл для загрузки медиафайла. Пожалуйста, убедитесь, что на вашем устройстве достаточно свободного места.
Поделиться ссылкой
- Содержимое (нажмите, чтобы добавить текст и медиа)
Опубликовано
Формат записи
Добавить медиа
+ Содержимое (нажмите, чтобы добавить текст и медиа)
Неправильное имя или пароль.
+ Извините, при модерировании произошла ошибка. Повторите попытку позже.
+ Извините, произошла ошибка. Повторите попытку позже.
+ Извините, при загрузке %s произошла ошибка.
+ Извините, при попытке удалить %s произошла ошибка.
+ Извините, при редактировании комментария произошла ошибка. Повторите попытку позже.
+ Вы превысили лимит попыток входа. Пожалуйста, убедитесь, что ваши координаты верны, и повторите попытку через несколько минут.
©2013 Automattic, Inc
- Пароль
+ URL блога
Имя
+ Пароль
Добавить блог
Имя рубрики
Ярлык рубрики (необязательно)
Описание рубрики (необязательно)
- Войти
- URL блога
Читать
Память устройства переполнена.
Не найден медиа-файл для загрузки. Был удален или перемещен?
- Использовать как миниатюру
- Включить изображение в текст записи
- Связанных с этой учётной записью %s блогов не найдено.
- дополнительных
Добавить все
- Добавить выбранные
Выбрать блоги
- Сеть недоступна
- Отсутствует соединение с сетью. Подключитесь к сети и повторите попытку.
+ Использовать как миниатюру
+ Включить изображение в текст записи
Извините, не удалось обновить %s. Повторите попытку позже.
- Извините, при подключении к статистике произошла ошибка. Повторите попытку позже.
+ Отсутствует соединение с сетью. Подключитесь к сети и повторите попытку.
Страницы
- Подпись (необязательно)
+ Извините, при подключении к статистике произошла ошибка. Повторите попытку позже.
Извините, не удалось подключиться к сайту на WordPress. Повторите попытку позже.
Ширина
Записи
- Аноним
- Блогов не найдено
Страница
+ Запись
Неверное имя пользователя, либо с данной учётной записью WordPress.com не связан блог.
+ Подпись (необязательно)
+ Аноним
+ Блогов не найдено
Горизонтальное выравнивание
- Запись
- Опубликовано из WordPress для BlackBerry
- имя-пользователя
- Колонка 1
+ Сеть недоступна
+ Добавить выбранные
+ дополнительных
+ Связанных с этой учётной записью %s блогов не найдено.
+ Настройки рисунка
Колонка 2
Блог WordPress
- Настройки рисунка
- WordPress для BlackBerry
- Коснитесь, чтобы добавить текст и медиафайлы
OK
+ Колонка 1
+ WordPress для BlackBerry
+ Опубликовано из WordPress для BlackBerry
Вы уверены, что хотите отказаться от изменения комментария?
+ имя-пользователя
+ Коснитесь, чтобы добавить текст и медиафайлы
Не удалось подключиться. Пожалуйста, введите полный путь к файлу xmlrpc.php на вашем сайте и попытайтесь ещё раз.
Вы внесли изменения в загруженную статью, которая ещё не была сохранена. Хотите обновить страницу и потерять локальные изменения?
Локальные изменения
Загружать и ссылаться на уменьшенное изображение
Пожалуйста, введите корректное значение ширины
Ширина уменьшенного изображения
- Текст комментария
Имя
Электронная почта
Адрес URL
Обновить комментарий
Сохранение изменений
+ Текст комментария
Необходимо указать текст комментария.
Темы
Обновить
@@ -84,11 +132,10 @@
Опубликовано
Поделиться записью
Нужна помощь?
+ URL
Создать ссылку
Текст ссылки (не обязательно)
- URL
Опубликовано из WordPress для Android
- Уведомления о комментариях
Спам
Загрузка
Загрузка медиафайла #
@@ -105,16 +152,15 @@
Выравнивание
Домой
Обновить
- Быстрое фото
Быстрое Видео
+ Быстрое фото
Извините, не удалось получить медиафайл из библиотеки.
Сбой при загрузке
Пожалуйста, убедитесь, что URL блога введен правильно.
Просмотреть в браузере
- Пожалуйста, введите текст в поле ответа.
+ Не найдено содержание
Без заголовка
Введите содержимое записи или прикрепите медиафайл.
- Не найдено содержание
Изменить
Запись
Страница
@@ -129,57 +175,57 @@
Пытаемся открыть ссылку
Выберите блог для QuickPress
Ярлык не может быть пустым
- Опубликовать
- На утверждении
Черновик
Личное
+ Опубликовать
+ На утверждении
Загружать и ссылаться на полное изображение
Неверный URL блога
Добавьте блог WordPress.com
Добавьте автономный блог
Геотегинг записей
Загрузить настройки сейчас?
- Неправильный логин
или сайт не найден.
+ Неправильный логин
Пожалуйста, заполните все поля перед отправкой.
Создайте блог на WordPress.com
Комментарий от
Пароль
- Имя пользователя
- Необходимо указать имя пользователя и пароль.
Заголовок
Метки (разделяйте запятыми)
- Модерирование комментариев
+ Имя пользователя
+ Необходимо указать имя пользователя и пароль.
Загрузить больше
Статистика
Удаление комментариев
+ Модерирование комментариев
Модерация завершена
Предпочтения
- Запись успешно удалена
- Попытка удаления записи
- Вы уверены, что хотите удалить запись
Удалить запись
Страница успешно удалена
Вы уверены, что хотите удалить страницу
Попытка удаления страницы
Удалить страницу
+ Запись успешно удалена
+ Попытка удаления записи
+ Вы уверены, что хотите удалить запись
Во время установки захваченного медиа произошла ошибка
+ Вибрация
Уведомление мигающей лампочкой
Уведомление звуком
- Вибрация
Данные о местонахождении недоступны. Пожалуйста, подождите.
Состояние
- Для загрузки изображений требуется подключённая SD-карта.
- Местонахождение
Сделайте новое видео
- Требуется SD-карта
Выберите видео из галереи
+ Требуется SD-карта
+ Для загрузки изображений требуется подключённая SD-карта.
+ Местонахождение
Просмотреть страницу
Просмотреть запись
- Настройки учётной записи
Медиа
- Не найдена учётная запись WordPress. Пожалуйста, добавьте её и попробуйте снова.
+ Настройки учётной записи
Выберите блог
+ Не найдена учётная запись WordPress. Пожалуйста, добавьте её и попробуйте снова.
Пожалуйста, проверьте свои настройки и попробуйте снова.
Рубрика успешно добавлена.
Необходимо указать название рубрики.
@@ -188,74 +234,72 @@
Одобрить
Отклонить
Обязательное поле
- Добавить новую рубрику
Пытаемся добавить рубрику
Добавление рубрики
Добавление рубрики не удалось.
Пусто
Родительская рубрика (необязательно):
+ Добавить новую рубрику
Обязательные поля
Выберите рубрику
- Блог успешно удалён
+ Блоги
Удалить блог
Вы уверены, что хотите удалить этот блог?
- Блоги
Не удалось удалить блог.
+ Блог успешно удалён
Выберите фото из галереи
Сделать новое фото
+ Спам
+ Нет
+ Отмена
Ошибка
+ Сохранить
Выбранные рубрики:
У вас нет блога?
- Получите бесплатно на WordPress.com
- Отмена
- Сохранить
Добавить
- Редактировать страницу
- Редактировать запись
+ Ошибка соединения
Обновить рубрики
Пытаемся обновить рубрики
- Ошибка соединения
- Произошла ошибка соединения. Пожалуйста попробуйте позже.
Рубрики обновлены
- Ошибка обновления рубрик
Отменить правку
- Вы уверены, что хотите отказаться от изменения записи?
- Новая учётная запись
+ Новая запись
+ Отправить ответ
Комментарии
+ Отменить черновик
+ на
Записи
+ Модерация комментариев
+ Удалить черновик
+ Да
+ Опубликовать?
Страницы
- Вы уверены, что хотите отказаться от изменения страницы?
Новая страница
Локальные черновики
- Новая запись
Введите ответ:
- Отправить ответ
Ответить на комментарий
- Требуемое поле
Предварительный просмотр
- Загрузка...
- на
- Одобрено
- Отклонено
- Спам
- Модерация комментариев
Ответ на комментарий
Комментарий успешно промодерирован
Ответ успешно добавлен
Действия с черновиком
Редактировать черновик
- Удалить черновик
Вы уверены что вы хотите удалить черновик
- Действия со страницей
- Действия над записью
Действия над комментарием
+ Ответ
+ Получите бесплатно на WordPress.com
Одобрить
+ Редактировать страницу
+ Редактировать запись
+ Вы уверены, что хотите отказаться от изменения записи?
+ Вы уверены, что хотите отказаться от изменения страницы?
Отклонить
- Ответ
+ Отклонено
+ Одобрено
+ Действия со страницей
+ Действия над записью
+ Ошибка обновления рубрик
+ Новая учётная запись
Настройка учётной записи
- Пытаемся настроить учётную запись
- Отменить черновик
- Опубликовать?
- Да
- Нет
+ Произошла ошибка соединения. Пожалуйста, повторите попытку позже.
+ Пожалуйста, введите что-нибудь в поле ответа.
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 44b7ed24f1ec..3b5ecdc413f1 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -1,6 +1,48 @@
- Besök webbplats
+ Logga in
+ Logga ut
+ Bekräfta radering
+ Är du säker på att du vill radera denna kommentar?
+ Loggar in...
+ Logga in
+ Notifikationer
+ Hantera notifikationer
+ Aktivera notifikationer
+ Typer
+ Inställningar
+ Är du säker på att du vill logga ut från WordPress.com?
+ Är du säker på att du vill logga ut?
+ Vänligen logga in igen.
+ WordPress.com
+ Stäng av
+ Stäng av i 1 timma
+ Stäng av i 8 timmar
+ Knacka för att svara
+ Publicerar svar
+ Svar publicerat
+ Följ
+ Sluta följ
+ %d nya notifikationer
+ och %d mer.
+ Knacka för att visa.
+ Moderera kommentar
+ Följ eller sluta följa denna blogg
+ Beklagar, ett fel uppstod när bloggen skulle följas. Vänligen försök igen senare.
+ Skicka Kommentar
+ Ange kommentar:
+ Kommentaren har lagts till
+ Lägg till kommentar
+ Skriv kommentar
+ Obligatoriskt fält
+ Vänligen skriv något i kommentarfältet.
+ Försöker skicka kommentar
+ Visa Adminpanel
+ Redigera kommentar
+ Dela sida
+ Vi är hemskt ledsna, men ett fel inträffade när appens databas skulle skapas. Vänligen försök ominstallera appen.
+ Laddar...
+ Visa webbplats
Licenser för öppen källkod
Inläggssignatur
Lägg till en signatur till nya inlägg
@@ -8,15 +50,14 @@
HTTP-lösenord
HTTP-uppgifter (valfritt)
Aktivera bloggar
- Uppdateringsintervall
Börja blogga på ett par sekunder.
Kunde inte skapa temporär fil för uppladdning. Vänligen kontrollera så att det finns tillräckligt med utrymme på din enhet.
Ett fel uppstod vid uppladdning av media
Dela länk
- Innehåll (knacka för att lägga till text och media)
Publicera
Inläggsformat
Lägg till media
+ Innehåll (knacka för att lägga till text och media)
Felaktigt användarnamn eller lösenord.
Beklagar, ett fel uppstod vid försök att radera %s.
Beklagar, ett fel uppstod. Försök igen senare.
@@ -31,11 +72,10 @@
Kategorinamn
Permalänk för kategori (valfritt)
Beskrivning för kategori (valfritt)
- Logga in
URL för blogg
Läsare
- Enheten har slut på minne.
Kunde inte hitta mediafilen för uppladdning. Har den raderats eller flyttats?
+ Enheten har slut på minne.
Använd som utvald bild
Inkludera bild i inlägget
Inga %s-bloggar kunde hittas för det kontot.
@@ -46,34 +86,34 @@
Inget nätverk tillgängligt
Inget nätverk tillgängligt. Vänligen anslut till ett nätverk och försök igen.
Beklagar, %s kunde inte uppdateras för tillfället. Vänligen försök igen senare.
- Beklagar. Ett problem uppstod vi anslutning till din statistik. Försök igen.
Sidor
Bildtext (valfritt)
- Beklagar, kunde inte ansluta till WordPress-siten. Vänligen försök igen senare.
Bredd
Inlägg
Anonym
Inga bloggar funna
Sida
- Felaktig inloggning eller så finns det ingen blogg länkat till detta konto på WordPress.com.
- Horisontal justering
Inlägg
- Postat med WordPress for Blackberry
+ Horisontal justering
+ Felaktig inloggning eller så finns det ingen blogg länkat till detta konto på WordPress.com.
+ Beklagar. Ett problem uppstod vi anslutning till din statistik. Försök igen.
+ Beklagar, kunde inte ansluta till WordPress-installationen. Vänligen försök igen senare.
användarnamn
Kolumn 1
Kolumn 2
WordPress-blogg
Bildinställningar
- WordPress for Blackberry
- Peka för att lägga till text och media
OK
Är du säker på att du vill avbryta redigeringen av denna kommentar?
+ Peka för att lägga till text och media
+ WordPress for Blackberry
+ Postat med WordPress for Blackberry
Kunde inte ansluta. Ange fullständig sökväg till xmlrpc.php för din webbplats och försök igen.
Du har gjort ändringar i ett uppladdat inlägg som inte sparats än. Fortsätt uppdatera och skriv över lokala ändringar?
Lokala ändringar
+ Välj bredd för bild
Ladda upp och länka till skalad bild
Ange ett giltigt värde för skalad bildbredd
- Välj bredd för bild
Kommentarsinnehåll
Namn
E-post
@@ -96,7 +136,6 @@
Länktext (frivilligt)
URL
Postat med WordPress för Android
- Kommentarsnotifikationer
Skräppost
Laddar upp
Laddar upp mediaobjekt #
@@ -117,15 +156,14 @@
Snabbvideo
Beklagar, mediafilen kunde inte hämtas från galleriet.
Uppladdningen misslyckades
- Kontrollera att bloggadressen som angivits är giltig.
Visa i webbläsaren
- Ange något i svarsfältet.
- Utan titel
- Var vänlig ange något att publicera eller bifoga en mediafil.
+ Kontrollera att bloggadressen som angivits är giltig.
Inget innehåll hittades
+ Var vänlig ange något att publicera eller bifoga en mediafil.
+ Utan titel
Redigera
- Inlägg
Sida
+ Inlägg
Lösenord (valfritt)
Omedelbart
Ange genvägsnamn
@@ -135,12 +173,12 @@
Inläggsstatus är inte publicerad
Sidstatus är inte publicerad
Försöker att hämta URL
- Välj blogg för QuickPress-genväg
Genvägsnamn kan inte vara tomt
+ Välj blogg för QuickPress-genväg
Publicera
- Väntar på granskning
Utkast
Privat
+ Väntar på granskning
Ladda upp och länka till originalbild
Ogiltig bloggurl
Lägg till en blogg på WordPress.com
@@ -164,26 +202,26 @@
Kommentarer modererade
Inställningar
Inlägg raderat
- Försöker radera inlägg
Är du säker på att du vill ta bort inlägget
Radera inlägg
Sida raderad
Är du säker på att du vill radera sidan
- Försöker radera sida
Radera sida
+ Försöker radera inlägg
+ Försöker radera sida
Ett fel uppstod vid inrättandet av skapad media.
- Blinka notifikationslampa
Spela notifikationsljud
+ Blinka notifikationslampa
Vibrera
Lokaliseringsuppgifter ännu inte tillgänglig, var god vänta.
Status
Ett monterat SD-kort krävs för att ladda upp media.
- Plats
Spela in en ny video
SD-kort krävs
Välj en video från galleriet
- Visa sida
+ Plats
Visa inlägg
+ Visa sida
Detaljer för konto
Media
Inget WordPresskonto hittades, lägg till ett konto och försöka igen.
@@ -204,66 +242,64 @@
Moderkategori (valfritt):
Obligatoriska fält
Välj kategorier
- Bloggen har tagits bort
- Ta bort blogg
- Är du säker på att du vill ta bort den här bloggen?
Bloggar
+ Är du säker på att du vill ta bort den här bloggen?
+ Ta bort blogg
+ Bloggen har tagits bort
Kunde inte ta bort bloggen.
Välj en bild från galleri
Ta ett nytt foto
- Fel
- Valda kategorier
- Har du ingen blogg än?
- Skaffa en gratis blogg på WordPress.com
+ Ett problem med anslutningen har uppstått. Var vänlig försök senare.
+ Avsluta utkast
+ Kommentarer
+ Skicka svar
+ Svara på kommentar
+ Var vänlig skriv något i svarsfältet.
+ Ange svar:
+ Åtgärder för kommentarer
+ Svara
+ Kommentar modererad
+ Svaret har lagts till
+ Inlägg
+ Uppdaterar kategorier
+ Anslutningsfel
+ Är du säker på att du vill avbryta redigeringen av det här inlägget?
Avbryt
Spara
Lägg till
- Redigera sida
- Redigera inlägg
- Uppdaterar kategorier
- Försöker uppdatera kategorier
- Anslutningsfel
- Ett problem med anslutningen har uppstått. Var vänlig försök senare.
Kategorier uppdaterade
Fel vid uppdatering av kategorier
Avbryt redigering
- Är du säker på att du vill avbryta redigeringen av det här inlägget?
Nytt konto
- Kommentarer
- Inlägg
Sidor
- Är du säker på att du vill avbryta redigeringen av denna sida?
Ny sida
- Lokala utkast
Nytt inlägg
- Ange svar:
- Skicka svar
- Svara på kommentar
- Obligatoriskt fält
- Förhandsgranska
- Laddar...
- den
Godkända
Icke godkända
Skräppost
- Granskar kommentar
- Svara på kommentar
- Kommentar modererad
- Svaret har lagts till
- Åtgärder för utkast
Redigera utkast
Ta bort utkast
Är du säker på att du vill radera utkastet
- Åtgärder för sida
- Åtgärder för inlägg
- Åtgärder för kommentarer
- Markera som godkänd
- Markera som väntande
- Svara
- Kontoinställningar
- Försöker att konfigurera konto
- Avsluta utkast
- Publicera?
+ Valda kategorier
Ja
Nej
+ Publicera?
+ Fel
+ Skaffa en gratis blogg på WordPress.com
+ Redigera sida
+ Försöker uppdatera kategorier
+ Är du säker på att du vill avbryta redigeringen av denna sida?
+ Granskar kommentar
+ Svara på kommentar
+ Markera som godkänd
+ Kontoinställningar
+ den
+ Har du ingen blogg än?
+ Redigera inlägg
+ Åtgärder för inlägg
+ Åtgärder för sida
+ Åtgärder för utkast
+ Markera som väntande
+ Lokala utkast
+ Förhandsgranska
diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml
index 3463320d5300..73d0a1c6e287 100644
--- a/res/values-v14/styles.xml
+++ b/res/values-v14/styles.xml
@@ -26,7 +26,6 @@
- @style/wordpress_solid_ActionBar
- @style/wordpress_solid_ActionBar
- - @drawable/menu_hardkey_panel_wordpress
- @style/Theme.wordpress.widget
- @style/Theme.wordpress.widget
diff --git a/res/values-zh/strings.xml b/res/values-zh/strings.xml
index b4f146fb7def..e9a1dbf61e3d 100644
--- a/res/values-zh/strings.xml
+++ b/res/values-zh/strings.xml
@@ -1,7 +1,55 @@
+ 关闭
+ 关闭 1 小时
+ 关闭 8 小时
+ 轻按可回复
+ 正在发表回复
+ 回复已发表
+ 关注
+ 取消关注
+ %d 条新通知
+ 以及其他 %d 人。
+ 轻按可查看。
+ 管理评论
+ 关注或取消关注该博客
+ 抱歉,尝试关注该博客时发生了一个错误。请稍候再试。
+ 你确定要从 WordPress.com 退出登录吗?
+ 你确定要退出吗?
+ 请再次登录。
+ WordPress.com
+ 登录
+ 退出
+ 确认删除
+ 是否确定要删除该评论?
+ 正在登录…
+ 登录
+ 通知
+ 管理通知
+ 启用通知
+ 类型
+ 选项
+ 发送评论
+ 输入评论:
+ 评论已成功添加
+ 添加评论
+ 撰写评论
+ 必填字段
+ 请在评论字段输入一些内容。
+ 正在尝试发表评论
+ 查看管理
+ 编辑评论
+ 分享页面
+ 非常抱歉,尝试创建应用数据库时发生错误。请尝试重新安装应用。
+ 正在加载…
+ 查看站点
+ 开源许可协议
+ 文章签名
+ 为新发表的博文添加签名
+ HTTP 用户名
+ HTTP 密码
+ HTTP 登录凭据 (可选)
启用博客
- 更新间隔
数秒内即可撰写博客。
无法创建上传媒体所需的临时文件。请确保设备有足够的空闲空间。
上传媒体时出错
@@ -24,7 +72,6 @@
分类名称
分类代号 (可选)
分类描述 (可选)
- 登录
博客 URL
阅读器
设备内存已满。
@@ -89,7 +136,6 @@
链接文字 (可选)
URL
发自 WordPress for Android
- 评论通知
垃圾
正在上传
正在上传媒体项 #
@@ -112,7 +158,6 @@
上传失败
请检查所输博客 URL 是否有效。
在浏览器中查看
- 请在回复字段中输入一些内容。
无标题
未找到内容
请输入一些博文内容或附加媒体对象。
@@ -229,8 +274,6 @@
输入回复:
发送回复
回复评论
- 必填字段
- 正在加载...
于
已通过
未通过
@@ -250,7 +293,6 @@
标记未通过
回复
账号配置
- 正在尝试配置账号
取消草稿
发布?
是
@@ -259,4 +301,5 @@
是否确定要取消编辑该博文?
是否确定要取消编辑该页面?
预览
+ 请在回复字段输入一些内容。
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
deleted file mode 100644
index 5dfe6443ec1a..000000000000
--- a/res/values/arrays.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- - 5 Minutes
- - 10 Minutes
- - 15 Minutes
- - 30 Minutes
- - 1 Hour
- - 3 Hours
- - 12 Hours
- - Daily
-
-
-
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index bbdab8e99bdf..50740fb30050 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -4,11 +4,15 @@
#CCCCCC
#FFFFFF
#CC2EA2CC
- #F7F7F7
+ #FFFFFF
#78c8e6
#CC2EA2CC
#DDFFFFFF
#44000000
+
+ #21759B
+ #E5E5E5
+ #CCC
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 42dcb36d41f6..abc69596e87f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -15,6 +15,9 @@
Enable blogs
No network available
There is no network available. Please connect to a network and try again.
+ Are you sure you want to sign out from WordPress.com?
+ Are you sure you want to sign out?
+ Please sign in again.
Select Categories
@@ -88,7 +91,10 @@
More
Sorry, an error occurred when attempting to access this blog.
Sorry, an error occurred when attempting to load the post. Please refresh your posts and try again.
-
+ WordPress.com
+ Sign in
+ Sign out
+
Comments
Posts
@@ -215,19 +221,17 @@
Delete
Approve
Unapprove
+ Confirm Deletion
+ Are you sure you want to delete this comment?
Account Setup
- Logging in…
+ Signing in…
Sorry, could not connect to the WordPress site. Please try again later.
Cancel Draft
-
- Comment Notifications
- Update interval
-
Select a photo from gallery
Take a new photo
@@ -318,7 +322,30 @@
Publish
Edit
Post Format
- Log In
+ Sign In
+
+
+ Notifications
+ Manage notifications
+ Enable notifications
+ Types
+ Options
+ Turn off
+ Turn off for 1 hour
+ Turn off for 8 hours
+ Tap to Reply
+ Publishing reply
+ Reply published
+ Follow
+ Unfollow
+ %d new notifications
+ and %d more.
+ Tap to view.
+ Moderate comment
+ Follow or unfollow this blog
+ Reply failed. Tap to retry.
+ Reply failed.
+ Tap to retry.
Reader
@@ -382,8 +409,10 @@
Sorry, an error occurred while attempting to delete the %s.
Sorry, %s could not be refreshed at this time. Please try again later.
Sorry, an error occurred. Please try again later.
+ Sorry, an error occurred while fetching data. Please try again later.
Sorry, an error occurred while attempting to moderate. Please try again later.
Sorry, an error occurred while editing the comment. Please try again later.
+ Sorry, an error occurred while attempting to follow the blog. Please try again later.
Sorry, an error occurred while uploading the %s.
An error occurred while uploading media
You have exceeded the login limit. Please ensure your login credentials are correct and try again in a few minutes.
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index dc68b6cb1fe8..06d8d3446856 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -1,37 +1,47 @@
-
+
-
+
+ android:title="@string/wpcom"
+ android:key="wp_pref_wpcom_auth" >
+
+
+ android:title="@string/manage_notifications">
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/src/com/commonsware/cwac/cache/AsyncCache.java b/src/com/commonsware/cwac/cache/AsyncCache.java
deleted file mode 100644
index 65a93fb52ac4..000000000000
--- a/src/com/commonsware/cwac/cache/AsyncCache.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.commonsware.cwac.cache;
-
-import com.commonsware.cwac.bus.AbstractBus;
-
-import java.io.File;
-
-abstract public class AsyncCache, M>
- extends CacheBase {
- protected abstract V create(K key, M message, int forceStyle);
-
- private B bus=null;
-
- public AsyncCache(File cacheRoot, B bus, DiskCachePolicy policy,
- int maxSize) {
- super(cacheRoot, policy, maxSize);
- this.bus=bus;
- }
-
- public V get(K key, M message) {
- V result=super.get(key);
-
- if (result==null) {
- result=create(key, message, FORCE_NONE);
- super.put(key, result);
- }
-
- return(result);
- }
-
- public void forceLoad(K key, M message, int forceStyle) {
- super.remove(key);
- super.put(key, create(key, message, forceStyle));
- }
-
- protected B getBus() {
- return(bus);
- }
-}
\ No newline at end of file
diff --git a/src/com/commonsware/cwac/cache/CacheBase.java b/src/com/commonsware/cwac/cache/CacheBase.java
deleted file mode 100644
index 34be0596a701..000000000000
--- a/src/com/commonsware/cwac/cache/CacheBase.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.commonsware.cwac.cache;
-
-import com.commonsware.cwac.task.AsyncTaskEx;
-
-import android.util.Log;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-abstract public class CacheBase {
- public static final int CACHE_MEMORY=1;
- public static final int CACHE_DISK=2;
- public static final int CACHE_NONE=3;
- public static final int FORCE_NONE=1;
- public static final int FORCE_SOFT=2;
- public static final int FORCE_HARD=3;
- private static String TAG="CacheBase";
- private File cacheRoot=null;
- private int maxSize=0;
- private Map cache=Collections.synchronizedMap(new LinkedHashMap(101, .75F, true) {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
-
- @SuppressWarnings("rawtypes")
- protected boolean removeEldestEntry(Map.Entry eldest) {
- return(size()>maxSize);
- }
- });
-
- public CacheBase(File cacheRoot, DiskCachePolicy policy,
- int maxSize) {
- this.cacheRoot=cacheRoot;
- this.maxSize=maxSize;
-
- if (cacheRoot!=null) {
- new CacheCleanTask().execute(policy);
- }
- }
-
- public V get(K key) {
- return(cache.get(key));
- }
-
- public int getStatus(K key) {
- if (cache.containsKey(key)) {
- return(CACHE_MEMORY);
- }
-
- return(CACHE_NONE);
- }
-
- protected void put(K key, V value) {
- cache.put(key, value);
- }
-
- public void remove(K key) {
- cache.remove(key);
- }
-
- protected File getCacheRoot() {
- return(cacheRoot);
- }
-
- public interface DiskCachePolicy {
- boolean eject(File cachedFile);
- }
-
- class CacheCleanTask extends AsyncTaskEx {
- @Override
- protected Void doInBackground(DiskCachePolicy... policies) {
- try {
- walkDir(cacheRoot, policies[0]);
- }
- catch (Throwable t) {
- Log.e(TAG, "Exception cleaning cache", t);
- }
-
- return(null);
- }
-
- void walkDir(File dir, DiskCachePolicy policy) {
- if (dir.isDirectory()) {
- String[] children=dir.list();
-
- for (int i=0; i
- extends CacheBase {
- //private static final String TAG="SimpleWebImageCache";
- private B bus=null;
-
- static public File buildCachedImagePath(File cacheRoot, String url)
- throws Exception {
- return(new File(cacheRoot, md5(url)));
- }
-
- static protected String md5(String s) throws Exception {
- MessageDigest md=MessageDigest.getInstance("MD5");
-
- md.update(s.getBytes());
-
- byte digest[]=md.digest();
- StringBuffer result=new StringBuffer();
-
- for (int i=0; i {
- @SuppressWarnings("unchecked")
- @Override
- protected Void doInBackground(Object... params) {
- String url=params[1].toString();
- File cache=(File)params[2];
-
- try {
- URLConnection connection=new URL(url).openConnection();
- InputStream stream=connection.getInputStream();
- BufferedInputStream in=new BufferedInputStream(stream);
- ByteArrayOutputStream out=new ByteArrayOutputStream(10240);
- int read;
- byte[] b=new byte[4096];
-
- while ((read = in.read(b)) != -1) {
- out.write(b, 0, read);
- }
-
- out.flush();
- out.close();
-
- byte[] raw=out.toByteArray();
-
- put(url, new BitmapDrawable(new ByteArrayInputStream(raw)));
-
- M message=(M)params[0];
-
- if (message!=null) {
- bus.send(message);
- }
-
- if (cache!=null) {
- FileOutputStream file=new FileOutputStream(cache);
-
- file.write(raw);
- file.flush();
- file.close();
- }
- }
- catch (Throwable t) {
- //Log.e(TAG, "Exception downloading image", t);
- }
-
- return(null);
- }
- }
-
- class LoadImageTask extends AsyncTaskEx {
- @SuppressWarnings("unchecked")
- @Override
- protected Void doInBackground(Object... params) {
- String url=params[1].toString();
- File cache=(File)params[2];
-
- try {
- put(url, new BitmapDrawable(cache.getAbsolutePath()));
-
-// M message=(M)params[0];
-
- if (params[0]!=null) {
- bus.send(params[0]);
- }
- }
- catch (Throwable t) {
- //Log.e(TAG, "Exception downloading image", t);
- }
-
- return(null);
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/commonsware/cwac/cache/WebImageCache.java b/src/com/commonsware/cwac/cache/WebImageCache.java
deleted file mode 100644
index df01b812daf7..000000000000
--- a/src/com/commonsware/cwac/cache/WebImageCache.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.commonsware.cwac.cache;
-
-import com.commonsware.cwac.bus.SimpleBus;
-import com.commonsware.cwac.task.AsyncTaskEx;
-
-import android.app.Activity;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.ImageView;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.security.MessageDigest;
-
-public class WebImageCache
- extends AsyncCache {
- private static String TAG="WebImageCache";
- private Drawable placeholder=null;
-
- static public File buildCachedImagePath(File cacheRoot, String url)
- throws Exception {
- return(new File(cacheRoot, md5(url)));
- }
-
- static protected String md5(String s) throws Exception {
- MessageDigest md=MessageDigest.getInstance("MD5");
-
- md.update(s.getBytes());
-
- byte digest[]=md.digest();
- StringBuffer result=new StringBuffer();
-
- for (int i=0; i() {
- public void onReceive(final Bundle message) {
- ((Activity)image.getContext()).runOnUiThread(new Runnable() {
- public void run() {
- if (url.equals(message.getString(SimpleBus.KEY))) {
- image.setImageDrawable(get(url, null));
- }
- else {
- }
- }
- });
- }
- };
-
- image.setTag(r);
- getBus().register(url, r, tag);
- }
- }
-
- @Override
- public int getStatus(String key) {
- int result=super.getStatus(key);
-
- if (result==CACHE_NONE && getCacheRoot()!=null) {
- try {
- File cache=buildCachedImagePath(key);
-
- if (cache.exists()) {
- result=CACHE_DISK;
- }
- }
- catch (Throwable t) {
- Log.e(TAG, "Exception getting cache status", t);
- }
- }
-
- return(result);
- }
-
- protected Drawable create(String key, Bundle message,
- int forceStyle) {
- if (getCacheRoot()!=null) {
- try {
- File cache=buildCachedImagePath(key);
-
- if (cache.exists() && forceStyle==FORCE_NONE) {
- return(new BitmapDrawable(cache.getAbsolutePath()));
- }
- else {
- new FetchImageTask().execute(message, key, cache);
- }
- }
- catch (Throwable t) {
- Log.e(TAG, "Exception loading image", t);
- }
- }
- else {
- new FetchImageTask().execute(message, key, null);
- }
-
- return(placeholder);
- }
-
- public File buildCachedImagePath(String url)
- throws Exception {
- return(buildCachedImagePath(getCacheRoot(), url));
- }
-
- class FetchImageTask
- extends AsyncTaskEx {
- @Override
- protected Void doInBackground(Object... params) {
- String url=params[1].toString();
- File cache=(File)params[2];
-
- try {
- URLConnection connection=new URL(url).openConnection();
- InputStream stream=connection.getInputStream();
- BufferedInputStream in=new BufferedInputStream(stream);
- ByteArrayOutputStream out=new ByteArrayOutputStream(10240);
- int read;
- byte[] b=new byte[4096];
-
- while ((read = in.read(b)) != -1) {
- out.write(b, 0, read);
- }
-
- out.flush();
- out.close();
-
- byte[] raw=out.toByteArray();
-
- WebImageCache.this.put(url, new BitmapDrawable(new ByteArrayInputStream(raw)));
-
- Bundle message=(Bundle)params[0];
-
- if (message!=null) {
- getBus().send(message);
- }
-
- if (cache!=null) {
- FileOutputStream file=new FileOutputStream(cache);
-
- file.write(raw);
- file.flush();
- file.close();
- }
- }
- catch (Throwable t) {
- Log.e(TAG, "Exception downloading image", t);
- }
-
- return(null);
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/commonsware/cwac/thumbnail/ThumbnailActivity.java b/src/com/commonsware/cwac/thumbnail/ThumbnailActivity.java
deleted file mode 100644
index 25f4223afe99..000000000000
--- a/src/com/commonsware/cwac/thumbnail/ThumbnailActivity.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright (c) 2008-09 -- CommonsWare, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.commonsware.cwac.thumbnail;
-
-import com.commonsware.cwac.cache.SimpleWebImageCache;
-
-import android.app.ListActivity;
-import android.widget.ListAdapter;
-
-abstract public class ThumbnailActivity extends ListActivity {
- abstract protected int[] getImageIdArray();
-
- private ThumbnailBus bus=new ThumbnailBus();
- private SimpleWebImageCache cache=
- new SimpleWebImageCache(null, null, 101, bus);
-
- @Override
- public void setListAdapter(ListAdapter adapter) {
- super.setListAdapter(new ThumbnailAdapter(this,
- adapter,
- cache,
- getImageIdArray()));
- }
-}
diff --git a/src/com/commonsware/cwac/thumbnail/ThumbnailAdapter.java b/src/com/commonsware/cwac/thumbnail/ThumbnailAdapter.java
deleted file mode 100644
index 6080278cc49a..000000000000
--- a/src/com/commonsware/cwac/thumbnail/ThumbnailAdapter.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.commonsware.cwac.thumbnail;
-
-import com.commonsware.cwac.adapter.AdapterWrapper;
-import com.commonsware.cwac.cache.SimpleWebImageCache;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Bitmap.Config;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.BitmapDrawable;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-
-public class ThumbnailAdapter extends AdapterWrapper {
- private static final String TAG="ThumbnailAdapter";
- private int[] imageIds;
- private SimpleWebImageCache cache=null;
- private Activity host=null;
- Bitmap bdRounded;
-
- /**
- * Constructor wrapping a supplied ListAdapter
- */
- public ThumbnailAdapter(Activity host,
- ListAdapter wrapped,
- SimpleWebImageCache cache,
- int[] imageIds) {
- super(wrapped);
-
- this.host=host;
- this.imageIds=imageIds;
- this.cache=cache;
-
- cache.getBus().register(getBusKey(), onCache);
- }
-
- public void close() {
- cache.getBus().unregister(onCache);
- }
-
- /**
- * Get a View that displays the data at the specified
- * position in the data set. In this case, if we are at
- * the end of the list and we are still in append mode,
- * we ask for a pending view and return it, plus kick
- * off the background task to append more data to the
- * wrapped adapter.
- * @param position Position of the item whose data we want
- * @param convertView View to recycle, if not null
- * @param parent ViewGroup containing the returned View
- */
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- View result=super.getView(position, convertView, parent);
-
- processView(result);
-
- return(result);
- }
-
- public void processView(View row) {
- for (int imageId : imageIds) {
- ImageView image=(ImageView)row.findViewById(imageId);
-
- if (image!=null && image.getTag()!=null) {
- ThumbnailMessage msg=cache
- .getBus()
- .createMessage(getBusKey());
-
- msg.setImageView(image);
- msg.setUrl(image.getTag().toString());
-
- try {
- cache.notify(msg.getUrl(), msg);
- }
- catch (Throwable t) {
- Log.e(TAG, "Exception trying to fetch image", t);
- }
- }
- }
- }
-
- private String getBusKey() {
- return(toString());
- }
-
- private ThumbnailBus.Receiver onCache=
- new ThumbnailBus.Receiver() {
- public void onReceive(final ThumbnailMessage message) {
- final ImageView image=message.getImageView();
-
- host.runOnUiThread(new Runnable() {
- public void run() {
- if (image.getTag()!=null &&
- image.getTag().toString().equals(message.getUrl())) {
- image.setImageDrawable(cache.get(message.getUrl()));
- }
- }
- });
- }
- };
-
- public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
-
- if (bitmap != null)
- {
- Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
-
- final int color = 0xff424242;
- final Paint paint = new Paint();
- final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
- final RectF rectF = new RectF(rect);
- final float roundPx = 4;
-
- paint.setAntiAlias(true);
- canvas.drawARGB(0, 0, 0, 0);
- paint.setColor(color);
- canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
-
- paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
- canvas.drawBitmap(bitmap, rect, rect, paint);
-
- return output;
- }
- else
- {
- return bitmap;
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/commonsware/cwac/thumbnail/ThumbnailBus.java b/src/com/commonsware/cwac/thumbnail/ThumbnailBus.java
deleted file mode 100644
index fce2598bda76..000000000000
--- a/src/com/commonsware/cwac/thumbnail/ThumbnailBus.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.commonsware.cwac.thumbnail;
-
-import com.commonsware.cwac.bus.AbstractBus;
-
-public class ThumbnailBus
- extends AbstractBus {
-
- public ThumbnailBus() {
- super();
-
- setStrategy(new MatchStrategy());
- }
-
- public ThumbnailMessage createMessage(String key) {
- return(new ThumbnailMessage(key));
- }
-
- class MatchStrategy
- implements AbstractBus.Strategy {
- public boolean isMatch(ThumbnailMessage message, String filter) {
- return(filter!=null && message!=null &&
- filter.equals(message.getKey()));
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/commonsware/cwac/thumbnail/ThumbnailMessage.java b/src/com/commonsware/cwac/thumbnail/ThumbnailMessage.java
deleted file mode 100644
index 8907dce317be..000000000000
--- a/src/com/commonsware/cwac/thumbnail/ThumbnailMessage.java
+++ /dev/null
@@ -1,47 +0,0 @@
- /***
- Copyright (c) 2008-2009 CommonsWare, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.commonsware.cwac.thumbnail;
-
-import android.widget.ImageView;
-
-public class ThumbnailMessage {
- private String key;
- private ImageView image;
- private String url;
-
- public ThumbnailMessage(String key) {
- this.key=key;
- }
-
- public String getKey() {
- return(key);
- }
-
- public ImageView getImageView() {
- return(image);
- }
-
- public void setImageView(ImageView image) {
- this.image=image;
- }
-
- public String getUrl() {
- return(url);
- }
-
- public void setUrl(String url) {
- this.url=url;
- }
-}
\ No newline at end of file
diff --git a/src/org/wordpress/android/CommentBroadcastReceiver.java b/src/org/wordpress/android/CommentBroadcastReceiver.java
deleted file mode 100644
index 31ebae1f0eb5..000000000000
--- a/src/org/wordpress/android/CommentBroadcastReceiver.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.wordpress.android;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-import org.wordpress.android.util.CommentService;
-
-public class CommentBroadcastReceiver extends BroadcastReceiver {
-
- public void onReceive(Context context, Intent intent) {
- context.stopService(new Intent(context, CommentService.class));
- context.startService(new Intent(context, CommentService.class));
- }
-
- public void onCancel(Context context, Intent intent) {
- context.stopService(new Intent(context, CommentService.class));
- }
-}
diff --git a/src/org/wordpress/android/Constants.java b/src/org/wordpress/android/Constants.java
index fba02ab0c997..a0d545f7fce1 100644
--- a/src/org/wordpress/android/Constants.java
+++ b/src/org/wordpress/android/Constants.java
@@ -11,6 +11,9 @@ public class Constants {
public static String readerTopicsURL = "http://en.wordpress.com/reader/mobile/v2/?template=topics";
public static String readerDetailURL = "https://en.wordpress.com/wp-admin/admin-ajax.php?action=wpcom_load_mobile&template=details&v=2";
+ public static String wpcomXMLRPCURL = "https://wordpress.com/xmlrpc.php";
+ public static String wpcomLoginURL = "https://wordpress.com/wp-login.php";
+
public static int QUICK_POST_PHOTO_CAMERA = 0;
public static int QUICK_POST_PHOTO_LIBRARY = 1;
public static int QUICK_POST_VIDEO_CAMERA = 2;
diff --git a/src/org/wordpress/android/GCMIntentService.java b/src/org/wordpress/android/GCMIntentService.java
new file mode 100644
index 000000000000..f3dd3b2686d2
--- /dev/null
+++ b/src/org/wordpress/android/GCMIntentService.java
@@ -0,0 +1,214 @@
+
+package org.wordpress.android;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.IntentCompat;
+import android.util.Log;
+import android.graphics.BitmapFactory;
+
+import com.google.android.gcm.GCMBaseIntentService;
+
+import org.wordpress.android.ui.notifications.NotificationsActivity;
+import org.wordpress.android.ui.posts.PostsActivity;
+import org.wordpress.android.util.ImageHelper;
+import org.wordpress.android.util.StringUtils;
+
+public class GCMIntentService extends GCMBaseIntentService {
+
+ public static Map activeNotificationsMap = new HashMap();
+ private int notificationId = 1337;
+
+ @Override
+ protected void onError(Context context, String errorId) {
+ Log.v("WORDPRESS", "GCM Error: " + errorId);
+ }
+
+ @Override
+ protected void onMessage(Context context, Intent intent) {
+ Log.v("WORDPRESS", "Received Message");
+
+ Bundle extras = intent.getExtras();
+
+ if (extras == null) {
+ Log.v("WORDPRESS", "Hrm. No notification message content received. Aborting.");
+ return;
+ }
+
+ String title = extras.getString("title");
+ if (title == null)
+ title = "WordPress";
+ String message = extras.getString("msg");
+
+ String note_id = extras.getString("note_id");
+
+ if (note_id == null)
+ note_id = StringUtils.getMd5Hash(message);
+
+ if (note_id != null) {
+ if (!activeNotificationsMap.containsKey(note_id))
+ activeNotificationsMap.put(note_id, extras);
+ }
+
+ String iconURL = extras.getString("icon");
+ Bitmap largeIconBitmap = null;
+ if (iconURL != null) {
+ try {
+ iconURL = URLDecoder.decode(iconURL, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ float screenDensity = getResources().getDisplayMetrics().densityDpi;
+ int size = Math.round(64 * (screenDensity / 160));
+ String resizedURL = iconURL.replaceAll("(?<=[?&;])s=[0-9]*", "s=" + size);
+ largeIconBitmap = ImageHelper.downloadBitmap(resizedURL);
+ }
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ boolean sound, vibrate, light;
+
+ sound = prefs.getBoolean("wp_pref_notification_sound", false);
+ vibrate = prefs.getBoolean("wp_pref_notification_vibrate", false);
+ light = prefs.getBoolean("wp_pref_notification_light", false);
+
+ NotificationCompat.Builder mBuilder;
+
+ Intent resultIntent = new Intent(this, PostsActivity.class);
+ resultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
+ | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
+ resultIntent.setAction("android.intent.action.MAIN");
+ resultIntent.addCategory("android.intent.category.LAUNCHER");
+ resultIntent.putExtra(NotificationsActivity.FROM_NOTIFICATION_EXTRA, true);
+
+ if (activeNotificationsMap.size() <= 1) {
+ mBuilder =
+ new NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.notification_icon)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setTicker(message)
+ .setAutoCancel(true)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(message));
+
+ if (note_id != null)
+ resultIntent.putExtra(NotificationsActivity.NOTE_ID_EXTRA, note_id);
+
+ // Add some actions if this is a comment notification
+ String noteType = extras.getString("type");
+ if (noteType != null && noteType.equals("c")) {
+ Intent commentReplyIntent = new Intent(this, PostsActivity.class);
+ commentReplyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
+ | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
+ commentReplyIntent.setAction("android.intent.action.MAIN");
+ commentReplyIntent.addCategory("android.intent.category.LAUNCHER");
+ commentReplyIntent.addCategory("comment-reply");
+ commentReplyIntent.putExtra(NotificationsActivity.FROM_NOTIFICATION_EXTRA, true);
+ commentReplyIntent.putExtra(NotificationsActivity.NOTE_INSTANT_REPLY_EXTRA, true);
+ if (note_id != null)
+ commentReplyIntent.putExtra(NotificationsActivity.NOTE_ID_EXTRA, note_id);
+ PendingIntent commentReplyPendingIntent = PendingIntent.getActivity(context, 0,
+ commentReplyIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ mBuilder.addAction(R.drawable.ab_icon_reply,
+ getResources().getText(R.string.reply), commentReplyPendingIntent);
+ }
+
+ if (largeIconBitmap != null) {
+ mBuilder.setLargeIcon(largeIconBitmap);
+ }
+ } else {
+
+ NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
+
+ int noteCtr = 1;
+ for (Bundle wpNotification : activeNotificationsMap.values()) {
+ // InboxStyle notification is limited to 5 lines
+ if (noteCtr > 5)
+ break;
+ if (wpNotification.getString("msg") != null) {
+ if (wpNotification.getString("type").equals("c"))
+ inboxStyle.addLine(wpNotification.getString("title") + ": "
+ + wpNotification.getString("msg"));
+ else
+ inboxStyle.addLine(wpNotification.getString("msg"));
+ noteCtr++;
+ }
+
+ }
+
+ if (activeNotificationsMap.size() > 5)
+ inboxStyle.setSummaryText(String.format(getString(R.string.more_notifications),
+ activeNotificationsMap.size() - 5));
+
+ String subject = String.format(getString(R.string.new_notifications),
+ activeNotificationsMap.size());
+
+ mBuilder =
+ new NotificationCompat.Builder(this)
+ .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.notification_multi))
+ .setSmallIcon(R.drawable.notification_icon)
+ .setContentTitle("WordPress")
+ .setContentText(subject)
+ .setTicker(message)
+ .setAutoCancel(true)
+ .setStyle(inboxStyle);
+ }
+
+ if (sound)
+ mBuilder.setSound(Uri.parse("android.resource://" + getPackageName() + "/"
+ + R.raw.notification));
+ if (vibrate)
+ mBuilder.setVibrate(new long[] {
+ 500, 500, 500
+ });
+ if (light)
+ mBuilder.setLights(0xff0000ff, 1000, 5000);
+
+
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ mBuilder.setContentIntent(pendingIntent);
+ NotificationManager mNotificationManager =
+ (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotificationManager.notify(notificationId, mBuilder.build());
+
+ }
+
+ @Override
+ protected void onRegistered(Context context, String regId) {
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
+ if (regId != null && regId.length() > 0) {
+ // Get or create UUID for WP.com notes api
+ String uuid = settings.getString("wp_pref_notifications_uuid", null);
+ if (uuid == null) {
+ uuid = UUID.randomUUID().toString();
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putString("wp_pref_notifications_uuid", uuid);
+ editor.commit();
+ }
+
+ WordPress.registerWPComToken(context, regId);
+ }
+ }
+
+ @Override
+ protected void onUnregistered(Context context, String regId) {
+ Log.v("WORDPRESS", "GCM Unregistered ID: " + regId);
+ }
+
+}
diff --git a/src/org/wordpress/android/ServiceUpdateUIListener.java b/src/org/wordpress/android/ServiceUpdateUIListener.java
deleted file mode 100644
index 5a2f92d70344..000000000000
--- a/src/org/wordpress/android/ServiceUpdateUIListener.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.wordpress.android;
-
-public interface ServiceUpdateUIListener {
-
- public void updateUI(String accountID, String accountName);
-
- }//end interface ServiceUpdateUIListener
diff --git a/src/org/wordpress/android/WordPress.java b/src/org/wordpress/android/WordPress.java
index 72886d5a8124..2a9902dd006b 100644
--- a/src/org/wordpress/android/WordPress.java
+++ b/src/org/wordpress/android/WordPress.java
@@ -1,21 +1,59 @@
package org.wordpress.android;
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URI;
import java.util.List;
import java.util.Map;
import android.app.Application;
+import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.preference.PreferenceManager;
+import android.util.Log;
+
+import com.android.volley.RequestQueue;
+import com.android.volley.toolbox.ImageLoader;
+import com.android.volley.toolbox.Volley;
+import com.android.volley.Request;
+import com.android.volley.VolleyError;
+
+import com.google.android.gcm.GCMRegistrar;
+import com.wordpress.rest.Oauth;
+import com.wordpress.rest.RestRequest;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xmlrpc.android.WPComXMLRPCApi;
+import org.xmlrpc.android.XMLRPCCallback;
+import org.xmlrpc.android.XMLRPCClient;
+import org.xmlrpc.android.XMLRPCException;
import org.wordpress.android.models.Blog;
import org.wordpress.android.models.Comment;
import org.wordpress.android.models.Post;
+import org.wordpress.android.ui.WPActionBarActivity;
+import org.wordpress.android.util.BitmapLruCache;
+import org.wordpress.android.util.WPRestClient;
+import org.wordpress.android.Config;
public class WordPress extends Application {
+ public static final String NOTES_CACHE="notifications.json";
+ public static final String ACCESS_TOKEN_PREFERENCE="wp_pref_wpcom_access_token";
+ public static final String WPCOM_USERNAME_PREFERENCE="wp_pref_wpcom_username";
+ public static final String WPCOM_PASSWORD_PREFERENCE="wp_pref_wpcom_password";
+ private static final String APP_ID_PROPERTY="oauth.app_id";
+ private static final String APP_SECRET_PROPERTY="oauth.app_secret";
+ private static final String APP_REDIRECT_PROPERTY="oauth.redirect_uri";
+
public static String versionName;
public static Blog currentBlog;
public static Comment currentComment;
@@ -24,19 +62,110 @@ public class WordPress extends Application {
public static OnPostUploadedListener onPostUploadedListener = null;
public static boolean postsShouldRefresh;
public static boolean shouldRestoreSelectedActivity;
+ public static WPRestClient restClient;
+ public static RequestQueue requestQueue;
+ public static ImageLoader imageLoader;
+ public static JSONObject latestNotes;
+
+ public static final String TAG="WordPress";
@Override
public void onCreate() {
versionName = getVersionName();
wpDB = new WordPressDB(this);
+ // Volley networking setup
+ requestQueue = Volley.newRequestQueue(this);
+ int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ // Use a small slice of available memory for the image cache
+ int cacheSize = maxMemory / 32;
+ imageLoader = new ImageLoader(requestQueue, new BitmapLruCache(cacheSize));
+
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
if (settings.getInt("wp_pref_last_activity", -1) >= 0)
shouldRestoreSelectedActivity = true;
+
+ restClient = new WPRestClient(requestQueue, new OauthAuthenticator(), settings.getString(ACCESS_TOKEN_PREFERENCE, null));
+ registerForCloudMessaging(this);
+ loadNotifications(this);
super.onCreate();
}
+ public static void registerForCloudMessaging(Context ctx) {
+
+ if (WordPress.hasValidWPComCredentials(ctx)) {
+ String token = null;
+ try {
+ // Register for Google Cloud Messaging
+ GCMRegistrar.checkDevice(ctx);
+ GCMRegistrar.checkManifest(ctx);
+ token = GCMRegistrar.getRegistrationId(ctx);
+ String gcmId = Config.GCM_ID;
+ if (gcmId != null && token.equals("")) {
+ GCMRegistrar.register(ctx, gcmId);
+ } else {
+ // Send the token to WP.com in case it was invalidated
+ registerWPComToken(ctx, token);
+ Log.v("WORDPRESS", "Already registered for GCM");
+ }
+ } catch (Exception e) {
+ Log.v("WORDPRESS", "Could not register for GCM: " + e.getMessage());
+ }
+ }
+ }
+
+ public static void registerWPComToken(final Context ctx, String token) {
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(ctx);
+ String uuid = settings.getString("wp_pref_notifications_uuid", null);
+ if (uuid == null)
+ return;
+ Object[] params = {
+ settings.getString(WPCOM_USERNAME_PREFERENCE, ""),
+ WordPressDB.decryptPassword(settings.getString(WPCOM_PASSWORD_PREFERENCE, "")),
+ token,
+ uuid,
+ "android",
+ false
+ };
+
+ XMLRPCClient client = new XMLRPCClient(URI.create(Constants.wpcomXMLRPCURL), "", "");
+ client.callAsync(new XMLRPCCallback() {
+ public void onSuccess(long id, Object result) {
+ Log.v("WORDPRESS", "Successfully registered device on WP.com");
+ new WPComXMLRPCApi().getNotificationSettings(null, ctx);
+ }
+
+ public void onFailure(long id, XMLRPCException error) {
+ Log.v("WORDPRESS", error.getMessage());
+ }
+ }, "wpcom.mobile_push_register_token", params);
+ }
+
+ public static void unregisterWPComToken(Context ctx, String token) {
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(ctx);
+ Object[] params = {
+ settings.getString(WPCOM_USERNAME_PREFERENCE, ""),
+ WordPressDB.decryptPassword(settings.getString(WPCOM_PASSWORD_PREFERENCE, "")),
+ token,
+ false,
+ "android"
+ };
+
+ XMLRPCClient client = new XMLRPCClient(URI.create(Constants.wpcomXMLRPCURL), "", "");
+ client.callAsync(new XMLRPCCallback() {
+ public void onSuccess(long id, Object result) {
+ Log.v("WORDPRESS", "Successfully unregistered device on WP.com");
+ }
+
+ public void onFailure(long id, XMLRPCException error) {
+ Log.v("WORDPRESS", error.getMessage());
+ }
+ }, "wpcom.mobile_push_unregister_token", params);
+ }
+
/**
* Get versionName from Manifest.xml
* @return versionName
@@ -103,18 +232,12 @@ public static Blog getCurrentBlog() {
* @return the blog with the specified ID, or null if blog could not be retrieved.
*/
public static Blog getBlog(int id) {
- List> accounts = WordPress.wpDB.getAccounts();
- for (Map account : accounts) {
- int accountId = (Integer) account.get("id");
- if (accountId == id) {
- try {
- return new Blog(id);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
+ try {
+ Blog blog = new Blog(id);
+ return blog;
+ } catch (Exception e) {
+ return null;
}
- return null;
}
/**
@@ -153,4 +276,188 @@ public static Blog setCurrentBlog(int id) {
return currentBlog;
}
+ /**
+ * Restores notifications from cached file
+ */
+ public static void loadNotifications(Context context){
+ File file = new File(context.getCacheDir(), NOTES_CACHE);
+ BufferedReader buf = null;
+ StringBuilder json = null;
+ try {
+ buf = new BufferedReader(new FileReader(file));
+ json = new StringBuilder();
+ String line;
+ do {
+ line = buf.readLine();
+ json.append(line);
+ } while(line == null);
+ } catch (java.io.FileNotFoundException e) {
+ json = null;
+ Log.e(TAG, "No cached notes", e);
+ } catch (IOException e) {
+ json = null;
+ Log.e(TAG, "Could not read cached notes", e);
+ } finally {
+ try {
+ if (buf != null) {
+ buf.close();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Couldn't close file buffer", e);
+ }
+ }
+ if (json == null) {
+ Log.d(TAG, "Could not find cached notes");
+ return;
+ }
+ try {
+ latestNotes = new JSONObject(json.toString());
+ } catch (JSONException e) {
+ latestNotes = null;
+ Log.e(TAG, "Failed to parse note json", e);
+ return;
+ }
+ Log.d(TAG, "Restored notes");
+ }
+ /**
+ * Refreshes latest notes and stores a copy of the response to disk.
+ */
+ public static void refreshNotifications(final Context context,
+ final RestRequest.Listener listener,
+ final RestRequest.ErrorListener errorListener){
+ restClient.getNotifications(
+ new RestRequest.Listener(){
+ @Override
+ public void onResponse(JSONObject response){
+ latestNotes = response;
+ File file = new File(context.getCacheDir(), NOTES_CACHE);
+ if (file.exists()) {
+ file.delete();
+ }
+ FileWriter writer = null;
+ try {
+ String json = response.toString();
+ writer = new FileWriter(file);
+ writer.write(json, 0, json.length());
+ writer.close();
+ Log.d(TAG, String.format("Wrote notes json to %s", file));
+ } catch (IOException e) {
+ Log.d(TAG, String.format("Failed to cache notifications to %s", file));
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ writer = null;
+ }
+ }
+ Log.d(TAG, String.format("Store file here %s", file));
+ if (listener != null) {
+ listener.onResponse(response);
+ }
+ }
+ },
+ new RestRequest.ErrorListener(){
+ @Override
+ public void onErrorResponse(VolleyError error){
+ if (errorListener != null) {
+ errorListener.onErrorResponse(error) ;
+ }
+ }
+ }
+ );
+ }
+ /**
+ * Delete cached notifications, usually due to account logout
+ */
+ public static void deleteCachedNotifications(Context context){
+ File file = new File(context.getCacheDir(), NOTES_CACHE);
+ if (file.exists()) {
+ file.delete();
+ }
+ latestNotes = null;
+ }
+
+ /**
+ * Checks for WordPress.com credentials
+ *
+ * @return true if we have credentials or false if not
+ */
+ public static boolean hasValidWPComCredentials(Context context) {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
+ String username = settings.getString(WPCOM_USERNAME_PREFERENCE, null);
+ String password = settings.getString(WPCOM_PASSWORD_PREFERENCE, null);
+
+ if (username != null && password != null)
+ return true;
+ else
+ return false;
+ }
+
+ class OauthAuthenticator implements WPRestClient.Authenticator {
+ private final RequestQueue mQueue = Volley.newRequestQueue(WordPress.this);
+ @Override
+ public void authenticate(WPRestClient.Request request){
+ // set the access token if we have one
+ if (!hasValidWPComCredentials(WordPress.this)) {
+ request.abort(new VolleyError("Missing WordPress.com Account"));
+ } else {
+ requestAccessToken(request);
+ }
+ }
+ public void requestAccessToken(final WPRestClient.Request request){
+ final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(WordPress.this);
+ String username = settings.getString(WPCOM_USERNAME_PREFERENCE, null);
+ String password = WordPressDB.decryptPassword(settings.getString(WPCOM_PASSWORD_PREFERENCE, null));
+ Oauth oauth = new Oauth(Config.OAUTH_APP_ID, Config.OAUTH_APP_SECRET, Config.OAUTH_REDIRECT_URI);
+ // make oauth volley request
+ Request oauthRequest = oauth.makeRequest(username, password,
+ new Oauth.Listener(){
+ @Override
+ public void onResponse(Oauth.Token token){
+ settings.edit().putString(ACCESS_TOKEN_PREFERENCE, token.toString())
+ .commit();
+ request.setAccessToken(token);
+ request.send();
+ }
+ },
+ new Oauth.ErrorListener(){
+ @Override
+ public void onErrorResponse(VolleyError error){
+ request.abort(error);
+ }
+ }
+ );
+ mQueue.add(oauthRequest);
+ // add oauth request to the request queue
+
+ }
+ }
+
+ /**
+ * Sign out from all accounts by clearing out the password, which will require user to sign in
+ * again
+ */
+ public static void signOut(Context context) {
+ unregisterWPComToken(
+ context,
+ GCMRegistrar.getRegistrationId(context));
+ try {
+ GCMRegistrar.checkDevice(context);
+ GCMRegistrar.unregister(context);
+ } catch (Exception e) {
+ Log.v("WORDPRESS", "Could not unregister for GCM: " + e.getMessage());
+ }
+ SharedPreferences.Editor editor = PreferenceManager
+ .getDefaultSharedPreferences(context).edit();
+ editor.remove(WordPress.WPCOM_USERNAME_PREFERENCE);
+ editor.remove(WordPress.WPCOM_PASSWORD_PREFERENCE);
+ editor.remove(WordPress.ACCESS_TOKEN_PREFERENCE);
+ editor.commit();
+ wpDB.deactivateAccounts();
+ wpDB.updateLastBlogId(-1);
+ currentBlog = null;
+ restClient.clearAccessToken();
+ }
}
diff --git a/src/org/wordpress/android/WordPressDB.java b/src/org/wordpress/android/WordPressDB.java
index 7fe07b9f8aeb..0cc968df7dd2 100644
--- a/src/org/wordpress/android/WordPressDB.java
+++ b/src/org/wordpress/android/WordPressDB.java
@@ -32,7 +32,7 @@
public class WordPressDB {
- private static final int DATABASE_VERSION = 16;
+ private static final int DATABASE_VERSION = 17;
private static final String CREATE_TABLE_SETTINGS = "create table if not exists accounts (id integer primary key autoincrement, "
+ "url text, blogName text, username text, password text, imagePlacement text, centerThumbnail boolean, fullSizeImage boolean, maxImageWidth text, maxImageWidthId integer, lastCommentId integer, runService boolean);";
@@ -125,7 +125,7 @@ public class WordPressDB {
private SQLiteDatabase db;
- protected static final String PASSWORD_SECRET = "nottherealpasscode";
+ protected static final String PASSWORD_SECRET = Config.DB_SECRET;
public String defaultBlog = "";
@@ -151,7 +151,7 @@ public WordPressDB(Context ctx) {
db.execSQL(CREATE_TABLE_CATEGORIES);
db.execSQL(CREATE_TABLE_QUICKPRESS_SHORTCUTS);
db.execSQL(CREATE_TABLE_MEDIA);
-
+
try {
if (db.getVersion() < 1) { // user is new install
db.execSQL(ADD_BLOGID);
@@ -213,6 +213,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 2) {
db.delete(POSTS_TABLE, null, null);
@@ -244,6 +245,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 3) {
db.delete(POSTS_TABLE, null, null);
@@ -272,6 +274,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 4) {
db.delete(POSTS_TABLE, null, null);
@@ -300,6 +303,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 5) {
db.delete(POSTS_TABLE, null, null);
@@ -327,6 +331,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 6) {
db.delete(POSTS_TABLE, null, null);
@@ -352,6 +357,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 7) {
db.delete(POSTS_TABLE, null, null);
@@ -369,6 +375,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 8) {
db.delete(POSTS_TABLE, null, null);
@@ -385,6 +392,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 9) {
db.delete(POSTS_TABLE, null, null);
@@ -401,6 +409,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePasswords();
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 10) {
db.delete(POSTS_TABLE, null, null);
@@ -471,6 +480,7 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_HOME_URL);
db.execSQL(ADD_BLOG_OPTIONS);
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 11) {
db.execSQL(ADD_SCALED_IMAGE);
@@ -480,24 +490,32 @@ public WordPressDB(Context ctx) {
db.execSQL(ADD_HOME_URL);
db.execSQL(ADD_BLOG_OPTIONS);
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 12) {
db.execSQL(ADD_FEATURED_IN_POST);
db.execSQL(ADD_HOME_URL);
db.execSQL(ADD_BLOG_OPTIONS);
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 13) {
db.execSQL(ADD_HOME_URL);
db.execSQL(ADD_BLOG_OPTIONS);
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 14) {
db.execSQL(ADD_BLOG_OPTIONS);
migratePreferences(ctx);
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
} else if (db.getVersion() == 15) {
migratePreferences(ctx);
+ migrateWPComAccount();
+ db.setVersion(DATABASE_VERSION);
+ } else if (db.getVersion() == 16) {
+ migrateWPComAccount();
db.setVersion(DATABASE_VERSION);
}
} catch (SQLException e) {
@@ -505,7 +523,7 @@ public WordPressDB(Context ctx) {
}
}
-
+
private void migratePreferences(Context ctx) {
// Migrate preferences out of the db
Map, ?> notificationOptions = getNotificationOptions();
@@ -528,6 +546,24 @@ private void migratePreferences(Context ctx) {
editor.commit();
}
}
+
+ private void migrateWPComAccount() {
+ Cursor c = db.query(SETTINGS_TABLE, new String[] { "username", "password" }, "dotcomFlag=1", null, null,
+ null, null);
+
+ if (c.getCount() > 0) {
+ c.moveToFirst();
+ String username = c.getString(0);
+ String password = c.getString(1);
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this.context);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putString(WordPress.WPCOM_USERNAME_PREFERENCE, username);
+ editor.putString(WordPress.WPCOM_PASSWORD_PREFERENCE, password);
+ editor.commit();
+ }
+
+ c.close();
+ }
public long addAccount(String url, String homeURL, String blogName, String username,
String password, String httpuser, String httppassword,
@@ -555,32 +591,38 @@ public long addAccount(String url, String homeURL, String blogName, String usern
return db.insert(SETTINGS_TABLE, null, values);
}
+ public boolean deactivateAccounts() {
+
+ ContentValues values = new ContentValues();
+ values.put("password", "");
+
+ boolean returnValue = db.update(SETTINGS_TABLE, values, null, null) > 0;
+
+ return (returnValue);
+ }
+
public List> getAccounts() {
Cursor c = db.query(SETTINGS_TABLE, new String[] { "id", "blogName",
- "username", "runService", "blogId", "url" }, null, null, null,
+ "username", "blogId", "url", "password" }, null, null, null,
null, null);
- int id;
- String blogName, username, url;
- int blogId;
- int runService;
+
int numRows = c.getCount();
c.moveToFirst();
List> accounts = new Vector>();
for (int i = 0; i < numRows; i++) {
- id = c.getInt(0);
- blogName = c.getString(1);
- username = c.getString(2);
- runService = c.getInt(3);
- blogId = c.getInt(4);
- url = c.getString(5);
- if (id > 0) {
+ int id = c.getInt(0);
+ String blogName = c.getString(1);
+ String username = c.getString(2);
+ int blogId = c.getInt(3);
+ String url = c.getString(4);
+ String password = c.getString(5);
+ if (!password.equals("") && id > 0) {
Map thisHash = new HashMap();
thisHash.put("id", id);
thisHash.put("blogName", blogName);
thisHash.put("username", username);
- thisHash.put("runService", runService);
thisHash.put("blogId", blogId);
thisHash.put("url", url);
accounts.add(thisHash);
@@ -592,23 +634,26 @@ public List> getAccounts() {
return accounts;
}
- public boolean checkMatch(String blogName, String blogURL, String username) {
+ public long checkMatch(String blogName, String blogURL, String username, String password) {
- Cursor c = db.query(SETTINGS_TABLE, new String[] { "blogName", "url" },
+ Cursor c = db.query(SETTINGS_TABLE, new String[] { "id", "blogName", "url" },
"blogName='" + addSlashes(blogName) + "' AND url='"
+ addSlashes(blogURL) + "'" + " AND username='"
+ username + "'", null, null, null, null);
int numRows = c.getCount();
- boolean result = false;
if (numRows > 0) {
- // this account is already saved, yo!
- result = true;
+ // This account is already saved
+ c.moveToFirst();
+ long blogID = c.getLong(0);
+ ContentValues values = new ContentValues();
+ values.put("password", encryptPassword(password));
+ db.update(SETTINGS_TABLE, values, "id=" + blogID, null);
+ return blogID;
}
c.close();
-
- return result;
+ return -1;
}
public static String addSlashes(String text) {
@@ -673,17 +718,20 @@ public boolean saveSettings(String id, String url, String homeURL, String userna
boolean returnValue = db.update(SETTINGS_TABLE, values, "id=" + id,
null) > 0;
if (isWPCom) {
- // update the login for other wordpress.com accounts
- ContentValues userPass = new ContentValues();
- userPass.put("username", username);
- userPass.put("password", encryptPassword(password));
- returnValue = db.update(SETTINGS_TABLE, userPass, "username=\""
- + originalUsername + "\" AND dotcomFlag=1", null) > 0;
+ returnValue = updateWPComCredentials(username, password);
}
-
return (returnValue);
}
+
+ public boolean updateWPComCredentials(String username, String password) {
+ // update the login for wordpress.com blogs
+ ContentValues userPass = new ContentValues();
+ userPass.put("username", username);
+ userPass.put("password", encryptPassword(password));
+ return db.update(SETTINGS_TABLE, userPass, "username=\""
+ + username + "\" AND dotcomFlag=1", null) > 0;
+ }
public boolean deleteAccount(Context ctx, int id) {
@@ -1650,7 +1698,7 @@ public static String encryptPassword(String clearText) {
return clearText;
}
- protected String decryptPassword(String encryptedPwd) {
+ public static String decryptPassword(String encryptedPwd) {
try {
DESKeySpec keySpec = new DESKeySpec(
PASSWORD_SECRET.getBytes("UTF-8"));
diff --git a/src/org/wordpress/android/models/Blog.java b/src/org/wordpress/android/models/Blog.java
index 6a38af7b83c4..1350428e919e 100644
--- a/src/org/wordpress/android/models/Blog.java
+++ b/src/org/wordpress/android/models/Blog.java
@@ -308,4 +308,8 @@ public String getBlogOptions() {
public void setBlogOptions(String blogOptions) {
this.blogOptions = blogOptions;
}
+
+ public boolean isActive() {
+ return !password.equals("");
+ }
}
diff --git a/src/org/wordpress/android/models/Note.java b/src/org/wordpress/android/models/Note.java
new file mode 100644
index 000000000000..83874eae6322
--- /dev/null
+++ b/src/org/wordpress/android/models/Note.java
@@ -0,0 +1,266 @@
+/**
+ * Note represents a single WordPress.com notification
+ */
+package org.wordpress.android.models;
+
+import android.text.Html;
+import android.text.Spanned;
+import android.util.Log;
+import android.text.style.ImageSpan;
+import android.text.style.QuoteSpan;
+import android.text.SpannableStringBuilder;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.json.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import org.wordpress.android.util.JSONUtil;
+import org.wordpress.android.util.Emoticons;
+import org.wordpress.android.util.WPHtml;
+
+public class Note {
+ protected static final String TAG="NoteModel";
+ public static final String UNKNOWN_TYPE="unknown";
+ public static final String COMMENT_TYPE="comment";
+ public static final String LIKE_TYPE="like";
+ // Notes have different types of "templates" for displaying differently
+ // this is not a canonical list but covers all the types currently in use
+ public static final String SINGLE_LINE_LIST_TEMPLATE="single-line-list";
+ public static final String MULTI_LINE_LIST_TEMPLATE="multi-line-list";
+ public static final String BIG_BADGE_TEMPLATE="big-badge";
+ // JSON keys and values for looking up values
+ private static final String NOTE_ACTION_REPLY="replyto-comment";
+ private static final String REPLY_CONTENT_PARAM_KEY="content";
+
+ private Map mActions;
+ private Reply mReply;
+ private JSONObject mNoteJSON;
+ private SpannableStringBuilder mComment = new SpannableStringBuilder();
+ /**
+ * Create a note using JSON from REST API
+ */
+ public Note(JSONObject noteJSON){
+ mNoteJSON = noteJSON;
+ // get the comment ready if it's a comment type
+ cleanupComment();
+ }
+
+ public String toString(){
+ return getSubject();
+ }
+
+ public JSONObject toJSONObject(){
+ return mNoteJSON;
+ }
+ public String getId(){
+ return queryJSON("id", "0");
+ }
+ public String getType(){
+ return queryJSON("type", UNKNOWN_TYPE);
+ }
+ public Boolean isType(String type){
+ return getType().equals(type);
+ }
+ public Boolean isCommentType(){
+ return isType(COMMENT_TYPE);
+ }
+ public String getSubject(){
+ String text = queryJSON("subject.text", "").trim();
+ if (text.equals("")) {
+ text = queryJSON("subject.html", "");
+ }
+ return Html.fromHtml(text).toString();
+ }
+ public String getIconURL(){
+ return queryJSON("subject.icon", "");
+ }
+ /**
+ * Removes HTML and cleans up newlines and whitespace
+ */
+ public String getCommentPreview(){
+ return getCommentBody().toString().replace("\n", " ").replaceAll("[\\s]{2,}", " ").trim();
+ }
+ /**
+ * Gets the comment's text with getCommentText() and sends it through HTML.fromHTML
+ */
+ public Spanned getCommentBody(){
+ return mComment;
+ }
+ /**
+ * For a comment note the text is in the body object's last item. It currently
+ * is only provided in HTML format.
+ */
+ public String getCommentText(){
+ return queryJSON("body.items[last].html", "");
+ }
+ /**
+ * The inverse of isRead
+ */
+ public Boolean isUnread(){
+ return !isRead();
+ }
+ /**
+ * A note can have an "unread" of 0 or more ("likes" can have unread of 2+) to indicate the
+ * quantity of likes that are "unread" within the single note. So for a note to be "read" it
+ * should have "0"
+ */
+ public Boolean isRead(){
+ return getUnreadCount().equals("0");
+ }
+ /**
+ * For some reason the unread count is a string in the JSON API but is truly representd
+ * by an Integer. We can handle a simple string.
+ */
+ public String getUnreadCount(){
+ return queryJSON("unread", "0");
+ }
+ /**
+ *
+ */
+ public void setUnreadCount(String count){
+ try {
+ mNoteJSON.putOpt("unread", count);
+ } catch (JSONException e){
+ Log.e(TAG, "Failed to set unread property", e);
+ }
+ }
+ public Reply buildReply(String content){
+ JSONObject replyAction = getActions().get(NOTE_ACTION_REPLY);
+ String restPath = JSONUtil.queryJSON(replyAction, "params.rest_path", "");
+ Log.d(TAG, String.format("Search actions %s", restPath));
+ Reply reply = new Reply(this, String.format("%s/replies/new", restPath), content);
+ return reply;
+ }
+ /**
+ * Get the timestamp provided by the API for the note.
+ */
+ public String getTimestamp(){
+ return queryJSON("timestamp", "");
+ }
+ public String getTemplate(){
+ return queryJSON("body.template", "");
+ }
+ public Boolean isMultiLineListTemplate(){
+ return getTemplate().equals(MULTI_LINE_LIST_TEMPLATE);
+ }
+ public Boolean isSingleLineListTemplate(){
+ return getTemplate().equals(SINGLE_LINE_LIST_TEMPLATE);
+ }
+ public Boolean isBigBadgeTemplate(){
+ return getTemplate().equals(BIG_BADGE_TEMPLATE);
+ }
+ public Map getActions(){
+ if (mActions == null) {
+ try {
+ JSONArray actions = queryJSON("body.actions", new JSONArray());
+ mActions = new HashMap(actions.length());
+ for (int i=0; i();
+ }
+ }
+ return mActions;
+ }
+ /**
+ * Prepares the comment HTML for being displayed. Cleans up emoticons.
+ *
+ * TODO: Caching comment images
+ */
+ protected void cleanupComment(){
+ if (isCommentType()) {
+ mComment = Note.prepareHtml(getCommentText());
+ }
+ }
+ protected Object queryJSON(String query){
+ Object defaultObject = "";
+ return JSONUtil.queryJSON(this.toJSONObject(), query, defaultObject);
+ }
+ /**
+ * Rudimentary system for pulling an item out of a JSON object hierarchy
+ */
+ public U queryJSON(String query, U defaultObject){
+ return JSONUtil.queryJSON(this.toJSONObject(), query, defaultObject);
+ }
+ /**
+ * Represents a user replying to a note. Holds
+ */
+ public static class Reply {
+ private Note mNote;
+ private String mContent;
+ private String mRestPath;
+ private JSONObject mCommentJson;
+
+ Reply(Note note, String restPath, String content){
+ mNote = note;
+ mRestPath = restPath;
+ mContent = content;
+ }
+ public String getContent(){
+ return mContent;
+ }
+ public Note getNote(){
+ return mNote;
+ }
+ public String getUrl(){
+ if (isComplete()) {
+ return JSONUtil.queryJSON(mCommentJson, "URL", "");
+ }
+ return null;
+ }
+ public String getAvatarUrl(){
+ if (isComplete()) {
+ return JSONUtil.queryJSON(mCommentJson, "author.avatar_URL", "");
+ } else {
+ return "";
+ }
+ }
+ /**
+ * Passes through Html.fromHtml to remove markup and replaces smilies with emoji
+ */
+ public String getCommentPreview(){
+ if (isComplete()) {
+ String text = JSONUtil.queryJSON(mCommentJson, "content", "");
+ SpannableStringBuilder html = (SpannableStringBuilder) Html.fromHtml(text);
+ return Emoticons.replaceEmoticonsWithEmoji(html).toString().trim();
+ } else {
+ return "";
+ }
+ }
+ public String getRestPath(){
+ return mRestPath;
+ }
+ public boolean isComplete(){
+ return mCommentJson != null;
+ }
+ public JSONObject getCommentJson(){
+ return mCommentJson;
+ }
+ public void setCommentJson(JSONObject commentJson){
+ mCommentJson = commentJson;
+ }
+ }
+
+ /**
+ * Replaces emoticons with emoji
+ */
+ public static SpannableStringBuilder prepareHtml(String text){
+ SpannableStringBuilder html = (SpannableStringBuilder) Html.fromHtml(text);
+ Emoticons.replaceEmoticonsWithEmoji(html);
+ QuoteSpan spans[] = html.getSpans(0, html.length(), QuoteSpan.class);
+ for (QuoteSpan span : spans) {
+ html.setSpan(new WPHtml.WPQuoteSpan(), html.getSpanStart(span), html.getSpanEnd(span), html.getSpanFlags(span));
+ html.removeSpan(span);
+ }
+ return html;
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/AddQuickPressShortcutActivity.java b/src/org/wordpress/android/ui/AddQuickPressShortcutActivity.java
index e725385f9c54..1f850de8c0a9 100644
--- a/src/org/wordpress/android/ui/AddQuickPressShortcutActivity.java
+++ b/src/org/wordpress/android/ui/AddQuickPressShortcutActivity.java
@@ -21,22 +21,20 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
+import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
-import com.commonsware.cwac.cache.SimpleWebImageCache;
-import com.commonsware.cwac.thumbnail.ThumbnailAdapter;
-import com.commonsware.cwac.thumbnail.ThumbnailBus;
-import com.commonsware.cwac.thumbnail.ThumbnailMessage;
+import com.android.volley.toolbox.NetworkImageView;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.models.Blog;
import org.wordpress.android.ui.accounts.NewAccountActivity;
-import org.wordpress.android.ui.comments.CommentsListFragment;
import org.wordpress.android.ui.posts.EditPostActivity;
import org.wordpress.android.util.EscapeUtils;
+import org.wordpress.android.util.StringUtils;
public class AddQuickPressShortcutActivity extends ListActivity {
static final int ADD_ACCOUNT_REQUEST = 0;
@@ -47,8 +45,6 @@ public class AddQuickPressShortcutActivity extends ListActivity {
public String[] accountUsers;
public String[] blavatars;
public List accountNames = new Vector();
- protected ThumbnailAdapter thumbs = null;
- protected static final int[] IMAGE_IDS = { R.id.blavatar };
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -97,7 +93,7 @@ private void displayAccounts() {
String[] urlSplit = url.split("/");
url = urlSplit[0];
url = "http://gravatar.com/blavatar/"
- + CommentsListFragment.getMd5Hash(url.trim())
+ + StringUtils.getMd5Hash(url.trim())
+ "?s=60&d=404";
blavatars[validBlogCtr] = url;
accountNames.add(validBlogCtr, blogNames[i]);
@@ -108,12 +104,7 @@ private void displayAccounts() {
accounts = WordPress.wpDB.getAccounts();
}
- ThumbnailBus bus = new ThumbnailBus();
- thumbs = new ThumbnailAdapter(this, new HomeListAdapter(),
- new SimpleWebImageCache(
- null, null, 101, bus), IMAGE_IDS);
-
- setListAdapter(thumbs);
+ setListAdapter(new HomeListAdapter());
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView> arg0, View row, int position, long id) {
@@ -222,80 +213,29 @@ public long getItemId(int position) {
}
public View getView(int position, View convertView, ViewGroup parent) {
- View pv = convertView;
- ViewWrapper wrapper = null;
- if (pv == null) {
+ RelativeLayout view = (RelativeLayout) convertView;
+ if (view == null) {
LayoutInflater inflater = getLayoutInflater();
- pv = inflater.inflate(R.layout.home_row, parent, false);
- wrapper = new ViewWrapper(pv);
- /*if (position == 0) {
- usenameHeight = wrapper.getBlogUsername().getHeight();
- }*/
- pv.setTag(wrapper);
- wrapper = new ViewWrapper(pv);
- pv.setTag(wrapper);
- } else {
- wrapper = (ViewWrapper) pv.getTag();
- }
+ view = (RelativeLayout)inflater.inflate(R.layout.home_row, parent, false);
+ }
String username = accountUsers[position];
- pv.setBackgroundDrawable(getResources().getDrawable(
+ view.setBackgroundDrawable(getResources().getDrawable(
R.drawable.list_bg_selector));
- pv.setId(Integer.valueOf(accountIDs[position]));
- if (wrapper.getBlogUsername().getHeight() == 0) {
- wrapper.getBlogUsername().setHeight(
- (int) wrapper.getBlogName().getTextSize()
- + wrapper.getBlogUsername().getPaddingBottom());
- }
+ view.setId(Integer.valueOf(accountIDs[position]));
- wrapper.getBlogName().setText(
+ TextView blogName = (TextView)view.findViewById(R.id.blogName);
+ TextView blogUsername = (TextView)view.findViewById(R.id.blogUser);
+ NetworkImageView blavatar = (NetworkImageView)view.findViewById(R.id.blavatar);
+
+ blogName.setText(
EscapeUtils.unescapeHtml(blogNames[position]));
- wrapper.getBlogUsername().setText(
+ blogUsername.setText(
EscapeUtils.unescapeHtml(username));
+ blavatar.setImageUrl(blavatars[position], WordPress.imageLoader);
+
+ return view;
- if (wrapper.getBlavatar() != null) {
- try {
- wrapper.getBlavatar().setImageResource(R.drawable.app_icon);
- wrapper.getBlavatar().setTag(blavatars[position]);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
-
- return pv;
-
- }
-
- }
-
- class ViewWrapper {
- View base;
- TextView blogName = null;
- TextView blogUsername = null;
- ImageView blavatar = null;
-
- ViewWrapper(View base) {
- this.base = base;
- }
-
- TextView getBlogName() {
- if (blogName == null) {
- blogName = (TextView) base.findViewById(R.id.blogName);
- }
- return (blogName);
- }
-
- TextView getBlogUsername() {
- if (blogUsername == null) {
- blogUsername = (TextView) base.findViewById(R.id.blogUser);
- }
- return (blogUsername);
}
- ImageView getBlavatar() {
- if (blavatar == null) {
- blavatar = (ImageView) base.findViewById(R.id.blavatar);
- }
- return (blavatar);
- }
}
}
diff --git a/src/org/wordpress/android/ui/MenuDrawerItem.java b/src/org/wordpress/android/ui/MenuDrawerItem.java
new file mode 100644
index 000000000000..65616e483757
--- /dev/null
+++ b/src/org/wordpress/android/ui/MenuDrawerItem.java
@@ -0,0 +1,100 @@
+/**
+ * Represents a single item in the WPActionBarActivity's menu drawer. A MenuDrawerItem determines
+ * the label and icon to use in the menu, its presence in the menu, its selection state, and the
+ * action that happens when the item is selected.
+ */
+package org.wordpress.android.ui;
+
+import android.view.View;
+
+public abstract class MenuDrawerItem {
+ /**
+ * Signifies that the item has no unique ID so should not be tracked in the last selected
+ * item preference.
+ */
+ public static int NO_ITEM_ID = -1;
+ /**
+ * Called when the menu item is selected.
+ */
+ abstract public void onSelectItem();
+ /**
+ * Determines if the menu item should be displayed in the menu. Default is always true.
+ */
+ public Boolean isVisible(){
+ return true;
+ };
+ /**
+ * Determines if the item is selected. Default is always false.
+ */
+ public Boolean isSelected(){
+ return false;
+ }
+ /**
+ * Method to allow the menu item to provide additional configuration to the view, default
+ * implementation does nothing.
+ */
+ public void onConfigureView(View view){};
+
+ // Resource id for the title string
+ protected int mTitle;
+ // Resource id for the icon drawable
+ protected int mIconRes;
+ // ID for the item for remembering which item was selected
+ private int mItemId;
+ /**
+ * Creates a MenuDrawerItem with the specific id, string resource id and drawable resource id
+ */
+ MenuDrawerItem(int itemId, int stringRes, int iconRes) {
+ mTitle = stringRes;
+ mIconRes = iconRes;
+ mItemId = itemId;
+ }
+ /**
+ * Creates a MenuDrawerItem with NO_ITEM_ID for it's id for items that shouldn't be remembered
+ * between application launches.
+ */
+ MenuDrawerItem(int stringRes, int iconRes){
+ this(NO_ITEM_ID, stringRes, iconRes);
+ }
+ /**
+ * Determines if the item has an id for remembering the last selected item
+ */
+ public boolean hasItemId(){
+ return getItemId() != NO_ITEM_ID;
+ }
+ /**
+ * Get's the item's unique ID
+ */
+ public int getItemId(){
+ return mItemId;
+ }
+ /**
+ * Returns the item's string representation (used by ArrayAdapter.getView)
+ */
+ public String toString(){
+ return "";
+ }
+ /**
+ * The resource id to use for the menu item's title
+ */
+ public int getTitleRes(){
+ return mTitle;
+ }
+ /**
+ * The resource id to use for the menu item's icon
+ */
+ public int getIconRes(){
+ return mIconRes;
+ }
+
+ public void selectItem(){
+ onSelectItem();
+ }
+ /**
+ * Allows the menu item to do additional manipulation to the view
+ */
+ public void configureView(View v){
+ onConfigureView(v);
+ }
+
+}
diff --git a/src/org/wordpress/android/ui/StatsActivity.java b/src/org/wordpress/android/ui/StatsActivity.java
index 8b5bdac4740f..5d37d6192434 100644
--- a/src/org/wordpress/android/ui/StatsActivity.java
+++ b/src/org/wordpress/android/ui/StatsActivity.java
@@ -187,7 +187,7 @@ protected void onDestroy() {
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getSupportMenuInflater();
- inflater.inflate(R.menu.refresh_only, menu);
+ inflater.inflate(R.menu.basic_menu, menu);
refreshMenuItem = menu.findItem(R.id.menu_refresh);
return true;
}
diff --git a/src/org/wordpress/android/ui/WPActionBarActivity.java b/src/org/wordpress/android/ui/WPActionBarActivity.java
index 16b96b2149d7..b55164a4bc33 100644
--- a/src/org/wordpress/android/ui/WPActionBarActivity.java
+++ b/src/org/wordpress/android/ui/WPActionBarActivity.java
@@ -2,15 +2,16 @@
package org.wordpress.android.ui;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import android.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@@ -25,7 +26,6 @@
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
@@ -46,6 +46,7 @@
import org.wordpress.android.models.Blog;
import org.wordpress.android.ui.accounts.NewAccountActivity;
import org.wordpress.android.ui.comments.CommentsActivity;
+import org.wordpress.android.ui.notifications.NotificationsActivity;
import org.wordpress.android.ui.posts.EditPostActivity;
import org.wordpress.android.ui.posts.PagesActivity;
import org.wordpress.android.ui.posts.PostsActivity;
@@ -70,6 +71,10 @@ public abstract class WPActionBarActivity extends SherlockFragmentActivity {
* Request code for reloading menu after returning from the PreferencesActivity.
*/
static final int SETTINGS_REQUEST = 200;
+ /**
+ * Request code for re-authentication
+ */
+ static final int AUTHENTICATE_REQUEST = 300;
/**
* Used to restore active activity on app creation
@@ -83,28 +88,42 @@ public abstract class WPActionBarActivity extends SherlockFragmentActivity {
protected static final int QUICK_VIDEO_ACTIVITY = 6;
protected static final int VIEW_SITE_ACTIVITY = 7;
protected static final int DASHBOARD_ACTIVITY = 8;
- protected static final int SETTINGS_ACTIVITY = 9;
+ protected static final int NOTIFICATIONS_ACTIVITY = 9;
+
+ protected static final String LAST_ACTIVITY_PREFERENCE = "wp_pref_last_activity";
protected MenuDrawer mMenuDrawer;
private static int[] blogIDs;
protected boolean isAnimatingRefreshButton;
protected boolean shouldAnimateRefreshButton;
protected boolean mShouldFinish;
- private boolean mIsDotComBlog;
private boolean mIsXLargeDevice;
private boolean mBlogSpinnerInitialized;
- private int mActivePosition;
+ private boolean mReauthCanceled;
private MenuAdapter mAdapter;
+ protected List mMenuItems = new ArrayList();
private ListView mListView;
private IcsSpinner mBlogSpinner;
-
+ protected boolean mFirstLaunch = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
if ((getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == 4)
- mIsXLargeDevice = true;
+ mIsXLargeDevice = true;
+
+ // configure all the available menu items
+ mMenuItems.add(new ReaderMenuItem());
+ mMenuItems.add(new NotificationsMenuItem());
+ mMenuItems.add(new PostsMenuItem());
+ mMenuItems.add(new PagesMenuItem());
+ mMenuItems.add(new CommentsMenuItem());
+ mMenuItems.add(new StatsMenuItem());
+ mMenuItems.add(new QuickPhotoMenuItem());
+ mMenuItems.add(new QuickVideoMenuItem());
+ mMenuItems.add(new ViewSiteMenuItem());
+ mMenuItems.add(new AdminMenuItem());
+
}
@Override
@@ -185,13 +204,16 @@ private MenuDrawer attachMenuDrawer() {
} else {
menuDrawer = MenuDrawer.attach(this, MenuDrawer.MENU_DRAG_CONTENT);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ menuDrawer.setDrawerIndicatorEnabled(true);
}
} else {
menuDrawer = MenuDrawer.attach(this, MenuDrawer.MENU_DRAG_CONTENT);
+ menuDrawer.setDrawerIndicatorEnabled(true);
}
int shadowSizeInPixels = getResources().getDimensionPixelSize(R.dimen.menu_shadow_width);
menuDrawer.setDropShadowSize(shadowSizeInPixels);
menuDrawer.setDropShadowColor(getResources().getColor(R.color.md__shadowColor));
+ menuDrawer.setSlideDrawable(R.drawable.ic_drawer);
return menuDrawer;
}
@@ -204,13 +226,36 @@ private void initMenuDrawer() {
mListView.setDivider(null);
mListView.setDividerHeight(0);
mListView.setCacheColorHint(android.R.color.transparent);
-
+ mAdapter = new MenuAdapter(this);
String[] blogNames = getBlogNames();
if (blogNames.length > 1) {
addBlogSpinner(blogNames);
}
-
- mListView.setOnItemClickListener(mItemClickListener);
+
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ // account for header views
+ int menuPosition = position - mListView.getHeaderViewsCount();
+ // bail if the adjusted position is out of bounds for the adapter
+ if (menuPosition < 0 || menuPosition >= mAdapter.getCount())
+ return;
+ MenuDrawerItem item = mAdapter.getItem(menuPosition);
+ // if the item has an id, remember it for launch
+ if (item.hasItemId()){
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(WPActionBarActivity.this);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(LAST_ACTIVITY_PREFERENCE, item.getItemId());
+ editor.commit();
+ }
+ // only perform selection if the item isn't already selected
+ if (!item.isSelected())
+ item.selectItem();
+ // save the last activity preference
+ // close the menu drawer
+ mMenuDrawer.closeMenu();
+ // if we have an intent, start the new activity
+ }
+ });
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
@@ -224,7 +269,7 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun
});
mMenuDrawer.setMenuView(mListView);
-
+ mListView.setAdapter(mAdapter);
updateMenuDrawer();
}
@@ -257,6 +302,11 @@ protected void startActivityWithDelay(final Intent i) {
// Tablets in landscape don't need a delay because the menu drawer doesn't close
startActivity(i);
} else {
+ // When switching to LAST_ACTIVITY_PREFERENCE onCreate we don't need to delay
+ if (mFirstLaunch) {
+ startActivity(i);
+ return;
+ }
// Let the menu animation finish before starting a new activity
new Handler().postDelayed(new Runnable() {
@Override
@@ -272,274 +322,56 @@ public void run() {
* blog.
*/
protected void updateMenuDrawer() {
-
- mIsDotComBlog = WordPress.currentBlog != null && WordPress.currentBlog.isDotcomFlag();
-
- List items = new ArrayList();
- Resources resources = getResources();
- if (mIsDotComBlog)
- items.add(new MenuDrawerItem(resources.getString(R.string.reader),
- R.drawable.dashboard_icon_subs));
- items.add(new MenuDrawerItem(resources.getString(R.string.posts),
- R.drawable.dashboard_icon_posts));
- items.add(new MenuDrawerItem(resources.getString(R.string.pages),
- R.drawable.dashboard_icon_pages));
- items.add(new MenuDrawerItem(resources.getString(R.string.tab_comments),
- R.drawable.dashboard_icon_comments));
- items.add(new MenuDrawerItem(resources.getString(R.string.tab_stats),
- R.drawable.dashboard_icon_stats));
- items.add(new MenuDrawerItem(resources.getString(R.string.quick_photo),
- R.drawable.dashboard_icon_photo));
- items.add(new MenuDrawerItem(resources.getString(R.string.quick_video),
- R.drawable.dashboard_icon_video));
- items.add(new MenuDrawerItem(resources.getString(R.string.view_site),
- R.drawable.dashboard_icon_view));
- items.add(new MenuDrawerItem(resources.getString(R.string.view_admin),
- R.drawable.dashboard_icon_wp));
- items.add(new MenuDrawerItem(resources.getString(R.string.settings),
- R.drawable.dashboard_icon_settings));
-
- if ((WPActionBarActivity.this instanceof ReaderActivity))
- mActivePosition = 0;
-
- if ((WPActionBarActivity.this instanceof PostsActivity))
- mActivePosition = 1;
-
- if ((WPActionBarActivity.this instanceof PagesActivity))
- mActivePosition = 2;
- else if ((WPActionBarActivity.this instanceof CommentsActivity))
- mActivePosition = 3;
- else if ((WPActionBarActivity.this instanceof StatsActivity))
- mActivePosition = 4;
- else if ((WPActionBarActivity.this instanceof ViewSiteActivity))
- mActivePosition = 7;
- else if ((WPActionBarActivity.this instanceof DashboardActivity))
- mActivePosition = 8;
-
- mAdapter = new MenuAdapter(items);
- mListView.setAdapter(mAdapter);
- }
-
- private AdapterView.OnItemClickListener mItemClickListener = new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- // Adjust position if only one blog is in the app
- if (mListView.getHeaderViewsCount() > 0 && position > 0)
- position--;
-
- if (!mIsDotComBlog)
- position++;
-
- if (position == mActivePosition) {
- // Same row selected
- mMenuDrawer.closeMenu();
- return;
+ mAdapter.clear();
+ // iterate over the available menu items and only show the ones that should be visible
+ Iterator availableItems = mMenuItems.iterator();
+ while(availableItems.hasNext()){
+ MenuDrawerItem item = availableItems.next();
+ if (item.isVisible()) {
+ mAdapter.add(item);
}
-
- int activityTag = (Integer) view.getTag();
-
- mActivePosition = position;
- mAdapter.notifyDataSetChanged();
- Intent intent = null;
-
- SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(WPActionBarActivity.this);
- SharedPreferences.Editor editor = settings.edit();
-
- switch (activityTag) {
- case READER_ACTIVITY:
- if (!(WPActionBarActivity.this instanceof ReaderActivity))
- mShouldFinish = true;
- int readerBlogID = WordPress.wpDB.getWPCOMBlogID();
- if
- (WordPress.currentBlog.isDotcomFlag()) {
- intent = new Intent(WPActionBarActivity.this, ReaderActivity.class);
- intent.putExtra("id", readerBlogID);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- editor.putInt("wp_pref_last_activity", READER_ACTIVITY);
- }
- break;
- case POSTS_ACTIVITY:
- if (!(WPActionBarActivity.this instanceof PostsActivity)
- || (WPActionBarActivity.this instanceof PagesActivity))
- mShouldFinish = true;
- intent = new
- Intent(WPActionBarActivity.this, PostsActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- editor.putInt("wp_pref_last_activity", POSTS_ACTIVITY);
- break;
- case PAGES_ACTIVITY:
- if (!(WPActionBarActivity.this instanceof PagesActivity))
- mShouldFinish = true;
- intent = new Intent(WPActionBarActivity.this, PagesActivity.class);
- intent.putExtra("id", WordPress.currentBlog.getId());
- intent.putExtra("isNew",
- true);
- intent.putExtra("viewPages", true);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- editor.putInt("wp_pref_last_activity", PAGES_ACTIVITY);
- break;
- case COMMENTS_ACTIVITY:
- if (!(WPActionBarActivity.this instanceof CommentsActivity))
- mShouldFinish = true;
- intent = new Intent(WPActionBarActivity.this, CommentsActivity.class);
- intent.putExtra("id", WordPress.currentBlog.getId());
- intent.putExtra("isNew",
- true);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- editor.putInt("wp_pref_last_activity", COMMENTS_ACTIVITY);
- break;
- case STATS_ACTIVITY:
- if (!(WPActionBarActivity.this instanceof StatsActivity))
- mShouldFinish = true;
- intent = new Intent(WPActionBarActivity.this, StatsActivity.class);
- intent.putExtra("id", WordPress.currentBlog.getId());
- intent.putExtra("isNew",
- true);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- editor.putInt("wp_pref_last_activity", STATS_ACTIVITY);
- break;
- case QUICK_PHOTO_ACTIVITY:
- mShouldFinish = false;
- intent = new Intent(WPActionBarActivity.this, EditPostActivity.class);
- intent.putExtra("quick-media", DeviceUtils.getInstance().hasCamera(getApplicationContext())
- ? Constants.QUICK_POST_PHOTO_CAMERA
- : Constants.QUICK_POST_PHOTO_LIBRARY);
- intent.putExtra("isNew", true);
- break;
- case QUICK_VIDEO_ACTIVITY:
- mShouldFinish = false;
- intent = new Intent(WPActionBarActivity.this, EditPostActivity.class);
- intent.putExtra("quick-media", DeviceUtils.getInstance().hasCamera(getApplicationContext())
- ? Constants.QUICK_POST_VIDEO_CAMERA
- : Constants.QUICK_POST_VIDEO_LIBRARY);
- intent.putExtra("isNew", true);
- break;
- case VIEW_SITE_ACTIVITY:
- if (!(WPActionBarActivity.this instanceof ViewSiteActivity))
- mShouldFinish = true;
- intent = new Intent(WPActionBarActivity.this, ViewSiteActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- editor.putInt("wp_pref_last_activity", VIEW_SITE_ACTIVITY);
- break;
- case DASHBOARD_ACTIVITY:
- if (!(WPActionBarActivity.this instanceof DashboardActivity))
- mShouldFinish = true;
- intent = new Intent(WPActionBarActivity.this, DashboardActivity.class);
- intent.putExtra("loadAdmin", true);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- editor.putInt("wp_pref_last_activity", DASHBOARD_ACTIVITY);
- break;
- case SETTINGS_ACTIVITY:
- // Settings shouldn't be launched with a delay, or close the drawer
- mShouldFinish = false;
- Intent settingsIntent = new Intent(WPActionBarActivity.this, PreferencesActivity.class);
- startActivityForResult(settingsIntent, SETTINGS_REQUEST);
- return;
- }
-
- editor.commit();
- if (intent != null) {
- mMenuDrawer.closeMenu();
- startActivityWithDelay(intent);
- }
- }
- };
-
- private class MenuAdapter extends BaseAdapter {
-
- private List mItems;
-
- MenuAdapter(List items) {
- mItems = items;
- }
-
- @Override
- public int getCount() {
- return mItems.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mItems.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
}
+ mAdapter.notifyDataSetChanged();
- @Override
- public int getItemViewType(int position) {
- return getItem(position) instanceof MenuItem ? 0 : 1;
- }
+ }
+
+ public static class MenuAdapter extends ArrayAdapter {
- @Override
- public int getViewTypeCount() {
- return 2;
+ MenuAdapter(Context context) {
+ super(context, R.layout.menu_drawer_row, R.id.menu_row_title, new ArrayList());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- View v = convertView;
- Object item = getItem(position);
-
- if (v == null) {
- v = getLayoutInflater().inflate(R.layout.menu_drawer_row, parent, false);
- }
+ View view = super.getView(position, convertView, parent);
+ MenuDrawerItem item = getItem(position);
- TextView titleTextView = (TextView) v.findViewById(R.id.menu_row_title);
- titleTextView.setText(((MenuDrawerItem) item).mTitle);
+ TextView titleTextView = (TextView) view.findViewById(R.id.menu_row_title);
+ titleTextView.setText(item.getTitleRes());
- ImageView iconImageView = (ImageView) v.findViewById(R.id.menu_row_icon);
- iconImageView.setImageResource(((MenuDrawerItem) item).mIconRes);
-
- v.setTag((mIsDotComBlog) ? position : position + 1);
+ ImageView iconImageView = (ImageView) view.findViewById(R.id.menu_row_icon);
+ iconImageView.setImageResource(item.getIconRes());
+ // Hide the badge always
+ view.findViewById(R.id.menu_row_badge).setVisibility(View.GONE);
- int positionCheck = mActivePosition;
- if (!mIsDotComBlog)
- positionCheck--;
- if ((position) == positionCheck) {
+ if (item.isSelected()) {
// http://stackoverflow.com/questions/5890379/setbackgroundresource-discards-my-xml-layout-attributes
- int bottom = v.getPaddingBottom();
- int top = v.getPaddingTop();
- int right = v.getPaddingRight();
- int left = v.getPaddingLeft();
- v.setBackgroundResource(R.drawable.menu_drawer_selected);
- v.setPadding(left, top, right, bottom);
+ int bottom = view.getPaddingBottom();
+ int top = view.getPaddingTop();
+ int right = view.getPaddingRight();
+ int left = view.getPaddingLeft();
+ view.setBackgroundResource(R.drawable.menu_drawer_selected);
+ view.setPadding(left, top, right, bottom);
} else {
- v.setBackgroundResource(R.drawable.md_list_selector);
+ view.setBackgroundResource(R.drawable.md_list_selector);
}
+ // allow the menudrawer item to configure the view
+ item.configureView(view);
-
- TextView bagdeTextView = (TextView) v.findViewById(R.id.menu_row_badge);
- int commentRow = (mIsDotComBlog) ? 3 : 2;
- if (position == commentRow && WordPress.currentBlog != null) {
- int commentCount = WordPress.currentBlog.getUnmoderatedCommentCount();
- if (commentCount > 0) {
- bagdeTextView.setVisibility(View.VISIBLE);
- } else
- {
- bagdeTextView.setVisibility(View.GONE);
- }
- bagdeTextView.setText(String.valueOf(commentCount));
- } else {
- bagdeTextView.setVisibility(View.GONE);
- }
-
- return v;
+ return view;
}
}
- private static class MenuDrawerItem {
-
- String mTitle;
- int mIconRes;
-
- MenuDrawerItem(String title, int iconRes) {
- mTitle = title;
- mIconRes = iconRes;
- }
- }
/**
* Called when the activity has detected the user's press of the back key.
@@ -601,8 +433,8 @@ private static String[] getBlogNames() {
public void setupCurrentBlog() {
Blog currentBlog = WordPress.getCurrentBlog();
- // no blogs are configured, so display new account activity
- if (currentBlog == null) {
+ // No blogs are configured or user has signed out, so display new account activity
+ if (currentBlog == null || getBlogNames().length == 0) {
Log.d(TAG, "No accounts configured. Sending user to set up an account");
mShouldFinish = false;
Intent i = new Intent(this, NewAccountActivity.class);
@@ -624,6 +456,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
setupCurrentBlog();
initMenuDrawer();
mMenuDrawer.peekDrawer(0);
+ WordPress.registerForCloudMessaging(this);
} else {
finish();
}
@@ -649,9 +482,19 @@ else if (blogNames.length > 1 && mBlogSpinner != null) {
setupCurrentBlog();
onBlogChanged();
}
+ WordPress.registerForCloudMessaging(this);
}
}
break;
+ case AUTHENTICATE_REQUEST:
+ if (resultCode == RESULT_CANCELED) {
+ mReauthCanceled = true;
+ Intent i = new Intent(this, NewAccountActivity.class);
+ startActivityForResult(i, ADD_ACCOUNT_REQUEST);
+ } else {
+ WordPress.registerForCloudMessaging(this);
+ }
+ break;
}
}
@@ -675,12 +518,36 @@ public void onNothingSelected(IcsAdapterView> arg0) {
};
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- if (mMenuDrawer != null) {
- mMenuDrawer.toggleMenu();
- return true;
- }
+ if (item.getItemId() == android.R.id.home) {
+ if (mMenuDrawer != null) {
+ mMenuDrawer.toggleMenu();
+ return true;
+ }
+ } else if (item.getItemId() == R.id.menu_settings) {
+ Intent i = new Intent(this, PreferencesActivity.class);
+ startActivityForResult(i, SETTINGS_REQUEST);
+ } else if (item.getItemId() == R.id.menu_signout) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+ dialogBuilder.setTitle(getResources().getText(R.string.sign_out));
+ dialogBuilder.setMessage(getString(R.string.sign_out_confirm));
+ dialogBuilder.setPositiveButton(R.string.sign_out,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ WordPress.signOut(WPActionBarActivity.this);
+ finish();
+ }
+ });
+ dialogBuilder.setNegativeButton(R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ // Just close the window.
+ }
+ });
+ dialogBuilder.setCancelable(true);
+ if (!isFinishing())
+ dialogBuilder.create().show();
}
return super.onOptionsItemSelected(item);
}
@@ -690,6 +557,26 @@ public boolean onOptionsItemSelected(MenuItem item) {
*/
public void onBlogChanged() {
WordPress.wpDB.updateLastBlogId(WordPress.currentBlog.getId());
+ // the menu may have changed, we need to change the selection if the selected item
+ // is not available in the menu anymore
+ Iterator itemIterator = mMenuItems.iterator();
+ while(itemIterator.hasNext()){
+ MenuDrawerItem item = itemIterator.next();
+ // if the item is selected, but it's no longer visible we need to
+ // select the first available item from the adapter
+ if (item.isSelected() && !item.isVisible()) {
+ // then select the first item and activate it
+ mAdapter.getItem(0).selectItem();
+ // if it has an item id save it to the preferences
+ if (item.hasItemId()){
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(WPActionBarActivity.this);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(LAST_ACTIVITY_PREFERENCE, item.getItemId());
+ editor.commit();
+ }
+ break;
+ }
+ }
}
public void startAnimatingRefreshButton(MenuItem refreshItem) {
@@ -715,10 +602,10 @@ public void stopAnimatingRefreshButton(MenuItem refreshItem) {
refreshItem.setActionView(null);
}
}
-
+
@Override
public void onConfigurationChanged(Configuration newConfig) {
-
+
if (mIsXLargeDevice) {
if (mMenuDrawer != null) {
// Re-attach the drawer if an XLarge device is rotated, so it can be static if in landscape
@@ -731,7 +618,223 @@ public void onConfigurationChanged(Configuration newConfig) {
}
}
}
-
+
super.onConfigurationChanged(newConfig);
}
+
+ private class ReaderMenuItem extends MenuDrawerItem {
+
+ ReaderMenuItem(){
+ super(READER_ACTIVITY, R.string.reader, R.drawable.dashboard_icon_subs);
+ }
+
+ @Override
+ public Boolean isVisible(){
+ return WordPress.hasValidWPComCredentials(WPActionBarActivity.this);
+ }
+
+ @Override
+ public Boolean isSelected(){
+ return WPActionBarActivity.this instanceof ReaderActivity;
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof ReaderActivity))
+ mShouldFinish = true;
+ int readerBlogID = WordPress.wpDB.getWPCOMBlogID();
+ Intent intent = new Intent(WPActionBarActivity.this, ReaderActivity.class);
+ intent.putExtra("id", readerBlogID);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+
+ }
+
+ private class PostsMenuItem extends MenuDrawerItem {
+ PostsMenuItem(){
+ super(POSTS_ACTIVITY, R.string.posts, R.drawable.dashboard_icon_posts);
+ }
+ @Override
+ public Boolean isSelected(){
+ WPActionBarActivity activity = WPActionBarActivity.this;
+ return (activity instanceof PostsActivity) && !(activity instanceof PagesActivity);
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof PostsActivity)
+ || (WPActionBarActivity.this instanceof PagesActivity))
+ mShouldFinish = true;
+ Intent intent = new Intent(WPActionBarActivity.this, PostsActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+ }
+
+ private class PagesMenuItem extends MenuDrawerItem {
+ PagesMenuItem(){
+ super(PAGES_ACTIVITY, R.string.pages, R.drawable.dashboard_icon_pages);
+ }
+ @Override
+ public Boolean isSelected(){
+ return WPActionBarActivity.this instanceof PagesActivity;
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof PagesActivity))
+ mShouldFinish = true;
+ Intent intent = new Intent(WPActionBarActivity.this, PagesActivity.class);
+ intent.putExtra("id", WordPress.currentBlog.getId());
+ intent.putExtra("isNew", true);
+ intent.putExtra("viewPages", true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+ }
+
+ private class CommentsMenuItem extends MenuDrawerItem {
+ CommentsMenuItem(){
+ super(COMMENTS_ACTIVITY, R.string.tab_comments, R.drawable.dashboard_icon_comments);
+ }
+ @Override
+ public Boolean isSelected(){
+ return WPActionBarActivity.this instanceof CommentsActivity;
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof CommentsActivity))
+ mShouldFinish = true;
+ Intent intent = new Intent(WPActionBarActivity.this, CommentsActivity.class);
+ intent.putExtra("id", WordPress.currentBlog.getId());
+ intent.putExtra("isNew",
+ true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+ @Override
+ public void configureView(View view){
+ if (WordPress.getCurrentBlog() != null) {
+ TextView bagdeTextView = (TextView) view.findViewById(R.id.menu_row_badge);
+ int commentCount = WordPress.getCurrentBlog().getUnmoderatedCommentCount();
+ if (commentCount > 0) {
+ bagdeTextView.setVisibility(View.VISIBLE);
+ } else
+ {
+ bagdeTextView.setVisibility(View.GONE);
+ }
+ bagdeTextView.setText(String.valueOf(commentCount));
+ }
+ }
+ }
+
+ private class StatsMenuItem extends MenuDrawerItem {
+ StatsMenuItem(){
+ super(STATS_ACTIVITY, R.string.tab_stats, R.drawable.dashboard_icon_stats);
+ }
+ @Override
+ public Boolean isSelected(){
+ return WPActionBarActivity.this instanceof StatsActivity;
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof StatsActivity))
+ mShouldFinish = true;
+ Intent intent = new Intent(WPActionBarActivity.this, StatsActivity.class);
+ intent.putExtra("id", WordPress.currentBlog.getId());
+ intent.putExtra("isNew",
+ true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+ }
+
+ private class QuickPhotoMenuItem extends MenuDrawerItem {
+ QuickPhotoMenuItem(){
+ super(R.string.quick_photo, R.drawable.dashboard_icon_photo);
+ }
+ @Override
+ public void onSelectItem(){
+ mShouldFinish = false;
+ Intent intent = new Intent(WPActionBarActivity.this, EditPostActivity.class);
+ intent.putExtra("quick-media", DeviceUtils.getInstance().hasCamera(getApplicationContext())
+ ? Constants.QUICK_POST_PHOTO_CAMERA
+ : Constants.QUICK_POST_PHOTO_LIBRARY);
+ intent.putExtra("isNew", true);
+ startActivityWithDelay(intent);
+ }
+ }
+
+ private class QuickVideoMenuItem extends MenuDrawerItem {
+ QuickVideoMenuItem(){
+ super(R.string.quick_video, R.drawable.dashboard_icon_video);
+ }
+ @Override
+ public void onSelectItem(){
+ mShouldFinish = false;
+ Intent intent = new Intent(WPActionBarActivity.this, EditPostActivity.class);
+ intent.putExtra("quick-media", DeviceUtils.getInstance().hasCamera(getApplicationContext())
+ ? Constants.QUICK_POST_VIDEO_CAMERA
+ : Constants.QUICK_POST_VIDEO_LIBRARY);
+ intent.putExtra("isNew", true);
+ startActivityWithDelay(intent);
+ }
+ }
+
+ private class ViewSiteMenuItem extends MenuDrawerItem {
+ ViewSiteMenuItem(){
+ super(VIEW_SITE_ACTIVITY, R.string.view_site, R.drawable.dashboard_icon_view);
+ }
+ @Override
+ public Boolean isSelected(){
+ return WPActionBarActivity.this instanceof ViewSiteActivity;
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof ViewSiteActivity))
+ mShouldFinish = true;
+ Intent intent = new Intent(WPActionBarActivity.this, ViewSiteActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+ }
+
+ private class AdminMenuItem extends MenuDrawerItem {
+ AdminMenuItem(){
+ super(DASHBOARD_ACTIVITY, R.string.view_admin, R.drawable.dashboard_icon_wp);
+ }
+ @Override
+ public Boolean isSelected(){
+ return WPActionBarActivity.this instanceof DashboardActivity;
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof DashboardActivity))
+ mShouldFinish = true;
+ Intent intent = new Intent(WPActionBarActivity.this, DashboardActivity.class);
+ intent.putExtra("loadAdmin", true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+ }
+
+ private class NotificationsMenuItem extends MenuDrawerItem {
+ NotificationsMenuItem(){
+ super(NOTIFICATIONS_ACTIVITY, R.string.notifications, R.drawable.dashboard_icon_notifications);
+ }
+ @Override
+ public Boolean isVisible(){
+ return WordPress.hasValidWPComCredentials(WPActionBarActivity.this);
+ }
+ @Override
+ public Boolean isSelected(){
+ return WPActionBarActivity.this instanceof NotificationsActivity;
+ }
+ @Override
+ public void onSelectItem(){
+ if (!(WPActionBarActivity.this instanceof NotificationsActivity))
+ mShouldFinish = true;
+ Intent intent = new Intent(WPActionBarActivity.this, NotificationsActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityWithDelay(intent);
+ }
+ }
}
diff --git a/src/org/wordpress/android/ui/accounts/AddAccountActivity.java b/src/org/wordpress/android/ui/accounts/AccountSetupActivity.java
similarity index 63%
rename from src/org/wordpress/android/ui/accounts/AddAccountActivity.java
rename to src/org/wordpress/android/ui/accounts/AccountSetupActivity.java
index b5bf114a9df8..a961e20f9665 100644
--- a/src/org/wordpress/android/ui/accounts/AddAccountActivity.java
+++ b/src/org/wordpress/android/ui/accounts/AccountSetupActivity.java
@@ -13,11 +13,13 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.preference.PreferenceManager;
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.LayoutInflater;
@@ -37,6 +39,7 @@
import android.widget.Toast;
import org.apache.http.conn.HttpHostConnectException;
+import org.wordpress.android.models.Blog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlrpc.android.ApiHelper;
import org.xmlrpc.android.XMLRPCClient;
@@ -45,28 +48,28 @@
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
+import org.wordpress.android.WordPressDB;
import org.wordpress.android.util.AlertUtil;
import org.wordpress.android.util.EscapeUtils;
-public class AddAccountActivity extends Activity implements OnClickListener {
+public class AccountSetupActivity extends Activity implements OnClickListener {
private static final String URL_WORDPRESS = "http://wordpress.com";
- private XMLRPCClient client;
- private String blogURL, xmlrpcURL;
- private ProgressDialog pd;
- private String httpuser = "";
- private String httppassword = "";
- private boolean wpcom = false;
- private int blogCtr = 0;
- private ArrayList aBlogNames = new ArrayList();
- private boolean isCustomURL = false;
+ private XMLRPCClient mClient;
+ private String mBlogURL, mXmlrpcURL;
+ private ProgressDialog mProgressDialog;
+ private String mHttpuser = "";
+ private String mHttppassword = "";
+ private boolean mIsWpcom = false, mAuthOnly = false;
+ private int mBlogCtr = 0;
+ private ArrayList mBlogNames = new ArrayList();
+ private boolean mIsCustomURL = false;
private ConnectivityManager mSystemService;
private EditText mUrlEdit;
private EditText mUsernameEdit;
private EditText mPasswordEdit;
private Button mSettingsButton;
- private Button mCancelButton;
private Button mSaveButton;
private Button mSignUpButton;
@@ -75,7 +78,6 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_account);
mSettingsButton = (Button) findViewById(R.id.settingsButton);
- mCancelButton = (Button) findViewById(R.id.cancel);
mSaveButton = (Button) findViewById(R.id.save);
mSignUpButton = (Button) findViewById(R.id.wordpressdotcom);
mUrlEdit = (EditText) findViewById(R.id.url);
@@ -88,27 +90,39 @@ protected void onCreate(Bundle savedInstanceState) {
Bundle extras = getIntent().getExtras();
if (extras != null) {
- wpcom = extras.getBoolean("wpcom", false);
+ mIsWpcom = extras.getBoolean("wpcom", false);
+ mAuthOnly = extras.getBoolean("auth-only", false);
String username = extras.getString("username");
if (username != null) {
mUsernameEdit.setText(username);
}
}
- if (wpcom) {
+ if (mIsWpcom) {
((EditText) findViewById(R.id.url)).setVisibility(View.GONE);
} else {
ImageView logo = (ImageView) findViewById(R.id.wpcomLogo);
logo.setImageDrawable(getResources().getDrawable(R.drawable.wplogo));
}
- if (wpcom)
+ if (mIsWpcom) {
mSettingsButton.setVisibility(View.GONE);
- else
+ if (!mAuthOnly && WordPress.hasValidWPComCredentials(this)) {
+ setupBlogs();
+ }
+ }
+ else {
+ if (mAuthOnly) {
+ Blog currentBlog = WordPress.getCurrentBlog();
+ if (currentBlog != null) {
+ mUrlEdit.setText(currentBlog.getHomeURL());
+ mUsernameEdit.requestFocus();
+ }
+ }
mSettingsButton.setOnClickListener(this);
+ }
mSaveButton.setOnClickListener(this);
- mCancelButton.setOnClickListener(this);
mSignUpButton.setOnClickListener(this);
}
@@ -117,8 +131,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == R.id.settingsButton) {
if (resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
- httpuser = extras.getString("httpuser");
- httppassword = extras.getString("httppassword");
+ mHttpuser = extras.getString("httpuser");
+ mHttppassword = extras.getString("httppassword");
}
}
}
@@ -131,61 +145,61 @@ public void onBackPressed() {
private void configureAccount() {
- if (wpcom) {
- blogURL = URL_WORDPRESS;
+ if (mIsWpcom) {
+ mBlogURL = URL_WORDPRESS;
} else {
- blogURL = mUrlEdit.getText().toString().trim();
+ mBlogURL = mUrlEdit.getText().toString().trim();
}
final String username = mUsernameEdit.getText().toString().trim();
final String password = mPasswordEdit.getText().toString().trim();
- if (blogURL.equals("") || username.equals("") || password.equals("")) {
- pd.dismiss();
- AlertUtil.showAlert(AddAccountActivity.this, R.string.required_fields, R.string.url_username_password_required);
+ if (mBlogURL.equals("") || username.equals("") || password.equals("")) {
+ mProgressDialog.dismiss();
+ AlertUtil.showAlert(AccountSetupActivity.this, R.string.required_fields, R.string.url_username_password_required);
return;
}
// add http to the beginning of the URL if needed
- if (!(blogURL.toLowerCase().startsWith("http://")) && !(blogURL.toLowerCase().startsWith("https://"))) {
- blogURL = "http://" + blogURL; // default to http
+ if (!(mBlogURL.toLowerCase().startsWith("http://")) && !(mBlogURL.toLowerCase().startsWith("https://"))) {
+ mBlogURL = "http://" + mBlogURL; // default to http
}
- if (!URLUtil.isValidUrl(blogURL)) {
- pd.dismiss();
- AlertUtil.showAlert(AddAccountActivity.this, R.string.invalid_url, R.string.invalid_url_message);
+ if (!URLUtil.isValidUrl(mBlogURL)) {
+ mProgressDialog.dismiss();
+ AlertUtil.showAlert(AccountSetupActivity.this, R.string.invalid_url, R.string.invalid_url_message);
return;
}
// attempt to get the XMLRPC URL via RSD
- String rsdUrl = getRSDMetaTagHrefRegEx(blogURL);
+ String rsdUrl = getRSDMetaTagHrefRegEx(mBlogURL);
if (rsdUrl == null) {
- rsdUrl = getRSDMetaTagHref(blogURL);
+ rsdUrl = getRSDMetaTagHref(mBlogURL);
}
if (rsdUrl != null) {
- xmlrpcURL = ApiHelper.getXMLRPCUrl(rsdUrl);
- if (xmlrpcURL == null)
- xmlrpcURL = rsdUrl.replace("?rsd", "");
+ mXmlrpcURL = ApiHelper.getXMLRPCUrl(rsdUrl);
+ if (mXmlrpcURL == null)
+ mXmlrpcURL = rsdUrl.replace("?rsd", "");
} else {
- isCustomURL = false;
+ mIsCustomURL = false;
// try the user entered path
try {
- client = new XMLRPCClient(blogURL, httpuser, httppassword);
+ mClient = new XMLRPCClient(mBlogURL, mHttpuser, mHttppassword);
try {
- client.call("system.listMethods");
- xmlrpcURL = blogURL;
- isCustomURL = true;
+ mClient.call("system.listMethods");
+ mXmlrpcURL = mBlogURL;
+ mIsCustomURL = true;
} catch (XMLRPCException e) {
// guess the xmlrpc path
- String guessURL = blogURL;
+ String guessURL = mBlogURL;
if (guessURL.substring(guessURL.length() - 1, guessURL.length()).equals("/")) {
guessURL = guessURL.substring(0, guessURL.length() - 1);
}
guessURL += "/xmlrpc.php";
- client = new XMLRPCClient(guessURL, httpuser, httppassword);
+ mClient = new XMLRPCClient(guessURL, mHttpuser, mHttppassword);
try {
- client.call("system.listMethods");
- xmlrpcURL = guessURL;
+ mClient.call("system.listMethods");
+ mXmlrpcURL = guessURL;
} catch (XMLRPCException ex) {
}
}
@@ -193,17 +207,45 @@ private void configureAccount() {
}
}
- if (xmlrpcURL == null) {
- pd.dismiss();
- AlertUtil.showAlert(AddAccountActivity.this, R.string.error, R.string.no_site_error);
+ if (mXmlrpcURL == null) {
+ mProgressDialog.dismiss();
+ AlertUtil.showAlert(AccountSetupActivity.this, R.string.error, R.string.no_site_error);
} else {
// verify settings
- client = new XMLRPCClient(xmlrpcURL, httpuser, httppassword);
+ mClient = new XMLRPCClient(mXmlrpcURL, mHttpuser, mHttppassword);
XMLRPCMethod method = new XMLRPCMethod("wp.getUsersBlogs", new XMLRPCMethodCallback() {
public void callFinished(Object[] result) {
+ Blog currentBlog = WordPress.getCurrentBlog();
+ if (mIsWpcom) {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(AccountSetupActivity.this);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putString(WordPress.WPCOM_USERNAME_PREFERENCE, username);
+ editor.putString(WordPress.WPCOM_PASSWORD_PREFERENCE, WordPressDB.encryptPassword(password));
+ editor.commit();
+ // fire off a request to get an access token
+ WordPress.restClient.get("me", null, null);
+ }
+
+ if (mAuthOnly) {
+ if (currentBlog != null) {
+ if (mIsWpcom) {
+ WordPress.wpDB.updateWPComCredentials(username, password);
+ if (currentBlog != null && currentBlog.isDotcomFlag()) {
+ currentBlog.setPassword(password);
+ }
+ } else {
+ currentBlog.setPassword(password);
+ }
+ currentBlog.save("");
+ }
+ setResult(RESULT_OK);
+ finish();
+ return;
+ }
+
final String[] blogNames = new String[result.length];
final String[] urls = new String[result.length];
final String[] homeURLs = new String[result.length];
@@ -211,72 +253,68 @@ public void callFinished(Object[] result) {
final boolean[] wpcoms = new boolean[result.length];
final String[] wpVersions = new String[result.length];
Map contentHash = new HashMap();
- blogCtr = 0;
+ mBlogCtr = 0;
// loop this!
for (int ctr = 0; ctr < result.length; ctr++) {
contentHash = (Map) result[ctr];
- // check if this blog is already set up
- boolean match = false;
- String matchBlogName = contentHash.get("blogName").toString();
- if (matchBlogName.length() == 0) {
- matchBlogName = contentHash.get("url").toString();
+
+ String blogName = contentHash.get("blogName").toString();
+ if (blogName.length() == 0) {
+ blogName = contentHash.get("url").toString();
+ }
+ blogNames[mBlogCtr] = blogName;
+
+ if (mIsCustomURL)
+ urls[mBlogCtr] = mBlogURL;
+ else
+ urls[mBlogCtr] = contentHash.get("xmlrpc").toString();
+ homeURLs[mBlogCtr] = contentHash.get("url").toString();
+ blogIds[mBlogCtr] = Integer.parseInt(contentHash.get("blogid").toString());
+ String blogURL = urls[mBlogCtr];
+
+ mBlogNames.add(EscapeUtils.unescapeHtml(blogNames[mBlogCtr]));
+
+ boolean wpcomFlag = false;
+ // check for wordpress.com
+ if (blogURL.toLowerCase().contains("wordpress.com")) {
+ wpcomFlag = true;
}
- match = WordPress.wpDB.checkMatch(matchBlogName, contentHash.get("xmlrpc").toString(), username);
- if (!match) {
- blogNames[blogCtr] = matchBlogName;
- if (isCustomURL)
- urls[blogCtr] = blogURL;
- else
- urls[blogCtr] = contentHash.get("xmlrpc").toString();
- homeURLs[blogCtr] = contentHash.get("url").toString();
- blogIds[blogCtr] = Integer.parseInt(contentHash.get("blogid").toString());
- String blogURL = urls[blogCtr];
-
- aBlogNames.add(EscapeUtils.unescapeHtml(blogNames[blogCtr]));
-
- boolean wpcomFlag = false;
- // check for wordpress.com
- if (blogURL.toLowerCase().contains("wordpress.com")) {
- wpcomFlag = true;
+ wpcoms[mBlogCtr] = wpcomFlag;
+
+ // attempt to get the software version
+ String wpVersion = "";
+ if (!wpcomFlag) {
+ Map hPost = new HashMap();
+ hPost.put("software_version", "software_version");
+ Object[] vParams = { 1, username, password, hPost };
+ Object versionResult = new Object();
+ try {
+ versionResult = (Object) mClient.call("wp.getOptions", vParams);
+ } catch (XMLRPCException e) {
}
- wpcoms[blogCtr] = wpcomFlag;
-
- // attempt to get the software version
- String wpVersion = "";
- if (!wpcomFlag) {
- Map hPost = new HashMap();
- hPost.put("software_version", "software_version");
- Object[] vParams = { 1, username, password, hPost };
- Object versionResult = new Object();
- try {
- versionResult = (Object) client.call("wp.getOptions", vParams);
- } catch (XMLRPCException e) {
- }
- if (versionResult != null) {
- try {
- contentHash = (Map) versionResult;
- Map, ?> sv = (Map, ?>) contentHash.get("software_version");
- wpVersion = sv.get("value").toString();
- } catch (Exception e) {
- }
+ if (versionResult != null) {
+ try {
+ contentHash = (Map) versionResult;
+ Map, ?> sv = (Map, ?>) contentHash.get("software_version");
+ wpVersion = sv.get("value").toString();
+ } catch (Exception e) {
}
- } else {
- wpVersion = "3.4";
}
-
- wpVersions[blogCtr] = wpVersion;
-
- blogCtr++;
+ } else {
+ wpVersion = "3.5";
}
+
+ wpVersions[mBlogCtr] = wpVersion;
+ mBlogCtr++;
} // end loop
- pd.dismiss();
- if (blogCtr == 0) {
+ mProgressDialog.dismiss();
+ if (mBlogCtr == 0) {
String additionalText = "";
if (result.length > 0) {
additionalText = getString(R.string.additional);
}
- AlertUtil.showAlert(AddAccountActivity.this, R.string.no_blogs_found,
+ AlertUtil.showAlert(AccountSetupActivity.this, R.string.no_blogs_found,
String.format(getString(R.string.no_blogs_message), additionalText), getString(R.string.ok),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
@@ -286,19 +324,19 @@ public void onClick(DialogInterface dialog, int whichButton) {
} else {
// take them to the blog selection screen if
// there's more than one blog
- if (blogCtr > 1) {
+ if (mBlogCtr > 1) {
- LayoutInflater inflater = (LayoutInflater) AddAccountActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LayoutInflater inflater = (LayoutInflater) AccountSetupActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final ListView lv = (ListView) inflater.inflate(R.layout.select_blogs_list, null);
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
lv.setItemsCanFocus(false);
- ArrayAdapter blogs = new ArrayAdapter(AddAccountActivity.this, R.layout.blogs_row,
- aBlogNames);
+ ArrayAdapter blogs = new ArrayAdapter(AccountSetupActivity.this, R.layout.blogs_row,
+ mBlogNames);
lv.setAdapter(blogs);
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AddAccountActivity.this);
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AccountSetupActivity.this);
dialogBuilder.setTitle(R.string.select_blogs);
dialogBuilder.setView(lv);
dialogBuilder.setNegativeButton(R.string.add_selected, new DialogInterface.OnClickListener() {
@@ -309,9 +347,13 @@ public void onClick(DialogInterface dialog, int whichButton) {
if (selectedItems.get(selectedItems.keyAt(i)) == true) {
int rowID = selectedItems.keyAt(i);
long blogID = -1;
- blogID = WordPress.wpDB.addAccount(urls[rowID], homeURLs[rowID], blogNames[rowID], username, password, httpuser,
- httppassword, "Above Text", false, false, "500", 5, false, blogIds[rowID],
- wpcoms[rowID], wpVersions[rowID]);
+
+ blogID = WordPress.wpDB.checkMatch(blogNames[rowID], urls[rowID], username, password);
+ if (blogID == -1) {
+ blogID = WordPress.wpDB.addAccount(urls[rowID], homeURLs[rowID], blogNames[rowID], username, password, mHttpuser,
+ mHttppassword, "Above Text", false, false, "500", 5, false, blogIds[rowID],
+ wpcoms[rowID], wpVersions[rowID]);
+ }
//Set the first blog in the list to the currentBlog
if (i == 0) {
if (blogID >= 0) {
@@ -329,10 +371,13 @@ public void onClick(DialogInterface dialog, int whichButton) {
dialogBuilder.setPositiveButton(R.string.add_all, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- for (int i = 0; i < blogCtr; i++) {
- long blogID = -1;
- blogID = WordPress.wpDB.addAccount(urls[i], homeURLs[i], blogNames[i], username, password, httpuser, httppassword,
- "Above Text", false, false, "500", 5, false, blogIds[i], wpcoms[i], wpVersions[i]);
+ for (int i = 0; i < mBlogCtr; i++) {
+ long blogID;
+ blogID = WordPress.wpDB.checkMatch(blogNames[i], urls[i], username, password);
+ if (blogID == -1) {
+ blogID = WordPress.wpDB.addAccount(urls[i], homeURLs[i], blogNames[i], username, password, mHttpuser, mHttppassword,
+ "Above Text", false, false, "500", 5, false, blogIds[i], wpcoms[i], wpVersions[i]);
+ }
//Set the first blog in the list to the currentBlog
if (i == 0) {
if (blogID >= 0) {
@@ -371,8 +416,12 @@ public void onItemClick(AdapterView> arg0, View arg1, int arg2, long arg3) {
});
} else {
- long blogID = WordPress.wpDB.addAccount(urls[0], homeURLs[0], blogNames[0], username, password, httpuser, httppassword, "Above Text",
+ long blogID;
+ blogID = WordPress.wpDB.checkMatch(blogNames[0], urls[0], username, password);
+ if (blogID == -1) {
+ blogID = WordPress.wpDB.addAccount(urls[0], homeURLs[0], blogNames[0], username, password, mHttpuser, mHttppassword, "Above Text",
false, false, "500", 5, false, blogIds[0], wpcoms[0], wpVersions[0]);
+ }
if (blogID >= 0) {
WordPress.setCurrentBlog((int) blogID);
}
@@ -419,7 +468,7 @@ public void call(Object[] params) {
public void run() {
try {
final Object[] result;
- result = (Object[]) client.call(method, params);
+ result = (Object[]) mClient.call(method, params);
handler.post(new Runnable() {
public void run() {
callBack.callFinished(result);
@@ -429,20 +478,20 @@ public void run() {
handler.post(new Runnable() {
public void run() {
// e.printStackTrace();
- pd.dismiss();
+ mProgressDialog.dismiss();
String message = e.getMessage();
if (message.contains("code 403")) {
// invalid login
Thread shake = new Thread() {
public void run() {
- Animation shake = AnimationUtils.loadAnimation(AddAccountActivity.this, R.anim.shake);
+ Animation shake = AnimationUtils.loadAnimation(AccountSetupActivity.this, R.anim.shake);
findViewById(R.id.section1).startAnimation(shake);
- Toast.makeText(AddAccountActivity.this, getString(R.string.invalid_login), Toast.LENGTH_SHORT).show();
+ Toast.makeText(AccountSetupActivity.this, getString(R.string.invalid_login), Toast.LENGTH_SHORT).show();
}
};
runOnUiThread(shake);
} else {
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AddAccountActivity.this);
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AccountSetupActivity.this);
dialogBuilder.setTitle(getString(R.string.connection_error));
if (message.contains("404"))
message = getString(R.string.xmlrpc_error);
@@ -464,12 +513,12 @@ public void onClick(DialogInterface dialog, int whichButton) {
public void run() {
Throwable couse = e.getCause();
e.printStackTrace();
- pd.dismiss();
+ mProgressDialog.dismiss();
String message = e.getMessage();
if (couse instanceof HttpHostConnectException) {
} else {
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AddAccountActivity.this);
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(AccountSetupActivity.this);
dialogBuilder.setTitle(getString(R.string.connection_error));
if (message.contains("404"))
message = getString(R.string.xmlrpc_error);
@@ -566,31 +615,38 @@ else if (attrName.equals("href"))
public void onClick(View v) {
int id = v.getId();
if (id == R.id.save) {
- if (mSystemService.getActiveNetworkInfo() == null) {
- AlertUtil.showAlert(AddAccountActivity.this, R.string.no_network_title, R.string.no_network_message);
- } else {
- pd = ProgressDialog.show(AddAccountActivity.this, getString(R.string.account_setup), getString(R.string.attempting_configure),
- true, false);
-
- Thread action = new Thread() {
- public void run() {
- Looper.prepare();
- configureAccount();
- Looper.loop();
- }
- };
- action.start();
- }
- } else if (id == R.id.cancel) {
- setResult(RESULT_CANCELED);
- finish();
+ setupBlogs();
} else if (id == R.id.settingsButton) {
- Intent settings = new Intent(AddAccountActivity.this, AddAcountSettingsActivity.class);
- settings.putExtra("httpuser", httpuser);
- settings.putExtra("httppassword", httppassword);
+ Intent settings = new Intent(AccountSetupActivity.this, AdditionalSettingsActivity.class);
+ settings.putExtra("httpuser", mHttpuser);
+ settings.putExtra("httppassword", mHttppassword);
startActivityForResult(settings, R.id.settingsButton);
} else if (id == R.id.wordpressdotcom) {
- startActivity(new Intent(AddAccountActivity.this, SignupActivity.class));
+ startActivity(new Intent(AccountSetupActivity.this, SignupActivity.class));
+ }
+ }
+
+ private void setupBlogs() {
+ if (mSystemService.getActiveNetworkInfo() == null) {
+ AlertUtil.showAlert(AccountSetupActivity.this, R.string.no_network_title, R.string.no_network_message);
+ } else {
+ mProgressDialog = ProgressDialog.show(AccountSetupActivity.this, getString(R.string.account_setup), getString(R.string.attempting_configure),
+ true, false);
+
+ if (mIsWpcom && WordPress.hasValidWPComCredentials(AccountSetupActivity.this)) {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(AccountSetupActivity.this);
+ mUsernameEdit.setText(settings.getString(WordPress.WPCOM_USERNAME_PREFERENCE, ""));
+ mPasswordEdit.setText(WordPressDB.decryptPassword(settings.getString(WordPress.WPCOM_PASSWORD_PREFERENCE, "")));
+ }
+
+ Thread action = new Thread() {
+ public void run() {
+ Looper.prepare();
+ configureAccount();
+ Looper.loop();
+ }
+ };
+ action.start();
}
}
}
diff --git a/src/org/wordpress/android/ui/accounts/AddAcountSettingsActivity.java b/src/org/wordpress/android/ui/accounts/AdditionalSettingsActivity.java
similarity index 97%
rename from src/org/wordpress/android/ui/accounts/AddAcountSettingsActivity.java
rename to src/org/wordpress/android/ui/accounts/AdditionalSettingsActivity.java
index 36e39135d8cc..7ad754ea1572 100644
--- a/src/org/wordpress/android/ui/accounts/AddAcountSettingsActivity.java
+++ b/src/org/wordpress/android/ui/accounts/AdditionalSettingsActivity.java
@@ -11,7 +11,7 @@
import org.wordpress.android.R;
-public class AddAcountSettingsActivity extends Activity {
+public class AdditionalSettingsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/src/org/wordpress/android/ui/accounts/NewAccountActivity.java b/src/org/wordpress/android/ui/accounts/NewAccountActivity.java
index ba690de1cdd9..3267735eabbb 100644
--- a/src/org/wordpress/android/ui/accounts/NewAccountActivity.java
+++ b/src/org/wordpress/android/ui/accounts/NewAccountActivity.java
@@ -29,7 +29,7 @@ public void onClick(View v) {
Button dotComButton = (Button) findViewById(R.id.dotcomExisting);
dotComButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
- Intent i = new Intent(NewAccountActivity.this, AddAccountActivity.class);
+ Intent i = new Intent(NewAccountActivity.this, AccountSetupActivity.class);
i.putExtra("wpcom", true);
startActivityForResult(i, EXISTING_COM_ACCOUNT_REQUEST);
}
@@ -38,7 +38,7 @@ public void onClick(View v) {
Button dotOrgButton = (Button) findViewById(R.id.dotorgExisting);
dotOrgButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
- Intent i = new Intent(NewAccountActivity.this, AddAccountActivity.class);
+ Intent i = new Intent(NewAccountActivity.this, AccountSetupActivity.class);
startActivityForResult(i, EXISTING_ORG_ACCOUNT_REQUEST);
}
});
@@ -53,7 +53,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && data != null) {
String username = data.getStringExtra("username");
if (username != null) {
- Intent i = new Intent(NewAccountActivity.this, AddAccountActivity.class);
+ Intent i = new Intent(NewAccountActivity.this, AccountSetupActivity.class);
i.putExtra("wpcom", true);
i.putExtra("username", username);
startActivityForResult(i, EXISTING_COM_ACCOUNT_REQUEST);
diff --git a/src/org/wordpress/android/ui/comments/CommentFragment.java b/src/org/wordpress/android/ui/comments/CommentFragment.java
index 02cce63478c7..f7a01a8c7c04 100644
--- a/src/org/wordpress/android/ui/comments/CommentFragment.java
+++ b/src/org/wordpress/android/ui/comments/CommentFragment.java
@@ -1,37 +1,32 @@
package org.wordpress.android.ui.comments;
import java.io.InputStream;
-import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.support.v4.app.Fragment;
import android.text.util.Linkify;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.TextView;
+import com.android.volley.toolbox.NetworkImageView;
+
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.models.Comment;
+import org.wordpress.android.util.StringUtils;
public class CommentFragment extends Fragment {
- private Drawable d;
private OnCommentStatusChangeListener onCommentStatusChangeListener;
public void onAttach(Activity activity) {
@@ -46,40 +41,6 @@ public void onAttach(Activity activity) {
}
}
- private Handler handler = new Handler() {
-
- public void handleMessage(Message msg) {
-
- super.handleMessage(msg);
-
- try {
- final ImageView ivGravatar = (ImageView) getActivity()
- .findViewById(R.id.gravatar);
- ivGravatar.setImageDrawable(d);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
-
- };
-
- private void getGravatar(final String gravatarURL) {
-
- new Thread() {
-
- public void run() {
-
- d = getDrawable(gravatarURL);
-
- handler.sendEmptyMessage(0);
-
- }
-
- }.start();
-
- }
-
@Override
public void onResume() {
super.onResume();
@@ -219,23 +180,6 @@ public Drawable getDrawable(String imgUrl) {
}
}
- public static String getMd5Hash(String input) {
- try {
- MessageDigest md = MessageDigest.getInstance("MD5");
- byte[] messageDigest = md.digest(input.getBytes());
- BigInteger number = new BigInteger(1, messageDigest);
- String md5 = number.toString(16);
-
- while (md5.length() < 32)
- md5 = "0" + md5;
-
- return md5;
- } catch (NoSuchAlgorithmException e) {
- Log.e("MD5", e.getMessage());
- return null;
- }
- }
-
@Override
public void onConfigurationChanged(Configuration newConfig) {
// ignore orientation change
@@ -247,9 +191,11 @@ public void loadComment(Comment comment) {
WordPress.currentComment = comment;
final String gravatarURL = "http://gravatar.com/avatar/"
- + getMd5Hash(comment.authorEmail) + "?s=200&d=mm";
+ + StringUtils.getMd5Hash(comment.authorEmail) + "?s=200&d=mm";
- getGravatar(gravatarURL);
+ NetworkImageView gravatar = (NetworkImageView)getActivity().findViewById(R.id.gravatar);
+ gravatar.setDefaultImageResId(R.drawable.placeholder);
+ gravatar.setImageUrl(gravatarURL, WordPress.imageLoader);
TextView tvName = (TextView) getActivity().findViewById(
R.id.commentDetailName);
@@ -332,11 +278,9 @@ public void clearContent() {
R.id.commentDetailPost);
tvPost.setText("");
- ImageView ivGravatar = (ImageView) getActivity()
+ NetworkImageView ivGravatar = (NetworkImageView) getActivity()
.findViewById(R.id.gravatar);
ivGravatar.setImageDrawable(null);
-
-
}
@Override
diff --git a/src/org/wordpress/android/ui/comments/CommentsActivity.java b/src/org/wordpress/android/ui/comments/CommentsActivity.java
index 4fcf7bf66fbb..066968c97750 100644
--- a/src/org/wordpress/android/ui/comments/CommentsActivity.java
+++ b/src/org/wordpress/android/ui/comments/CommentsActivity.java
@@ -8,7 +8,6 @@
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Looper;
import android.support.v4.app.FragmentManager;
@@ -71,6 +70,7 @@ public void onCreate(Bundle savedInstanceState) {
}
FragmentManager fm = getSupportFragmentManager();
+ fm.addOnBackStackChangedListener(mOnBackStackChangedListener);
commentList = (CommentsListFragment) fm.findFragmentById(R.id.commentList);
WordPress.currentComment = null;
@@ -91,7 +91,7 @@ public void onBlogChanged() {
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getSupportMenuInflater();
- inflater.inflate(R.menu.refresh_only, menu);
+ inflater.inflate(R.menu.basic_menu, menu);
refreshMenuItem = menu.findItem(R.id.menu_refresh);
if (shouldAnimateRefreshButton) {
shouldAnimateRefreshButton = false;
@@ -119,6 +119,13 @@ public boolean onOptionsItemSelected(final MenuItem item) {
return super.onOptionsItemSelected(item);
}
+ private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
+ public void onBackStackChanged() {
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0)
+ mMenuDrawer.setDrawerIndicatorEnabled(true);
+ }
+ };
+
protected void popCommentDetail() {
FragmentManager fm = getSupportFragmentManager();
CommentFragment f = (CommentFragment) fm
@@ -189,6 +196,7 @@ public void onCommentSelected(Comment comment) {
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
+ mMenuDrawer.setDrawerIndicatorEnabled(false);
} else {
f.loadComment(comment);
}
@@ -212,19 +220,48 @@ public void run() {
}
}.start();
} else if (status.equals("delete")) {
- showDialog(ID_DIALOG_DELETING);
- // pop out of the detail view if on a smaller screen
- FragmentManager fm = getSupportFragmentManager();
- CommentFragment f = (CommentFragment) fm
- .findFragmentById(R.id.commentDetail);
- if (f == null) {
- fm.popBackStack();
- }
- new Thread() {
+ Thread action3 = new Thread() {
public void run() {
- deleteComment(commentID);
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(
+ CommentsActivity.this);
+ dialogBuilder.setTitle(getResources().getText(
+ R.string.confirm_delete));
+ dialogBuilder.setMessage(getResources().getText(R.string.confirm_delete_data));
+ dialogBuilder.setPositiveButton(getString(R.string.yes),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ showDialog(ID_DIALOG_DELETING);
+ // pop out of the detail view if on a smaller screen
+ FragmentManager fm = getSupportFragmentManager();
+ CommentFragment f = (CommentFragment) fm
+ .findFragmentById(R.id.commentDetail);
+ if (f == null) {
+ fm.popBackStack();
+ }
+ new Thread() {
+ public void run() {
+ deleteComment(commentID);
+ }
+ }.start();
+
+
+ }
+ });
+ dialogBuilder.setNegativeButton(getString(R.string.no),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ //Don't delete Comment
+ }
+ });
+ dialogBuilder.setCancelable(true);
+ if (!isFinishing()) {
+ dialogBuilder.create().show();
+ }
}
- }.start();
+ };
+ runOnUiThread(action3);
} else if (status.equals("reply")) {
Intent i = new Intent(CommentsActivity.this, AddCommentActivity.class);
@@ -285,7 +322,7 @@ public void run() {
runOnUiThread(action);
Thread action2 = new Thread() {
public void run() {
- commentList.thumbs.notifyDataSetChanged();
+ commentList.getListView().invalidateViews();
}
};
runOnUiThread(action2);
@@ -539,12 +576,4 @@ private void attemptToSelectComment() {
}
}
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- //titleBar.switchDashboardLayout(newConfig.orientation);
-
- }
}
diff --git a/src/org/wordpress/android/ui/comments/CommentsListFragment.java b/src/org/wordpress/android/ui/comments/CommentsListFragment.java
index 043859505ef7..8c33bb187106 100644
--- a/src/org/wordpress/android/ui/comments/CommentsListFragment.java
+++ b/src/org/wordpress/android/ui/comments/CommentsListFragment.java
@@ -1,9 +1,6 @@
package org.wordpress.android.ui.comments;
-import java.math.BigInteger;
import java.net.URI;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -22,7 +19,6 @@
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ListFragment;
-import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
@@ -40,17 +36,13 @@
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
-import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewSwitcher;
-import com.commonsware.cwac.cache.SimpleWebImageCache;
-import com.commonsware.cwac.thumbnail.ThumbnailAdapter;
-import com.commonsware.cwac.thumbnail.ThumbnailBus;
-import com.commonsware.cwac.thumbnail.ThumbnailMessage;
+import com.android.volley.toolbox.NetworkImageView;
import org.xmlrpc.android.ApiHelper;
import org.xmlrpc.android.XMLRPCClient;
@@ -61,11 +53,10 @@
import org.wordpress.android.WordPress;
import org.wordpress.android.models.Comment;
import org.wordpress.android.util.EscapeUtils;
+import org.wordpress.android.util.StringUtils;
import org.wordpress.android.util.WPAlertDialogFragment;
public class CommentsListFragment extends ListFragment {
- private static final int[] IMAGE_IDS = { R.id.avatar };
- public ThumbnailAdapter thumbs = null;
public ArrayList model = null;
private XMLRPCClient client;
private String accountName = "", moderateErrorMsg = "";
@@ -134,13 +125,6 @@ public void onClick(View v) {
switcher.addView(footer);
switcher.addView(progress);
- /*
- * if (fromNotification) // dismiss the notification { //
- * NotificationManager nm = (NotificationManager) //
- * getSystemService(NOTIFICATION_SERVICE); // nm.cancel(22 +
- * Integer.valueOf(id)); // loadComments(false, false); }
- */
-
getActivity().setTitle(accountName + " - Moderate Comments");
Button deleteComments = (Button) v.findViewById(R.id.bulkDeleteComment);
@@ -260,13 +244,13 @@ public void run() {
Toast.LENGTH_SHORT).show();
checkedCommentTotal = 0;
hideModerationBar();
- thumbs.notifyDataSetChanged();
+ getListView().invalidateViews();
} else {
// there was an xmlrpc error
if (!getActivity().isFinishing()) {
checkedCommentTotal = 0;
hideModerationBar();
- thumbs.notifyDataSetChanged();
+ getListView().invalidateViews();
FragmentTransaction ft = getFragmentManager()
.beginTransaction();
WPAlertDialogFragment alert = WPAlertDialogFragment
@@ -389,28 +373,17 @@ public boolean loadComments(boolean refresh, boolean loadMore) {
dateCreatedFormatted, comment, status, postTitle,
authorURL, authorEmail, URI
.create("http://gravatar.com/avatar/"
- + getMd5Hash(authorEmail.trim())
+ + StringUtils.getMd5Hash(authorEmail.trim())
+ "?s=140&d=404")));
}
if (!refreshOnly) {
- try {
- ThumbnailBus bus = new ThumbnailBus();
- thumbs = new ThumbnailAdapter(
- getActivity(),
- new CommentAdapter(),
- new SimpleWebImageCache(
- null, null, 101, bus), IMAGE_IDS);
- } catch (Exception e1) {
- e1.printStackTrace();
- }
-
ListView listView = this.getListView();
listView.removeFooterView(switcher);
if (loadedPosts.size() % 30 == 0) {
listView.addFooterView(switcher);
}
- setListAdapter(thumbs);
+ setListAdapter(new CommentAdapter());
listView.setOnItemClickListener(new OnItemClickListener() {
@@ -419,7 +392,7 @@ public void onItemClick(AdapterView> arg0, View view,
selectedPosition = position;
Comment comment = model.get((int) id);
onCommentSelectedListener.onCommentSelected(comment);
- thumbs.notifyDataSetChanged();
+ getListView().invalidateViews();
}
});
@@ -475,9 +448,7 @@ public void onCreateContextMenu(ContextMenu menu, View v,
}
});
} else {
- if (thumbs != null) {
- thumbs.notifyDataSetChanged();
- }
+ getListView().invalidateViews();
}
if (this.shouldSelectAfterLoad) {
@@ -549,20 +520,6 @@ public void onDestroy() {
}
- /*
- * @Override public Object onRetainNonConfigurationInstance() { return
- * (model); }
- */
-
- private void goBlooey(Throwable t) {
- Log.e("WordPress", "Exception!", t);
-
- FragmentTransaction ft = getFragmentManager().beginTransaction();
- WPAlertDialogFragment alert = WPAlertDialogFragment.newInstance(t
- .toString());
- alert.show(ft, "alert");
- }
-
class CommentAdapter extends ArrayAdapter {
int sdk_version = 7;
@@ -585,8 +542,9 @@ public View getView(int position, View convertView, ViewGroup parent) {
if (row == null) {
LayoutInflater inflater = getActivity().getLayoutInflater();
-
row = inflater.inflate(R.layout.comment_row, null);
+ NetworkImageView avatar = (NetworkImageView)row.findViewById(R.id.avatar);
+ avatar.setDefaultImageResId(R.drawable.placeholder);
wrapper = new CommentEntryWrapper(row);
row.setTag(wrapper);
} else {
@@ -605,7 +563,7 @@ class CommentEntryWrapper {
private TextView comment = null;
private TextView status = null;
private TextView postTitle = null;
- private ImageView avatar = null;
+ private NetworkImageView avatar = null;
private View row = null;
private CheckBox bulkCheck = null;
private RelativeLayout bulkEditGroup = null;
@@ -669,14 +627,7 @@ public void onClick(View arg0) {
}
});
- if (s.profileImageUrl != null) {
- try {
- getAvatar().setImageResource(R.drawable.placeholder);
- getAvatar().setTag(s.profileImageUrl.toString());
- } catch (Throwable t) {
- goBlooey(t);
- }
- }
+ getAvatar().setImageUrl(s.profileImageUrl.toString(), WordPress.imageLoader);
}
TextView getName() {
@@ -721,9 +672,9 @@ TextView getPostTitle() {
return (postTitle);
}
- ImageView getAvatar() {
+ NetworkImageView getAvatar() {
if (avatar == null) {
- avatar = (ImageView) row.findViewById(R.id.avatar);
+ avatar = (NetworkImageView) row.findViewById(R.id.avatar);
}
return (avatar);
@@ -765,23 +716,6 @@ protected void showOrHideModerateButtons() {
}
}
- public static String getMd5Hash(String input) {
- try {
- MessageDigest md = MessageDigest.getInstance("MD5");
- byte[] messageDigest = md.digest(input.getBytes());
- BigInteger number = new BigInteger(1, messageDigest);
- String md5 = number.toString(16);
-
- while (md5.length() < 32)
- md5 = "0" + md5;
-
- return md5;
- } catch (NoSuchAlgorithmException e) {
- Log.e("MD5", e.getLocalizedMessage());
- return null;
- }
- }
-
public void hideModerationBar() {
AnimationSet set = new AnimationSet(true);
Animation animation = new AlphaAnimation(1.0f, 0.0f);
@@ -1020,18 +954,16 @@ protected void onPostExecute(
if (commentsResult == null) {
- if (thumbs != null) {
- if (model.size() == 1) {
- WordPress.wpDB.clearComments(WordPress.currentBlog
- .getId());
- model.clear();
- allComments.clear();
- thumbs.notifyDataSetChanged();
- onCommentStatusChangeListener
- .onCommentStatusChanged("clear");
- WordPress.currentComment = null;
- loadComments(false, false);
- }
+ if (model.size() == 1) {
+ WordPress.wpDB.clearComments(WordPress.currentBlog
+ .getId());
+ model.clear();
+ allComments.clear();
+ getListView().invalidateViews();
+ onCommentStatusChangeListener
+ .onCommentStatusChanged("clear");
+ WordPress.currentComment = null;
+ loadComments(false, false);
}
onAnimateRefreshButton.onAnimateRefreshButton(false);
diff --git a/src/org/wordpress/android/ui/notifications/BigBadgeFragment.java b/src/org/wordpress/android/ui/notifications/BigBadgeFragment.java
new file mode 100644
index 000000000000..5b3db7bddc72
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/BigBadgeFragment.java
@@ -0,0 +1,56 @@
+package org.wordpress.android.ui.notifications;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.volley.toolbox.NetworkImageView;
+
+import org.wordpress.android.R;
+import org.wordpress.android.WordPress;
+import org.wordpress.android.models.Note;
+import org.wordpress.android.util.JSONUtil;
+
+public class BigBadgeFragment extends Fragment implements NotificationFragment {
+ private Note mNote;
+ private NetworkImageView mBadgeImageView;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle state){
+ View view = inflater.inflate(R.layout.notifications_big_badge, parent, false);
+ mBadgeImageView = (NetworkImageView) view.findViewById(R.id.badge);
+
+ TextView bodyTextView = (TextView) view.findViewById(R.id.body);
+ bodyTextView.setMovementMethod(LinkMovementMethod.getInstance());
+ String noteHTML = JSONUtil.queryJSON(getNote().toJSONObject(), "body.html", "");
+ if (noteHTML.equals(""))
+ noteHTML = getNote().getSubject();
+ bodyTextView.setText(Html.fromHtml(noteHTML));
+
+ // Get the badge
+ String iconURL = getNote().getIconURL();
+ if (!iconURL.equals("")) {
+ mBadgeImageView.setImageUrl(iconURL, WordPress.imageLoader);
+ }
+
+ return view;
+ }
+
+ public void setNote(Note note){
+ mNote = note;
+ }
+ public Note getNote(){
+ return mNote;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/DetailHeader.java b/src/org/wordpress/android/ui/notifications/DetailHeader.java
new file mode 100644
index 000000000000..f74dfc16d9e0
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/DetailHeader.java
@@ -0,0 +1,60 @@
+/**
+ * Set a line of text and a URL to open in the browser when clicked
+ */
+package org.wordpress.android.ui.notifications;
+
+import android.view.View;
+import android.widget.TextView;
+import android.widget.LinearLayout;
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.provider.Browser;
+import android.net.Uri;
+
+import org.wordpress.android.R;
+
+public class DetailHeader extends LinearLayout {
+ public DetailHeader(Context context){
+ super(context);
+ }
+ public DetailHeader(Context context, AttributeSet attributes){
+ super(context, attributes);
+ }
+ public DetailHeader(Context context, AttributeSet attributes, int defStyle){
+ super(context, attributes, defStyle);
+ }
+ public TextView getTextView(){
+ return (TextView) findViewById(R.id.label);
+ }
+ public void setText(CharSequence text){
+ getTextView().setText(text);
+ }
+ public void setUrl(final String url){
+ if (url == null) {
+ setClickable(false);
+ setOnClickListener(null);
+ } else {
+ setClickable(true);
+ setOnClickListener(new View.OnClickListener(){
+ @Override
+ public void onClick(View view){
+ Uri uri = Uri.parse(url);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ Context context = getContext();
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+ context.startActivity(intent);
+ }
+ });
+ }
+ }
+ public void setClickable(boolean clickable){
+ super.setClickable(clickable);
+ View indicator = findViewById(R.id.indicator);
+ if (clickable == false) {
+ indicator.setVisibility(GONE);
+ } else {
+ indicator.setVisibility(VISIBLE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/FollowListener.java b/src/org/wordpress/android/ui/notifications/FollowListener.java
new file mode 100644
index 000000000000..cca606910bce
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/FollowListener.java
@@ -0,0 +1,70 @@
+package org.wordpress.android.ui.notifications;
+
+import android.content.Context;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.volley.VolleyError;
+
+import com.wordpress.rest.RestRequest.Listener;
+import com.wordpress.rest.RestRequest.ErrorListener;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import org.wordpress.android.R;
+import org.wordpress.android.WordPress;
+
+class FollowListener implements FollowRow.OnFollowListener {
+
+ protected Context currentContent = null;
+
+ public FollowListener(Context currentContent) {
+ super();
+ this.currentContent = currentContent;
+ }
+
+ class FollowResponseHandler implements Listener, ErrorListener {
+ private FollowRow mRow;
+ private String mSiteId;
+ private boolean mShouldFollow;
+ FollowResponseHandler(FollowRow row, String siteId, boolean shouldFollow){
+ mRow = row;
+ mSiteId = siteId;
+ mShouldFollow = shouldFollow;
+ disableButton();
+ }
+ @Override
+ public void onResponse(JSONObject response){
+ if (mRow.isSiteId(mSiteId)) {
+ mRow.setFollowing(mShouldFollow);
+ }
+ enableButton();
+ }
+ @Override
+ public void onErrorResponse(VolleyError error){
+ enableButton();
+ Log.d("WPNotifications", String.format("Failed to follow the blog: %s ", error));
+ }
+ public void disableButton(){
+ if (mRow.isSiteId(mSiteId)) {
+ mRow.getFollowButton().setEnabled(false);
+ }
+ }
+ public void enableButton(){
+ if (mRow.isSiteId(mSiteId)) {
+ mRow.getFollowButton().setEnabled(true);
+ }
+ }
+ }
+ @Override
+ public void onFollow(final FollowRow row, final String siteId){
+ FollowResponseHandler handler = new FollowResponseHandler(row, siteId, true);
+ WordPress.restClient.followSite(siteId, handler, handler);
+ }
+ @Override
+ public void onUnfollow(final FollowRow row, final String siteId){
+ FollowResponseHandler handler = new FollowResponseHandler(row, siteId, false);
+ WordPress.restClient.unfollowSite(siteId, handler, handler);
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/FollowRow.java b/src/org/wordpress/android/ui/notifications/FollowRow.java
new file mode 100644
index 000000000000..2dd41640faf5
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/FollowRow.java
@@ -0,0 +1,235 @@
+/**
+ * A row with and avatar, name and follow button
+ *
+ * The follow button switches between "Follow" and "Unfollow" depending on the follow status
+ * and provides and interface to know when the user has tried to follow or unfollow by tapping
+ * the button.
+ *
+ * Potentially can integrate with Gravatar using the avatar url to find profile JSON.
+ */
+package org.wordpress.android.ui.notifications;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.volley.toolbox.NetworkImageView;
+
+import org.json.JSONObject;
+import org.json.JSONException;
+
+import org.wordpress.android.R;
+
+public class FollowRow extends LinearLayout {
+
+ public static interface OnFollowListener {
+ public void onUnfollow( FollowRow row, String blogId);
+ public void onFollow( FollowRow row, String blogId);
+ }
+
+ private static final String TAG="FollowRow";
+ private static String PARAMS_FIELD="params";
+ private static String TYPE_FIELD="type";
+ private static String ACTION_TYPE="follow";
+ private static String BLOG_ID_PARAM="blog_id";
+ private static String IS_FOLLOWING_PARAM="is_following";
+ private static String BLOG_URL_PARAM="blog_url";
+ private static String BLOG_DOMAIN_PARAM="blog_domain";
+
+ private boolean mFollowing = false;
+ private String mBlogId = null;
+ private OnFollowListener mListener = null;
+ private JSONObject mParams = null;
+ private CharSequence mDefaultText = "";
+ private String mBlogURL = null;
+
+ public FollowRow(Context context){
+ super(context);
+ }
+ public FollowRow(Context context, AttributeSet attributes){
+ super(context, attributes);
+ }
+ public FollowRow(Context context, AttributeSet attributes, int defStyle){
+ super(context, attributes, defStyle);
+ }
+
+ public void setAction(JSONObject actionJSON){
+ ImageButton followButton = getFollowButton();
+ View followDivider = getFollowDivider();
+ getImageView().setDefaultImageResId(R.drawable.placeholder);
+ try {
+ if (actionJSON.getString(TYPE_FIELD).equals(ACTION_TYPE)) {
+ // get the parms for following
+ mParams = actionJSON.getJSONObject(PARAMS_FIELD);
+ // show the button
+ followButton.setVisibility(VISIBLE);
+ followButton.setOnClickListener(new ClickListener());
+ followButton.setOnLongClickListener(new LongClickListener());
+ followDivider.setVisibility(VISIBLE);
+ getSiteTextView().setText(getSiteDomain());
+ setClickable(true);
+ } else {
+ mParams = null;
+ followButton.setVisibility(GONE);
+ followButton.setOnClickListener(null);
+ followDivider.setVisibility(GONE);
+ getSiteTextView().setText("");
+ setClickable(false);
+ }
+
+ if (hasParams())
+ setSiteUrl(mParams.optString(BLOG_URL_PARAM, null));
+
+ updateButton();
+ }catch (JSONException e) {
+ Log.e(TAG, String.format("Could not set action from %s", actionJSON), e);
+ getFollowButton().setVisibility(GONE);
+ getFollowDivider().setVisibility(GONE);
+ getSiteTextView().setText("");
+ setClickable(false);
+ mParams = null;
+ }
+ }
+
+ public JSONObject getParams(){
+ return mParams;
+ }
+ public boolean hasParams(){
+ return mParams != null;
+ }
+ public NetworkImageView getImageView(){
+ return (NetworkImageView) findViewById(R.id.avatar);
+ }
+ public ImageButton getFollowButton(){
+ return (ImageButton) findViewById(R.id.follow_button);
+ }
+ public TextView getTextView(){
+ return (TextView) findViewById(R.id.name);
+ }
+ public TextView getSiteTextView(){
+ return (TextView) findViewById(R.id.url);
+ }
+ private View getFollowDivider() {
+ return (View) findViewById(R.id.follow_divider);
+ }
+ public void setDefaultText(CharSequence text){
+ setText(text);
+ mDefaultText = getTextView().getText();
+ }
+ public void setText(CharSequence text){
+ getTextView().setText(text);
+ }
+ public void setText(int resourceId){
+ getTextView().setText(resourceId);
+ }
+ public boolean isSiteId(String siteId){
+ return getSiteId().equals(siteId);
+ }
+ public void setFollowing(boolean following){
+ if (hasParams()) {
+ try {
+ mParams.putOpt(IS_FOLLOWING_PARAM, following);
+ } catch (JSONException e) {
+ Log.e(TAG, String.format("Could not set following %b", following), e);
+ }
+ };
+ updateButton();
+ }
+ public boolean isFollowing(){
+ if (hasParams()) {
+ return mParams.optBoolean(IS_FOLLOWING_PARAM, false);
+ } else {
+ return false;
+ }
+ }
+ public String getSiteId(){
+ if (hasParams()) {
+ return mParams.optString(BLOG_ID_PARAM, null);
+ } else {
+ return null;
+ }
+ }
+ public void setSiteUrl(String url){
+ mBlogURL = url;
+ if (url != null) {
+ this.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mBlogURL != null) {
+ try {
+ Uri uri = Uri.parse(mBlogURL);
+ getContext().startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
+ } else {
+ this.setOnClickListener(null);
+ }
+ }
+ public String getSiteUrl(){
+ return mBlogURL;
+ }
+ public String getSiteDomain(){
+ if (hasParams()) {
+ return mParams.optString(BLOG_DOMAIN_PARAM, null);
+ } else {
+ return null;
+ }
+ }
+ public OnFollowListener getListener(){
+ return mListener;
+ }
+ public void setListener(OnFollowListener listener){
+ mListener = listener;
+ }
+ public boolean hasListener(){
+ return mListener != null;
+ }
+ protected void updateButton(){
+ ImageButton followButton = getFollowButton();
+ followButton.setSelected(isFollowing());
+ if(isFollowing()){
+ followButton.setImageResource(R.drawable.follow_minus);
+ } else {
+ followButton.setImageResource(R.drawable.follow_plus);
+ }
+ }
+ class ClickListener implements View.OnClickListener {
+ public void onClick(View v) {
+ if (!hasListener()) {
+ return;
+ }
+ ImageButton followButton = getFollowButton();
+ OnFollowListener listener = getListener();
+ if (isFollowing()) {
+ followButton.setImageResource(R.drawable.follow_plus);
+ listener.onUnfollow(FollowRow.this, getSiteId());
+ } else {
+ followButton.setImageResource(R.drawable.follow_minus);
+ listener.onFollow(FollowRow.this, getSiteId());
+ }
+ }
+ }
+
+ class LongClickListener implements View.OnLongClickListener {
+ @Override
+ public boolean onLongClick(View v) {
+ Toast.makeText(getContext(), getResources().getString(R.string.tooltip_follow), Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/NoteCommentFragment.java b/src/org/wordpress/android/ui/notifications/NoteCommentFragment.java
new file mode 100644
index 000000000000..8a15757101a8
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/NoteCommentFragment.java
@@ -0,0 +1,440 @@
+package org.wordpress.android.ui.notifications;
+
+import java.util.Map;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.NotificationCompat;
+import android.text.Editable;
+import android.text.Html;
+import android.text.SpannableStringBuilder;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ImageSpan;
+import android.text.style.ClickableSpan;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.ImageLoader;
+import com.android.volley.toolbox.ImageLoader.ImageContainer;
+
+import com.wordpress.rest.RestRequest;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import org.wordpress.android.R;
+import org.wordpress.android.WordPress;
+import org.wordpress.android.models.Note;
+import org.wordpress.android.ui.posts.PostsActivity;
+import org.wordpress.android.util.EscapeUtils;
+import org.wordpress.android.util.JSONUtil;
+
+public class NoteCommentFragment extends Fragment implements NotificationFragment {
+ private static final String TAG="NoteComment";
+ private TextView mCommentText, mModeratingText;
+ private ReplyField mReplyField;
+ private Note mNote;
+ private FollowRow mFollowRow;
+ private DetailHeader mDetailHeader;
+ private ReplyList mReplyList;
+ private ScrollView mScrollView;
+ private ImageButton mApproveButton, mSpamButton, mTrashButton;
+ private LinearLayout mModerateContainer, mModerateSection;
+
+ private static final String APPROVE_TAG = "approve-comment";
+ private static final String UNAPPROVE_TAG = "unapprove-comment";
+ private static final String SPAM_TAG = "spam-comment";
+ private static final String TRASH_TAG = "trash-comment";
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle state){
+ View view = inflater.inflate(R.layout.notifications_comment, parent, false);
+ mReplyField = (ReplyField) view.findViewById(R.id.replyField);
+ mCommentText = (TextView) view.findViewById(R.id.note_text);
+ mFollowRow = (FollowRow) view.findViewById(R.id.follow_row);
+ mDetailHeader = (DetailHeader) view.findViewById(R.id.header);
+ mReplyList = (ReplyList) view.findViewById(R.id.replies);
+ mScrollView = (ScrollView) view.findViewById(R.id.scroll_view);
+ mApproveButton = (ImageButton)view.findViewById(R.id.note_moderate_approve);
+ mSpamButton = (ImageButton)view.findViewById(R.id.note_moderate_spam);
+ mTrashButton = (ImageButton)view.findViewById(R.id.note_moderate_trash);
+ mModeratingText = (TextView)view.findViewById(R.id.comment_moderating);
+ mModerateContainer = (LinearLayout)view.findViewById(R.id.moderate_buttons_container);
+ mModerateSection = (LinearLayout)view.findViewById(R.id.moderate_section);
+
+ ((TextView) view.findViewById(R.id.moderate_comment_header)).setText(getResources().getString(R.string.moderate_comment).toUpperCase());
+
+ return view;
+ }
+
+ @Override
+ public void onStart(){
+ super.onStart();
+ mFollowRow.getImageView().setImageUrl(getNote().getIconURL(), WordPress.imageLoader);
+ SpannableStringBuilder html = (SpannableStringBuilder) getNote().getCommentBody();
+ final Html.ImageGetter imgGetter = new AsyncImageGetter(mCommentText);
+ ImageSpan imgs[] = html.getSpans(0, html.length(), ImageSpan.class);
+ for(ImageSpan img : imgs){
+ // create a new image span using the image getter
+ final String src = img.getSource();
+ final RemoteDrawable remoteDrawable = (RemoteDrawable) imgGetter.getDrawable(src);
+ ClickableSpan clickListener = new ClickableSpan(){
+ @Override
+ public void onClick(View v){
+ if (remoteDrawable.didFail()) {
+ imgGetter.getDrawable(src);
+ }
+ }
+ };
+ ImageSpan remote = new ImageSpan(remoteDrawable, img.getSource());
+ // now replace
+ int spanStart = html.getSpanStart(img);
+ int spanEnd = html.getSpanEnd(img);
+ int spanFlags = html.getSpanFlags(img);
+ html.setSpan(remote, spanStart, spanEnd, spanFlags);
+ html.setSpan(clickListener, spanStart, spanEnd, spanFlags);
+ html.removeSpan(img);
+ }
+ mCommentText.setText(html);
+ mCommentText.setMovementMethod(LinkMovementMethod.getInstance());
+ mReplyField.setOnReplyListener(mReplyListener);
+ mDetailHeader.setText(getNote().getSubject());
+
+ Map noteActions = getNote().getActions();
+ boolean hasModerateAction = false;
+ if (noteActions.containsKey(APPROVE_TAG)) {
+ hasModerateAction = true;
+ mApproveButton.setImageResource(R.drawable.moderate_approve);
+ mApproveButton.setVisibility(View.VISIBLE);
+ mApproveButton.setOnClickListener(mModerateClickListener);
+ mApproveButton.setTag(APPROVE_TAG);
+ }
+ if (noteActions.containsKey(UNAPPROVE_TAG)) {
+ hasModerateAction = true;
+ mApproveButton.setImageResource(R.drawable.moderate_unapprove);
+ mApproveButton.setVisibility(View.VISIBLE);
+ mApproveButton.setOnClickListener(mModerateClickListener);
+ mApproveButton.setTag(UNAPPROVE_TAG);
+ }
+ if (noteActions.containsKey(SPAM_TAG)) {
+ hasModerateAction = true;
+ mSpamButton.setVisibility(View.VISIBLE);
+ mSpamButton.setOnClickListener(mModerateClickListener);
+ mSpamButton.setTag(SPAM_TAG);
+ } else {
+ mSpamButton.setVisibility(View.GONE);
+ }
+ if (noteActions.containsKey(TRASH_TAG)) {
+ hasModerateAction = true;
+ mTrashButton.setVisibility(View.VISIBLE);
+ mTrashButton.setOnClickListener(mModerateClickListener);
+ mTrashButton.setTag(TRASH_TAG);
+ } else {
+ mTrashButton.setVisibility(View.GONE);
+ }
+
+ if (!hasModerateAction)
+ mModerateSection.setVisibility(View.GONE);
+
+ String url = getNote().queryJSON("body.items[last].header_link", "");
+ if (!url.equals("")) {
+ mDetailHeader.setUrl(url);
+ }
+ JSONObject followAction = getNote().queryJSON("body.items[last].action", new JSONObject());
+ mFollowRow.setDefaultText(EscapeUtils.unescapeHtml(getNote().queryJSON("body.items[-1].header_text", "")));
+ mFollowRow.setAction(followAction);
+ mFollowRow.setListener(new FollowListener(getActivity().getApplicationContext()));
+ Bundle arguments = getArguments();
+ if (arguments != null && (arguments.containsKey(NotificationsActivity.NOTE_REPLY_EXTRA) || arguments.containsKey(NotificationsActivity.NOTE_INSTANT_REPLY_EXTRA))) {
+ if (arguments.containsKey(NotificationsActivity.NOTE_REPLY_EXTRA))
+ mReplyField.setText(arguments.getString(NotificationsActivity.NOTE_REPLY_EXTRA));
+
+ mReplyField.mTextField.requestFocus();
+ InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (inputMethodManager != null)
+ inputMethodManager.showSoftInput(mReplyField.mTextField, 0);
+ }
+
+ }
+
+ @Override
+ public void onPause(){
+ super.onPause();
+ dismissKeyboard();
+ }
+
+ protected void dismissKeyboard(){
+ InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(getView().getWindowToken(), 0x0);
+ }
+
+ private OnClickListener mModerateClickListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ String tag = (String) v.getTag();
+ if (getNote().getActions().containsKey(tag)) {
+
+ animateModeration(true);
+ JSONObject moderateAction = getNote().getActions().get(tag);
+ String siteId = String.valueOf(JSONUtil.queryJSON(moderateAction, "params.site_id",
+ -1));
+ String commentId = String.valueOf(JSONUtil.queryJSON(moderateAction,
+ "params.comment_id", -1));
+ String status = JSONUtil.queryJSON(moderateAction, "params.rest_body.status", "");
+
+ if (getActivity() != null) {
+ ((NotificationsActivity)getActivity()).moderateComment(siteId, commentId, status, getNote());
+ }
+
+ }
+ }
+ };
+
+ public void animateModeration(boolean start) {
+ // show some fancy animations
+ for (int i = 0; i < mModerateContainer.getChildCount(); i++) {
+ View view = mModerateContainer.getChildAt(i);
+ if (view instanceof ImageButton && view.getVisibility() == View.VISIBLE) {
+ if (start)
+ view.setClickable(false);
+ else
+ view.setClickable(true);
+ Animation zoom = AnimationUtils.loadAnimation(getActivity()
+ .getBaseContext(), (start) ? R.anim.rotate_zoom_out : R.anim.rotate_zoom_in);
+ zoom.setStartOffset(i * 100);
+ view.startAnimation(zoom);
+ }
+ }
+
+ Animation moderatingAnimation = AnimationUtils.loadAnimation(getActivity()
+ .getBaseContext(), (start) ? R.anim.blink : R.anim.fade_out);
+ mModeratingText.setVisibility((start) ? View.VISIBLE : View.GONE);
+ mModeratingText.startAnimation(moderatingAnimation);
+
+ }
+
+ private ReplyField.OnReplyListener mReplyListener = new ReplyField.OnReplyListener() {
+ @Override
+ public void onReply(ReplyField field, Editable replyText){
+ Note.Reply reply = getNote().buildReply(replyText.toString());
+ replyText.clear();
+ dismissKeyboard();
+ ReplyRow row = mReplyList.addReply(reply);
+ ReplyResponseHandler handler = new ReplyResponseHandler(reply, row);
+ WordPress.restClient.replyToComment(reply, handler, handler);
+ mScrollView.scrollTo(0, mReplyList.getBottom());
+ }
+ };
+
+ class ReplyResponseHandler implements RestRequest.Listener, RestRequest.ErrorListener, View.OnClickListener {
+ private Toast mToast;
+ private Note.Reply mReply;
+ private ReplyRow mRow;
+ private NotificationManager mNotificationManager;
+ private Notification mFailureNotification;
+ private static final int NOTE_ID = 0x0;
+ ReplyResponseHandler(Note.Reply reply, ReplyRow row){
+ mReply = reply;
+ mRow = row;
+ mNotificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
+ // create intent for failure case
+ Intent failureIntent = new Intent(getActivity(), PostsActivity.class);
+ failureIntent.setAction(Intent.ACTION_EDIT);
+ failureIntent.addFlags(NotificationsActivity.FLAG_FROM_NOTE);
+ failureIntent.putExtra(NotificationsActivity.NOTE_ID_EXTRA, getNote().getId());
+ failureIntent.putExtra(NotificationsActivity.NOTE_REPLY_EXTRA, mReply.getContent());
+ failureIntent.putExtra(NotificationsActivity.FROM_NOTIFICATION_EXTRA, true);
+ // TODO: Improve failure text. Who was it they tried to reply to and a better
+ // reason why it failed. Need to make sure id's are unique.
+ mFailureNotification = new NotificationCompat.Builder(getActivity())
+ .setContentTitle(getString(R.string.reply_failed))
+ .setContentText(getString(R.string.tap_retry))
+ .setTicker(getString(R.string.reply_failed))
+ .setWhen(0)
+ .setSmallIcon(R.drawable.notification_icon)
+ .setContentIntent(PendingIntent.getActivity(getActivity(), NOTE_ID, failureIntent, NOTE_ID))
+ .build();
+ // Toast for notifying the user that comment was published successfully
+ mToast = Toast.makeText(getActivity(), R.string.note_reply_successful, Toast.LENGTH_SHORT);
+ }
+ @Override
+ public void onResponse(JSONObject response){
+ // remove the notification if it's there
+ mNotificationManager.cancel(NOTE_ID);
+ if (getActivity() != null) {
+ mReply.setCommentJson(response);
+ mRow.setComplete(true);
+ mRow.setUrl(mReply.getUrl());
+ mRow.setText(String.format("\u201c%s\u201d", mReply.getCommentPreview()));
+ mRow.getImageView().setImageUrl(mReply.getAvatarUrl(), WordPress.imageLoader);
+ } else {
+ mToast.show();
+ }
+ }
+
+ @Override
+ public void onErrorResponse(VolleyError error){
+ if (error.networkResponse != null) {
+ String body = new String(error.networkResponse.data);
+ Log.e(TAG, body, error);
+ }
+ mRow.setFailed(true);
+ mRow.setText(R.string.retry_reply);
+ mRow.setOnClickListener(this);
+ mNotificationManager.notify("reply", 0xFA, mFailureNotification);
+ }
+
+ @Override
+ public void onClick(View v){
+ mRow.setFailed(false);
+ mRow.setComplete(false);
+ WordPress.restClient.replyToComment(mReply, this, this);
+ }
+
+ }
+
+ public void setNote(Note note){
+ mNote = note;
+ }
+
+ public Note getNote(){
+ return mNote;
+ }
+
+ private class AsyncImageGetter implements Html.ImageGetter {
+ private TextView mView;
+ public AsyncImageGetter(TextView view){
+ mView = view;
+ }
+ @Override
+ public Drawable getDrawable(final String source){
+ // TODO: cancel any requests when the view is destroyed
+ if (getActivity() == null) {
+ return null;
+ }
+ Drawable loading = getResources().getDrawable(R.drawable.remote_image);
+ Drawable failed = getResources().getDrawable(R.drawable.remote_failed);
+ final RemoteDrawable remote = new RemoteDrawable(loading, failed);
+ // Kick off the async task of downloading the image
+ WordPress.imageLoader.get(source, new ImageLoader.ImageListener(){
+ @Override
+ public void onErrorResponse(VolleyError error){
+ Log.e(TAG, "Failed to load image", error);
+ remote.displayFailed();
+ mView.invalidate();
+ }
+ @Override
+ public void onResponse(ImageContainer response, boolean isImmediate){
+ if (response.getBitmap() != null) {
+ // view is gone? then stop
+ if (mView == null) {
+ return;
+ }
+ Drawable drawable = new BitmapDrawable(getResources(), response.getBitmap());
+ final int oldHeight = remote.getBounds().height();
+ int maxWidth = mView.getWidth() - mView.getPaddingLeft() - mView.getPaddingRight();
+ remote.setRemoteDrawable(drawable, maxWidth);
+ // TODO: resize image to fit visibliy within the TextView
+ // image is from cache? don't need to modify view height
+ if (isImmediate) {
+ return;
+ }
+ int newHeight = remote.getBounds().height();
+ mView.invalidate();
+ // For ICS
+ mView.setHeight(mView.getHeight() + newHeight - oldHeight);
+ // Pre ICS
+ mView.setEllipsize(null);
+ }
+ }
+ });
+ return remote;
+ }
+
+ }
+
+ private class RemoteDrawable extends BitmapDrawable {
+ protected Drawable mRemoteDrawable;
+ protected Drawable mLoadingDrawable;
+ protected Drawable mFailedDrawable;
+ private boolean mDidFail=false;
+ public RemoteDrawable(Drawable loadingDrawable, Drawable failedDrawable){
+ mLoadingDrawable = loadingDrawable;
+ mFailedDrawable = failedDrawable;
+ setBounds(0, 0, mLoadingDrawable.getIntrinsicWidth(), mLoadingDrawable.getIntrinsicHeight());
+ }
+ public void displayFailed(){
+ mDidFail = true;
+ }
+ public void setBounds(int x, int y, int width, int height){
+ super.setBounds(x, y, width, height);
+ if (mRemoteDrawable != null) {
+ mRemoteDrawable.setBounds(x, y, width, height);
+ return;
+ }
+ if (mLoadingDrawable != null) {
+ mLoadingDrawable.setBounds(x, y, width, height);
+ mFailedDrawable.setBounds(x, y, width, height);
+ }
+ }
+ public void setRemoteDrawable(Drawable remote){
+ mRemoteDrawable = remote;
+ setBounds(0, 0, mRemoteDrawable.getIntrinsicWidth(), mRemoteDrawable.getIntrinsicHeight());
+ }
+ public void setRemoteDrawable(Drawable remote, int maxWidth){
+ // null sentinel for now
+ if (remote == null) {
+ // throw error
+ return;
+ }
+ mRemoteDrawable = remote;
+ // determine if we need to scale the image to fit in view
+ int imgWidth = remote.getIntrinsicWidth();
+ int imgHeight = remote.getIntrinsicHeight();
+ float xScale = (float) imgWidth/(float) maxWidth;
+ if (xScale > 1.0f) {
+ setBounds(0, 0, Math.round(imgWidth/xScale), Math.round(imgHeight/xScale));
+ } else {
+ setBounds(0, 0, imgWidth, imgHeight);
+ }
+ }
+ public boolean didFail(){
+ return mDidFail;
+ }
+ public void draw(Canvas canvas){
+ if (mRemoteDrawable != null) {
+ mRemoteDrawable.draw(canvas);
+ } else if (didFail()) {
+ mFailedDrawable.draw(canvas);
+ } else {
+ mLoadingDrawable.draw(canvas);
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/NotificationFragment.java b/src/org/wordpress/android/ui/notifications/NotificationFragment.java
new file mode 100644
index 000000000000..d18659d3e985
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/NotificationFragment.java
@@ -0,0 +1,15 @@
+/**
+ * Provides a list view and list adapter to display a note. It will have a header view to show
+ * the avatar and other details for the post.
+ *
+ * More specialized note adapters will need to be made to provide the correct views for the type
+ * of note/note template it has.
+ */
+package org.wordpress.android.ui.notifications;
+
+import org.wordpress.android.models.Note;
+
+interface NotificationFragment {
+ public Note getNote();
+ public void setNote(Note note);
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/NotificationsActivity.java b/src/org/wordpress/android/ui/notifications/NotificationsActivity.java
new file mode 100644
index 000000000000..86b386cd09bb
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/NotificationsActivity.java
@@ -0,0 +1,472 @@
+package org.wordpress.android.ui.notifications;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+import android.content.Intent;
+import android.support.v4.content.IntentCompat;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+
+import org.wordpress.android.GCMIntentService;
+import org.wordpress.android.R;
+import org.wordpress.android.WordPress;
+import org.wordpress.android.ui.WPActionBarActivity;
+import org.wordpress.android.models.Note;
+import static org.wordpress.android.WordPress.*;
+
+import com.wordpress.rest.RestRequest;
+
+import com.android.volley.VolleyError;
+
+import org.json.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class NotificationsActivity extends WPActionBarActivity {
+ public static final String TAG="WPNotifications";
+ public static final String NOTE_ID_EXTRA="noteId";
+ public static final String FROM_NOTIFICATION_EXTRA="fromNotification";
+ public static final String NOTE_REPLY_EXTRA="replyContent";
+ public static final String NOTE_INSTANT_REPLY_EXTRA = "instantReply";
+ public static final int FLAG_FROM_NOTE=Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ Intent.FLAG_ACTIVITY_SINGLE_TOP |
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ IntentCompat.FLAG_ACTIVITY_CLEAR_TASK;
+
+ Set fragmentDetectors = new HashSet();
+
+ private NotificationsListFragment mNotesList;
+ private MenuItem mRefreshMenuItem;
+ private boolean mLoadingMore = false;
+ private boolean mFirstLoadComplete = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+ createMenuDrawer(R.layout.notifications);
+
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayShowTitleEnabled(true);
+ setTitle(getResources().getString(R.string.notifications));
+
+ FragmentManager fm = getSupportFragmentManager();
+ fm.addOnBackStackChangedListener(mOnBackStackChangedListener);
+ mNotesList = (NotificationsListFragment) fm.findFragmentById(R.id.notes_list);
+ mNotesList.setNoteProvider(new NoteProvider());
+ mNotesList.setOnNoteClickListener(new NoteClickListener());
+
+ try {
+ if(WordPress.latestNotes != null){
+ mNotesList.getNotesAdapter().addAll(parseNotes(WordPress.latestNotes));
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "No cached notes");
+ }
+
+ fragmentDetectors.add(new FragmentDetector(){
+ @Override
+ public Fragment getFragment(Note note){
+ if (note.isCommentType()) {
+ Fragment fragment = new NoteCommentFragment();
+ return fragment;
+ }
+ return null;
+ }
+ });
+ fragmentDetectors.add(new FragmentDetector(){
+ @Override
+ public Fragment getFragment(Note note){
+ if (note.isSingleLineListTemplate()) {
+ Fragment fragment = new SingleLineListFragment();
+ return fragment;
+ }
+ return null;
+ }
+ });
+ fragmentDetectors.add(new FragmentDetector(){
+ @Override
+ public Fragment getFragment(Note note){
+ Log.d(TAG, String.format("Is it a big badge template? %b", note.isBigBadgeTemplate()));
+ if (note.isBigBadgeTemplate()) {
+ Fragment fragment = new BigBadgeFragment();
+ return fragment;
+ }
+ return null;
+ }
+ });
+
+ GCMIntentService.activeNotificationsMap.clear();
+
+ launchWithNoteId();
+ refreshNotes();
+
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ GCMIntentService.activeNotificationsMap.clear();
+
+ }
+
+ private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
+ public void onBackStackChanged() {
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0)
+ mMenuDrawer.setDrawerIndicatorEnabled(true);
+ }
+ };
+
+ /**
+ * Detect if Intent has a noteId extra and display that specific note detail fragment
+ */
+ private void launchWithNoteId(){
+ Intent intent = getIntent();
+ if (intent.hasExtra(NOTE_ID_EXTRA)) {
+ // find it/load it etc
+ Map params = new HashMap();
+ params.put("ids", intent.getStringExtra(NOTE_ID_EXTRA));
+ NotesResponseHandler handler = new NotesResponseHandler(){
+ @Override
+ public void onNotes(List notes){
+ // there should only be one note!
+ if (!notes.isEmpty()) {
+ Note note = notes.get(0);
+ openNote(note);
+ }
+ }
+ };
+ restClient.getNotifications(params, handler, handler);
+ }
+ }
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.equals(mRefreshMenuItem)) {
+ refreshNotes();
+ return true;
+ } else if (item.getItemId() == android.R.id.home){
+ FragmentManager fm = getSupportFragmentManager();
+ if (fm.getBackStackEntryCount() > 0) {
+ popNoteDetail();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuInflater inflater = getSupportMenuInflater();
+ inflater.inflate(R.menu.notifications, menu);
+ mRefreshMenuItem = menu.findItem(R.id.menu_refresh);
+ if (shouldAnimateRefreshButton) {
+ shouldAnimateRefreshButton = false;
+ startAnimatingRefreshButton(mRefreshMenuItem);
+ }
+ return true;
+ }
+
+ public void popNoteDetail(){
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment f = fm.findFragmentById(R.id.commentDetail);
+ if (f == null) {
+ fm.popBackStack();
+ }
+ }
+ /**
+ * Tries to pick the correct fragment detail type for a given note using the
+ * fragment detectors
+ */
+ private Fragment fragmentForNote(Note note){
+ Iterator templates = fragmentDetectors.iterator();
+ while(templates.hasNext()){
+ FragmentDetector detector = templates.next();
+ Fragment fragment = detector.getFragment(note);
+ if (fragment != null){
+ return fragment;
+ }
+ }
+ return null;
+ }
+ /**
+ * Open a note fragment based on the type of note
+ */
+ public void openNote(final Note note){
+ if (note == null)
+ return;
+ // if note is "unread" set note to "read"
+ if (note.isUnread()) {
+ // send a request to mark note as read
+ restClient.markNoteAsRead(note,
+ new RestRequest.Listener(){
+ @Override
+ public void onResponse(JSONObject response){
+ note.setUnreadCount("0");
+ mNotesList.getNotesAdapter().notifyDataSetChanged();
+ }
+ },
+ new RestRequest.ErrorListener(){
+ @Override
+ public void onErrorResponse(VolleyError error){
+ Log.d(TAG, String.format("Failed to mark as read %s", error));
+ }
+ }
+ );
+ }
+
+ FragmentManager fm = getSupportFragmentManager();
+ // remove the note detail if it's already on there
+ if (fm.getBackStackEntryCount() > 0){
+ fm.popBackStack();
+ }
+ Fragment fragment = fragmentForNote(note);
+ if (fragment == null) {
+ Log.d(TAG, String.format("No fragment found for %s", note.toJSONObject()));
+ return;
+ }
+ // swap the fragment
+ NotificationFragment noteFragment = (NotificationFragment) fragment;
+ Intent intent = getIntent();
+ if (intent.hasExtra(NOTE_ID_EXTRA) && intent.getStringExtra(NOTE_ID_EXTRA).equals(note.getId())) {
+ if (intent.hasExtra(NOTE_REPLY_EXTRA) || intent.hasExtra(NOTE_INSTANT_REPLY_EXTRA)) {
+ fragment.setArguments(intent.getExtras());
+ }
+ }
+ noteFragment.setNote(note);
+ FragmentTransaction transaction = fm.beginTransaction();
+ View container = findViewById(R.id.note_fragment_container);
+ transaction.replace(R.id.note_fragment_container, fragment);
+ transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
+ // only add to backstack if we're removing the list view from the fragment container
+ if (container.findViewById(R.id.notes_list) != null) {
+ mMenuDrawer.setDrawerIndicatorEnabled(false);
+ transaction.addToBackStack(null);
+ }
+ transaction.commit();
+ }
+
+ public void moderateComment(String siteId, String commentId, String status, final Note originalNote) {
+ RestRequest.Listener success = new RestRequest.Listener(){
+ @Override
+ public void onResponse(JSONObject response){
+ Map params = new HashMap();
+ params.put("ids", originalNote.getId());
+ NotesResponseHandler handler = new NotesResponseHandler() {
+ @Override
+ public void onNotes(List notes) {
+ // there should only be one note!
+ if (!notes.isEmpty()) {
+ Note updatedNote = notes.get(0);
+ updateNote(originalNote, updatedNote);
+ }
+ }
+ };
+ WordPress.restClient.getNotifications(params, handler, handler);
+ }
+ };
+ RestRequest.ErrorListener failure = new RestRequest.ErrorListener(){
+ @Override
+ public void onErrorResponse(VolleyError error){
+ Log.d(TAG, String.format("Error moderating comment: %s", error));
+ if (isFinishing())
+ return;
+ Toast.makeText(NotificationsActivity.this, getString(R.string.error_moderate_comment), Toast.LENGTH_LONG).show();
+ FragmentManager fm = getSupportFragmentManager();
+ NoteCommentFragment f = (NoteCommentFragment) fm.findFragmentById(R.id.note_fragment_container);
+ if (f != null) {
+ f.animateModeration(false);
+ }
+ }
+ };
+ WordPress.restClient.moderateComment(siteId, commentId, status, success, failure);
+ }
+
+ public void updateNote(Note originalNote, Note updatedNote) {
+ if (isFinishing())
+ return;
+ int position = mNotesList.getNotesAdapter().getPosition(originalNote);
+ if (position >= 0) {
+ mNotesList.getNotesAdapter().remove(originalNote);
+ mNotesList.getNotesAdapter().insert(updatedNote, position);
+ mNotesList.getNotesAdapter().notifyDataSetChanged();
+ // Update comment detail fragment if we're still viewing the same note
+ if (position == mNotesList.getListView().getCheckedItemPosition()) {
+ FragmentManager fm = getSupportFragmentManager();
+ NoteCommentFragment f = (NoteCommentFragment) fm.findFragmentById(R.id.note_fragment_container);
+ if (f != null) {
+ f.setNote(updatedNote);
+ f.onStart();
+ f.animateModeration(false);
+ }
+ }
+ }
+ }
+
+ public void refreshNotes(){
+ mFirstLoadComplete = false;
+ shouldAnimateRefreshButton = true;
+ startAnimatingRefreshButton(mRefreshMenuItem);
+ NotesResponseHandler handler = new NotesResponseHandler(){
+ @Override
+ public void onNotes(List notes){
+ mFirstLoadComplete = true;
+ final NotificationsListFragment.NotesAdapter adapter = mNotesList.getNotesAdapter();
+ adapter.clear();
+ adapter.addAll(notes);
+ adapter.notifyDataSetChanged();
+ // mark last seen timestampe
+ if (!notes.isEmpty()) {
+ updateLastSeen(notes.get(0).getTimestamp());
+ }
+ stopAnimatingRefreshButton(mRefreshMenuItem);
+ }
+ @Override
+ public void onErrorResponse(VolleyError error){
+ //We need to show an error message? and remove the loading indicator from the list?
+ mFirstLoadComplete = true;
+ final NotificationsListFragment.NotesAdapter adapter = mNotesList.getNotesAdapter();
+ adapter.clear();
+ adapter.addAll(new ArrayList());
+ adapter.notifyDataSetChanged();
+ Toast.makeText(NotificationsActivity.this, String.format(getResources().getString(R.string.error_refresh), getResources().getText(R.string.notifications).toString().toLowerCase()), Toast.LENGTH_LONG).show();
+ stopAnimatingRefreshButton(mRefreshMenuItem);
+ shouldAnimateRefreshButton = false;
+ }
+ };
+ WordPress.refreshNotifications(this, handler, handler);
+ }
+ protected void updateLastSeen(String timestamp){
+
+ restClient.markNotificationsSeen(timestamp,
+ new RestRequest.Listener(){
+ @Override
+ public void onResponse(JSONObject response){
+ Log.d(TAG, String.format("Set last seen time %s", response));
+ }
+ },
+ new RestRequest.ErrorListener(){
+ @Override
+ public void onErrorResponse(VolleyError error){
+ Log.d(TAG, String.format("Could not set last seen time %s", error));
+ }
+ }
+ );
+ }
+ public void requestNotesBefore(Note note){
+ Map params = new HashMap();
+ Log.d(TAG, String.format("Requesting more notes before %s", note.queryJSON("timestamp", "")));
+ params.put("before", note.queryJSON("timestamp", ""));
+ NotesResponseHandler handler = new NotesResponseHandler(){
+ @Override
+ public void onNotes(List notes){
+ // API returns 'on or before' timestamp, so remove first item
+ if (notes.size() >= 1)
+ notes.remove(0);
+ NotificationsListFragment.NotesAdapter adapter = mNotesList.getNotesAdapter();
+ adapter.addAll(notes);
+ adapter.notifyDataSetChanged();
+ }
+ };
+ restClient.getNotifications(params, handler, handler);
+ }
+
+ private class NoteProvider implements NotificationsListFragment.NoteProvider {
+ @Override
+ public void onRequestMoreNotifications(ListView notesList, ListAdapter notesAdapter){
+ if (mFirstLoadComplete && !mLoadingMore) {
+ NotificationsListFragment.NotesAdapter adapter = mNotesList.getNotesAdapter();
+ if (adapter.getCount() > 0) {
+ Note lastNote = adapter.getItem(adapter.getCount()-1);
+ requestNotesBefore(lastNote);
+ }
+ }
+ }
+ }
+
+ private class NoteClickListener implements NotificationsListFragment.OnNoteClickListener {
+ @Override
+ public void onClickNote(Note note){
+ openNote(note);
+ }
+ }
+
+ abstract class NotesResponseHandler implements RestRequest.Listener, RestRequest.ErrorListener {
+ NotesResponseHandler(){
+ mLoadingMore = true;
+ }
+ abstract void onNotes(List notes);
+
+ @Override
+ public void onResponse(JSONObject response){
+ mLoadingMore = false;
+ List notes = null;
+
+ if( response == null ) {
+ //Not sure this could ever happen, but make sure we're catching all response types
+ Log.w(TAG, "Success, but did not receive any notes");
+ notes = new ArrayList(0);
+ onNotes(notes);
+ return;
+ }
+
+ try {
+ notes = parseNotes(response);
+ onNotes(notes);
+ } catch (JSONException e) {
+ Log.e(TAG, "Success, but can't parse the response", e);
+ showError(getString(R.string.error_parsing_response));
+ return;
+ }
+ }
+
+ @Override
+ public void onErrorResponse(VolleyError error){
+ mLoadingMore = false;
+ showError();
+ Log.d(TAG, String.format("Error retrieving notes: %s", error));
+ }
+
+ public void showError(String errorMessage){
+ Toast.makeText(NotificationsActivity.this, errorMessage, Toast.LENGTH_LONG).show();
+ }
+
+ public void showError(){
+ Toast.makeText(NotificationsActivity.this, getString(R.string.error_generic), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private abstract class FragmentDetector {
+ abstract public Fragment getFragment(Note note);
+ }
+
+ public static List parseNotes(JSONObject response) throws JSONException {
+ List notes = null;
+ JSONArray notesJSON = response.getJSONArray("notes");
+ notes = new ArrayList(notesJSON.length());
+ for (int i=0; i {
+ NotesAdapter(){
+ this(getActivity());
+ }
+ NotesAdapter(Context context){
+ this(context, new ArrayList());
+ }
+ NotesAdapter(Context context, List notes){
+ super(context, R.layout.note_list_item, R.id.note_label, notes);
+ }
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ View view = super.getView(position, convertView, parent);
+ final Note note = getItem(position);
+ TextView detailText = (TextView) view.findViewById(R.id.note_detail);
+ if (note.isCommentType()) {
+ detailText.setText(note.getCommentPreview());
+ detailText.setVisibility(View.VISIBLE);
+ } else {
+ detailText.setVisibility(View.GONE);
+ }
+ final NetworkImageView avatarView = (NetworkImageView) view.findViewById(R.id.note_avatar);
+ avatarView.setDefaultImageResId(R.drawable.placeholder);
+ avatarView.setImageUrl(note.getIconURL(), WordPress.imageLoader);
+
+ int imageID = getResources().getIdentifier("note_icon_" + note.getType(),"drawable", getActivity().getPackageName());
+ if (imageID > 0) {
+ final ImageView iconView = (ImageView) view.findViewById(R.id.note_icon);
+ iconView.setImageResource(imageID);
+ }
+
+ final TextView unreadIndicator = (TextView) view.findViewById(R.id.unread_indicator);
+ if (note.isUnread()) {
+ unreadIndicator.setVisibility(View.VISIBLE);
+ }
+ else {
+ unreadIndicator.setVisibility(View.GONE);
+ }
+
+ return view;
+ }
+ public Note getLastNote(){
+ return getItem(getCount()-1);
+ }
+ public void addAll(List notes){
+
+ if (notes.size() == 0) {
+ // No more notes available
+ mAllNotesLoaded = true;
+ mProgressFooterView.setVisibility(View.GONE);
+ } else {
+ Iterator noteIterator = notes.iterator();
+ while(noteIterator.hasNext()){
+ add(noteIterator.next());
+ }
+ }
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ if (mProgressFooterView != null)
+ mProgressFooterView.setVisibility(View.GONE);
+ }
+ }
+
+ private class ListScrollListener implements AbsListView.OnScrollListener {
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){
+ if (mAllNotesLoaded)
+ return;
+
+ // if we're within 5 from the last item we should ask for more items
+ if (firstVisibleItem + visibleItemCount >= totalItemCount - LOAD_MORE_WITHIN_X_ROWS) {
+ if (totalItemCount <= 1)
+ mProgressFooterView.setVisibility(View.GONE);
+ else
+ mProgressFooterView.setVisibility(View.VISIBLE);
+
+ requestMoreNotifications();
+ }
+ }
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState){
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/ReplyField.java b/src/org/wordpress/android/ui/notifications/ReplyField.java
new file mode 100644
index 000000000000..b79d12e3e9b4
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/ReplyField.java
@@ -0,0 +1,81 @@
+package org.wordpress.android.ui.notifications;
+
+import android.view.View;
+import android.view.KeyEvent;
+import android.text.Editable;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.EditText;
+import android.content.Context;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+
+import org.wordpress.android.R;
+
+public class ReplyField extends LinearLayout {
+ interface OnReplyListener {
+ public void onReply(ReplyField field, Editable replyText);
+ }
+
+ EditText mTextField;
+ ImageButton mReplyButton;
+ OnReplyListener mOnReplyListener;
+ public ReplyField(Context context){
+ super(context);
+ }
+ public ReplyField(Context context, AttributeSet attributes){
+ super(context, attributes);
+ }
+ public ReplyField(Context context, AttributeSet attributes, int defStyle){
+ super(context, attributes, defStyle);
+ }
+ public void clearFocus(){
+ getTextField().clearFocus();
+ }
+ public void setOnReplyListener(OnReplyListener l){
+ mOnReplyListener = l;
+ }
+ public ImageButton getReplyButton(){
+ return mReplyButton;
+ }
+ public EditText getTextField(){
+ return mTextField;
+ }
+ public Editable getText(){
+ return mTextField.getText();
+ }
+ public void setText(CharSequence text){
+ mTextField.setText(text);
+ }
+ public boolean isEmpty(){
+ return TextUtils.isEmpty(getText());
+ }
+ protected void onFinishInflate(){
+ mTextField = (EditText) findViewById(R.id.note_reply_field);
+ mReplyButton = (ImageButton) findViewById(R.id.note_reply_button);
+ mReplyButton.setEnabled(!isEmpty());
+ mReplyButton.setOnClickListener(new View.OnClickListener(){
+ @Override
+ public void onClick(View v){
+ if (!isEmpty()) {
+ mOnReplyListener.onReply(ReplyField.this, getText());
+ }
+ }
+ });
+ mTextField.addTextChangedListener(new TextWatcher(){
+ @Override
+ public void afterTextChanged(Editable text){
+ mReplyButton.setEnabled(!isEmpty());
+ }
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after){
+ // noop
+ }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count){
+ // noop
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/ReplyList.java b/src/org/wordpress/android/ui/notifications/ReplyList.java
new file mode 100644
index 000000000000..7a60f8afea12
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/ReplyList.java
@@ -0,0 +1,51 @@
+package org.wordpress.android.ui.notifications;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.os.Build;
+
+import org.wordpress.android.R;
+import org.wordpress.android.models.Note;
+
+public class ReplyList extends LinearLayout {
+ // Gingerbread has weird layout issues regarding clipChildren and clipToPadding so we're
+ // disabling the animations for anything before Jelly Bean
+ private static final boolean ANIMATE=Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
+ public ReplyList(Context context){
+ super(context);
+ }
+ public ReplyList(Context context, AttributeSet attrs){
+ super(context, attrs);
+ }
+ public ReplyList(Context context, AttributeSet attrs, int defStyle){
+ super(context, attrs, defStyle);
+ }
+ protected void onFinishInflate(){
+ if (ANIMATE) {
+ setClipChildren(false);
+ setClipToPadding(false);
+ }
+ }
+ /**
+ * Add a reply item
+ */
+ public ReplyRow addReply(Note.Reply reply){
+ // inflate the view
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingLeft());
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ ReplyRow row = (ReplyRow) inflater.inflate(R.layout.notifications_reply_row, this, false);
+ addView(row);
+
+ if (ANIMATE) {
+ Animation zoom = AnimationUtils.loadAnimation(getContext(), R.anim.zoom);
+ row.startAnimation(zoom);
+ }
+
+ return row;
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/ReplyRow.java b/src/org/wordpress/android/ui/notifications/ReplyRow.java
new file mode 100644
index 000000000000..7cced6d6617d
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/ReplyRow.java
@@ -0,0 +1,91 @@
+package org.wordpress.android.ui.notifications;
+
+import android.content.Intent;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.net.Uri;
+import android.provider.Browser;
+
+import com.android.volley.toolbox.NetworkImageView;
+
+import org.wordpress.android.R;
+
+class ReplyRow extends LinearLayout {
+ private ImageView mButton, mErrorIndicator;
+ private NetworkImageView mAvatar;
+ private TextView mTextField;
+ private ProgressBar mProgress;
+ public ReplyRow(Context context){
+ super(context);
+ }
+ public ReplyRow(Context context, AttributeSet attrs){
+ super(context, attrs);
+ }
+ public ReplyRow(Context context, AttributeSet attrs, int defStyle){
+ super(context, attrs, defStyle);
+ }
+ protected void onFinishInflate(){
+ mButton = (ImageView) findViewById(R.id.button);
+ mAvatar = (NetworkImageView) findViewById(R.id.avatar);
+ mTextField = (TextView) findViewById(R.id.text);
+ mProgress = (ProgressBar) findViewById(R.id.progress);
+ mErrorIndicator = (ImageView) findViewById(R.id.error);
+ }
+
+ public void setComplete(boolean complete){
+ if (complete) {
+ mProgress.setVisibility(View.GONE);
+ mAvatar.setVisibility(View.VISIBLE);
+ } else {
+ mProgress.setVisibility(View.VISIBLE);
+ mAvatar.setVisibility(View.GONE);
+ }
+ }
+
+ public void setFailed(boolean failed){
+ if (failed) {
+ mProgress.setVisibility(View.GONE);
+ mAvatar.setVisibility(View.GONE);
+ mErrorIndicator.setVisibility(View.VISIBLE);
+ } else {
+ mErrorIndicator.setVisibility(View.GONE);
+ }
+ }
+
+ public void setUrl(final String url){
+ if (url == null) {
+ mButton.setVisibility(View.GONE);
+ setOnClickListener(null);
+ } else {
+ mButton.setVisibility(View.VISIBLE);
+ setOnClickListener(new View.OnClickListener(){
+ @Override
+ public void onClick(View view){
+ Uri uri = Uri.parse(url);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ Context context = getContext();
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+ context.startActivity(intent);
+ }
+ });
+ }
+ }
+
+ public void setText(CharSequence text){
+ mTextField.setText(text);
+ }
+
+ public void setText(int stringRes){
+ mTextField.setText(stringRes);
+ }
+
+ public NetworkImageView getImageView(){
+ return mAvatar;
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/notifications/SingleLineListFragment.java b/src/org/wordpress/android/ui/notifications/SingleLineListFragment.java
new file mode 100644
index 000000000000..ac342d742b2a
--- /dev/null
+++ b/src/org/wordpress/android/ui/notifications/SingleLineListFragment.java
@@ -0,0 +1,120 @@
+/**
+ * Behaves much list a ListFragment
+ */
+package org.wordpress.android.ui.notifications;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import org.wordpress.android.R;
+import org.wordpress.android.WordPress;
+import org.wordpress.android.models.Note;
+import org.wordpress.android.util.JSONUtil;
+
+public class SingleLineListFragment extends ListFragment implements NotificationFragment {
+ public static final String TAG="NoteDetail";
+ public static final String NOTE_ID_ARGUMENT="note_id";
+ public static final String NOTE_JSON_ARGUMENT="note_json";
+
+ protected Note mNote;
+
+ @Override
+ public void onCreate(Bundle bundle){
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle bundle){
+ super.onActivityCreated(bundle);
+
+ ListView list = getListView();
+ list.setDivider(getResources().getDrawable(R.drawable.list_divider));
+ list.setDividerHeight(1);
+ list.setHeaderDividersEnabled(false);
+
+ // set the header
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ DetailHeader noteHeader = (DetailHeader) inflater.inflate(R.layout.notifications_detail_header, null);
+ noteHeader.setText(getNote().queryJSON("body.header_text", ""));
+ noteHeader.setBackgroundColor(getResources().getColor(R.color.light_gray));
+ noteHeader.getTextView().setGravity(Gravity.CENTER_HORIZONTAL);
+ noteHeader.setClickable(false);
+ list.addHeaderView(noteHeader);
+
+ // set the footer
+ DetailHeader noteFooter = (DetailHeader) inflater.inflate(R.layout.notifications_detail_header, null);
+ String footerText = getNote().queryJSON("body.footer_text", "");
+ if (!footerText.equals("")) {
+ noteFooter.setText(footerText);
+ String footerUrl = getNote().queryJSON("body.footer_link", "");
+ if (!footerUrl.equals("")) {
+ noteFooter.setUrl(footerUrl);
+ }
+ list.addFooterView(noteFooter);
+ }
+ // set the adapter
+ setListAdapter(new NoteAdapter());
+ }
+
+ @Override
+ public void setListAdapter(ListAdapter adapter) {
+ super.setListAdapter(adapter);
+ }
+
+ @Override
+ public void setNote(Note note){
+ mNote = note;
+ }
+
+ @Override
+ public Note getNote(){
+ return mNote;
+ }
+
+ class NoteAdapter extends BaseAdapter {
+ private JSONArray mItems;
+ NoteAdapter(){
+ mItems = getNote().queryJSON("body.items", new JSONArray());
+ }
+
+ public View getView(int position, View cachedView, ViewGroup parent){
+ View v;
+ if (cachedView == null) {
+ v = getActivity().getLayoutInflater().inflate(R.layout.notifications_follow_row, null);
+ } else {
+ v = cachedView;
+ }
+ JSONObject noteItem = getItem(position);
+ JSONObject followAction = JSONUtil.queryJSON(noteItem, "action", new JSONObject());
+ FollowRow row = (FollowRow) v;
+ row.setListener(new FollowListener(getActivity().getApplicationContext()));
+ row.setAction(followAction);
+ row.setText(JSONUtil.queryJSON(noteItem, "header_text", ""));
+ row.getImageView().setImageUrl(JSONUtil.queryJSON(noteItem, "icon", ""), WordPress.imageLoader);
+
+ return v;
+ }
+
+ public long getItemId(int position){
+ return (long) position;
+ }
+
+ public JSONObject getItem(int position){
+ return JSONUtil.queryJSON(mItems, String.format("[%d]", position), new JSONObject());
+ }
+
+ public int getCount(){
+ return mItems.length();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/wordpress/android/ui/posts/EditPostActivity.java b/src/org/wordpress/android/ui/posts/EditPostActivity.java
index 424b28c2cfee..7d302b54203c 100644
--- a/src/org/wordpress/android/ui/posts/EditPostActivity.java
+++ b/src/org/wordpress/android/ui/posts/EditPostActivity.java
@@ -20,7 +20,6 @@
import android.app.TimePickerDialog;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Address;
@@ -99,7 +98,7 @@
import org.wordpress.android.util.LocationHelper;
import org.wordpress.android.util.LocationHelper.LocationResult;
import org.wordpress.android.util.PostUploadService;
-import org.wordpress.android.util.StringHelper;
+import org.wordpress.android.util.StringUtils;
import org.wordpress.android.util.WPEditText;
import org.wordpress.android.util.WPHtml;
import org.wordpress.android.util.WPImageSpan;
@@ -1643,7 +1642,7 @@ protected void setContent() {
} else {
// add link tag around URLs, trac #64
text = text.replaceAll("((http|https|ftp|mailto):\\S+)", "$1 ");
- mContentEditText.setText(WPHtml.fromHtml(StringHelper.addPTags(text), EditPostActivity.this, mPost));
+ mContentEditText.setText(WPHtml.fromHtml(StringUtils.addPTags(text), EditPostActivity.this, mPost));
}
} else {
String action = intent.getAction();
diff --git a/src/org/wordpress/android/ui/posts/PostsActivity.java b/src/org/wordpress/android/ui/posts/PostsActivity.java
index ce5d77332455..7f8b2412235e 100644
--- a/src/org/wordpress/android/ui/posts/PostsActivity.java
+++ b/src/org/wordpress/android/ui/posts/PostsActivity.java
@@ -4,6 +4,7 @@
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;
@@ -32,18 +33,15 @@
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.models.Post;
-import org.wordpress.android.ui.DashboardActivity;
-import org.wordpress.android.ui.StatsActivity;
-import org.wordpress.android.ui.ViewSiteActivity;
+import org.wordpress.android.ui.MenuDrawerItem;
import org.wordpress.android.ui.WPActionBarActivity;
import org.wordpress.android.ui.comments.AddCommentActivity;
-import org.wordpress.android.ui.comments.CommentsActivity;
import org.wordpress.android.ui.posts.ViewPostFragment.OnDetailPostActionListener;
import org.wordpress.android.ui.posts.ViewPostsFragment.OnPostActionListener;
import org.wordpress.android.ui.posts.ViewPostsFragment.OnPostSelectedListener;
import org.wordpress.android.ui.posts.ViewPostsFragment.OnRefreshListener;
-import org.wordpress.android.ui.reader.ReaderActivity;
import org.wordpress.android.util.WPAlertDialogFragment.OnDialogConfirmListener;
+import org.wordpress.android.ui.notifications.NotificationsActivity;
public class PostsActivity extends WPActionBarActivity implements OnPostSelectedListener,
OnRefreshListener, OnPostActionListener, OnDetailPostActionListener, OnDialogConfirmListener {
@@ -70,52 +68,47 @@ public void onCreate(Bundle savedInstanceState) {
return;
}
+ // Check if we came from a notification, if so let's launch NotificationsActivity
+ Bundle extras = getIntent().getExtras();
+ if (extras != null && extras.getBoolean(NotificationsActivity.FROM_NOTIFICATION_EXTRA)) {
+ startNotificationsAcivity(extras);
+ return;
+ }
+
// Restore last selection on app creation
if (WordPress.shouldRestoreSelectedActivity && WordPress.getCurrentBlog() != null
&& !(this instanceof PagesActivity)) {
+ // Refresh blog content when returning to the app
+ new ApiHelper.RefreshBlogContentTask(this, WordPress.getCurrentBlog()).execute(false);
+
WordPress.shouldRestoreSelectedActivity = false;
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
- int lastActivitySelection = settings.getInt("wp_pref_last_activity", -1);
- if (lastActivitySelection >= 0) {
- Intent i = null;
- switch (lastActivitySelection) {
- case READER_ACTIVITY:
- i = new Intent(this, ReaderActivity.class);
- break;
- case PAGES_ACTIVITY:
- i = new Intent(this, PagesActivity.class);
- i.putExtra("viewPages", true);
- break;
- case COMMENTS_ACTIVITY:
- i = new Intent(this, CommentsActivity.class);
- break;
- case STATS_ACTIVITY:
- i = new Intent(this, StatsActivity.class);
- break;
- case VIEW_SITE_ACTIVITY:
- i = new Intent(this, ViewSiteActivity.class);
- break;
- case DASHBOARD_ACTIVITY:
- i = new Intent(this, DashboardActivity.class);
- break;
- }
- if (i != null) {
- startActivity(i);
- finish();
+ int lastActivitySelection = settings.getInt(LAST_ACTIVITY_PREFERENCE, -1);
+ if (lastActivitySelection > MenuDrawerItem.NO_ITEM_ID) {
+ Iterator itemIterator = mMenuItems.iterator();
+ while(itemIterator.hasNext()){
+ MenuDrawerItem item = itemIterator.next();
+ // if we have a matching item id, and it's not selected and it's visible, call it
+ if (item.hasItemId() && item.getItemId() == lastActivitySelection && !item.isSelected() && item.isVisible()) {
+ mFirstLaunch = true;
+ item.selectItem();
+ finish();
+ return;
+ }
}
}
}
-
+
createMenuDrawer(R.layout.posts);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(true);
FragmentManager fm = getSupportFragmentManager();
+ fm.addOnBackStackChangedListener(mOnBackStackChangedListener);
postList = (ViewPostsFragment) fm.findFragmentById(R.id.postList);
postList.setListShown(true);
- Bundle extras = getIntent().getExtras();
if (extras != null) {
isPage = extras.getBoolean("viewPages");
String errorMessage = extras.getString("errorMessage");
@@ -145,8 +138,6 @@ public void OnPostUploaded() {
attemptToSelectPost();
}
-
-
private void showPostUploadErrorAlert(String errorMessage) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(
@@ -166,14 +157,18 @@ public void onClick(DialogInterface dialog,
dialogBuilder.create().show();
}
-
-
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Bundle extras = intent.getExtras();
if (extras != null) {
+ // Check if we came from a notification, if so let's launch NotificationsActivity
+ if (extras.getBoolean(NotificationsActivity.FROM_NOTIFICATION_EXTRA)) {
+ startNotificationsAcivity(extras);
+ return;
+ }
+
String errorMessage = extras.getString("errorMessage");
if (errorMessage != null)
showPostUploadErrorAlert(errorMessage);
@@ -181,7 +176,25 @@ protected void onNewIntent(Intent intent) {
}
+ private void startNotificationsAcivity(Bundle extras) {
+ // Manually set last selection to notifications
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(LAST_ACTIVITY_PREFERENCE, NOTIFICATIONS_ACTIVITY);
+ editor.commit();
+
+ Intent i = new Intent(this, NotificationsActivity.class);
+ i.putExtras(extras);
+ startActivity(i);
+ finish();
+ }
+ private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
+ public void onBackStackChanged() {
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0)
+ mMenuDrawer.setDrawerIndicatorEnabled(true);
+ }
+ };
protected void checkForLocalChanges(boolean shouldPrompt) {
boolean hasLocalChanges = WordPress.wpDB.findLocalChanges();
@@ -277,8 +290,8 @@ public boolean onCreateOptionsMenu(Menu menu) {
public boolean onOptionsItemSelected(final MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_refresh) {
- postList.refreshPosts(false);
- new ApiHelper.RefreshBlogContentTask(this, WordPress.currentBlog).execute(true);
+ checkForLocalChanges(true);
+ new ApiHelper.RefreshBlogContentTask(this, WordPress.currentBlog).execute(false);
return true;
} else if (itemId == R.id.menu_new_post) {
Intent i = new Intent(this, EditPostActivity.class);
@@ -350,6 +363,7 @@ public void onPostSelected(Post post) {
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
+ mMenuDrawer.setDrawerIndicatorEnabled(false);
} else {
f.loadPost(post);
}
diff --git a/src/org/wordpress/android/ui/posts/ViewPostFragment.java b/src/org/wordpress/android/ui/posts/ViewPostFragment.java
index aff01a3e11bb..97eedf2b6ed4 100644
--- a/src/org/wordpress/android/ui/posts/ViewPostFragment.java
+++ b/src/org/wordpress/android/ui/posts/ViewPostFragment.java
@@ -15,7 +15,7 @@
import org.wordpress.android.WordPress;
import org.wordpress.android.models.Post;
import org.wordpress.android.util.EscapeUtils;
-import org.wordpress.android.util.StringHelper;
+import org.wordpress.android.util.StringUtils;
public class ViewPostFragment extends Fragment {
/** Called when the activity is first created. */
@@ -161,7 +161,7 @@ public void loadPost(Post post) {
tv.setVisibility(View.GONE);
webView.setVisibility(View.VISIBLE);
- String html = StringHelper.addPTags(post.getDescription()
+ String html = StringUtils.addPTags(post.getDescription()
+ "\n\n" + post.getMt_text_more());
String htmlText = ""
diff --git a/src/org/wordpress/android/ui/posts/ViewPostsFragment.java b/src/org/wordpress/android/ui/posts/ViewPostsFragment.java
index e8c3be8da055..218edeebf0a7 100644
--- a/src/org/wordpress/android/ui/posts/ViewPostsFragment.java
+++ b/src/org/wordpress/android/ui/posts/ViewPostsFragment.java
@@ -32,6 +32,7 @@
import android.widget.TextView;
import android.widget.ViewSwitcher;
+import org.xmlrpc.android.ApiHelper;
import org.xmlrpc.android.XMLRPCClient;
import org.xmlrpc.android.XMLRPCException;
@@ -40,7 +41,7 @@
import org.wordpress.android.models.Blog;
import org.wordpress.android.models.Post;
import org.wordpress.android.util.EscapeUtils;
-import org.wordpress.android.util.StringHelper;
+import org.wordpress.android.util.StringUtils;
import org.wordpress.android.util.WPAlertDialogFragment;
public class ViewPostsFragment extends ListFragment {
@@ -234,11 +235,11 @@ public boolean loadPosts(boolean loadMore) { // loads posts from the db
boolean drafts = loadDrafts();
if (drafts) {
- mPostIDs = StringHelper.mergeStringArrays(mDraftIDs, mPostIDs);
- mTitles = StringHelper.mergeStringArrays(mDraftTitles, mTitles);
- mDateCreatedFormatted = StringHelper.mergeStringArrays(
+ mPostIDs = StringUtils.mergeStringArrays(mDraftIDs, mPostIDs);
+ mTitles = StringUtils.mergeStringArrays(mDraftTitles, mTitles);
+ mDateCreatedFormatted = StringUtils.mergeStringArrays(
mDraftDateCreated, mDateCreatedFormatted);
- mStatuses = StringHelper.mergeStringArrays(mDraftStatuses, mStatuses);
+ mStatuses = StringUtils.mergeStringArrays(mDraftStatuses, mStatuses);
} else {
if (mPostListAdapter != null) {
mPostListAdapter.notifyDataSetChanged();
@@ -249,6 +250,8 @@ public boolean loadPosts(boolean loadMore) { // loads posts from the db
ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setBackgroundColor(getResources().getColor(R.color.list_row_bg));
+ listView.setDivider(getResources().getDrawable(R.drawable.list_divider));
+ listView.setDividerHeight(1);
listView.removeFooterView(switcher);
if (loadedPosts != null) {
if (loadedPosts.size() >= 20) {
@@ -386,6 +389,8 @@ public void onCreateContextMenu(ContextMenu menu, View v,
if (loadedPosts == null) {
refreshPosts(false);
+ if (!isPage)
+ new ApiHelper.RefreshBlogContentTask(getActivity(), WordPress.getCurrentBlog()).execute(false);
}
return false;
diff --git a/src/org/wordpress/android/ui/prefs/BlogPreferencesActivity.java b/src/org/wordpress/android/ui/prefs/BlogPreferencesActivity.java
index e1ad4b152486..4a1bafe90722 100644
--- a/src/org/wordpress/android/ui/prefs/BlogPreferencesActivity.java
+++ b/src/org/wordpress/android/ui/prefs/BlogPreferencesActivity.java
@@ -35,6 +35,15 @@ public class BlogPreferencesActivity extends SherlockFragmentActivity {
/** The blog this activity is managing settings for. */
private Blog blog;
private boolean mBlogDeleted;
+ private EditText mUsernameET;
+ private EditText mPasswordET;
+ private EditText mHttpUsernameET;
+ private EditText mHttpPasswordET;
+ private CheckBox mFullSizeCB;
+ private CheckBox mScaledCB;
+ private CheckBox mLocationCB;
+ private Spinner mImageWidthSpinner;
+ private EditText mScaledImageWidthET;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -44,7 +53,30 @@ public void onCreate(Bundle savedInstanceState) {
Integer id = getIntent().getIntExtra("id", -1);
blog = WordPress.getBlog(id);
+ if (blog == null) {
+ Toast.makeText(this, getString(R.string.blog_not_found), Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
getSupportActionBar().setTitle(EscapeUtils.unescapeHtml(blog.getBlogName()));
+
+ mUsernameET = (EditText) findViewById(R.id.username);
+ mPasswordET = (EditText) findViewById(R.id.password);
+ mHttpUsernameET = (EditText) findViewById(R.id.httpuser);
+ mHttpPasswordET = (EditText) findViewById(R.id.httppassword);
+ mScaledImageWidthET = (EditText) findViewById(R.id.scaledImageWidth);
+ mFullSizeCB = (CheckBox) findViewById(R.id.fullSizeImage);
+ mScaledCB = (CheckBox) findViewById(R.id.scaledImage);
+ mLocationCB = (CheckBox) findViewById(R.id.location);
+ mImageWidthSpinner = (Spinner) findViewById(R.id.maxImageWidth);
+
+ if (blog.isDotcomFlag()) {
+ // Hide credentials section
+ RelativeLayout credentialsRL = (RelativeLayout)findViewById(R.id.section1);
+ credentialsRL.setVisibility(View.GONE);
+ }
+
loadSettingsForBlog();
}
@@ -55,19 +87,13 @@ protected void onPause() {
if (mBlogDeleted)
return;
- EditText usernameET = (EditText) findViewById(R.id.username);
- blog.setUsername(usernameET.getText().toString());
- EditText passwordET = (EditText) findViewById(R.id.password);
- blog.setPassword(passwordET.getText().toString());
- EditText httpuserET = (EditText) findViewById(R.id.httpuser);
- blog.setHttpuser(httpuserET.getText().toString());
- EditText httppasswordET = (EditText) findViewById(R.id.httppassword);
- blog.setHttppassword(httppasswordET.getText().toString());
+ blog.setUsername(mUsernameET.getText().toString());
+ blog.setPassword(mPasswordET.getText().toString());
+ blog.setHttpuser(mHttpUsernameET.getText().toString());
+ blog.setHttppassword(mHttpPasswordET.getText().toString());
- CheckBox fullSize = (CheckBox) findViewById(R.id.fullSizeImage);
- blog.setFullSizeImage(fullSize.isChecked());
- CheckBox scaledImage = (CheckBox) findViewById(R.id.scaledImage);
- blog.setScaledImage(scaledImage.isChecked());
+ blog.setFullSizeImage(mFullSizeCB.isChecked());
+ blog.setScaledImage(mScaledCB.isChecked());
if (blog.isScaledImage()) {
EditText scaledImgWidth = (EditText) findViewById(R.id.scaledImageWidth);
@@ -97,16 +123,15 @@ public void onClick(DialogInterface dialog, int whichButton) {
blog.setScaledImageWidth(width);
}
}
- Spinner spinner = (Spinner) findViewById(R.id.maxImageWidth);
- blog.setMaxImageWidth(spinner.getSelectedItem().toString());
+
+ blog.setMaxImageWidth(mImageWidthSpinner.getSelectedItem().toString());
- long maxImageWidthId = spinner.getSelectedItemId();
+ long maxImageWidthId = mImageWidthSpinner.getSelectedItemId();
int maxImageWidthIdInt = (int) maxImageWidthId;
blog.setMaxImageWidthId(maxImageWidthIdInt);
- CheckBox locationCB = (CheckBox) findViewById(R.id.location);
- blog.setLocation(locationCB.isChecked());
+ blog.setLocation(mLocationCB.isChecked());
blog.save(originalUsername);
@@ -131,7 +156,6 @@ private void loadSettingsForBlog() {
((TextView) findViewById(R.id.l_maxImageWidth)).setText(getResources().getString(R.string.max_thumbnail_px_width).toUpperCase(Locale.getDefault()));
((TextView) findViewById(R.id.l_httpuser)).setText(getResources().getString(R.string.http_credentials).toUpperCase(Locale.getDefault()));
- Spinner spinner = (Spinner) this.findViewById(R.id.maxImageWidth);
ArrayAdapter
spinnerArrayAdapter = new ArrayAdapter(this,
R.layout.spinner_textview, new String[] {
"Original Size", "100", "200", "300", "400", "500", "600", "700", "800",
@@ -139,37 +163,28 @@ private void loadSettingsForBlog() {
"1800", "1900", "2000"
});
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spinner.setAdapter(spinnerArrayAdapter);
+ mImageWidthSpinner.setAdapter(spinnerArrayAdapter);
- EditText usernameET = (EditText) findViewById(R.id.username);
- usernameET.setText(blog.getUsername());
+ mUsernameET.setText(blog.getUsername());
originalUsername = blog.getUsername();
-
- EditText passwordET = (EditText) findViewById(R.id.password);
- passwordET.setText(blog.getPassword());
-
- EditText httpUserET = (EditText) findViewById(R.id.httpuser);
- httpUserET.setText(blog.getHttpuser());
-
- EditText httpPasswordET = (EditText) findViewById(R.id.httppassword);
- httpPasswordET.setText(blog.getHttppassword());
- TextView httpUserLabel = (TextView) findViewById(R.id.l_httpuser);
+ mPasswordET.setText(blog.getPassword());
+ mHttpUsernameET.setText(blog.getHttpuser());
+ mHttpPasswordET.setText(blog.getHttppassword());
+ TextView httpUserLabel = (TextView)findViewById(R.id.l_httpuser);
if (blog.isDotcomFlag()) {
- httpPasswordET.setVisibility(View.GONE);
+ mHttpUsernameET.setVisibility(View.GONE);
+ mHttpPasswordET.setVisibility(View.GONE);
httpUserLabel.setVisibility(View.GONE);
- httpUserET.setVisibility(View.GONE);
} else {
- httpPasswordET.setVisibility(View.VISIBLE);
+ mHttpUsernameET.setVisibility(View.VISIBLE);
+ mHttpPasswordET.setVisibility(View.VISIBLE);
httpUserLabel.setVisibility(View.VISIBLE);
- httpUserET.setVisibility(View.VISIBLE);
}
- CheckBox fullSize = (CheckBox) findViewById(R.id.fullSizeImage);
- fullSize.setChecked(blog.isFullSizeImage());
- CheckBox scaledImage = (CheckBox) findViewById(R.id.scaledImage);
- scaledImage.setChecked(blog.isScaledImage());
- EditText scaledImageWidth = (EditText) findViewById(R.id.scaledImageWidth);
- scaledImageWidth.setText("" + blog.getScaledImageWidth());
+ mFullSizeCB.setChecked(blog.isFullSizeImage());
+ mScaledCB.setChecked(blog.isScaledImage());
+
+ this.mScaledImageWidthET.setText("" + blog.getScaledImageWidth());
showScaledSetting(blog.isScaledImage());
// sets up a state listener for the scaled image checkbox
((CheckBox) findViewById(R.id.scaledImage)).setOnClickListener(new View.OnClickListener() {
@@ -207,16 +222,15 @@ public void onClick(View v) {
}
}
- CheckBox locationCB = (CheckBox) findViewById(R.id.location);
if (hasLocationProvider) {
- locationCB.setChecked(blog.isLocation());
+ mLocationCB.setChecked(blog.isLocation());
} else {
- locationCB.setChecked(false);
+ mLocationCB.setChecked(false);
RelativeLayout locationLayout = (RelativeLayout) findViewById(R.id.section3);
locationLayout.setVisibility(View.GONE);
}
- spinner.setSelection(blog.getMaxImageWidthId());
+ mImageWidthSpinner.setSelection(blog.getMaxImageWidthId());
}
/**
diff --git a/src/org/wordpress/android/ui/prefs/PreferencesActivity.java b/src/org/wordpress/android/ui/prefs/PreferencesActivity.java
index afe7a6d76a3b..e1cc1f8911ec 100644
--- a/src/org/wordpress/android/ui/prefs/PreferencesActivity.java
+++ b/src/org/wordpress/android/ui/prefs/PreferencesActivity.java
@@ -2,44 +2,72 @@
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
-import android.preference.ListPreference;
import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceManager;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
import com.actionbarsherlock.view.MenuItem;
+import com.google.android.gcm.GCMRegistrar;
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.internal.StringMap;
+
+import org.xmlrpc.android.WPComXMLRPCApi;
+import org.xmlrpc.android.XMLRPCCallback;
+import org.xmlrpc.android.XMLRPCException;
-import org.wordpress.android.CommentBroadcastReceiver;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.models.Blog;
+import org.wordpress.android.ui.WPActionBarActivity;
+import org.wordpress.android.ui.accounts.AccountSetupActivity;
import org.wordpress.android.ui.accounts.NewAccountActivity;
-import org.wordpress.android.util.CommentService;
import org.wordpress.android.util.DeviceUtils;
import org.wordpress.android.util.EscapeUtils;
+@SuppressWarnings("deprecation")
public class PreferencesActivity extends SherlockPreferenceActivity {
- ListPreference notificationIntervalPreference;
EditTextPreference taglineTextPreference;
OnPreferenceChangeListener preferenceChangeListener;
-
+
+ private Object[] mTypeList;
+ private ArrayList> mMutedBlogsList;
+ private Map mNotificationSettings;
+ private SharedPreferences mSettings;
+
+ private PreferenceGroup mNotificationsGroup;
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+
+ overridePendingTransition(R.anim.slide_up, R.anim.do_nothing);
setTitle(getResources().getText(R.string.settings));
@@ -55,6 +83,8 @@ public void onCreate(Bundle icicle) {
}
}
addPreferencesFromResource(R.xml.preferences);
+
+ mNotificationsGroup = (PreferenceGroup)findPreference("wp_pref_notifications_category");
preferenceChangeListener = new OnPreferenceChangeListener() {
@Override
@@ -65,10 +95,29 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
}
};
- notificationIntervalPreference = (ListPreference) findPreference("wp_pref_notifications_interval");
- notificationIntervalPreference.setOnPreferenceChangeListener(preferenceChangeListener);
taglineTextPreference = (EditTextPreference) findPreference("wp_pref_post_signature");
taglineTextPreference.setOnPreferenceChangeListener(preferenceChangeListener);
+
+ Preference signOutPreference = (Preference) findPreference("wp_pref_sign_out");
+ signOutPreference.setOnPreferenceClickListener(signOutPreferenceClickListener);
+
+ mSettings = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Request notification settings if needed
+ if (WordPress.hasValidWPComCredentials(PreferencesActivity.this)) {
+ String settingsJson = mSettings.getString("wp_pref_notification_settings", null);
+ if (settingsJson == null) {
+ new WPComXMLRPCApi().getNotificationSettings(new XMLRPCCallback() {
+ public void onSuccess(long id, Object result) {
+ refreshWPComAuthCategory();
+ }
+
+ public void onFailure(long id, XMLRPCException error) {
+ // prompt?
+ }
+ }, this);
+ }
+ }
displayPreferences();
}
@@ -81,6 +130,15 @@ public void onResume() {
updateBlogsPreferenceCategory();
}
+
+
+ @Override
+ protected void onPause() {
+ overridePendingTransition(R.anim.do_nothing, R.anim.slide_down);
+ setResult(RESULT_OK);
+ super.onPause();
+ }
+
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
@@ -142,43 +200,11 @@ protected int getEnabledBlogsCount() {
}
public void displayPreferences() {
- List> accounts = WordPress.wpDB.getAccounts();
- if (accounts.size() > 0) {
-
- for (int i = 0; i < accounts.size(); i++) {
-
- Map curHash = accounts.get(i);
- String curBlogName = curHash.get("blogName").toString();
- String accountID = curHash.get("id").toString();
- int runService = Integer.valueOf(curHash.get("runService").toString());
-
- PreferenceScreen selectBlogsCategory = (PreferenceScreen) findPreference("wp_pref_notification_blogs");
-
- CheckBoxPreference blogNotificationPreference = new CheckBoxPreference(this);
- blogNotificationPreference.setKey(accountID);
- blogNotificationPreference.setTitle(EscapeUtils.unescapeHtml(curBlogName));
- blogNotificationPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- int blogID;
- try {
- blogID = Integer.valueOf(preference.getKey());
- WordPress.wpDB.updateNotificationFlag(blogID, (Boolean) newValue);
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- return true;
- }
- });
- if (runService == 1)
- blogNotificationPreference.setChecked(true);
- else
- blogNotificationPreference.setChecked(false);
-
- selectBlogsCategory.addPreference(blogNotificationPreference);
- }
- }
-
+
+ // WordPress.com auth area and notifications
+ refreshWPComAuthCategory();
+
+ // Post signature
if (taglineTextPreference.getText() == null || taglineTextPreference.getText().equals("")) {
if (DeviceUtils.getInstance().isBlackBerry()) {
taglineTextPreference.setSummary(R.string.posted_from_blackberry);
@@ -190,13 +216,6 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
} else {
taglineTextPreference.setSummary(taglineTextPreference.getText());
}
-
- if (notificationIntervalPreference.getValue() == null || notificationIntervalPreference.getValue().equals("")) {
- notificationIntervalPreference.setValue("10 Minutes");
- notificationIntervalPreference.setSummary("10 Minutes");
- } else {
- notificationIntervalPreference.setSummary(notificationIntervalPreference.getValue());
- }
if (DeviceUtils.getInstance().isBlackBerry()) {
PreferenceCategory appAboutSectionName = (PreferenceCategory) findPreference("wp_pref_app_about_section");
@@ -205,55 +224,279 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
appName.setTitle(getString(R.string.app_title_blackberry));
}
}
-
- @Override
- protected void onPause() {
- if (getEnabledBlogsCount() > 0) {
-
- int UPDATE_INTERVAL = 3600000;
- ListPreference notificationIntervalPreference = (ListPreference) findPreference("wp_pref_notifications_interval");
- String notificationInterval = notificationIntervalPreference.getValue();
- // configure time interval
- if (notificationInterval.equals("5 Minutes")) {
- UPDATE_INTERVAL = 300000;
- } else if (notificationInterval.equals("10 Minutes")) {
- UPDATE_INTERVAL = 600000;
- } else if (notificationInterval.equals("15 Minutes")) {
- UPDATE_INTERVAL = 900000;
- } else if (notificationInterval.equals("30 Minutes")) {
- UPDATE_INTERVAL = 1800000;
- } else if (notificationInterval.equals("1 Hour")) {
- UPDATE_INTERVAL = 3600000;
- } else if (notificationInterval.equals("3 Hours")) {
- UPDATE_INTERVAL = 10800000;
- } else if (notificationInterval.equals("6 Hours")) {
- UPDATE_INTERVAL = 21600000;
- } else if (notificationInterval.equals("12 Hours")) {
- UPDATE_INTERVAL = 43200000;
- } else if (notificationInterval.equals("Daily")) {
- UPDATE_INTERVAL = 86400000;
+
+ /**
+ * Listens for changes to notification type settings
+ */
+ private OnPreferenceChangeListener mTypeChangeListener = new OnPreferenceChangeListener() {
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ // Update the mNoteSettings map with the new value
+ if (preference instanceof CheckBoxPreference) {
+ CheckBoxPreference checkBoxPreference = (CheckBoxPreference) preference;
+ boolean isChecked = (Boolean) newValue;
+ String key = preference.getKey();
+ StringMap typeMap = (StringMap) mNotificationSettings.get(key);
+ typeMap.put("value", (isChecked) ? 1 : 0);
+ mNotificationSettings.put(key, typeMap);
+ checkBoxPreference.setChecked(isChecked);
+ new sendNotificationSettingsTask().execute();
}
+ return false;
+ }
+ };
+
+ /**
+ * Listens for changes to notification blogs settings
+ */
+ private OnPreferenceChangeListener mMuteBlogChangeListener = new OnPreferenceChangeListener() {
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference instanceof CheckBoxPreference) {
+ CheckBoxPreference checkBoxPreference = (CheckBoxPreference) preference;
+ boolean isChecked = (Boolean) newValue;
+ int id = checkBoxPreference.getOrder();
+ StringMap blogMap = (StringMap) mMutedBlogsList.get(id);
+ blogMap.put("value", (!isChecked) ? 1.0 : 0.0);
+ mMutedBlogsList.set(id, blogMap);
+ StringMap mutedBlogsMap = (StringMap) mNotificationSettings.get("muted_blogs");
+ mutedBlogsMap.put("value", mMutedBlogsList);
+ mNotificationSettings.put("muted_blogs", mutedBlogsMap);
+ checkBoxPreference.setChecked(isChecked);
+ new sendNotificationSettingsTask().execute();
+ }
+ return false;
+ }
+ };
+
+ /**
+ * Listens for changes to notification enabled toggle
+ */
+ private OnPreferenceChangeListener mNotificationsEnabledChangeListener = new OnPreferenceChangeListener() {
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference instanceof CheckBoxPreference) {
+ final boolean isChecked = (Boolean) newValue;
+ if (isChecked) {
+ StringMap muteUntilMap = (StringMap) mNotificationSettings.get("mute_until");
+ muteUntilMap.put("value", "0");
+ mNotificationSettings.put("mute_until", muteUntilMap);
+ new sendNotificationSettingsTask().execute();
+ return true;
+ } else {
+ final Dialog dialog = new Dialog(PreferencesActivity.this);
+ dialog.setContentView(R.layout.notifications_enabled_dialog);
+ dialog.setTitle(R.string.notifications);
+ dialog.setCancelable(true);
+
+ Button offButton = (Button) dialog.findViewById(R.id.notificationsOff);
+ offButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ enabledButtonClick(v);
+ dialog.dismiss();
+ }
+ });
+ Button oneHourButton = (Button) dialog.findViewById(R.id.notifications1Hour);
+ oneHourButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ enabledButtonClick(v);
+ dialog.dismiss();
+ }
+ });
+ Button eightHoursButton = (Button) dialog.findViewById(R.id.notifications8Hours);
+ eightHoursButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ enabledButtonClick(v);
+ dialog.dismiss();
+ }
+ });
+ dialog.show();
+ }
+
+ }
+ return false;
+ }
+ };
+
+ private void enabledButtonClick(View v) {
+ StringMap muteUntilMap = (StringMap) mNotificationSettings
+ .get("mute_until");
+ if (muteUntilMap != null) {
+ if (v.getId() == R.id.notificationsOff) {
+ muteUntilMap.put("value", "forever");
+ } else if (v.getId() == R.id.notifications1Hour) {
+ muteUntilMap.put("value",
+ String.valueOf((System.currentTimeMillis() / 1000) + 3600));
+ } else if (v.getId() == R.id.notifications8Hours) {
+ muteUntilMap.put("value",
+ String.valueOf((System.currentTimeMillis() / 1000) + (3600 * 8)));
+ }
+ CheckBoxPreference enabledCheckBoxPreference = (CheckBoxPreference) findPreference("wp_pref_notifications_enabled");
+ enabledCheckBoxPreference.setChecked(false);
+ mNotificationSettings.put("mute_until", muteUntilMap);
+ new sendNotificationSettingsTask().execute();
+ }
+ }
+
+ /**
+ * Performs the notification settings save in the background
+ */
+ private class sendNotificationSettingsTask extends AsyncTask {
+
+ // Sends updated notification settings to WP.com
+
+ @Override
+ protected Object doInBackground(Object... params) {
+
+ if (mNotificationSettings != null) {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(PreferencesActivity.this);
+ SharedPreferences.Editor editor = settings.edit();
+ Gson gson = new Gson();
+ String settingsJson = gson.toJson(mNotificationSettings);
+ editor.putString("wp_pref_notification_settings", settingsJson);
+ editor.commit();
+ new WPComXMLRPCApi().setNotificationSettings(PreferencesActivity.this);
+ }
+ return null;
+ }
+ }
- // TODO: start service after reboot?
- Intent intent = new Intent(PreferencesActivity.this, CommentBroadcastReceiver.class);
- PendingIntent pIntent = PendingIntent.getBroadcast(PreferencesActivity.this, 0, intent, 0);
-
- AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
+ private void refreshWPComAuthCategory() {
+ PreferenceCategory wpcomCategory = (PreferenceCategory) findPreference("wp_pref_wpcom_auth");
+ wpcomCategory.removeAll();
+
+ if (WordPress.hasValidWPComCredentials(PreferencesActivity.this)) {
+ String username = mSettings.getString(WordPress.WPCOM_USERNAME_PREFERENCE, null);
+ Preference usernamePref = new Preference(this);
+ usernamePref.setTitle(getString(R.string.username));
+ usernamePref.setSummary(username);
+ usernamePref.setSelectable(false);
+
+ wpcomCategory.addPreference(usernamePref);
+
+ loadNotifications();
+ } else {
+ Preference signInPref = new Preference(this);
+ signInPref.setTitle(getString(R.string.sign_in));
+ signInPref.setOnPreferenceClickListener(signInPreferenceClickListener);
+ wpcomCategory.addPreference(signInPref);
+
+ PreferenceScreen rootScreen = (PreferenceScreen)findPreference("wp_pref_root");
+ rootScreen.removePreference(mNotificationsGroup);
+ }
+ }
- alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (5 * 1000), UPDATE_INTERVAL, pIntent);
+ private void loadNotifications() {
+
+ // Add notifications group back in case it was previously removed from being logged out
+ PreferenceScreen rootScreen = (PreferenceScreen)findPreference("wp_pref_root");
+ rootScreen.addPreference(mNotificationsGroup);
+
+ PreferenceCategory notificationTypesCategory = (PreferenceCategory) findPreference("wp_pref_notification_types");
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+ String settingsJson = settings.getString("wp_pref_notification_settings", null);
+ if (settingsJson == null) {
+ rootScreen.removePreference(mNotificationsGroup);
+ return;
} else {
- Intent stopIntent = new Intent(PreferencesActivity.this, CommentBroadcastReceiver.class);
- PendingIntent stopPIntent = PendingIntent.getBroadcast(PreferencesActivity.this, 0, stopIntent, 0);
- AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
- alarmManager.cancel(stopPIntent);
+ try {
+ Gson gson = new Gson();
+ mNotificationSettings = gson.fromJson(settingsJson, HashMap.class);
+ StringMap> mutedBlogsMap = (StringMap>) mNotificationSettings.get("muted_blogs");
+ mMutedBlogsList = (ArrayList>) mutedBlogsMap.get("value");
+ mTypeList = mNotificationSettings.keySet().toArray();
+
+ for (int i = 0; i < mTypeList.length; i++) {
+ if (!mTypeList[i].equals("muted_blogs") && !mTypeList[i].equals("mute_until")) {
+ StringMap> typeMap = (StringMap>) mNotificationSettings
+ .get(mTypeList[i].toString());
+ CheckBoxPreference typePreference = new CheckBoxPreference(this);
+ typePreference.setKey(mTypeList[i].toString());
+ typePreference.setChecked(typeMap.get("value").toString().equals("1"));
+ typePreference.setTitle(typeMap.get("desc").toString());
+ typePreference.setOnPreferenceChangeListener(mTypeChangeListener);
+ notificationTypesCategory.addPreference(typePreference);
+ }
+ }
+
+ PreferenceCategory selectBlogsCategory = (PreferenceCategory) findPreference("wp_pref_notification_blogs");
+ for (int i = 0; i < mMutedBlogsList.size(); i++) {
+ StringMap> blogMap = (StringMap>) mMutedBlogsList.get(i);
+ String blogName = (String) blogMap.get("blog_name");
+ if (blogName == null || blogName.trim().equals(""))
+ blogName = (String) blogMap.get("url");
+ CheckBoxPreference blogPreference = new CheckBoxPreference(this);
+ blogPreference.setChecked(!blogMap.get("value").toString().equals("1"));
+ blogPreference.setTitle(EscapeUtils.unescapeHtml(blogName));
+ blogPreference.setOnPreferenceChangeListener(mMuteBlogChangeListener);
+ selectBlogsCategory.addPreference(blogPreference);
+ }
- Intent service = new Intent(PreferencesActivity.this, CommentService.class);
- stopService(service);
+ } catch (JsonSyntaxException e) {
+ Log.v("WORDPRESS", "Notification Settings Json could not be parsed.");
+ return;
+ } catch (Exception e) {
+ Log.v("WORDPRESS", "Failed to load notification settings.");
+ return;
+ }
+
+ CheckBoxPreference notificationsEnabledCheckBox = (CheckBoxPreference) findPreference("wp_pref_notifications_enabled");
+ notificationsEnabledCheckBox.setOnPreferenceChangeListener(mNotificationsEnabledChangeListener);
+
}
- Intent intent = new Intent();
- setResult(RESULT_OK, intent);
- super.onPause();
}
+ private OnPreferenceClickListener signInPreferenceClickListener = new OnPreferenceClickListener() {
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent i = new Intent(PreferencesActivity.this, AccountSetupActivity.class);
+ i.putExtra("wpcom", true);
+ i.putExtra("auth-only", true);
+ startActivityForResult(i, 0);
+ return true;
+ }
+ };
+
+ private OnPreferenceClickListener signOutPreferenceClickListener = new OnPreferenceClickListener() {
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(PreferencesActivity.this);
+ dialogBuilder.setTitle(getResources().getText(R.string.sign_out));
+ dialogBuilder.setMessage(getString(R.string.sign_out_confirm));
+ dialogBuilder.setPositiveButton(R.string.sign_out,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ WordPress.signOut(PreferencesActivity.this);
+ finish();
+ }
+ });
+ dialogBuilder.setNegativeButton(R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ // Just close the window.
+ }
+ });
+ dialogBuilder.setCancelable(true);
+ if (!isFinishing())
+ dialogBuilder.create().show();
+ return true;
+ }
+ };
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ refreshWPComAuthCategory();
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
diff --git a/src/org/wordpress/android/ui/reader/ReaderActivity.java b/src/org/wordpress/android/ui/reader/ReaderActivity.java
index 171e43970ada..0a6d331e4deb 100644
--- a/src/org/wordpress/android/ui/reader/ReaderActivity.java
+++ b/src/org/wordpress/android/ui/reader/ReaderActivity.java
@@ -20,6 +20,7 @@
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
@@ -163,36 +164,29 @@ public Fragment getItem(int location) {
return f;
}
-
}
@Override
- public boolean onPrepareOptionsMenu(Menu menu) {
-
- menu.clear();
- menu.add(0, 0, 0, getResources().getText(R.string.refresh));
- refreshMenuItem = menu.findItem(0);
- refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- refreshMenuItem.setIcon(R.drawable.ab_icon_refresh);
-
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuInflater inflater = getSupportMenuInflater();
+ inflater.inflate(R.menu.reader, menu);
+ refreshMenuItem = menu.findItem(R.id.menu_refresh);
if (shouldAnimateRefreshButton) {
shouldAnimateRefreshButton = false;
startAnimatingRefreshButton(refreshMenuItem);
}
-
+
if (readerPager.getCurrentItem() > 1) {
- menu.removeItem(0);
- menu.add(0, 1, 0, getResources().getText(R.string.view_in_browser));
- MenuItem viewMenuItem = menu.findItem(1);
- viewMenuItem.setIcon(R.drawable.ab_icon_web);
- viewMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- menu.add(0, 2, 0, getResources().getText(R.string.share_link));
- MenuItem shareMenuItem = menu.findItem(2);
- shareMenuItem.setIcon(R.drawable.ab_icon_share);
- shareMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.findItem(R.id.menu_refresh).setVisible(false);
+ menu.findItem(R.id.menu_browser).setVisible(true);
+ menu.findItem(R.id.menu_share_link).setVisible(true);
+ } else {
+ menu.findItem(R.id.menu_refresh).setVisible(true);
+ menu.findItem(R.id.menu_browser).setVisible(false);
+ menu.findItem(R.id.menu_share_link).setVisible(false);
}
-
- return super.onPrepareOptionsMenu(menu);
+ return true;
}
public boolean onOptionsItemSelected(final MenuItem item) {
@@ -200,11 +194,11 @@ public boolean onOptionsItemSelected(final MenuItem item) {
final ReaderDetailPageFragment readerPageDetailFragment = (ReaderDetailPageFragment) detailPage;
int itemId = item.getItemId();
- if (itemId == 0) {
+ if (itemId == R.id.menu_refresh) {
ReaderImplFragment readerPageFragment = (ReaderImplFragment) readerPage;
readerPageFragment.refreshReader();
return true;
- } else if (itemId == 1) {
+ } else if (itemId == R.id.menu_browser) {
if (readerPageDetailFragment != null && readerPageDetailFragment != null) {
if (readerPager.getCurrentItem() == 2) {
runOnUiThread(new Runnable() {
@@ -226,7 +220,7 @@ public void run() {
}
}
return true;
- } else if (itemId == 2) {
+ } else if (itemId == R.id.menu_share_link) {
if (readerWebPageFragment != null && readerPageDetailFragment != null) {
if (readerPager.getCurrentItem() == 2) {
runOnUiThread(new Runnable() {
@@ -251,8 +245,11 @@ public void run() {
if (readerPager.getCurrentItem() > 1) {
readerPager.setCurrentItem(readerPager.getCurrentItem() - 1);
supportInvalidateOptionsMenu();
+ if (readerPager.getCurrentItem() == 1) {
+ mMenuDrawer.setDrawerIndicatorEnabled(true);
+ }
return true;
- }
+ }
}
return super.onOptionsItemSelected(item);
}
@@ -289,6 +286,7 @@ public void run() {
@Override
public void onPostSelected(String requestedURL) {
readerPager.setCurrentItem(2, true);
+ mMenuDrawer.setDrawerIndicatorEnabled(false);
supportInvalidateOptionsMenu();
}
@@ -300,6 +298,9 @@ public void onBackPressed() {
readerPageDetailFragment.wv.loadUrl("javascript:Reader2.clear_article_details();");
}
readerPager.setCurrentItem(readerPager.getCurrentItem() - 1, true);
+ if (readerPager.getCurrentItem() == 1) {
+ mMenuDrawer.setDrawerIndicatorEnabled(true);
+ }
supportInvalidateOptionsMenu();
} else
super.onBackPressed();
@@ -450,17 +451,6 @@ public void onBlogChanged() {
if (WordPress.currentBlog.isDotcomFlag()) {
ReaderImplFragment readerPageFragment = (ReaderImplFragment) readerPage;
readerPageFragment.reloadReader();
- } else {
- // Self-hosted blogs do not have Reader access, send to posts instead
- SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(ReaderActivity.this);
- SharedPreferences.Editor editor = settings.edit();
- editor.putInt("wp_pref_last_activity", POSTS_ACTIVITY);
- editor.commit();
- mShouldFinish = true;
- Intent i = new Intent(ReaderActivity.this, PostsActivity.class);
- i.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- mMenuDrawer.closeMenu();
- startActivityWithDelay(i);
}
}
diff --git a/src/org/wordpress/android/ui/reader/ReaderImplFragment.java b/src/org/wordpress/android/ui/reader/ReaderImplFragment.java
index b86a651be5ec..f267094569c1 100644
--- a/src/org/wordpress/android/ui/reader/ReaderImplFragment.java
+++ b/src/org/wordpress/android/ui/reader/ReaderImplFragment.java
@@ -3,11 +3,13 @@
import java.util.List;
import android.app.Activity;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -17,7 +19,6 @@
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
-import android.widget.Toast;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
@@ -28,10 +29,10 @@
import org.wordpress.android.Constants;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
+import org.wordpress.android.WordPressDB;
public class ReaderImplFragment extends ReaderBaseFragment {
/** Called when the activity is first created. */
- private String loginURL = "";
public WebView wv;
public String topicsID;
private PostSelectedListener onPostSelectedListener;
@@ -177,14 +178,14 @@ public void run() {
}).start();
}
- private class loadReaderTask extends AsyncTask> {
+ private class loadReaderTask extends AsyncTask {
@Override
protected void onPreExecute() {
((ReaderActivity) getActivity()).startAnimatingButton();
}
- protected void onPostExecute(List> result) {
+ protected void onPostExecute(Void result) {
if (getActivity() != null)
((ReaderActivity) getActivity()).stopAnimatingButton();
@@ -215,22 +216,16 @@ public void run() {
}
@Override
- protected List> doInBackground(String... args) {
+ protected Void doInBackground(Void... args) {
- if (WordPress.currentBlog == null) {
- WordPress.getCurrentBlog();
+ if ( !WordPress.hasValidWPComCredentials(getActivity().getApplicationContext()) ){
+ return null;
}
-
- loginURL = WordPress.currentBlog.getUrl().replace("xmlrpc.php",
- "wp-login.php");
- if (WordPress.currentBlog.getUrl().lastIndexOf("/") != -1)
- loginURL = WordPress.currentBlog.getUrl().substring(0,
- WordPress.currentBlog.getUrl().lastIndexOf("/"))
- + "/wp-login.php";
- else
- loginURL = WordPress.currentBlog.getUrl().replace("xmlrpc.php",
- "wp-login.php");
-
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
+ String username = settings.getString(WordPress.WPCOM_USERNAME_PREFERENCE, null);
+ String password = WordPressDB.decryptPassword(settings.getString(WordPress.WPCOM_PASSWORD_PREFERENCE, null));
+
String readerURL = Constants.readerURL_v3;
try {
@@ -251,13 +246,13 @@ protected List> doInBackground(String... args) {
+ ""
+ ""
+ "