summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/layout-sw600dp/feeds.xml6
-rw-r--r--res/layout-sw600dp/headlines.xml2
-rw-r--r--src/android/webkit/WebViewClassic.java14
-rw-r--r--src/com/nobu_games/android/view/web/TitleBarWebView.java353
-rw-r--r--src/org/fox/ttrss/ArticleFragment.java4
-rw-r--r--src/org/fox/ttrss/FeedsActivity.java24
-rw-r--r--src/org/fox/ttrss/HeadlinesActivity.java6
-rw-r--r--src/org/fox/ttrss/offline/OfflineActivity.java2
-rw-r--r--src/org/fox/ttrss/offline/OfflineFeedsActivity.java49
9 files changed, 447 insertions, 13 deletions
diff --git a/res/layout-sw600dp/feeds.xml b/res/layout-sw600dp/feeds.xml
index 3e75a37c..5f37b900 100644
--- a/res/layout-sw600dp/feeds.xml
+++ b/res/layout-sw600dp/feeds.xml
@@ -2,6 +2,7 @@
android:id="@+id/main"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
+ android:background="?headlinesBackgroundSolid"
android:orientation="vertical" >
<LinearLayout
@@ -26,13 +27,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
+ android:weightSum="1.05"
android:orientation="horizontal" >
<FrameLayout
android:id="@+id/feeds_fragment"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="0.4"
+ android:layout_weight="1"
android:background="?feedlistBackground" >
</FrameLayout>
@@ -40,7 +42,7 @@
android:id="@+id/headlines_fragment"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="0.6"
+ android:layout_weight="2"
android:background="?headlinesBackground" >
</FrameLayout>
</LinearLayout>
diff --git a/res/layout-sw600dp/headlines.xml b/res/layout-sw600dp/headlines.xml
index 54eabd01..b6cb1c38 100644
--- a/res/layout-sw600dp/headlines.xml
+++ b/res/layout-sw600dp/headlines.xml
@@ -34,7 +34,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.4"
- android:background="?headlinesBackground" >
+ android:background="?feedlistBackground" >
</FrameLayout>
<FrameLayout
diff --git a/src/android/webkit/WebViewClassic.java b/src/android/webkit/WebViewClassic.java
new file mode 100644
index 00000000..e315beea
--- /dev/null
+++ b/src/android/webkit/WebViewClassic.java
@@ -0,0 +1,14 @@
+package android.webkit;
+
+import android.view.View;
+
+/**
+ * Trojan class for getting access to a hidden API level 16 interface
+ */
+public class WebViewClassic {
+ public interface TitleBarDelegate {
+ int getTitleHeight();
+
+ public void onSetEmbeddedTitleBar(final View title);
+ }
+}
diff --git a/src/com/nobu_games/android/view/web/TitleBarWebView.java b/src/com/nobu_games/android/view/web/TitleBarWebView.java
new file mode 100644
index 00000000..4931bed0
--- /dev/null
+++ b/src/com/nobu_games/android/view/web/TitleBarWebView.java
@@ -0,0 +1,353 @@
+package com.nobu_games.android.view.web;
+
+/*
+ * Copyright (C) 2012 Thomas Werner
+ * Portions Copyright (C) 2006 The Android Open Source Project
+ * Portions Copyright (C) 2012 The K-9 Dog Walkers
+ *
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
+import android.webkit.WebViewClassic.TitleBarDelegate;
+import android.widget.FrameLayout;
+
+/**
+ * WebView derivative with custom setEmbeddedTitleBar implementation for Android
+ * versions that do not support that feature anymore.
+ * <p>
+ * <b>Usage</b>
+ * <p>
+ * Call {@link #setEmbeddedTitleBarCompat(View)} for setting a view as embedded
+ * title bar on top of the displayed WebView page.
+ */
+public class TitleBarWebView extends WebView implements TitleBarDelegate {
+ /**
+ * Internally used view wrapper for suppressing unwanted touch events on the
+ * title bar view when WebView contents is being touched.
+ */
+ private class TouchBlockView extends FrameLayout {
+ public TouchBlockView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if(!mTouchInTitleBar) {
+ return false;
+ } else {
+ switch(ev.getAction() & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mTouchInTitleBar = false;
+ break;
+
+ default:
+
+ }
+
+ return super.dispatchTouchEvent(ev);
+ }
+ }
+ }
+
+ private static final String TAG = "TitleBarWebView";
+ View mTitleBar;
+ int mTitleBarOffs;
+ boolean mTouchInTitleBar;
+ boolean mTouchMove;
+ private Rect mClipBounds = new Rect();
+ private Matrix mMatrix = new Matrix();
+ private Method mNativeGetVisibleTitleHeightMethod;
+
+ /**
+ * This will always contain a reference to the title bar view no matter if
+ * {@code setEmbeddedTitleBar()} or the Jelly Bean workaround is used. We use this in
+ * {@link #getTitleHeight()}.
+ */
+ private View mTitleBarView;
+
+ public TitleBarWebView(Context context) {
+ super(context);
+ init();
+ }
+
+ public TitleBarWebView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public TitleBarWebView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ /**
+ * <i>Corrects the visual displacement caused by the title bar view.</i>
+ * <p>
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if(mTitleBar != null) {
+ final int sy = getScrollY();
+ final int visTitleHeight = getVisibleTitleHeightCompat();
+ final float x = event.getX();
+ float y = event.getY();
+
+ switch(event.getAction() & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ if(y <= visTitleHeight) {
+ mTouchInTitleBar = true;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ mTouchMove = true;
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mTouchMove = false;
+ break;
+
+ default:
+ }
+
+ if(mTouchInTitleBar) {
+ y += sy;
+ event.setLocation(x, y);
+
+ return mTitleBar.dispatchTouchEvent(event);
+ } else {
+ if(Build.VERSION.SDK_INT < 16) {
+ if(!mTouchMove) {
+ mTitleBarOffs = getVisibleTitleHeightCompat();
+ }
+
+ y -= mTitleBarOffs;
+ if(y < 0) y = 0;
+ event.setLocation(x, y);
+ }
+
+ return super.dispatchTouchEvent(event);
+ }
+ } else {
+ return super.dispatchTouchEvent(event);
+ }
+ }
+
+ /**
+ * Sets a {@link View} as an embedded title bar to appear on top of the
+ * WebView page.
+ * <p>
+ * This method tries to call the hidden API method setEmbeddedTitleBar if
+ * present. On failure the custom implementation provided by this class will
+ * be used instead.
+ *
+ * @param v The view to set or null for removing the title bar view.
+ */
+ public void setEmbeddedTitleBarCompat(View v) {
+ try {
+ Method nativeMethod = getClass().getMethod("setEmbeddedTitleBar",
+ View.class);
+ nativeMethod.invoke(this, v);
+ } catch(Exception e) {
+ Log.d(TAG,
+ "Native setEmbeddedTitleBar not available. Starting workaround");
+ setEmbeddedTitleBarJellyBean(v);
+ }
+
+ mTitleBarView = v;
+ }
+
+ @Override
+ protected int computeVerticalScrollExtent() {
+ if(mTitleBar == null) return super.computeVerticalScrollExtent();
+ return getViewHeightWithTitle() - getVisibleTitleHeightCompat();
+ }
+
+ @Override
+ protected int computeVerticalScrollOffset() {
+ if(mTitleBar == null) return super.computeVerticalScrollOffset();
+ return Math.max(getScrollY() - getTitleHeight(), 0);
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ canvas.save();
+
+ if(child == mTitleBar) {
+ mTitleBar.offsetLeftAndRight(getScrollX() - mTitleBar.getLeft());
+
+ if(Build.VERSION.SDK_INT < 16) {
+ mMatrix.set(canvas.getMatrix());
+ mMatrix.postTranslate(0, -getScrollY());
+ canvas.setMatrix(mMatrix);
+ }
+ }
+
+ boolean result = super.drawChild(canvas, child, drawingTime);
+ canvas.restore();
+
+ return result;
+ }
+
+ /**
+ * Gets the currently visible height of the title bar view if set.
+ *
+ * @return Visible height of title bar view or 0 if not set.
+ */
+ protected int getVisibleTitleHeightCompat() {
+ if(mTitleBar == null && mNativeGetVisibleTitleHeightMethod != null) {
+ try {
+ return (Integer) mNativeGetVisibleTitleHeightMethod
+ .invoke(this);
+ } catch(Exception e) {
+ }
+ }
+
+ return Math.max(getTitleHeight() - Math.max(0, getScrollY()), 0);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (Build.VERSION.SDK_INT >= 16) {
+ super.onDraw(canvas);
+ return;
+ }
+
+ canvas.save();
+
+ if(mTitleBar != null) {
+ final int sy = getScrollY();
+ final int sx = getScrollX();
+ mClipBounds.top = sy;
+ mClipBounds.left = sx;
+ mClipBounds.right = mClipBounds.left + getWidth();
+ mClipBounds.bottom = mClipBounds.top + getHeight();
+ canvas.clipRect(mClipBounds);
+ mMatrix.set(canvas.getMatrix());
+ int titleBarOffs = getVisibleTitleHeightCompat();
+ if(titleBarOffs < 0) titleBarOffs = 0;
+ mMatrix.postTranslate(0, titleBarOffs);
+ canvas.setMatrix(mMatrix);
+ }
+
+ super.onDraw(canvas);
+ canvas.restore();
+ }
+
+ /**
+ * Overrides a hidden method by replicating the behavior of the original
+ * WebView class from Android 2.3.4.
+ * <p>
+ * The worst that could happen is that this method never gets called, which
+ * isn't too bad because this does not harm the functionality of this class.
+ */
+ protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+ int l, int t, int r, int b) {
+ int sy = getScrollY();
+
+ if(sy < 0) {
+ t -= sy;
+ }
+ scrollBar.setBounds(l, t + getVisibleTitleHeightCompat(), r, b);
+ scrollBar.draw(canvas);
+ }
+
+ /**
+ * Get the height of the title bar view.
+ *
+ * <p>
+ * In the Jelly Bean workaround we need this method because we have to implement the
+ * {@link TitleBarDelegate} interface. But by implementing this method we override the hidden
+ * {@code getTitleHeight()} of the {@link WebView}s in older Android versions.
+ * <br>
+ * What we should do, is return the title height on Jelly Bean and call through to the parent
+ * class on older Android versions. But this would require even more trickery, so we just
+ * inline the parent functionality which simply calls {@link View#getHeight()}. This is exactly
+ * what we do on Jelly Bean anyway.
+ * </p>
+ */
+ @Override
+ public int getTitleHeight() {
+ if (mTitleBarView != null) {
+ return mTitleBarView.getHeight();
+ }
+ return 0;
+ }
+
+ private int getViewHeightWithTitle() {
+ int height = getHeight();
+ if(isHorizontalScrollBarEnabled() && !overlayHorizontalScrollbar()) {
+ height -= getHorizontalScrollbarHeight();
+ }
+ return height;
+ }
+
+ private void init() {
+ try {
+ mNativeGetVisibleTitleHeightMethod = WebView.class
+ .getDeclaredMethod("getVisibleTitleHeight");
+ } catch(NoSuchMethodException e) {
+ Log.w(TAG,
+ "Could not retrieve native hidden getVisibleTitleHeight method");
+ }
+ }
+
+ /**
+ * The hidden method setEmbeddedTitleBar has been removed from Jelly Bean.
+ * This method replicates the functionality.
+ *
+ * @param v
+ */
+ private void setEmbeddedTitleBarJellyBean(View v) {
+ if(mTitleBar == v) return;
+
+ if(mTitleBar != null) {
+ removeView(mTitleBar);
+ }
+
+ if(null != v) {
+ LayoutParams vParams = new LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0);
+
+ TouchBlockView tbv = new TouchBlockView(getContext());
+ FrameLayout.LayoutParams tbvParams = new FrameLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ tbv.addView(v, tbvParams);
+ addView(tbv, vParams);
+ v = tbv;
+ }
+
+ mTitleBar = v;
+ }
+
+ @Override
+ public void onSetEmbeddedTitleBar(View title) { /* unused */ }
+}
diff --git a/src/org/fox/ttrss/ArticleFragment.java b/src/org/fox/ttrss/ArticleFragment.java
index 9e3734e8..86fc4472 100644
--- a/src/org/fox/ttrss/ArticleFragment.java
+++ b/src/org/fox/ttrss/ArticleFragment.java
@@ -126,6 +126,10 @@ public class ArticleFragment extends Fragment implements GestureDetector.OnDoubl
else
titleStr = m_article.title;
+ if (!m_activity.isSmallScreen()) {
+ title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 21f);
+ }
+
title.setText(titleStr);
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
title.setOnClickListener(new OnClickListener() {
diff --git a/src/org/fox/ttrss/FeedsActivity.java b/src/org/fox/ttrss/FeedsActivity.java
index 7d95e317..58f8eda6 100644
--- a/src/org/fox/ttrss/FeedsActivity.java
+++ b/src/org/fox/ttrss/FeedsActivity.java
@@ -10,6 +10,7 @@ import org.fox.ttrss.util.AppRater;
import android.view.ViewGroup;
import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -21,6 +22,7 @@ import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
+import android.widget.LinearLayout;
public class FeedsActivity extends OnlineActivity implements HeadlinesEventListener {
private final String TAG = this.getClass().getSimpleName();
@@ -97,7 +99,7 @@ public class FeedsActivity extends OnlineActivity implements HeadlinesEventListe
} else {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
+
if (m_prefs.getBoolean("enable_cats", false)) {
ft.replace(R.id.feeds_fragment, new FeedCategoriesFragment(), FRAG_CATS);
} else {
@@ -115,7 +117,13 @@ public class FeedsActivity extends OnlineActivity implements HeadlinesEventListe
}
} else { // savedInstanceState != null
m_actionbarUpEnabled = savedInstanceState.getBoolean("actionbarUpEnabled");
-
+
+ if (!isSmallScreen()) {
+ // temporary hack because FeedsActivity doesn't track whether active feed is open
+ LinearLayout container = (LinearLayout) findViewById(R.id.fragment_container);
+ container.setWeightSum(3f);
+ }
+
if (!isCompatMode() && m_actionbarUpEnabled) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
@@ -175,7 +183,17 @@ public class FeedsActivity extends OnlineActivity implements HeadlinesEventListe
ft.replace(R.id.headlines_fragment, new LoadingFragment(), null);
ft.commit();
-
+
+ if (!isCompatMode()) {
+ LinearLayout container = (LinearLayout) findViewById(R.id.fragment_container);
+ float wSum = container.getWeightSum();
+ if (wSum <= 2.0f) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(container, "weightSum", wSum, 3.0f);
+ anim.setDuration(200);
+ anim.start();
+ }
+ }
+
final Feed fFeed = feed;
new Handler().postDelayed(new Runnable() {
diff --git a/src/org/fox/ttrss/HeadlinesActivity.java b/src/org/fox/ttrss/HeadlinesActivity.java
index 81dd1043..81c4ca8c 100644
--- a/src/org/fox/ttrss/HeadlinesActivity.java
+++ b/src/org/fox/ttrss/HeadlinesActivity.java
@@ -121,7 +121,11 @@ public class HeadlinesActivity extends OnlineActivity implements HeadlinesEventL
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ overridePendingTransition(0, R.anim.right_slide_out);
+ return true;
default:
Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId());
return super.onOptionsItemSelected(item);
diff --git a/src/org/fox/ttrss/offline/OfflineActivity.java b/src/org/fox/ttrss/offline/OfflineActivity.java
index f7ee9768..608b9bcf 100644
--- a/src/org/fox/ttrss/offline/OfflineActivity.java
+++ b/src/org/fox/ttrss/offline/OfflineActivity.java
@@ -76,6 +76,8 @@ public class OfflineActivity extends CommonActivity {
if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_DARK")) {
setTheme(R.style.DarkTheme);
+ } else if (m_prefs.getString("theme", "THEME_DARK").equals("THEME_SEPIA")) {
+ setTheme(R.style.SepiaTheme);
} else {
setTheme(R.style.LightTheme);
}
diff --git a/src/org/fox/ttrss/offline/OfflineFeedsActivity.java b/src/org/fox/ttrss/offline/OfflineFeedsActivity.java
index 07bedc37..1b35d9e1 100644
--- a/src/org/fox/ttrss/offline/OfflineFeedsActivity.java
+++ b/src/org/fox/ttrss/offline/OfflineFeedsActivity.java
@@ -1,12 +1,16 @@
package org.fox.ttrss.offline;
import org.fox.ttrss.GlobalState;
+import org.fox.ttrss.LoadingFragment;
import org.fox.ttrss.R;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.sqlite.SQLiteStatement;
import android.os.Bundle;
+import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
@@ -14,6 +18,8 @@ import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
public class OfflineFeedsActivity extends OfflineActivity implements OfflineHeadlinesEventListener {
private final String TAG = this.getClass().getSimpleName();
@@ -96,6 +102,16 @@ public class OfflineFeedsActivity extends OfflineActivity implements OfflineHead
findViewById(R.id.loading_container).setVisibility(View.GONE);
initMenu();
+
+ /* if (!isSmallScreen()) {
+ LinearLayout container = (LinearLayout) findViewById(R.id.fragment_container);
+ container.setWeightSum(3f);
+ } */
+
+ if (!isCompatMode() && !isSmallScreen()) {
+ ((ViewGroup)findViewById(R.id.headlines_fragment)).setLayoutTransition(new LayoutTransition());
+ ((ViewGroup)findViewById(R.id.feeds_fragment)).setLayoutTransition(new LayoutTransition());
+ }
}
public void openFeedArticles(int feedId, boolean isCat) {
@@ -189,7 +205,7 @@ public class OfflineFeedsActivity extends OfflineActivity implements OfflineHead
onFeedSelected(feedId, false, true);
}
- public void onFeedSelected(int feedId, boolean isCat, boolean open) {
+ public void onFeedSelected(final int feedId, final boolean isCat, boolean open) {
if (open) {
if (isSmallScreen()) {
@@ -201,13 +217,34 @@ public class OfflineFeedsActivity extends OfflineActivity implements OfflineHead
startActivityForResult(intent, 0);
} else {
- FragmentTransaction ft = getSupportFragmentManager()
- .beginTransaction();
+ /* if (!isCompatMode()) {
+ LinearLayout container = (LinearLayout) findViewById(R.id.fragment_container);
+ float wSum = container.getWeightSum();
+ if (wSum <= 2.0f) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(container, "weightSum", wSum, 3.0f);
+ anim.setDuration(200);
+ anim.start();
+ }
+ } */
- OfflineHeadlinesFragment hf = new OfflineHeadlinesFragment(feedId, isCat);
- ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
+ // ^ no idea why the animation hangs half the time :(
+
+ LinearLayout container = (LinearLayout) findViewById(R.id.fragment_container);
+ container.setWeightSum(3f);
+
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ OfflineHeadlinesFragment hf = new OfflineHeadlinesFragment(feedId, isCat);
+ ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
+
+ ft.commit();
+ }
+ }, 10);
- ft.commit();
}
}
}