diff --git a/app/build.gradle b/app/build.gradle index 010f64d..fe44829 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.jtmcn.archwiki.viewer" minSdkVersion 15 targetSdkVersion 25 - versionCode 8 - versionName "1.0.7" + versionCode 12 + versionName "1.0.11" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -27,7 +27,13 @@ dependencies { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:support-v4:25.3.1' + compile 'com.jakewharton:butterknife:8.6.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' + compile 'com.google.code.gson:gson:2.7' + + 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' } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 24873f9..7cbbc76 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -13,3 +13,13 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #}-keepnames class org.glassfish.** { *; } + +-keep class android.support.v7.internal.** { *; } +-keep interface android.support.v7.internal.** { *; } +-keep class android.support.v7.** { *; } +-keep interface android.support.v7.** { *; } + +# butterknife +-dontwarn butterknife.internal.** +-keep class **$$ViewInjector { *; } +-keepnames class * { @butterknife.InjectView *;} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 39a077e..5392b8f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,10 +38,8 @@ android:resource="@xml/searchable" /> - - diff --git a/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java b/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java new file mode 100644 index 0000000..01836bc --- /dev/null +++ b/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 takahirom + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.takahirom.webview_in_coodinator_layout; + +import android.content.Context; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.NestedScrollingChild; +import android.support.v4.view.NestedScrollingChildHelper; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.webkit.WebView; + +public class NestedWebView extends WebView implements NestedScrollingChild { + private final int[] mScrollOffset = new int[2]; + private final int[] mScrollConsumed = new int[2]; + private int mLastY; + private int mNestedOffsetY; + private NestedScrollingChildHelper mChildHelper; + + public NestedWebView(Context context) { + this(context, null); + } + + public NestedWebView(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.webViewStyle); + } + + public NestedWebView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mChildHelper = new NestedScrollingChildHelper(this); + setNestedScrollingEnabled(true); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean returnValue = false; + + MotionEvent event = MotionEvent.obtain(ev); + final int action = MotionEventCompat.getActionMasked(event); + if (action == MotionEvent.ACTION_DOWN) { + mNestedOffsetY = 0; + } + int eventY = (int) event.getY(); + event.offsetLocation(0, mNestedOffsetY); + switch (action) { + case MotionEvent.ACTION_MOVE: + int deltaY = mLastY - eventY; + // NestedPreScroll + if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { + deltaY -= mScrollConsumed[1]; + mLastY = eventY - mScrollOffset[1]; + event.offsetLocation(0, -mScrollOffset[1]); + mNestedOffsetY += mScrollOffset[1]; + } + returnValue = super.onTouchEvent(event); + + // NestedScroll + if (dispatchNestedScroll(0, mScrollOffset[1], 0, deltaY, mScrollOffset)) { + event.offsetLocation(0, mScrollOffset[1]); + mNestedOffsetY += mScrollOffset[1]; + mLastY -= mScrollOffset[1]; + } + break; + case MotionEvent.ACTION_DOWN: + returnValue = super.onTouchEvent(event); + mLastY = eventY; + // start NestedScroll + startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + returnValue = super.onTouchEvent(event); + // end NestedScroll + stopNestedScroll(); + break; + } + return returnValue; + } + + @Override + public boolean isNestedScrollingEnabled() { + return mChildHelper.isNestedScrollingEnabled(); + } + + // Nested Scroll implements + @Override + public void setNestedScrollingEnabled(boolean enabled) { + mChildHelper.setNestedScrollingEnabled(enabled); + } + + @Override + public boolean startNestedScroll(int axes) { + return mChildHelper.startNestedScroll(axes); + } + + @Override + public void stopNestedScroll() { + mChildHelper.stopNestedScroll(); + } + + @Override + public boolean hasNestedScrollingParent() { + return mChildHelper.hasNestedScrollingParent(); + } + + @Override + public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, + int[] offsetInWindow) { + return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow); + } + + @Override + public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { + return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); + } + + @Override + public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { + return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); + } + + @Override + public boolean dispatchNestedPreFling(float velocityX, float velocityY) { + return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java b/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java index 8630ee0..5105bdd 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/MainActivity.java @@ -1,21 +1,20 @@ package com.jtmcn.archwiki.viewer; -import android.app.Activity; import android.app.SearchManager; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; +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.WebChromeClient; import android.webkit.WebSettings; import android.widget.ProgressBar; -import android.widget.SearchView; -import android.widget.Toast; import com.jtmcn.archwiki.viewer.data.SearchResult; import com.jtmcn.archwiki.viewer.data.SearchResultsBuilder; @@ -23,27 +22,34 @@ 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; -public class MainActivity extends Activity implements FetchUrl.OnFinish> { +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 WikiView wikiViewer; private List currentSuggestions; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.wiki_layout); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); - wikiViewer = (WikiView) findViewById(R.id.wvMain); - ProgressBar progressBar = (ProgressBar) findViewById(R.id.ProgressBar); + setSupportActionBar(toolbar); - wikiViewer.buildView(progressBar, getActionBar()); + ProgressBar progressBar = ButterKnife.findById(this, R.id.progress_bar); + wikiViewer.buildView(progressBar, getSupportActionBar()); - wikiViewer.setWebChromeClient(new WebChromeClient()); handleIntent(getIntent()); } @@ -78,15 +84,7 @@ private void handleIntent(Intent intent) { */ public void updateWebSettings() { WebSettings webSettings = wikiViewer.getSettings(); - - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - - // 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 - // todo make a settings utils class to wrap this - String fontSizePref = prefs.getString(WikiPrefsActivity.KEY_TEXT_SIZE, "2"); - int fontSize = Integer.valueOf(fontSizePref); + 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 @@ -114,7 +112,7 @@ public void updateWebSettings() { public boolean onPrepareOptionsMenu(Menu menu) { SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); searchMenuItem = menu.findItem(R.id.menu_search); - final SearchView searchView = (SearchView) searchMenuItem.getActionView(); + searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem); searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { @@ -124,7 +122,6 @@ public void onFocusChange(View v, boolean hasFocus) { } }); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); - this.searchView = searchView; searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { @@ -135,12 +132,12 @@ public boolean onQueryTextSubmit(String query) { @Override public boolean onQueryTextChange(String newText) { if (newText.isEmpty()) { - searchView.setSuggestionsAdapter(null); + setCursorAdapter(new ArrayList()); return true; } else { String searchUrl = SearchResultsBuilder.getSearchQuery(newText); Fetch.search(MainActivity.this, searchUrl); - return false; + return true; } } }); @@ -170,24 +167,25 @@ public void hideSearchView() { @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.options, 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_settings: - startActivity(new Intent(this, WikiPrefsActivity.class)); - break; case R.id.menu_share: WikiPage wikiPage = wikiViewer.getCurrentWebPage(); - if (wikiPage != null) { - AndroidUtils.shareText(wikiPage.getPageTitle(), wikiPage.getPageUrl(), this); - } else { //// TODO: 5/14/2017 either make sure this never happens or localize the strings - Log.w(TAG, "Failed to share current page " + wikiViewer.getUrl()); - Toast.makeText(this, "Sorry, can't share this page!", Toast.LENGTH_SHORT).show(); - } + 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(); @@ -200,6 +198,12 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onFinish(List results) { currentSuggestions = results; - searchView.setSuggestionsAdapter(SearchResultsAdapter.getCursorAdapter(this, currentSuggestions)); + 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/PreferencesActivity.java b/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.java new file mode 100644 index 0000000..eb62c9c --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/PreferencesActivity.java @@ -0,0 +1,63 @@ +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/SearchResultsAdapter.java b/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.java index 44cc70a..f85720c 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/SearchResultsAdapter.java @@ -1,9 +1,11 @@ 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.widget.SimpleCursorAdapter; +import android.support.v4.widget.SimpleCursorAdapter; import com.jtmcn.archwiki.viewer.data.SearchResult; @@ -14,9 +16,9 @@ * list the search results for a {@link android.widget.SearchView} */ public class SearchResultsAdapter { - private static final String[] columnNames = {"_id", "title"}; - private static final String[] from = {"title"}; - private static final int[] to = new int[]{android.R.id.text1}; + 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}. @@ -25,7 +27,7 @@ public class SearchResultsAdapter { * @param results the results to be placed in the adapter. * @return the adapter. */ - public static SimpleCursorAdapter getCursorAdapter(Context context, List results) { + public static CursorAdapter getCursorAdapter(Context context, List results) { int id = 0; MatrixCursor cursor = new MatrixCursor(columnNames); for (SearchResult item : results) { @@ -39,7 +41,7 @@ public static SimpleCursorAdapter getCursorAdapter(Context context, List 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; @@ -35,8 +40,12 @@ public WikiClient(ProgressBar progressBar, ActionBar actionBar, WebView wikiView * 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.d(TAG, "Adding page " + wikiPage.getPageTitle() + ". Stack size= " + webpageStack.size()); + Log.i(TAG, "Adding page " + wikiPage.getPageTitle() + ". Stack size= " + webpageStack.size()); } /** @@ -54,7 +63,6 @@ public void loadWikiHtml(WikiPage wikiPage) { ); setSubtitle(wikiPage.getPageTitle()); - hideProgress(); } /** @@ -70,7 +78,7 @@ 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); + Fetch.page(this, url, true); showProgress(); return false; @@ -80,6 +88,39 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { } } + @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); } @@ -108,8 +149,10 @@ public int getHistoryStackSize() { */ public void goBackHistory() { WikiPage removed = webpageStack.pop(); - Log.d(TAG, "Removing " + removed.getPageTitle() + " from stack"); - loadWikiHtml(webpageStack.peek()); + loadedUrls.remove(removed.getPageUrl()); + Log.i(TAG, "Removing " + removed.getPageTitle() + " from stack"); + WikiPage newPage = webpageStack.peek(); + loadWikiHtml(newPage); } /** @@ -126,4 +169,22 @@ 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(); + 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/WikiPrefsActivity.java b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiPrefsActivity.java deleted file mode 100644 index f034f2e..0000000 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiPrefsActivity.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.jtmcn.archwiki.viewer; - -import android.os.Bundle; -import android.preference.PreferenceActivity; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; - -/** - * The {@link PreferenceActivity} to change settings for the application. - */ -public class WikiPrefsActivity extends PreferenceActivity { - public static final String KEY_TEXT_SIZE = "textSize"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - getFragmentManager().beginTransaction() - .replace(android.R.id.content, new ApplicationPreferenceFragment()) - .commit(); - } - - - /** - * Loads the activities preferences into the fragment. - */ - public static class ApplicationPreferenceFragment extends PreferenceFragment { - @Override - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - - // sets default values if they haven't been set before - // I think some people do this in a custom Application class - PreferenceManager.setDefaultValues(getActivity(), R.xml.prefs, false); - - addPreferencesFromResource(R.xml.prefs); - - } - } - -} diff --git a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java index b972b70..c4b3d33 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/WikiView.java @@ -1,28 +1,28 @@ package com.jtmcn.archwiki.viewer; -import android.app.ActionBar; 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.webkit.WebView; 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 WebView { +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); - this.context = context; 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); @@ -41,7 +41,8 @@ public void buildView(ProgressBar progressBar, ActionBar actionBar) { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && wikiClient.getHistoryStackSize() > 1) { - Log.d(TAG, "Loading previous page."); + Log.i(TAG, "Loading previous page."); + Log.d(TAG, "Position on page currently at " + getScrollY()); wikiClient.goBackHistory(); return true; } else { @@ -69,4 +70,10 @@ public void passSearch(String query) { public WikiPage getCurrentWebPage() { return wikiClient.getCurrentWebPage(); } + + @Override + public void onRefresh() { + wikiClient.refreshPage(); + stopLoading(); + } } 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 index 90cecc2..239368a 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/data/WikiPage.java @@ -7,6 +7,7 @@ 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. @@ -40,4 +41,28 @@ public String toString() { 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/tasks/Fetch.java b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.java index e9fe768..887e8dd 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/Fetch.java @@ -57,7 +57,8 @@ public static AsyncTask> search( */ public static AsyncTask page( FetchUrl.OnFinish onFinish, - String url + String url, + boolean caching ) { return new 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 index 46fe58b..da0db51 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/tasks/FetchUrl.java @@ -16,15 +16,23 @@ 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) { + public FetchUrl(OnFinish onFinish, FetchUrlMapper mapper, boolean caching) { this.onFinish = onFinish; this.mapper = mapper; + this.caching = caching; } @Override @@ -53,7 +61,7 @@ protected void onPostExecute(Result values) { private StringBuilder getItem(String url) { StringBuilder toReturn; try { - toReturn = NetworkUtils.fetchURL(url); + toReturn = NetworkUtils.fetchURL(url, caching); } catch (IOException e) { //network exception Log.w(TAG, "Could not connect to: " + url, e); toReturn = new StringBuilder(); 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 index 6d99ec3..93a5877 100644 --- a/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.java +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/AndroidUtils.java @@ -1,8 +1,12 @@ 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; @@ -18,17 +22,18 @@ private AndroidUtils() { /** * Creates an intent to prompt the user for sharing text. * - * @param title The name of the text being stored. - * @param message The message to be shared. - * @param context The context needed to start the intent. + * @param title The name of the text being stored. + * @param url The url to be shared. + * @param activity The current activity. */ - public static void shareText(String title, String message, Context context) { - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_TITLE, title); - intent.putExtra(Intent.EXTRA_TEXT, message); - intent.setType(TEXT_PLAIN_MIME); - context.startActivity(intent); + 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(); } /** 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 new file mode 100644 index 0000000..f778506 --- /dev/null +++ b/app/src/main/java/com/jtmcn/archwiki/viewer/utils/SettingsUtils.java @@ -0,0 +1,24 @@ +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/res/drawable/ic_search_white_24dp.xml b/app/src/main/res/drawable/ic_search_white_24dp.xml new file mode 100644 index 0000000..47432c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_search_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..e2b17f7 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_preferences.xml b/app/src/main/res/layout/activity_preferences.xml new file mode 100644 index 0000000..cc3c025 --- /dev/null +++ b/app/src/main/res/layout/activity_preferences.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/search_suggestions_list_item.xml b/app/src/main/res/layout/search_suggestions_list_item.xml new file mode 100644 index 0000000..c7e1e09 --- /dev/null +++ b/app/src/main/res/layout/search_suggestions_list_item.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml new file mode 100644 index 0000000..6aa7aaa --- /dev/null +++ b/app/src/main/res/layout/toolbar.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/wiki_layout.xml b/app/src/main/res/layout/wiki_layout.xml deleted file mode 100644 index a42346a..0000000 --- a/app/src/main/res/layout/wiki_layout.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu.xml b/app/src/main/res/menu/menu.xml new file mode 100644 index 0000000..0319a72 --- /dev/null +++ b/app/src/main/res/menu/menu.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/options.xml b/app/src/main/res/menu/options.xml deleted file mode 100644 index da8e693..0000000 --- a/app/src/main/res/menu/options.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b9ab7fe..eaa1bff 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -6,4 +6,5 @@ Suchen Schluss Schriftgröße + Refresh \ No newline at end of file diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index f2cf58b..aa01b72 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -6,4 +6,5 @@ חיפוש Exit Text Size + Refresh diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..acf42cd --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,12 @@ + + + #0288d1 + #272727 + #2196f3 + + #ffffff + #ccc + #333 + #08c + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3bf62b9..b34df43 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,4 +6,5 @@ Search Exit Text Size + Refresh \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3da2d61..fe22248 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,5 +1,20 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/xml/prefs.xml b/app/src/main/res/xml/prefs.xml index a1c5e40..76c6ef2 100644 --- a/app/src/main/res/xml/prefs.xml +++ b/app/src/main/res/xml/prefs.xml @@ -1,14 +1,15 @@ + android:key="applicationPreference" + android:title="@string/menu_settings"> + + - \ No newline at end of file diff --git a/playstore/screen1.png b/playstore/screen1.png index 3b0e70b..46f752a 100644 Binary files a/playstore/screen1.png and b/playstore/screen1.png differ diff --git a/playstore/screen2.png b/playstore/screen2.png index 1708fd4..35b4ba9 100644 Binary files a/playstore/screen2.png and b/playstore/screen2.png differ diff --git a/playstore/screen3.png b/playstore/screen3.png index f6c299d..a98924c 100644 Binary files a/playstore/screen3.png and b/playstore/screen3.png differ diff --git a/playstore/screen4.png b/playstore/screen4.png index 539338f..0f043dd 100644 Binary files a/playstore/screen4.png and b/playstore/screen4.png differ diff --git a/playstore/screen5.png b/playstore/screen5.png index 7ef0302..5eb8082 100644 Binary files a/playstore/screen5.png and b/playstore/screen5.png differ