From 214656093af2a39782a3b5ac3a317e5c5c6a81d7 Mon Sep 17 00:00:00 2001 From: Kevin Hinterlong Date: Sat, 4 Aug 2018 14:07:09 -0500 Subject: [PATCH 1/7] Update gradle and android sdk versions --- .gitignore | 1 + .idea/codeStyles/Project.xml | 251 +++++++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/misc.xml | 101 +++++++++ .idea/modules.xml | 9 + .idea/runConfigurations.xml | 12 ++ .idea/vcs.xml | 6 + app/build.gradle | 35 ++-- build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 12 +- 10 files changed, 413 insertions(+), 23 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 520a863..3907196 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ captures/ .idea/gradle.xml .idea/dictionaries .idea/libraries +.idea/caches # Keystore files *.jks diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..033d808 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,251 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..cd74ae8 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..bbda0ff --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a7b061e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ 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 @@ + + + + + + \ 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 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 38af075..a18caf2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 27 + buildToolsVersion "27.0.3" defaultConfig { - applicationId "com.jtmcn.archwiki.viewer" - minSdkVersion 15 - targetSdkVersion 25 versionCode 13 versionName "1.0.12" + applicationId "com.jtmcn.archwiki.viewer" + minSdkVersion 15 + targetSdkVersion 27 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -23,17 +23,20 @@ android { } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile 'com.jakewharton:butterknife:8.6.0' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' + implementation fileTree(include: ['*.jar'], dir: 'libs') + + // Core libraries + implementation 'com.android.support:design:27.1.1' + implementation 'com.android.support:support-v4:27.1.1' + implementation 'com.android.support:appcompat-v7:27.1.1' - compile 'com.google.code.gson:gson:2.7' + // Other dependecies + implementation 'com.jakewharton:butterknife:8.8.1' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' + implementation 'com.google.code.gson:gson:2.8.2' - compile 'com.android.support:design:25.3.1' - compile 'com.android.support:support-v4:25.3.1' - compile 'com.android.support:appcompat-v7:25.3.1' - testCompile 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) } diff --git a/build.gradle b/build.gradle index 8674409..6c5cbc3 100644 --- a/build.gradle +++ b/build.gradle @@ -2,14 +2,16 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.1.3' } } allprojects { repositories { jcenter() + google() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aba3e89..008ec5e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Apr 03 11:24:59 CDT 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 +#Sat Aug 04 13:54:57 CDT 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip From b99570d02764f34e97c68e0d1491915583da06ce Mon Sep 17 00:00:00 2001 From: Kevin Hinterlong Date: Sat, 4 Aug 2018 17:43:58 -0500 Subject: [PATCH 2/7] Partiall convert to kotlin --- .idea/kotlinc.xml | 8 ++ app/build.gradle | 2 + .../com/jtmcn/archwiki/viewer/Constants.java | 20 ----- .../com/jtmcn/archwiki/viewer/Constants.kt | 21 +++++ .../com/jtmcn/archwiki/viewer/WikiClient.java | 3 +- .../archwiki/viewer/data/SearchResult.java | 37 --------- .../archwiki/viewer/data/SearchResult.kt | 3 + .../viewer/data/SearchResultsBuilder.java | 83 ------------------- .../viewer/data/SearchResultsBuilder.kt | 56 +++++++++++++ .../jtmcn/archwiki/viewer/data/WikiPage.java | 68 --------------- .../jtmcn/archwiki/viewer/data/WikiPage.kt | 12 +++ .../archwiki/viewer/data/WikiPageBuilder.java | 74 ----------------- .../archwiki/viewer/data/WikiPageBuilder.kt | 68 +++++++++++++++ .../archwiki/viewer/utils/AndroidUtils.java | 49 ----------- .../archwiki/viewer/utils/AndroidUtils.kt | 40 +++++++++ .../viewer/data/SearchResultsBuilderTest.java | 4 +- .../viewer/data/WikiPageBuilderTest.java | 3 +- build.gradle | 3 + 18 files changed, 219 insertions(+), 335 deletions(-) create mode 100644 .idea/kotlinc.xml delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/Constants.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..1d2cf4e --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a18caf2..1ed1eb4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { compileSdkVersion 27 @@ -31,6 +32,7 @@ dependencies { implementation 'com.android.support:appcompat-v7:27.1.1' // Other dependecies + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' implementation 'com.google.code.gson:gson:2.8.2' diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.java b/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.java deleted file mode 100644 index 2721570..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jtmcn.archwiki.viewer; - -/** - * Various constants used throughout the program. - */ -public class Constants { - //format types - public static final String TEXT_HTML_MIME = "text/html"; - public static final String TEXT_PLAIN_MIME = "text/plain"; - public static final String UTF_8 = "UTF-8"; - - //arch wiki urls - public static final String ARCHWIKI_BASE = "https://wiki.archlinux.org/"; - public static final String ARCHWIKI_MAIN = ARCHWIKI_BASE + "index.php/Main_page"; - public static final String ARCHWIKI_SEARCH_URL = ARCHWIKI_BASE + "index.php?&search=%s"; - - //local file paths - public static final String ASSETS_FOLDER = "file:///android_asset/"; - public static final String LOCAL_CSS = ASSETS_FOLDER + "style.css"; -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt new file mode 100644 index 0000000..0be659f --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt @@ -0,0 +1,21 @@ +@file:JvmName("Constants") + +package com.jtmcn.archwiki.viewer + +/** + * Various constants used throughout the program. + */ + +// format types +const val TEXT_HTML_MIME = "text/html" +const val TEXT_PLAIN_MIME = "text/plain" +const val UTF_8 = "UTF-8" + +//arch wiki urls +const val ARCHWIKI_BASE = "https://wiki.archlinux.org/" +const val ARCHWIKI_MAIN = ARCHWIKI_BASE + "index.php/Main_page" +const val ARCHWIKI_SEARCH_URL = ARCHWIKI_BASE + "index.php?&search=%s" + +//local file paths +const val ASSETS_FOLDER = "file:///android_asset/" +const val LOCAL_CSS = ASSETS_FOLDER + "style.css" diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java index ffe1032..229bf3a 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java @@ -62,7 +62,8 @@ public void loadWikiHtml(WikiPage wikiPage) { null ); - setSubtitle(wikiPage.getPageTitle()); + String sub = wikiPage.getPageTitle() != null ? wikiPage.getPageTitle() : "No Title Found"; + setSubtitle(sub); } /** diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.java b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.java deleted file mode 100644 index 0a0c9b7..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.jtmcn.archwiki.viewer.data; - -/** - * A page on the wiki which only knows the name and url. - */ -public class SearchResult { - private final String pageName; - private final String pageUrl; - - /** - * Create a search result. - * - * @param pageName the name of the page as shown on the wiki. - * @param pageUrl the string url on the wiki. - */ - public SearchResult(String pageName, String pageUrl) { - this.pageName = pageName; - this.pageUrl = pageUrl; - } - - public String getPageName() { - return pageName; - } - - public String getPageUrl() { - return pageUrl; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("SearchResult{"); - sb.append("title='").append(pageName).append('\''); - sb.append(", url='").append(pageUrl).append('\''); - sb.append('}'); - return sb.toString(); - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.kt new file mode 100644 index 0000000..4da7b5c --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResult.kt @@ -0,0 +1,3 @@ +package com.jtmcn.archwiki.viewer.data + +data class SearchResult(val pageName: String, val pageUrl: String) diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.java b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.java deleted file mode 100644 index ee11f65..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.jtmcn.archwiki.viewer.data; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; - -import java.util.ArrayList; -import java.util.List; - -import static com.jtmcn.archwiki.viewer.Constants.ARCHWIKI_BASE; - -/** - * Provides a simple interface to make queries against - * and parse data from the arch wiki for searches. - */ -public class SearchResultsBuilder { - public static final String SEARCH_URL = ARCHWIKI_BASE + "api.php?action=opensearch" + - "&format=json&formatversion=2&namespace=0&limit=%d" + - "&suggest=true&search=%s"; - private static final int DEFAULT_LIMIT = 10; - - private SearchResultsBuilder() { - - } - - /** - * Builds a string url to fetch search results. - * - * @param query the text to search for. - * @return a url to fetch. - */ - public static String getSearchQuery(String query) { - return getSearchQuery(query, DEFAULT_LIMIT); - } - - /** - * Builds a string url to fetch search results. - * - * @param query the text to search for. - * @param limit the maximum number of results to retrieve. - * @return a url to fetch. - */ - public static String getSearchQuery(String query, int limit) { - return String.format(SEARCH_URL, limit, query); - } - - /** - * Builds a {@link List} from the result of fetching with {@link #getSearchQuery(String, int)}. - * - * @param jsonResult the string returned from the query. - * @return a parsed list of the results. - */ - public static List parseSearchResults(String jsonResult) { - JsonParser jsonParser = new JsonParser(); - JsonElement jsonRoot = jsonParser.parse(jsonResult); - List toReturn = new ArrayList<>(); - if (jsonRoot.isJsonArray()) { - JsonArray jsonArray = jsonRoot.getAsJsonArray(); - if (jsonArray.size() == 4) { - String[] listOfPageTitles = getJsonArrayAsStringArray(jsonArray.get(1).getAsJsonArray()); - String[] listOfPageUrls = getJsonArrayAsStringArray(jsonArray.get(3).getAsJsonArray()); - for (int i = 0; i < listOfPageTitles.length; i++) { - toReturn.add(new SearchResult(listOfPageTitles[i], listOfPageUrls[i])); - } - } - } - return toReturn; - } - - /** - * Convert a {@link JsonArray} into an array of strings. - * - * @param jsonArray the array to be parsed. - * @return the string array which was parsed. - */ - private static String[] getJsonArrayAsStringArray(JsonArray jsonArray) { - String[] s2 = new String[jsonArray.size()]; - for (int i = 0; i < jsonArray.size(); i++) { - s2[i] = jsonArray.get(i).getAsString(); - } - return s2; - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt new file mode 100644 index 0000000..bf161f9 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt @@ -0,0 +1,56 @@ +@file:JvmName("SearchResultsBuilder") + +package com.jtmcn.archwiki.viewer.data + +import com.google.gson.JsonArray +import com.google.gson.JsonParser +import com.jtmcn.archwiki.viewer.ARCHWIKI_BASE + +/** + * Provides a simple interface to make queries against + * and parse data from the arch wiki for searches. + */ +private val jsonParser = JsonParser() +private const val DEFAULT_LIMIT = 10 + +/** + * Builds a string url to fetch search results. + * + * @param query the text to search for. + * @param limit the maximum number of results to retrieve. + * @return a url to fetch. + */ +@JvmOverloads +fun getSearchQuery(query: String, limit: Int = DEFAULT_LIMIT): String { + return "${ARCHWIKI_BASE}api.php?" + + "action=opensearch&format=json&formatversion=2&namespace=0&suggest=true" + + "&search=${query}&limit=${limit}" +} + +/** + * Builds a [List] from the result of fetching with [getSearchQuery]. + * + * @param jsonResult the string returned from the query. + * @return a parsed list of the results. + */ +fun parseSearchResults(jsonResult: String): List { + val jsonRoot = jsonParser.parse(jsonResult) + if (!jsonRoot.isJsonArray || jsonRoot.asJsonArray.size() != 4) return listOf() + + val jsonArray = jsonRoot.asJsonArray + + val listOfPageTitles = getJsonArrayAsStringArray(jsonArray.get(1).asJsonArray) + val listOfPageUrls = getJsonArrayAsStringArray(jsonArray.get(3).asJsonArray) + + return listOfPageTitles.zip(listOfPageUrls).map { SearchResult(it.first, it.second) } +} + +/** + * Convert a [JsonArray] into an array of strings. + * + * @param jsonArray the array to be parsed. + * @return the string array which was parsed. + */ +private fun getJsonArrayAsStringArray(jsonArray: JsonArray): List { + return jsonArray.map { it.asString }.filterNotNull() +} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.java b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.java deleted file mode 100644 index 239368a..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.jtmcn.archwiki.viewer.data; - -/** - * Wrapper for a downloaded wiki page which holds the title and html. - */ -public class WikiPage { - private final String pageUrl; - private final String pageTitle; - private final String htmlString; - private int scrollPosition = 0; - - /** - * Store the url, title, and html of a page on the wiki. - * - * @param pageUrl the string url on the wiki. - * @param pageTitle the title of the page on the wiki. - * @param htmlString the html which should be shown to represent the page. - */ - public WikiPage(String pageUrl, String pageTitle, String htmlString) { - this.pageUrl = pageUrl; - this.pageTitle = pageTitle; - this.htmlString = htmlString; - } - - public String getPageUrl() { - return pageUrl; - } - - public String getPageTitle() { - return pageTitle; - } - - public String getHtmlString() { - return htmlString; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("WikiPage{"); - sb.append("title='").append(pageTitle).append('\''); - sb.append('}'); - return sb.toString(); - } - - public int getScrollPosition() { - return scrollPosition; - } - - public void setScrollPosition(int scrollPosition) { - this.scrollPosition = scrollPosition; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof WikiPage)) return false; - - WikiPage wikiPage = (WikiPage) o; - - return getPageUrl() != null ? getPageUrl().equals(wikiPage.getPageUrl()) : wikiPage.getPageUrl() == null; - - } - - @Override - public int hashCode() { - return getPageUrl() != null ? getPageUrl().hashCode() : 0; - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt new file mode 100644 index 0000000..dfbec82 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt @@ -0,0 +1,12 @@ +package com.jtmcn.archwiki.viewer.data + +/** + * Store the url, title, and html of a page on the wiki. + * + * @param pageUrl the string url on the wiki. + * @param pageTitle the title of the page on the wiki. + * @param htmlString the html which should be shown to represent the page. + */ +data class WikiPage(val pageUrl: String, val pageTitle: String?, val htmlString: String) { + var scrollPosition = 0 +} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.java b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.java deleted file mode 100644 index 4efdb6a..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.jtmcn.archwiki.viewer.data; - -import static com.jtmcn.archwiki.viewer.Constants.LOCAL_CSS; - -/** - * Helps with creating a {@link WikiPage} by extracting content from the - * html fetched from the ArchWiki. - */ -public class WikiPageBuilder { - //NOTE: spaces are allowed in ""/etc, but parsing this way should be fine - public static final String HTML_HEAD_OPEN = ""; - public static final String HTML_HEAD_CLOSE = ""; - public static final String HTML_TITLE_OPEN = ""; - public static final String HTML_TITLE_CLOSE = ""; - public static final String HEAD_TO_INJECT = "" - + ""; - public static final String DEFAULT_TITLE = " - ArchWiki"; - - private WikiPageBuilder() { - - } - - /** - * Builds a page containing the title, url, and injects local css. - * - * @param stringUrl url to download. - * @param html stringbuilder containing the html of the wikipage - * @return {@link WikiPage} containing downloaded page. - */ - public static WikiPage buildPage(String stringUrl, StringBuilder html) { - String pageTitle = getPageTitle(html); - injectLocalCSS(html, LOCAL_CSS); - return new WikiPage(stringUrl, pageTitle, html.toString()); - } - - /** - * Finds the name of the page within the title block of the html. - * The returned string removes the " - ArchWiki" if found. - * - * @param htmlString The html of the page as a string. - * @return the extracted title from the page. - */ - public static String getPageTitle(StringBuilder htmlString) { - int titleStart = (htmlString.indexOf(HTML_TITLE_OPEN) + HTML_TITLE_OPEN.length()); - int titleEnd = htmlString.indexOf(HTML_TITLE_CLOSE, titleStart); - if (titleStart > 0 && titleEnd > titleStart) { // if there is an html title block - String title = htmlString.substring(titleStart, titleEnd); - return title.replace(DEFAULT_TITLE, ""); // drop DEFAULT_TITLE from page title - } - //todo should be handled somewhere else when no title is found - return "No title found"; - } - - /** - * Removes the contents within the head block of the html - * and replaces it with the a reference to a local css file. - * - * @param htmlString The html of the page as a string. - * @param localCSSFilePath The path of the css file to inject. - * @return true if the block was successfully replaced. - */ - public static boolean injectLocalCSS(StringBuilder htmlString, String localCSSFilePath) { - int headStart = htmlString.indexOf(HTML_HEAD_OPEN) + HTML_HEAD_OPEN.length(); - int headEnd = htmlString.indexOf(HTML_HEAD_CLOSE, headStart); - - if (headStart > 0 && headEnd >= headStart) { - String injectedHeadHtml = String.format(HEAD_TO_INJECT, localCSSFilePath); - htmlString.replace(headStart, headEnd, injectedHeadHtml); - return true; - } - - return false; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt new file mode 100644 index 0000000..7135e25 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt @@ -0,0 +1,68 @@ +@file:JvmName("WikiPageBuilder") +package com.jtmcn.archwiki.viewer.data + +import com.jtmcn.archwiki.viewer.LOCAL_CSS + +/** + * Helps with creating a [WikiPage] by extracting content from the + * html fetched from the ArchWiki. + */ + +//NOTE: spaces are allowed in ""/etc, but parsing this way should be fine +const val HTML_HEAD_OPEN = "" +const val HTML_HEAD_CLOSE = "" +const val HTML_TITLE_OPEN = "" +const val HTML_TITLE_CLOSE = "" +private const val HEAD_TO_INJECT = "" + "" +private const val DEFAULT_TITLE = " - ArchWiki" + +/** + * Builds a page containing the title, url, and injects local css. + * + * @param stringUrl url to download. + * @param html stringbuilder containing the html of the wikipage + * @return [WikiPage] containing downloaded page. + */ +fun buildPage(stringUrl: String, html: StringBuilder): WikiPage { + val pageTitle = getPageTitle(html) + injectLocalCSS(html, LOCAL_CSS) + return WikiPage(stringUrl, pageTitle, html.toString()) +} + +/** + * Finds the name of the page within the title block of the html. + * The returned string removes the " - ArchWiki" if found. + * + * @param htmlString The html of the page as a string. + * @return the extracted title from the page. + */ +fun getPageTitle(htmlString: StringBuilder): String? { + val titleStart = htmlString.indexOf(HTML_TITLE_OPEN) + HTML_TITLE_OPEN.length + val titleEnd = htmlString.indexOf(HTML_TITLE_CLOSE, titleStart) + if (titleStart in 1 until titleEnd) { // if there is an html title block + val title = htmlString.substring(titleStart, titleEnd) + return title.replace(DEFAULT_TITLE, "") // drop DEFAULT_TITLE from page title + } + return null +} + +/** + * Removes the contents within the head block of the html + * and replaces it with the a reference to a local css file. + * + * @param htmlString The html of the page as a string. + * @param localCSSFilePath The path of the css file to inject. + * @return true if the block was successfully replaced. + */ +fun injectLocalCSS(htmlString: StringBuilder, localCSSFilePath: String): Boolean { + val headStart = htmlString.indexOf(HTML_HEAD_OPEN) + HTML_HEAD_OPEN.length + val headEnd = htmlString.indexOf(HTML_HEAD_CLOSE, headStart) + + if (headStart in 1..headEnd) { + val injectedHeadHtml = String.format(HEAD_TO_INJECT, localCSSFilePath) + htmlString.replace(headStart, headEnd, injectedHeadHtml) + return true + } + + return false +} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.java b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.java deleted file mode 100644 index 93a5877..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.jtmcn.archwiki.viewer.utils; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.support.v4.app.ShareCompat; - -import com.jtmcn.archwiki.viewer.R; - -import static com.jtmcn.archwiki.viewer.Constants.TEXT_PLAIN_MIME; - -/** - * Utilities class for Android specific actions. - */ -public class AndroidUtils { - - private AndroidUtils() { - - } - - /** - * Creates an intent to prompt the user for sharing text. - * - * @param title The name of the text being stored. - * @param url The url to be shared. - * @param activity The current activity. - */ - public static Intent shareText(String title, String url, Activity activity) { - return ShareCompat.IntentBuilder.from(activity) - .setSubject(title) - .setText(url) - .setStream(Uri.parse(url)) - .setType(TEXT_PLAIN_MIME) - .setChooserTitle(R.string.share) - .getIntent(); - } - - /** - * Creates an intent to open a link. - * - * @param url The url to be opened. - * @param context The context needed to start the intent. - */ - public static void openLink(String url, Context context) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - context.startActivity(intent); - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt new file mode 100644 index 0000000..d78bbb6 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt @@ -0,0 +1,40 @@ +@file:JvmName("AndroidUtils") +package com.jtmcn.archwiki.viewer.utils + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.support.v4.app.ShareCompat + +import com.jtmcn.archwiki.viewer.R + +import com.jtmcn.archwiki.viewer.TEXT_PLAIN_MIME + +/** + * Creates an intent to prompt the user for sharing text. + * + * @param title The name of the text being stored. + * @param url The url to be shared. + * @param activity The current activity. + */ +fun shareText(title: String, url: String, activity: Activity): Intent { + return ShareCompat.IntentBuilder.from(activity) + .setSubject(title) + .setText(url) + .setStream(Uri.parse(url)) + .setType(TEXT_PLAIN_MIME) + .setChooserTitle(R.string.share) + .intent +} + +/** + * Creates an intent to open a link. + * + * @param url The url to be opened. + * @param context The context needed to start the intent. + */ +fun openLink(url: String, context: Context) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + context.startActivity(intent) +} diff --git a/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java b/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java index f1fd5fe..35cb1e5 100644 --- a/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java +++ b/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java @@ -29,10 +29,10 @@ public void parseSearchResults() throws Exception { @Test public void getSearchQuery() { String query = SearchResultsBuilder.getSearchQuery("arch"); - assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&limit=10&suggest=true&search=arch",query); + assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&suggest=true&search=arch&limit=10",query); String queryWithLength = SearchResultsBuilder.getSearchQuery("arch",9); - assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&limit=9&suggest=true&search=arch",queryWithLength); + assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&suggest=true&search=arch&limit=9",queryWithLength); } @Test diff --git a/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java b/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java index 9155c7b..e879fe4 100644 --- a/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java +++ b/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -21,7 +22,7 @@ public void getPageTitle() throws Exception { @Test public void getEmptyTitle() throws Exception { - assertEquals("No title found", WikiPageBuilder.getPageTitle(new StringBuilder(""))); + assertNull(WikiPageBuilder.getPageTitle(new StringBuilder(""))); } @Test diff --git a/build.gradle b/build.gradle index 6c5cbc3..4f95ce4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,20 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.2.60' repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:3.1.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } allprojects { repositories { jcenter() + mavenCentral() google() } } From b280e448a8bfa572092defbd64eeb2de5ba6947e Mon Sep 17 00:00:00 2001 From: Kevin Hinterlong Date: Sun, 5 Aug 2018 11:01:17 -0500 Subject: [PATCH 3/7] Finish converting to kotlin --- .idea/misc.xml | 2 +- app/build.gradle | 1 + .../com/jtmcn/archwiki/viewer/Constants.kt | 2 - .../jtmcn/archwiki/viewer/MainActivity.java | 211 ------------------ .../com/jtmcn/archwiki/viewer/MainActivity.kt | 162 ++++++++++++++ .../archwiki/viewer/PreferencesActivity.java | 63 ------ .../archwiki/viewer/PreferencesActivity.kt | 58 +++++ .../archwiki/viewer/SearchResultsAdapter.java | 51 ----- .../archwiki/viewer/SearchResultsAdapter.kt | 49 ++++ .../com/jtmcn/archwiki/viewer/WikiClient.java | 193 ---------------- .../com/jtmcn/archwiki/viewer/WikiClient.kt | 164 ++++++++++++++ .../com/jtmcn/archwiki/viewer/WikiView.java | 79 ------- .../com/jtmcn/archwiki/viewer/WikiView.kt | 70 ++++++ .../viewer/data/SearchResultsBuilder.kt | 5 +- .../archwiki/viewer/data/WikiPageBuilder.kt | 1 - .../jtmcn/archwiki/viewer/tasks/Fetch.java | 66 ------ .../com/jtmcn/archwiki/viewer/tasks/Fetch.kt | 43 ++++ .../jtmcn/archwiki/viewer/tasks/FetchUrl.java | 90 -------- .../jtmcn/archwiki/viewer/tasks/FetchUrl.kt | 59 +++++ .../archwiki/viewer/utils/AndroidUtils.kt | 1 - .../archwiki/viewer/utils/NetworkUtils.java | 69 ------ .../archwiki/viewer/utils/NetworkUtils.kt | 59 +++++ .../archwiki/viewer/utils/SettingsUtils.java | 24 -- .../archwiki/viewer/utils/SettingsUtils.kt | 15 ++ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/toolbar.xml | 2 +- app/src/main/res/menu/menu.xml | 2 +- .../viewer/data/SearchResultsBuilderTest.java | 54 ----- .../viewer/data/SearchResultsBuilderTest.kt | 51 +++++ .../viewer/data/WikiPageBuilderTest.java | 47 ---- .../viewer/data/WikiPageBuilderTest.kt | 46 ++++ 31 files changed, 782 insertions(+), 959 deletions(-) delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.java create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt delete mode 100644 app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java create mode 100644 app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.kt delete mode 100644 app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java create mode 100644 app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index bbda0ff..9e60209 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -92,7 +92,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 1ed1eb4..35639c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt index 0be659f..03633d1 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt @@ -1,5 +1,3 @@ -@file:JvmName("Constants") - package com.jtmcn.archwiki.viewer /** diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java b/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java deleted file mode 100644 index c480b6f..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java +++ /dev/null @@ -1,211 +0,0 @@ -package com.jtmcn.archwiki.viewer; - -import android.app.SearchManager; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.SearchView; -import android.support.v7.widget.ShareActionProvider; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.webkit.WebSettings; -import android.widget.ProgressBar; - -import com.jtmcn.archwiki.viewer.data.SearchResult; -import com.jtmcn.archwiki.viewer.data.SearchResultsBuilder; -import com.jtmcn.archwiki.viewer.data.WikiPage; -import com.jtmcn.archwiki.viewer.tasks.Fetch; -import com.jtmcn.archwiki.viewer.tasks.FetchUrl; -import com.jtmcn.archwiki.viewer.utils.AndroidUtils; -import com.jtmcn.archwiki.viewer.utils.SettingsUtils; - -import java.util.ArrayList; -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; - -public class MainActivity extends AppCompatActivity implements FetchUrl.OnFinish> { - public static final String TAG = MainActivity.class.getSimpleName(); - @BindView(R.id.wiki_view) WikiView wikiViewer; - @BindView(R.id.toolbar) Toolbar toolbar; - private ShareActionProvider shareActionProvider; - private SearchView searchView; - private MenuItem searchMenuItem; - private List currentSuggestions; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - ButterKnife.bind(this); - - setSupportActionBar(toolbar); - - ProgressBar progressBar = ButterKnife.findById(this, R.id.progress_bar); - wikiViewer.buildView(progressBar, getSupportActionBar()); - - handleIntent(getIntent()); - } - - @Override - protected void onResume() { - super.onResume(); - updateWebSettings(); - } - - @Override - protected void onNewIntent(Intent intent) { - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - if (intent == null) { - return; - } - - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - String query = intent.getStringExtra(SearchManager.QUERY); - wikiViewer.passSearch(query); - hideSearchView(); - } else if (Intent.ACTION_VIEW.equals(intent.getAction())) { - final String url = intent.getDataString(); - wikiViewer.wikiClient.shouldOverrideUrlLoading(wikiViewer, url); - } - } - - /** - * Update the font size used in the webview. - */ - public void updateWebSettings() { - WebSettings webSettings = wikiViewer.getSettings(); - int fontSize = SettingsUtils.getFontSize(this); - - //todo this setting should be changed to a slider, remove deprecated call - // deprecated method must be used until Android API 14 - // https://developer.android.com/reference/android/webkit/WebSettings.TextSize.html#NORMAL - switch (fontSize) { - case 0: - webSettings.setTextSize(WebSettings.TextSize.SMALLEST); //50% - break; - case 1: - webSettings.setTextSize(WebSettings.TextSize.SMALLER); //75% - break; - case 2: - webSettings.setTextSize(WebSettings.TextSize.NORMAL); //100% - break; - case 3: - webSettings.setTextSize(WebSettings.TextSize.LARGER); //150% - break; - case 4: - webSettings.setTextSize(WebSettings.TextSize.LARGEST); //200% - break; - } - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); - searchMenuItem = menu.findItem(R.id.menu_search); - searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem); - searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (!hasFocus) { - hideSearchView(); - } - } - }); - searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - wikiViewer.passSearch(query); - return false; - } - - @Override - public boolean onQueryTextChange(String newText) { - if (newText.isEmpty()) { - setCursorAdapter(new ArrayList()); - return true; - } else { - String searchUrl = SearchResultsBuilder.getSearchQuery(newText); - Fetch.search(MainActivity.this, searchUrl); - return true; - } - } - }); - - searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { - @Override - public boolean onSuggestionSelect(int position) { - return false; - } - - @Override - public boolean onSuggestionClick(int position) { - SearchResult searchResult = currentSuggestions.get(position); - Log.d(TAG, "Opening '" + searchResult.getPageName() + "' from search suggestion."); - wikiViewer.wikiClient.shouldOverrideUrlLoading(wikiViewer, searchResult.getPageUrl()); - hideSearchView(); - return true; - } - }); - return true; - } - - public void hideSearchView() { - searchMenuItem.collapseActionView(); - wikiViewer.requestFocus(); //pass control back to the wikiview - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu, menu); - MenuItem share = menu.findItem(R.id.menu_share); - shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(share); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_share: - WikiPage wikiPage = wikiViewer.getCurrentWebPage(); - if (wikiPage != null) { - Intent intent = AndroidUtils.shareText(wikiPage.getPageTitle(), wikiPage.getPageUrl(), this); - shareActionProvider.setShareIntent(intent); - } - break; - case R.id.refresh: - wikiViewer.onRefresh(); - break; - case R.id.menu_settings: - startActivity(new Intent(this, PreferencesActivity.class)); - break; - case R.id.exit: - finish(); - break; - } - return true; - } - - - @Override - public void onFinish(List results) { - currentSuggestions = results; - setCursorAdapter(currentSuggestions); - } - - private void setCursorAdapter(List currentSuggestions) { - searchView.setSuggestionsAdapter( - SearchResultsAdapter.getCursorAdapter(this, currentSuggestions) - ); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt new file mode 100644 index 0000000..06e1164 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt @@ -0,0 +1,162 @@ +package com.jtmcn.archwiki.viewer + +import android.app.SearchManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v4.view.MenuItemCompat +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.SearchView +import android.support.v7.widget.ShareActionProvider +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.webkit.WebSettings +import com.jtmcn.archwiki.viewer.data.SearchResult +import com.jtmcn.archwiki.viewer.data.getSearchQuery +import com.jtmcn.archwiki.viewer.tasks.Fetch +import com.jtmcn.archwiki.viewer.utils.getFontSize +import com.jtmcn.archwiki.viewer.utils.shareText +import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.toolbar.* +import java.util.* + +class MainActivity : AppCompatActivity() { + val TAG = MainActivity::class.java.simpleName + private var shareActionProvider: ShareActionProvider? = null + private lateinit var searchView: SearchView + private lateinit var searchMenuItem: MenuItem + private var currentSuggestions: List = listOf() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + setSupportActionBar(toolbar) + + wikiViewer.buildView(progressBar, supportActionBar) + + onNewIntent(intent) + } + + override fun onResume() { + super.onResume() + updateWebSettings() + } + + override fun onNewIntent(intent: Intent?) { + if (intent == null) { + return + } + + if (Intent.ACTION_SEARCH == intent.action) { + val query = intent.getStringExtra(SearchManager.QUERY) + wikiViewer.passSearch(query) + hideSearchView() + } else if (Intent.ACTION_VIEW == intent.action) { + val url = intent.dataString + wikiViewer.wikiClient.shouldOverrideUrlLoading(wikiViewer, url) + } + } + + /** + * Update the font size used in the webview. + */ + fun updateWebSettings() { + val webSettings = wikiViewer.settings + val fontSize = getFontSize(this) + + //todo this setting should be changed to a slider, remove deprecated call + // deprecated method must be used until Android API 14 + // https://developer.android.com/reference/android/webkit/WebSettings.TextSize.html#NORMAL + when (fontSize) { + 0 -> webSettings.textSize = WebSettings.TextSize.SMALLEST //50% + 1 -> webSettings.textSize = WebSettings.TextSize.SMALLER //75% + 2 -> webSettings.textSize = WebSettings.TextSize.NORMAL //100% + 3 -> webSettings.textSize = WebSettings.TextSize.LARGER //150% + 4 -> webSettings.textSize = WebSettings.TextSize.LARGEST //200% + } + } + + override fun onPrepareOptionsMenu(menu: Menu): Boolean { + val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager + searchMenuItem = menu.findItem(R.id.menu_search) + searchView = MenuItemCompat.getActionView(searchMenuItem) as SearchView + searchView.setOnQueryTextFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + hideSearchView() + } + } + searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName)) + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + wikiViewer.passSearch(query) + return false + } + + override fun onQueryTextChange(newText: String): Boolean { + if (newText.isEmpty()) { + setCursorAdapter(ArrayList()) + return true + } else { + val searchUrl = getSearchQuery(newText) + Fetch.search(this@MainActivity::onFinish, searchUrl) + return true + } + } + }) + + searchView.setOnSuggestionListener(object : SearchView.OnSuggestionListener { + override fun onSuggestionSelect(position: Int): Boolean { + return false + } + + override fun onSuggestionClick(position: Int): Boolean { + val (pageName, pageUrl) = currentSuggestions[position] + Log.d(TAG, "Opening '$pageName' from search suggestion.") + wikiViewer.wikiClient.shouldOverrideUrlLoading(wikiViewer, pageUrl) + hideSearchView() + return true + } + }) + return true + } + + fun hideSearchView() { + searchMenuItem.collapseActionView() + wikiViewer.requestFocus() //pass control back to the wikiview + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu, menu) + val share = menu.findItem(R.id.menuShare) + shareActionProvider = MenuItemCompat.getActionProvider(share) as ShareActionProvider + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menuShare -> { + val wikiPage = wikiViewer.currentWebPage + if (wikiPage != null) { + val intent = shareText(wikiPage.pageTitle!!, wikiPage.pageUrl, this) + shareActionProvider!!.setShareIntent(intent) + } + } + R.id.refresh -> wikiViewer.onRefresh() + R.id.menu_settings -> startActivity(Intent(this, PreferencesActivity::class.java)) + R.id.exit -> finish() + } + return true + } + + + fun onFinish(results: List) { + currentSuggestions = results + setCursorAdapter(currentSuggestions) + } + + private fun setCursorAdapter(currentSuggestions: List?) { + searchView.suggestionsAdapter = SearchResultsAdapter.getCursorAdapter(this, currentSuggestions!!) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.java b/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.java deleted file mode 100644 index eb62c9c..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.jtmcn.archwiki.viewer; - -import android.os.Bundle; -import android.preference.PreferenceActivity; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.MenuItem; - -/** - * The {@link PreferenceActivity} to change settings for the application. - */ -public class PreferencesActivity extends AppCompatActivity { - public static final String KEY_TEXT_SIZE = "textSize"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - PreferenceManager.setDefaultValues(this, R.xml.prefs, false); - } - - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - setContentView(R.layout.activity_preferences); - - getFragmentManager().beginTransaction() - .replace(R.id.settings_content, new ApplicationPreferenceFragment()) - .commit(); - - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - setTitle(R.string.menu_settings); - - if (getSupportActionBar() != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - onBackPressed(); - break; - } - return true; - } - - /** - * Loads the activities preferences into the fragment. - */ - public static class ApplicationPreferenceFragment extends PreferenceFragment { - @Override - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - addPreferencesFromResource(R.xml.prefs); - } - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt new file mode 100644 index 0000000..1ef69be --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt @@ -0,0 +1,58 @@ +package com.jtmcn.archwiki.viewer + +import android.os.Bundle +import android.preference.PreferenceActivity +import android.preference.PreferenceFragment +import android.preference.PreferenceManager +import android.support.v7.app.AppCompatActivity +import android.view.MenuItem +import kotlinx.android.synthetic.main.toolbar.* + + +/** + * The [PreferenceActivity] to change settings for the application. + */ +class PreferencesActivity : AppCompatActivity() { + companion object { + const val KEY_TEXT_SIZE = "textSize" + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + PreferenceManager.setDefaultValues(this, R.xml.prefs, false) + } + + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + setContentView(R.layout.activity_preferences) + + fragmentManager.beginTransaction() + .replace(R.id.settings_content, ApplicationPreferenceFragment()) + .commit() + + setSupportActionBar(toolbar) + setTitle(R.string.menu_settings) + + with(supportActionBar) { + this?.setDisplayHomeAsUpEnabled(true) + this?.setHomeButtonEnabled(true) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.home -> onBackPressed() + } + return true + } + + /** + * Loads the activities preferences into the fragment. + */ + class ApplicationPreferenceFragment : PreferenceFragment() { + override fun onCreate(bundle: Bundle?) { + super.onCreate(bundle) + addPreferencesFromResource(R.xml.prefs) + } + } +} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.java b/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.java deleted file mode 100644 index f85720c..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.jtmcn.archwiki.viewer; - -import android.app.SearchManager; -import android.content.Context; -import android.database.MatrixCursor; -import android.provider.BaseColumns; -import android.support.v4.widget.CursorAdapter; -import android.support.v4.widget.SimpleCursorAdapter; - -import com.jtmcn.archwiki.viewer.data.SearchResult; - -import java.util.List; - -/** - * Helper for creating a {@link SimpleCursorAdapter} which will - * list the search results for a {@link android.widget.SearchView} - */ -public class SearchResultsAdapter { - private static final String[] columnNames = {BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1}; - private static final String[] from = {SearchManager.SUGGEST_COLUMN_TEXT_1}; - private static final int[] to = new int[]{R.id.url}; - - /** - * Creates a cursor adapter given a {@link List}. - * https://stackoverflow.com/questions/11628172/converting-an-arrayadapter-to-cursoradapter-for-use-in-a-searchview/11628527#11628527 - * - * @param results the results to be placed in the adapter. - * @return the adapter. - */ - public static CursorAdapter getCursorAdapter(Context context, List results) { - int id = 0; - MatrixCursor cursor = new MatrixCursor(columnNames); - for (SearchResult item : results) { - String[] temp = new String[2]; - temp[0] = String.valueOf(id); // "_id" - temp[1] = item.getPageName(); // "title" - - cursor.addRow(temp); - id++; - } - - return new SimpleCursorAdapter( - context, - R.layout.search_suggestions_list_item, - cursor, - from, - to, - CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER - ); - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.kt new file mode 100644 index 0000000..74584c9 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.kt @@ -0,0 +1,49 @@ +package com.jtmcn.archwiki.viewer + +import android.app.SearchManager +import android.content.Context +import android.database.MatrixCursor +import android.provider.BaseColumns +import android.support.v4.widget.CursorAdapter +import android.support.v4.widget.SimpleCursorAdapter + +import com.jtmcn.archwiki.viewer.data.SearchResult + +/** + * Helper for creating a [SimpleCursorAdapter] which will + * list the search results for a [android.widget.SearchView] + */ +object SearchResultsAdapter { + private val columnNames = arrayOf(BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1) + private val from = arrayOf(SearchManager.SUGGEST_COLUMN_TEXT_1) + private val to = intArrayOf(R.id.url) + + /** + * Creates a cursor adapter given a [<]. + * https://stackoverflow.com/questions/11628172/converting-an-arrayadapter-to-cursoradapter-for-use-in-a-searchview/11628527#11628527 + * + * @param results the results to be placed in the adapter. + * @return the adapter. + */ + fun getCursorAdapter(context: Context, results: List): CursorAdapter { + var id = 0 + val cursor = MatrixCursor(columnNames) + for ((pageName) in results) { + val temp = arrayOfNulls(2) + temp[0] = id.toString() // "_id" + temp[1] = pageName // "title" + + cursor.addRow(temp) + id++ + } + + return SimpleCursorAdapter( + context, + R.layout.search_suggestions_list_item, + cursor, + from, + to, + CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER + ) + } +} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java deleted file mode 100644 index 229bf3a..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.jtmcn.archwiki.viewer; - -import android.os.Handler; -import android.support.v7.app.ActionBar; -import android.util.Log; -import android.view.View; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.ProgressBar; - -import com.jtmcn.archwiki.viewer.data.WikiPage; -import com.jtmcn.archwiki.viewer.tasks.Fetch; -import com.jtmcn.archwiki.viewer.tasks.FetchUrl; -import com.jtmcn.archwiki.viewer.utils.AndroidUtils; - -import java.util.HashSet; -import java.util.Set; -import java.util.Stack; - -import static com.jtmcn.archwiki.viewer.Constants.ARCHWIKI_BASE; -import static com.jtmcn.archwiki.viewer.Constants.TEXT_HTML_MIME; -import static com.jtmcn.archwiki.viewer.Constants.UTF_8; - -public class WikiClient extends WebViewClient implements FetchUrl.OnFinish { - public static final String TAG = WikiClient.class.getSimpleName(); - private final WebView webView; - private final Stack webpageStack = new Stack<>(); - private final ProgressBar progressBar; - private final ActionBar actionBar; - private Set loadedUrls = new HashSet<>(); // this is used to see if we should restore the scroll position - private String lastLoadedUrl = null; //https://stackoverflow.com/questions/11601134/android-webview-function-onpagefinished-is-called-twice - - public WikiClient(ProgressBar progressBar, ActionBar actionBar, WebView wikiViewer) { - this.progressBar = progressBar; - this.actionBar = actionBar; - webView = wikiViewer; - } - - /* - * Manage page history - */ - public void addHistory(WikiPage wikiPage) { - if (webpageStack.size() > 0) { - Log.d(TAG, "Saving " + getCurrentWebPage().getPageTitle() + " at " + webView.getScrollY()); - getCurrentWebPage().setScrollPosition(webView.getScrollY()); - } - webpageStack.push(wikiPage); - Log.i(TAG, "Adding page " + wikiPage.getPageTitle() + ". Stack size= " + webpageStack.size()); - } - - /** - * Loads the html from a {@link WikiPage} into the webview. - * - * @param wikiPage the page to be loaded. - */ - public void loadWikiHtml(WikiPage wikiPage) { - webView.loadDataWithBaseURL( - wikiPage.getPageUrl(), - wikiPage.getHtmlString(), - TEXT_HTML_MIME, - UTF_8, - null - ); - - String sub = wikiPage.getPageTitle() != null ? wikiPage.getPageTitle() : "No Title Found"; - setSubtitle(sub); - } - - /** - * Intercept url when clicked. If it's part of the wiki load it here. - * If not, open the device's default browser. - * - * @param view webview being loaded into - * @param url url being loaded - * @return true if should override url loading - */ - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - // deprecated until min api 21 is used - if (url.startsWith(ARCHWIKI_BASE)) { - webView.stopLoading(); - Fetch.page(this, url, true); - showProgress(); - - return false; - } else { - AndroidUtils.openLink(url, view.getContext()); - return true; - } - } - - @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); - final WikiPage currentWebPage = getCurrentWebPage(); - Log.d(TAG, "Calling onPageFinished(view, " + currentWebPage.getPageTitle() + ")"); - // make sure we're loading the current page and that - // this page's url doesn't have an anchor (only on first page load) - if (url.equals(currentWebPage.getPageUrl()) && !url.equals(lastLoadedUrl)) { - if (!isFirstLoad(currentWebPage)) { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - int scrollY = currentWebPage.getScrollPosition(); - Log.d(TAG, "Restoring " + currentWebPage.getPageTitle() + " at " + scrollY); - webView.setScrollY(scrollY); - } - }, 25); - } - - lastLoadedUrl = url; - hideProgress(); - } - } - - private boolean isFirstLoad(WikiPage currentWebPage) { - if (loadedUrls.contains(currentWebPage.getPageUrl())) { - return false; - } else { - loadedUrls.add(currentWebPage.getPageUrl()); - return true; - } - } - - public void showProgress() { - progressBar.setVisibility(View.VISIBLE); - } - - public void hideProgress() { - progressBar.setVisibility(View.GONE); - } - - public void setSubtitle(String title) { - if (actionBar != null) { - actionBar.setSubtitle(title); - } - } - - /** - * Get the number of pages that are in the history. - * - * @return number of pages on the stack. - */ - public int getHistoryStackSize() { - return webpageStack.size(); - } - - /** - * Go back to the last loaded page. - */ - public void goBackHistory() { - WikiPage removed = webpageStack.pop(); - loadedUrls.remove(removed.getPageUrl()); - Log.i(TAG, "Removing " + removed.getPageTitle() + " from stack"); - WikiPage newPage = webpageStack.peek(); - loadWikiHtml(newPage); - } - - /** - * Returns null or the current page. - * - * @return The current page - */ - public WikiPage getCurrentWebPage() { - return webpageStack.size() == 0 ? null : webpageStack.peek(); - } - - @Override - public void onFinish(WikiPage results) { - addHistory(results); - loadWikiHtml(getCurrentWebPage()); - } - - public void refreshPage() { - lastLoadedUrl = null; // set to null if page should restore position, otherwise start at top of page - WikiPage currentWebPage = getCurrentWebPage(); - if (currentWebPage != null) { - final int scrollPosition = currentWebPage.getScrollPosition(); - - String url = currentWebPage.getPageUrl(); - showProgress(); - Fetch.page(new FetchUrl.OnFinish() { - @Override - public void onFinish(WikiPage wikiPage) { - webpageStack.pop(); - webpageStack.push(wikiPage); - wikiPage.setScrollPosition(scrollPosition); - loadWikiHtml(wikiPage); - } - }, url, false); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt new file mode 100644 index 0000000..9a99080 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt @@ -0,0 +1,164 @@ +package com.jtmcn.archwiki.viewer + +import android.os.Handler +import android.support.v7.app.ActionBar +import android.util.Log +import android.view.View +import android.webkit.WebView +import android.webkit.WebViewClient +import android.widget.ProgressBar +import com.jtmcn.archwiki.viewer.data.WikiPage +import com.jtmcn.archwiki.viewer.tasks.Fetch +import com.jtmcn.archwiki.viewer.tasks.OnFinish +import com.jtmcn.archwiki.viewer.utils.openLink +import java.util.* + +class WikiClient(private val progressBar: ProgressBar, private val actionBar: ActionBar?, private val webView: WebView) : WebViewClient() { + val TAG = WikiClient::class.java.simpleName + private val webpageStack = Stack() + private val loadedUrls = HashSet() // this is used to see if we should restore the scroll position + private var lastLoadedUrl: String? = null //https://stackoverflow.com/questions/11601134/android-webview-function-onpagefinished-is-called-twice + + /** + * Get the number of pages that are in the history. + * + * @return number of pages on the stack. + */ + val historyStackSize: Int + get() = webpageStack.size + + /** + * Returns null or the current page. + * + * @return The current page + */ + val currentWebPage: WikiPage? + get() = if (webpageStack.size == 0) null else webpageStack.peek() + + /* + * Manage page history + */ + fun addHistory(wikiPage: WikiPage) { + if (webpageStack.size > 0) { + Log.d(TAG, "Saving " + currentWebPage!!.pageTitle + " at " + webView.scrollY) + currentWebPage!!.scrollPosition = webView.scrollY + } + webpageStack.push(wikiPage) + Log.i(TAG, "Adding page " + wikiPage.pageTitle + ". Stack size= " + webpageStack.size) + } + + /** + * Loads the html from a [WikiPage] into the webview. + * + * @param wikiPage the page to be loaded. + */ + fun loadWikiHtml(wikiPage: WikiPage?) { + webView.loadDataWithBaseURL( + wikiPage!!.pageUrl, + wikiPage.htmlString, + TEXT_HTML_MIME, + UTF_8, null + ) + + val sub = wikiPage.pageTitle ?: "No Title Found" + setSubtitle(sub) + } + + /** + * Intercept url when clicked. If it's part of the wiki load it here. + * If not, open the device's default browser. + * + * @param view webview being loaded into + * @param url url being loaded + * @return true if should override url loading + */ + override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { + // deprecated until min api 21 is used + if (url.startsWith(ARCHWIKI_BASE)) { + webView.stopLoading() + Fetch.page(this::onFinish, url) + showProgress() + + return false + } else { + openLink(url, view.context) + return true + } + } + + override fun onPageFinished(view: WebView, url: String) { + super.onPageFinished(view, url) + val currentWebPage = currentWebPage + Log.d(TAG, "Calling onPageFinished(view, " + currentWebPage!!.pageTitle + ")") + // make sure we're loading the current page and that + // this page's url doesn't have an anchor (only on first page load) + if (url == currentWebPage.pageUrl && url != lastLoadedUrl) { + if (!isFirstLoad(currentWebPage)) { + Handler().postDelayed({ + val scrollY = currentWebPage.scrollPosition + Log.d(TAG, "Restoring " + currentWebPage.pageTitle + " at " + scrollY) + webView.scrollY = scrollY + }, 25) + } + + lastLoadedUrl = url + hideProgress() + } + } + + private fun isFirstLoad(currentWebPage: WikiPage): Boolean { + if (loadedUrls.contains(currentWebPage.pageUrl)) { + return false + } else { + loadedUrls.add(currentWebPage.pageUrl) + return true + } + } + + fun showProgress() { + progressBar.visibility = View.VISIBLE + } + + fun hideProgress() { + progressBar.visibility = View.GONE + } + + fun setSubtitle(title: String) { + actionBar?.setSubtitle(title) + } + + /** + * Go back to the last loaded page. + */ + fun goBackHistory() { + val (pageUrl, pageTitle) = webpageStack.pop() + loadedUrls.remove(pageUrl) + Log.i(TAG, "Removing $pageTitle from stack") + val newPage = webpageStack.peek() + loadWikiHtml(newPage) + } + + fun onFinish(results: WikiPage) { + addHistory(results) + loadWikiHtml(currentWebPage) + } + + fun refreshPage() { + lastLoadedUrl = null // set to null if page should restore position, otherwise start at top of page + val currentWebPage = currentWebPage + if (currentWebPage != null) { + val scrollPosition = currentWebPage.scrollPosition + + val url = currentWebPage.pageUrl + showProgress() + Fetch.page(object : OnFinish { + override fun invoke(wikiPage: WikiPage) { + webpageStack.pop() + webpageStack.push(wikiPage) + wikiPage.scrollPosition = scrollPosition + loadWikiHtml(wikiPage) + } + }, url) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java deleted file mode 100644 index c4b3d33..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.jtmcn.archwiki.viewer; - -import android.content.Context; -import android.os.Build; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.app.ActionBar; -import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; -import android.webkit.WebSettings; -import android.widget.ProgressBar; - -import com.github.takahirom.webview_in_coodinator_layout.NestedWebView; -import com.jtmcn.archwiki.viewer.data.WikiPage; - -import static com.jtmcn.archwiki.viewer.Constants.ARCHWIKI_MAIN; -import static com.jtmcn.archwiki.viewer.Constants.ARCHWIKI_SEARCH_URL; - -public class WikiView extends NestedWebView implements SwipeRefreshLayout.OnRefreshListener { - public static final String TAG = WikiView.class.getSimpleName(); - WikiClient wikiClient; - private Context context; - - public WikiView(Context context, AttributeSet attrs) { - super(context, attrs); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !isInEditMode()) { - //this allows the webview to inject the css (otherwise it blocks it for security reasons) - getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - } - } - - /** - * Initializes the wiki client and loads the main page. - */ - public void buildView(ProgressBar progressBar, ActionBar actionBar) { - wikiClient = new WikiClient(progressBar, actionBar, this); - setWebViewClient(wikiClient); - wikiClient.shouldOverrideUrlLoading(this, ARCHWIKI_MAIN); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK && wikiClient.getHistoryStackSize() > 1) { - Log.i(TAG, "Loading previous page."); - Log.d(TAG, "Position on page currently at " + getScrollY()); - wikiClient.goBackHistory(); - return true; - } else { - Log.d(TAG, "Passing up button press."); - return super.onKeyDown(keyCode, event); - } - } - - /** - * Performs a search against the wiki. - * - * @param query the text to search for. - */ - public void passSearch(String query) { - Log.d(TAG, "Searching for " + query); - String searchUrl = String.format(ARCHWIKI_SEARCH_URL, query); - wikiClient.shouldOverrideUrlLoading(this, searchUrl); - } - - /** - * Returns the current {@link WikiPage} being shown or null. - * - * @return current wiki page being shown. - */ - public WikiPage getCurrentWebPage() { - return wikiClient.getCurrentWebPage(); - } - - @Override - public void onRefresh() { - wikiClient.refreshPage(); - stopLoading(); - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt new file mode 100644 index 0000000..94df872 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt @@ -0,0 +1,70 @@ +package com.jtmcn.archwiki.viewer + +import android.content.Context +import android.os.Build +import android.support.v4.widget.SwipeRefreshLayout +import android.support.v7.app.ActionBar +import android.util.AttributeSet +import android.util.Log +import android.view.KeyEvent +import android.webkit.WebSettings +import android.widget.ProgressBar +import com.github.takahirom.webview_in_coodinator_layout.NestedWebView +import com.jtmcn.archwiki.viewer.data.WikiPage + +class WikiView(context: Context, attrs: AttributeSet) : NestedWebView(context, attrs), SwipeRefreshLayout.OnRefreshListener { + val TAG = WikiView::class.java.simpleName + internal lateinit var wikiClient: WikiClient + + /** + * Returns the current [WikiPage] being shown or null. + * + * @return current wiki page being shown. + */ + val currentWebPage: WikiPage? + get() = wikiClient.currentWebPage + + init { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !isInEditMode) { + //this allows the webview to inject the css (otherwise it blocks it for security reasons) + settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW + } + } + + /** + * Initializes the wiki client and loads the main page. + */ + fun buildView(progressBar: ProgressBar, actionBar: ActionBar?) { + wikiClient = WikiClient(progressBar, actionBar, this) + webViewClient = wikiClient + wikiClient.shouldOverrideUrlLoading(this, ARCHWIKI_MAIN) + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK && wikiClient.historyStackSize > 1) { + Log.i(TAG, "Loading previous page.") + Log.d(TAG, "Position on page currently at $scrollY") + wikiClient.goBackHistory() + return true + } else { + Log.d(TAG, "Passing up button press.") + return super.onKeyDown(keyCode, event) + } + } + + /** + * Performs a search against the wiki. + * + * @param query the text to search for. + */ + fun passSearch(query: String) { + Log.d(TAG, "Searching for $query") + val searchUrl = String.format(ARCHWIKI_SEARCH_URL, query) + wikiClient.shouldOverrideUrlLoading(this, searchUrl) + } + + override fun onRefresh() { + wikiClient.refreshPage() + stopLoading() + } +} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt index bf161f9..7e366a9 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt @@ -1,5 +1,3 @@ -@file:JvmName("SearchResultsBuilder") - package com.jtmcn.archwiki.viewer.data import com.google.gson.JsonArray @@ -10,7 +8,6 @@ import com.jtmcn.archwiki.viewer.ARCHWIKI_BASE * Provides a simple interface to make queries against * and parse data from the arch wiki for searches. */ -private val jsonParser = JsonParser() private const val DEFAULT_LIMIT = 10 /** @@ -34,7 +31,7 @@ fun getSearchQuery(query: String, limit: Int = DEFAULT_LIMIT): String { * @return a parsed list of the results. */ fun parseSearchResults(jsonResult: String): List { - val jsonRoot = jsonParser.parse(jsonResult) + val jsonRoot = JsonParser().parse(jsonResult) if (!jsonRoot.isJsonArray || jsonRoot.asJsonArray.size() != 4) return listOf() val jsonArray = jsonRoot.asJsonArray diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt index 7135e25..d8c4391 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt @@ -1,4 +1,3 @@ -@file:JvmName("WikiPageBuilder") package com.jtmcn.archwiki.viewer.data import com.jtmcn.archwiki.viewer.LOCAL_CSS diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.java b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.java deleted file mode 100644 index 887e8dd..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jtmcn.archwiki.viewer.tasks; - -import android.os.AsyncTask; - -import com.jtmcn.archwiki.viewer.data.SearchResult; -import com.jtmcn.archwiki.viewer.data.SearchResultsBuilder; -import com.jtmcn.archwiki.viewer.data.WikiPage; -import com.jtmcn.archwiki.viewer.data.WikiPageBuilder; - -import java.util.List; - -/** - * Wrapper for {@link FetchUrl} which gives an easy to use interface - * for fetching {@link SearchResult} and {@link WikiPage}. - */ -public class Fetch { - public static final FetchUrl.FetchUrlMapper> SEARCH_RESULTS_MAPPER = - new FetchUrl.FetchUrlMapper>() { - @Override - public List mapTo(String url, StringBuilder stringBuilder) { - return SearchResultsBuilder.parseSearchResults(stringBuilder.toString()); - } - }; - - public static final FetchUrl.FetchUrlMapper WIKIPAGE_MAPPER = - new FetchUrl.FetchUrlMapper() { - @Override - public WikiPage mapTo(String url, StringBuilder sb) { - return WikiPageBuilder.buildPage(url, sb); - } - }; - - private Fetch() { - - } - - /** - * Fetches a {@link List} from the url. - * - * @param onFinish The listener called when search results are ready. - * @param url The url to fetch the search results from. - * @return the async task fetching the data. - */ - public static AsyncTask> search( - FetchUrl.OnFinish> onFinish, - String url - ) { - return new FetchUrl<>(onFinish, SEARCH_RESULTS_MAPPER).execute(url); - } - - /** - * Fetches a {@link WikiPage} from the url. - * - * @param onFinish The listener called when the page is ready. - * @param url The url to fetch the page from. - * @return the async task fetching the data. - */ - public static AsyncTask page( - FetchUrl.OnFinish onFinish, - String url, - boolean caching - ) { - return new FetchUrl<>(onFinish, WIKIPAGE_MAPPER).execute(url); - } - -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt new file mode 100644 index 0000000..542b382 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt @@ -0,0 +1,43 @@ +package com.jtmcn.archwiki.viewer.tasks + +import android.os.AsyncTask +import com.jtmcn.archwiki.viewer.data.SearchResult +import com.jtmcn.archwiki.viewer.data.WikiPage +import com.jtmcn.archwiki.viewer.data.buildPage +import com.jtmcn.archwiki.viewer.data.parseSearchResults + +/** + * Wrapper for [FetchUrl] which gives an easy to use interface + * for fetching [SearchResult] and [WikiPage]. + */ +object Fetch { + val SEARCH_RESULTS_MAPPER: FetchUrlMapper> = { _, sb -> parseSearchResults(sb.toString()) } + + val WIKIPAGE_MAPPER: FetchUrlMapper = { url, sb -> buildPage(url, sb) } + + /** + * Fetches a [<] from the url. + * + * @param onFinish The listener called when search results are ready. + * @param url The url to fetch the search results from. + * @return the async task fetching the data. + */ + fun search( + onFinish: OnFinish>, + url: String + ): AsyncTask> { + return FetchUrl(onFinish, SEARCH_RESULTS_MAPPER).execute(url) + } + + /** + * Fetches a [WikiPage] from the url. + * + * @param onFinish The listener called when the page is ready. + * @param url The url to fetch the page from. + * @return the async task fetching the data. + */ + fun page(onFinish: OnFinish, url: String): AsyncTask { + return FetchUrl(onFinish, WIKIPAGE_MAPPER).execute(url) + } + +} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.java b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.java deleted file mode 100644 index da0db51..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.jtmcn.archwiki.viewer.tasks; - -import android.os.AsyncTask; -import android.util.Log; - -import com.jtmcn.archwiki.viewer.utils.NetworkUtils; - -import java.io.IOException; - -/** - * Fetches a url, maps it to a {@link Result}, and returns it. - * - * @param The type which the fetched url's text will be mapped to. - */ -public class FetchUrl extends AsyncTask { - private static final String TAG = FetchUrl.class.getSimpleName(); - private final OnFinish onFinish; - private final FetchUrlMapper mapper; - private final boolean caching; - - public FetchUrl(OnFinish onFinish, FetchUrlMapper mapper) { - this(onFinish, mapper, true); - } - - /** - * Fetches the first url and notifies the {@link OnFinish} listener. - * - * @param onFinish The function to be called when the result is ready. - * @param mapper The function to map from the url and downloaded page to the desired type. - * @param caching Whether or not to use cached results - */ - public FetchUrl(OnFinish onFinish, FetchUrlMapper mapper, boolean caching) { - this.onFinish = onFinish; - this.mapper = mapper; - this.caching = caching; - } - - @Override - protected Result doInBackground(String... params) { - if (params.length >= 1) { - String url = params[0]; - StringBuilder toAdd = getItem(url); - return mapper.mapTo(url, toAdd); - } - return null; - } - - @Override - protected void onPostExecute(Result values) { - super.onPostExecute(values); - if (onFinish != null) { - onFinish.onFinish(values); - } - } - - /** - * Fetches a url and returns what was downloaded or null - * - * @param url to query - */ - private StringBuilder getItem(String url) { - StringBuilder toReturn; - try { - toReturn = NetworkUtils.fetchURL(url, caching); - } catch (IOException e) { //network exception - Log.w(TAG, "Could not connect to: " + url, e); - toReturn = new StringBuilder(); - } - - return toReturn; - } - - /** - * A listener which is called when {@link Result} is ready. - * - * @param the type of object which has been created. - */ - public interface OnFinish { - void onFinish(Result result); - } - - /** - * Maps the url and fetched text to {@link R} - * - * @param The type which the text will be mapped to. - */ - public interface FetchUrlMapper { - R mapTo(String url, StringBuilder sb); - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt new file mode 100644 index 0000000..564b29b --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt @@ -0,0 +1,59 @@ +package com.jtmcn.archwiki.viewer.tasks + +import android.os.AsyncTask +import android.util.Log +import com.jtmcn.archwiki.viewer.utils.NetworkUtils +import java.io.IOException + + +typealias OnFinish = (Result) -> Unit +typealias FetchUrlMapper = (url: String, sb: StringBuilder) -> Result + +/** + * Fetches a url, maps it to a [Result], and returns it. + * + * @param The type which the fetched url's text will be mapped to. + */ +class FetchUrl +/** + * Fetches the first url and notifies the [OnFinish] listener. + * + * @param onFinish The function to be called when the result is ready. + * @param mapper The function to map from the url and downloaded page to the desired type. + * @param caching Whether or not to use cached results + */ +@JvmOverloads constructor(private val onFinish: OnFinish?, private val mapper: FetchUrlMapper, private val caching: Boolean = true) : AsyncTask() { + private val TAG = FetchUrl::class.java.simpleName + + override fun doInBackground(vararg params: String): Result? { + if (params.isNotEmpty()) { + val url = params[0] + val toAdd = getItem(url) + return mapper.invoke(url, toAdd) + } + return null + } + + override fun onPostExecute(values: Result) { + super.onPostExecute(values) + onFinish?.invoke(values) + } + + /** + * Fetches a url and returns what was downloaded or null + * + * @param url to query + */ + private fun getItem(url: String): StringBuilder { + var toReturn: StringBuilder + try { + toReturn = NetworkUtils.fetchURL(url, caching) + } catch (e: IOException) { //network exception + Log.w(TAG, "Could not connect to: $url", e) + toReturn = StringBuilder() + } + + return toReturn + } + +} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt index d78bbb6..0c68f4d 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt @@ -1,4 +1,3 @@ -@file:JvmName("AndroidUtils") package com.jtmcn.archwiki.viewer.utils import android.app.Activity diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.java b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.java deleted file mode 100644 index e8edbb9..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.jtmcn.archwiki.viewer.utils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; - -/** - * Utility class for performing basic network tasks. - */ -public class NetworkUtils { - private static final Map downloadCache = new HashMap<>(); - - private NetworkUtils() { - - } - - /** - * Fetches a url with caching. - * - * @param stringUrl url to be fetched. - * @return the string that was fetched. - * @throws IOException on network failure. - */ - public static StringBuilder fetchURL(String stringUrl) throws IOException { - return fetchURL(stringUrl, true); - } - - /** - * Fetches a url with optional caching. - * - * @param stringUrl url to be fetched. - * @param useCache whether or not it should return a cached value. - * @return the string that was fetched. - * @throws IOException on network failure. - */ - public static StringBuilder fetchURL(String stringUrl, boolean useCache) throws IOException { - StringBuilder sb = new StringBuilder(""); - URL url = new URL(stringUrl); - if (useCache && downloadCache.containsKey(url)) { - return new StringBuilder(downloadCache.get(url)); - } - HttpURLConnection urlConnection = (HttpURLConnection) url - .openConnection(); - - urlConnection.setReadTimeout(10000); // milliseconds - urlConnection.setConnectTimeout(15000); // milliseconds - urlConnection.setRequestMethod("GET"); - - BufferedReader in = new BufferedReader(new InputStreamReader( - urlConnection.getInputStream()), 8);// buffer 8k - - String line; - String lineSeparator = System.getProperty("line.separator"); - - while ((line = in.readLine()) != null) { - sb.append(line).append(lineSeparator); - } - - urlConnection.disconnect(); - in.close(); - - downloadCache.put(url, new StringBuilder(sb)); - return sb; - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt new file mode 100644 index 0000000..217c7cf --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt @@ -0,0 +1,59 @@ +package com.jtmcn.archwiki.viewer.utils + +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.HttpURLConnection +import java.net.URL +import java.util.* + +/** + * Utility class for performing basic network tasks. + */ +object NetworkUtils { + private val downloadCache = HashMap() + + /** + * Fetches a url with optional caching. + * + * @param stringUrl url to be fetched. + * @param useCache whether or not it should return a cached value. + * @return the string that was fetched. + * @throws IOException on network failure. + */ + @Throws(IOException::class) + @JvmOverloads + @JvmStatic + fun fetchURL(stringUrl: String, useCache: Boolean = true): StringBuilder { + val sb = StringBuilder("") + val url = URL(stringUrl) + if (useCache && downloadCache.containsKey(url)) { + return StringBuilder(downloadCache[url]) + } + val urlConnection = url.openConnection() as HttpURLConnection + + urlConnection.readTimeout = 10000 // milliseconds + urlConnection.connectTimeout = 15000 // milliseconds + urlConnection.requestMethod = "GET" + + val inStream = BufferedReader(InputStreamReader( + urlConnection.inputStream), 8)// buffer 8k + + var line: String? = "" + val lineSeparator = System.getProperty("line.separator") + + while (true) { + line = inStream.readLine() + if (line == null) { + break; + } + sb.append(line).append(lineSeparator) + } + + urlConnection.disconnect() + inStream.close() + + downloadCache[url] = StringBuilder(sb) + return sb + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.java b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.java deleted file mode 100644 index f778506..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jtmcn.archwiki.viewer.utils; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -import com.jtmcn.archwiki.viewer.PreferencesActivity; - -/** - * Created by kevin on 6/7/2017. - */ - -public class SettingsUtils { - - public static int getFontSize(Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - // https://stackoverflow.com/questions/11346916/listpreference-use-string-array-as-entry-and-integer-array-as-entry-values-does - // the value of this preference must be parsed as a string - String fontSizePref = prefs.getString(PreferencesActivity.KEY_TEXT_SIZE, "2"); - return Integer.valueOf(fontSizePref); - - } -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt new file mode 100644 index 0000000..e3c8c7d --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt @@ -0,0 +1,15 @@ +package com.jtmcn.archwiki.viewer.utils + +import android.content.Context +import android.preference.PreferenceManager +import com.jtmcn.archwiki.viewer.PreferencesActivity + +fun getFontSize(context: Context): Int { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + + // https://stackoverflow.com/questions/11346916/listpreference-use-string-array-as-entry-and-integer-array-as-entry-values-does + // the value of this preference must be parsed as a string + val fontSizePref = prefs.getString(PreferencesActivity.KEY_TEXT_SIZE, "2") + return Integer.valueOf(fontSizePref) + +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e2b17f7..2e63016 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,7 +11,7 @@ diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml index 6aa7aaa..01bf5da 100644 --- a/app/src/main/res/layout/toolbar.xml +++ b/app/src/main/res/layout/toolbar.xml @@ -26,7 +26,7 @@ app:layout_scrollFlags="scroll|enterAlways"/> diff --git a/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java b/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java deleted file mode 100644 index 35cb1e5..0000000 --- a/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.jtmcn.archwiki.viewer.data; - -import com.jtmcn.archwiki.viewer.data.SearchResult; -import com.jtmcn.archwiki.viewer.data.SearchResultsBuilder; - -import org.junit.Test; - -import java.util.List; - -import static junit.framework.Assert.assertEquals; - -public class SearchResultsBuilderTest { - private final String realResult = "[\"arch\", [\"Arch-based Distros\", \"Arch-based distributions\", \"Arch-chroot\", \"Arch32\", \"Arch64 FAQ\"],\n" + - "\t[\"\", \"\", \"\", \"\", \"\"],\n" + - "\t[\"https://wiki.archlinux.org/index.php/Arch-based_Distros\", \"https://wiki.archlinux.org/index.php/Arch-based_distributions\", \"https://wiki.archlinux.org/index.php/Arch-chroot\", \"https://wiki.archlinux.org/index.php/Arch32\", \"https://wiki.archlinux.org/index.php/Arch64_FAQ\"]\n" + - "]"; - - @Test - public void parseSearchResults() throws Exception { - List searchResults = SearchResultsBuilder.parseSearchResults(realResult); - - assertEquals("Arch-based Distros",searchResults.get(0).getPageName()); - assertEquals("https://wiki.archlinux.org/index.php/Arch-based_Distros", searchResults.get(0).getPageUrl()); - - assertEquals("Arch-chroot",searchResults.get(2).getPageName()); - assertEquals("https://wiki.archlinux.org/index.php/Arch-chroot", searchResults.get(2).getPageUrl()); - } - - @Test - public void getSearchQuery() { - String query = SearchResultsBuilder.getSearchQuery("arch"); - assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&suggest=true&search=arch&limit=10",query); - - String queryWithLength = SearchResultsBuilder.getSearchQuery("arch",9); - assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&suggest=true&search=arch&limit=9",queryWithLength); - } - - @Test - public void emptySearchCorrectFormat() throws Exception { - String fakeResult = "[\"\", [],\n" + - "\t[],\n" + - "\t[]\n" + - "]"; - List searchResults = SearchResultsBuilder.parseSearchResults(fakeResult); - assertEquals(0,searchResults.size()); - } - - @Test - public void emptyStringSearch() throws Exception { - String fakeResult = ""; - List searchResults = SearchResultsBuilder.parseSearchResults(fakeResult); - assertEquals(0,searchResults.size()); - } -} \ No newline at end of file diff --git a/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.kt b/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.kt new file mode 100644 index 0000000..4809c34 --- /dev/null +++ b/app/src/test/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilderTest.kt @@ -0,0 +1,51 @@ +package com.jtmcn.archwiki.viewer.data + +import junit.framework.Assert.assertEquals +import org.junit.Test + +class SearchResultsBuilderTest { + private val realResult = """["arch", ["Arch-based Distros", "Arch-based distributions", "Arch-chroot", "Arch32", "Arch64 FAQ"], + ["", "", "", "", ""], + ["https://wiki.archlinux.org/index.php/Arch-based_Distros", "https://wiki.archlinux.org/index.php/Arch-based_distributions", "https://wiki.archlinux.org/index.php/Arch-chroot", "https://wiki.archlinux.org/index.php/Arch32", "https://wiki.archlinux.org/index.php/Arch64_FAQ"] +]""" + + @Test + @Throws(Exception::class) + fun parseSearchResults() { + val searchResults = parseSearchResults(realResult) + + assertEquals("Arch-based Distros", searchResults[0].pageName) + assertEquals("https://wiki.archlinux.org/index.php/Arch-based_Distros", searchResults[0].pageUrl) + + assertEquals("Arch-chroot", searchResults[2].pageName) + assertEquals("https://wiki.archlinux.org/index.php/Arch-chroot", searchResults[2].pageUrl) + } + + @Test + fun getSearchQuery() { + val query = getSearchQuery("arch") + assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&suggest=true&search=arch&limit=10", query) + + val queryWithLength = getSearchQuery("arch", 9) + assertEquals("https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&suggest=true&search=arch&limit=9", queryWithLength) + } + + @Test + @Throws(Exception::class) + fun emptySearchCorrectFormat() { + val fakeResult = "[\"\", [],\n" + + "\t[],\n" + + "\t[]\n" + + "]" + val searchResults = parseSearchResults(fakeResult) + assertEquals(0, searchResults.size) + } + + @Test + @Throws(Exception::class) + fun emptyStringSearch() { + val fakeResult = "" + val searchResults = parseSearchResults(fakeResult) + assertEquals(0, searchResults.size) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java b/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java deleted file mode 100644 index e879fe4..0000000 --- a/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.jtmcn.archwiki.viewer.data; - -import com.jtmcn.archwiki.viewer.Constants; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - - -public class WikiPageBuilderTest { - @Test - public void getPageTitle() throws Exception { - String fakeTitle = " fake title..1!@#!@ #!RASDF"; - StringBuilder wrapped = new StringBuilder(WikiPageBuilder.HTML_TITLE_OPEN) - .append(fakeTitle) - .append(WikiPageBuilder.HTML_TITLE_CLOSE); - assertEquals(fakeTitle, WikiPageBuilder.getPageTitle(wrapped)); - } - - @Test - public void getEmptyTitle() throws Exception { - assertNull(WikiPageBuilder.getPageTitle(new StringBuilder(""))); - } - - @Test - public void injectLocalCSS() throws Exception { - StringBuilder head = new StringBuilder(WikiPageBuilder.HTML_HEAD_OPEN) - .append(WikiPageBuilder.HTML_HEAD_CLOSE); - boolean passed = WikiPageBuilder.injectLocalCSS(head, Constants.LOCAL_CSS); - assertTrue(passed); - assertEquals("", - head.toString()); - } - - @Test - public void injectLocalCSSFail() throws Exception { - String fakeHead = " "; - StringBuilder head = new StringBuilder(fakeHead); - boolean passed = WikiPageBuilder.injectLocalCSS(head, Constants.LOCAL_CSS); - assertFalse(passed); - assertEquals(fakeHead, head.toString()); - } - -} \ No newline at end of file diff --git a/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.kt b/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.kt new file mode 100644 index 0000000..e8ca987 --- /dev/null +++ b/app/src/test/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilderTest.kt @@ -0,0 +1,46 @@ +package com.jtmcn.archwiki.viewer.data + +import com.jtmcn.archwiki.viewer.LOCAL_CSS +import org.junit.Assert.* +import org.junit.Test + + +class WikiPageBuilderTest { + @Test + @Throws(Exception::class) + fun getPageTitle() { + val fakeTitle = " fake title..1!@#!@ #!RASDF" + val wrapped = StringBuilder(HTML_TITLE_OPEN) + .append(fakeTitle) + .append(HTML_TITLE_CLOSE) + assertEquals(fakeTitle, getPageTitle(wrapped)) + } + + @Test + @Throws(Exception::class) + fun getEmptyTitle() { + assertNull(getPageTitle(StringBuilder(""))) + } + + @Test + @Throws(Exception::class) + fun injectLocalCSS() { + val head = StringBuilder(HTML_HEAD_OPEN) + .append(HTML_HEAD_CLOSE) + val passed = injectLocalCSS(head, LOCAL_CSS) + assertTrue(passed) + assertEquals("", + head.toString()) + } + + @Test + @Throws(Exception::class) + fun injectLocalCSSFail() { + val fakeHead = " " + val head = StringBuilder(fakeHead) + val passed = injectLocalCSS(head, LOCAL_CSS) + assertFalse(passed) + assertEquals(fakeHead, head.toString()) + } + +} \ No newline at end of file From 4ea1130ac5dec2ca975c91bb3b7deed869d2670a Mon Sep 17 00:00:00 2001 From: Kevin Hinterlong Date: Sun, 5 Aug 2018 11:19:31 -0500 Subject: [PATCH 4/7] Fix back button in preference activity --- .../java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt index 1ef69be..7bdb2ca 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.kt @@ -33,15 +33,12 @@ class PreferencesActivity : AppCompatActivity() { setSupportActionBar(toolbar) setTitle(R.string.menu_settings) - with(supportActionBar) { - this?.setDisplayHomeAsUpEnabled(true) - this?.setHomeButtonEnabled(true) - } + supportActionBar?.setDisplayHomeAsUpEnabled(true) } override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.home -> onBackPressed() + android.R.id.home -> onBackPressed() } return true } From e795c7c298804747182f6c59ea6e348f2fad5ba1 Mon Sep 17 00:00:00 2001 From: Kevin Hinterlong Date: Mon, 6 Aug 2018 20:22:25 -0500 Subject: [PATCH 5/7] Add timber and upgrade compile version --- .idea/misc.xml | 2 +- app/build.gradle | 20 ++++++++++--------- app/src/main/AndroidManifest.xml | 1 + .../archwiki/viewer/ArchwikiViewerApp.kt | 13 ++++++++++++ .../com/jtmcn/archwiki/viewer/MainActivity.kt | 5 ++--- .../com/jtmcn/archwiki/viewer/WikiClient.kt | 13 ++++++------ .../com/jtmcn/archwiki/viewer/WikiView.kt | 11 +++++----- 7 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/ArchwikiViewerApp.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 9e60209..bbda0ff 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -92,7 +92,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 35639c1..00fca0e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,16 +3,16 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 27 - buildToolsVersion "27.0.3" + compileSdkVersion 28 + buildToolsVersion '28.0.0' defaultConfig { versionCode 13 - versionName "1.0.12" - applicationId "com.jtmcn.archwiki.viewer" + versionName '1.0.12' + applicationId 'com.jtmcn.archwiki.viewer' minSdkVersion 15 - targetSdkVersion 27 - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + targetSdkVersion 28 + testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' } buildTypes { @@ -28,15 +28,17 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') // Core libraries - implementation 'com.android.support:design:27.1.1' - implementation 'com.android.support:support-v4:27.1.1' - implementation 'com.android.support:appcompat-v7:27.1.1' + def supportVersion = '28.0.0-beta01' + implementation "com.android.support:design:$supportVersion" + implementation "com.android.support:support-v4:$supportVersion" + implementation "com.android.support:appcompat-v7:$supportVersion" // Other dependecies implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' implementation 'com.google.code.gson:gson:2.8.2' + implementation 'com.jakewharton.timber:timber:4.7.1' testImplementation 'junit:junit:4.12' androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5392b8f..c5da698 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ () private val loadedUrls = HashSet() // this is used to see if we should restore the scroll position private var lastLoadedUrl: String? = null //https://stackoverflow.com/questions/11601134/android-webview-function-onpagefinished-is-called-twice @@ -40,11 +39,11 @@ class WikiClient(private val progressBar: ProgressBar, private val actionBar: Ac */ fun addHistory(wikiPage: WikiPage) { if (webpageStack.size > 0) { - Log.d(TAG, "Saving " + currentWebPage!!.pageTitle + " at " + webView.scrollY) + Timber.d("Saving " + currentWebPage!!.pageTitle + " at " + webView.scrollY) currentWebPage!!.scrollPosition = webView.scrollY } webpageStack.push(wikiPage) - Log.i(TAG, "Adding page " + wikiPage.pageTitle + ". Stack size= " + webpageStack.size) + Timber.i("Adding page " + wikiPage.pageTitle + ". Stack size= " + webpageStack.size) } /** @@ -89,14 +88,14 @@ class WikiClient(private val progressBar: ProgressBar, private val actionBar: Ac override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) val currentWebPage = currentWebPage - Log.d(TAG, "Calling onPageFinished(view, " + currentWebPage!!.pageTitle + ")") + Timber.d("Calling onPageFinished(view, " + currentWebPage!!.pageTitle + ")") // make sure we're loading the current page and that // this page's url doesn't have an anchor (only on first page load) if (url == currentWebPage.pageUrl && url != lastLoadedUrl) { if (!isFirstLoad(currentWebPage)) { Handler().postDelayed({ val scrollY = currentWebPage.scrollPosition - Log.d(TAG, "Restoring " + currentWebPage.pageTitle + " at " + scrollY) + Timber.d("Restoring " + currentWebPage.pageTitle + " at " + scrollY) webView.scrollY = scrollY }, 25) } @@ -133,7 +132,7 @@ class WikiClient(private val progressBar: ProgressBar, private val actionBar: Ac fun goBackHistory() { val (pageUrl, pageTitle) = webpageStack.pop() loadedUrls.remove(pageUrl) - Log.i(TAG, "Removing $pageTitle from stack") + Timber.i("Removing $pageTitle from stack") val newPage = webpageStack.peek() loadWikiHtml(newPage) } diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt index 94df872..d3abdae 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt @@ -5,15 +5,14 @@ import android.os.Build import android.support.v4.widget.SwipeRefreshLayout import android.support.v7.app.ActionBar import android.util.AttributeSet -import android.util.Log import android.view.KeyEvent import android.webkit.WebSettings import android.widget.ProgressBar import com.github.takahirom.webview_in_coodinator_layout.NestedWebView import com.jtmcn.archwiki.viewer.data.WikiPage +import timber.log.Timber class WikiView(context: Context, attrs: AttributeSet) : NestedWebView(context, attrs), SwipeRefreshLayout.OnRefreshListener { - val TAG = WikiView::class.java.simpleName internal lateinit var wikiClient: WikiClient /** @@ -42,12 +41,12 @@ class WikiView(context: Context, attrs: AttributeSet) : NestedWebView(context, a override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { if (keyCode == KeyEvent.KEYCODE_BACK && wikiClient.historyStackSize > 1) { - Log.i(TAG, "Loading previous page.") - Log.d(TAG, "Position on page currently at $scrollY") + Timber.i("Loading previous page.") + Timber.d("Position on page currently at $scrollY") wikiClient.goBackHistory() return true } else { - Log.d(TAG, "Passing up button press.") + Timber.d("Passing up button press.") return super.onKeyDown(keyCode, event) } } @@ -58,7 +57,7 @@ class WikiView(context: Context, attrs: AttributeSet) : NestedWebView(context, a * @param query the text to search for. */ fun passSearch(query: String) { - Log.d(TAG, "Searching for $query") + Timber.d("Searching for $query") val searchUrl = String.format(ARCHWIKI_SEARCH_URL, query) wikiClient.shouldOverrideUrlLoading(this, searchUrl) } From 0e337e09aaa18c7cc189df9620d5b2699d08134a Mon Sep 17 00:00:00 2001 From: Kevin Hinterlong Date: Wed, 19 Dec 2018 13:35:00 -0600 Subject: [PATCH 6/7] Rewrite in Kotlin - Currently the title doesn't stay up to date but everything else works --- .idea/kotlinc.xml | 8 - .idea/misc.xml | 8 +- .idea/vcs.xml | 2 +- app/build.gradle | 11 +- app/proguard-rules.pro | 15 +- .../com/jtmcn/archwiki/viewer/Constants.kt | 4 - .../com/jtmcn/archwiki/viewer/MainActivity.kt | 57 ++++-- .../com/jtmcn/archwiki/viewer/WikiClient.kt | 163 ------------------ .../com/jtmcn/archwiki/viewer/WikiView.kt | 114 ++++++++---- .../viewer/data/SearchResultsBuilder.kt | 12 +- .../jtmcn/archwiki/viewer/data/WikiPage.kt | 6 +- .../archwiki/viewer/data/WikiPageBuilder.kt | 7 +- .../com/jtmcn/archwiki/viewer/tasks/Fetch.kt | 43 ----- .../jtmcn/archwiki/viewer/tasks/FetchUrl.kt | 59 ------- .../archwiki/viewer/utils/AndroidUtils.kt | 3 +- .../archwiki/viewer/utils/NetworkUtils.kt | 57 ++---- .../archwiki/viewer/utils/SettingsUtils.kt | 2 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 19 files changed, 184 insertions(+), 393 deletions(-) delete mode 100644 .idea/kotlinc.xml delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt delete mode 100644 app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index 1d2cf4e..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bbda0ff..459c03a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -72,22 +72,26 @@ diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 00fca0e..ebc5349 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,13 +4,13 @@ apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 28 - buildToolsVersion '28.0.0' + buildToolsVersion '28.0.3' defaultConfig { versionCode 13 versionName '1.0.12' applicationId 'com.jtmcn.archwiki.viewer' - minSdkVersion 15 + minSdkVersion 21 targetSdkVersion 28 testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' } @@ -28,17 +28,18 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') // Core libraries - def supportVersion = '28.0.0-beta01' + def supportVersion = '28.0.0' implementation "com.android.support:design:$supportVersion" implementation "com.android.support:support-v4:$supportVersion" implementation "com.android.support:appcompat-v7:$supportVersion" - // Other dependecies + // Other dependencies implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' - implementation 'com.google.code.gson:gson:2.8.2' + implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.jakewharton.timber:timber:4.7.1' + implementation 'com.squareup.okhttp3:okhttp:3.11.0' testImplementation 'junit:junit:4.12' androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 7cbbc76..4484b02 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -22,4 +22,17 @@ # butterknife -dontwarn butterknife.internal.** -keep class **$$ViewInjector { *; } --keepnames class * { @butterknife.InjectView *;} \ No newline at end of file +-keepnames class * { @butterknife.InjectView *;} + +# OkHttp +# JSR 305 annotations are for embedding nullability information. +-dontwarn javax.annotation.** + +# A resource is loaded with a relative path so the package of this class must be preserved. +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase + +# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. +-dontwarn org.codehaus.mojo.animal_sniffer.* + +# OkHttp platform used only on JVM and when Conscrypt dependency is available. +-dontwarn okhttp3.internal.platform.ConscryptPlatform diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt index 03633d1..00eb3e1 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/Constants.kt @@ -1,9 +1,5 @@ package com.jtmcn.archwiki.viewer -/** - * Various constants used throughout the program. - */ - // format types const val TEXT_HTML_MIME = "text/html" const val TEXT_PLAIN_MIME = "text/plain" diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt index 5f2bd55..0812bf1 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.kt @@ -8,19 +8,26 @@ import android.support.v4.view.MenuItemCompat import android.support.v7.app.AppCompatActivity import android.support.v7.widget.SearchView import android.support.v7.widget.ShareActionProvider +import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.webkit.WebSettings import com.jtmcn.archwiki.viewer.data.SearchResult import com.jtmcn.archwiki.viewer.data.getSearchQuery -import com.jtmcn.archwiki.viewer.tasks.Fetch +import com.jtmcn.archwiki.viewer.data.parseSearchResults +import com.jtmcn.archwiki.viewer.utils.NetworkUtils import com.jtmcn.archwiki.viewer.utils.getFontSize import com.jtmcn.archwiki.viewer.utils.shareText import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.toolbar.* +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Response import timber.log.Timber +import java.io.IOException import java.util.* + class MainActivity : AppCompatActivity() { private var shareActionProvider: ShareActionProvider? = null private lateinit var searchView: SearchView @@ -54,7 +61,7 @@ class MainActivity : AppCompatActivity() { hideSearchView() } else if (Intent.ACTION_VIEW == intent.action) { val url = intent.dataString - wikiViewer.wikiClient.shouldOverrideUrlLoading(wikiViewer, url) + wikiViewer.loadUrl(url) } } @@ -68,12 +75,13 @@ class MainActivity : AppCompatActivity() { //todo this setting should be changed to a slider, remove deprecated call // deprecated method must be used until Android API 14 // https://developer.android.com/reference/android/webkit/WebSettings.TextSize.html#NORMAL - when (fontSize) { - 0 -> webSettings.textSize = WebSettings.TextSize.SMALLEST //50% - 1 -> webSettings.textSize = WebSettings.TextSize.SMALLER //75% - 2 -> webSettings.textSize = WebSettings.TextSize.NORMAL //100% - 3 -> webSettings.textSize = WebSettings.TextSize.LARGER //150% - 4 -> webSettings.textSize = WebSettings.TextSize.LARGEST //200% + webSettings.textSize = when (fontSize) { + 0 -> WebSettings.TextSize.SMALLEST //50% + 1 -> WebSettings.TextSize.SMALLER //75% + 2 -> WebSettings.TextSize.NORMAL //100% + 3 -> WebSettings.TextSize.LARGER //150% + 4 -> WebSettings.TextSize.LARGEST //200% + else -> WebSettings.TextSize.NORMAL } } @@ -99,7 +107,19 @@ class MainActivity : AppCompatActivity() { return true } else { val searchUrl = getSearchQuery(newText) - Fetch.search(this@MainActivity::onFinish, searchUrl) + NetworkUtils.fetchURL(searchUrl, object : Callback { + override fun onFailure(call: Call?, e: IOException?) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onResponse(call: Call?, response: Response?) { + runOnUiThread { + val searchResults = parseSearchResults(response?.body()?.string() + ?: "") + onFinish(searchResults) + } + } + }) return true } } @@ -113,7 +133,7 @@ class MainActivity : AppCompatActivity() { override fun onSuggestionClick(position: Int): Boolean { val (pageName, pageUrl) = currentSuggestions[position] Timber.d("Opening '$pageName' from search suggestion.") - wikiViewer.wikiClient.shouldOverrideUrlLoading(wikiViewer, pageUrl) + wikiViewer.loadUrl(pageUrl) hideSearchView() return true } @@ -158,4 +178,21 @@ class MainActivity : AppCompatActivity() { private fun setCursorAdapter(currentSuggestions: List?) { searchView.suggestionsAdapter = SearchResultsAdapter.getCursorAdapter(this, currentSuggestions!!) } + + override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { + if (event.action == KeyEvent.ACTION_DOWN) { + when (keyCode) { + KeyEvent.KEYCODE_BACK -> { + if (wikiViewer.canGoBack()) { + wikiViewer.goBack() + } else { + finish() + } + return true + } + } + + } + return super.onKeyDown(keyCode, event) + } } \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt deleted file mode 100644 index 60dc384..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiClient.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.jtmcn.archwiki.viewer - -import android.os.Handler -import android.support.v7.app.ActionBar -import android.view.View -import android.webkit.WebView -import android.webkit.WebViewClient -import android.widget.ProgressBar -import com.jtmcn.archwiki.viewer.data.WikiPage -import com.jtmcn.archwiki.viewer.tasks.Fetch -import com.jtmcn.archwiki.viewer.tasks.OnFinish -import com.jtmcn.archwiki.viewer.utils.openLink -import timber.log.Timber -import java.util.* - -class WikiClient(private val progressBar: ProgressBar, private val actionBar: ActionBar?, private val webView: WebView) : WebViewClient() { - private val webpageStack = Stack() - private val loadedUrls = HashSet() // this is used to see if we should restore the scroll position - private var lastLoadedUrl: String? = null //https://stackoverflow.com/questions/11601134/android-webview-function-onpagefinished-is-called-twice - - /** - * Get the number of pages that are in the history. - * - * @return number of pages on the stack. - */ - val historyStackSize: Int - get() = webpageStack.size - - /** - * Returns null or the current page. - * - * @return The current page - */ - val currentWebPage: WikiPage? - get() = if (webpageStack.size == 0) null else webpageStack.peek() - - /* - * Manage page history - */ - fun addHistory(wikiPage: WikiPage) { - if (webpageStack.size > 0) { - Timber.d("Saving " + currentWebPage!!.pageTitle + " at " + webView.scrollY) - currentWebPage!!.scrollPosition = webView.scrollY - } - webpageStack.push(wikiPage) - Timber.i("Adding page " + wikiPage.pageTitle + ". Stack size= " + webpageStack.size) - } - - /** - * Loads the html from a [WikiPage] into the webview. - * - * @param wikiPage the page to be loaded. - */ - fun loadWikiHtml(wikiPage: WikiPage?) { - webView.loadDataWithBaseURL( - wikiPage!!.pageUrl, - wikiPage.htmlString, - TEXT_HTML_MIME, - UTF_8, null - ) - - val sub = wikiPage.pageTitle ?: "No Title Found" - setSubtitle(sub) - } - - /** - * Intercept url when clicked. If it's part of the wiki load it here. - * If not, open the device's default browser. - * - * @param view webview being loaded into - * @param url url being loaded - * @return true if should override url loading - */ - override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { - // deprecated until min api 21 is used - if (url.startsWith(ARCHWIKI_BASE)) { - webView.stopLoading() - Fetch.page(this::onFinish, url) - showProgress() - - return false - } else { - openLink(url, view.context) - return true - } - } - - override fun onPageFinished(view: WebView, url: String) { - super.onPageFinished(view, url) - val currentWebPage = currentWebPage - Timber.d("Calling onPageFinished(view, " + currentWebPage!!.pageTitle + ")") - // make sure we're loading the current page and that - // this page's url doesn't have an anchor (only on first page load) - if (url == currentWebPage.pageUrl && url != lastLoadedUrl) { - if (!isFirstLoad(currentWebPage)) { - Handler().postDelayed({ - val scrollY = currentWebPage.scrollPosition - Timber.d("Restoring " + currentWebPage.pageTitle + " at " + scrollY) - webView.scrollY = scrollY - }, 25) - } - - lastLoadedUrl = url - hideProgress() - } - } - - private fun isFirstLoad(currentWebPage: WikiPage): Boolean { - if (loadedUrls.contains(currentWebPage.pageUrl)) { - return false - } else { - loadedUrls.add(currentWebPage.pageUrl) - return true - } - } - - fun showProgress() { - progressBar.visibility = View.VISIBLE - } - - fun hideProgress() { - progressBar.visibility = View.GONE - } - - fun setSubtitle(title: String) { - actionBar?.setSubtitle(title) - } - - /** - * Go back to the last loaded page. - */ - fun goBackHistory() { - val (pageUrl, pageTitle) = webpageStack.pop() - loadedUrls.remove(pageUrl) - Timber.i("Removing $pageTitle from stack") - val newPage = webpageStack.peek() - loadWikiHtml(newPage) - } - - fun onFinish(results: WikiPage) { - addHistory(results) - loadWikiHtml(currentWebPage) - } - - fun refreshPage() { - lastLoadedUrl = null // set to null if page should restore position, otherwise start at top of page - val currentWebPage = currentWebPage - if (currentWebPage != null) { - val scrollPosition = currentWebPage.scrollPosition - - val url = currentWebPage.pageUrl - showProgress() - Fetch.page(object : OnFinish { - override fun invoke(wikiPage: WikiPage) { - webpageStack.pop() - webpageStack.push(wikiPage) - wikiPage.scrollPosition = scrollPosition - loadWikiHtml(wikiPage) - } - }, url) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt index d3abdae..e504356 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.kt @@ -1,69 +1,121 @@ package com.jtmcn.archwiki.viewer import android.content.Context -import android.os.Build +import android.graphics.Bitmap +import android.os.Handler +import android.os.Looper import android.support.v4.widget.SwipeRefreshLayout import android.support.v7.app.ActionBar import android.util.AttributeSet -import android.view.KeyEvent +import android.view.View +import android.webkit.WebResourceRequest import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebViewClient import android.widget.ProgressBar import com.github.takahirom.webview_in_coodinator_layout.NestedWebView import com.jtmcn.archwiki.viewer.data.WikiPage +import com.jtmcn.archwiki.viewer.data.buildPage +import com.jtmcn.archwiki.viewer.utils.NetworkUtils +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Response import timber.log.Timber +import java.io.IOException -class WikiView(context: Context, attrs: AttributeSet) : NestedWebView(context, attrs), SwipeRefreshLayout.OnRefreshListener { - internal lateinit var wikiClient: WikiClient +class WikiView(context: Context, attrs: AttributeSet) : NestedWebView(context, attrs), SwipeRefreshLayout.OnRefreshListener { /** * Returns the current [WikiPage] being shown or null. * * @return current wiki page being shown. */ val currentWebPage: WikiPage? - get() = wikiClient.currentWebPage + get() = null // wikiClient.currentWebPage - init { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !isInEditMode) { - //this allows the webview to inject the css (otherwise it blocks it for security reasons) - settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW - } - } + var actionBar: ActionBar? = null /** * Initializes the wiki client and loads the main page. */ fun buildView(progressBar: ProgressBar, actionBar: ActionBar?) { - wikiClient = WikiClient(progressBar, actionBar, this) - webViewClient = wikiClient - wikiClient.shouldOverrideUrlLoading(this, ARCHWIKI_MAIN) + this.actionBar = actionBar + webViewClient = object : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + progressBar.visibility = View.VISIBLE + } + + override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { + val url = request?.url + var urlString = url?.toString() + if (urlString == null || !isArchWikiDomain(urlString) && !urlString.startsWith("file://")) { + return super.shouldOverrideUrlLoading(view, request) + } + + if (urlString.startsWith("file://")) { + urlString = ARCHWIKI_BASE + "index.php/${url?.lastPathSegment?.toString()}" + } + loadArchWikiPage(urlString) + return true + } + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + progressBar.visibility = View.GONE + + actionBar?.subtitle = view?.title + } + } + + loadArchWikiPage(ARCHWIKI_MAIN) } - override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { - if (keyCode == KeyEvent.KEYCODE_BACK && wikiClient.historyStackSize > 1) { - Timber.i("Loading previous page.") - Timber.d("Position on page currently at $scrollY") - wikiClient.goBackHistory() - return true - } else { - Timber.d("Passing up button press.") - return super.onKeyDown(keyCode, event) + fun isArchWikiDomain(url: String? = getUrl()): Boolean { + return url?.startsWith(ARCHWIKI_BASE) == true + } + + private fun loadArchWikiPage(url: String) { + Timber.d("Loading new wikipage $url") + fetchPage(url) { + Timber.d("page url is ${it.pageUrl}") + loadDataWithBaseURL( + "file:///android_asset/", + it.html, + TEXT_HTML_MIME, + UTF_8, + it.pageUrl + ) + actionBar?.subtitle = it.pageTitle } } - /** - * Performs a search against the wiki. - * - * @param query the text to search for. - */ + + private fun fetchPage(url: String, onFinish: (WikiPage) -> Unit = {}) { + NetworkUtils.fetchURL(url, object : Callback { + override fun onFailure(call: Call?, e: IOException?) { + TODO("not implemented") + } + + override fun onResponse(call: Call?, response: Response?) { + Handler(Looper.getMainLooper()).post { + val html = StringBuilder(response?.body()?.string()) + response?.body()?.close() + + val wikiPage = buildPage(url, html) + onFinish(wikiPage) + } + } + }) + } + fun passSearch(query: String) { Timber.d("Searching for $query") val searchUrl = String.format(ARCHWIKI_SEARCH_URL, query) - wikiClient.shouldOverrideUrlLoading(this, searchUrl) + loadUrl(searchUrl) } override fun onRefresh() { - wikiClient.refreshPage() - stopLoading() + reload() } } diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt index 7e366a9..cc62ccb 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/SearchResultsBuilder.kt @@ -4,12 +4,6 @@ import com.google.gson.JsonArray import com.google.gson.JsonParser import com.jtmcn.archwiki.viewer.ARCHWIKI_BASE -/** - * Provides a simple interface to make queries against - * and parse data from the arch wiki for searches. - */ -private const val DEFAULT_LIMIT = 10 - /** * Builds a string url to fetch search results. * @@ -17,11 +11,11 @@ private const val DEFAULT_LIMIT = 10 * @param limit the maximum number of results to retrieve. * @return a url to fetch. */ -@JvmOverloads -fun getSearchQuery(query: String, limit: Int = DEFAULT_LIMIT): String { +fun getSearchQuery(query: String, limit: Int = 10): String { return "${ARCHWIKI_BASE}api.php?" + "action=opensearch&format=json&formatversion=2&namespace=0&suggest=true" + - "&search=${query}&limit=${limit}" + "&search=$query" + + "&limit=$limit" } /** diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt index dfbec82..e93db6d 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.kt @@ -3,10 +3,10 @@ package com.jtmcn.archwiki.viewer.data /** * Store the url, title, and html of a page on the wiki. * - * @param pageUrl the string url on the wiki. + * @param pageUrl the url on the wiki. * @param pageTitle the title of the page on the wiki. - * @param htmlString the html which should be shown to represent the page. + * @param html the html which should be shown to represent the page. */ -data class WikiPage(val pageUrl: String, val pageTitle: String?, val htmlString: String) { +data class WikiPage(val pageUrl: String, val pageTitle: String?, val html: String) { var scrollPosition = 0 } diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt index d8c4391..88acb20 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPageBuilder.kt @@ -12,7 +12,8 @@ const val HTML_HEAD_OPEN = "" const val HTML_HEAD_CLOSE = "" const val HTML_TITLE_OPEN = "" const val HTML_TITLE_CLOSE = "" -private const val HEAD_TO_INJECT = "" + "" +private const val HEAD_TO_INJECT = "" + + "" private const val DEFAULT_TITLE = " - ArchWiki" /** @@ -38,7 +39,7 @@ fun buildPage(stringUrl: String, html: StringBuilder): WikiPage { fun getPageTitle(htmlString: StringBuilder): String? { val titleStart = htmlString.indexOf(HTML_TITLE_OPEN) + HTML_TITLE_OPEN.length val titleEnd = htmlString.indexOf(HTML_TITLE_CLOSE, titleStart) - if (titleStart in 1 until titleEnd) { // if there is an html title block + if (titleStart > 0 && titleStart < titleEnd) { // if there is an html title block val title = htmlString.substring(titleStart, titleEnd) return title.replace(DEFAULT_TITLE, "") // drop DEFAULT_TITLE from page title } @@ -57,7 +58,7 @@ fun injectLocalCSS(htmlString: StringBuilder, localCSSFilePath: String): Boolean val headStart = htmlString.indexOf(HTML_HEAD_OPEN) + HTML_HEAD_OPEN.length val headEnd = htmlString.indexOf(HTML_HEAD_CLOSE, headStart) - if (headStart in 1..headEnd) { + if (headStart > 0 && headStart < headEnd) { val injectedHeadHtml = String.format(HEAD_TO_INJECT, localCSSFilePath) htmlString.replace(headStart, headEnd, injectedHeadHtml) return true diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt deleted file mode 100644 index 542b382..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.jtmcn.archwiki.viewer.tasks - -import android.os.AsyncTask -import com.jtmcn.archwiki.viewer.data.SearchResult -import com.jtmcn.archwiki.viewer.data.WikiPage -import com.jtmcn.archwiki.viewer.data.buildPage -import com.jtmcn.archwiki.viewer.data.parseSearchResults - -/** - * Wrapper for [FetchUrl] which gives an easy to use interface - * for fetching [SearchResult] and [WikiPage]. - */ -object Fetch { - val SEARCH_RESULTS_MAPPER: FetchUrlMapper> = { _, sb -> parseSearchResults(sb.toString()) } - - val WIKIPAGE_MAPPER: FetchUrlMapper = { url, sb -> buildPage(url, sb) } - - /** - * Fetches a [<] from the url. - * - * @param onFinish The listener called when search results are ready. - * @param url The url to fetch the search results from. - * @return the async task fetching the data. - */ - fun search( - onFinish: OnFinish>, - url: String - ): AsyncTask> { - return FetchUrl(onFinish, SEARCH_RESULTS_MAPPER).execute(url) - } - - /** - * Fetches a [WikiPage] from the url. - * - * @param onFinish The listener called when the page is ready. - * @param url The url to fetch the page from. - * @return the async task fetching the data. - */ - fun page(onFinish: OnFinish, url: String): AsyncTask { - return FetchUrl(onFinish, WIKIPAGE_MAPPER).execute(url) - } - -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt deleted file mode 100644 index 564b29b..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.jtmcn.archwiki.viewer.tasks - -import android.os.AsyncTask -import android.util.Log -import com.jtmcn.archwiki.viewer.utils.NetworkUtils -import java.io.IOException - - -typealias OnFinish = (Result) -> Unit -typealias FetchUrlMapper = (url: String, sb: StringBuilder) -> Result - -/** - * Fetches a url, maps it to a [Result], and returns it. - * - * @param The type which the fetched url's text will be mapped to. - */ -class FetchUrl -/** - * Fetches the first url and notifies the [OnFinish] listener. - * - * @param onFinish The function to be called when the result is ready. - * @param mapper The function to map from the url and downloaded page to the desired type. - * @param caching Whether or not to use cached results - */ -@JvmOverloads constructor(private val onFinish: OnFinish?, private val mapper: FetchUrlMapper, private val caching: Boolean = true) : AsyncTask() { - private val TAG = FetchUrl::class.java.simpleName - - override fun doInBackground(vararg params: String): Result? { - if (params.isNotEmpty()) { - val url = params[0] - val toAdd = getItem(url) - return mapper.invoke(url, toAdd) - } - return null - } - - override fun onPostExecute(values: Result) { - super.onPostExecute(values) - onFinish?.invoke(values) - } - - /** - * Fetches a url and returns what was downloaded or null - * - * @param url to query - */ - private fun getItem(url: String): StringBuilder { - var toReturn: StringBuilder - try { - toReturn = NetworkUtils.fetchURL(url, caching) - } catch (e: IOException) { //network exception - Log.w(TAG, "Could not connect to: $url", e) - toReturn = StringBuilder() - } - - return toReturn - } - -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt index 0c68f4d..70ad68f 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.kt @@ -34,6 +34,5 @@ fun shareText(title: String, url: String, activity: Activity): Intent { * @param context The context needed to start the intent. */ fun openLink(url: String, context: Context) { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - context.startActivity(intent) + context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) } diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt index 217c7cf..9f1bc0c 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/NetworkUtils.kt @@ -1,59 +1,26 @@ package com.jtmcn.archwiki.viewer.utils -import java.io.BufferedReader +import okhttp3.Callback +import okhttp3.OkHttpClient +import okhttp3.Request import java.io.IOException -import java.io.InputStreamReader -import java.net.HttpURLConnection -import java.net.URL -import java.util.* + /** * Utility class for performing basic network tasks. */ object NetworkUtils { - private val downloadCache = HashMap() + private val client = OkHttpClient.Builder().build() + private val builder = Request.Builder() /** * Fetches a url with optional caching. * - * @param stringUrl url to be fetched. - * @param useCache whether or not it should return a cached value. - * @return the string that was fetched. - * @throws IOException on network failure. + * @param url url to be fetched. + * @param cb callback to handle result */ - @Throws(IOException::class) - @JvmOverloads - @JvmStatic - fun fetchURL(stringUrl: String, useCache: Boolean = true): StringBuilder { - val sb = StringBuilder("") - val url = URL(stringUrl) - if (useCache && downloadCache.containsKey(url)) { - return StringBuilder(downloadCache[url]) - } - val urlConnection = url.openConnection() as HttpURLConnection - - urlConnection.readTimeout = 10000 // milliseconds - urlConnection.connectTimeout = 15000 // milliseconds - urlConnection.requestMethod = "GET" - - val inStream = BufferedReader(InputStreamReader( - urlConnection.inputStream), 8)// buffer 8k - - var line: String? = "" - val lineSeparator = System.getProperty("line.separator") - - while (true) { - line = inStream.readLine() - if (line == null) { - break; - } - sb.append(line).append(lineSeparator) - } - - urlConnection.disconnect() - inStream.close() - - downloadCache[url] = StringBuilder(sb) - return sb - } + fun fetchURL(url: String, cb: Callback) = + client.newCall( + builder.url(url).build() + ).enqueue(cb) } \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt index e3c8c7d..80c2c6e 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.kt @@ -7,7 +7,7 @@ import com.jtmcn.archwiki.viewer.PreferencesActivity fun getFontSize(context: Context): Int { val prefs = PreferenceManager.getDefaultSharedPreferences(context) - // https://stackoverflow.com/questions/11346916/listpreference-use-string-array-as-entry-and-integer-array-as-entry-values-does + // https://stackoverflow.com/q/11346916 // the value of this preference must be parsed as a string val fontSizePref = prefs.getString(PreferencesActivity.KEY_TEXT_SIZE, "2") return Integer.valueOf(fontSizePref) diff --git a/build.gradle b/build.gradle index 4f95ce4..8f1503e 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.3' + classpath 'com.android.tools.build:gradle:3.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 008ec5e..b4be88f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Aug 04 13:54:57 CDT 2018 +#Tue Dec 18 12:39:56 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip From 22a3e3f4206ac0e9d7d7d4d0b1c444b51bf277da Mon Sep 17 00:00:00 2001 From: zhelemysh <81683871+zhelemysh@users.noreply.github.com> Date: Wed, 31 Mar 2021 13:27:38 +0300 Subject: [PATCH 7/7] Updated ru (Russian) translation Commit made via Stringlate --- app/src/main/res/values-ru/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/src/main/res/values-ru/strings.xml diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000..a1fa7b6 --- /dev/null +++ b/app/src/main/res/values-ru/strings.xml @@ -0,0 +1,10 @@ + + + ArchWiki Viewer + Параметры + Поделиться ссылкой + Поиск + Выход + Размер шрифта + Обновить +