package com.door43.translationstudio.newui.translate; import android.app.Activity; import android.content.ContentValues; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.design.widget.TabLayout; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.widget.Button; import android.widget.ImageButton; import android.widget.TextView; import com.door43.translationstudio.R; import com.door43.translationstudio.core.Chapter; import com.door43.translationstudio.core.ChapterTranslation; import com.door43.translationstudio.core.FrameTranslation; import com.door43.translationstudio.core.ProjectTranslation; import com.door43.translationstudio.core.SourceLanguage; import com.door43.translationstudio.core.TargetLanguage; import com.door43.translationstudio.core.TranslationFormat; import com.door43.translationstudio.core.Frame; import com.door43.translationstudio.core.Library; import com.door43.translationstudio.core.SourceTranslation; import com.door43.translationstudio.core.TargetTranslation; import com.door43.translationstudio.core.TranslationViewMode; import com.door43.translationstudio.core.Translator; import com.door43.translationstudio.core.Typography; import com.door43.translationstudio.dialogs.CustomAlertDialog; import com.door43.translationstudio.rendering.ClickableRenderingEngine; import com.door43.translationstudio.rendering.Clickables; import com.door43.translationstudio.rendering.DefaultRenderer; import com.door43.translationstudio.rendering.RenderingGroup; import com.door43.translationstudio.AppContext; import com.door43.translationstudio.spannables.NoteSpan; import com.door43.translationstudio.spannables.Span; import com.door43.widget.ViewUtil; import java.util.ArrayList; import java.util.List; /** * Created by joel on 9/9/2015. */ public class ReadModeAdapter extends ViewModeAdapter<ReadModeAdapter.ViewHolder> { private final CharSequence[] mRenderedTargetBody; private SourceLanguage mSourceLanguage; private final TargetLanguage mTargetLanguage; private boolean[] mTargetStateOpen; private CharSequence[] mRenderedSourceBody; private final Activity mContext; private static final int BOTTOM_ELEVATION = 2; private static final int TOP_ELEVATION = 3; private final TargetTranslation mTargetTranslation; private SourceTranslation mSourceTranslation; private final Library mLibrary; private final Translator mTranslator; private Chapter[] mChapters; private int mLayoutBuildNumber = 0; private ContentValues[] mTabs; public ReadModeAdapter(Activity context, String targetTranslationId, String sourceTranslationId, String chapterId, String frameId) { mLibrary = AppContext.getLibrary(); mTranslator = AppContext.getTranslator(); mContext = context; mTargetTranslation = mTranslator.getTargetTranslation(targetTranslationId); mSourceTranslation = mLibrary.getSourceTranslation(sourceTranslationId); mSourceLanguage = mLibrary.getSourceLanguage(mSourceTranslation.projectSlug, mSourceTranslation.sourceLanguageSlug); mTargetLanguage = mLibrary.getTargetLanguage(mTargetTranslation.getTargetLanguageId()); mChapters = mLibrary.getChapters(mSourceTranslation); if(chapterId != null) { // identify starting selection for (int i = 0; i < mChapters.length; i ++) { Chapter c = mChapters[i]; if (c.getId().equals(chapterId)) { setListStartPosition(i); break; } } } mTargetStateOpen = new boolean[mChapters.length]; mRenderedSourceBody = new CharSequence[mChapters.length]; mRenderedTargetBody = new CharSequence[mChapters.length]; loadTabInfo(); } /** * Updates the source translation displayed * @param sourceTranslationId */ public void setSourceTranslation(String sourceTranslationId) { mSourceTranslation = mLibrary.getSourceTranslation(sourceTranslationId); mSourceLanguage = mLibrary.getSourceLanguage(mSourceTranslation.projectSlug, mSourceTranslation.sourceLanguageSlug); mChapters = mLibrary.getChapters(mSourceTranslation); mTargetStateOpen = new boolean[mChapters.length]; mRenderedSourceBody = new CharSequence[mChapters.length]; loadTabInfo(); notifyDataSetChanged(); } @Override void onCoordinate(ViewHolder holder) { } @Override public String getFocusedFrameId(int position) { return null; } @Override public String getFocusedChapterId(int position) { if(position >= 0 && position < mChapters.length) { return mChapters[position].getId(); } else { return null; } } @Override public int getItemPosition(String chapterId, String frameId) { for(int i = 0; i < mChapters.length; i ++) { Chapter chapter = mChapters[i]; if(chapter.getId().equals(chapterId)) { return i; } } return -1; } @Override public void reload() { setSourceTranslation(mSourceTranslation.getId()); } @Override public ViewHolder onCreateManagedViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_read_list_item, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } /** * Rebuilds the card tabs */ private void loadTabInfo() { List<ContentValues> tabContents = new ArrayList<>(); String[] sourceTranslationIds = AppContext.getOpenSourceTranslationIds(mTargetTranslation.getId()); for(String id:sourceTranslationIds) { SourceTranslation sourceTranslation = mLibrary.getSourceTranslation(id); if(sourceTranslation != null) { ContentValues values = new ContentValues(); // include the resource id if there are more than one if(mLibrary.getResources(sourceTranslation.projectSlug, sourceTranslation.sourceLanguageSlug).length > 1) { values.put("title", sourceTranslation.getSourceLanguageTitle() + " " + sourceTranslation.resourceSlug.toUpperCase()); } else { values.put("title", sourceTranslation.getSourceLanguageTitle()); } values.put("tag", sourceTranslation.getId()); tabContents.add(values); } } mTabs = tabContents.toArray(new ContentValues[tabContents.size()]); } @Override public void onBindViewHolder(final ViewHolder holder, final int position) { int cardMargin = mContext.getResources().getDimensionPixelSize(R.dimen.card_margin); int stackedCardMargin = mContext.getResources().getDimensionPixelSize(R.dimen.stacked_card_margin); if(mTargetStateOpen[position]) { // target on top // elevation takes precedence for API 21+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { holder.mSourceCard.setElevation(BOTTOM_ELEVATION); holder.mTargetCard.setElevation(TOP_ELEVATION); } holder.mTargetCard.bringToFront(); CardView.LayoutParams targetParams = (CardView.LayoutParams)holder.mTargetCard.getLayoutParams(); targetParams.setMargins(cardMargin, cardMargin, stackedCardMargin, stackedCardMargin); holder.mTargetCard.setLayoutParams(targetParams); CardView.LayoutParams sourceParams = (CardView.LayoutParams)holder.mSourceCard.getLayoutParams(); sourceParams.setMargins(stackedCardMargin, stackedCardMargin, cardMargin, cardMargin); holder.mSourceCard.setLayoutParams(sourceParams); ((View) holder.mTargetCard.getParent()).requestLayout(); ((View) holder.mTargetCard.getParent()).invalidate(); // disable new tab button so we don't accidently open it holder.mNewTabButton.setEnabled(false); } else { // source on top // elevation takes precedence for API 21+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { holder.mTargetCard.setElevation(BOTTOM_ELEVATION); holder.mSourceCard.setElevation(TOP_ELEVATION); } holder.mSourceCard.bringToFront(); CardView.LayoutParams sourceParams = (CardView.LayoutParams)holder.mSourceCard.getLayoutParams(); sourceParams.setMargins(cardMargin, cardMargin, stackedCardMargin, stackedCardMargin); holder.mSourceCard.setLayoutParams(sourceParams); CardView.LayoutParams targetParams = (CardView.LayoutParams)holder.mTargetCard.getLayoutParams(); targetParams.setMargins(stackedCardMargin, stackedCardMargin, cardMargin, cardMargin); holder.mTargetCard.setLayoutParams(targetParams); ((View) holder.mSourceCard.getParent()).requestLayout(); ((View) holder.mSourceCard.getParent()).invalidate(); // re-enable new tab button holder.mNewTabButton.setEnabled(true); } holder.mTargetCard.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { openTargetTranslationCard(holder, position); } }); holder.mSourceCard.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { closeTargetTranslationCard(holder, position); } }); holder.mNewTabButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (getListener() != null) { getListener().onNewSourceTranslationTabClick(); } } }); final Chapter chapter = mChapters[position]; // render the source chapter body if(mRenderedSourceBody[position] == null) { String chapterBody = mLibrary.getChapterBody(mSourceTranslation, chapter.getId()); TranslationFormat bodyFormat = mLibrary.getChapterBodyFormat(mSourceTranslation, chapter.getId()); RenderingGroup sourceRendering = new RenderingGroup(); if (Clickables.isClickableFormat(bodyFormat)) { // TODO: add click listeners Span.OnClickListener noteClickListener = new Span.OnClickListener() { @Override public void onClick(View view, Span span, int start, int end) { if(span instanceof NoteSpan) { CustomAlertDialog.Create(mContext) .setTitle(R.string.title_note) .setMessage(((NoteSpan)span).getNotes()) .setPositiveButton(R.string.dismiss, null) .show("note"); } } @Override public void onLongClick(View view, Span span, int start, int end) { } }; ClickableRenderingEngine renderer = Clickables.setupRenderingGroup(bodyFormat, sourceRendering, null, noteClickListener, true); // In read mode (and only in read mode), pull leading major section headings out for // display above chapter headings. renderer.setSuppressLeadingMajorSectionHeadings(true); CharSequence heading = renderer.getLeadingMajorSectionHeading(chapterBody); holder.mSourceHeading.setText(heading); holder.mSourceHeading.setVisibility( heading.length() > 0 ? View.VISIBLE : View.GONE); } else { sourceRendering.addEngine(new DefaultRenderer()); } sourceRendering.init(chapterBody); mRenderedSourceBody[position] = sourceRendering.start(); } holder.mSourceBody.setText(mRenderedSourceBody[position]); ViewUtil.makeLinksClickable(holder.mSourceBody); String chapterTitle = chapter.title; if(chapter.title.isEmpty()) { chapterTitle = mSourceTranslation.getProjectTitle() + " " + Integer.parseInt(chapter.getId()); } holder.mSourceTitle.setText(chapterTitle); // render the target chapter body if(mRenderedTargetBody[position] == null) { TranslationFormat bodyFormat = mTargetTranslation.getFormat(); String chapterBody = ""; String[] frameSlugs = mLibrary.getFrameSlugs(mSourceTranslation, chapter.getId()); for (String frameSlug : frameSlugs) { Frame simpleFrame = new Frame(frameSlug, chapter.getId(), null, bodyFormat, null); FrameTranslation frameTranslation = mTargetTranslation.getFrameTranslation(simpleFrame); chapterBody += " " + frameTranslation.body; } RenderingGroup targetRendering = new RenderingGroup(); if(Clickables.isClickableFormat(bodyFormat)) { // TODO: add click listeners ClickableRenderingEngine renderer = Clickables.setupRenderingGroup(bodyFormat, targetRendering, null, null, true); renderer.setVersesEnabled(true); } else { targetRendering.addEngine(new DefaultRenderer()); } targetRendering.init(chapterBody); mRenderedTargetBody[position] = targetRendering.start(); } // display begin translation button if(mRenderedTargetBody[position].toString().trim().isEmpty()) { holder.mBeginButton.setVisibility(View.VISIBLE); final GestureDetector detector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { Bundle args = new Bundle(); args.putBoolean(ChunkModeFragment.EXTRA_TARGET_OPEN, true); args.putString(AppContext.EXTRA_CHAPTER_ID, chapter.getId()); getListener().openTranslationMode(TranslationViewMode.CHUNK, args); return true; } }); holder.mBeginButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return detector.onTouchEvent(event); } }); } else { holder.mBeginButton.setVisibility(View.GONE); } // TODO: indicate completed chapter translations // if(frameTranslation.isTitleFinished()) { // holder.mTargetInnerCard.setBackgroundResource(R.color.white); // } else { // holder.mTargetInnerCard.setBackgroundResource(R.drawable.paper_repeating); // } holder.mTargetBody.setText(mRenderedTargetBody[position]); // ChapterTranslation getChapterTranslation(String chapterSlug); String targetCardTitle = ""; // look for translated chapter title first final ChapterTranslation chapterTranslation = mTargetTranslation.getChapterTranslation(chapter); if(null != chapterTranslation) { targetCardTitle = chapterTranslation.title; } if (targetCardTitle.isEmpty() && !chapterTitle.isEmpty()) { // if no target chapter title translation, fall back to source chapter title if(!chapter.title.isEmpty()) { targetCardTitle = chapterTitle; } } if (targetCardTitle.isEmpty()) { // if no chapter titles, fall back to project title, try translated title first ProjectTranslation projTrans = mTargetTranslation.getProjectTranslation(); if(!projTrans.getTitle().isEmpty()) { targetCardTitle = projTrans.getTitle() + " " + Integer.parseInt(chapter.getId()); } } if (targetCardTitle.isEmpty()) { // fall back to project source title targetCardTitle = mSourceTranslation.getProjectTitle() + " " + Integer.parseInt(chapter.getId()); } holder.mTargetTitle.setText(targetCardTitle + " - " + mTargetLanguage.name); // load tabs holder.mTabLayout.setOnTabSelectedListener(null); holder.mTabLayout.removeAllTabs(); for(ContentValues values:mTabs) { TabLayout.Tab tab = holder.mTabLayout.newTab(); tab.setText(values.getAsString("title")); tab.setTag(values.getAsString("tag")); holder.mTabLayout.addTab(tab); } // select correct tab for(int i = 0; i < holder.mTabLayout.getTabCount(); i ++) { TabLayout.Tab tab = holder.mTabLayout.getTabAt(i); if(tab.getTag().equals(mSourceTranslation.getId())) { tab.select(); break; } } // hook up listener holder.mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { final String sourceTranslationId = (String) tab.getTag(); if (getListener() != null) { Handler hand = new Handler(Looper.getMainLooper()); hand.post(new Runnable() { @Override public void run() { getListener().onSourceTranslationTabClick(sourceTranslationId); } }); } } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { } }); // set up fonts if(holder.mLayoutBuildNumber != mLayoutBuildNumber) { holder.mLayoutBuildNumber = mLayoutBuildNumber; Typography.formatTitle(mContext, holder.mSourceHeading, mSourceLanguage.getId(), mSourceLanguage.getDirection()); Typography.formatTitle(mContext, holder.mSourceTitle, mSourceLanguage.getId(), mSourceLanguage.getDirection()); Typography.format(mContext, holder.mSourceBody, mSourceLanguage.getId(), mSourceLanguage.getDirection()); Typography.formatTitle(mContext, holder.mTargetTitle, mTargetLanguage.getId(), mTargetLanguage.getDirection()); Typography.format(mContext, holder.mTargetBody, mTargetLanguage.getId(), mTargetLanguage.getDirection()); } } @Override public int getItemCount() { return mChapters.length; } public void rebuild() { mLayoutBuildNumber ++; notifyDataSetChanged(); } /** * Toggle the target translation card between front and back * @param holder * @param position * @param swipeLeft * @return true if action was taken, else false */ public void toggleTargetTranslationCard(final ViewHolder holder, final int position, final boolean swipeLeft) { if (mTargetStateOpen[position]) { closeTargetTranslationCard( holder, position, !swipeLeft); return; } openTargetTranslationCard( holder, position, !swipeLeft); return; } /** * Moves the target translation card to the back * @param holder * @param position * @param leftToRight */ public void closeTargetTranslationCard(final ViewHolder holder, final int position, final boolean leftToRight) { if (mTargetStateOpen[position]) { ViewUtil.animateSwapCards(holder.mTargetCard, holder.mSourceCard, TOP_ELEVATION, BOTTOM_ELEVATION, leftToRight, new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mTargetStateOpen[position] = false; } @Override public void onAnimationRepeat(Animation animation) { } }); // re-enable new tab button holder.mNewTabButton.setEnabled(true); } } /** * Moves the target translation card to the back - left to right * @param holder * @param position * @return true if action was taken, else false */ public void closeTargetTranslationCard(final ViewHolder holder, final int position) { closeTargetTranslationCard ( holder, position, true); } /** * Moves the target translation to the top * @param holder * @param position * @param leftToRight */ public void openTargetTranslationCard(final ViewHolder holder, final int position, final boolean leftToRight) { if (!mTargetStateOpen[position]) { ViewUtil.animateSwapCards(holder.mSourceCard, holder.mTargetCard, TOP_ELEVATION, BOTTOM_ELEVATION, leftToRight, new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mTargetStateOpen[position] = true; } @Override public void onAnimationRepeat(Animation animation) { } }); // disable new tab button so we don't accidently open it holder.mNewTabButton.setEnabled(false); } } /** * Moves the target translation to the top * @param holder * @param position * @return true if action was taken, else false */ public void openTargetTranslationCard(final ViewHolder holder, final int position) { openTargetTranslationCard( holder, position, false); } public static class ViewHolder extends RecyclerView.ViewHolder { private final Button mBeginButton; private final TextView mTargetTitle; private final TextView mTargetBody; private final CardView mTargetCard; private final CardView mSourceCard; private final ImageButton mNewTabButton; public TextView mSourceHeading; public TextView mSourceTitle; public TextView mSourceBody; public TabLayout mTabLayout; public int mLayoutBuildNumber = -1; public ViewHolder(View v) { super(v); mSourceCard = (CardView)v.findViewById(R.id.source_translation_card); mSourceHeading = (TextView)v.findViewById(R.id.source_translation_heading); mSourceTitle = (TextView)v.findViewById(R.id.source_translation_title); mSourceBody = (TextView)v.findViewById(R.id.source_translation_body); mTargetCard = (CardView)v.findViewById(R.id.target_translation_card); mTargetTitle = (TextView)v.findViewById(R.id.target_translation_title); mTargetBody = (TextView)v.findViewById(R.id.target_translation_body); mTabLayout = (TabLayout)v.findViewById(R.id.source_translation_tabs); mTabLayout.setTabTextColors(R.color.dark_disabled_text, R.color.dark_secondary_text); mNewTabButton = (ImageButton) v.findViewById(R.id.new_tab_button); mBeginButton = (Button) v.findViewById(R.id.begin_translating_button); } } }