Java SDK Bugs / Gotchas
  • SDK fix classes

Use the V49 Classes in place of the Bananas parent classes to fix these and other bugs and to get some useful enhancements to make your life easier.

  • Missing Constants (From MultiTaskerApplication):
/** according to an error logged: "rendered text exceeded max allowed dimensions of 1920x1080" */
public static final int LIMIT_TEXT_RENDER_WIDTH=1920;
/** according to an error logged: "rendered text exceeded max allowed dimensions of 1920x1080" */
public static final int LIMIT_TEXT_RENDER_HEIGHT=1080;  
 
/** Media status code, resource at end of stream. */
public static final int RSRC_STATUS_END = 11;
/** Media status code, resource at end of available buffer. */
public static final int RSRC_STATUS_BUFFER_OVERFLOW = 12;
  • SDK Bug in error handling during destroy (From MultiTaskerApplication)
    private boolean destroyedAlready = false;
    public void destroy() {
        // protect against SDK bug: if there is a fatal error during below
        // operations (e.g. remove operations) close gets called which calls
        // destroy again.
 
        // what happens: after the 2nd call to here, we'll get HMEException from 
        // the remove call that caused it which contains the actual cause
        // (unless it was originally an HMEException)
        // setId(-1) has not been called on the object yet
 
        // SO: want to catch HMEExceptions if we want to finish our destroy operation
        if(destroyedAlready)
            return;
        destroyedAlready = true;
 
//... operations that could cause a fatal error, e.g. "remove" calls
 
        super.destroy(); // in case e.g. BApplication ever starts doing something there.
    }
  • handleApplicationError doesn't include a resource ID for resource errors (From MultiTaskerApplication)
    /**
     * Improved version of
     * {@link Application#handleApplicationError(int, String)} that includes the
     * resourceId related to the error - critical for knowing when you have a
     * problem with a resource you created. Default implementation simply calls
     * {@link #handleApplicationError(int, String)}. ApplicationError codes 1
     * (e.g. "can't create rsrc. unsupported stream type
     * /avatar/cf1e61a4330e75d5d1d7a744c5ef38c4") and 3 (e.g. "resource 2091 not
     * found (type type[-1])") about a resource never provide a resourceinfo
     * event!
     * @param errorCode the error code of the error (one of the
     *        {@link IHmeProtocol} APP_ERROR_ constants)
     * @param errorText details of the error
     * @param resourceId the id associated with the error - could be null.
     *        Suitable for use with {@link #getResource(Object)} (or
     *        ((WeakReference){@link #getResources()}.get(Object)).get() to
     *        avoid automatic SDK warning message when it's not found)
     * @return true if the event was handled.
     */
    public boolean handleApplicationError(int errorCode, String errorText, Integer resourceId) {
        if( handleApplicationError( errorCode, errorText ) ) {
            return true; 
        } else {
            //FIXME add processing needed by e.g. BackgroundView to know when it has a bad resource.
            return false;
        }
    }
 
    public boolean handleEvent(HmeEvent event) {
//...
        if(event.getOpCode() == EVT_APP_INFO) {
            HmeEvent.ApplicationInfo info = (HmeEvent.ApplicationInfo)event;
            if (info.getMap().get("error.code") != null) {
                int errorCode = Integer.parseInt((String)info.getMap().get("error.code"));
                Integer resourceId = null;
                if(info.getMap().get("error.rsrc") != null) {
                    try {
                        resourceId = new Integer((String)info.getMap().get("error.rsrc"));
                    } catch(NumberFormatException e) {
                        resourceId = null;
                    }
                }
                String errorText = (String)info.getMap().get("error.text");
                if(resourceId != null) {
                    WeakReference res_wr = (WeakReference)getResources().get(resourceId);
                    if(res_wr != null && res_wr.get() != null) {
                        Resource res = (Resource)res_wr.get();
                        Map resInfoMap = info.getMap();
                        res.postEvent( new ResourceInfo(resourceId.intValue(), Resource.RSRC_STATUS_ERROR, resInfoMap) );
                    }
                }
                return handleApplicationError(errorCode, errorText, resourceId);
            }
        }
        return super.handleEvent( event );
    }
  • parseQuery(String) (and therefore createStream(String, Map)) is completely broken in SDK, here is the fix (from MultiTaskerApplication)
    /**
     * SDK Bug Fix - OMG! sick bug in HMEObject that has never been noticed or
     * exercised since nobody has apparently attempted to do App-in-app with
     * {@link #createStream(String, Map)}. They forgot a "new" keyword and
     * auto-"fixed" the problem creating a method named LinkedHashMap that has
     * default method content returning null! Also, calling code won't handle
     * the nulls that are returned in certain circumstances, so now returning an
     * empty map here. Lucky for us it is public and non-final. {@inheritDoc}
     * @see com.tivo.hme.sdk.HmeObject#parseQuery(java.lang.String)
     */
    public Map parseQuery(String query) {
        Map map = new LinkedHashMap();
        if (query == null || query.indexOf('=') == -1) {
            return map;
        }
 
        int at = 0;
        int len = query.length();
 
        if (query.startsWith("?")) {
            ++at;
        }
        do {
            // find = and &
            int equal = query.indexOf('=', at);
            if (equal < 0) {
                throw new IllegalArgumentException("invalid query (trailing key, = not found)");
            }
            int amp = query.indexOf('&', equal);
            if (amp == -1) {
                amp = query.length();
            }
 
            // add the key/value
            try {
                String key = URLDecoder.decode(query.substring(at, equal), "UTF-8");
                String value = URLDecoder.decode(query.substring(equal + 1, amp), "UTF-8");
                map.put(key.toLowerCase(), value);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
 
            // advance
            at = amp + 1;
        } while (at < len);
        return map;
    }
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License