2
votes

So as you may know the screen space on Google Glass is limited. The Mirror API has a nice "Read more" feature whereby cards with long text are converted into multiple cards through which the user can scroll. I've been trying to achieve the same effect with text in the GDK to no avail.

So the question is, quite simply, how do I display long text to the user with GDK? This is really vital for reading news articles, for instance.

UPDATE: not finding anything currently available, I've created an activity for automatically splitting up text into multiple cards. It's not ideal so hopefully someone can improve upon it. The interface is to start an activity with a class intent and text extra:

case R.id.read_more:
    intent = new Intent(this, ReadMoreActivity.class);
    intent.putExtra(ReadMoreActivity.TEXT, text);
    startActivity(intent);
    return true;

This calls the ReadMoreActivity class which breaks up the text into as many cards as are needed so that the full text can be viewed. The way this is implemented, I wouldn't recommend doing a whole novel this way, but it seems to work up to a dozen or more cards worth of text:

public class ReadMoreActivity extends Activity {

public static final String TEXT = "text";

private static final boolean DEBUG = true;
private static final String TAG = ReadMoreActivity.class.getSimpleName();

private static final float SPACING_MULT = 1.0f;
private static final float SPACING_ADD = 1.0f;
private static final boolean INCLUDE_PAD = false;

private String mText;
private CardScrollView mCardScrollView;
private TextCardScrollAdapter mAdapter;
private List<Card> mCards;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null && savedInstanceState.containsKey(TEXT))
        mText = savedInstanceState.getString(TEXT);
    else if (getIntent() != null && getIntent().hasExtra(TEXT))
        mText = getIntent().getStringExtra(TEXT);
    else {
        Log.e(TAG, "no text found");
        finish();
        return;
    }

    createCardsFromText();
    mAdapter = new TextCardScrollAdapter();

    View rootLayout = getLayoutInflater().inflate(R.layout.card_scroll_layout, null);
    mCardScrollView = (CardScrollView)rootLayout.findViewById(R.id.card_scroll_view);
    mCardScrollView.setAdapter(mAdapter);
    mCardScrollView.activate();
    setContentView(rootLayout);
}

private void createCardsFromText() {
    int textSize = getResources().getDimensionPixelSize(R.dimen.card_main_layout_main_min_textSize);
    int maxWidth = getResources().getDimensionPixelSize(R.dimen.card_main_layout_main_width);
    int maxHeight = getResources().getDimensionPixelSize(R.dimen.card_main_layout_main_height);

    TextView tv = new TextView(this);
    tv.setTextSize(textSize);
    tv.setText(mText);
    StaticLayout measure = new StaticLayout(tv.getText(), tv.getPaint(),
            maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 1.0f, false);
    int numLines = measure.getLineCount();
    if (DEBUG) Log.i(TAG, "calculated lines=" + numLines + " for text=[" + mText + "]");

    int linesPerCard = maxHeight / textSize;
    int numFullCards = numLines / linesPerCard;
    int numCards = numFullCards + (numLines % linesPerCard > 0 ? 1 : 0);

    mCards = new ArrayList<Card>();
    for (int cardNum = 0; cardNum < numCards; cardNum++) {
        int startLine = cardNum * linesPerCard;
        int nextStartLine = startLine + linesPerCard;
        int startOffset = measure.getLineStart(startLine);
        int endOffset = (nextStartLine < numLines) ? measure.getLineStart(nextStartLine) : mText.length();
        String cardText = mText.substring(startOffset, endOffset);
        Card card = new Card(this);
        card.setText(cardText);
        String cardFootnote = getString(R.string.read_more_format, cardNum + 1, numCards);
        card.setFootnote(cardFootnote);
        mCards.add(card);
    }
}

private class TextCardScrollAdapter extends CardScrollAdapter {
    @Override
    public int findIdPosition(Object id) {
        return -1;
    }
    @Override
    public int findItemPosition(Object item) {
        return mCards.indexOf(item);
    }
    @Override
    public int getCount() {
        return mCards.size();
    }
    @Override
    public Object getItem(int position) {
        return mCards.get(position);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return mCards.get(position).toView();
    }
}
}

The layout file card_scroll_layout.xml' for this is pretty simple, just a wrapper around theCardScrollView`:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black">

    <com.google.android.glass.widget.CardScrollView
        android:id="@+id/card_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

The dimensions file dimens.xml needs to know the card dimensions for the text calculation:

<dimen name="card_main_layout_main_width">560px</dimen>
<dimen name="card_main_layout_main_height">240px</dimen>
<dimen name="card_main_layout_main_min_textSize">30px</dimen>
<dimen name="card_main_layout_footnote_width">560px</dimen>
<dimen name="card_main_layout_footnote_height">40px</dimen>

Also in your strings.xml you need something for the footnote so the users knows their position in the Read More:

<string name="read_more_format">read more ~ page %1$d of %2$d</string>
1
Any update in GDK to show ling text in card builder?Ankur Chaudhary

1 Answers

1
votes

As of now, GDK doesn't offer the API to show a menu for a static Card object; only the Mirror API offers that ("Read more" is just one menu option). Google will very likely add the menu support for static cards in the next GDK update.

You'll have to use a live card or immersion to display long text, as both live cards and immersions can attach menus and give you more control over UI. Please see https://developers.google.com/glass/develop/gdk/ui/index for a quick comparison of the three types of UI options.


You can actually make a Card seem to have a menu attached to it by attaching a menu to an activity where you create a CardScrollView of a bunch of Card objects - but those Cards are not really static cards in the timeline, although they are created using the same Card API as static cards.