понедельник, 4 июля 2016 г.

Building and running Android Studio gradle project on android device

Recently I was just checking out if AIDE team added support to build gradle-based projects. So, indeed they added:

AIDE also supports basic Android Studio projects, which follow the default project structure. The full gradle build system is not yet supported though
(source: http://www.android-ide.com/tutorial_androidstudio.html)

So, if your gradle setup is simple, you can just open your android project in AIDE and run.

But what to do if not? As before we have a workarond: create eclipse project files (.classpath + .project) for our project. AIDE works with eclipse project structure quite well. But what to do with the dependencies? It's insane to write all of them by hand. Likely, LibGdx enthusiasts gives us script which generates those strings for us (see for example eclipse task here)

I'll give details on example of how to build & run LibGdx gradle project in AIDE.

суббота, 30 апреля 2016 г.

MSI X99A SLI PLUS Windows 10 hang issue resolved

I've become a happy owner of MSI X99A Sli Plus mobo. I already read about many issues with asus burninng, msi burning, random Windows hangs due to problems with USB. And I want to say that I encountered nothing of that with my MSI mobo (bios 1.9, recently updated it to 1.B).

But I encountered an annoying hang for which I found a solution, so I want to share it because I don't see it anywhere on the web.

Problem: after unplugging PC from the outlet and plugging back Windows 10 hangs for the first time.
Solution: insert USB stick in one of the USB3.1 slots. You just need to keep 1 USB3.1 slot occupied to not run into that problem

четверг, 7 апреля 2016 г.

C#: Why Dictionary with hashcode as key is dangerous

Recently, I encountered following code:

        private readonly Dictionary<int, View> _listCellsForDisposal;
            if (!_listCellsForDisposal.Contains(view.GetHashCode()))
             {
                _listCellsForDisposal.Add(view.GetHashCode(), view);
             }

The intent of that code is just add item to the list if it doesn't present. But is it doing work well? It doesn't. Let's see why.

At first, let's look at the signature of GetHashCode:

public override int GetHashCode()

So, it returns int. int in c# is platform-dependent. For example int32 takes 4 bytes, it's range is:

-2,147,483,648 to 2,147,483,647

(source: https://msdn.microsoft.com/ru-ru/library/5kzh1b5w.aspx)

But how many objects can we add to the dictionary? Is it limited to about 4 billions (int32 can count) ? Obviously, not. It's limited just with computer's memory. That's the first point (1)

How do you think we access values of dictionary so fast? One of the simplest implementation of Dictionary is an array where key's hashcode is used as index. But 2 different objects can have same hashcode (at least because of (1)). So, how do we store that 2 objects? The only left solution is to keep not 1 object per hashcode but many, so use list. And to differentiate equals() comes into play.

(source: https://msdn.microsoft.com/en-us/library/4yh14awz(v=vs.110).aspx)

So, general rule to write equals and hashcode is:
1. hashcode may be same for 2 different objects
2. if equals() return true for 2 objects, than hashcode must be equal too

Keeping that in mind will return to code:

private readonly Dictionary<int, View> _listCellsForDisposal;
            if (!_listCellsForDisposal.Contains(view.GetHashCode()))
             {
                _listCellsForDisposal.Add(view.GetHashCode(), view);
             }

So, what does that code do? Keeping in mind that 2 different views can have same hashcode it just wount add 2nd object to dictionary. The proper solution will be:

private readonly Dictionary<View, View> _listCellsForDisposal;
            if (!_listCellsForDisposal.Contains(view))
             {
                _listCellsForDisposal.Add(view, view);
             }

Or simply:

private readonly HashSet<View> _listCellsForDisposal;
            if (!_listCellsForDisposal.Contains(view))
             {
                _listCellsForDisposal.Add(view);
             }

среда, 9 марта 2016 г.

Why static is pain

Bugs, related to static variables may be difficult to understand. I don't think it's worth investigating. As a general practice, just avoid keeping references to objects in static. When using a static reference you allways must ask youself when to free this static and when to use.

Example: keep reference to view in static variable. User closes our app, OS frees memory, so all statics are freed too. When user opens our app again, static gets newly created view. All is well.
Then, time passes and we add background running service in our app. So, when user quits our app OS can't just free memory - static may be accessed from our background service. And reference to view remains. Afterwards user open app again and static has view that was created FOR ALREADY DESTROYED VIEW HIERARCHY. And it is luck if it will work with new one. It is only one example of what can happen when using statics. Using statics is not bad. But statics need more attention from developer. They often must be freed explicitly (for example, recreated at app start). Better keep only primitive data in statics - ints, strings...

Connect to genymotion emulator from another machine via wifi

1. Change NAT to bridge in VirtualBox settings for emulator (network adapter is same used to access network using which you'll connect)
2. download adb wireless by henry (root)
3. enable debugging
4. disable all adb processes started by genymotion for emulator (I choose "use custom sdk" and then renamed adb.exe -> _adb.exe) and restart emulator
5. adb connect ip:5555
done
adb devices should say:
device (not offline)

воскресенье, 14 февраля 2016 г.

Method override in C#

Hi, everyone! Recently, I decided to put all basic info about method overriding in one place. Keeping in mind that method, which is overriden may be virtual or not. Or you may just want to make name hiding (don't know why anybody may want to do that). So, here we go.

Non-virtual methods

class A
{
    void Foo() {}
}

class B: A
{
    void Foo() {} // compiler warning B.Foo() hides A.Foo()
}
To resolve warning:
class B: A
{
    new void Foo() {} // explicitly say to compiler, that hiding is intended using "new" keyword
}

воскресенье, 6 сентября 2015 г.

Making fast performance complex animations in android

Recently, I've ran into a problem with animating item's height inside a list for some devices with poor performance. And I've done a library for that. It animates RecyclerView or ListView item's height, but isn't heavy-bounded to that containers - see on github. That animation is also known as expand-collapse animation. Here I describe main problems and solutions founded so far, which is used in library. Library is finished and I'll be very glad if you find some issues or propose pull requests.

So, let's start. To be more clear, I'll write some straightforward code which does what I'm telling about:

ValueAnimator animator = ValueAnimator.ofInt(100, 300);
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
    listViewItem.getLayoutParams().height = (Integer) animation.getAnimatedValue();
    listViewItem.requestLayout();
  }
});
animator.start();
Many libraries, I so before, use this technique. But that is heavy because of requestLayout() - it causes layout and measure steps repeated again each call. From the other side, it has it's rights for living - android developers already introduces nice LayoutTransition which does it's job using exactly same technique: the layout is animated, so layout and measure steps are done again and again until animation finish. That's the only way making CHANGE_APPEARING and CHANGE_DISAPPEARING work when animating for example from layout with items AC to layout with items ABC (C must go down with animating and recalculating everything around to keep layout constraints). So, the following must be considered performance hack. Which may be useful to know when making simple animations.

So, here are some key thoughts:

1. So, all we know, that following animations are fast:
a. Alpha
b. Scale
c. Translate
d. Rotate

2. To change items with just translation and not repositioning other items we can make items heigher than they are so that each next item will hide bottom part of the previos one. They will be like game cards - each one above the other but not fully to see what card is below. This can be achived in many ways:
a. negative bottom margin
b. simply make a wrapper for view which will hold item and clip. FrameLayout does that by default

3. That's not all. Because items are still there. We must care about touch dispatching. Suppose we are clicking on item to expand it. After expand it will be above the previos. We click again for some reason and there is a button on the previos card which steals touch event! User even don't see it, because it is below, but android doesn't use only item's visibility when dispatching touch events. We override dispatchTouchEvent() of item which is animating. So we do simple wrapper for each list item which does the work for us:

    public class VerticalClipLayout extends FrameLayout {
    private float expandCoef = 0; // collapsed by default
    private float clipCoef = 1; // so, full clipping of animated part

    public VerticalClipLayout(Context context) {
        super(context);
        initialize();
    }

    public VerticalClipLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public VerticalClipLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    @TargetApi(21)
    public VerticalClipLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initialize();
    }

    private void initialize() {
        setWillNotDraw(false);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // do not dispatch touch events to view or it's children when clicking the clipped area
        return ev.getY() < getClippedHeight() && super.dispatchTouchEvent(ev);
    }

    // In xml layout_height of VerticalClipLayout is the collapsed height. Inside VerticalClipLayout
    // must be exactly one child view/layout. It's height will be the expanded height.
    // So, setting layout_height="match_parent" for child view is useless.
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if(getChildCount() == 1) {
            View child = getChildAt(0);

            super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, MeasureSpec.getMode(heightMeasureSpec)));
            MarginLayoutParams margins = (MarginLayoutParams)getLayoutParams();
            margins.bottomMargin = MeasureSpec.getSize(heightMeasureSpec) - child.getMeasuredHeight();
        } else {
            throw new IllegalArgumentException("VerticalClipLayout should have exactly 1 child");
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // clipping bottom part to force see video beneath
        canvas.clipRect(0, 0, getWidth(), getClippedHeight()); //Region.Op.REPLACE is no use because some applied clippings (for example from action bar) are not applied sometimes with REPLACE
        super.onDraw(canvas);
    }

    // set's current expand in 0..1 range
    // expandCoef is the coef to expand - y changes when expanding
    public void setExpandCoef(float expandCoef) {
        if(0 > expandCoef || 1 < expandCoef) {
            throw new IllegalArgumentException("expandCoef is out of range [0, 1], expandCoef: " + expandCoef);
        }
        this.expandCoef = expandCoef;

        setTranslationY(expandCoef * ((MarginLayoutParams) getLayoutParams()).bottomMargin);
    }

    // clipHeightCoef is in range 0..1, where 1 means clip all the animated area and show only the allways shown part at the top. 0 means show everything, you'll see fully expaded view
    public void setClipCoef(float clipHeightCoef) {
        if(0 > clipHeightCoef || 1 < clipHeightCoef) {
            throw new IllegalArgumentException("clipHeightCoef is out of range [0, 1], clipHeightCoef: " + clipHeightCoef);
        }
        clipCoef = clipHeightCoef;
        invalidate();
    }

    public float getExpandCoef() {
        return expandCoef;
    }

    // returns y, which view would have, if setExpandCoef(1) was called
    public float getYWhenExpanded() {
        return super.getTop() + ((MarginLayoutParams) getLayoutParams()).bottomMargin;
    }

    public int getExpandedHeight() {
        return getHeight();
    }

    public float getClippedHeight() {
        return getHeight() + clipCoef * ((MarginLayoutParams) getLayoutParams()).bottomMargin;
    }
}
4. I also like that wrapping view, because of encapsulating actual implementation (negative bottom margin or whatever) inside that class to not mess things around.

5. Make an animator class, which just helps us control some ValueAnimator
public class ExpandCollapseAnimator implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {

    public ExpandCollapseAnimator(float speed);
    public void setOnViewExpandCollapseListener(OnViewExpandCollapseListener listener);

    //call in onResume()
    public void start();

    //call in onPause()
    public void pause();

    // adds view to processing
    public void add(int position, VerticalClipLayout view);

    public void remove(int position);

    // start expand animation for view. Note, that it is neccessary for view to be add()'ed before that call
    public void setExpanding(int position);

    public interface OnViewExpandCollapseListener {
        void onViewStartExpanding(int position, VerticalClipLayout v);
        void onViewExpanded(int position, VerticalClipLayout v);
        void onViewStartCollapsing(int position, VerticalClipLayout v);
        void onViewsChanging();
    }
}

Note: that implementation is compatible with both ListView and RecyclerView. And it has no problems with not animating footer. Thats example:
recyclerView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
            @Override
            public void onChildViewAdded(View parent, final View child) {
                if(recyclerView.getChildViewHolder(child).getItemViewType() != PhoneCardAdapter.VIEW_TYPE_CARD) {
                    return; // just don't add it to our animator
                }
                child.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        child.getViewTreeObserver().removeOnPreDrawListener(this);

                        int position = recyclerView.getChildAdapterPosition(child);
                        animator.add(position, (VerticalClipLayout) child);
                        return false;
                    }
                });
            }

            @Override
            public void onChildViewRemoved(View parent, View child) {
                if(recyclerView.getChildViewHolder(child).getItemViewType() != PhoneCardAdapter.VIEW_TYPE_CARD) {
                    return;
                }
                animator.remove(position);
            }
        });

6. Almost forgot, using OnPreDrawListener() we just assure, that item is positioned, scaled, etc. properly so we can take that things and animate properly.

All this is done as library available with example usage on github