package info.guardianproject.securereaderinterface.views;
import info.guardianproject.securereaderinterface.R;
import info.guardianproject.securereader.Settings.ReaderSwipeDirection;
import info.guardianproject.securereaderinterface.App;
import info.guardianproject.securereaderinterface.MainActivity;
import info.guardianproject.securereaderinterface.models.PagedViewContent;
import info.guardianproject.securereaderinterface.ui.MediaViewCollection;
import info.guardianproject.securereaderinterface.ui.PackageHelper;
import info.guardianproject.securereaderinterface.ui.MediaViewCollection.OnMediaLoadedListener;
import info.guardianproject.securereaderinterface.uiutil.UIHelpers;
import info.guardianproject.securereaderinterface.widgets.AnimatedRelativeLayout;
import info.guardianproject.securereaderinterface.widgets.CustomFontTextView;
import info.guardianproject.securereaderinterface.widgets.HeightLimitedRelativeLayout;
import info.guardianproject.securereaderinterface.widgets.PagedView;
import info.guardianproject.securereaderinterface.widgets.UpdatingTextView;
import info.guardianproject.securereaderinterface.widgets.UpdatingTextView.OnUpdateListener;
import info.guardianproject.securereader.SocialReader;
import java.text.Bidi;
import java.util.ArrayList;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Paint;
import android.graphics.Rect;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tinymission.rss.Item;
public class StoryItemView implements PagedViewContent, OnUpdateListener, OnMediaLoadedListener
{
private PagedView mPagedView;
private final Item mItem;
private MediaViewCollection mMediaViewCollection;
private ArrayList<View> mBlueprintViews;
private ArrayList<View> mPages;
private SparseArray<Rect> mStoredPositions;
private float mDefaultTextSize;
private float mDefaultAuthorTextSize;
public StoryItemView(Item item)
{
mItem = item;
}
private void createBlueprintViews(ViewGroup parentContainer)
{
mBlueprintViews = new ArrayList<View>();
LayoutInflater inflater = LayoutInflater.from(parentContainer.getContext());
ViewGroup blueprint = (ViewGroup) inflater.inflate(R.layout.story_item_page_blueprint, parentContainer, false);
populateViewWithItem(blueprint, mItem);
blueprint.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
while (blueprint.getChildCount() > 0)
{
View child = blueprint.getChildAt(0);
mBlueprintViews.add(child);
blueprint.removeViewAt(0);
}
}
// Used for layout!
//
private ViewGroup _CurrentColumn;
private ViewGroup _NextColumn;
private View createPage(boolean isFirstPage)
{
View newPage = null;
if (isFirstPage)
{
newPage = LayoutInflater.from(mPagedView.getContext()).inflate(R.layout.story_item_fullscreen_view_page_1, mPagedView, false);
}
else
{
newPage = LayoutInflater.from(mPagedView.getContext()).inflate(R.layout.story_item_fullscreen_view_page_n, mPagedView, false);
}
populateViewWithItem((ViewGroup) newPage, mItem);
newPage.measure(View.MeasureSpec.makeMeasureSpec(mPagedView.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(mPagedView.getHeight(), View.MeasureSpec.EXACTLY));
newPage.layout(0, 0, newPage.getMeasuredWidth(), newPage.getMeasuredHeight());
return newPage;
}
private ViewGroup getNextColumn(ArrayList<View> pages)
{
if (_NextColumn == null || _CurrentColumn == _NextColumn)
{
View newPage = createPage(_CurrentColumn == null);
_CurrentColumn = (ViewGroup) newPage.findViewById(R.id.column1);
_NextColumn = (ViewGroup) newPage.findViewById(R.id.column2);
pages.add(newPage);
}
else
{
_CurrentColumn = _NextColumn;
}
return _CurrentColumn;
}
private boolean isTwoColumnMode()
{
return _NextColumn != null;
}
private boolean isInFirstColumn()
{
return _CurrentColumn != _NextColumn;
}
private boolean willCreateMediaView()
{
if (mMediaViewCollection != null && mMediaViewCollection.getCount() > 0)
return true;
return false;
}
private ArrayList<View> relayout()
{
updateTextSize();
ArrayList<View> ret = new ArrayList<View>();
ArrayList<View> blueprints = new ArrayList<View>(mBlueprintViews);
// Null old layout helpers
_CurrentColumn = null;
_NextColumn = null;
ViewGroup column = getNextColumn(ret);
int columnHeightMax = column.getHeight();
int currentColumnHeight = 0;
int fullMarginTop = column.getResources().getDimensionPixelOffset(R.dimen.full_top_margin);
int marginLeft = column.getResources().getDimensionPixelOffset(R.dimen.card_left_margin);
int marginRight = column.getResources().getDimensionPixelOffset(R.dimen.card_right_margin);
int marginCenter = column.getResources().getDimensionPixelOffset(R.dimen.card_center_margin);
for (int idxView = 0; idxView < blueprints.size();)
{
ViewGroup currentPageView = ((ViewGroup) ret.get(ret.size() - 1));
View child = blueprints.get(idxView);
if (child.getParent() != null)
((ViewGroup) child.getParent()).removeView(child);
if (child.getId() == R.id.layout_media)
{
HeightLimitedRelativeLayout hlrl = (HeightLimitedRelativeLayout) child;
StoryMediaContentView mcv = (StoryMediaContentView) child.findViewById(R.id.ivPhotos);
if (isTwoColumnMode())
{
hlrl.setHeightLimit(0); // Full bleed
mcv.setUseFinalSizeForDownloadView(true);
if (currentColumnHeight == 0)
{
// Allow image to bleed to end of margin!
columnHeightMax += ((MarginLayoutParams)_CurrentColumn.getLayoutParams()).bottomMargin;
}
}
else
{
hlrl.setHeightLimit(1.75f);
}
mcv.setScaleType(!isTwoColumnMode());
}
else if (child instanceof CustomFontTextView)
{
CustomFontTextView tv = (CustomFontTextView) child;
tv.setMaxLines(Integer.MAX_VALUE);
}
if (child.getVisibility() == View.GONE)
{
// Dont add this view
idxView++;
continue;
}
MarginLayoutParams lpChild = (MarginLayoutParams) child.getLayoutParams();
// Adjust for the center margin between columns
if (isTwoColumnMode())
{
if (isInFirstColumn())
{
if (lpChild.rightMargin == marginRight)
lpChild.rightMargin = marginCenter;
}
else
{
if (lpChild.leftMargin == marginLeft)
lpChild.leftMargin = marginCenter;
}
}
// If at top of column and child is not photo, add margin
if (currentColumnHeight == 0 && child.getId() != R.id.layout_media)
currentColumnHeight += fullMarginTop;
else if (currentColumnHeight != 0)
currentColumnHeight += lpChild.topMargin;
if (child.getId() == R.id.layout_media && isTwoColumnMode() && currentColumnHeight != 0 && willCreateMediaView())
{
// Do nothing. This will pull up a new column for us, in which we will be topmost!
}
else
{
boolean spillToNextColumn = false;
// Adjust to column
int widthChild = column.getWidth() - lpChild.leftMargin - lpChild.rightMargin;
if (child.getId() == R.id.layout_media && isTwoColumnMode() && willCreateMediaView())
child.measure(View.MeasureSpec.makeMeasureSpec(widthChild, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(columnHeightMax - currentColumnHeight - lpChild.bottomMargin, View.MeasureSpec.EXACTLY));
else
child.measure(View.MeasureSpec.makeMeasureSpec(widthChild, View.MeasureSpec.EXACTLY), View.MeasureSpec.UNSPECIFIED);
child.layout(lpChild.leftMargin, 0, lpChild.leftMargin + child.getMeasuredWidth(), child.getMeasuredHeight());
int currentPlusHeight = child.getHeight() + lpChild.bottomMargin + currentColumnHeight;
if (child instanceof CustomFontTextView
&& currentPlusHeight > columnHeightMax)
{
CustomFontTextView tv = (CustomFontTextView) child;
int numVisibleLines = tv.getVisibleLines();
// Special case, we can't line break the "read more"
// control
if (child.getId() == R.id.tvReadMore)
numVisibleLines = 0;
if (numVisibleLines == 0) {
column = getNextColumn(ret);
columnHeightMax = column.getHeight();
currentColumnHeight = 0;
continue;
}
tv.setHeight(columnHeightMax - currentColumnHeight - lpChild.bottomMargin);
tv.measure(View.MeasureSpec.makeMeasureSpec(
widthChild, View.MeasureSpec.EXACTLY),
View.MeasureSpec.UNSPECIFIED);
tv.layout(lpChild.leftMargin, 0, lpChild.leftMargin + tv.getMeasuredWidth(),
tv.getMeasuredHeight());
if (tv.getId() == R.id.tvContent || tv.getId() == R.id.tvTitle
|| (tv.getTag() != null
&& tv.getTag() instanceof Integer && ((Integer) tv
.getTag()).intValue() == R.id.tvContent)) {
// Split it in two!
CustomFontTextView newClone = ((CustomFontTextView) child)
.createClone();
newClone.setText(tv.getOverflowingText());
newClone.setTag(Integer.valueOf(R.id.tvContent));
RelativeLayout.LayoutParams lpNewClone = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lpNewClone.leftMargin = marginLeft;
lpNewClone.rightMargin = marginRight;
newClone.setLayoutParams(lpNewClone);
blueprints.add(idxView + 1, newClone);
}
spillToNextColumn = true;
}
idxView++;
// Fits
RelativeLayout.LayoutParams relLayout = new RelativeLayout.LayoutParams(child.getWidth(), child.getHeight());
relLayout.leftMargin = UIHelpers.getRelativeLeft(column) + lpChild.leftMargin;
relLayout.topMargin = UIHelpers.getRelativeTop(column) + currentColumnHeight;
currentPageView.addView(child);
child.setLayoutParams(relLayout);
currentColumnHeight += child.getHeight();
if (!spillToNextColumn)
continue;
}
column = getNextColumn(ret);
columnHeightMax = column.getHeight();
currentColumnHeight = 0;
}
// Remove the column views themselves
for (View page : ret)
{
View col1 = page.findViewById(R.id.column1);
View col2 = page.findViewById(R.id.column2);
if (col1 != null)
((ViewGroup) page).removeView(col1);
if (col2 != null)
((ViewGroup) page).removeView(col2);
}
// Animations?
View page1 = ret.get(0);
if (page1 != null && page1 instanceof AnimatedRelativeLayout)
{
((AnimatedRelativeLayout) page1).setStartPositions(mStoredPositions);
}
mPages = ret;
mStoredPositions = getStoredPositions();
this.updateTime();
return ret;
}
/**
* Use this method to set optional initial starting positions for the views.
*
* @param storedPositions
*/
public void setStoredPositions(SparseArray<Rect> storedPositions)
{
mStoredPositions = storedPositions;
}
public void resetToStoredPositions(int duration)
{
View page1 = mPages.get(0);
if (page1 != null && page1 instanceof AnimatedRelativeLayout)
{
((AnimatedRelativeLayout) page1).animateToStartPositions(duration);
}
}
private SparseArray<Rect> getStoredPositions()
{
if (mPages == null || mPages.size() == 0)
return null;
SparseArray<Rect> positions = new SparseArray<Rect>();
for (View child : mBlueprintViews)
{
if (child.getId() != View.NO_ID)
{
if (child.getLayoutParams() != null && child.getLayoutParams() instanceof RelativeLayout.LayoutParams)
{
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) child.getLayoutParams();
Rect currentRect = new Rect(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getWidth(), lp.topMargin + child.getHeight());
positions.put(child.getId(), currentRect);
}
}
}
return positions;
}
private void updateTextSize()
{
for (View view : this.mBlueprintViews)
{
if (view.getId() == R.id.tvContent)
{
((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX, this.mDefaultTextSize + App.getSettings().getContentFontSizeAdjustment());
}
else if (view.getId() == R.id.tvAuthor)
{
((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX, this.mDefaultAuthorTextSize + App.getSettings().getContentFontSizeAdjustment());
}
}
}
private void populateViewWithItem(ViewGroup blueprint, Item story)
{
// Set title
//
TextView tv = (TextView) blueprint.findViewById(R.id.tvTitle);
if (tv != null)
tv.setText(story.getTitle());
// Set image(s)
//
StoryMediaContentView mediaContent = (StoryMediaContentView) blueprint.findViewById(R.id.ivPhotos);
if (mediaContent != null && mMediaViewCollection != null)
{
mediaContent.setMediaCollection(mMediaViewCollection, true, true);
}
View mediaContainer = blueprint.findViewById(R.id.layout_media);
if (mediaContainer != null)
{
if (!willCreateMediaView())
mediaContainer.setVisibility(View.GONE);
else
mediaContainer.setVisibility(View.VISIBLE);
}
// Author
tv = (TextView) blueprint.findViewById(R.id.tvAuthor);
if (tv != null)
{
mDefaultAuthorTextSize = tv.getTextSize();
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() + App.getSettings().getContentFontSizeAdjustment());
if (story.getAuthor() != null)
tv.setText(blueprint.getContext().getString(R.string.story_item_short_author, story.getAuthor()));
else
tv.setText(null);
if (TextUtils.isEmpty(tv.getText()))
{
tv.setVisibility(View.GONE);
}
}
// Author date
tv = (TextView) blueprint.findViewById(R.id.tvAuthorDate);
if (tv != null)
tv.setText(UIHelpers.dateDateDisplayString(story.getPublicationTime(), tv.getContext()));
// Author time
tv = (TextView) blueprint.findViewById(R.id.tvAuthorTime);
if (tv != null)
tv.setText(UIHelpers.dateTimeDisplayString(story.getPublicationTime(), tv.getContext()));
// Content
tv = (TextView) blueprint.findViewById(R.id.tvContent);
if (tv != null)
{
mDefaultTextSize = tv.getTextSize();
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() + App.getSettings().getContentFontSizeAdjustment());
tv.setText(story.getCleanMainContent());
tv.setPaintFlags(tv.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
if (TextUtils.isEmpty(tv.getText()))
tv.setVisibility(View.GONE);
}
// Set source
//
tv = (TextView) blueprint.findViewById(R.id.tvSource);
if (tv != null)
{
tv.setText(story.getSource());
}
// Time
tv = (TextView) blueprint.findViewById(R.id.tvTime);
if (tv != null)
{
onUpdateNeeded((UpdatingTextView) tv);
}
// Read more
tv = (TextView) blueprint.findViewById(R.id.tvReadMore);
if (tv != null)
{
if (story.getLink() != null)
{
boolean isReadMoreEnabled = !TextUtils.isEmpty(story.getLink()) && App.getInstance().socialReader.isOnline() == SocialReader.ONLINE;
tv.setEnabled(isReadMoreEnabled);
if (!isReadMoreEnabled)
{
//tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_read_orweb_gray, 0, 0, 0);
tv.setOnClickListener(null);
}
else
{
//tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_read_orweb, 0, 0, 0);
//if (PackageHelper.isOrwebInstalled(blueprint.getContext()))
tv.setOnClickListener(new ReadMoreClickListener(story));
//else
// tv.setOnClickListener(new PromptOrwebClickListener(blueprint.getContext()));
}
tv.setVisibility(View.VISIBLE);
}
else
{
tv.setVisibility(View.GONE);
}
}
}
@Override
public boolean usesReverseSwipe()
{
boolean bReverse = false;
if (mItem != null)
{
// Use the bidi class to figure out the swipe direction!
if (App.getSettings().readerSwipeDirection() == ReaderSwipeDirection.Automatic)
{
try
{
Bidi bidi = new Bidi(mItem.getCleanMainContent(), Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
if (!bidi.baseIsLeftToRight())
bReverse = true;
}
catch (IllegalArgumentException e)
{
// Content probably null for some reason.
}
}
else if (App.getSettings().readerSwipeDirection() == ReaderSwipeDirection.Ltr)
{
bReverse = true;
}
}
return bReverse;
}
@Override
public ArrayList<View> createPages(PagedView parent)
{
mPagedView = parent;
if (mMediaViewCollection != null)
{
mMediaViewCollection.removeListener(this);
mMediaViewCollection.recycle();
}
mMediaViewCollection = null;
if (mItem.getMediaContent() != null && mItem.getMediaContent().size()> 0)
{
mMediaViewCollection = new MediaViewCollection(parent.getContext(), mItem);
mMediaViewCollection.load(false, true);
mMediaViewCollection.addListener(this);
}
createBlueprintViews(parent);
relayout();
return mPages;
}
@Override
public void onUpdateNeeded(UpdatingTextView view)
{
if (view != null)
{
if (mItem != null)
{
view.setText(UIHelpers.dateDiffDisplayString(mItem.getPublicationTime(), view.getContext(), R.string.story_item_short_published_never,
R.string.story_item_short_published_recently, R.string.story_item_short_published_minutes, R.string.story_item_short_published_minute,
R.string.story_item_short_published_hours, R.string.story_item_short_published_hour, R.string.story_item_short_published_days,
R.string.story_item_short_published_day));
}
else
{
view.setText(R.string.story_item_short_published_never);
}
}
}
protected void updateTime()
{
if (mPages == null)
return;
for (View view : mPages)
{
UpdatingTextView tvTime = (UpdatingTextView) view.findViewById(R.id.tvTime);
if (tvTime != null)
{
this.onUpdateNeeded(tvTime);
tvTime.setOnUpdateListener(this);
}
}
}
@Override
public void onViewLoaded(MediaViewCollection collection, int index, boolean wasCached)
{
Log.v("StoryItemView", "Media content has requested relayout.");
mPagedView.recreateViewsForContent(this);
}
private class ReadMoreClickListener implements View.OnClickListener
{
private final Item mItem;
public ReadMoreClickListener(Item item)
{
mItem = item;
}
@Override
public void onClick(View v)
{
try
{
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mItem.getLink()));
//intent.setClassName(PackageHelper.URI_ORWEB, PackageHelper.URI_ORWEB + ".Browser");
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
v.getContext().startActivity(intent);
}
catch (Exception e)
{
Log.d(MainActivity.LOGTAG, "Error trying to open read more link: " + mItem.getLink());
}
}
}
private class PromptOrwebClickListener implements View.OnClickListener
{
private final Context mContext;
public PromptOrwebClickListener(Context context)
{
mContext = context;
}
@Override
public void onClick(View v)
{
AlertDialog dialog = PackageHelper.showDownloadDialog(mContext, R.string.install_orweb_title, R.string.install_orweb_prompt, android.R.string.ok,
android.R.string.cancel, PackageHelper.URI_ORWEB_PLAY);
}
}
}