aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Renard 2016-04-05 01:52:13 -0300
committerLibravatar Renard 2016-04-05 01:52:13 -0300
commit90ca8b34d823eed19d26f6611716ca231d60424c (patch)
tree65d389b05acb542f14a8faba8abeaa7b36ae227e
downloadBaiApp-90ca8b34d823eed19d26f6611716ca231d60424c.tar.gz
BaiApp-90ca8b34d823eed19d26f6611716ca231d60424c.tar.xz
BaiApp-90ca8b34d823eed19d26f6611716ca231d60424c.zip
Initial commit
-rw-r--r--.gitignore8
-rw-r--r--.idea/.name1
-rw-r--r--.idea/compiler.xml22
-rw-r--r--.idea/copyright/profiles_settings.xml3
-rw-r--r--.idea/encodings.xml6
-rw-r--r--.idea/gradle.xml19
-rw-r--r--.idea/inspectionProfiles/Project_Default.xml10
-rw-r--r--.idea/inspectionProfiles/profiles_settings.xml7
-rw-r--r--.idea/misc.xml46
-rw-r--r--.idea/modules.xml9
-rw-r--r--.idea/runConfigurations.xml12
-rw-r--r--.idea/vcs.xml6
-rw-r--r--app/.gitignore1
-rw-r--r--app/build.gradle43
-rw-r--r--app/libs/android-gif-drawable-1.1.14.aarbin0 -> 201234 bytes
-rw-r--r--app/libs/subsampling-scale-image-view-3.4.1.aarbin0 -> 48417 bytes
-rw-r--r--app/proguard-rules.pro17
-rw-r--r--app/src/androidTest/java/org/bienvenidoainternet/baiparser/ApplicationTest.java13
-rw-r--r--app/src/main/AndroidManifest.xml35
-rw-r--r--app/src/main/assets/fonts/mona.ttfbin0 -> 2828124 bytes
-rw-r--r--app/src/main/ic_launcher-web.pngbin0 -> 77382 bytes
-rw-r--r--app/src/main/java/layout/fragmentThreadList.java779
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/AppCompatPreferenceActivity.java109
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/CustomFragmentPagerAdapter.java53
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/MainActivity.java478
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/RecentPostAdapter.java90
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/ResponseActivity.java216
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/SettingsActivity.java184
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/ThemeManager.java55
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/ThreadListAdapter.java281
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/UpdaterActivity.java127
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/ViewerActivity.java247
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/structure/Board.java60
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/structure/BoardItem.java310
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/structure/JsonType.java10
-rw-r--r--app/src/main/java/org/bienvenidoainternet/baiparser/structure/ReplyID.java48
-rw-r--r--app/src/main/java/utils/ContentProviderUtils.java148
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_download.pngbin0 -> 318 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_font_bold.pngbin0 -> 431 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_action_font_italic.pngbin0 -> 312 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_edit.pngbin0 -> 304 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_refresh.pngbin0 -> 787 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_sync.pngbin0 -> 359 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_download.pngbin0 -> 213 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_font_bold.pngbin0 -> 303 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_action_font_italic.pngbin0 -> 248 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_addreply.pngbin0 -> 183 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_edit.pngbin0 -> 241 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_refresh.pngbin0 -> 557 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_sync.pngbin0 -> 231 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_download.pngbin0 -> 355 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_font_bold.pngbin0 -> 504 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_action_font_italic.pngbin0 -> 447 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_edit.pngbin0 -> 356 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_refresh.pngbin0 -> 1050 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_sync.pngbin0 -> 396 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_download.pngbin0 -> 548 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_font_bold.pngbin0 -> 779 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_action_font_italic.pngbin0 -> 571 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_edit.pngbin0 -> 524 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_refresh.pngbin0 -> 1576 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_sync.pngbin0 -> 595 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_download.pngbin0 -> 1450 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_font_bold.pngbin0 -> 2049 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_action_font_italic.pngbin0 -> 1475 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_edit.pngbin0 -> 657 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_refresh.pngbin0 -> 2249 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_sync.pngbin0 -> 726 bytes
-rw-r--r--app/src/main/res/drawable/Thumbs.dbbin0 -> 26624 bytes
-rw-r--r--app/src/main/res/drawable/bai_banner.pngbin0 -> 41530 bytes
-rw-r--r--app/src/main/res/drawable/bai_logo.pngbin0 -> 18807 bytes
-rw-r--r--app/src/main/res/drawable/bai_mona.pngbin0 -> 14125 bytes
-rw-r--r--app/src/main/res/drawable/blank.pngbin0 -> 2792 bytes
-rw-r--r--app/src/main/res/drawable/side_nav_bar.xml8
-rw-r--r--app/src/main/res/layout/activity_main.xml25
-rw-r--r--app/src/main/res/layout/activity_response.xml134
-rw-r--r--app/src/main/res/layout/activity_updater.xml64
-rw-r--r--app/src/main/res/layout/activity_viewer.xml24
-rw-r--r--app/src/main/res/layout/app_bar_main.xml35
-rw-r--r--app/src/main/res/layout/boardthread_item.xml39
-rw-r--r--app/src/main/res/layout/content_main.xml22
-rw-r--r--app/src/main/res/layout/fragment_fragment_recent.xml17
-rw-r--r--app/src/main/res/layout/fragment_fragment_thread_list.xml61
-rw-r--r--app/src/main/res/layout/nav_header_main.xml28
-rw-r--r--app/src/main/res/layout/recentpost_item.xml39
-rw-r--r--app/src/main/res/layout/thread_item.xml95
-rw-r--r--app/src/main/res/menu/activity_main_drawer.xml7
-rw-r--r--app/src/main/res/menu/main.xml35
-rw-r--r--app/src/main/res/menu/menu_reply.xml13
-rw-r--r--app/src/main/res/menu/menu_viewer.xml10
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 5908 bytes
-rw-r--r--app/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 3243 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 9179 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 17329 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 27776 bytes
-rw-r--r--app/src/main/res/values-v21/styles.xml9
-rw-r--r--app/src/main/res/values-w820dp/dimens.xml6
-rw-r--r--app/src/main/res/values/attrs.xml8
-rw-r--r--app/src/main/res/values/colors.xml36
-rw-r--r--app/src/main/res/values/dimens.xml9
-rw-r--r--app/src/main/res/values/drawables.xml8
-rw-r--r--app/src/main/res/values/strings.xml76
-rw-r--r--app/src/main/res/values/styles.xml88
-rw-r--r--app/src/main/res/xml/preferences.xml61
-rw-r--r--app/src/test/java/org/bienvenidoainternet/baiparser/ExampleUnitTest.java15
-rw-r--r--build.gradle23
-rw-r--r--gradle.properties18
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 53637 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--gradlew160
-rw-r--r--gradlew.bat90
-rw-r--r--settings.gradle1
112 files changed, 4723 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c6cbe56
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..170a3e9
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Bai Parser \ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ <entry name="!?*.aj" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+<component name="CopyrightManager">
+ <settings default="" />
+</component> \ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding">
+ <file url="PROJECT" charset="UTF-8" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..fc132b2
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="distributionType" value="LOCAL" />
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="gradleHome" value="C:\Program Files\Android\Android Studio\gradle\gradle-2.8" />
+ <option name="gradleJvm" value="1.8" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/app" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..959f65b
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,10 @@
+<component name="InspectionProjectProfileManager">
+ <profile version="1.0">
+ <option name="myName" value="Project Default" />
+ <inspection_tool class="AndroidLintRtlCompat" enabled="false" level="ERROR" enabled_by_default="false" />
+ <inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
+ <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
+ <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
+ </inspection_tool>
+ </profile>
+</component> \ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..3b31283
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,7 @@
+<component name="InspectionProjectProfileManager">
+ <settings>
+ <option name="PROJECT_PROFILE" value="Project Default" />
+ <option name="USE_PROJECT_PROFILE" value="true" />
+ <version value="1.0" />
+ </settings>
+</component> \ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ </component>
+ <component name="NullableNotNullManager">
+ <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
+ <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
+ <option name="myNullables">
+ <value>
+ <list size="4">
+ <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
+ <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
+ <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
+ <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
+ </list>
+ </value>
+ </option>
+ <option name="myNotNulls">
+ <value>
+ <list size="4">
+ <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
+ <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
+ <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
+ <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
+ </list>
+ </value>
+ </option>
+ </component>
+ <component name="ProjectLevelVcsManager" settingsEditedManually="false">
+ <OptionsSetting value="true" id="Add" />
+ <OptionsSetting value="true" id="Remove" />
+ <OptionsSetting value="true" id="Checkout" />
+ <OptionsSetting value="true" id="Update" />
+ <OptionsSetting value="true" id="Status" />
+ <OptionsSetting value="true" id="Edit" />
+ <ConfirmationsSetting value="0" id="Add" />
+ <ConfirmationsSetting value="0" id="Remove" />
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/build/classes" />
+ </component>
+ <component name="ProjectType">
+ <option name="id" value="Android" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c7df3fa
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/BaiParser.iml" filepath="$PROJECT_DIR$/BaiParser.iml" />
+ <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
+ </modules>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="RunConfigurationProducerService">
+ <option name="ignoredProducers">
+ <set>
+ <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
+ <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
+ <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
+ </set>
+ </option>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project> \ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..f0c0327
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,43 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.2"
+ packagingOptions {
+ exclude 'META-INF/DEPENDENCIES.txt'
+ exclude 'META-INF/LICENSE.txt'
+ exclude 'META-INF/NOTICE.txt'
+ exclude 'META-INF/NOTICE'
+ exclude 'META-INF/LICENSE'
+ exclude 'META-INF/DEPENDENCIES'
+ exclude 'META-INF/notice.txt'
+ exclude 'META-INF/license.txt'
+ exclude 'META-INF/dependencies.txt'
+ exclude 'META-INF/LGPL2.1'
+ }
+ defaultConfig {
+ applicationId "org.bienvenidoainternet.baiparser"
+ minSdkVersion 15
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.1"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:appcompat-v7:23.1.1'
+ compile 'com.android.support:design:23.1.1'
+ compile 'com.android.support:support-v4:23.1.1'
+ compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.4.1'
+ compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.14'
+ compile 'com.koushikdutta.ion:ion:2.+'
+}
+
diff --git a/app/libs/android-gif-drawable-1.1.14.aar b/app/libs/android-gif-drawable-1.1.14.aar
new file mode 100644
index 0000000..2ee006f
--- /dev/null
+++ b/app/libs/android-gif-drawable-1.1.14.aar
Binary files differ
diff --git a/app/libs/subsampling-scale-image-view-3.4.1.aar b/app/libs/subsampling-scale-image-view-3.4.1.aar
new file mode 100644
index 0000000..4c42066
--- /dev/null
+++ b/app/libs/subsampling-scale-image-view-3.4.1.aar
Binary files differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..5743f5d
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Users\DSM\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/app/src/androidTest/java/org/bienvenidoainternet/baiparser/ApplicationTest.java b/app/src/androidTest/java/org/bienvenidoainternet/baiparser/ApplicationTest.java
new file mode 100644
index 0000000..ca23e0c
--- /dev/null
+++ b/app/src/androidTest/java/org/bienvenidoainternet/baiparser/ApplicationTest.java
@@ -0,0 +1,13 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..31f37e2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.bienvenidoainternet.baiparser">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/bai_mona"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".ResponseActivity" />
+ <activity
+ android:name=".SettingsActivity"
+ android:label="@string/title_activity_settings" />
+ <activity
+ android:name=".ViewerActivity"
+ android:theme="@style/TransparentCompat"></activity>
+ <activity android:name=".UpdaterActivity"></activity>
+ </application>
+
+</manifest>
diff --git a/app/src/main/assets/fonts/mona.ttf b/app/src/main/assets/fonts/mona.ttf
new file mode 100644
index 0000000..d19a9ec
--- /dev/null
+++ b/app/src/main/assets/fonts/mona.ttf
Binary files differ
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..57044d4
--- /dev/null
+++ b/app/src/main/ic_launcher-web.png
Binary files differ
diff --git a/app/src/main/java/layout/fragmentThreadList.java b/app/src/main/java/layout/fragmentThreadList.java
new file mode 100644
index 0000000..c06fd93
--- /dev/null
+++ b/app/src/main/java/layout/fragmentThreadList.java
@@ -0,0 +1,779 @@
+package layout;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.ColorDrawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v7.view.ContextThemeWrapper;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.koushikdutta.async.future.FutureCallback;
+import com.koushikdutta.ion.Ion;
+
+import org.bienvenidoainternet.baiparser.MainActivity;
+import org.bienvenidoainternet.baiparser.R;
+import org.bienvenidoainternet.baiparser.RecentPostAdapter;
+import org.bienvenidoainternet.baiparser.ResponseActivity;
+import org.bienvenidoainternet.baiparser.ThreadListAdapter;
+import org.bienvenidoainternet.baiparser.structure.Board;
+import org.bienvenidoainternet.baiparser.structure.BoardItem;
+import org.bienvenidoainternet.baiparser.structure.ReplyID;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.List;
+
+public class fragmentThreadList extends Fragment {
+ public static final String ARG_CURRENTBOARD = "currentBoard", ARG_THREAD_ID = "currentThreadId", ARG_MAIN_FRAGMENT = "imMainFragment",
+ SAVED_BOARDITEMS = "savedBoardItems", RECENT_POST_MODE = "recentpostmode", ARG_CURRENT_THREAD = "currentThread";
+ List<ReplyID> idList = new ArrayList<>();
+ public ArrayList<BoardItem> boardItems = new ArrayList<BoardItem>();
+ public Board currentBoard = null;
+ public BoardItem currentThread = null;
+ private boolean imMainFragment;
+ private OnFragmentInteractionListener mListener;
+ private ThreadListAdapter listViewAdapter;
+ private RecentPostAdapter recentPostAdapter;
+ private ListView listViewBoardItems = null;;
+ private ProgressBar loadingBar = null;
+ SharedPreferences settings;
+ private boolean loadingMoreThreads = false;
+ View themedContext;
+ private int currentOffset = 0;
+
+ ViewGroup rootView;
+ private boolean recentPostMode = false;
+
+// ProgressBar barThreadProcess;
+ LinearLayout layoutThreadProcess;
+ TextView txtThreadProcess;
+
+ public fragmentThreadList() {
+ // Required empty public constructor
+
+ }
+
+ public static fragmentThreadList newInstance(boolean mainFragment, Board board, BoardItem thread){
+ fragmentThreadList fragment = new fragmentThreadList();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_CURRENTBOARD, board);
+ args.putParcelable(ARG_CURRENT_THREAD, thread);
+ args.putBoolean(ARG_MAIN_FRAGMENT, mainFragment);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.setRetainInstance(true);
+ if (getArguments() != null) {
+ this.currentBoard = getArguments().getParcelable(ARG_CURRENTBOARD);
+ this.currentThread = getArguments().getParcelable(ARG_CURRENT_THREAD);
+ this.imMainFragment = getArguments().getBoolean(ARG_MAIN_FRAGMENT);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelableArrayList(SAVED_BOARDITEMS, boardItems);
+ outState.putBoolean(RECENT_POST_MODE, recentPostMode);
+ outState.putParcelable(ARG_CURRENT_THREAD, currentThread);
+ outState.putParcelable(ARG_CURRENTBOARD, currentBoard);
+ }
+
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // cargamos la instancia si esta guardada
+ if (savedInstanceState != null){
+ recentPostMode = savedInstanceState.getBoolean(RECENT_POST_MODE);
+ currentBoard = savedInstanceState.getParcelable(ARG_CURRENTBOARD);
+ currentThread = savedInstanceState.getParcelable(ARG_CURRENT_THREAD);
+ boardItems = savedInstanceState.getParcelableArrayList(SAVED_BOARDITEMS);
+ }
+
+ // Aplicación del Tema
+ settings = PreferenceManager.getDefaultSharedPreferences(this.getContext());
+ int themeResId = ((MainActivity)getActivity()).getCurrentThemeId();
+ Context context = new ContextThemeWrapper(getActivity(), themeResId);
+ LayoutInflater localInflater = inflater.cloneInContext(context);
+ View v = localInflater.inflate(R.layout.fragment_fragment_thread_list, container, false);
+ themedContext = v;
+ this.rootView = (ViewGroup)v;
+
+ // Seteamos los controles que son guardados globalmente
+ listViewBoardItems = (ListView)v.findViewById(R.id.lvThreadList);
+// barThreadProcess = (ProgressBar)rootView.findViewById(R.id.barThreadProcess);
+ layoutThreadProcess = (LinearLayout)rootView.findViewById(R.id.layoutThreadProcess);
+ txtThreadProcess = (TextView)rootView.findViewById(R.id.txtThreadError);
+ this.loadingBar = (ProgressBar)rootView.findViewById(R.id.progressBar);
+
+ // Agregamos color al divider del listview
+ ColorDrawable cd = new ColorDrawable((((MainActivity) getActivity()).themeManager).getMarginColor());
+ listViewBoardItems.setDivider(cd);
+ listViewBoardItems.setDividerHeight(1);
+
+ // registramos los menus del listview
+ registerForContextMenu(listViewBoardItems);
+ // Creamos los dos adaptadores y los seteamos dependiendo del modo del fragmento
+ listViewAdapter = new ThreadListAdapter(v.getContext(), boardItems, (((MainActivity) getActivity()).themeManager));
+ recentPostAdapter = new RecentPostAdapter(v.getContext(), boardItems);
+ if (recentPostMode){
+ listViewBoardItems.setAdapter(recentPostAdapter);
+ }else{
+ listViewBoardItems.setAdapter(listViewAdapter);
+ }
+
+ if (!imMainFragment){
+ listViewAdapter.listThreads = true;
+ }
+
+ listViewBoardItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (imMainFragment && !recentPostMode) {
+ BoardItem bi = listViewAdapter.getItem(position);
+ mListener.showThread(currentBoard, bi);
+ }else if (imMainFragment && recentPostMode){
+ BoardItem bi = boardItems.get(position);
+ mListener.showThread(bi.getParentBoard(), bi);
+ }
+ }
+ });
+
+ listViewBoardItems.setOnScrollListener(new AbsListView.OnScrollListener() {
+ private int lastFirstVisibleItem = 0;
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if(lastFirstVisibleItem < firstVisibleItem) { // Scrolling down
+ mListener.hideActionButton();
+ }else if(lastFirstVisibleItem > firstVisibleItem) { // Scrolling Up
+ mListener.showActionButton();
+ }
+ lastFirstVisibleItem = firstVisibleItem;
+ for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
+ if (!recentPostMode){
+ BoardItem bi = listViewAdapter.getItem(i);
+ if (!bi.getThumb().isEmpty() && bi.getThumbBitmap() == null && !bi.downloadingThumb) {
+ getThumbnail(bi);
+ }
+ }
+ }
+// System.out.println("[Scroll] firstItem: " + firstVisibleItem + " visible: " + visibleItemCount + " total: " + totalItemCount);
+ if (totalItemCount == firstVisibleItem + visibleItemCount && !loadingMoreThreads && imMainFragment && totalItemCount != 0 && !recentPostMode) {
+ loadingMoreThreads = true;
+ currentOffset += 10;
+ System.out.println("[Scroll] loading more threads! currentThreadCount " + totalItemCount);
+ getThreadList(currentOffset); // TODO: Offset incorrecto
+// new TaskParseJSON(currentBoard, true).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ }
+ });
+ hideProgressBar();
+ if (boardItems.isEmpty()){
+ if (currentBoard == null && currentThread == null && imMainFragment){
+ loadRecentPost();
+ }else{
+ updateBoardItems(currentBoard, currentThread);
+ }
+ }else{
+ listViewAdapter.notifyDataSetChanged();
+ recentPostAdapter.notifyDataSetChanged();
+ }
+ return v;
+ }
+
+ private void hideProgressBar(){
+ if (loadingBar != null)
+ loadingBar.setVisibility(View.GONE);
+ }
+
+ private void showProgressBar(){
+ if (loadingBar != null)
+ loadingBar.setVisibility(View.VISIBLE);
+ }
+
+
+ public void onButtonPressed(Uri uri) {
+ if (mListener != null) {
+ mListener.onFragmentInteraction(uri);
+ }
+ }
+
+ public void updateBoardItems(Board board, BoardItem thread){
+ currentBoard = board;
+ currentThread = thread;
+
+ if (listViewAdapter != null){
+ boardItems.clear();
+ listViewAdapter.notifyDataSetChanged();
+ }
+ if (imMainFragment){
+ if (currentBoard != null) {
+ System.out.println("[MainFragment] Updating -> boardName: " + board.getBoardName() + " dir: " + board.getBoardDir());
+ if (currentThread == null){
+ System.out.println("[MainFragment] isCurrentThread null? (it should be!) " + (currentThread == null));
+ }
+ showProgressBar();
+// new TaskParseJSON(board).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ getThreadList(0);
+ }else{
+ System.out.println("[MainFragment] Trying to update from a null board object");
+ }
+ }else{
+ if (currentBoard != null && currentThread != null){
+ System.out.println("atUpdateBoardItems ChildFragment threadID: " + currentThread.getId() + " parentID: " + currentThread.getParentId() + " boardName: " + board.getBoardName() + " " + board.getBoardDir());
+ showProgressBar();
+// new TaskParseJSON(currentBoard, currentThread).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ getThreadReplies();
+ }else{
+ System.out.println("[childFragment] trying to update from null objects");
+ System.out.println("[childFragment] isCurrentBoard null? " + (currentBoard == null));
+ System.out.println("[childFragment] isCurrentThread null? " + (currentThread == null));
+ }
+ }
+ }
+
+
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (context instanceof OnFragmentInteractionListener) {
+ mListener = (OnFragmentInteractionListener) context;
+ } else {
+ throw new RuntimeException(context.toString()
+ + " must implement OnFragmentInteractionListener");
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mListener = null;
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ if (info.targetView.getParent() == listViewBoardItems){
+ BoardItem bi = boardItems.get(info.position);
+ switch (item.getItemId()){
+ case R.id.menu_copy:
+ System.out.println("Post copiado");
+ ClipboardManager cm = (ClipboardManager)getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData cd = ClipData.newPlainText("Reply", boardItems.get(info.position).getMessage());
+ cm.setPrimaryClip(cd);
+ break;
+ case R.id.menu_reply:
+ Intent in = new Intent(getActivity().getApplicationContext(), ResponseActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("theReply", boardItems.get(info.position));
+ b.putParcelable("theBoard", currentBoard);
+ in.putExtras(b);
+ getActivity().startActivity(in);
+ break;
+ case R.id.menu_savereply:
+ try {
+ File txt = new File(Environment.getExternalStorageDirectory().getPath() + "/Bai/" + bi.getParentBoard().getBoardDir() + "_" + bi.getId() + ".txt");
+ FileOutputStream stream = new FileOutputStream(txt);
+ OutputStreamWriter outputStreamWriter = new OutputStreamWriter(stream);
+ outputStreamWriter.write(bi.getMessage());
+ outputStreamWriter.close();
+ stream.close();
+ Toast.makeText(getContext(), bi.getParentBoard().getBoardDir() + "_" + bi.getId() + ".txt guardado.", Toast.LENGTH_SHORT).show();
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ break;
+ case R.id.menu_delpost:
+ deletePost(false, bi);
+ break;
+ case R.id.menu_delimage:
+ deletePost(true, bi);
+ break;
+ }
+ }
+ return super.onContextItemSelected(item);
+ }
+
+
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ if (v.getId() == R.id.lvThreadList){
+ getActivity().getMenuInflater().inflate(R.menu.menu_reply, menu);
+ return;
+ }
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ public void refresh() {
+ if (recentPostMode){
+ boardItems.clear();
+ recentPostAdapter.notifyDataSetChanged();
+ getRecentPosts();
+ }else{
+ updateBoardItems(currentBoard, currentThread);
+ }
+ }
+
+ public void setRecentPostMode() {
+ this.recentPostMode = true;
+ }
+
+ public void setCatalogMode() {
+ if (recentPostMode){
+ boardItems.clear();
+// listViewAdapter = new ThreadListAdapter(themedContext.getContext(), boardItems, ((MainActivity)getActivity()).themeManager);
+ listViewBoardItems.setAdapter(listViewAdapter);
+ this.recentPostMode = false;
+ }
+ }
+
+ public void loadRecentPost(){
+ // Cambiamos el flag
+ setRecentPostMode();
+ mListener.updateToolbar("Post recientes");
+ // Borramos el listview
+ boardItems.clear();
+ listViewAdapter.clear();
+ listViewAdapter.notifyDataSetChanged();
+ // Cargamos un nuevo adaptador.
+// recentPostAdapter = new RecentPostAdapter(themedContext.getContext(), boardItems);
+ listViewBoardItems.setAdapter(recentPostAdapter);
+ // Cargamos la nueva lista
+ getRecentPosts();
+ }
+
+ public boolean getMode() {
+ return recentPostMode;
+ }
+
+
+ public interface OnFragmentInteractionListener {
+ void onFragmentInteraction(Uri uri);
+ void showThread(Board board, BoardItem thread);
+ void updateToolbar(Board currentBoard, BoardItem boardItem);
+ void updateToolbar(String s);
+ void hideActionButton();
+ void showActionButton();
+ }
+
+ public void scrollToBotton(){
+ if (!listViewAdapter.isEmpty()){
+ listViewBoardItems.setSelection(boardItems.size());
+ }
+ }
+ public void scrollToTop(){
+ if (!listViewAdapter.isEmpty()){
+ listViewBoardItems.setSelection(0);
+ }
+ }
+
+ public void getThreadList(int offset){
+ loadingMoreThreads = true;
+ showProgressBar();
+ String strOffset = "";
+ if (offset == 0){
+ currentOffset = 0;
+ boardItems.clear();
+ }else{
+ strOffset = "&offset=" + offset;
+ }
+ setUpThreadProgess();
+
+ final String repliesForCatalog = settings.getString("pref_repliesperthread", "5");
+ Ion.with(getContext())
+ .load("http://bienvenidoainternet.org/cgi/api/list?dir=" + currentBoard.getBoardDir() + "&replies=" + repliesForCatalog + strOffset)
+ .setLogging("getThreadList", Log.INFO)
+// .progressBar(barThreadProcess)
+ .noCache()
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ hideProgressBar();
+ if (e != null) {
+ e.printStackTrace();
+ displayError(e.getMessage());
+ } else {
+ try {
+ JSONObject json = new JSONObject(result);
+ JSONArray threads = json.getJSONArray("threads");
+ for (int i = 0; i < threads.length(); i++) {
+ JSONObject thread = threads.getJSONObject(i);
+ BoardItem item = new BoardItem();
+ item.setEmail(thread.getString("email"));
+ item.setFile(thread.getString("file"));
+ item.setFilesize(thread.getInt("file_size"));
+ item.setId(thread.getInt("id"));
+ item.setMessage(thread.getString("message"));
+ item.setName(thread.getString("name"));
+ item.setSubject(thread.getString("subject"));
+ item.setThumb(thread.getString("thumb"));
+ item.setThumbHeight(thread.getInt("thumb_height"));
+ item.setThumbWidth(thread.getInt("thumb_width"));
+ item.setTimeStamp(thread.getLong("timestamp"));
+ item.setTotalFiles(thread.getInt("total_files"));
+ item.setTotalReplies(thread.getInt("total_replies"));
+ item.setTripcode(thread.getString("tripcode"));
+ item.setTimeStampFormatted(thread.getString("timestamp_formatted"));
+ if (item.getTimeStampFormatted().contains("ID")){
+ item.setPosterId(item.getTimeStampFormatted().split(" ")[1].replace("ID :", ""));
+ }
+ item.setParentBoard(currentBoard);
+ item.setParentId(0);
+ item.setIdColor(addReplyID(item.getPosterId()));
+ if (currentBoard.getBoardType() == 1){
+ item.setBbsId(1);
+ }
+ boardItems.add(item);
+ if (!repliesForCatalog.equals("0")){
+ JSONArray replies = thread.getJSONArray("replies");
+ for (int r = 0; r < replies.length(); r++){
+ JSONObject jReply = replies.getJSONObject(r);
+ BoardItem reply = new BoardItem();
+ reply.setDeletedCode(jReply.getInt("IS_DELETED"));
+ if (currentBoard.getBoardType() == 1){
+ reply.setBbsId(item.getTotalReplies() - (Integer.valueOf(repliesForCatalog) - r) + 2);
+ }
+ if (reply.getDeletedCode() == 0){
+ reply.setEmail(jReply.getString("email"));
+ reply.setFile(jReply.getString("file"));
+ reply.setFilesize(jReply.getInt("file_size"));
+ reply.setId(jReply.getInt("id"));
+ reply.setParentId(item.getId());
+ reply.setMessage(jReply.getString("message"));
+ reply.setName(jReply.getString("name"));
+ reply.setSubject(jReply.getString("subject"));
+ reply.setThumb(jReply.getString("thumb"));
+ reply.setThumbHeight(jReply.getInt("thumb_height"));
+ reply.setThumbWidth(jReply.getInt("thumb_width"));
+ reply.setTimeStamp(jReply.getLong("timestamp"));
+ reply.setTripcode(jReply.getString("tripcode"));
+ reply.setParentBoard(currentBoard);
+ reply.setTimeStampFormatted(jReply.getString("timestamp_formatted"));
+ reply.isReply = true;
+ if (reply.getTimeStampFormatted().contains("ID")){
+ reply.setPosterId(reply.getTimeStampFormatted().split(" ")[1].replace("ID:", ""));
+ }
+ reply.setIdColor(addReplyID(reply.getPosterId()));
+ //
+ reply.setTotalReplies(item.getTotalReplies());
+ }else{
+ reply.setTimeStamp(jReply.getLong("timestamp"));
+ reply.setId(jReply.getInt("id"));
+ reply.isReply = true;
+ }
+ boardItems.add(reply);
+ }
+ }
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ displayError(e1.getMessage());
+ }
+ }
+ listViewAdapter.notifyDataSetChanged();
+ loadingMoreThreads = false;
+ if (boardItems.isEmpty()){
+ mListener.updateToolbar(currentBoard, currentThread);
+ }
+ }
+ });
+
+
+ }
+
+ private void getThreadReplies() {
+ showProgressBar();
+ boardItems.clear();
+ setUpThreadProgess();
+ int limit = Integer.valueOf(settings.getString("pref_lastreplies", "1000"));
+ int parentTotalReplies = currentThread.getTotalReplies(); // TODO: asddas
+ String offset = "&offset=0";
+ if (limit <= parentTotalReplies){
+ offset = "&offset=" + (parentTotalReplies - limit + 1);
+ }else{
+ limit = 1337;
+ }
+ final int finalLimit = limit;
+ Ion.with(getContext())
+ .load("http://bienvenidoainternet.org/cgi/api/thread?id=" + currentThread.realParentId() + "&dir=" + currentThread.getParentBoard().getBoardDir() + "&limit=" + limit + offset)
+ .setLogging("getThreadReplies", Log.INFO)
+ .noCache()
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ if (e != null){
+ e.printStackTrace();
+ displayError(e.getMessage());
+ }else{
+ try {
+ JSONObject json = new JSONObject(result);
+ JSONArray thread = json.getJSONArray("posts");
+ for (int i = 0; i < thread.length(); i++){
+ JSONObject reply = thread.getJSONObject(i);
+ BoardItem item = new BoardItem();
+ item.setDeletedCode(reply.getInt("IS_DELETED"));
+ if (item.getDeletedCode() == 0){
+ item.setEmail(reply.getString("email"));
+ item.setFile(reply.getString("file"));
+ item.setFilesize(reply.getInt("file_size"));
+ item.setId(reply.getInt("id"));
+ item.setMessage(reply.getString("message"));
+ item.setName(reply.getString("name"));
+ item.setSubject(reply.getString("subject"));
+ item.setThumb(reply.getString("thumb"));
+ item.setThumbHeight(reply.getInt("thumb_height"));
+ item.setThumbWidth(reply.getInt("thumb_width"));
+ item.setTimeStamp(reply.getLong("timestamp"));
+ item.setParentId(json.getInt("id"));
+ item.setTripcode(reply.getString("tripcode"));
+ item.setTimeStampFormatted(reply.getString("timestamp_formatted"));
+ if (item.getTimeStampFormatted().contains("ID")){
+ item.setPosterId(item.getTimeStampFormatted().split(" ")[1].replace("ID:", ""));
+ }
+ item.setParentBoard(currentBoard);
+ item.isReply = true;
+ item.setIdColor(addReplyID(item.getPosterId()));
+ item.setTotalReplies(json.getInt("total_replies"));
+ if (currentBoard.getBoardType() == 1){
+ if (item.getTotalReplies() < finalLimit){
+ item.setBbsId(i + 1);
+ }else{
+ item.setBbsId((item.getTotalReplies() - finalLimit + i) + 2);
+ }
+ }
+
+ } else {
+ item.setId(reply.getInt("id"));
+ item.setTimeStamp(reply.getLong("timestamp"));
+ item.isReply = true;
+ item.setTotalReplies(json.getInt("total_replies"));
+ if (currentBoard.getBoardType() == 1){
+ if (item.getTotalReplies() < finalLimit){
+ item.setBbsId(i + 1);
+ }else{
+ item.setBbsId((item.getTotalReplies() - finalLimit + i) + 2);
+ }
+ }
+ }
+ boardItems.add(item);
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ displayError(e1.getMessage());
+ }
+ }
+ listViewAdapter.notifyDataSetChanged();
+ if (settings.getBoolean("setting_scrollatnewthread", true)){
+ listViewBoardItems.setSelection(boardItems.size());
+ mListener.showActionButton();
+ }
+ hideProgressBar();
+ }
+ });
+ }
+
+ private void getRecentPosts(){
+ boardItems.clear();
+ loadingMoreThreads = true;
+ setUpThreadProgess();
+ String limit = settings.getString("pref_lastreplies_limit", "30");
+ Ion.with(getContext())
+ .load("http://bienvenidoainternet.org/cgi/api/last?limit=" + limit)
+ .setLogging("getRecentPosts", Log.INFO)
+// .progressBar(barThreadProcess)
+ .noCache()
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ if (e != null){
+ e.printStackTrace();
+ displayError(e.getMessage());
+ }else{
+ try {
+ JSONObject json = new JSONObject(result);
+ JSONArray posts = json.getJSONArray("posts");
+ for (int i = 0; i < posts.length(); i++){
+ JSONObject jPost = posts.getJSONObject(i);
+ BoardItem recentPost = new BoardItem();
+ recentPost.setEmail(jPost.getString("email"));
+ recentPost.setFile(jPost.getString("file"));
+ recentPost.setFilesize(jPost.getInt("file_size"));
+ recentPost.setId(jPost.getInt("id"));
+ recentPost.setMessage(jPost.getString("message"));
+ recentPost.setName(jPost.getString("name"));
+ recentPost.setSubject(jPost.getString("subject"));
+ recentPost.setThumb(jPost.getString("thumb"));
+ recentPost.setThumbHeight(jPost.getInt("thumb_height"));
+ recentPost.setThumbWidth(jPost.getInt("thumb_width"));
+ recentPost.setTimeStamp(jPost.getLong("timestamp"));
+ recentPost.setTripcode(jPost.getString("tripcode"));
+ recentPost.setTimeStampFormatted(jPost.getString("timestamp_formatted"));
+ if (recentPost.getTimeStampFormatted().contains("ID")){
+ recentPost.setPosterId(recentPost.getTimeStampFormatted().split(" ")[1].replace("ID:", ""));
+ }
+ recentPost.setParentBoard(((MainActivity) getActivity()).getBoardFromDir(jPost.getString("dir")));
+ recentPost.setParentId(jPost.getInt("parentid"));
+ boardItems.add(recentPost);
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ displayError(e1.getMessage());
+ }
+ }
+ recentPostAdapter.notifyDataSetChanged();
+ }
+ });
+
+ }
+
+ private void getThumbnail(final BoardItem bi){
+ bi.downloadingThumb = true;
+ ConnectivityManager cm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = cm.getActiveNetworkInfo();
+ boolean usingWifi = (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI);
+
+ ContextWrapper cw = new ContextWrapper(getActivity().getApplicationContext());
+ File directory = cw.getDir("thumbs", Context.MODE_PRIVATE);
+ File mypath = new File(directory, currentBoard.getBoardDir() + "_" + bi.getThumb());
+ if (mypath.exists()){
+ try {
+ Bitmap b = BitmapFactory.decodeStream(new FileInputStream(mypath));
+ bi.setThumbBitmap(b);
+ Log.i("getThumb", bi.getThumb() + " from cache");
+ return;
+ }catch (Exception e){
+ e.printStackTrace();
+ displayError(e.getMessage());
+ }
+ }
+ if (settings.getBoolean("setting_downloadOnlyWithWifi", false) == true && !usingWifi){
+ Log.i("getThumb", "Not using wifi");
+ return;
+ }
+ Ion.with(getContext())
+ .load("http://bienvenidoainternet.org/" + bi.getParentBoard().getBoardDir() + "/thumb/" + bi.getThumb())
+ .setLogging("getThumbnail", Log.INFO)
+ .asBitmap()
+ .setCallback(new FutureCallback<Bitmap>() {
+ @Override
+ public void onCompleted(Exception e, Bitmap result) {
+ if (e != null) {
+ displayError(e.getMessage());
+ e.printStackTrace();
+ } else {
+ bi.setThumbBitmap(result);
+ listViewAdapter.notifyDataSetChanged();
+ }
+ }
+ });
+ }
+
+ private void deletePost(final boolean imageOnly, BoardItem reply) {
+ String password = settings.getString("pref_password", "12345678");
+ Ion.with(getContext())
+ .load("http://bienvenidoainternet.org/cgi/api/delete?dir=" + currentThread.getParentBoard().getBoardDir() + "&id=" + reply.getId() + "&password=" + password + "&imageonly=" + (imageOnly ? 1 : 0))
+ .setLogging("deletePost", Log.INFO)
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ if (e != null) {
+ e.printStackTrace();
+ displayError(e.getMessage());
+ } else {
+ JSONObject json = null;
+ try {
+ json = new JSONObject(result);
+ if (json.getString("state").equals("success")) {
+ Toast.makeText(getContext(), imageOnly ? "Imágen" : "Respuesta" + " eliminada", Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(getContext(), URLDecoder.decode(json.getString("message"), "UTF-8"), Toast.LENGTH_LONG).show();
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ displayError(e1.getMessage());
+ } catch (UnsupportedEncodingException e1) {
+ e1.printStackTrace();
+ displayError(e1.getMessage());
+ }
+ }
+ }
+ });
+ }
+
+
+
+ public int addReplyID(String s){
+ if (!idList.contains(new ReplyID(s))){
+ idList.add(new ReplyID(s));
+ }
+ for (ReplyID r : idList){
+ if (r.id.equals(s)){return r.color;}
+ }
+ return 0;
+ }
+
+ private void setUpThreadProgess(){
+// barThreadProcess.setVisibility(View.VISIBLE);
+ txtThreadProcess.setVisibility(View.GONE);
+ layoutThreadProcess.setVisibility(View.VISIBLE);
+ }
+
+ private void displayError(String error){
+ hideProgressBar();
+ if (error != null){
+ layoutThreadProcess.setVisibility(View.VISIBLE);
+ txtThreadProcess.setVisibility(View.VISIBLE);
+ txtThreadProcess.setText("( ; u ; ) \r\n/!\\ ERROR\r\n" + error);
+ }
+
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/AppCompatPreferenceActivity.java b/app/src/main/java/org/bienvenidoainternet/baiparser/AppCompatPreferenceActivity.java
new file mode 100644
index 0000000..e89a927
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/AppCompatPreferenceActivity.java
@@ -0,0 +1,109 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(@Nullable Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/CustomFragmentPagerAdapter.java b/app/src/main/java/org/bienvenidoainternet/baiparser/CustomFragmentPagerAdapter.java
new file mode 100644
index 0000000..40052b8
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/CustomFragmentPagerAdapter.java
@@ -0,0 +1,53 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Renard on 04-03-2016.
+ */
+
+public class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
+
+ // List of fragments which are going to set in the view pager widget
+ List<Fragment> fragments;
+
+ /**
+ * Constructor
+ *
+ * @param fm
+ * interface for interacting with Fragment objects inside of an
+ * Activity
+ */
+ public CustomFragmentPagerAdapter(FragmentManager fm) {
+ super(fm);
+ this.fragments = new ArrayList<Fragment>();
+ }
+
+ /**
+ * Add a new fragment in the list.
+ *
+ * @param fragment
+ * a new fragment
+ */
+ public void addFragment(Fragment fragment) {
+ this.fragments.add(fragment);
+ }
+
+ @Override
+ public Fragment getItem(int arg0) {
+ return this.fragments.get(arg0);
+ }
+
+ @Override
+ public int getCount() {
+ return this.fragments.size();
+ }
+
+
+
+} \ No newline at end of file
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/MainActivity.java b/app/src/main/java/org/bienvenidoainternet/baiparser/MainActivity.java
new file mode 100644
index 0000000..050cfe9
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/MainActivity.java
@@ -0,0 +1,478 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.NavigationView;
+import android.support.design.widget.Snackbar;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.view.ViewPager;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+import android.widget.BaseAdapter;
+import android.widget.HeaderViewListAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.koushikdutta.async.future.FutureCallback;
+import com.koushikdutta.ion.Ion;
+
+import org.bienvenidoainternet.baiparser.structure.Board;
+import org.bienvenidoainternet.baiparser.structure.BoardItem;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import layout.fragmentThreadList;
+
+public class MainActivity extends AppCompatActivity
+ implements NavigationView.OnNavigationItemSelectedListener, fragmentThreadList.OnFragmentInteractionListener {
+
+ public static final float CURRENT_VERSION = 1.5F;
+ private ViewPager pager; // variable del ViewPager
+ CustomFragmentPagerAdapter pagerAdapter; // Adaptador del ViewPager
+ NavigationView navigationView;
+ DrawerLayout drawer;
+ FloatingActionButton fab;
+ public ThemeManager themeManager;
+ fragmentThreadList childFragment; // Segunda página del ViewPager, se muestra un solo hilo (selecionado del catálogo)
+ fragmentThreadList mainFragment; // Primera página del ViewPager, se muestra una lista de hilos. (catálogo)
+// fragmentThreadList recentFragment;
+ Toolbar toolbar = null;
+ public int currentThemeId = 0, themeId = 0; // Id del recurso, Id del tema
+ public ArrayList<Board> boardList = new ArrayList<>();
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("currentThemeId", currentThemeId);
+ outState.putInt("themeId", themeId);
+ outState.putParcelableArrayList("boardList", boardList);
+ if (getSupportFragmentManager().getFragments() != null) {
+ if (getSupportFragmentManager().getFragments().size() != 0) {
+ try {
+ getSupportFragmentManager().putFragment(outState, "mainFragment", mainFragment);
+ getSupportFragmentManager().putFragment(outState, "childFragment", childFragment);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public int getCurrentThemeId() {
+ return currentThemeId;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ currentThemeId = savedInstanceState.getInt("currentThemeId");
+ boardList = savedInstanceState.getParcelableArrayList("boardList");
+ }
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+ themeId = Integer.valueOf(settings.getString("pref_theme", "1"));
+
+ if (settings.getString("pref_password", "").isEmpty()){
+ SharedPreferences.Editor edit = settings.edit();
+ edit.putString("pref_password", makePassword());
+ edit.commit();
+ }
+
+ switch (themeId) {
+ case 1:
+ currentThemeId = R.style.AppTheme_NoActionBar;
+ break;
+ case 2:
+ currentThemeId = R.style.AppTheme_Dark;
+ break;
+ case 3:
+ currentThemeId = R.style.AppTheme_HeadLine;
+ setTheme(R.style.AppTheme_HeadLine_Activity);
+ break;
+ case 4:
+ currentThemeId = R.style.AppTheme_Black;
+ setTheme(R.style.AppTheme_Black_Activity);
+ break;
+ }
+
+ themeManager = new ThemeManager(this);
+ Log.d("ThemeManager", "isDarkTheme: " + themeManager.isDarkTheme());
+
+ setContentView(R.layout.activity_main);
+ toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar.setTitle("Bievenido a internet");
+ this.setSupportActionBar(toolbar);
+
+
+ fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (childFragment.currentBoard != null) {
+ if (!childFragment.boardItems.isEmpty()) {
+ try {
+ Intent in = new Intent(getApplicationContext(), ResponseActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("theReply", childFragment.boardItems.get(0));
+ b.putParcelable("theBoard", childFragment.currentBoard);
+ in.putExtras(b);
+ startActivity(in);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ });
+ fab.setVisibility(View.GONE);
+
+ drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.setDrawerListener(toggle);
+ toggle.syncState();
+
+ navigationView = (NavigationView) findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ if (savedInstanceState != null) {
+ mainFragment = (fragmentThreadList) getSupportFragmentManager().getFragment(savedInstanceState, "mainFragment");
+ childFragment = (fragmentThreadList) getSupportFragmentManager().getFragment(savedInstanceState, "childFragment");
+// recentFragment = (fragmentThreadList) getSupportFragmentManager().getFragment(savedInstanceState, "recentFragment");
+ } else {
+ mainFragment = fragmentThreadList.newInstance(true, null, null);
+ childFragment = fragmentThreadList.newInstance(false, null, null);
+// recentFragment = fragmentThreadList.newInstance(false, null, -1);
+ }
+
+ this.pager = (ViewPager) findViewById(R.id.pager);
+ this.pagerAdapter = new CustomFragmentPagerAdapter(getSupportFragmentManager());
+ pagerAdapter.addFragment(mainFragment);
+ pagerAdapter.addFragment(childFragment);
+// pagerAdapter.addFragment(recentFragment);
+ this.pager.setAdapter(pagerAdapter);
+
+ pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (position == 0){
+ if (mainFragment.currentBoard != null) {
+ toolbar.setTitle("Catálogo");
+ toolbar.setSubtitle(mainFragment.currentBoard.getBoardName());
+ }
+ if (mainFragment.getMode()){
+ toolbar.setTitle("Post recientes");
+ toolbar.setSubtitle("");
+ }
+ fab.setVisibility(View.INVISIBLE);
+ }else if (position == 1){
+ if (childFragment.currentBoard != null) {
+ toolbar.setTitle(childFragment.currentBoard.getBoardName());
+ if (!childFragment.boardItems.isEmpty()){
+ toolbar.setSubtitle(childFragment.boardItems.get(0).getSubject());
+ }
+ fab.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ if (boardList.isEmpty()){
+ getBoardList();
+ }else{
+ Menu menu = navigationView.getMenu();
+ SubMenu sub = menu.addSubMenu("Lista de Boards");
+ for (Board b : boardList) {
+ sub.add(b.getBoardName());
+ }
+ refreshNavigator();
+ }
+
+ // TODO: Aplicar tema al navigator
+// navigationView.setBackgroundColor(themeManager.getPrimaryDarkColor());
+ checkForUpdates();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == 1) {
+ if(resultCode == Activity.RESULT_OK){
+ boolean result = data.getBooleanExtra("result", false);
+ if (result){
+ this.recreate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (this.pager.getCurrentItem() == 0) {
+ super.onBackPressed();
+ } else {
+ this.pager.setCurrentItem(this.pager.getCurrentItem() - 1);
+ return;
+ }
+ DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ switch (id) {
+ case R.id.action_exit:
+ System.exit(0);
+ break;
+ case R.id.action_refresh:
+ if (pager.getCurrentItem() == 0) {
+ mainFragment.refresh();
+ } else {
+ childFragment.refresh();
+ }
+ if (boardList.isEmpty()){
+ getBoardList();
+ }
+ break;
+ case R.id.action_settings:
+ Intent in2 = new Intent(getApplicationContext(), SettingsActivity.class);
+ startActivityForResult(in2, 1);
+ break;
+ case R.id.action_to_bot:
+ if (pager.getCurrentItem() == 0) {
+ mainFragment.scrollToBotton();
+ } else {
+ childFragment.scrollToBotton();
+ }
+ break;
+ case R.id.action_to_top:
+ if (pager.getCurrentItem() == 0) {
+ mainFragment.scrollToTop();
+ } else {
+ childFragment.scrollToTop();
+ }
+ break;
+ case R.id.action_update:
+ Intent updater = new Intent(getApplicationContext(), UpdaterActivity.class);
+ startActivity(updater);
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @SuppressWarnings("StatementWithEmptyBody")
+ @Override
+ public boolean onNavigationItemSelected(MenuItem item) {
+ // Handle navigation view item clicks here.
+ DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ int id = item.getItemId();
+ toolbar.setSubtitle(item.getTitle());
+ if (id == R.id.nav_recent_post){
+ toolbar.setTitle("Post recientes");
+ toolbar.setSubtitle("");
+ pager.setCurrentItem(0);
+ mainFragment.loadRecentPost();
+ }
+ for (Board b : boardList){
+ if (b.getBoardName() == item.getTitle()){
+ System.out.println("Updating mainfragment to " + b.getBoardName() + " d: " + b.getBoardDir());
+ mainFragment.setCatalogMode();
+ mainFragment.updateBoardItems(b, null);
+ pager.setCurrentItem(0);
+ navigationView.getMenu().findItem(R.id.nav_recent_post).setChecked(false);
+ }
+ }
+ return true;
+ }
+
+ public Board getBoardFromDir(String dir){
+ for (Board b : boardList){
+ if (b.getBoardDir().equals(dir)){
+ return b;
+ }
+ }
+ System.out.println("[MainActivity] Board not found " + dir);
+ return null;
+ }
+
+
+ @Override
+ public void onFragmentInteraction(Uri uri) {
+
+ }
+
+ @Override
+ public void showThread(Board board, BoardItem thread) {
+ childFragment.updateBoardItems(board, thread);
+ pager.setCurrentItem(1);
+ }
+
+
+ @Override
+ public void updateToolbar(Board cBoard, BoardItem btem) {
+ if (pager.getCurrentItem() == 1){
+ toolbar.setTitle(cBoard.getBoardName());
+ toolbar.setSubtitle(btem.getSubject());
+ }
+ }
+
+ @Override
+ public void updateToolbar(String s) {
+ toolbar.setTitle(s);
+ toolbar.setSubtitle("");
+ }
+
+ @Override
+ public void hideActionButton() {
+ if (pager.getCurrentItem() == 1){
+ fab.hide();
+ }
+ }
+
+ @Override
+ public void showActionButton() {
+ if (pager.getCurrentItem() == 1){
+ fab.show();
+ }
+ }
+
+ private void getBoardList(){
+ Menu menu = navigationView.getMenu();
+ final SubMenu sub = menu.addSubMenu("Lista de Boards");
+ Ion.with(getApplicationContext())
+ .load("http://bienvenidoainternet.org/cgi/api/boards")
+ .asJsonObject()
+ .setCallback(new FutureCallback<JsonObject>() {
+ @Override
+ public void onCompleted(Exception e, JsonObject result) {
+ if (e != null) {
+ e.printStackTrace();
+ Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
+ } else {
+ JsonArray boards = result.get("boards").getAsJsonArray();
+ for (int i = 0; i < boards.size(); i++) {
+ try {
+ JSONObject board = new JSONObject(boards.get(i).toString());
+ Board parsedBoard = new Board(board.getString("name"), board.getString("dir"), board.getInt("board_type"));
+ sub.add(parsedBoard.getBoardName());
+ boardList.add(parsedBoard);
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ Toast.makeText(getApplicationContext(), "Error parsing JSON", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+ }
+ });
+ refreshNavigator();
+ }
+
+ public void refreshNavigator(){
+ for (int i = 0, count = navigationView.getChildCount(); i < count; i++) {
+ final View child = navigationView.getChildAt(i);
+ if (child != null && child instanceof ListView) {
+ final ListView menuView = (ListView) child;
+ final HeaderViewListAdapter adapter = (HeaderViewListAdapter) menuView.getAdapter();
+ final BaseAdapter wrapped = (BaseAdapter) adapter.getWrappedAdapter();
+ wrapped.notifyDataSetChanged();
+ }
+ }
+ }
+ /*
+ Crea una secuencia de caracteres de 8 digitos aleatorios (incluye mayusculas, minisculas y numeros).
+ */
+
+ public String makePassword(){
+ Random r = new Random();
+ String rnd = "";
+ for (int i = 0; i < 8; i++){
+ int a = r.nextInt(3);
+ char b;
+ if (a == 0){
+ b = (char)(66 + r.nextInt(25));
+ }else if (a == 1){
+ b = (char)(97 + r.nextInt(25));
+ }else{
+ b = (char) (48 + r.nextInt(9));
+ }
+ rnd = rnd + b;
+ }
+ return rnd;
+ }
+
+ public void checkForUpdates(){
+ Ion.with(getApplicationContext())
+ .load("http://ahri.xyz/bai/version.php")
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ if (e != null){
+ e.printStackTrace();
+ }else{
+ try {
+ JSONObject version = new JSONObject(result);
+ float lastVersion = (float) version.getDouble("version");
+ if (CURRENT_VERSION == lastVersion){
+ Log.v("Updater", "Up to date");
+ }else{
+ Log.v("Updater", "New version available : " + lastVersion);
+ Snackbar.make(getCurrentFocus(), "Nueva versión disponible", Snackbar.LENGTH_LONG)
+ .setAction("Actualizar", new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent updater = new Intent(getApplicationContext(), UpdaterActivity.class);
+ startActivity(updater);
+ }
+ })
+ .setActionTextColor(Color.rgb(255,127,0))
+ .show();
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/RecentPostAdapter.java b/app/src/main/java/org/bienvenidoainternet/baiparser/RecentPostAdapter.java
new file mode 100644
index 0000000..e67f276
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/RecentPostAdapter.java
@@ -0,0 +1,90 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.content.Context;
+import android.text.Html;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import org.bienvenidoainternet.baiparser.structure.BoardItem;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by Renard on 21-03-2016.
+ */
+public class RecentPostAdapter extends ArrayAdapter<BoardItem> {
+
+ public RecentPostAdapter(Context context, List<BoardItem> objects) {
+ super(context, 0, objects);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater)getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View listItemView = convertView;
+ if (null == convertView) {
+ listItemView = inflater.inflate(
+ R.layout.recentpost_item,
+ parent,
+ false);
+ }
+ final BoardItem postItem = getItem(position);
+ TextView rp_message = (TextView) listItemView.findViewById(R.id.rp_message);
+ TextView rp_title = (TextView) listItemView.findViewById(R.id.rp_title);
+ TextView rp_timediff = (TextView) listItemView.findViewById(R.id.rp_timediff);
+ if (postItem.getParentBoard() != null){
+ rp_title.setText(postItem.getParentBoard().getBoardName() + ": " + postItem.getSubject());
+ }else{
+ rp_title.setText(postItem.getSubject());
+ }
+ rp_message.setText(Html.fromHtml(postItem.getMessage()));
+ Map<TimeUnit,Long> timeDiff = computeDiff(new Date(postItem.getTimeStamp() * 1000L), new Date(System.currentTimeMillis()));
+ String strTimeDiff = "";
+ if (timeDiff.get(TimeUnit.SECONDS) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.SECONDS) + (timeDiff.get(TimeUnit.SECONDS) == 1 ? " segundo" : " segundos");
+ }
+
+ if (timeDiff.get(TimeUnit.MINUTES) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.MINUTES) + (timeDiff.get(TimeUnit.MINUTES) == 1 ? " minuto" : " minutos");
+ }
+
+ if (timeDiff.get(TimeUnit.HOURS) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.HOURS) + (timeDiff.get(TimeUnit.HOURS) == 1 ? " hora" : " horas");
+ }
+
+ if (timeDiff.get(TimeUnit.DAYS) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.DAYS) + (timeDiff.get(TimeUnit.DAYS) == 1 ? " día" : " días");
+ }
+ rp_timediff.setText(strTimeDiff);
+
+
+ return listItemView;
+ }
+
+
+ public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {
+ long diffInMillies = date2.getTime() - date1.getTime();
+ List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
+ Collections.reverse(units);
+ Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
+ long milliesRest = diffInMillies;
+ for ( TimeUnit unit : units ) {
+ long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
+ long diffInMilliesForUnit = unit.toMillis(diff);
+ milliesRest = milliesRest - diffInMilliesForUnit;
+ result.put(unit,diff);
+ }
+ return result;
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/ResponseActivity.java b/app/src/main/java/org/bienvenidoainternet/baiparser/ResponseActivity.java
new file mode 100644
index 0000000..b319426
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/ResponseActivity.java
@@ -0,0 +1,216 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.koushikdutta.async.future.FutureCallback;
+import com.koushikdutta.ion.Ion;
+
+import org.bienvenidoainternet.baiparser.structure.Board;
+import org.bienvenidoainternet.baiparser.structure.BoardItem;
+import org.w3c.dom.Document;
+
+import java.io.File;
+
+import utils.ContentProviderUtils;
+
+//import org.apache.http.HttpEntity;
+//import org.apache.http.entity.ContentType;
+//import org.apache.http.entity.mime.HttpMultipartMode;
+//import org.apache.http.entity.mime.MultipartEntityBuilder;
+//import org.apache.http.entity.mime.content.FileBody;
+//import org.apache.http.entity.mime.content.StringBody;
+
+public class ResponseActivity extends AppCompatActivity {
+
+ private BoardItem theReply = null;
+ private Board currentBoard = null;
+ private SharedPreferences settings;
+ private String password;
+ private String selectedFile = "";
+ private final int PICK_IMAGE = 1;
+ EditText filePath;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_response);
+ settings = PreferenceManager.getDefaultSharedPreferences(this);
+ password = settings.getString("pref_password", "12345678");
+ Log.v("password", password);
+
+ if (savedInstanceState != null){
+ this.theReply = savedInstanceState.getParcelable("theReply");
+ this.currentBoard = savedInstanceState.getParcelable("theBoard");
+ }
+ if (getIntent().getExtras() != null){
+ this.theReply = getIntent().getParcelableExtra("theReply");
+ this.currentBoard = getIntent().getParcelableExtra("theBoard");
+ }
+ if (theReply != null && currentBoard != null){
+ System.out.println(theReply.getId() + " " + theReply.getName());
+ }
+
+ LinearLayout layoutProcess = (LinearLayout)findViewById(R.id.layoutPostProcess);
+ layoutProcess.setVisibility(View.GONE);
+ filePath = (EditText) findViewById(R.id.txtFilePath);
+ Button send = (Button)findViewById(R.id.btnSend);
+ send.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TextView txtName = (TextView) findViewById(R.id.txtPosterName);
+ TextView txtEmail = (TextView) findViewById(R.id.txtEmail);
+ TextView txtMessage = (TextView) findViewById(R.id.txtResponse);
+ makePost(txtName.getText().toString(), txtEmail.getText().toString(), txtMessage.getText().toString());
+
+ }
+ });
+
+ Button bBold = (Button) findViewById(R.id.buttonBold);
+ bBold.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TextView txtMessage = (TextView) findViewById(R.id.txtResponse);
+ if (txtMessage.getSelectionStart() == -1){
+ txtMessage.setText(txtMessage.getText() + "<b></b>");
+ }else{
+ String s = txtMessage.getText().toString();
+ String a = s.substring(0, txtMessage.getSelectionStart());
+ String b = s.substring(txtMessage.getSelectionStart(), txtMessage.getSelectionEnd());
+ String c = s.substring(txtMessage.getSelectionEnd(), txtMessage.getText().length());
+ txtMessage.setText(a + "<b>" + b + "</b>" + c);
+ }
+ }
+ });
+ Button bItalic = (Button) findViewById(R.id.buttonItalic);
+ bItalic.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TextView txtMessage = (TextView) findViewById(R.id.txtResponse);
+ if (txtMessage.getSelectionStart() == -1){
+ txtMessage.setText(txtMessage.getText() + "<i></i>");
+ }else{
+ String s = txtMessage.getText().toString();
+ String a = s.substring(0, txtMessage.getSelectionStart());
+ String b = s.substring(txtMessage.getSelectionStart(), txtMessage.getSelectionEnd());
+ String c = s.substring(txtMessage.getSelectionEnd(), txtMessage.getText().length());
+ txtMessage.setText(a + "<i>" + b + "</i>" + c);
+ }
+ }
+ });
+
+ Button select = (Button) findViewById(R.id.btnSelectFiles);
+
+ select.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent();
+ intent.setType("image/*");
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ startActivityForResult(Intent.createChooser(intent, "Seleccionar Archivo"), PICK_IMAGE);
+ }
+ });
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && null != data) {
+ Uri selectedImage = data.getData();
+ String picturePath = ContentProviderUtils.getPath(getApplicationContext(), selectedImage);
+ selectedFile = picturePath;
+ filePath.setText(picturePath);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ }
+
+ private void makePost(String name, String email, String message){
+ int parentId = theReply.getParentId();
+ if (theReply.getParentId() == 0 || theReply.getParentId() == -1){
+ parentId = theReply.getId();
+ }
+ LinearLayout layoutProcess = (LinearLayout)findViewById(R.id.layoutPostProcess);
+ layoutProcess.setVisibility(View.VISIBLE);
+ final RelativeLayout formSendPost = (RelativeLayout) findViewById(R.id.layoutForm);
+ formSendPost.setVisibility(View.GONE);
+ ProgressBar progess = (ProgressBar) findViewById(R.id.barPosting);
+ final TextView err = (TextView)findViewById(R.id.txtPostingState);
+ err.setText("");
+ File up = new File(selectedFile);
+ if (selectedFile.isEmpty()) Ion.with(getApplicationContext())
+ .load("http://bienvenidoainternet.org/cgi/post")
+ .setLogging("posting", Log.VERBOSE)
+ .uploadProgressBar(progess)
+ .setMultipartParameter("board", currentBoard.getBoardDir())
+ .setMultipartParameter("parent", String.valueOf(theReply.realParentId()))
+ .setMultipartParameter("password", password)
+ .setMultipartParameter("fielda", name)
+ .setMultipartParameter("fieldb", email)
+ .setMultipartParameter("name", "")
+ .setMultipartParameter("email", "")
+ .setMultipartParameter("message", message)
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ Log.v("sendPost", result);
+ if (e != null){
+ Toast.makeText(getApplicationContext(), "Ha ocurrido un error! ;_;", Toast.LENGTH_LONG).show();
+ formSendPost.setVisibility(View.VISIBLE);
+ err.setText("Error: " + e.getMessage());
+ e.printStackTrace();
+ }else{
+ Toast.makeText(getApplicationContext(), "Post enviado", Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+ });
+ else{
+ Ion.with(getApplicationContext())
+ .load("http://bienvenidoainternet.org/cgi/post")
+ .uploadProgressBar(progess)
+ .setMultipartParameter("board", currentBoard.getBoardDir())
+ .setMultipartParameter("parent", String.valueOf(parentId))
+ .setMultipartParameter("password", password)
+ .setMultipartParameter("fielda", name)
+ .setMultipartParameter("fieldb", email)
+ .setMultipartParameter("name", "")
+ .setMultipartParameter("email", "")
+ .setMultipartParameter("message", message)
+ .setMultipartFile("file", up)
+ .asDocument()
+ .setCallback(new FutureCallback<Document>() {
+ @Override
+ public void onCompleted(Exception e, Document result) {
+ if (e != null){
+ Toast.makeText(getApplicationContext(), "Ha ocurrido un error! ;_;", Toast.LENGTH_LONG).show();
+ formSendPost.setVisibility(View.VISIBLE);
+ err.setText("Error: " + e.getMessage());
+ e.printStackTrace();
+ }else{
+ Toast.makeText(getApplicationContext(), "Post enviado", Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/SettingsActivity.java b/app/src/main/java/org/bienvenidoainternet/baiparser/SettingsActivity.java
new file mode 100644
index 0000000..e8e7df4
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/SettingsActivity.java
@@ -0,0 +1,184 @@
+package org.bienvenidoainternet.baiparser;
+
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+import android.support.v7.app.ActionBar;
+import android.view.MenuItem;
+
+import java.util.List;
+
+/**
+ * A {@link PreferenceActivity} that presents a set of application settings. On
+ * handset devices, settings are presented as a single list. On tablets,
+ * settings are split by category, with category headers shown to the left of
+ * the list of settings.
+ * <p/>
+ * See <a href="http://developer.android.com/design/patterns/settings.html">
+ * Android Design: Settings</a> for design guidelines and the <a
+ * href="http://developer.android.com/guide/topics/ui/settings.html">Settings
+ * API Guide</a> for more information on developing a Settings UI.
+ */
+public class SettingsActivity extends AppCompatPreferenceActivity {
+ /**
+ * A preference value change listener that updates the preference's summary
+ * to reflect its new value.
+ */
+ private static boolean requireReset = false;
+ private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ String stringValue = value.toString();
+
+ if (preference instanceof ListPreference) {
+ // For list preferences, look up the correct display value in
+ // the preference's 'entries' list.
+ ListPreference listPreference = (ListPreference) preference;
+ int index = listPreference.findIndexOfValue(stringValue);
+
+ // Set the summary to reflect the new value.
+ preference.setSummary(
+ index >= 0
+ ? listPreference.getEntries()[index]
+ : null);
+
+ }else {
+ // For all other preferences, set the summary to the value's
+ // simple string representation.
+ preference.setSummary(stringValue);
+ }
+ System.out.println(preference.getKey());
+ return true;
+ }
+ };
+
+ /**
+ * Helper method to determine if the device has an extra-large screen. For
+ * example, 10" tablets are extra-large.
+ */
+ private static boolean isXLargeTablet(Context context) {
+ return (context.getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ }
+
+ /**
+ * Binds a preference's summary to its value. More specifically, when the
+ * preference's value is changed, its summary (line of text below the
+ * preference title) is updated to reflect the value. The summary is also
+ * immediately updated upon calling this method. The exact display format is
+ * dependent on the type of preference.
+ *
+ * @see #sBindPreferenceSummaryToValueListener
+ */
+ private static void bindPreferenceSummaryToValue(Preference preference) {
+ // Set the listener to watch for value changes.
+ preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
+ requireReset = true;
+ // Trigger the listener immediately with the preference's
+ // current value.
+ sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
+ PreferenceManager
+ .getDefaultSharedPreferences(preference.getContext())
+ .getString(preference.getKey(), ""));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setupActionBar();
+ getFragmentManager().beginTransaction().replace(android.R.id.content, new GeneralPreferenceFragment()).commit();
+ }
+
+ /**
+ * Set up the {@link android.app.ActionBar}, if the API is available.
+ */
+ private void setupActionBar() {
+ ActionBar actionBar = getSupportActionBar();
+
+ if (actionBar != null) {
+ // Show the Up button in the action bar.
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onIsMultiPane() {
+ return isXLargeTablet(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void onBuildHeaders(List<Header> target) {
+// loadHeadersFromResource(R.xml.pref_headers, target);
+ }
+
+ /**
+ * This method stops fragment injection in malicious applications.
+ * Make sure to deny any unknown fragments here.
+ */
+ protected boolean isValidFragment(String fragmentName) {
+ return PreferenceFragment.class.getName().equals(fragmentName)
+ || GeneralPreferenceFragment.class.getName().equals(fragmentName)
+ ;
+ }
+
+ @Override
+ public void onBackPressed() {
+ Intent returnIntent = new Intent();
+ returnIntent.putExtra("result", requireReset);
+ setResult(Activity.RESULT_OK,returnIntent);
+ finish();
+ super.onBackPressed();
+ }
+
+ /**
+ * This fragment shows general preferences only. It is used when the
+ * activity is showing a two-pane settings UI.
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public class GeneralPreferenceFragment extends PreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ setHasOptionsMenu(true);
+
+ // Bind the summaries of EditText/List/Dialog/Ringtone preferences
+ // to their values. When their values change, their summaries are
+ // updated to reflect the new value, per the Android Design
+ // guidelines.
+// bindPreferenceSummaryToValue(findPreference("example_text"));
+ bindPreferenceSummaryToValue(findPreference("pref_theme"));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+// startActivity(new Intent(getActivity(), SettingsActivity.class));
+ Intent returnIntent = new Intent();
+ returnIntent.putExtra("result",requireReset);
+ setResult(Activity.RESULT_OK,returnIntent);
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/ThemeManager.java b/app/src/main/java/org/bienvenidoainternet/baiparser/ThemeManager.java
new file mode 100644
index 0000000..7f0be86
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/ThemeManager.java
@@ -0,0 +1,55 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.content.res.TypedArray;
+import android.graphics.Color;
+
+/**
+ * Created by Renard on 16-03-2016.
+ */
+public class ThemeManager {
+ private MainActivity ac;
+ private int currentThemeId;
+ public ThemeManager(MainActivity ac){
+ this.ac = ac;
+ this.currentThemeId = ac.getCurrentThemeId();
+ }
+
+ public int getSageColor(){
+ TypedArray a = ac.getTheme().obtainStyledAttributes(currentThemeId, new int[]{R.attr.sageColor});
+ return a.getColor(0, Color.CYAN);
+ }
+
+ public int getMarginColor(){
+ TypedArray a = ac.getTheme().obtainStyledAttributes(currentThemeId, new int[]{R.attr.marginColor});
+ return a.getColor(0, Color.CYAN);
+ }
+
+ public void updateThemeId(int id){
+ this.currentThemeId = id;
+ }
+
+ public int getNameColor() {
+ TypedArray a = ac.getTheme().obtainStyledAttributes(currentThemeId, new int[]{R.attr.nameColor});
+ return a.getColor(0, Color.CYAN);
+ }
+
+ public int getTripcodeColor() {
+ TypedArray a = ac.getTheme().obtainStyledAttributes(currentThemeId, new int[]{R.attr.tripcodeColor});
+ return a.getColor(0, Color.CYAN);
+ }
+
+ public int getPrimaryColor(){
+ TypedArray a = ac.getTheme().obtainStyledAttributes(currentThemeId, new int[]{R.attr.colorPrimary});
+ return a.getColor(0, Color.CYAN);
+ }
+ public int getPrimaryDarkColor(){
+ TypedArray a = ac.getTheme().obtainStyledAttributes(currentThemeId, new int[]{R.attr.colorPrimaryDark});
+ return a.getColor(0, Color.CYAN);
+ }
+
+ public boolean isDarkTheme(){
+ TypedArray a = ac.getTheme().obtainStyledAttributes(currentThemeId, new int[]{R.attr.isDarkTheme});
+ return a.getBoolean(0, false);
+ }
+
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/ThreadListAdapter.java b/app/src/main/java/org/bienvenidoainternet/baiparser/ThreadListAdapter.java
new file mode 100644
index 0000000..118e9c6
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/ThreadListAdapter.java
@@ -0,0 +1,281 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.Html;
+import android.text.Layout;
+import android.text.Spannable;
+import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.bienvenidoainternet.baiparser.structure.BoardItem;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by Renard on 11-03-2016.
+ */
+public class ThreadListAdapter extends ArrayAdapter<BoardItem>{
+ private Context context;
+ private ThemeManager tm;
+ Typeface monaFont;
+ public boolean listThreads = false;
+
+ public ThreadListAdapter(Context context, List<BoardItem> objects, ThemeManager tm) {
+ super(context, 0, objects);
+ this.context = context;
+ this.tm = tm;
+ monaFont = Typeface.createFromAsset(context.getAssets(), "fonts/mona.ttf");
+ }
+
+ private String intToHexString(int i){
+ return String.format("#%06X", (0xFFFFFF & i));
+ }
+ public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {
+ long diffInMillies = date2.getTime() - date1.getTime();
+ List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
+ Collections.reverse(units);
+ Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
+ long milliesRest = diffInMillies;
+ for ( TimeUnit unit : units ) {
+ long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
+ long diffInMilliesForUnit = unit.toMillis(diff);
+ milliesRest = milliesRest - diffInMilliesForUnit;
+ result.put(unit,diff);
+ }
+ return result;
+ }
+ @Override
+ public View getView(int position, final View convertView, final ViewGroup parent){
+ LayoutInflater inflater = (LayoutInflater)getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View listItemView = convertView;
+ if (null == convertView) {
+ listItemView = inflater.inflate(
+ R.layout.thread_item,
+ parent,
+ false);
+ }
+
+ final BoardItem boardItem = getItem(position);
+ if (boardItem == null){
+ return listItemView;
+ }
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this.getContext());
+ boolean useMonaFont = settings.getBoolean("setting_monafont", true);
+ boolean monaBbsOnly = settings.getBoolean("setting_mona_bbsonly", true);
+ int marginColor = tm.getMarginColor();
+ int sageColor = tm.getSageColor();
+ int nameColor = tm.getNameColor();
+ int tripcodeColor = tm.getTripcodeColor();
+ String hexColor =intToHexString(boardItem.getIdColor());
+ String sageHexColor = intToHexString(sageColor);
+ String nameHexColor = intToHexString(nameColor);
+ String tripcodeHexColor = intToHexString(tripcodeColor);
+ String strId = "";
+
+ TextView txtTitle = (TextView)listItemView.findViewById(R.id.lv_txtTitle);
+ TextView txtPoster = (TextView)listItemView.findViewById(R.id.lv_txtPoster);
+ TextView txtBody = (TextView) listItemView.findViewById(R.id.lv_txtBody);
+ TextView txtReplies = (TextView) listItemView.findViewById(R.id.lv_txtReplyCounter);
+ TextView txtFileInfo = (TextView) listItemView.findViewById(R.id.lv_txtFileInfo);
+ ImageView ivMargin = (ImageView)listItemView.findViewById(R.id.ivMargin);
+ ImageView ivThumb = (ImageView)listItemView.findViewById(R.id.ivThumb);
+
+ ivThumb.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!boardItem.getThumb().isEmpty() && convertView != null){
+ if (boardItem.getFile().endsWith(".webm")){
+ Intent in = new Intent(Intent.ACTION_VIEW, Uri.parse("http://bienvenidoainternet.org/" + boardItem.getParentBoard().getBoardDir() + "/src/" + boardItem.getFile()));
+ in.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ v.getContext().startActivity(in);
+ }else {
+ Intent in = new Intent(convertView.getContext(), ViewerActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("boardItem", boardItem);
+ in.putExtras(b);
+ convertView.getContext().startActivity(in);
+ }
+ }
+ }
+ });
+
+
+ ivMargin.setImageDrawable(new ColorDrawable(marginColor));
+
+ if (useMonaFont){
+ if (monaBbsOnly && boardItem.getParentBoard() != null){
+ if (boardItem.getParentBoard().getBoardType() == 1){
+ txtBody.setTypeface(monaFont);
+ }
+ }else{
+ txtBody.setTypeface(monaFont);
+ }
+ }
+
+ // Si es una respuesta ocultamos el margen
+ if (boardItem.isReply){
+ ivMargin.setVisibility(View.VISIBLE);
+ txtTitle.setVisibility(View.GONE);
+ }else{
+ txtTitle.setVisibility(View.VISIBLE);
+ ivMargin.setVisibility(View.GONE);
+ txtTitle.setText(boardItem.getSubject());
+ }
+
+ // Si el fragmento esta viendo un hilo ocultamos los margenes
+ if (listThreads){
+ ivMargin.setVisibility(View.GONE);
+ }
+
+ // Si el item está eliminado activamos el soporte de <strike>
+ if (boardItem.getDeletedCode() != 0){
+ txtBody.setPaintFlags(txtBody.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ }else{
+ txtBody.setPaintFlags(txtBody.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
+ }
+
+
+ if (boardItem.getThumb().isEmpty()){
+ ivThumb.setVisibility(View.GONE);
+ }else{
+ if (boardItem.getThumbBitmap() != null){
+ ivThumb.setVisibility(View.VISIBLE);
+ ivThumb.setImageBitmap(boardItem.getThumbBitmap());
+ }else{
+ ivThumb.setVisibility(View.VISIBLE);
+ ivThumb.setImageResource(R.drawable.blank);
+ }
+ }
+
+ Map<TimeUnit,Long> timeDiff = computeDiff(new Date(boardItem.getTimeStamp() * 1000L), new Date(System.currentTimeMillis()));
+ String strTimeDiff = "";
+ if (timeDiff.get(TimeUnit.SECONDS) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.SECONDS) + (timeDiff.get(TimeUnit.SECONDS) == 1 ? " segundo" : " segundos");
+ }
+ if (timeDiff.get(TimeUnit.MINUTES) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.MINUTES) + (timeDiff.get(TimeUnit.MINUTES) == 1 ? " minuto" : " minutos");
+ }
+ if (timeDiff.get(TimeUnit.HOURS) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.HOURS) + (timeDiff.get(TimeUnit.HOURS) == 1 ? " hora" : " horas");
+ }
+ if (timeDiff.get(TimeUnit.DAYS) != 0){
+ strTimeDiff = "Hace " + timeDiff.get(TimeUnit.DAYS) + (timeDiff.get(TimeUnit.DAYS) == 1 ? " día" : " días");
+ }
+
+
+ if (!boardItem.getPosterId().isEmpty() && !boardItem.getPosterId().equals("???")){
+ strId = "<font color=" + hexColor + ">[" + boardItem.getPosterId() + "]</font> ";
+ }
+
+ // Si estamos mostrando un item de BBS, mostrar el ID_BBS en ves del ID del post
+ int idToDisplay = 0;
+ if (boardItem.getParentBoard() != null){
+ if (boardItem.getParentBoard().getBoardType() == 1){
+ idToDisplay = boardItem.getBbsId();
+ }else{
+ idToDisplay = boardItem.getId();
+ }
+ }else{
+ idToDisplay = boardItem.getId();
+ }
+
+ txtPoster.setText(Html.fromHtml("<b>No. " + idToDisplay + "</b> por <font color=" + (boardItem.isSage() ? sageHexColor : nameHexColor) + ">" + boardItem.getName() + "</font> "
+ + (boardItem.getTripcode() == "" ? "" : "<font color=" + tripcodeHexColor + ">" + boardItem.getTripcode() + "</font>") + strId + " " + strTimeDiff));
+ txtBody.setText(Html.fromHtml(boardItem.getMessage()));
+
+ txtReplies.setVisibility(boardItem.isReply ? View.GONE : View.VISIBLE);
+ txtReplies.setText(boardItem.getTotalReplies() + " respuestas " + (boardItem.getTotalFiles() == 0 ? "" : ", " + boardItem.getTotalFiles() + " archivos"));
+
+ txtFileInfo.setVisibility(boardItem.getThumb().isEmpty() ? View.GONE : View.VISIBLE);
+ txtFileInfo.setText((boardItem.getFileSize() / 1024) + " KB " + boardItem.getThumbHeight() + "x" + boardItem.getThumbWidth());
+
+ // Trasnparentar items con sage
+ if (convertView != null){
+ convertView.setAlpha(boardItem.isSage() ? 0.75F : 1.0F);
+ }
+
+ /*
+ http://stackoverflow.com/questions/8558732/listview-textview-with-linkmovementmethod-makes-list-item-unclickable
+ */
+ txtBody.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ boolean ret = false;
+ CharSequence text = ((TextView) v).getText();
+ Spannable stext = Spannable.Factory.getInstance().newSpannable(text);
+ TextView widget = (TextView) v;
+ int action = event.getAction();
+
+ if (action == MotionEvent.ACTION_UP ||
+ action == MotionEvent.ACTION_DOWN) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+
+ x -= widget.getTotalPaddingLeft();
+ y -= widget.getTotalPaddingTop();
+
+ x += widget.getScrollX();
+ y += widget.getScrollY();
+
+ Layout layout = widget.getLayout();
+ int line = layout.getLineForVertical(y);
+ int off = layout.getOffsetForHorizontal(line, x);
+
+ ClickableSpan[] link = stext.getSpans(off, off, ClickableSpan.class);
+/*04-03 17:46:54.646 13693-13693/org.bienvenidoainternet.baiparser V/URLParts: zonavip
+04-03 17:46:54.646 13693-13693/org.bienvenidoainternet.baiparser V/URLParts: read
+04-03 17:46:54.646 13693-13693/org.bienvenidoainternet.baiparser V/URLParts: 43872
+04-03 17:46:54.650 13693-13693/org.bienvenidoainternet.baiparser V/URLParts: 25*/
+ if (link.length != 0) {
+ if (link[0] instanceof URLSpan){
+ URLSpan uspan = (URLSpan) link[0];
+ if (uspan.getURL().contains("/read/") && !uspan.getURL().contains("http")){
+ String url = uspan.getURL();
+ String[] parts = url.split("/");
+ if (parts.length == 4 && listThreads){
+ Log.v("ConvertView", convertView.getParent().toString());
+ }
+ return true;
+ }
+ }
+ if (action == MotionEvent.ACTION_UP) {
+ link[0].onClick(widget);
+ }
+ ret = true;
+ }
+ }
+ return ret;
+ }
+ });
+ return listItemView;
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/UpdaterActivity.java b/app/src/main/java/org/bienvenidoainternet/baiparser/UpdaterActivity.java
new file mode 100644
index 0000000..e98f303
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/UpdaterActivity.java
@@ -0,0 +1,127 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.text.Html;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.koushikdutta.async.future.FutureCallback;
+import com.koushikdutta.ion.Ion;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
+public class UpdaterActivity extends AppCompatActivity {
+ private float lastVersion = 1.0F;
+ Button btnUpdate;
+ ProgressBar barUpdate;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_updater);
+ setTheme(R.style.AppTheme_Black_Activity);
+ btnUpdate = (Button) findViewById(R.id.btnDownloadLastVersion);
+ barUpdate = (ProgressBar) findViewById(R.id.barUpdateProgress);
+ TextView txtCurrentVersion = (TextView) findViewById(R.id.txtCurrentVersion);
+ btnUpdate.setEnabled(false);
+ txtCurrentVersion.setText("Versión actual: " + MainActivity.CURRENT_VERSION);
+ getVersionData();
+ btnUpdate.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ downloadApk();
+ }
+ });
+ }
+
+ private void getVersionData(){
+ Ion.with(getApplicationContext())
+ .load("http://ahri.xyz/bai/version.php")
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ if (e != null) {
+ e.printStackTrace();
+ } else {
+ JSONObject version = null;
+ try {
+ version = new JSONObject(result);
+ lastVersion = (float) version.getDouble("version");
+ TextView txtLastVersion = (TextView) findViewById(R.id.txtLastVersion);
+ txtLastVersion.setText("Última versión: " + lastVersion);
+
+ if (lastVersion > MainActivity.CURRENT_VERSION) {
+ getChangelog();
+ btnUpdate.setEnabled(true);
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ });
+ }
+
+ private void getChangelog(){
+ Ion.with(getApplicationContext())
+ .load("http://ahri.xyz/bai/lastChangelog.txt")
+ .asString()
+ .setCallback(new FutureCallback<String>() {
+ @Override
+ public void onCompleted(Exception e, String result) {
+ if (e != null){
+ e.printStackTrace();
+ }else{
+ TextView txtChangelog = (TextView) findViewById(R.id.txtChangelog);
+ txtChangelog.setText(Html.fromHtml(result));
+ }
+ }
+ });
+ }
+
+ private void downloadApk(){
+ ContextWrapper cw = new ContextWrapper(getApplicationContext());
+ File directory = cw.getDir("src", Context.MODE_PRIVATE);
+ if (!directory.exists()) {
+ directory.mkdir();
+ }
+ final File filePath = new File(directory, "last.apk");
+ if (filePath.exists()) {
+ filePath.delete();
+ }
+ Ion.with(getApplicationContext())
+ .load("http://ahri.xyz/bai/" + lastVersion + "/last.apk")
+ .setLogging("Updater", Log.VERBOSE)
+ .progressBar(barUpdate)
+ .write(filePath)
+ .setCallback(new FutureCallback<File>() {
+ @Override
+ public void onCompleted(Exception e, File result) {
+ if (e != null) {
+ Toast.makeText(getApplicationContext(), "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
+ } else {
+ Intent promptInstall = new Intent(Intent.ACTION_VIEW)
+ .setDataAndType(Uri.fromFile(filePath),
+ "application/vnd.android.package-archive");
+ startActivity(promptInstall);
+ }
+ }
+ });
+ }
+}
+
+
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/ViewerActivity.java b/app/src/main/java/org/bienvenidoainternet/baiparser/ViewerActivity.java
new file mode 100644
index 0000000..b113c2a
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/ViewerActivity.java
@@ -0,0 +1,247 @@
+package org.bienvenidoainternet.baiparser;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import com.davemorrissey.labs.subscaleview.ImageSource;
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
+import com.koushikdutta.async.future.FutureCallback;
+import com.koushikdutta.ion.Ion;
+
+import org.bienvenidoainternet.baiparser.structure.BoardItem;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import pl.droidsonroids.gif.GifDrawable;
+import pl.droidsonroids.gif.GifImageView;
+
+public class ViewerActivity extends AppCompatActivity {
+ private SubsamplingScaleImageView imageView;
+ private GifImageView gifView;
+ private BoardItem bi;
+ File imagePath;
+
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+// SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+// int themeId = Integer.valueOf(settings.getString("setting_theme", "1")), currentThemeId = R.style.AppTheme;
+// switch (themeId) {
+// case 1:
+// currentThemeId = R.style.AppTheme_NoActionBar;
+// break;
+// case 2:
+// currentThemeId = R.style.AppTheme_Dark;
+// break;
+// case 3:
+// currentThemeId = R.style.AppTheme_HeadLine;
+// break;
+// case 4:
+// currentThemeId = R.style.AppTheme_Black;
+// break;
+// }
+// setTheme(currentThemeId);
+
+
+ if (savedInstanceState != null){
+ bi = savedInstanceState.getParcelable("boardItem");
+ }
+ if (getIntent().getExtras() != null){
+ bi = getIntent().getParcelableExtra("boardItem");
+ }
+ setContentView(R.layout.activity_viewer);
+ imageView = (SubsamplingScaleImageView)findViewById(R.id.imageView);
+ gifView = (GifImageView) findViewById(R.id.gifView);
+ setTitle(bi.getFile());
+// imageView.setOnClickListener(new View.OnClickListener() {
+// new TaskDownloadFile().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ downloadFile();
+ this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ File baiDir = new File(Environment.getExternalStorageDirectory().getPath() + "/Bai/");
+ if (!baiDir.exists()){
+ baiDir.mkdir();
+ }
+ if (item.getItemId() == R.id.menu_save_img){
+ File to = new File(Environment.getExternalStorageDirectory().getPath() + "/Bai/" + bi.getFile());
+ try{
+ InputStream in = new FileInputStream(imagePath);
+ OutputStream out = new FileOutputStream(to);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ in.close();
+ out.close();
+ Toast.makeText(getApplicationContext(), bi.getFile() + " guardado.", Toast.LENGTH_LONG).show();
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+
+ }
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_viewer, menu);
+ return true;
+ }
+
+ private void downloadFile(){
+ ContextWrapper cw = new ContextWrapper(getApplicationContext());
+ File directory = cw.getDir("src", Context.MODE_PRIVATE);
+ final File filePath = new File(directory, bi.getParentBoard().getBoardDir() + "_" + bi.getFile());
+ final ProgressBar downloadBar = (ProgressBar) findViewById(R.id.downloadProgressBar);
+ if (filePath.exists()){
+ downloadBar.setVisibility(View.GONE);
+ if (bi.getFile().endsWith(".gif")){
+ try {
+ GifDrawable gifFromFile = new GifDrawable(filePath);
+ gifView.setImageDrawable(gifFromFile);
+ imageView.setVisibility(View.GONE);
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+ }else{
+ imageView.setImage(ImageSource.uri(filePath.toURI().getPath()));
+ gifView.setVisibility(View.GONE);
+ }
+ }
+ Ion.with(getApplicationContext())
+ .load("http://bienvenidoainternet.org/" + bi.getParentBoard().getBoardDir() + "/src/" + bi.getFile())
+ .progressBar(downloadBar)
+ .asInputStream()
+ .setCallback(new FutureCallback<InputStream>() {
+ @Override
+ public void onCompleted(Exception e, InputStream result) {
+ downloadBar.setVisibility(View.GONE);
+ if (e != null){
+ e.printStackTrace();
+ }else{
+ FileOutputStream fout;
+ try {
+ fout = new FileOutputStream(filePath);
+ final byte data[] = new byte[1024];
+ int count;
+ while ((count = result.read(data, 0, 1024)) != -1) {
+ fout.write(data, 0, count);
+ }
+ }catch(Exception e1) {
+ e1.printStackTrace();
+ }
+ if (bi.getFile().endsWith(".gif")){
+ try {
+ GifDrawable gifFromFile = new GifDrawable(filePath);
+ gifView.setImageDrawable(gifFromFile);
+ imageView.setVisibility(View.GONE);
+ }catch(Exception e2){
+ e2.printStackTrace();
+ }
+ }else{
+ imageView.setImage(ImageSource.uri(filePath.toURI().getPath()));
+ gifView.setVisibility(View.GONE);
+ }
+ }
+ }
+ });
+ }
+
+ class TaskDownloadFile extends AsyncTask<Void, Void, File> {
+
+ @Override
+ protected File doInBackground(Void... params) {
+ Bitmap downloadedBitmap = null;
+ ContextWrapper cw = new ContextWrapper(getApplicationContext());
+ File directory = cw.getDir("src", Context.MODE_PRIVATE);
+ File mypath = new File(directory, bi.getParentBoard().getBoardDir() + "_" + bi.getFile());
+ if (mypath.exists()){
+ System.out.println("[Viewer] resource exist!");
+ return mypath;
+ }
+ try {
+ String sUrl = "http://bienvenidoainternet.org/" + bi.getParentBoard().getBoardDir() + "/src/" + bi.getFile();
+ System.out.println("[Viewer]dwonloading " + sUrl);
+// System.out.println(sUrl);
+ InputStream in = new java.net.URL(sUrl).openStream();
+// downloadedBitmap = BitmapFactory.decodeStream(in);
+
+// if (downloadedBitmap != null){
+ FileOutputStream fout = null;
+ try {
+// in = new BufferedInputStream(new URL(urlString).openStream());
+ fout = new FileOutputStream(mypath);
+
+ final byte data[] = new byte[1024];
+ int count;
+ while ((count = in.read(data, 0, 1024)) != -1) {
+ fout.write(data, 0, count);
+ }
+
+
+ }catch(Exception e){
+ e.printStackTrace();
+ }finally {
+ if (in != null) {
+ in.close();
+ }
+ if (fout != null) {
+ fout.close();
+ }
+ }
+// }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return mypath;
+ }
+
+ @Override
+ protected void onPostExecute(File file) {
+ super.onPostExecute(file);
+ imagePath = file;
+// iv.setImageBitmap(bitmap);
+ if (bi.getFile().endsWith(".gif")){
+ try {
+ GifDrawable gifFromFile = new GifDrawable(file);
+ gifView.setImageDrawable(gifFromFile);
+ imageView.setVisibility(View.GONE);
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+ }else{
+ imageView.setImage(ImageSource.uri(file.toURI().getPath()));
+ gifView.setVisibility(View.GONE);
+// imageView.setImage(ImageSource.resource(R.drawable.bai));
+// System.out.println("not a gif file: " + file.toURI().getPath());
+ }
+
+ }
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/structure/Board.java b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/Board.java
new file mode 100644
index 0000000..209804e
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/Board.java
@@ -0,0 +1,60 @@
+package org.bienvenidoainternet.baiparser.structure;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Created by Renard on 17-03-2016.
+ */
+
+public class Board implements Parcelable{
+ private String boardName, boardDir;
+ private int boardType;
+ public Board(String boardName,String boardDir,int boardType){
+ this.boardName = boardName;
+ this.boardDir = boardDir;
+ this.boardType = boardType;
+ }
+
+ public Board(Parcel in){
+ this.boardName = in.readString();
+ this.boardDir = in.readString();
+ this.boardType = in.readInt();
+ }
+
+ public String getBoardDir() {
+ return boardDir;
+ }
+
+ public String getBoardName() {
+ return boardName;
+ }
+
+ public int getBoardType() {
+ return boardType;
+ }
+
+ public static final Creator<Board> CREATOR = new Creator<Board>() {
+ @Override
+ public Board createFromParcel(Parcel in) {
+ return new Board(in);
+ }
+
+ @Override
+ public Board[] newArray(int size) {
+ return new Board[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(boardName);
+ dest.writeString(boardDir);
+ dest.writeInt(boardType);
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/structure/BoardItem.java b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/BoardItem.java
new file mode 100644
index 0000000..c43384a
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/BoardItem.java
@@ -0,0 +1,310 @@
+package org.bienvenidoainternet.baiparser.structure;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Created by Renard on 17-03-2016.
+ */
+public class BoardItem implements Parcelable {
+ private String name = "";
+ private String timestamp_formatted = "";
+ private String thumb = "";
+ private String tripcode = "";
+ private String email = "";
+ private String file = "";
+ private String message = "";
+ private String subject = "";
+ private String posterId = "";
+ private int parentid, id, idcolor, totalreplies = 0, totalfiles, thumb_height, thumb_weight, filesize, deleted_code, bbs_id = 1, parentPostCount;
+ private long timestamp = 0;
+
+ private Bitmap thumbBitmap = null;
+ public boolean downloadingThumb = false;
+ public boolean isReply = false;
+ private Board parentBoard = null;
+
+ protected BoardItem(Parcel in) {
+ name = in.readString();
+ timestamp_formatted = in.readString();
+ thumb = in.readString();
+ tripcode = in.readString();
+ email = in.readString();
+ file = in.readString();
+ message = in.readString();
+ subject = in.readString();
+ posterId = in.readString();
+ parentid = in.readInt();
+ id = in.readInt();
+ idcolor = in.readInt();
+ totalreplies = in.readInt();
+ totalfiles = in.readInt();
+ thumb_height = in.readInt();
+ thumb_weight = in.readInt();
+ filesize = in.readInt();
+ deleted_code = in.readInt();
+ bbs_id = in.readInt();
+ parentPostCount = in.readInt();
+ timestamp = in.readLong();
+ thumbBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ downloadingThumb = in.readByte() != 0;
+ isReply = in.readByte() != 0;
+ parentBoard = in.readParcelable(Board.class.getClassLoader());
+ }
+
+ public static final Creator<BoardItem> CREATOR = new Creator<BoardItem>() {
+ @Override
+ public BoardItem createFromParcel(Parcel in) {
+ return new BoardItem(in);
+ }
+
+ @Override
+ public BoardItem[] newArray(int size) {
+ return new BoardItem[size];
+ }
+ };
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getFile() {
+ return file;
+ }
+
+ public void setFile(String file) {
+ this.file = file;
+ }
+
+ public int getFileSize() {
+ return filesize;
+ }
+
+ public void setFilesize(int filesize) {
+ this.filesize = filesize;
+ }
+
+ public int getIdColor() {
+ return idcolor;
+ }
+
+ public void setIdColor(int idcolor) {
+ this.idcolor = idcolor;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ // TODO: Dar formato a los mensajes de otra forma
+ this.message = message.replace("<span class=\"unkfunc\">", "<font color=#85c749><i>");
+ this.message = this.message.replace("</span>", "</i></font>");
+ if (this.message.contains("<hr")){
+ this.message = this.message.replace("<hr />", "<br><br><small><font color=#FF8800><i>") + "</i></small></font>";
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Board getParentBoard() {
+ return parentBoard;
+ }
+
+ public void setParentBoard(Board parentBoard) {
+ this.parentBoard = parentBoard;
+ }
+
+ public int getParentId() {
+ return parentid;
+ }
+
+ public void setParentId(int parentid) {
+ this.parentid = parentid;
+ }
+
+ public int getParentPostCount() {
+ return parentPostCount;
+ }
+
+ public void setParentPostCount(int parentPostCount) {
+ this.parentPostCount = parentPostCount;
+ }
+
+ public String getPosterId() {
+ return posterId;
+ }
+
+ public void setPosterId(String posterId) {
+ this.posterId = posterId;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getThumb() {
+ return thumb;
+ }
+
+ public void setThumb(String thumb) {
+ this.thumb = thumb;
+ }
+
+ public int getThumbHeight() {
+ return thumb_height;
+ }
+
+ public void setThumbHeight(int thumb_height) {
+ this.thumb_height = thumb_height;
+ }
+
+ public int getThumbWidth() {
+ return thumb_weight;
+ }
+
+ public void setThumbWidth(int thumb_weight) {
+ this.thumb_weight = thumb_weight;
+ }
+
+ public Bitmap getThumbBitmap() {
+ return thumbBitmap;
+ }
+
+ public void setThumbBitmap(Bitmap thumbBitmap) {
+ this.thumbBitmap = thumbBitmap;
+ }
+
+ public long getTimeStamp() {
+ return timestamp;
+ }
+
+ public void setTimeStamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getTimeStampFormatted() {
+ return timestamp_formatted;
+ }
+
+ public void setTimeStampFormatted(String timestamp_formatted) {
+ this.timestamp_formatted = timestamp_formatted;
+ }
+
+ public int getTotalFiles() {
+ return totalfiles;
+ }
+
+ public void setTotalFiles(int totalfiles) {
+ this.totalfiles = totalfiles;
+ }
+
+ public int getTotalReplies() {
+ return totalreplies;
+ }
+
+ public void setTotalReplies(int totalreplies) {
+ this.totalreplies = totalreplies;
+ }
+
+ public String getTripcode() {
+ return tripcode;
+ }
+
+ public void setTripcode(String tripcode) {
+ this.tripcode = tripcode;
+ }
+
+ public int getDeletedCode() {
+ return deleted_code;
+ }
+
+ public void setDeletedCode(int deleted_code) {
+ this.deleted_code = deleted_code;
+ if (deleted_code == 1){
+ this.message = "Eliminado por el usuario.";
+ }else if (deleted_code == 2){
+ this.message = "Eliminado por el Staff.";
+ }
+ }
+
+ public int getBbsId() {
+ return bbs_id;
+ }
+
+ public void setBbsId(int bbs_id) {
+ this.bbs_id = bbs_id;
+ }
+
+ public int realParentId(){
+ if (parentid == 0){
+ return id;
+ }
+ return parentid;
+ }
+
+ public BoardItem() {
+
+ }
+
+ public boolean isSage(){
+ return this.email.equals("sage");
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(name);
+ dest.writeString(timestamp_formatted);
+ dest.writeString(thumb);
+ dest.writeString(tripcode);
+ dest.writeString(email);
+ dest.writeString(file);
+ dest.writeString(message);
+ dest.writeString(subject);
+ dest.writeString(posterId);
+ dest.writeInt(parentid);
+ dest.writeInt(id);
+ dest.writeInt(idcolor);
+ dest.writeInt(totalreplies);
+ dest.writeInt(totalfiles);
+ dest.writeInt(thumb_height);
+ dest.writeInt(thumb_weight);
+ dest.writeInt(filesize);
+ dest.writeInt(deleted_code);
+ dest.writeInt(bbs_id);
+ dest.writeInt(parentPostCount);
+ dest.writeLong(timestamp);
+ dest.writeParcelable(thumbBitmap, flags);
+ dest.writeByte((byte) (downloadingThumb ? 1 : 0));
+ dest.writeByte((byte) (isReply ? 1 : 0));
+ dest.writeParcelable(parentBoard, flags);
+ }
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/structure/JsonType.java b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/JsonType.java
new file mode 100644
index 0000000..8db5c11
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/JsonType.java
@@ -0,0 +1,10 @@
+package org.bienvenidoainternet.baiparser.structure;
+
+/**
+ * Created by Renard on 17-03-2016.
+ */
+public enum JsonType {
+ BOARD_THREAD_LIST,
+ THREAD_REPLY_LIST,
+ BOARD_LIST;
+}
diff --git a/app/src/main/java/org/bienvenidoainternet/baiparser/structure/ReplyID.java b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/ReplyID.java
new file mode 100644
index 0000000..b6758fb
--- /dev/null
+++ b/app/src/main/java/org/bienvenidoainternet/baiparser/structure/ReplyID.java
@@ -0,0 +1,48 @@
+package org.bienvenidoainternet.baiparser.structure;
+
+import android.graphics.Color;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Random;
+
+/**
+ * Created by Renard on 18-03-2016.
+ */
+public class ReplyID implements Parcelable{
+ public String id;
+ public int color;
+ public ReplyID(String id){
+ this.id = id;
+ Random r = new Random();
+ this.color = Color.rgb(r.nextInt(125) + 127, r.nextInt(127) + 127, r.nextInt(127) + 127);
+ }
+
+ protected ReplyID(Parcel in) {
+ id = in.readString();
+ color = in.readInt();
+ }
+
+ public static final Creator<ReplyID> CREATOR = new Creator<ReplyID>() {
+ @Override
+ public ReplyID createFromParcel(Parcel in) {
+ return new ReplyID(in);
+ }
+
+ @Override
+ public ReplyID[] newArray(int size) {
+ return new ReplyID[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(id);
+ dest.writeInt(color);
+ }
+}
diff --git a/app/src/main/java/utils/ContentProviderUtils.java b/app/src/main/java/utils/ContentProviderUtils.java
new file mode 100644
index 0000000..fca8477
--- /dev/null
+++ b/app/src/main/java/utils/ContentProviderUtils.java
@@ -0,0 +1,148 @@
+package utils;
+
+import android.annotation.TargetApi;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+/**
+ * Created by Paul Burke
+ * http://stackoverflow.com/questions/20067508/get-real-path-from-uri-android-kitkat-new-storage-access-framework
+ */
+
+public class ContentProviderUtils {
+ /**
+ * Get a file path from a Uri. This will get the the path for Storage Access
+ * Framework Documents, as well as the _data field for the MediaStore and
+ * other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @author paulburke
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ public static String getPath(final Context context, final Uri uri) {
+
+ final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+ // DocumentProvider
+ if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+
+ // TODO handle non-primary volumes
+ }
+ // DownloadsProvider
+ else if (isDownloadsDocument(uri)) {
+
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ }
+ // MediaProvider
+ else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri = null;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[] {
+ split[1]
+ };
+
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+ }
+ // MediaStore (and general)
+ else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ return getDataColumn(context, uri, null, null);
+ }
+ // File
+ else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {
+ column
+ };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ final int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ return null;
+ }
+
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+}
diff --git a/app/src/main/res/drawable-hdpi/ic_action_download.png b/app/src/main/res/drawable-hdpi/ic_action_download.png
new file mode 100644
index 0000000..f08b335
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_action_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_font_bold.png b/app/src/main/res/drawable-hdpi/ic_action_font_bold.png
new file mode 100644
index 0000000..480e2fb
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_action_font_bold.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_font_italic.png b/app/src/main/res/drawable-hdpi/ic_action_font_italic.png
new file mode 100644
index 0000000..0405024
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_action_font_italic.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_edit.png b/app/src/main/res/drawable-hdpi/ic_edit.png
new file mode 100644
index 0000000..29046d9
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_edit.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_refresh.png b/app/src/main/res/drawable-hdpi/ic_refresh.png
new file mode 100644
index 0000000..455000e
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_refresh.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_sync.png b/app/src/main/res/drawable-hdpi/ic_sync.png
new file mode 100644
index 0000000..4840445
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_download.png b/app/src/main/res/drawable-mdpi/ic_action_download.png
new file mode 100644
index 0000000..76e1c9b
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_action_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_font_bold.png b/app/src/main/res/drawable-mdpi/ic_action_font_bold.png
new file mode 100644
index 0000000..66242fc
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_action_font_bold.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_font_italic.png b/app/src/main/res/drawable-mdpi/ic_action_font_italic.png
new file mode 100644
index 0000000..19868a5
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_action_font_italic.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_addreply.png b/app/src/main/res/drawable-mdpi/ic_addreply.png
new file mode 100644
index 0000000..cacba05
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_addreply.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_edit.png b/app/src/main/res/drawable-mdpi/ic_edit.png
new file mode 100644
index 0000000..559aac9
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_edit.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_refresh.png b/app/src/main/res/drawable-mdpi/ic_refresh.png
new file mode 100644
index 0000000..765ed43
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_refresh.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_sync.png b/app/src/main/res/drawable-mdpi/ic_sync.png
new file mode 100644
index 0000000..ecdd7f1
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_download.png b/app/src/main/res/drawable-xhdpi/ic_action_download.png
new file mode 100644
index 0000000..46501ca
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_action_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_font_bold.png b/app/src/main/res/drawable-xhdpi/ic_action_font_bold.png
new file mode 100644
index 0000000..bec0285
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_action_font_bold.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_font_italic.png b/app/src/main/res/drawable-xhdpi/ic_action_font_italic.png
new file mode 100644
index 0000000..1959c9f
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_action_font_italic.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_edit.png b/app/src/main/res/drawable-xhdpi/ic_edit.png
new file mode 100644
index 0000000..daa50e6
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_edit.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_refresh.png b/app/src/main/res/drawable-xhdpi/ic_refresh.png
new file mode 100644
index 0000000..58b116e
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_refresh.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_sync.png b/app/src/main/res/drawable-xhdpi/ic_sync.png
new file mode 100644
index 0000000..3b8bbbc
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_download.png b/app/src/main/res/drawable-xxhdpi/ic_action_download.png
new file mode 100644
index 0000000..80a2365
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_font_bold.png b/app/src/main/res/drawable-xxhdpi/ic_action_font_bold.png
new file mode 100644
index 0000000..c5428d6
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_font_bold.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_font_italic.png b/app/src/main/res/drawable-xxhdpi/ic_action_font_italic.png
new file mode 100644
index 0000000..8c04ef7
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_action_font_italic.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_edit.png b/app/src/main/res/drawable-xxhdpi/ic_edit.png
new file mode 100644
index 0000000..e36a9c2
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_edit.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_refresh.png b/app/src/main/res/drawable-xxhdpi/ic_refresh.png
new file mode 100644
index 0000000..98cdb9c
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_refresh.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_sync.png b/app/src/main/res/drawable-xxhdpi/ic_sync.png
new file mode 100644
index 0000000..08f7a61
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_download.png b/app/src/main/res/drawable-xxxhdpi/ic_action_download.png
new file mode 100644
index 0000000..a017260
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_download.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_font_bold.png b/app/src/main/res/drawable-xxxhdpi/ic_action_font_bold.png
new file mode 100644
index 0000000..c0da59e
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_font_bold.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_font_italic.png b/app/src/main/res/drawable-xxxhdpi/ic_action_font_italic.png
new file mode 100644
index 0000000..e756a29
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_action_font_italic.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_edit.png b/app/src/main/res/drawable-xxxhdpi/ic_edit.png
new file mode 100644
index 0000000..c7fa470
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_edit.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_refresh.png b/app/src/main/res/drawable-xxxhdpi/ic_refresh.png
new file mode 100644
index 0000000..02e21a8
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_refresh.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_sync.png b/app/src/main/res/drawable-xxxhdpi/ic_sync.png
new file mode 100644
index 0000000..0bf53c6
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_sync.png
Binary files differ
diff --git a/app/src/main/res/drawable/Thumbs.db b/app/src/main/res/drawable/Thumbs.db
new file mode 100644
index 0000000..07c6d5a
--- /dev/null
+++ b/app/src/main/res/drawable/Thumbs.db
Binary files differ
diff --git a/app/src/main/res/drawable/bai_banner.png b/app/src/main/res/drawable/bai_banner.png
new file mode 100644
index 0000000..d919b2f
--- /dev/null
+++ b/app/src/main/res/drawable/bai_banner.png
Binary files differ
diff --git a/app/src/main/res/drawable/bai_logo.png b/app/src/main/res/drawable/bai_logo.png
new file mode 100644
index 0000000..a1bf494
--- /dev/null
+++ b/app/src/main/res/drawable/bai_logo.png
Binary files differ
diff --git a/app/src/main/res/drawable/bai_mona.png b/app/src/main/res/drawable/bai_mona.png
new file mode 100644
index 0000000..b583466
--- /dev/null
+++ b/app/src/main/res/drawable/bai_mona.png
Binary files differ
diff --git a/app/src/main/res/drawable/blank.png b/app/src/main/res/drawable/blank.png
new file mode 100644
index 0000000..874fc75
--- /dev/null
+++ b/app/src/main/res/drawable/blank.png
Binary files differ
diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml
new file mode 100644
index 0000000..73fe2d2
--- /dev/null
+++ b/app/src/main/res/drawable/side_nav_bar.xml
@@ -0,0 +1,8 @@
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="90"
+ android:endColor="@color/colorPrimaryDark"
+ android:startColor="@color/colorPrimary"
+ android:type="linear" />
+</shape> \ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..a61d8a6
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:openDrawer="start">
+
+ <include
+ layout="@layout/app_bar_main"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <android.support.design.widget.NavigationView
+ android:id="@+id/nav_view"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:fitsSystemWindows="true"
+ app:headerLayout="@layout/nav_header_main"
+ app:menu="@menu/activity_main_drawer" />
+
+</android.support.v4.widget.DrawerLayout>
diff --git a/app/src/main/res/layout/activity_response.xml b/app/src/main/res/layout/activity_response.xml
new file mode 100644
index 0000000..19194d4
--- /dev/null
+++ b/app/src/main/res/layout/activity_response.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context="org.bienvenidoainternet.baiparser.ResponseActivity"
+ android:textAlignment="textEnd">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/layoutForm">
+ <EditText
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPersonName"
+ android:ems="10"
+ android:id="@+id/txtPosterName"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:hint="@string/txt_postername" />
+
+ <EditText
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textEmailAddress"
+ android:ems="10"
+ android:id="@+id/txtEmail"
+ android:layout_below="@+id/txtPosterName"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:hint="@string/txt_email" />
+
+ <EditText
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textMultiLine"
+ android:ems="10"
+ android:id="@+id/txtResponse"
+ android:hint="@string/txt_response"
+ android:layout_below="@+id/buttonBold"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:clickable="false"
+ android:capitalize="sentences" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/btn_send"
+ android:id="@+id/btnSend"
+ android:layout_alignWithParentIfMissing="false"
+ android:layout_below="@+id/txtResponse"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+
+ <Button
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:id="@+id/buttonBold"
+ android:background="@drawable/ic_action_font_bold"
+ android:layout_below="@+id/txtFilePath"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <Button
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:background="@drawable/ic_action_font_italic"
+ android:id="@+id/buttonItalic"
+ android:layout_alignTop="@+id/buttonBold"
+ android:layout_toRightOf="@+id/buttonBold"
+ android:layout_toEndOf="@+id/buttonBold" />
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/txtFilePath"
+ android:layout_below="@+id/txtEmail"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:enabled="false"
+ android:hint="Archivo "
+ android:layout_toLeftOf="@+id/btnSelectFiles"
+ android:layout_toStartOf="@+id/btnSelectFiles" />
+
+ <Button
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="..."
+ android:id="@+id/btnSelectFiles"
+ android:layout_below="@+id/txtEmail"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@+id/layoutForm"
+ android:layout_centerHorizontal="true"
+ android:id="@+id/layoutPostProcess">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="Diviendiendo por 0 ..."
+ android:id="@+id/txtPostingState"
+ android:layout_gravity="center_horizontal" />
+
+ <ProgressBar
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/barPosting"
+ android:layout_below="@+id/relativeLayout"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignRight="@+id/relativeLayout"
+ android:layout_alignEnd="@+id/relativeLayout"
+ android:layout_marginTop="3dp" />
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/activity_updater.xml b/app/src/main/res/layout/activity_updater.xml
new file mode 100644
index 0000000..b287293
--- /dev/null
+++ b/app/src/main/res/layout/activity_updater.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context="org.bienvenidoainternet.baiparser.UpdaterActivity">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Versión actual:"
+ android:id="@+id/txtCurrentVersion"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="Última versión:"
+ android:id="@+id/txtLastVersion"
+ android:layout_below="@+id/txtCurrentVersion"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Descargar última versión"
+ android:id="@+id/btnDownloadLastVersion"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true" />
+
+ <ProgressBar
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/barUpdateProgress"
+ android:layout_above="@+id/btnDownloadLastVersion"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text=""
+ android:id="@+id/txtChangelog"
+ android:layout_below="@+id/txtLastVersion"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_marginTop="5dp"
+ android:typeface="monospace"
+ android:layout_above="@+id/barUpdateProgress"
+ android:layout_marginBottom="5dp" />
+</RelativeLayout>
diff --git a/app/src/main/res/layout/activity_viewer.xml b/app/src/main/res/layout/activity_viewer.xml
new file mode 100644
index 0000000..b66e66b
--- /dev/null
+++ b/app/src/main/res/layout/activity_viewer.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="org.bienvenidoainternet.baiparser.ViewerActivity">
+
+ <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
+ android:id="@+id/imageView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ <pl.droidsonroids.gif.GifImageView
+ android:id="@+id/gifView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <ProgressBar
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/downloadProgressBar"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true" />
+</RelativeLayout>
diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml
new file mode 100644
index 0000000..1055aa2
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_main.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:context="org.bienvenidoainternet.baiparser.MainActivity">
+
+ <android.support.design.widget.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/AppTheme.AppBarOverlay">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:popupTheme="@style/AppTheme.PopupOverlay" />
+
+ </android.support.design.widget.AppBarLayout>
+
+ <include layout="@layout/content_main" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|end"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_edit"
+ android:alpha="128" />
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/boardthread_item.xml b/app/src/main/res/layout/boardthread_item.xml
new file mode 100644
index 0000000..e969e5e
--- /dev/null
+++ b/app/src/main/res/layout/boardthread_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="thread_title"
+ android:id="@+id/threadlist_thread_title"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginTop="10dp"
+ android:layout_marginLeft="10dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="reply_count"
+ android:id="@+id/threadlist_replycount"
+ android:layout_below="@+id/threadlist_thread_title"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="10dp"
+ android:layout_marginBottom="10dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="last_reply"
+ android:id="@+id/threadlist_lastreply"
+ android:layout_alignTop="@+id/threadlist_replycount"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_marginRight="10dp" />
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
new file mode 100644
index 0000000..28cc377
--- /dev/null
+++ b/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ tools:context="org.bienvenidoainternet.baiparser.MainActivity"
+ tools:showIn="@layout/app_bar_main">
+ <android.support.v4.view.ViewPager
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+
+</RelativeLayout>
+
+ <!--android:paddingBottom="@dimen/activity_vertical_margin"-->
+ <!--android:paddingLeft="@dimen/activity_horizontal_margin"-->
+ <!--android:paddingRight="@dimen/activity_horizontal_margin"-->
+ <!--android:paddingTop="@dimen/activity_vertical_margin"--> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_fragment_recent.xml b/app/src/main/res/layout/fragment_fragment_recent.xml
new file mode 100644
index 0000000..fa6e67c
--- /dev/null
+++ b/app/src/main/res/layout/fragment_fragment_recent.xml
@@ -0,0 +1,17 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="layout.fragmentRecent">
+
+ <!-- TODO: Update blank fragment layout -->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="New Text"
+ android:id="@+id/textView"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+</RelativeLayout>
diff --git a/app/src/main/res/layout/fragment_fragment_thread_list.xml b/app/src/main/res/layout/fragment_fragment_thread_list.xml
new file mode 100644
index 0000000..af88df4
--- /dev/null
+++ b/app/src/main/res/layout/fragment_fragment_thread_list.xml
@@ -0,0 +1,61 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/layoutThreadProcess"
+ android:visibility="gone">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="Error: Unknow"
+ android:id="@+id/txtThreadError"
+ android:layout_margin="3dp"
+ android:textColor="#ff0000"
+ android:visibility="visible"
+ android:textAlignment="center" />
+
+ </LinearLayout>
+
+ <RelativeLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_below="@+id/layoutThreadProcess"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true">
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="1"
+ android:indeterminate="true"
+ android:indeterminateBehavior="repeat"
+ android:indeterminateOnly="true"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp" />
+
+ <ListView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/lvThreadList"
+ android:divider="#FF00FF"
+ android:layout_weight="1"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+ </RelativeLayout>
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml
new file mode 100644
index 0000000..71f218d
--- /dev/null
+++ b/app/src/main/res/layout/nav_header_main.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/nav_header_height"
+ android:background="@drawable/side_nav_bar"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark">
+ <!--android:gravity="bottom"-->
+
+ <!--<ImageView-->
+ <!--android:layout_width="64dp"-->
+ <!--android:layout_height="64dp"-->
+ <!--android:paddingTop="@dimen/nav_header_vertical_spacing"-->
+ <!--android:src="@drawable/bai"-->
+ <!--android:id="@+id/imageView"-->
+ <!--android:layout_alignParentBottom="false"-->
+ <!--android:layout_centerHorizontal="true"-->
+ <!--android:layout_alignParentTop="true" />-->
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/bai_banner"
+ android:background="#00000000"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:adjustViewBounds="true" />
+</RelativeLayout>
diff --git a/app/src/main/res/layout/recentpost_item.xml b/app/src/main/res/layout/recentpost_item.xml
new file mode 100644
index 0000000..852b317
--- /dev/null
+++ b/app/src/main/res/layout/recentpost_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="rp_message"
+ android:id="@+id/rp_message"
+ android:layout_below="@+id/rp_title"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="5dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="rp_title"
+ android:id="@+id/rp_title"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="5dp"
+ android:layout_marginTop="5dp"
+ android:textSize="12sp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="rp_timediff"
+ android:id="@+id/rp_timediff"
+ android:textSize="12sp"
+ android:layout_below="@+id/rp_message"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_marginRight="5dp"
+ android:layout_marginBottom="3dp" />
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/thread_item.xml b/app/src/main/res/layout/thread_item.xml
new file mode 100644
index 0000000..a4396b4
--- /dev/null
+++ b/app/src/main/res/layout/thread_item.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:layout_width="5dp"
+ android:layout_height="match_parent"
+ android:id="@+id/ivMargin"
+ android:scaleType="fitXY"
+ android:src="@color/defaultMarginColor"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@+id/ivMargin"
+ android:layout_toEndOf="@+id/ivMargin">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="397 Nombre: VIPPEADOR : 11/03/16(vie)20:18:31"
+ android:id="@+id/lv_txtPoster"
+ android:textSize="10sp"
+ android:layout_below="@+id/lv_txtTitle"
+ android:layout_toRightOf="@+id/ivThumb"
+ android:layout_toEndOf="@+id/ivThumb"
+ android:layout_marginLeft="3dp"
+ android:layout_marginTop="3dp"
+ android:layout_marginRight="3dp" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="Cuerpo del post"
+ android:id="@+id/lv_txtBody"
+ android:layout_below="@+id/lv_txtFileInfo"
+ android:layout_toRightOf="@+id/ivThumb"
+ android:layout_toEndOf="@+id/ivThumb"
+ android:layout_marginLeft="3dp"
+ android:layout_marginTop="3dp"
+ android:layout_marginRight="3dp" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="Titulo"
+ android:id="@+id/lv_txtTitle"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@+id/ivThumb"
+ android:layout_toEndOf="@+id/ivThumb"
+ android:layout_marginLeft="3dp"
+ android:layout_marginTop="3dp"
+ android:layout_marginRight="3dp" />/>
+ <ImageView
+ android:layout_width="70dp"
+ android:layout_height="70dp"
+ android:id="@+id/ivThumb"
+ android:src="@drawable/blank"
+ android:layout_alignParentTop="true"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="123 Respuestas, 123 Imágenes"
+ android:id="@+id/lv_txtReplyCounter"
+ android:layout_below="@+id/lv_txtBody"
+ android:layout_toRightOf="@+id/ivThumb"
+ android:layout_toEndOf="@+id/ivThumb"
+ android:layout_marginLeft="3dp"
+ android:layout_marginTop="3dp"
+ android:layout_marginBottom="3dp"
+ android:layout_marginRight="3dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="New Text"
+ android:id="@+id/lv_txtFileInfo"
+ android:textSize="10sp"
+ android:layout_below="@+id/lv_txtPoster"
+ android:layout_toRightOf="@+id/ivThumb"
+ android:layout_toEndOf="@+id/ivThumb"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="3dp" />
+ </RelativeLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
new file mode 100644
index 0000000..5561ddb
--- /dev/null
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/nav_recent_post"
+ android:icon="@drawable/ic_sync"
+ android:title="Post recientes" />
+</menu>
diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml
new file mode 100644
index 0000000..280b121
--- /dev/null
+++ b/app/src/main/res/menu/main.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/action_to_top"
+ android:title="Ir al principio"
+ android:orderInCategory="100"
+ app:showAsAction="never"/>
+ <item
+ android:id="@+id/action_to_bot"
+ android:title="Ir al final"
+ android:orderInCategory="100"
+ app:showAsAction="never"/>
+ <item
+ android:id="@+id/action_refresh"
+ android:orderInCategory="300"
+ android:icon="@drawable/ic_refresh"
+ android:title="@string/action_settings"
+ app:showAsAction="always" />
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="400"
+ android:title="@string/action_settings"
+ app:showAsAction="never" />
+ <item
+ android:title="Buscar actualizaciones"
+ android:id="@+id/action_update"
+ android:orderInCategory="500"
+ app:showAsAction="never"/>
+ <item
+ android:id="@+id/action_exit"
+ android:orderInCategory="600"
+ android:title="@string/action_exit"
+ app:showAsAction="never" />
+</menu>
diff --git a/app/src/main/res/menu/menu_reply.xml b/app/src/main/res/menu/menu_reply.xml
new file mode 100644
index 0000000..d0dcaaa
--- /dev/null
+++ b/app/src/main/res/menu/menu_reply.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_reply"
+ android:title="@string/menu_reply"/>
+ <item android:id="@+id/menu_copy"
+ android:title="@string/menu_copy"/>
+ <item android:id="@+id/menu_savereply"
+ android:title="@string/menu_savereply"/>
+ <item android:id="@+id/menu_delpost"
+ android:title="@string/menu_delpost"/>
+ <item android:id="@+id/menu_delimage"
+ android:title="@string/menu_delimage"/>
+</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/menu_viewer.xml b/app/src/main/res/menu/menu_viewer.xml
new file mode 100644
index 0000000..31069eb
--- /dev/null
+++ b/app/src/main/res/menu/menu_viewer.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:tools="http://schemas.android.com/tools"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:title="Guardar"
+ android:id="@+id/menu_save_img"
+ android:orderInCategory="100"
+ android:icon="@drawable/ic_action_download"
+ app:showAsAction="always"/>
+</menu> \ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..0a0d553
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..6a879da
--- /dev/null
+++ b/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..da08cb9
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b55a4af
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..fa6a765
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..251fb9f
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,9 @@
+<resources>>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ </style>
+</resources>
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..ad73763
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="sageColor" format="reference"/>
+ <attr name="marginColor" format="reference"/>
+ <attr name="nameColor" format="reference"/>
+ <attr name="tripcodeColor" format="reference"/>
+ <attr name="isDarkTheme" format="boolean"/>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..5806545
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3B6B94</color>
+ <color name="colorPrimaryDark">#1B3345</color>
+ <color name="colorAccent">#3B6B94</color>
+ <color name="transparentBackground">#CC000000</color>
+
+ <color name="defaultSageColor">#0000FF</color>
+ <color name="defaultNameColor">#008000</color>
+ <color name="defaultMarginColor">#CCCCCC</color>
+ <color name="defaultTripcodeColor">#FF0000</color>
+
+ <color name="nightBackground">#2F3D48</color> <!-- 1 -->
+ <color name="nightMarginColor">#58636c</color> <!-- 3 58636c-->
+ <color name="nightTextColor">#979ea3</color> <!-- 6 -->
+ <color name="nightLinkColor">#c0c4c8</color> <!-- 8 -->
+ <color name="nightNameColor">#2e5f96</color>
+ <color name="nightSageColor">#5f962e</color>
+ <color name="nightTripcodeColor">#962e5f</color>
+
+ <color name="headlineBackground">#DDDDDD</color> <!-- 1 -->
+ <color name="headlineMarginColor">#EEEEEE</color> <!-- 3 58636c-->
+ <color name="headlineTextColor">#333333</color> <!-- 6 -->
+ <color name="headlineLinkColor">#FF6600</color> <!-- 8 -->
+ <color name="headlineNameColor">#004A99</color>
+ <color name="headlineSageColor">#FF6600</color>
+ <color name="headlineTripcodeColor">#d279ef</color>
+
+ <color name="blackBackground">#282A2E</color> <!-- 1 -->
+ <color name="blackMarginColor">#1D1F21</color> <!-- 3 58636c-->
+ <color name="blackTextColor">#c5c8c6</color> <!-- 6 -->
+ <color name="blackLinkColor">#81a2be</color> <!-- 8 -->
+ <color name="blackNameColor">#c5c8c6</color>
+ <color name="blackSageColor">#81a2be</color>
+ <color name="blackTripcodeColor">#b294bb</color>
+</resources>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..c2effc5
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,9 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="nav_header_vertical_spacing">16dp</dimen>
+ <dimen name="nav_header_height">160dp</dimen>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
diff --git a/app/src/main/res/values/drawables.xml b/app/src/main/res/values/drawables.xml
new file mode 100644
index 0000000..52c6a6c
--- /dev/null
+++ b/app/src/main/res/values/drawables.xml
@@ -0,0 +1,8 @@
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <item name="ic_menu_camera" type="drawable">@android:drawable/ic_menu_camera</item>
+ <item name="ic_menu_gallery" type="drawable">@android:drawable/ic_menu_gallery</item>
+ <item name="ic_menu_slideshow" type="drawable">@android:drawable/ic_menu_slideshow</item>
+ <item name="ic_menu_manage" type="drawable">@android:drawable/ic_menu_manage</item>
+ <item name="ic_menu_share" type="drawable">@android:drawable/ic_menu_share</item>
+ <item name="ic_menu_send" type="drawable">@android:drawable/ic_menu_send</item>
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7fa3e34
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,76 @@
+<resources>
+ <string name="app_name">Bienvenido a Internet</string>
+
+ <string name="navigation_drawer_open">Open navigation drawer</string>
+ <string name="navigation_drawer_close">Close navigation drawer</string>
+
+ <string name="action_settings">Opciones</string>
+ <string name="action_exit">Salir</string>
+ <string name="action_threadlist">Lista de Hilos</string>
+
+ <string name="txt_postername">Nombre</string>
+ <string name="txt_email">E-mail</string>
+ <string name="txt_response">Respuesta</string>
+ <string name="btn_send">Responder</string>
+
+ <string name="menu_reply">Citar</string>
+ <string name="menu_copy">Copiar</string>
+ <string name="menu_savereply">Guardar</string>
+ <string name="menu_delpost">Eliminar respuesta</string>
+ <string name="menu_delimage">Eliminar imagen</string>
+
+
+ <string name="todo_pasando">Todo pasando en B.a.I</string>
+
+ <string-array name="pref_themes">
+ <item>psud0ch</item>
+ <item>Night</item>
+ <item>Photon</item>
+ <item>Tomorrow</item>
+ </string-array>
+ <string-array name="pref_themes_values">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>4</item>
+ </string-array>
+ <string-array name="pref_repliescount">
+ <item>Todas las respuestas</item>
+ <item>500 ultimas respuestas</item>
+ <item>250 ultimas respuestas</item>
+ <item>100 ultimas respuestas</item>
+ <item>50 ultimas respuestas</item>
+ </string-array>
+ <string-array name="pref_repliescount_values">
+ <item>1000</item>
+ <item>500</item>
+ <item>250</item>
+ <item>100</item>
+ <item>50</item>
+ </string-array>
+ <string-array name="pref_catalog_replies">
+ <item>Mostrar solo el post original</item>
+ <item>5 respuestas</item>
+ <item>10 respuestas</item>
+ </string-array>
+ <string-array name="pref_catalog_replies_values">
+ <item>0</item>
+ <item>5</item>
+ <item>10</item>
+ </string-array>
+ <string-array name="pref_lastreplies_desc">
+ <item>50 items</item>
+ <item>30 items</item>
+ <item>10 items</item>
+ <item>5 items</item>
+ </string-array>
+ <string-array name="pref_lastreplies_values">
+ <item>50</item>
+ <item>30</item>
+ <item>10</item>
+ <item>5</item>
+ </string-array>
+
+ <string name="title_activity_settings">Opciones</string>
+
+</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5cadb6e
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,88 @@
+<resources>
+ <style name="TransparentCompat" parent="@style/Theme.AppCompat.Light.DarkActionBar">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowBackground">@color/transparentBackground</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation</item>
+ </style>
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ <item name="android:divider">@color/defaultMarginColor</item>
+ <item name="sageColor">@color/defaultSageColor</item>
+ <item name="nameColor">@color/defaultNameColor</item>
+ <item name="marginColor">@color/defaultMarginColor</item>
+ <item name="tripcodeColor">@color/defaultTripcodeColor</item>
+ <item name="isDarkTheme">false</item>
+ </style>
+
+ <style name="AppTheme.Dark" parent="AppTheme.NoActionBar">
+ <item name="android:textColor">@color/nightTextColor</item>
+ <item name="android:background">@color/nightBackground</item>
+ <item name="android:divider">@color/nightMarginColor</item>
+ <item name="android:textColorLink">@color/nightLinkColor</item>
+ <item name="sageColor">@color/nightSageColor</item>
+ <item name="nameColor">@color/nightNameColor</item>
+ <item name="marginColor">@color/nightMarginColor</item>
+ <item name="tripcodeColor">@color/nightTripcodeColor</item>
+ <item name="isDarkTheme">true</item>
+ </style>
+
+ <style name="AppTheme.HeadLine.Activity" parent="AppTheme.NoActionBar">
+ <item name="colorPrimary">@color/headlineTextColor</item>
+ <item name="colorPrimaryDark">@color/blackBackground</item>
+ <item name="colorAccent">@color/headlineLinkColor</item>
+ <item name="isDarkTheme">true</item>
+ </style>
+
+ <style name="AppTheme.HeadLine" parent="AppTheme.NoActionBar">
+ <item name="android:textColor">@color/headlineTextColor</item>
+ <item name="android:background">@color/headlineBackground</item>
+ <item name="android:divider">@color/headlineMarginColor</item>
+ <item name="android:textColorLink">@color/headlineLinkColor</item>
+ <item name="sageColor">@color/headlineSageColor</item>
+ <item name="nameColor">@color/headlineNameColor</item>
+ <item name="marginColor">@color/headlineMarginColor</item>
+ <item name="tripcodeColor">@color/headlineTripcodeColor</item>
+ <item name="colorPrimary">@color/headlineTextColor</item>
+ <item name="colorPrimaryDark">@color/headlineLinkColor</item>
+ <item name="colorAccent">@color/headlineLinkColor</item>
+ <item name="isDarkTheme">false</item>
+ </style>
+
+ <style name="AppTheme.Black.Activity" parent="AppTheme.NoActionBar">
+ <item name="colorPrimary">@color/blackBackground</item>
+ <item name="colorPrimaryDark">@color/blackMarginColor</item>
+ <item name="colorAccent">@color/blackMarginColor</item>
+ <item name="isDarkTheme">true</item>
+ </style>
+
+ <style name="AppTheme.Black" parent="AppTheme.NoActionBar">
+ <item name="android:textColor">@color/blackTextColor</item>
+ <item name="android:background">@color/blackBackground</item>
+ <item name="android:divider">@color/blackMarginColor</item>
+ <item name="android:textColorLink">@color/blackLinkColor</item>
+ <item name="sageColor">@color/blackSageColor</item>
+ <item name="nameColor">@color/blackNameColor</item>
+ <item name="marginColor">@color/blackMarginColor</item>
+ <item name="tripcodeColor">@color/blackTripcodeColor</item>
+ <item name="colorPrimary">@color/blackBackground</item>
+ <item name="colorPrimaryDark">@color/blackMarginColor</item>
+ <item name="colorAccent">@color/blackSageColor</item>
+ <item name="isDarkTheme">true</item>
+ </style>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+
+</resources>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..7f6d5b9
--- /dev/null
+++ b/app/src/main/res/xml/preferences.xml
@@ -0,0 +1,61 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <PreferenceCategory
+ android:title="Apariencia">
+ <ListPreference
+ android:title="Tema"
+ android:summary="Requiere un reinicio de la aplicación"
+ android:key="pref_theme"
+ android:defaultValue="1"
+ android:entries="@array/pref_themes"
+ android:entryValues="@array/pref_themes_values" />
+ <SwitchPreference
+ android:title="Usar fuente Mona"
+ android:key="setting_monafont"
+ android:summary="No es compatible con todas las versiones de Android"
+ android:defaultValue="true" />
+ <SwitchPreference
+ android:title="Usar Mona.tff solo en BSS"
+ android:key="setting_mona_bbsonly"
+ android:defaultValue="true"/>
+ </PreferenceCategory>
+ <PreferenceCategory
+ android:title="Comportamiento">
+ <SwitchPreference
+ android:title="Descargar imágenes solo con Wi-Fi"
+ android:key="setting_downloadOnlyWithWifi"
+ android:defaultValue="true" />
+ <SwitchPreference
+ android:title="Ir al final al abrir un hilo"
+ android:key="setting_scrollatnewthread"
+ android:defaultValue="false" />
+ <ListPreference
+ android:title="Respuestas por tema"
+ android:key="pref_lastreplies"
+ android:summary="Cantidad de respuestas que serán cargadas en la vista de tema"
+ android:defaultValue="1000"
+ android:entries="@array/pref_repliescount"
+ android:entryValues="@array/pref_repliescount_values"/>
+ <ListPreference
+ android:title="Respuestas en catálogo"
+ android:key="pref_repliesperthread"
+ android:summary="Cantidad de respuestas que se muestran en el catálogo"
+ android:defaultValue="5"
+ android:entries="@array/pref_catalog_replies"
+ android:entryValues="@array/pref_catalog_replies_values"/>
+ <ListPreference
+ android:title="Post recientes"
+ android:summary="Cantidad de post a cargar"
+ android:key="pref_lastreplies_limit"
+ android:entries="@array/pref_lastreplies_desc"
+ android:entryValues="@array/pref_lastreplies_values"
+ android:defaultValue="30"/>
+ </PreferenceCategory>
+ <PreferenceCategory
+ android:title="Formulario de respuesta">
+ <EditTextPreference
+ android:title="Contraseña"
+ android:summary="Usada para la eliminación de tus posts"
+ android:key="pref_password" />
+
+ </PreferenceCategory>
+</PreferenceScreen> \ No newline at end of file
diff --git a/app/src/test/java/org/bienvenidoainternet/baiparser/ExampleUnitTest.java b/app/src/test/java/org/bienvenidoainternet/baiparser/ExampleUnitTest.java
new file mode 100644
index 0000000..7c67f02
--- /dev/null
+++ b/app/src/test/java/org/bienvenidoainternet/baiparser/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package org.bienvenidoainternet.baiparser;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+} \ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..e0b366a
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.5.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true \ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..05ef575
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..f23df6e
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Oct 21 11:34:03 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app'