aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore60
-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.xml18
-rw-r--r--.idea/markdown-navigator.xml71
-rw-r--r--.idea/markdown-navigator/profiles_settings.xml3
-rw-r--r--.idea/misc.xml78
-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.gradle76
-rw-r--r--app/proguard-rules.pro25
-rw-r--r--app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java26
-rw-r--r--app/src/main/AndroidManifest.xml95
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt19
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt127
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt17
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FBURL.kt15
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt75
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Changelog.kt98
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/FragmentUtils.kt15
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Kotterknife.kt137
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt12
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt30
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt11
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt138
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshBase.kt31
-rw-r--r--app/src/main/res/drawable-nodpi/splash_logo.9.pngbin0 -> 13202 bytes
-rw-r--r--app/src/main/res/drawable/splash_screen.xml13
-rw-r--r--app/src/main/res/layout/activity_main.xml36
-rw-r--r--app/src/main/res/layout/changelog_content.xml27
-rw-r--r--app/src/main/res/layout/changelog_title.xml14
-rw-r--r--app/src/main/res/layout/fragment_main.xml12
-rw-r--r--app/src/main/res/layout/swipe_webview.xml15
-rw-r--r--app/src/main/res/menu/menu_main.xml15
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 2734 bytes
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_launcher_round.pngbin0 -> 4208 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 3454 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_launcher_round.pngbin0 -> 6114 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 5279 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngbin0 -> 10056 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 6850 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngbin0 -> 14696 bytes
-rw-r--r--app/src/main/res/values/colors.xml9
-rw-r--r--app/src/main/res/values/dimens.xml12
-rw-r--r--app/src/main/res/values/strings.xml11
-rw-r--r--app/src/main/res/values/styles.xml23
-rw-r--r--app/src/main/res/xml/changelog.xml11
-rw-r--r--app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java17
-rw-r--r--build.gradle45
-rw-r--r--docs/Changelog.md4
-rw-r--r--gradle.properties36
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 53636 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--gradlew160
-rw-r--r--gradlew.bat90
-rw-r--r--settings.gradle1
59 files changed, 1740 insertions, 53 deletions
diff --git a/.gitignore b/.gitignore
index 520a8635..39fb081a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,55 +1,9 @@
-# Built application files
-*.apk
-*.ap_
-
-# Files for the ART/Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-out/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-# Android Studio captures folder
-captures/
-
-# Intellij
*.iml
-.idea/workspace.xml
-.idea/tasks.xml
-.idea/gradle.xml
-.idea/dictionaries
-.idea/libraries
-
-# Keystore files
-*.jks
-
-# External native build folder generated in Android Studio 2.2 and later
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
.externalNativeBuild
-
-# Google Services (e.g. APIs or Firebase)
-google-services.json
-
-# Freeline
-freeline.py
-freeline/
-freeline_project_description.json
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 00000000..96cc43ef
--- /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 00000000..e7bedf33
--- /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 00000000..97626ba4
--- /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 00000000..7ac24c77
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="distributionType" value="DEFAULT_WRAPPED" />
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/app" />
+ </set>
+ </option>
+ <option name="resolveModulePerSourceSet" value="false" />
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml
new file mode 100644
index 00000000..838ef398
--- /dev/null
+++ b/.idea/markdown-navigator.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="MarkdownProjectSettings">
+ <PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.25" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true">
+ <PanelProvider>
+ <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
+ </PanelProvider>
+ </PreviewSettings>
+ <ParserSettings gitHubSyntaxChange="false">
+ <PegdownExtensions>
+ <option name="ABBREVIATIONS" value="false" />
+ <option name="ANCHORLINKS" value="true" />
+ <option name="ASIDE" value="false" />
+ <option name="ATXHEADERSPACE" value="true" />
+ <option name="AUTOLINKS" value="true" />
+ <option name="DEFINITIONS" value="false" />
+ <option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
+ <option name="FENCED_CODE_BLOCKS" value="true" />
+ <option name="FOOTNOTES" value="false" />
+ <option name="HARDWRAPS" value="false" />
+ <option name="INSERTED" value="false" />
+ <option name="QUOTES" value="false" />
+ <option name="RELAXEDHRULES" value="true" />
+ <option name="SMARTS" value="false" />
+ <option name="STRIKETHROUGH" value="true" />
+ <option name="SUBSCRIPT" value="false" />
+ <option name="SUPERSCRIPT" value="false" />
+ <option name="SUPPRESS_HTML_BLOCKS" value="false" />
+ <option name="SUPPRESS_INLINE_HTML" value="false" />
+ <option name="TABLES" value="true" />
+ <option name="TASKLISTITEMS" value="true" />
+ <option name="TOC" value="false" />
+ <option name="WIKILINKS" value="true" />
+ </PegdownExtensions>
+ <ParserOptions>
+ <option name="COMMONMARK_LISTS" value="false" />
+ <option name="DUMMY" value="false" />
+ <option name="EMOJI_SHORTCUTS" value="true" />
+ <option name="FLEXMARK_FRONT_MATTER" value="false" />
+ <option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
+ <option name="GFM_TABLE_RENDERING" value="true" />
+ <option name="GITBOOK_URL_ENCODING" value="false" />
+ <option name="GITHUB_EMOJI_URL" value="false" />
+ <option name="GITHUB_LISTS" value="true" />
+ <option name="GITHUB_WIKI_LINKS" value="true" />
+ <option name="JEKYLL_FRONT_MATTER" value="false" />
+ <option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
+ </ParserOptions>
+ </ParserSettings>
+ <HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
+ <GeneratorProvider>
+ <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
+ </GeneratorProvider>
+ <headerTop />
+ <headerBottom />
+ <bodyTop />
+ <bodyBottom />
+ </HtmlSettings>
+ <CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
+ <StylesheetProvider>
+ <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
+ </StylesheetProvider>
+ <ScriptProviders />
+ <cssText />
+ </CssSettings>
+ <HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
+ <LinkMapSettings>
+ <textMaps />
+ </LinkMapSettings>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml
new file mode 100644
index 00000000..57927c5a
--- /dev/null
+++ b/.idea/markdown-navigator/profiles_settings.xml
@@ -0,0 +1,3 @@
+<component name="MarkdownNavigator.ProfileManager">
+ <settings default="" pdf-export="" />
+</component> \ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..085136f8
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,78 @@
+<?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="ProjectInspectionProfilesVisibleTreeState">
+ <entry key="Project Default">
+ <profile-state>
+ <expanded-state>
+ <State>
+ <id />
+ </State>
+ </expanded-state>
+ <selected-state>
+ <State>
+ <id>Android</id>
+ </State>
+ </selected-state>
+ </profile-state>
+ </entry>
+ </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>
+ <component name="masterDetails">
+ <states>
+ <state key="ProjectJDKs.UI">
+ <settings>
+ <last-edited>1.8</last-edited>
+ <splitter-proportions>
+ <option name="proportions">
+ <list>
+ <option value="0.2" />
+ </list>
+ </option>
+ </splitter-proportions>
+ </settings>
+ </state>
+ </states>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..7451d719
--- /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$/Frost-for-Facebook.iml" filepath="$PROJECT_DIR$/Frost-for-Facebook.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 00000000..7f68460d
--- /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 00000000..94a25f7f
--- /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 00000000..796b96d1
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 00000000..ab9c2770
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,76 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'realm-android'
+
+android {
+ compileSdkVersion Integer.parseInt(project.TARGET_SDK)
+ buildToolsVersion project.BUILD_TOOLS
+
+ defaultConfig {
+ applicationId "${project.APP_GROUP}." + project.APP_ID.toLowerCase() + ".sample"
+ minSdkVersion Integer.parseInt(project.MIN_SDK)
+ targetSdkVersion Integer.parseInt(project.TARGET_SDK)
+ versionCode Integer.parseInt(project.VERSION_CODE)
+ versionName project.VERSION_NAME
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.each { output ->
+ output.outputFile = new File(output.outputFile.parent,
+ "${project.APP_ID}-v${variant.versionName}.apk")
+ }
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ testCompile 'junit:junit:4.12'
+
+ compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+
+ compile "com.android.support:appcompat-v7:${ANDROID_SUPPORT_LIBS}"
+ compile "com.android.support:support-v4:${ANDROID_SUPPORT_LIBS}"
+ compile "com.android.support:support-v13:${ANDROID_SUPPORT_LIBS}"
+ compile "com.android.support:design:${ANDROID_SUPPORT_LIBS}"
+ compile "com.android.support:recyclerview-v7:${ANDROID_SUPPORT_LIBS}"
+ compile "com.android.support:cardview-v7:${ANDROID_SUPPORT_LIBS}"
+ compile "com.android.support:preference-v14:${ANDROID_SUPPORT_LIBS}"
+
+ //Logging
+ compile "com.jakewharton.timber:timber:${TIMBER}"
+
+ //Dialog
+ compile "com.afollestad.material-dialogs:core:${MD}"
+
+ //Icons
+ compile "com.mikepenz:iconics-core:${ICONICS}@aar"
+ compile "com.mikepenz:google-material-typeface:${GMD}.original@aar"
+
+ //Butterknife
+ compile "com.jakewharton:butterknife:${BUTTERKNIFE}"
+ annotationProcessor "com.jakewharton:butterknife-compiler:${BUTTERKNIFE}"
+
+ compile "io.reactivex.rxjava2:rxjava:${RX_JAVA}"
+ compile "io.reactivex.rxjava2:rxandroid:${RX_ANDROID}"
+ compile "com.jakewharton.rxbinding2:rxbinding:${RX_BINDING}"
+ compile "com.jakewharton.rxbinding2:rxbinding-appcompat-v7:${RX_BINDING}"
+
+ compile "com.facebook.stetho:stetho-okhttp3:${STETHO}"
+
+ compile "com.github.bumptech.glide:glide:${GLIDE}"
+ annotationProcessor "com.github.bumptech.glide:compiler:${GLIDE}"
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 00000000..2b766bc4
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Users\User7681\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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..9e27deaf
--- /dev/null
+++ b/app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.pitchedapps.frost;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.pitchedapps.frost", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..74428503
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.pitchedapps.frost">
+
+ <!-- To auto-complete the email text field in the login form with the user's emails -->
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.READ_PROFILE" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.USE_FINGERPRINT" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".StartActivity"
+ android:configChanges="orientation|screenSize|locale"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.Splash">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".MainActivity"
+ android:configChanges="orientation|screenSize|locale"
+ android:hardwareAccelerated="true"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data
+ android:host="m.facebook.com"
+ android:scheme="http" />
+ <data
+ android:host="m.facebook.com"
+ android:scheme="https" />
+ <data
+ android:host="mobile.facebook.com"
+ android:scheme="http" />
+ <data
+ android:host="mobile.facebook.com"
+ android:scheme="https" />
+ <data
+ android:host="touch.facebook.com"
+ android:scheme="http" />
+ <data
+ android:host="touch.facebook.com"
+ android:scheme="https" />
+ <data
+ android:host="fb.com"
+ android:scheme="http" />
+ <data
+ android:host="fb.com"
+ android:scheme="https" />
+ <data
+ android:host="fb.me"
+ android:scheme="http" />
+ <data
+ android:host="fb.me"
+ android:scheme="https" />
+ <data
+ android:host="facebook.com"
+ android:scheme="http" />
+ <data
+ android:host="facebook.com"
+ android:scheme="https" />
+ <data
+ android:host="www.facebook.com"
+ android:scheme="http" />
+ <data
+ android:host="www.facebook.com"
+ android:scheme="https" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
new file mode 100644
index 00000000..4e287ee7
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
@@ -0,0 +1,19 @@
+package com.pitchedapps.frost
+
+import android.app.Application
+import com.pitchedapps.frost.utils.Prefs
+
+/**
+ * Created by Allan Wang on 2017-05-28.
+ */
+class FrostApp : Application() {
+
+ companion object {
+ lateinit var prefs: Prefs
+ }
+
+ override fun onCreate() {
+ prefs = Prefs(applicationContext)
+ super.onCreate()
+ }
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt
new file mode 100644
index 00000000..4922a292
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt
@@ -0,0 +1,127 @@
+package com.pitchedapps.frost
+
+import android.os.Bundle
+import android.support.design.widget.FloatingActionButton
+import android.support.design.widget.Snackbar
+import android.support.design.widget.TabLayout
+import android.support.v4.app.Fragment
+import android.support.v4.app.FragmentManager
+import android.support.v4.app.FragmentPagerAdapter
+import android.support.v4.view.ViewPager
+import android.support.v7.app.AppCompatActivity
+import android.support.v7.widget.Toolbar
+import android.view.*
+import android.widget.TextView
+import butterknife.ButterKnife
+import com.pitchedapps.frost.fragments.WebFragment
+import com.pitchedapps.frost.utils.Changelog
+import com.pitchedapps.frost.utils.bindView
+
+class MainActivity : AppCompatActivity() {
+
+ private var mSectionsPagerAdapter: SectionsPagerAdapter? = null
+ val toolbar: Toolbar by bindView(R.id.toolbar)
+ val viewPager: ViewPager by bindView(R.id.container)
+ val fab: FloatingActionButton by bindView(R.id.fab)
+ val tabs: TabLayout by bindView(R.id.tabs)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ ButterKnife.bind(this)
+ setSupportActionBar(toolbar)
+ // Create the adapter that will return a fragment for each of the three
+ // primary sections of the activity.
+ mSectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager)
+
+ // Set up the ViewPager with the sections adapter.
+ viewPager.adapter = mSectionsPagerAdapter
+ viewPager.offscreenPageLimit = 5
+ tabs.setupWithViewPager(viewPager)
+
+ fab.setOnClickListener { view ->
+ Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
+ .setAction("Action", null).show()
+ }
+
+ }
+
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ when (item.itemId) {
+ R.id.action_settings -> return true
+ R.id.action_changelog -> Changelog.show(this)
+ else -> return super.onOptionsItemSelected(item)
+ }
+ return true
+ }
+
+ /**
+ * A placeholder fragment containing a simple view.
+ */
+ class PlaceholderFragment : Fragment() {
+
+ override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val rootView = inflater!!.inflate(R.layout.fragment_main, container, false)
+ (rootView.findViewById(R.id.section_label) as TextView).text = getString(R.string.section_format, arguments.getInt(ARG_SECTION_NUMBER))
+ return rootView
+ }
+
+ companion object {
+ /**
+ * The fragment argument representing the section number for this
+ * fragment.
+ */
+ private val ARG_SECTION_NUMBER = "section_number"
+
+ /**
+ * Returns a new instance of this fragment for the given section
+ * number.
+ */
+ fun newInstance(sectionNumber: Int): PlaceholderFragment {
+ val fragment = PlaceholderFragment()
+ val args = Bundle()
+ args.putInt(ARG_SECTION_NUMBER, sectionNumber)
+ fragment.arguments = args
+ return fragment
+ }
+ }
+ }
+
+ /**
+ * A [FragmentPagerAdapter] that returns a fragment corresponding to
+ * one of the sections/tabs/pages.
+ */
+ inner class SectionsPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
+
+ override fun getItem(position: Int): Fragment {
+ // getItem is called to instantiate the fragment for the given page.
+ // Return a PlaceholderFragment (defined as a static inner class below).
+ return WebFragment.newInstance()
+ }
+
+ override fun getCount(): Int {
+ // Show 3 total pages.
+ return 3
+ }
+
+ override fun getPageTitle(position: Int): CharSequence? {
+ when (position) {
+ 0 -> return "SECTION 1"
+ 1 -> return "SECTION 2"
+ 2 -> return "SECTION 3"
+ }
+ return null
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
new file mode 100644
index 00000000..e8c65be3
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
@@ -0,0 +1,17 @@
+package com.pitchedapps.frost
+
+import android.content.Intent
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+
+/**
+ * Created by Allan Wang on 2017-05-28.
+ */
+class StartActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ startActivity(Intent(this, MainActivity::class.java))
+ finish()
+ }
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FBURL.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FBURL.kt
new file mode 100644
index 00000000..489f12a7
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FBURL.kt
@@ -0,0 +1,15 @@
+package com.pitchedapps.frost.facebook
+
+/**
+ * Created by Allan Wang on 2017-05-29.
+ */
+enum class FBURL(val url: String) {
+ FEED("https://touch.facebook.com/"),
+ PROFILE("https://touch.facebook.com/me/"),
+ BOOKMARKS("https://touch.facebook.com/bookmarks"),
+ SEARCH("https://touch.facebook.com/search"),
+ EVENTS("https://touch.facebook.com/events/upcoming"),
+ FRIEND_REQUESTS("https://touch.facebook.com/requests"),
+ MESSAGES("https://touch.facebook.com/messages"),
+ NOTIFICATIONS("https://touch.facebook.com/notifications");
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt
new file mode 100644
index 00000000..140d8df1
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt
@@ -0,0 +1,75 @@
+package com.pitchedapps.frost.fragments
+
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.support.v4.widget.SwipeRefreshLayout
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import butterknife.ButterKnife
+import butterknife.Unbinder
+import com.pitchedapps.frost.R
+import com.pitchedapps.frost.facebook.FBURL
+import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.utils.bindView
+import com.pitchedapps.frost.utils.withBundle
+import com.pitchedapps.frost.views.FrostWebView
+import com.pitchedapps.frost.views.SwipeRefreshBase
+import com.pitchedapps.frost.views.WebStatus
+
+/**
+ * Created by Allan Wang on 2017-05-29.
+ */
+
+
+class WebFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
+
+ override fun onRefresh() {
+ web.reload()
+ }
+
+ companion object {
+ private val ARG_URL = "arg_url"
+ fun newInstance(url: String) = WebFragment().withBundle { b -> b.putString(ARG_URL, url) }
+ fun newInstance(url: FBURL = FBURL.FEED) = newInstance(url.url)
+ }
+
+ val refresh: SwipeRefreshBase by bindView(R.id.swipe_refresh)
+ val web: FrostWebView by bindView(R.id.frost_webview)
+ lateinit var url: String
+ private lateinit var unbinder: Unbinder
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ url = arguments.getString(ARG_URL)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ super.onCreateView(inflater, container, savedInstanceState)
+ val view = inflater.inflate(R.layout.swipe_webview, container, false)
+ unbinder = ButterKnife.bind(this, view)
+ return view
+ }
+
+ override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ web.observable.subscribe {
+ t: WebStatus ->
+ when (t) {
+ WebStatus.LOADED, WebStatus.ERROR -> refresh.isRefreshing = false
+ WebStatus.LOADING -> refresh.isRefreshing = true
+ }
+ }
+ refresh.setOnRefreshListener(this)
+ refresh.shouldSwipe = {
+ L.e("Y ${web.scrollY}")
+ SwipeRefreshBase.shouldScroll(web)
+ }
+ web.loadUrl(url)
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ unbinder.unbind()
+ }
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Changelog.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Changelog.kt
new file mode 100644
index 00000000..14a095a1
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Changelog.kt
@@ -0,0 +1,98 @@
+package com.pitchedapps.frost.utils
+
+import android.content.Context
+import android.content.res.XmlResourceParser
+import android.os.Handler
+import android.support.annotation.LayoutRes
+import android.support.annotation.NonNull
+import android.support.annotation.XmlRes
+import android.support.v4.app.FragmentActivity
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.afollestad.materialdialogs.MaterialDialog
+import com.pitchedapps.frost.R
+import org.xmlpull.v1.XmlPullParser
+import java.util.*
+
+
+/**
+ * Created by Allan Wang on 2017-05-28.
+ */
+class Changelog {
+ companion object {
+ fun show(@NonNull activity: FragmentActivity, @XmlRes xmlRes: Int = R.xml.changelog) {
+ val mHandler = Handler()
+ Thread(Runnable {
+ val items = parse(activity, xmlRes)
+ mHandler.post(object : TimerTask() {
+ override fun run() {
+ MaterialDialog.Builder(activity)
+ .title(R.string.changelog)
+ .positiveText(R.string.great)
+ .adapter(ChangelogAdapter(items), null)
+ .show()
+ }
+ })
+ }).start()
+ }
+ }
+}
+
+private class ChangelogAdapter(val items: List<Pair<String, ChangelogType>>) : RecyclerView.Adapter<ChangelogAdapter.ChangelogVH>() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ChangelogVH(LayoutInflater.from(parent.context)
+ .inflate(getLayout(viewType), parent, false))
+
+ private fun getLayout(position: Int) = items[position].second.layout
+
+ override fun onBindViewHolder(holder: ChangelogVH, position: Int) {
+ holder.text.text = items[position].first
+ }
+
+ override fun getItemId(position: Int) = position.toLong()
+
+ override fun getItemViewType(position: Int) = position
+
+ override fun getItemCount() = items.size
+
+ internal class ChangelogVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val text: TextView = itemView.findViewById(R.id.changelog_text) as TextView
+ }
+}
+
+private fun parse(context: Context, @XmlRes xmlRes: Int): List<Pair<String, ChangelogType>> {
+ val items = mutableListOf<Pair<String, ChangelogType>>()
+ context.resources.getXml(xmlRes).use {
+ parser ->
+ var eventType = parser.eventType
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG)
+ ChangelogType.values.any { it.add(parser, items) }
+ eventType = parser.next()
+ }
+ }
+ return items
+}
+
+private enum class ChangelogType(val tag: String, val attr: String, @LayoutRes val layout: Int) {
+ TITLE("title", "version", R.layout.changelog_title),
+ ITEM("item", "text", R.layout.changelog_content);
+
+ companion object {
+ val values = values()
+ }
+
+ /**
+ * Returns true if tag matches; false otherwise
+ */
+ fun add(parser: XmlResourceParser, list: MutableList<Pair<String, ChangelogType>>): Boolean {
+ if (parser.name != tag) return false
+ if (parser.getAttributeValue(null, attr).isNotBlank())
+ list.add(Pair(parser.getAttributeValue(null, attr), this))
+ return true
+ }
+}
+
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/FragmentUtils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/FragmentUtils.kt
new file mode 100644
index 00000000..cd638068
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/FragmentUtils.kt
@@ -0,0 +1,15 @@
+package com.pitchedapps.frost.utils
+
+import android.os.Bundle
+import android.support.v4.app.Fragment
+
+/**
+ * Created by Allan Wang on 2017-05-29.
+ */
+
+fun Fragment.withBundle(creator: (Bundle) -> Unit): Fragment {
+ val bundle = Bundle()
+ creator.invoke(bundle)
+ this.arguments = bundle
+ return this
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Kotterknife.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Kotterknife.kt
new file mode 100644
index 00000000..6e3b5c24
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Kotterknife.kt
@@ -0,0 +1,137 @@
+package com.pitchedapps.frost.utils
+
+/**
+ * Created by Allan Wang on 2017-05-29.
+ *
+ * Courtesy of Jake Wharton
+ *
+ * https://github.com/JakeWharton/kotterknife/blob/master/src/main/kotlin/kotterknife/ButterKnife.kt
+ */
+import android.app.Activity
+import android.app.Dialog
+import android.app.DialogFragment
+import android.app.Fragment
+import android.support.v7.widget.RecyclerView.ViewHolder
+import android.view.View
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+import android.support.v4.app.DialogFragment as SupportDialogFragment
+import android.support.v4.app.Fragment as SupportFragment
+
+public fun <V : View> View.bindView(id: Int)
+ : ReadOnlyProperty<View, V> = required(id, viewFinder)
+public fun <V : View> Activity.bindView(id: Int)
+ : ReadOnlyProperty<Activity, V> = required(id, viewFinder)
+public fun <V : View> Dialog.bindView(id: Int)
+ : ReadOnlyProperty<Dialog, V> = required(id, viewFinder)
+public fun <V : View> DialogFragment.bindView(id: Int)
+ : ReadOnlyProperty<DialogFragment, V> = required(id, viewFinder)
+public fun <V : View> SupportDialogFragment.bindView(id: Int)
+ : ReadOnlyProperty<SupportDialogFragment, V> = required(id, viewFinder)
+public fun <V : View> Fragment.bindView(id: Int)
+ : ReadOnlyProperty<Fragment, V> = required(id, viewFinder)
+public fun <V : View> SupportFragment.bindView(id: Int)
+ : ReadOnlyProperty<SupportFragment, V> = required(id, viewFinder)
+public fun <V : View> ViewHolder.bindView(id: Int)
+ : ReadOnlyProperty<ViewHolder, V> = required(id, viewFinder)
+
+public fun <V : View> View.bindOptionalView(id: Int)
+ : ReadOnlyProperty<View, V?> = optional(id, viewFinder)
+public fun <V : View> Activity.bindOptionalView(id: Int)
+ : ReadOnlyProperty<Activity, V?> = optional(id, viewFinder)
+public fun <V : View> Dialog.bindOptionalView(id: Int)
+ : ReadOnlyProperty<Dialog, V?> = optional(id, viewFinder)
+public fun <V : View> DialogFragment.bindOptionalView(id: Int)
+ : ReadOnlyProperty<DialogFragment, V?> = optional(id, viewFinder)
+public fun <V : View> SupportDialogFragment.bindOptionalView(id: Int)
+ : ReadOnlyProperty<SupportDialogFragment, V?> = optional(id, viewFinder)
+public fun <V : View> Fragment.bindOptionalView(id: Int)
+ : ReadOnlyProperty<Fragment, V?> = optional(id, viewFinder)
+public fun <V : View> SupportFragment.bindOptionalView(id: Int)
+ : ReadOnlyProperty<SupportFragment, V?> = optional(id, viewFinder)
+public fun <V : View> ViewHolder.bindOptionalView(id: Int)
+ : ReadOnlyProperty<ViewHolder, V?> = optional(id, viewFinder)
+
+public fun <V : View> View.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<View, List<V>> = required(ids, viewFinder)
+public fun <V : View> Activity.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<Activity, List<V>> = required(ids, viewFinder)
+public fun <V : View> Dialog.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<Dialog, List<V>> = required(ids, viewFinder)
+public fun <V : View> DialogFragment.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<DialogFragment, List<V>> = required(ids, viewFinder)
+public fun <V : View> SupportDialogFragment.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<SupportDialogFragment, List<V>> = required(ids, viewFinder)
+public fun <V : View> Fragment.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<Fragment, List<V>> = required(ids, viewFinder)
+public fun <V : View> SupportFragment.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<SupportFragment, List<V>> = required(ids, viewFinder)
+public fun <V : View> ViewHolder.bindViews(vararg ids: Int)
+ : ReadOnlyProperty<ViewHolder, List<V>> = required(ids, viewFinder)
+
+public fun <V : View> View.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<View, List<V>> = optional(ids, viewFinder)
+public fun <V : View> Activity.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<Activity, List<V>> = optional(ids, viewFinder)
+public fun <V : View> Dialog.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<Dialog, List<V>> = optional(ids, viewFinder)
+public fun <V : View> DialogFragment.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<DialogFragment, List<V>> = optional(ids, viewFinder)
+public fun <V : View> SupportDialogFragment.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<SupportDialogFragment, List<V>> = optional(ids, viewFinder)
+public fun <V : View> Fragment.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<Fragment, List<V>> = optional(ids, viewFinder)
+public fun <V : View> SupportFragment.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<SupportFragment, List<V>> = optional(ids, viewFinder)
+public fun <V : View> ViewHolder.bindOptionalViews(vararg ids: Int)
+ : ReadOnlyProperty<ViewHolder, List<V>> = optional(ids, viewFinder)
+
+private val View.viewFinder: View.(Int) -> View?
+ get() = { findViewById(it) }
+private val Activity.viewFinder: Activity.(Int) -> View?
+ get() = { findViewById(it) }
+private val Dialog.viewFinder: Dialog.(Int) -> View?
+ get() = { findViewById(it) }
+private val DialogFragment.viewFinder: DialogFragment.(Int) -> View?
+ get() = { dialog.findViewById(it) }
+private val SupportDialogFragment.viewFinder: SupportDialogFragment.(Int) -> View?
+ get() = { dialog.findViewById(it) }
+private val Fragment.viewFinder: Fragment.(Int) -> View?
+ get() = { view.findViewById(it) }
+private val SupportFragment.viewFinder: SupportFragment.(Int) -> View?
+ get() = { view!!.findViewById(it) }
+private val ViewHolder.viewFinder: ViewHolder.(Int) -> View?
+ get() = { itemView.findViewById(it) }
+
+private fun viewNotFound(id:Int, desc: KProperty<*>): Nothing =
+ throw IllegalStateException("View ID $id for '${desc.name}' not found.")
+
+@Suppress("UNCHECKED_CAST")
+private fun <T, V : View> required(id: Int, finder: T.(Int) -> View?)
+ = Lazy { t: T, desc -> t.finder(id) as V? ?: viewNotFound(id, desc) }
+
+@Suppress("UNCHECKED_CAST")
+private fun <T, V : View> optional(id: Int, finder: T.(Int) -> View?)
+ = Lazy { t: T, desc -> t.finder(id) as V? }
+
+@Suppress("UNCHECKED_CAST")
+private fun <T, V : View> required(ids: IntArray, finder: T.(Int) -> View?)
+ = Lazy { t: T, desc -> ids.map { t.finder(it) as V? ?: viewNotFound(it, desc) } }
+
+@Suppress("UNCHECKED_CAST")
+private fun <T, V : View> optional(ids: IntArray, finder: T.(Int) -> View?)
+ = Lazy { t: T, desc -> ids.map { t.finder(it) as V? }.filterNotNull() }
+
+// Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it
+private class Lazy<T, V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {
+ private object EMPTY
+ private var value: Any? = EMPTY
+
+ override fun getValue(thisRef: T, property: KProperty<*>): V {
+ if (value == EMPTY) {
+ value = initializer(thisRef, property)
+ }
+ @Suppress("UNCHECKED_CAST")
+ return value as V
+ }
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt
new file mode 100644
index 00000000..279d595e
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt
@@ -0,0 +1,12 @@
+package com.pitchedapps.frost.utils
+
+/**
+ * Created by Allan Wang on 2017-05-28.
+ */
+class L {
+ companion object {
+ val TAG = "Frost"
+ fun e(s: String) = android.util.Log.e(com.pitchedapps.frost.utils.L.Companion.TAG, s)
+ fun d(s: String) = android.util.Log.d(com.pitchedapps.frost.utils.L.Companion.TAG, s)
+ }
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
new file mode 100644
index 00000000..c98fd5a5
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
@@ -0,0 +1,30 @@
+package com.pitchedapps.frost.utils
+
+import com.pitchedapps.frost.FrostApp
+
+/**
+ * Created by Allan Wang on 2017-05-28.
+ */
+val prefs: Prefs by lazy { FrostApp.prefs }
+
+class Prefs(c: android.content.Context) {
+ private companion object {
+ val PREFERENCE_NAME = "${com.pitchedapps.frost.BuildConfig.APPLICATION_ID}.prefs"
+ val LAST_ACTIVE = "last_active"
+ }
+
+ var lastActive: Long
+ get() = prefs.getLong(com.pitchedapps.frost.utils.Prefs.Companion.LAST_ACTIVE, -1)
+ set(value) = set(com.pitchedapps.frost.utils.Prefs.Companion.LAST_ACTIVE, System.currentTimeMillis())
+
+ init {
+ lastActive = 0
+ }
+
+ private val prefs: android.content.SharedPreferences by lazy { c.getSharedPreferences(com.pitchedapps.frost.utils.Prefs.Companion.PREFERENCE_NAME, android.content.Context.MODE_PRIVATE) }
+
+ private fun set(key: String, value: Boolean) = prefs.edit().putBoolean(key, value).apply()
+ private fun set(key: String, value: Int) = prefs.edit().putInt(key, value).apply()
+ private fun set(key: String, value: Long) = prefs.edit().putLong(key, value).apply()
+ private fun set(key: String, value: String) = prefs.edit().putString(key, value).apply()
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
new file mode 100644
index 00000000..bbf0e1f0
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
@@ -0,0 +1,11 @@
+package com.pitchedapps.frost.utils
+
+import android.content.res.Resources
+
+/**
+ * Created by Allan Wang on 2017-05-28.
+ */
+object Utils {
+ fun dpToPx(dp: Int) = (dp * android.content.res.Resources.getSystem().displayMetrics.density).toInt()
+ fun pxToDp(px:Int) = (px / android.content.res.Resources.getSystem().displayMetrics.density).toInt()
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt
new file mode 100644
index 00000000..e7314c20
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt
@@ -0,0 +1,138 @@
+package com.pitchedapps.frost.views
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Bitmap
+import android.support.v4.view.MotionEventCompat
+import android.support.v4.view.NestedScrollingChild
+import android.support.v4.view.NestedScrollingChildHelper
+import android.support.v4.view.ViewCompat
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import android.webkit.WebResourceError
+import android.webkit.WebResourceRequest
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import com.pitchedapps.frost.utils.L
+import io.reactivex.subjects.BehaviorSubject
+import io.reactivex.subjects.Subject
+
+enum class WebStatus {
+ LOADING, LOADED, ERROR
+}
+
+/**
+ * Created by Allan Wang on 2017-05-29.
+ *
+ * Courtesy of takahirom
+ *
+ * https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
+ */
+class FrostWebView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : WebView(context, attrs, defStyleAttr), NestedScrollingChild {
+
+ private val childHelper = NestedScrollingChildHelper(this)
+ private var lastY: Int = 0
+ private val scrollOffset = IntArray(2)
+ private val scrollConsumed = IntArray(2)
+ private var nestedOffsetY: Int = 0
+ val observable: Subject<WebStatus>
+
+ init {
+ isNestedScrollingEnabled = true
+ observable = BehaviorSubject.create<WebStatus>()
+ setupWebview()
+ }
+
+ @SuppressLint("SetJavaScriptEnabled")
+ fun setupWebview() {
+ settings.javaScriptEnabled = true
+ setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ setWebViewClient(object : WebViewClient() {
+ override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
+ super.onReceivedError(view, request, error)
+ observable.onNext(WebStatus.ERROR)
+ L.e("Error ${request}")
+ }
+
+ override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
+ super.onPageStarted(view, url, favicon)
+ observable.onNext(WebStatus.LOADING)
+ }
+
+ override fun onPageFinished(view: WebView?, url: String?) {
+ super.onPageFinished(view, url)
+ observable.onNext(WebStatus.LOADED)
+// CookieManager.getInstance().flush()
+ }
+ })
+ }
+
+ override fun onTouchEvent(ev: MotionEvent): Boolean {
+ val event = MotionEvent.obtain(ev)
+ val action = MotionEventCompat.getActionMasked(event)
+ if (action == MotionEvent.ACTION_DOWN)
+ nestedOffsetY = 0
+ val eventY = event.y.toInt()
+ event.offsetLocation(0f, nestedOffsetY.toFloat())
+ val returnValue: Boolean
+ when (action) {
+ MotionEvent.ACTION_MOVE -> {
+ var deltaY = lastY - eventY
+ // NestedPreScroll
+ if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
+ deltaY -= scrollConsumed[1]
+ event.offsetLocation(0f, -scrollOffset[1].toFloat())
+ nestedOffsetY += scrollOffset[1]
+ }
+ lastY = eventY - scrollOffset[1]
+ returnValue = super.onTouchEvent(event)
+ // NestedScroll
+ if (dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
+ event.offsetLocation(0f, scrollOffset[1].toFloat())
+ nestedOffsetY += scrollOffset[1]
+ lastY -= scrollOffset[1]
+ }
+ }
+ MotionEvent.ACTION_DOWN -> {
+ returnValue = super.onTouchEvent(event)
+ lastY = eventY
+ startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
+ }
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+ returnValue = super.onTouchEvent(event)
+ stopNestedScroll()
+ }
+ else -> return false
+ }
+ return returnValue
+ }
+
+ // Nested Scroll implements
+ override fun setNestedScrollingEnabled(enabled: Boolean) {
+ childHelper.isNestedScrollingEnabled = enabled
+ }
+
+ override fun isNestedScrollingEnabled() = childHelper.isNestedScrollingEnabled
+
+ override fun startNestedScroll(axes: Int) = childHelper.startNestedScroll(axes)
+
+ override fun stopNestedScroll() = childHelper.stopNestedScroll()
+
+ override fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent()
+
+ override fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?)
+ = childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow)
+
+ override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?)
+ = childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)
+
+ override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean)
+ = childHelper.dispatchNestedFling(velocityX, velocityY, consumed)
+
+ override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float)
+ = childHelper.dispatchNestedPreFling(velocityX, velocityY)
+
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshBase.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshBase.kt
new file mode 100644
index 00000000..59d42722
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshBase.kt
@@ -0,0 +1,31 @@
+package com.pitchedapps.frost.views
+
+import android.content.Context
+import android.support.v4.widget.SwipeRefreshLayout
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.webkit.WebView
+import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.utils.Utils
+
+
+/**
+ * Created by Allan Wang on 2017-05-28.
+ */
+class SwipeRefreshBase @JvmOverloads constructor(
+ context: Context?, attrs: AttributeSet? = null
+) : SwipeRefreshLayout(context, attrs) {
+
+ lateinit var shouldSwipe: (ev: MotionEvent) -> Boolean
+
+ companion object {
+ private val SCROLL_BUFFER by lazy { Utils.dpToPx(5) }
+ fun shouldScroll(webview: WebView) = webview.scrollY <= SCROLL_BUFFER
+ }
+
+// override fun onInterceptTouchEvent(ev: MotionEvent):Boolean {
+// val b = shouldSwipe.invoke(ev) && super.onInterceptTouchEvent(ev)
+// L.e("Should swipe $b")
+// return b
+// }
+} \ No newline at end of file
diff --git a/app/src/main/res/drawable-nodpi/splash_logo.9.png b/app/src/main/res/drawable-nodpi/splash_logo.9.png
new file mode 100644
index 00000000..9c012d1d
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/splash_logo.9.png
Binary files differ
diff --git a/app/src/main/res/drawable/splash_screen.xml b/app/src/main/res/drawable/splash_screen.xml
new file mode 100644
index 00000000..49085412
--- /dev/null
+++ b/app/src/main/res/drawable/splash_screen.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:opacity="opaque">
+ <!-- The background color, preferably the same as your normal theme -->
+ <item android:drawable="@color/splashBackground"/>
+ <!-- Your product logo - 288px x 288px color version of your app icon -->
+ <item android:bottom="@dimen/splash_logo">
+ <bitmap
+ android:gravity="center"
+ android:src="@drawable/splash_logo"/>
+ </item>
+</layer-list> \ 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 00000000..5ee18cf0
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,36 @@
+<?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:id="@+id/main_content"
+ android:layout_width="match_parent" android:layout_height="match_parent"
+ android:fitsSystemWindows="true" tools:context=".MainActivity">
+
+ <android.support.design.widget.AppBarLayout android:id="@+id/appbar"
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:paddingTop="@dimen/appbar_padding_top"
+ 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:layout_scrollFlags="scroll|enterAlways"
+ app:popupTheme="@style/AppTheme.PopupOverlay">
+
+ </android.support.v7.widget.Toolbar>
+
+ <android.support.design.widget.TabLayout
+ android:id="@+id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </android.support.design.widget.AppBarLayout>
+
+ <android.support.v4.view.ViewPager android:id="@+id/container"
+ android:layout_width="match_parent" android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+
+ <android.support.design.widget.FloatingActionButton android:id="@+id/fab"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom" android:layout_margin="@dimen/fab_margin"
+ app:srcCompat="@android:drawable/ic_dialog_email" />
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/changelog_content.xml b/app/src/main/res/layout/changelog_content.xml
new file mode 100644
index 00000000..e4084796
--- /dev/null
+++ b/app/src/main/res/layout/changelog_content.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="8.4sp"
+ android:paddingLeft="@dimen/dialog_margin"
+ android:paddingRight="@dimen/dialog_margin">
+
+ <!--padding bottom is 14sp * 0.6-->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:lineSpacingMultiplier="1.6"
+ android:paddingRight="5dp"
+ android:text="@string/u2022"
+ android:textAppearance="@style/TextAppearance.AppCompat.Small" />
+
+ <TextView
+ android:id="@+id/changelog_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:lineSpacingMultiplier="1.6"
+ android:textAppearance="@style/TextAppearance.AppCompat.Small" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/changelog_title.xml b/app/src/main/res/layout/changelog_title.xml
new file mode 100644
index 00000000..d00e54f1
--- /dev/null
+++ b/app/src/main/res/layout/changelog_title.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/changelog_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:lineSpacingMultiplier="1.6"
+ android:paddingBottom="@dimen/dialog_margin_bottom"
+ android:paddingLeft="@dimen/dialog_margin"
+ android:paddingRight="@dimen/dialog_margin"
+ android:paddingTop="@dimen/dialog_margin_bottom"
+ android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+ android:textStyle="bold" />
+
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 00000000..6d4a0fab
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,12 @@
+<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="com.pitchedapps.frost.MainActivity$PlaceholderFragment">
+
+ <TextView android:id="@+id/section_label" android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/swipe_webview.xml b/app/src/main/res/layout/swipe_webview.xml
new file mode 100644
index 00000000..87df67b2
--- /dev/null
+++ b/app/src/main/res/layout/swipe_webview.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.pitchedapps.frost.views.SwipeRefreshBase xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/swipe_refresh"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.pitchedapps.frost.views.FrostWebView
+ android:id="@+id/frost_webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+</com.pitchedapps.frost.views.SwipeRefreshBase>
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 00000000..b4140da4
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,15 @@
+<menu 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"
+ tools:context="com.pitchedapps.myapplication.MainActivity">
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:title="@string/action_settings"
+ app:showAsAction="never" />
+ <item
+ android:id="@+id/action_changelog"
+ android:orderInCategory="200"
+ android:title="@string/changelog"
+ app:showAsAction="never" />
+</menu>
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 00000000..88b0e234
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..9a078e3e
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_launcher_round.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 00000000..0e1a37d5
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..3af2608a
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.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 00000000..e0b0af6b
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..9bec2e62
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.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 00000000..115ce95a
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..34947cd6
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..2215743c
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+ <color name="splashBackground">@color/facebook_blue</color>
+ <color name="facebook_blue">#3b5998</color>
+ <color name="facebook_blue_dark">#2e4b86</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 00000000..06c8ca73
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,12 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="dialog_margin">24dp</dimen>
+ <dimen name="dialog_margin_bottom">16dp</dimen>
+
+ <dimen name="fab_margin">16dp</dimen>
+ <dimen name="appbar_padding_top">8dp</dimen>
+ <dimen name="splash_logo">16dp</dimen>
+
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..4142ff6b
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,11 @@
+<resources>
+ <string name="app_name">Frost for Facebook</string>
+
+ <string name="action_settings">Settings</string>
+ <string name="section_format">Hello World from section: %1$d</string>
+
+
+ <string name="changelog">Changelog</string>
+ <string name="great">Great</string>
+ <string name="u2022">•</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 00000000..c04a9609
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,23 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</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" />
+
+ <style name="Theme.Splash" parent="AppTheme">
+ <item name="android:windowBackground">@drawable/splash_screen</item>
+ <item name="android:navigationBarColor">@color/splashBackground</item>
+ <item name="colorPrimaryDark">@color/splashBackground</item>
+ <item name="colorAccent">@color/splashBackground</item>
+ </style>
+</resources>
diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
new file mode 100644
index 00000000..58c3fd1d
--- /dev/null
+++ b/app/src/main/res/xml/changelog.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <!--
+ <version title="v"/>
+ <item text="" />
+ -->
+
+ <version title="v0.1" />
+ <item text="Initial" />
+</resources> \ No newline at end of file
diff --git a/app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java b/app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java
new file mode 100644
index 00000000..a702863a
--- /dev/null
+++ b/app/src/test/java/com/pitchedapps/frost/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.pitchedapps.frost;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+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 00000000..ec3796d5
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,45 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext.kotlin_version = '1.1.2-4'
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "io.realm:realm-gradle-plugin:3.3.1"
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ mavenCentral()
+ maven { url "https://jitpack.io" }
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
+
+task generateChangelogMd() {
+ def parsedProjectXml = (new XmlParser()).parse("$project.rootDir/app/src/main/res/xml/changelog.xml")
+ def sw = new StringWriter()
+ sw.append("# Changelog\n")
+ parsedProjectXml.depthFirst().each {
+ switch (it.name()) {
+ case "version":
+ sw.append("\n## ${it.@title}\n")
+ break
+ case "item":
+ if (it.@text?.trim())
+ sw.append("* ${it.@text}\n")
+ }
+ }
+ def changelogMd = new File("$project.rootDir/docs/Changelog.md")
+ changelogMd.write(sw.toString())
+} \ No newline at end of file
diff --git a/docs/Changelog.md b/docs/Changelog.md
new file mode 100644
index 00000000..df173bb4
--- /dev/null
+++ b/docs/Changelog.md
@@ -0,0 +1,4 @@
+# Changelog
+
+## v0.1
+* Initial
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..f24a32a9
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,36 @@
+# 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.
+org.gradle.jvmargs=-Xmx1536m
+
+# 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
+APP_ID=Frost
+APP_GROUP=com.pitchedapps
+MIN_SDK=21
+TARGET_SDK=25
+BUILD_TOOLS=25.0.2
+VERSION_CODE=1
+VERSION_NAME=0.1
+
+ANDROID_SUPPORT_LIBS=25.3.1
+TIMBER=4.5.1
+MD=0.9.4.3
+ICONICS=2.8.3
+GMD=3.0.1.0
+BUTTERKNIFE=8.5.1
+RX_JAVA=2.0.7
+RX_ANDROID=2.0.1
+RX_BINDING=2.0.0
+STETHO=1.4.2
+GLIDE=4.0.0-RC0
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..13372aef
--- /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 00000000..aa82d1ab
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun May 28 21:23:49 PDT 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 00000000..9d82f789
--- /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 00000000..8a0b282a
--- /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 00000000..e7b4def4
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app'