lorensiuswlt/NewQuickAction3D — Issue #6: Inflate actions from XML file
Author: mariotaku
Created: 2012-01-01 04:56:46+00:00
State: open

Hi! I used your code in my own project. I'd like to contribute some code for you!
This code is QuickActionInflater, use it as MenuInflater, example:

```java
QuickActionInflater mInflater = new QuickActionInflater(this);
mQuickAction = new QuickAction(this, QuickAction.VERTICAL);
mInflater.inflate(R.menu.options_menu_playback, mQuickAction);
```

Source code:

```java
/*
* Copyright (C) 2012 The MusicMod Project
*
* 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 net.londatiga.android;

import java.io.IOException;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
import android.view.InflateException;

/**
* This class is used to instantiate menu XML files into Menu objects.
*

* For performance reasons, menu inflation relies heavily on pre-processing of
* XML files that is done at build time. Therefore, it is not currently possible
* to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
* it only works with an XmlPullParser returned from a compiled resource (R.
* something file.)
*/
public class QuickActionInflater {
/** Menu tag name in XML. */
private static final String XML_MENU = "menu";

/** Item tag name in XML. */
private static final String XML_ITEM = "item";

/** Namespaces to read attributes */
private static final String ANDROID_NS = "http://schemas.android.com/apk/res/android";

/** Attribute names */
private static final String ATTR_ID = "id";
private static final String ATTR_TITLE = "title";
private static final String ATTR_ICON = "icon";

private Context mContext;

/**
* Constructs a menu inflater.
*
* @see Activity#getMenuInflater()
*/
public QuickActionInflater(Context context) {
mContext = context;
}

/**
* Inflate a menu hierarchy from the specified XML resource. Throws
* {@link InflateException} if there is an error.
*
* @param menuRes Resource ID for an XML layout resource to load (e.g.,
* R.menu.main_activity)
* @param menu The Menu to inflate into. The items and submenus will be
* added to this Menu.
*/
public void inflate(int menuRes, QuickAction action) {
XmlResourceParser parser = null;
try {
parser = mContext.getResources().getLayout(menuRes);
AttributeSet attrs = Xml.asAttributeSet(parser);

parseMenu(parser, attrs, action);
} catch (XmlPullParserException e) {
throw new InflateException("Error inflating menu XML", e);
} catch (IOException e) {
throw new InflateException("Error inflating menu XML", e);
} finally {
if (parser != null) parser.close();
}
}

/**
* Called internally to fill the given menu. If a sub menu is seen, it will
* call this recursively.
*/
private void parseMenu(XmlPullParser parser, AttributeSet attrs, QuickAction action)
throws XmlPullParserException, IOException {
QuickActionState quickActionState = new QuickActionState(action);

int eventType = parser.getEventType();
String tagName;
boolean lookingForEndOfUnknownTag = false;
String unknownTagName = null;

// This loop will skip to the menu start tag
do {
if (eventType == XmlPullParser.START_TAG) {
tagName = parser.getName();
if (tagName.equals(XML_MENU)) {
// Go to next tag
eventType = parser.next();
break;
}

throw new RuntimeException("Expecting menu, got " + tagName);
}
eventType = parser.next();
} while (eventType != XmlPullParser.END_DOCUMENT);

boolean reachedEndOfMenu = false;
while (!reachedEndOfMenu) {
switch (eventType) {
case XmlPullParser.START_TAG:
if (lookingForEndOfUnknownTag) {
break;
}
tagName = parser.getName();
if (tagName.equals(XML_ITEM)) {
quickActionState.readItem(attrs);
} else {
lookingForEndOfUnknownTag = true;
unknownTagName = tagName;
}
break;
case XmlPullParser.END_TAG:
tagName = parser.getName();
if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
lookingForEndOfUnknownTag = false;
unknownTagName = null;
} else if (tagName.equals(XML_ITEM)) {
// Add the item if it hasn't been added (if the item was
// a submenu, it would have been added already)
if (!quickActionState.hasAddedItem()) {
quickActionState.addItem();
}
} else if (tagName.equals(XML_MENU)) {
reachedEndOfMenu = true;
}
break;

case XmlPullParser.END_DOCUMENT:
throw new RuntimeException("Unexpected end of document");
}

eventType = parser.next();
}
}

/**
* State for the current menu.
*

* Groups can not be nested unless there is another menu (which will have
* its state class).
*/
private class QuickActionState {
private QuickAction action;

private boolean itemAdded;
private int itemId;
private String itemTitle;
private Drawable itemIcon;

public QuickActionState(final QuickAction action) {
this.action = action;

}

/**
* Called when the parser is pointing to an item tag.
*/
public void readItem(AttributeSet attrs) {
// Inherit attributes from the group as default value
Log.d("QuickActionInflater", "item read! itemId = " + itemId);
itemId = attrs.getAttributeResourceValue(ANDROID_NS, ATTR_ID, 0);
itemTitle = mContext.getString(attrs.getAttributeResourceValue(ANDROID_NS, ATTR_TITLE, 0));
itemIcon = mContext.getResources().getDrawable(attrs.getAttributeResourceValue(ANDROID_NS, ATTR_ICON, android.R.color.transparent));
itemAdded = false;
}

private ActionItem setItem() {
return new ActionItem(itemId,itemTitle,itemIcon);
}

public void addItem() {
itemAdded = true;
action.addActionItem(setItem());
}

public boolean hasAddedItem() {
return itemAdded;
}
}

}

```