/* * Copyright 2014 Yaroslav Mytkalyk * 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.docd.purefm.ui.view; import java.io.File; import android.content.Context; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.util.AttributeSet; import android.view.View; import android.view.ViewParent; import android.widget.HorizontalScrollView; import android.widget.TextView; /** * TextView that builds and shows clickable bread crumb links * Don't use {@link #setText(char[], int, int)} method to build view. * Instead, use {@link #setFile(java.io.File)} * * @author Doctoror */ public class BreadCrumbTextView extends TextView { private static final String SEQUENCE_SEPARATOR = " > "; private static final String ROOT = "root"; public interface OnSequenceClickListener { void onSequenceClick(String sequence); } private final Runnable SCROLL_RIGHT = new Runnable() { @Override public void run() { parent.fullScroll(View.FOCUS_RIGHT); } }; private OnSequenceClickListener mOnSequenceClickListener; private HorizontalScrollView parent; @SuppressWarnings("unused") public BreadCrumbTextView(Context context) { super(context); init(); } @SuppressWarnings("unused") public BreadCrumbTextView(Context context, AttributeSet attrs) { super(context, attrs); init(); } @SuppressWarnings("unused") public BreadCrumbTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { this.setMovementMethod(LinkMovementMethod.getInstance()); } public final void setFile(final File file) { setText(formatFilePath(file)); if (this.parent == null) { final ViewParent parent = this.getParent(); if (parent == null || !(parent instanceof HorizontalScrollView)) { throw new RuntimeException("BreadCrumbTextView must have HorizontalScrollView parent"); } this.parent = (HorizontalScrollView) parent; } this.parent.postDelayed(SCROLL_RIGHT, 100L); } private CharSequence formatFilePath(final File file) { final SpannableStringBuilder formatted = new SpannableStringBuilder(ROOT + file.getPath()); final int length = formatted.length(); if (formatted.charAt(length - 1) == File.separatorChar) { formatted.delete(length - 1, length); } int prevIndex = 0; int index; do { String currentlyFormattedString = formatted.toString(); index = currentlyFormattedString.indexOf(File.separatorChar, prevIndex); if (index == -1) { formatted.setSpan(new NavigationSpan(toPathForListener(currentlyFormattedString.substring(0, formatted.length()))), prevIndex, formatted.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); } else { formatted.replace(index, index + 1, SEQUENCE_SEPARATOR); currentlyFormattedString = formatted.toString(); formatted.setSpan(new NavigationSpan(toPathForListener(currentlyFormattedString.substring(0, index))), prevIndex, index, Spannable.SPAN_INCLUSIVE_INCLUSIVE); prevIndex = index + SEQUENCE_SEPARATOR.length(); } } while (index != -1); return formatted; } private static String toPathForListener(final String withSequences) { return withSequences.replace(SEQUENCE_SEPARATOR, File.separator); } public final void setOnSequenceClickListener(OnSequenceClickListener l) { this.mOnSequenceClickListener = l; } public final void fullScrollRight() { if (this.parent != null) { this.parent.fullScroll(View.FOCUS_RIGHT); } } private final class NavigationSpan extends ClickableSpan implements ParcelableSpan { private final String mPath; NavigationSpan(final String path) { // remove artificial "root" prefix if (path.equals(ROOT)) { this.mPath = File.separator; } else { this.mPath = path.substring(ROOT.length(), path.length()); } } @Override public void onClick(final View widget) { mOnSequenceClickListener.onSequenceClick(this.mPath); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.mPath); } @Override public int getSpanTypeId() { return 666; } @Override public int describeContents() { return 0; } } }