Pages

getteammates.com

Friday, 25 December 2015

A simple mask shader for Unity3D

Let me share a simple mask shader which takes two texture the main texture and the mask texture. Main texture is rendered if the mask texture does not have alpha as 0.

Shader "TRSquareLab/Mask" {

     Properties {
          // Main Texture
          _MainTex ("Texture", 2D) = "white" { }
          
          // Mask texture, color from main texture will be applied if
          // the mask texture does not have alpha as 0
          _MaskTex ("Mask", 2D) = "white" { }     
     }
     
     SubShader {
          Pass
               {
                    CGPROGRAM
                    
                    #pragma vertex vert
                    #pragma fragment frag
                    #include "UnityCG.cginc"
               
                    sampler2D _MainTex;
                    sampler2D _MaskTex;
               
                    struct v2f {
                         float4 pos : SV_POSITION;
                         float2 uv : TEXCOORD0;
                    };
                    
                    float4 _MainTex_ST;
                    
                    v2f vert (appdata_base v)
                    {
                         v2f o;
                         o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                         o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
                         return o;
                    }
                    
                    fixed4 frag (v2f i) : SV_Target
                    {
                         fixed4 texcol = tex2D (_MainTex, i.uv);
                         fixed4 maskcol = tex2D (_MaskTex, i.uv);
                         // Discard the pixel if the alpha is 0
                         if (maskcol.a == 0) {
                              discard;
                         }
                         // Else return the main texture color
                         return texcol;
                    }
                    
                    ENDCG
               }
     }
     
     FallBack Off
}


A simple implementationof item decorator for RecyclerView

I am going to show you a simple implementation of an item decorator which fills the item and also draws a boarder.
package com.example.recycleviewexample;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import java.util.Random;

/**
 * A simple item decorator which will,
 * 1. Fill the item view with a Random color from ColorList
 * 2. Draw a dark Gray coloured boarder
 */
public class SimpleItemDecorator extends RecyclerView.ItemDecoration {
    private Paint mPaintFill;    // This Paint object is used for filling the view
    private Paint mPaintStroke;  // This Paint object is used for drawing the boarder

    private Random mRandom = new Random();

    // Item view will be filled with one of the below given colour
    // which is selected randomly
    private static final int[] ColorList = {
            Color.RED,
            Color.GREEN,
            Color.BLUE,
            Color.YELLOW,
            Color.CYAN,
            Color.MAGENTA,
            Color.GRAY};

    public SimpleItemDecorator(Context context) {

        // Create the fill paint
        mPaintFill = new Paint();
        mPaintFill.setStyle(Paint.Style.FILL);

        // Create the stroke paint
        mPaintStroke = new Paint();
        mPaintStroke.setStyle(Paint.Style.STROKE);
        mPaintStroke.setStrokeWidth(5.0f);
        mPaintStroke.setColor(Color.DKGRAY);

        // Set seed for random number generator
        mRandom.setSeed(System.currentTimeMillis());
    }

    // This method is called before the item view is rendered
    // This method will be used for filling the item view
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

        int childCount = parent.getChildCount();

        // For each child view we would need to calculate the
        // bounds and fill it by drawing a rectangle
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

            // Get the bounds
            RectF rect = getBounds(child);

            // Set a random color
            mPaintFill.setColor(ColorList[mRandom.nextInt(ColorList.length)]);

            // Fill the view by drawing a rectangle with rounded corner
            c.drawRoundRect (rect, 20, 20, mPaintFill);
        }
    }

    // This method is called after the item view is rendered
    // This method will be used for drawing the boarder
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {

        int childCount = parent.getChildCount();

        // For each child view we would need to calculate the
        // bounds and fill it by drawing a rectangle
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

            // Get the bounds
            RectF rect = getBounds(child);

            // Fill the view by drawing a rectangle with rounded corner
            c.drawRoundRect (rect, 10, 10, mPaintStroke);
        }
    }

    // Calculate the bounds for a child
    private RectF getBounds(View child) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

        int left = child.getPaddingLeft();
        int right = child.getWidth() - child.getPaddingRight();

        int top = child.getTop() - params.topMargin;
        int bottom = child.getBottom() + params.bottomMargin;

        return new RectF(left, top, right, bottom);
    }
}

Complete example can be found at https://github.com/trsquarelab/androidexamples/tree/master/RecycleViewExample

Tuesday, 22 December 2015

Creating custom item animator implementation for RecyclerView

This post is on creating custom animation in Android RecyclerView.

It is recommended that you go through the earlier post on RecyclerView.

To implement custom animation you can derive from RecyclerView.ItemAnimator or SimpleItemAnimator, which is derived from RecyclerView.ItemAnimator.

This post is based on the assumption that you are deriving your class from SimpleItemAnimator.

Types of animation in RecyclerView


Lets see the basic types of animation supported in a RecyclerView
  • Add animation : This is performed when adding a new element to the recycler view
  • Remove animation : This is performed when removing an element from the recycler view
  • Change animation : This is performed when an element is changed in the RecyclerView
  • Move animation : This is performed when element(s) are required to move to as a consequence of add or remove operation.

Implementing animations

For all the method you can return false to not to have animation.

Add Animation

Method animateAdd will be called when a new element is added , the prototype of the method is shown below,
public boolean animateAdd(final ViewHolder holder);

In this method you should,
  1. stop any previously running animation for this view
  2. register the view for add animation, do not start with the animation here, a different method will be called for that purpose. There is no method to register for animation, you can probably keep the view in a array.

Remove Animation

Method animateRemove will be called when an element is removed, the prototype of the method is shown below,
public boolean animateRemove(final ViewHolder holder)
The step you take when this method is called is very similar to that of animateAdd

In this method you should,
  1. stop any previously running animation for this view
  2. register the view for remove animation, do not start with the animation here. A different method will be called for that purpose.

Change Animation

animateChange will be called to start the animations for changing an element. The prototype of the method is shown below,
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
                                 int fromX, int fromY, int toX, int toY)

In this method you should,
  1. stop any previously running animation for this view
  2. register the views for change animation, do not start with the animation here. A different method will be called for that purpose.

Move animation

animateMove is called when elements are required to move to accommodate a new element or to fill a vacant spot when an element is removed.
The prototype of the method is shown below,
public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
                               int toX, int toY)

In this method you should,
  1. stop any previously running animation for this view
  2. register the views for move animation, do not start with the animation here. A different method will be called for that purpose.

Running the animation

runPendingAnimations method will be called to start the animations for the views for which one of the animateXXX method is called.

In this method run the animation in the following order,
  1. Remove animations
  2. Move animations once the remove animation is complete. Use getRemoveDuration to get the duration of remove animation. Use ViewCompat.postOnAnimationDelayed to run an animation after a specified delay.
  3. Change animations once remove animations and move animations are complete. Use getMoveDuration to get the duration of move animations.
  4. Add animations once remove animations, move animations and change animations are complete. Use getChangeDuration to get the duration of change animations.
Please remember to call the corresponding dispatch method. For example dispatchAddStarting when add animation is starting, dispatchAddFinished when add animation is finished. There is dispatch method for each type of animations.
These method will be calling RecyclerView.dispatchAnimationStarted and RecyclerView.dispatchAnimationsFinished eventually.

End calls to stop the animation

endAnimation will be called to stop animation on a specified view.
endAnimations will be called to stop all the running animations.

You would need to keep track of running animations. Method isRunning will be called to check whether animations are running or not.

Thats it, now you can start implementing your own animation for RecyclerView!

As you can see, creating a custom animation is not a very simple process.

The easy way

Let me share with you an easy way to implement custom animation. By default RecyclerView has DefaultItemAnimator set as the animator.
To add your own animation you can take the code of DefaultItemAnimator and modify it according to your need.


You would want to modify method animateRemoveImpl, animateMoveImpl, animateChangeImpl or animateAddImpl to add your custom animation for remove, move, change and add operations respectively.

I have modified the file to support my own animation. My animations are,
  1. Scale to right side when element is removed
  2. Scale from left side when a new element is added
  3. Do animation 1 and 2 in that order when element is changed.
I have kept the same implementation for the move animation.  


Modified file is pasted below for your reference, I have highlighted the modified part.

/*
 * Copyright (C) 2014 The Android Open Source 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 com.example.recycleviewexample;

import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.support.v7.widget.SimpleItemAnimator;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.util.Log;
import android.view.View;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

public class ScaleInItemAnimator extends SimpleItemAnimator {
    private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<ViewHolder>();
    private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<ViewHolder>();
    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<MoveInfo>();
    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<ChangeInfo>();


    private ArrayList<ArrayList<ViewHolder>> mAdditionsList =
            new ArrayList<ArrayList<ViewHolder>>();
    private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<ArrayList<MoveInfo>>();
    private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<ArrayList<ChangeInfo>>();


    private ArrayList<ViewHolder> mAddAnimations = new ArrayList<ViewHolder>();
    private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<ViewHolder>();
    private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<ViewHolder>();
    private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<ViewHolder>();

    private static class MoveInfo {
        public ViewHolder holder;
        public int fromX, fromY, toX, toY;
        private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
            this.holder = holder;
            this.fromX = fromX;
            this.fromY = fromY;
            this.toX = toX;
            this.toY = toY;
        }
    }

    private static class ChangeInfo {
        public ViewHolder oldHolder, newHolder;
        public int fromX, fromY, toX, toY;
        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
            this.oldHolder = oldHolder;
            this.newHolder = newHolder;
        }
        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
                           int fromX, int fromY, int toX, int toY) {
            this(oldHolder, newHolder);
            this.fromX = fromX;
            this.fromY = fromY;
            this.toX = toX;
            this.toY = toY;
        }
    }

    @Override
    public void runPendingAnimations() {
        boolean removalsPending = !mPendingRemovals.isEmpty();
        boolean movesPending = !mPendingMoves.isEmpty();
        boolean changesPending = !mPendingChanges.isEmpty();
        boolean additionsPending = !mPendingAdditions.isEmpty();
        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
            // nothing to animate
            return;
        }
        // First, remove stuff
        for (ViewHolder holder : mPendingRemovals) {
            animateRemoveImpl(holder);
        }
        mPendingRemovals.clear();
        // Next, move stuff
        if (movesPending) {
            final ArrayList<MoveInfo> moves = new ArrayList<MoveInfo>();
            moves.addAll(mPendingMoves);
            mMovesList.add(moves);
            mPendingMoves.clear();
            Runnable mover = new Runnable() {
                @Override
                public void run() {
                    for (MoveInfo moveInfo : moves) {
                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
                                moveInfo.toX, moveInfo.toY);
                    }
                    moves.clear();
                    mMovesList.remove(moves);
                }
            };
            if (removalsPending) {
                View view = moves.get(0).holder.itemView;
                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
            } else {
                mover.run();
            }
        }
        // Next, change stuff, to run in parallel with move animations
        if (changesPending) {
            final ArrayList<ChangeInfo> changes = new ArrayList<ChangeInfo>();
            changes.addAll(mPendingChanges);
            mChangesList.add(changes);
            mPendingChanges.clear();
            Runnable changer = new Runnable() {
                @Override
                public void run() {
                    for (ChangeInfo change : changes) {
                        animateChangeImpl(change);
                    }
                    changes.clear();
                    mChangesList.remove(changes);
                }
            };
            if (removalsPending) {
                ViewHolder holder = changes.get(0).oldHolder;
                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
            } else {
                changer.run();
            }
        }
        // Next, add stuff
        if (additionsPending) {
            final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();
            additions.addAll(mPendingAdditions);
            mAdditionsList.add(additions);
            mPendingAdditions.clear();
            Runnable adder = new Runnable() {
                public void run() {
                    for (ViewHolder holder : additions) {
                        animateAddImpl(holder);
                    }
                    additions.clear();
                    mAdditionsList.remove(additions);
                }
            };
            if (removalsPending || movesPending || changesPending) {
                long removeDuration = removalsPending ? getRemoveDuration() : 0;
                long moveDuration = movesPending ? getMoveDuration() : 0;
                long changeDuration = changesPending ? getChangeDuration() : 0;
                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
                View view = additions.get(0).itemView;
                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
            } else {
                adder.run();
            }
        }
    }

    @Override
    public boolean animateRemove(final ViewHolder holder) {
        endAnimation(holder);
        mPendingRemovals.add(holder);
        return true;
    }

    private void animateRemoveImpl(final ViewHolder holder) {
        final View view = holder.itemView;
  
  // Set the pivot to width so that the element scale to the right
  // Also changed the animation to scaleX
        ViewCompat.setPivotX(view, view.getWidth());
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        animation.setDuration(getRemoveDuration())
                .scaleX(0).setListener(new VpaListenerAdapter() {
            @Override
            public void onAnimationStart(View view) {
                dispatchRemoveStarting(holder);
            }

            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);
                ViewCompat.setAlpha(view, 1);
                dispatchRemoveFinished(holder);
                mRemoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
        mRemoveAnimations.add(holder);
    }

    @Override
    public boolean animateAdd(final ViewHolder holder) {
        endAnimation(holder);
        ViewCompat.setAlpha(holder.itemView, 0);

        mPendingAdditions.add(holder);
        return true;
    }

    private void animateAddImpl(final ViewHolder holder) {
        final View view = holder.itemView;
        mAddAnimations.add(holder);

  // Set the pivot to width so that the element scale from left
  // Also changed the animation to scaleX
        ViewCompat.setPivotX(view, 0.0f);
        ViewCompat.setScaleX(view, 0.0f);
        ViewCompat.setAlpha(view, 1.0f);

        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        animation.scaleX(1.0f).setDuration(getAddDuration()).
                setListener(new VpaListenerAdapter() {
                    @Override
                    public void onAnimationStart(View view) {
                        dispatchAddStarting(holder);
                    }

                    @Override
                    public void onAnimationCancel(View view) {
                        ViewCompat.setAlpha(view, 1);
                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        animation.setListener(null);
                        dispatchAddFinished(holder);
                        mAddAnimations.remove(holder);
                        dispatchFinishedWhenDone();
                    }
                }).start();
    }

    @Override
    public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
                               int toX, int toY) {
        final View view = holder.itemView;
        fromX += ViewCompat.getTranslationX(holder.itemView);
        fromY += ViewCompat.getTranslationY(holder.itemView);
        endAnimation(holder);
        int deltaX = toX - fromX;
        int deltaY = toY - fromY;
        if (deltaX == 0 && deltaY == 0) {
            dispatchMoveFinished(holder);
            return false;
        }
        if (deltaX != 0) {
            ViewCompat.setTranslationX(view, -deltaX);
        }
        if (deltaY != 0) {
            ViewCompat.setTranslationY(view, -deltaY);
        }
        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
        return true;
    }

    private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
        final View view = holder.itemView;
        final int deltaX = toX - fromX;
        final int deltaY = toY - fromY;
        if (deltaX != 0) {
            ViewCompat.animate(view).translationX(0);
        }
        if (deltaY != 0) {
            ViewCompat.animate(view).translationY(0);
        }
        // TODO: make EndActions end listeners instead, since end actions aren't called when
        // vpas are canceled (and can't end them. why?)
        // need listener functionality in VPACompat for this. Ick.
        mMoveAnimations.add(holder);
        final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
        animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
            @Override
            public void onAnimationStart(View view) {
                dispatchMoveStarting(holder);
            }
            @Override
            public void onAnimationCancel(View view) {
                if (deltaX != 0) {
                    ViewCompat.setTranslationX(view, 0);
                }
                if (deltaY != 0) {
                    ViewCompat.setTranslationY(view, 0);
                }
            }
            @Override
            public void onAnimationEnd(View view) {
                animation.setListener(null);
                dispatchMoveFinished(holder);
                mMoveAnimations.remove(holder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }

    @Override
    public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
                                 int fromX, int fromY, int toX, int toY) {
        final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
        final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
        final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
        endAnimation(oldHolder);
        int deltaX = (int) (toX - fromX - prevTranslationX);
        int deltaY = (int) (toY - fromY - prevTranslationY);
        // recover prev translation state after ending animation
        ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
        ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
        ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
        if (newHolder != null && newHolder.itemView != null) {
            // carry over translation values
            endAnimation(newHolder);
            ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
            ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
            ViewCompat.setAlpha(newHolder.itemView, 0);
        }
        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
        return true;
    }

    private void animateChangeImpl(final ChangeInfo changeInfo) {
        final ViewHolder holder = changeInfo.oldHolder;
        final View view = holder == null ? null : holder.itemView;
        final ViewHolder newHolder = changeInfo.newHolder;
        final View newView = newHolder != null ? newHolder.itemView : null;
        if (view != null) {
            mChangeAnimations.add(changeInfo.oldHolder);
            final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
                    getChangeDuration());
            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
   
   // Same as remove animation
            ViewCompat.setPivotX(view, view.getWidth());
            oldViewAnim.scaleX(0).setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchChangeStarting(changeInfo.oldHolder, true);
                }
                @Override
                public void onAnimationEnd(View view) {
                    oldViewAnim.setListener(null);
                    ViewCompat.setAlpha(view, 1);
                    ViewCompat.setTranslationX(view, 0);
                    ViewCompat.setTranslationY(view, 0);
                    ViewCompat.setPivotX(view, 0);
                    dispatchChangeFinished(changeInfo.oldHolder, true);
                    mChangeAnimations.remove(changeInfo.oldHolder);
                    dispatchFinishedWhenDone();
                }
            }).start();
        }
        if (newView != null) {
            mChangeAnimations.add(changeInfo.newHolder);
            final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);

            Log.d(Common.Tag, " Pivot : " + ViewCompat.getPivotX(view));

   // Same as add animation
            ViewCompat.setPivotX(newView, 0.0f);
            ViewCompat.setScaleX(newView, 0.0f);
            newViewAnimation.translationX(0).translationY(0).scaleX(1.0f).setDuration(getChangeDuration()).
                    alpha(1).setListener(new VpaListenerAdapter() {
                @Override
                public void onAnimationStart(View view) {
                    dispatchChangeStarting(changeInfo.newHolder, false);
                }
                @Override
                public void onAnimationEnd(View view) {
                    newViewAnimation.setListener(null);
                    ViewCompat.setAlpha(newView, 1);
                    ViewCompat.setTranslationX(newView, 0);
                    ViewCompat.setTranslationY(newView, 0);
                    dispatchChangeFinished(changeInfo.newHolder, false);
                    mChangeAnimations.remove(changeInfo.newHolder);
                    dispatchFinishedWhenDone();
                }
            }).start();
        }
    }

    private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
        for (int i = infoList.size() - 1; i >= 0; i--) {
            ChangeInfo changeInfo = infoList.get(i);
            if (endChangeAnimationIfNecessary(changeInfo, item)) {
                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
                    infoList.remove(changeInfo);
                }
            }
        }
    }

    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
        if (changeInfo.oldHolder != null) {
            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
        }
        if (changeInfo.newHolder != null) {
            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
        }
    }

    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
        boolean oldItem = false;
        if (changeInfo.newHolder == item) {
            changeInfo.newHolder = null;
        } else if (changeInfo.oldHolder == item) {
            changeInfo.oldHolder = null;
            oldItem = true;
        } else {
            return false;
        }
        ViewCompat.setAlpha(item.itemView, 1);
        ViewCompat.setTranslationX(item.itemView, 0);
        ViewCompat.setTranslationY(item.itemView, 0);
        dispatchChangeFinished(item, oldItem);
        return true;
    }

    @Override
    public void endAnimation(ViewHolder item) {
        final View view = item.itemView;
        // this will trigger end callback which should set properties to their target values.
        ViewCompat.animate(view).cancel();
        // TODO if some other animations are chained to end, how do we cancel them as well?
        for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
            MoveInfo moveInfo = mPendingMoves.get(i);
            if (moveInfo.holder == item) {
                ViewCompat.setTranslationY(view, 0);
                ViewCompat.setTranslationX(view, 0);
                dispatchMoveFinished(item);
                mPendingMoves.remove(item);
            }
        }
        endChangeAnimation(mPendingChanges, item);
        if (mPendingRemovals.remove(item)) {
            ViewCompat.setAlpha(view, 1);
            dispatchRemoveFinished(item);
        }
        if (mPendingAdditions.remove(item)) {
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
        }
        for (int i = mChangesList.size() - 1; i >= 0; i--) {
            ArrayList<ChangeInfo> changes = mChangesList.get(i);
            endChangeAnimation(changes, item);
            if (changes.isEmpty()) {
                mChangesList.remove(changes);
            }
        }
        for (int i = mMovesList.size() - 1; i >= 0; i--) {
            ArrayList<MoveInfo> moves = mMovesList.get(i);
            for (int j = moves.size() - 1; j >= 0; j--) {
                MoveInfo moveInfo = moves.get(j);
                if (moveInfo.holder == item) {
                    ViewCompat.setTranslationY(view, 0);
                    ViewCompat.setTranslationX(view, 0);
                    dispatchMoveFinished(item);
                    moves.remove(j);
                    if (moves.isEmpty()) {
                        mMovesList.remove(moves);
                    }
                    break;
                }
            }
        }
        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
            ArrayList<ViewHolder> additions = mAdditionsList.get(i);
            if (additions.remove(item)) {
                ViewCompat.setAlpha(view, 1);
                dispatchAddFinished(item);
                if (additions.isEmpty()) {
                    mAdditionsList.remove(additions);
                }
            }
        }
        dispatchFinishedWhenDone();
    }

    @Override
    public boolean isRunning() {
        return (!mPendingAdditions.isEmpty() ||
                !mPendingChanges.isEmpty() ||
                !mPendingMoves.isEmpty() ||
                !mPendingRemovals.isEmpty() ||
                !mMoveAnimations.isEmpty() ||
                !mRemoveAnimations.isEmpty() ||
                !mAddAnimations.isEmpty() ||
                !mChangeAnimations.isEmpty() ||
                !mMovesList.isEmpty() ||
                !mAdditionsList.isEmpty() ||
                !mChangesList.isEmpty());
    }

    private void dispatchFinishedWhenDone() {
        if (!isRunning()) {
            dispatchAnimationsFinished();
        }
    }

    @Override
    public void endAnimations() {
        int count = mPendingMoves.size();
        for (int i = count - 1; i >= 0; i--) {
            MoveInfo item = mPendingMoves.get(i);
            View view = item.holder.itemView;
            ViewCompat.setTranslationY(view, 0);
            ViewCompat.setTranslationX(view, 0);
            dispatchMoveFinished(item.holder);
            mPendingMoves.remove(i);
        }
        count = mPendingRemovals.size();
        for (int i = count - 1; i >= 0; i--) {
            ViewHolder item = mPendingRemovals.get(i);
            dispatchRemoveFinished(item);
            mPendingRemovals.remove(i);
        }
        count = mPendingAdditions.size();
        for (int i = count - 1; i >= 0; i--) {
            ViewHolder item = mPendingAdditions.get(i);
            View view = item.itemView;
            ViewCompat.setAlpha(view, 1);
            dispatchAddFinished(item);
            mPendingAdditions.remove(i);
        }
        count = mPendingChanges.size();
        for (int i = count - 1; i >= 0; i--) {
            endChangeAnimationIfNecessary(mPendingChanges.get(i));
        }
        mPendingChanges.clear();
        if (!isRunning()) {
            return;
        }
        int listCount = mMovesList.size();
        for (int i = listCount - 1; i >= 0; i--) {
            ArrayList<MoveInfo> moves = mMovesList.get(i);
            count = moves.size();
            for (int j = count - 1; j >= 0; j--) {
                MoveInfo moveInfo = moves.get(j);
                ViewHolder item = moveInfo.holder;
                View view = item.itemView;
                ViewCompat.setTranslationY(view, 0);
                ViewCompat.setTranslationX(view, 0);
                dispatchMoveFinished(moveInfo.holder);
                moves.remove(j);
                if (moves.isEmpty()) {
                    mMovesList.remove(moves);
                }
            }
        }
        listCount = mAdditionsList.size();
        for (int i = listCount - 1; i >= 0; i--) {
            ArrayList<ViewHolder> additions = mAdditionsList.get(i);
            count = additions.size();
            for (int j = count - 1; j >= 0; j--) {
                ViewHolder item = additions.get(j);
                View view = item.itemView;
                ViewCompat.setAlpha(view, 1);
                dispatchAddFinished(item);
                additions.remove(j);
                if (additions.isEmpty()) {
                    mAdditionsList.remove(additions);
                }
            }
        }
        listCount = mChangesList.size();
        for (int i = listCount - 1; i >= 0; i--) {
            ArrayList<ChangeInfo> changes = mChangesList.get(i);
            count = changes.size();
            for (int j = count - 1; j >= 0; j--) {
                endChangeAnimationIfNecessary(changes.get(j));
                if (changes.isEmpty()) {
                    mChangesList.remove(changes);
                }
            }
        }
        cancelAll(mRemoveAnimations);
        cancelAll(mMoveAnimations);
        cancelAll(mAddAnimations);
        cancelAll(mChangeAnimations);
        dispatchAnimationsFinished();
    }
    void cancelAll(List<ViewHolder> viewHolders) {
        for (int i = viewHolders.size() - 1; i >= 0; i--) {
            ViewCompat.animate(viewHolders.get(i).itemView).cancel();
        }
    }
    private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
        @Override
        public void onAnimationStart(View view) {}
        @Override
        public void onAnimationEnd(View view) {}
        @Override
        public void onAnimationCancel(View view) {}
    };

    private static final int AnimDuration = 500;

    @Override
    public long getAddDuration() {
        return AnimDuration;
    }

    @Override
    public long getRemoveDuration() {
        return AnimDuration;
    }

    @Override
    public long getChangeDuration() {
        return AnimDuration;
    }

    @Override
    public long getMoveDuration() {
        return AnimDuration;
    }
}

That was easy!
You could change the class in such a way that you delegate animation setting and resetting step to further derived classes for more customization. 


Event handling and add, remove and change operations in Android RecyclerView

It is highly recommended that you go through the earlier post on RecyclerView, please click here to go to there.

We will create a simple layout file with RecyclerView and radio buttons to select the operations

Creating the layout file.

Layout file is shown below, lets name it as main2.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/alphaList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />

    <RadioGroup
        android:id="@+id/opGroup"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <RadioButton
            android:id="@+id/opAdd"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Add"
            android:checked="true"/>
        <RadioButton
            android:id="@+id/opRemove"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Remove"
            android:checked="false"/>
        <RadioButton
            android:id="@+id/opChange"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Change"
            android:checked="false"/>
    </RadioGroup>

</RelativeLayout>

Create one more layout file to represent a row in the RecyclerView. Let us name the file as fruit.xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/fruitNameText"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:layout_margin="1dp"
        android:text="name"/>

    <TextView
        android:id="@+id/vitaminCText"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:layout_margin="1dp"
        android:text="description"/>

</LinearLayout>


In our example we will have fruit and its vitamin C content as a list.

Lets create a simple class to store the values.
package com.example.recycleviewexample;

public class FruitData {
    public String mName;
    public String mVitaminC;

    public FruitData(String n, String d) {
        mName = n;
        mVitaminC = d;
    }

    // These data represent the initial data the the RecyclerView will have
    // These are just sample data
    static final FruitData[] getList1() {
        return new FruitData[] {
                new FruitData("Apricots", "5000"),
                new FruitData("Apple", "5000"),
                new FruitData("Banana", "10.000"),
                new FruitData("Blackberries", "150.000"),
                new FruitData("Cherries", "10.000"),
                new FruitData("Grapefruit", "40.000"),
                new FruitData("Grapes", "3000")
        };
    }

    // These data will be added/changed when the user select the corresponding operations
    static final FruitData[] getList2() {
        return new FruitData[] {
                new FruitData("Kiwi", "70.000"),
                new FruitData("Lemon", "40.000"),
                new FruitData("Lychee", "23.000"),
                new FruitData("Mango", "23.000"),
                new FruitData("Orange", "49.000"),
                new FruitData("Peach", "7000"),
                new FruitData("Pear", "4000"),
                new FruitData("Pineapple", "25.000"),
                new FruitData("Plum", "5000"),
                new FruitData("Pumpkin", "16.000"),
                new FruitData("Raspberries", "5000")
        };
    }
}

Adapter and View holder

package com.example.recycleviewexample;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

// An implementation of the RecyclerView.Adapter with ClickableFruitAdapter.ViewHolder as ViewHolder
public class ClickableFruitAdapter extends RecyclerView.Adapter<ClickableFruitAdapter.ViewHolder> {

    // Upon click event mListener.onClicked(int pos) will be raised
    private FruitItemClickedListener mListener;

    // View holder, it will have views for fruitNameText(fruit name) and vitaminCText(vitamin C content). 
    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mNameText;
        public TextView mVitaminCText;

 // In the constructor we will assign the views
        public ViewHolder(View v) {
            super(v);
            mNameText = (TextView) v.findViewById(R.id.fruitNameText);
            mVitaminCText = (TextView) v.findViewById(R.id.vitaminCText);
        }
    }

    private ArrayList<FruitData> mFruits = new ArrayList<FruitData>();

    public ClickableFruitAdapter(FruitData[] desc, FruitItemClickedListener l) {
        for (FruitData d: desc) {
            mFruits.add(d);
        }
        mListener = l;
    }

    // Add a fruit to the RecyclerView
    public void addFruit(int pos, FruitData data) {
        mFruits.add(pos, data);
        notifyItemInserted(pos);
    }

    // Remove a fruit from the RecyclerView
    public FruitData removeFruit(int pos) {
        FruitData d = mFruits.get(pos);
        mFruits.remove(pos);
        notifyItemRemoved(pos);
        return d;
    }

     // Change a fruit in the RecyclerView
    public FruitData changeFruit(int pos, FruitData data) {
        FruitData d = mFruits.get(pos);
        mFruits.set(pos, data);
        notifyItemChanged(pos);
        return d;
    }

    // In this method we should create a row for our RecyclerView
    // and return an instance of ViewHolder
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit, parent, false);
        return new ViewHolder(v);
    }

    public static class NameClickListener implements View.OnClickListener {

        private ViewHolder mHolder;
        private FruitItemClickedListener mListener;

        public NameClickListener(ViewHolder holder, FruitItemClickedListener listener) {
            mHolder = holder;
            mListener = listener;
        }

        @Override
        public void onClick(View view) {
            if (mHolder.getLayoutPosition() >= 0) {
                // Raise the onClicked event after getting the position using getLayoutPosition() method,
  // You can also use getAdapterPosition () method to retrieve the position
  // getLayoutPosition()  returns the position of the ViewHolder in terms of the latest layout pass.
  // getAdapterPosition() returns the Adapter position of the item represented by this ViewHolder.
  // getAdapterPosition() might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet.
                mListener.onClicked(mHolder.getLayoutPosition());
            }
        }
    }

    // Assign the values for the ViewHolder
    // This method can be called when we have to assign a newly created row or when recycling is required.
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        final FruitData fruit = mFruits.get(position);
        holder.mNameText.setText(fruit.mName);
        holder.mVitaminCText.setText(fruit.mVitaminC);

 // Set click listener for the view
        holder.itemView.setOnClickListener(new NameClickListener(holder, mListener));
    }

    @Override
    public int getItemCount() {
        return mFruits.size();
    }
}

The listener class
package com.example.recycleviewexample;

public interface FruitItemClickedListener {

    void onClicked(int pos);
}

The Activity implementation

Now we have almost everything ready, lets put everything together in our Activity implementation.
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import android.os.Bundle;
import android.util.Log;
import android.widget.RadioGroup;

import java.util.ArrayList;

public class RecyclerItemOperationActivity extends AppCompatActivity implements FruitItemClickedListener {
    protected RecyclerView mRecyclerView;
    protected RecyclerView.LayoutManager mLayoutManager;

    protected ArrayList<fruitdata> mExtraFruits = new ArrayList<fruitdata>();
    protected ClickableFruitAdapter mAdapter;
    protected int mMode = R.id.opAdd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main2);

        mRecyclerView  = (RecyclerView)findViewById(R.id.alphaList);
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new ClickableFruitAdapter(FruitData.getList1(), this);
        mRecyclerView.setAdapter(mAdapter);

        for (FruitData d: FruitData.getList2()) {
            mExtraFruits.add(d);
        }

        RadioGroup opGroup = (RadioGroup)findViewById(R.id.opGroup);
        opGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                mMode = i;
            }
        });

    }

    @Override
    public void onClicked(int pos) {
        if (mMode == R.id.opAdd) {
            if (mExtraFruits.size() > 0) {
                mAdapter.addFruit(pos, mExtraFruits.get(0));
                mExtraFruits.remove(0);
            }
        } else if (mMode == R.id.opRemove) {
            mExtraFruits.add(mAdapter.removeFruit(pos));
        } else if (mMode == R.id.opChange) {
            if (mExtraFruits.size() > 0) {
                FruitData d = mExtraFruits.get(0);
                mExtraFruits.add(mAdapter.changeFruit(pos, d));
                mExtraFruits.remove(0);
                mExtraFruits.add(d);
            }
        }
    }
}


Introduction to Android RecyclerView


RecyclerView can be used to display large data. RecyclerView provides multiple enhancement over ListView or a GridView.

In RecyclerView we can set custom layout management for its children's, it has animation support built-in, has item decorator support and it uses the View-holder pattern.

Adding RecyclerView support

It is provided as part of the v7 support library.

To add RecyclerView support add the following dependency in your gradle build file.

dependencies {
 compile "com.android.support:recyclerview-v7:23.1.1"
}

Adding RecyclerView in your application is very similar to that of ListView. Lets go through the steps,

Create the layout file

Let activity_main.xml be our main layout file. Layout file should have android.support.v7.widget.RecyclerView.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/fruitList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />
  
</RelativeLayout>

Create one more layout file to represent a row in the RecyclerView. Let us name the file as fruit.xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/fruitNameText"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:layout_margin="1dp"
        android:text="name"/>

    <TextView
        android:id="@+id/vitaminCText"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:layout_margin="1dp"
        android:text="description"/>

</LinearLayout>

In our example we will have fruit and its vitamin C content as a list.
Lets create a simple class to store the values.
package com.example.recycleviewexample;

public class FruitData {
    public String mName;
    public String mVitaminC;

    public FruitData(String n, String d) {
        mName = n;
        mVitaminC = d;
    }

 // These data represent the initial data the the RecyclerView will have
 // These are just sample data
    static final FruitData[] getList1() {
        return new FruitData[] {
                new FruitData("Apricots", "5000"),
                new FruitData("Apple", "5000"),
                new FruitData("Banana", "10.000"),
                new FruitData("Blackberries", "150.000"),
                new FruitData("Cherries", "10.000"),
                new FruitData("Grapefruit", "40.000"),
                new FruitData("Grapes", "3000")
        };
    }
}

To add RecyclerView support we should have at-least three component, Adapter, ViewHolder and the RecyclerView itself.

Adapter and ViewHolder

Adapter manages the data, it should be an implementation of RecyclerView.Adapter. Our adapter class is shown below,

ViewHolder stores the view which we want to recycle. It will store view for one entry in the RecyclerView.
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

// An implementation of the RecyclerView.Adapter with BasicFruitAdapter.ViewHolder as ViewHolder
public class BasicFruitAdapter extends RecyclerView.Adapter {

 // View holder, it will have views for fruitNameText(fruit name) and vitaminCText(vitamin C content). 
    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mNameText;  // for fruitNameText
        public TextView mVitaminCText;  // for vitaminCText

  // In the constructor we will assign the views
        public ViewHolder(View v) {
            super(v);
            mNameText = (TextView) v.findViewById(R.id.fruitNameText);
            mVitaminCText = (TextView) v.findViewById(R.id.vitaminCText);
        }
    }

 // Stores the array of fruit data, FruitData has name and vitamin c content
    private ArrayList<fruitdata> mFruits = new ArrayList<fruitdata>();

    public BasicFruitAdapter(FruitData[] desc) {
        for (FruitData d: desc) {
            mFruits.add(d);
        }
    }

 // In this method we should create a row for our RecyclerView
 // and return an instance of ViewHolder
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit, parent, false);
        return new ViewHolder(v);
    }

 // Assign the values for the ViewHolder
 // This method can be called when we have to assign a newly created row or when recycling is required.
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        final FruitData fruit = mFruits.get(position);
        holder.mNameText.setText(fruit.mName);
        holder.mVitaminCText.setText(fruit.mVitaminC);
    }

    @Override
    public int getItemCount() {
        return mFruits.size();
    }
}

The final touch

Now we have almost everything ready, lets put everything together in our Activity implementation. 
package com.example.recycleviewexample;

import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import android.os.Bundle;

public class RecyclerBasicActivity extends AppCompatActivity {
    protected RecyclerView mRecyclerView;
    protected RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main1);

  // Retrieve the RecyclerView
        mRecyclerView  = (RecyclerView)findViewById(R.id.alphaList);
  
  // Set LinearLayoutManager as we want to just display the data in linear fashion
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
  
  // Set the adapter to the RecyclerView
        mRecyclerView.setAdapter(new BasicFruitAdapter(FruitData.getList1()));
    }
} 

That's it, we should now be able to display the data using the RecyclerView.


Monday, 7 December 2015

A simple box blur implementation in OpenGL/ES using shaders

Here I am going to share one of the simplest approach to apply blur. This is not an optimized solution. For an optimized solution you can go for multi-pass rendering and (or) down sampling.
In this approach bluring will be done by taking average color value around a rectangular are of the pixel. Most of this operation will be performed in the fragment shader. This is a single pass algorithm.

Vertex Shader

As we are doing most of the operation in the fragment shader, vertex shader will be very straight forward.

// Vertex coordinate
attribute vec4 aPosition;

// Texture coordinate
attribute vec2 aTexCoordinate;

// Texture coordinate to be passed to fragment shader
varying  vec2 vTexCoordinate;

void main() {
    vTexCoordinate = aTexCoordinate;
    gl_Position = aPosition;
}

Fragment Shader

precision mediump float;

// The sampler object
uniform sampler2D uSampler;

// Texture coordinate passed from vertex shader
varying  vec2 vTexCoordinate;

// This is the value that needs to be added(subtracted) to a 
// texture point to get the next(previous) pixel in the X direction
// This value can be calculated as 1/texturewidth
uniform float uXPixelDistance;

// This is the value that needs to be added(subtracted) to a
// texture point to get the next(previous) pixel in the Y direction
// This value can be calculated as 1/textureheight
uniform float uYPixelDistance;

// How to jump to next pixel, 1 means the very next pixel,
// 2 means the 2th pixel, and so on.
const float jump = 2.0f;

// Number of points around the current point
const float pointRange = 10.0f;

void main() {
    vec4 color = vec4(0, 0, 0, 0);
    vec2 point;
    int count = 0;
 
    // Calculate the total color intensity around the pixel
    // In this case we are calculating pixel intensity around 10 pixels
    for(float u = -pointRange; u < pointRange ; u+=jump) {
        for(float v = -pointRange ; v < pointRange ; v+=jump) {
            point.x = vTexCoordinate.x  + u * uXPixelDistance;
            point.y = vTexCoordinate.y  + v * uYPixelDistance;
            
            // If the point is within the range[0, 1]
            if (point.y >= 0.0f && point.x >= 0.0f &&
                point.y <= 1.0f && point.x <= 1.0f ) {
                ++count;
                color += texture2D(uSampler, point.xy);
            }
        }
    }
    
    // Take the average intensity for the region
    color = color / float(count);
 
    gl_FragColor = vec4(color.xyz, 1.0f);
}

Thursday, 12 November 2015

Android M run-time permission using Fragments in Unity 3D

If application is built for Android M then the dangerous permissions are granted at run time on devices with Android M, not installation time. Users can revoke permission whenever they want, irrespective of the target SDK set.

Run-time permission support can be implemented in your Activity implementation or Fragment implementation.

In Unity, Activity is created by its framework and adding Fragment to support permission required for your Android plug-in looks the better approach for me. With this approach we do not need to modify the Activity implementation at all.

We would need to get the permission granted that are additionally required by us. Permissions required by unity features will be handled by Unity itself(e.g. WebCamTexture).

Now lets add run-time permission support for our Unity application.

Update the target SDK

 First thing to build application with Unity for M is to set the target SDK as 23. At the time of writing this blog Unity does not have the support for Android M.
So we can go ahead and set the target SDK as Android M in the manifest file. If you do not have the AndroidManifest file available with you, you can export
the Android project(To export select File->Build Settings and select Android and then Google Android Project) form Unity editor.
Modify the manifest file to set the target SDK as,

 <uses-sdk android:minsdkversion="14" android:targetsdkversion="23"/>
Manifest file should be kept at UnityProject\Assets\Plugins\Android.

Create Android plug-in which requests permissions

Our Android plug-in will be a jar library with just two classes, first one the fragment which requests permissions and the second one a helper class which will be used from c# code.

The jar library will also be kept at UnityProject\Assets\Plugins\Android

To create the jar library you can use Eclipse or Android Studio or you can use javac and jar directly.

Fragment class is given below,

package com.example.androidproject.flash;

import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

public class PermissionFragment extends Fragment {

 // This is used while requesting for permission
    public static final int PERMISSION_REQUEST_CODE = 1000;

 // Required permissions list
    private static final String [] REQUIRED_PERMISSIOS = {
            "android.permission.CAMERA"
    };

    private boolean mAskedPermission = false;

    public PermissionFragment() {}


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        checkThemePermissions();
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    public void checkThemePermissions() {

  // Check and request for permissions for Android M and older versions
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !mAskedPermission) {

            ArrayList<String> requiredPermissions = new ArrayList<String>();

            for (String perm : REQUIRED_PERMISSIOS) {
                if (getActivity().checkSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
                    requiredPermissions.add(perm);
                }
            }

            if (requiredPermissions.size() > 0) {
                boolean pr = false;
                for (String p: requiredPermissions) {
                    pr = shouldShowRequestPermissionRationale(p);
                    if (pr) {
                        break;
                    }
                }

                if (pr) {
                    // We've been denied once before,
                    // Add you logic here as to why we should request for permission once again
                }
                this.requestPermissions(requiredPermissions.toArray(new String[requiredPermissions.size()]), PERMISSION_REQUEST_CODE);
            } else {
                PermissionRequester.instance().onComplete(true);
            }
        } else {
            PermissionRequester.instance().onComplete(true);
        }

        mAskedPermission = true;
    }

 // Once user allow or deny permissions this method is called
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CODE) {
            boolean res = true;
            for (int i: grantResults) {
                if (i != PackageManager.PERMISSION_GRANTED) {
                    res = false;
                    break;
                }
            }

            PermissionRequester.instance().onComplete(res);
        }

        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
The helper class is given below, this class will be used from Unity from c# code,
package com.example.androidproject.flash;

import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.hardware.Camera;

public class PermissionRequester {
    // A derived class will be created from this interface in the unity side
 // to notify once onRequestPermissionsResult is called.
 // This method onComplete will be called from PermissionFragment.onRequestPermissionsResult
    public static interface Listener {
        public void onComplete(boolean status);
    }

    private Activity mActivity;
    private Listener mListener;

    public static PermissionRequester mListner = null;

    public static PermissionRequester instance() {
        return mListner;
    }

    public PermissionRequester(Activity a, Listener l) {
        mListner = this;

        mActivity = a;
        mListener = l;

  // Create fragment PermissionFragment and add to our activity.
        FragmentManager fm = mActivity.getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(new PermissionFragment(), "perm_fragment");
        ft.commit();
    }

 // Called from PermissionFragment.onRequestPermissionsResult
 // Notify the listner once this method is called
    public void onComplete(boolean v) {
        mListener.onComplete(v);
    }    
}

Requesting permission from C# code

Create the proxy class(derived from PermissionRequester.Listener) to receive onRequestPermissionsResult notification
private sealed class PermissionListnerProxy : AndroidJavaProxy {
  
 public PermissionListnerProxy()
  : base("com.example.androidproject.flash.PermissionRequester$Listener") {
 }
  
 public void onComplete(bool res) {
  Debug.Log("Permission status :" + res);
 }
}

Now lets make use of PermissionRequester to actually request for permissions.
To do that lets get a reference to the Activity class first,

 AndroidJavaClass unityPlayer = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
 AndroidJavaObject currentActivity = unityPlayer.GetStatic<androidjavaobject> ("currentActivity");

Now lets create an instance of PermissionRequester which will be requesting for permissions, if required.


    currentActivity.Call ("runOnUiThread", new AndroidJavaRunnable (() => {
        permissionRequester = new AndroidJavaObject("com.example.androidproject.flash.PermissionRequester",
                                      currentActivity,
                                      new PermissionListnerProxy());
    }));

Thats it, we have actually added support for Android M without doing any modification to the Activity.


Another way to support Android M permission related changes is,
  1. Get the Android project exported from Unity editor
  2. Modify the Activity implementation class in the exported project similar to what we done in PermissionFragment
  3. Create the jar file
  4. Copy the jar and manifest file to UnityProject\Assets\Plugins\Android

Friday, 12 June 2015

Setting fields of an android java object from unity C#, including array

Lets say we want to set fields of a Java object in Unity with different types, including array.

Let the class in java be,
package com.example; 

public class Address {
 int mHouseNumber;
 String mStreet;
 String mCity;
 String mCountry;
 String [] mPhoneNumders;
}

Now lets create Address object from c# and populate its fields.
First of all lets create an Address object
AndroidJavaObject addressObj = new AndroidJavaObject ("com.example.Address");

Now populate all the fields except the mPhoneNumders.
addressObj.Set<int> ("mHouseNumber", 10);
addressObj.Set<string> ("mStreet", "abc");
addressObj.Set<string> ("mCity", "def");
addressObj.Set<string> ("mCountry", "ghi");

Now lets create a java object from the c# array so that we can set the field mPhoneNumders. Lets create a small method for it.
private AndroidJavaObject javaArrayFromCS(string [] values) {
    AndroidJavaClass arrayClass  = new AndroidJavaClass("java.lang.reflect.Array");
    AndroidJavaObject arrayObject = arrayClass.CallStatic<AndroidJavaObject>("newInstance",
                                             new AndroidJavaClass("java.lang.String"),
            values.Count());
 for (int i=0; i<values.Count(); ++i) {
     arrayClass.CallStatic("set", arrayObject, i,
                               new AndroidJavaObject("java.lang.String", values[i])));
 }
}

Now as the last step lets set the field mPhoneNumders
string [] phoneNumbers = {"12345678", "87654321"};
addressObj.Set<AndroidJavaObject> ("mPhoneNumders", javaArrayFromCS(phoneNumbers));

Thursday, 21 May 2015

Extracting video frames in unity 3d as textures


Video can be played using MovieTexture in Unity. There is no easy way of retrieving the video frame in unity though.

One of the hard way to retrieve the video frame is to render the video in to a RenderTexture and create video frame as texture by reading pixels from the render texture. Lets do it.

So let’s start and create a scene with a Camera, let’s call it MovieCamera, a cube and direction light.

Our scene will look like,




 
Let the camera be an orthographic one. We will use the cube to map the video on to it.


Playing the video



To play video you might need QuickTime Player, especially if you are not using ogg format. Keep the video in the Assets folder. Now drag and drop the video on to the cube object.

This will not start playing the video; for that you would need to write a script and call the Play method of MovieTexture.

script

Attach below given scripts to the cube game object.
using UnityEngine;
using System.Collections;

public class VideoPlayer : MonoBehaviour {

    // assign the video to this texture
    public MovieTexture movie;

    void Start () {
        GetComponent<renderer>().material.mainTexture = movie;

        // play the video
        movie.Play();
        movie.loop = true;

        // scale the cube so that it fills the screen
        float height = Camera.main.orthographicSize * 2.0f;
        float width = height * (float)Screen.width / (float)Screen.height;
        transform.localScale = new Vector3(width, height, 0.1f);
   }

   void Update () {
   }
}

The movie variable should be assigned with the video.
The inspector view of the Cube will be,

With this much of code we will be able to play the video. But our aim is to retrieve the video frames as textures.


To capture video frames we will render the video onto a RenderTexture first. For that instead of the camera rendering onto the screen we will make it render on to the RenderTexture.
 
Once video rendered on to the RenderTexture we can use ReadPixels method of Texture2D to retrieve the video frames.
Actually it is not so difficult to do, just attach the below shown script to the camera(MovieCamera) and we are done.

using UnityEngine;
using System.Collections;

public class MovieCamera : MonoBehaviour {
    private RenderTexture renderTexture = null;
    private Texture2D videoFrame;
    private Rect rect;   // Rect from where we need to read the pixels(entire  screen)
 
    void Start () {
    }

    void Update () {
    }

    void OnPreRender() {
        if (renderTexture == null) {
            Create();
        }

        // make camera’s targetTexture as current rendering target
        // and then read the pixels.
        RenderTexture orig = RenderTexture.active;
        RenderTexture.active = renderTexture;
        videoFrame.ReadPixels(rect, 0, 0, false);
        videoFrame.Apply();
        RenderTexture.active = orig;
    }

    void OnGUI () { 
        // draw the read texture on to the screen
        Graphics.Blit(videoFrame, (RenderTexture)null);
    }

    private void Create() {
        renderTexture = new RenderTexture (Screen.width, Screen.height, 8, RenderTextureFormat.ARGB32);
        videoFrame = new Texture2D(Screen.width, Screen.height, TextureFormat.ARGB32, false);
  
        // set targetTexture for the Camera
        GetComponent<camera>().targetTexture = renderTexture;
  
        // Rect to read the pixels; entire screen
        rect = new Rect(0, 0, Screen.width, Screen.height);
    }
}

Our Camera's(MovieCamera) inspector view,


In order to try this you would need a Unity pro version.
The video that I have used can be downloaded from https://peach.blender.org/download/

Thursday, 7 May 2015

Compiling C++11 and C++14 programs with GCC

Compiling C++11 programs

With gcc 4.7 and above we can use -std=c++11 or -std=c++0x or -std=gnu++0x or -std=gnu++11 option to compile the programs as shown below,
g++ -std=c++11 hellocpp11.cpp

For earlier version of GCC, try -std=c++0x or -std=gnu++0x
g++ -std=c++0x hellocpp11.cpp

Please see the link https://gcc.gnu.org/projects/cxx0x.html for more information on feature support.

Compiling C++14 programs

C++14 is supported from gcc 4.9 onwards, so please update your gcc if it is below 4.9.
g++ -std=c++14 hellocpp14.cpp

If the default g++ compiler is not of version 4.9 and you have updated it, please use the compiler with version suffix. For example,
g++-4.9 -std=c++14 hellocpp14.cpp

-std=c++1y or -std=gnu++1y or or -std=gnu++14 can also be used instead of -std=c++14

Please see the link https://gcc.gnu.org/projects/cxx1y.html for more information on feature support.

Updating GCC to version 4.9 in Ubuntu

We will use toolchain ppa test repository to update the GCC version. Use below given commands to complete the installation.
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install g++-4.9

After finishing the installation you can use g++-4.9 to compile programs written in c++14.

Proxy configuration

In case you are under proxy, try setting the following environment variables.
export http_proxy=http://<host>:<port>
export https_proxy=http://<host>:<port>

Also try add-apt-repository with sudo -E option if just sudo did not work for you.

In case your proxy server requires user name and password, try setting the proxy environment variables with user name and password
export https_proxy=<user_name>:<password>@<host>:<port>

Tuesday, 14 April 2015

Advanced c++ interview questions and answers 3


1. How to define an postfix increment operator function. How to call it explicitly?

SHOW/HIDE ANSWER
Declaration,
    1 Integer operator++(int)
    2 {
    3  Integer v = *this;
    4  ++mV;
    5  return v;
    6 }
Calling it explicitly,
    1 Integer v1(2);
    2 Integer v2 = v1.operator++(0); 
 
 

2. What is the output of the following program?

    1 #include <iostream>
    2 
    3 class Base
    4 {
    5 public:
    6     void func()
    7     {
    8         std::cout << "Base::func" << std::endl;
    9     }
   10 };
   11 
   12 class Derived: public Base
   13 {
   14 public:
   15     void func(int i)
   16     {
   17         std::cout << "Derived::func" << std::endl;
   18     }
   19 };
   20 
   21 int main(int argc, char **argv)
   22 {
   23     Derived derived;
   24     derived.func();
   25     return 0;
   26 } 
SHOW/HIDE ANSWER
Compilation error.

3. What is the output of the following program?

    1 #include <iostream>
    2 
    3 class Base
    4 {
    5     int i;
    6 public:
    7     Base(int v)
    8      : i(v)
    9     {}
   10 
   11     virtual void print()
   12     {
   13         std::cout << i << std::endl;
   14     }
   15 };
   16 
   17 class Derived: public Base
   18 {
   19     int j;
   20 
   21 public:
   22     Derived(int v)
   23      : Base(v),
   24        j(v)
   25     {}
   26 
   27     void print()
   28     {
   29         Base::print();
   30         std::cout << j << std::endl;
   31     }
   32 };
   33 
   34 int main(int argc, char **argv)
   35 {
   36     Derived d1(1);
   37     Derived d2(2);
   38 
   39     Base &b1 = d2;
   40 
   41     b1 = d1;
   42 
   43     d1.print();
   44     d2.print();
   45 
   46     return 0;
   47 } 

4. What is the output of the following program?

    1 #include <iostream>
    2 
    3 class Base
    4 {
    5     int i;
    6 public:
    7     Base(int v)
    8     {}
    9 
   10     virtual void print()
   11     {
   12         std::cout << i << std::endl;
   13     }
   14 };
   15 
   16 class Derived: public Base
   17 {
   18     int j;
   19 
   20 public:
   21     Derived(int v)
   22      : Base(v),
   23        j(v)
   24     {}
   25 
   26     void print()
   27     {
   28         Base::print();
   29         std::cout << j << std::endl;
   30     }
   31 };
   32 
   33 void func(Base b)
   34 {
   35     if (dynamic_cast<Derived *>(&b) == 0) {
   36         std::cout << "It is not an instance of Derived";
   37     } else {
   38         std::cout << "It is an instance of Derived";
   39     }
   40 }
   41 
   42 int main(int argc, char **argv)
   43 {
   44     Derived d(1);
   45 
   46     func(d);
   47 
   48     return 0;
   49 } 

SHOW/HIDE ANSWER
It is not an instance of Derived

Slicing removed the Derived class part from the object.

5. Where is the location of vptr in an object?

SHOW/HIDE ANSWER
At the start of the object. In case of multiple inheritance there will be multiple vptr so other vptr will be with an offset.


6. What is the output of the following program?

    1 #include <iostream>
    2 
    3 class A
    4 {
    5     int i;
    6 public:
    7     virtual ~A()
    8     {}
    9 
   10 };
   11 
   12 class B
   13 {
   14     int j;
   15 public:
   16     virtual ~B()
   17     {}
   18 };
   19 
   20 class C: public A, public B
   21 {
   22     int k;
   23 };
   24 
   25 int main(int argc, char **argv)
   26 {
   27     C obj;
   28 
   29     std::cout << "A:" << (A *)&obj << "\nB:" << (B *)&obj << "\nC:" << (C *)&obj << std::endl;
   30     return 0;
   31 }
SHOW/HIDE ANSWER

Sample output for a 32 bit build:
A:0xffc338dc
B:0xffc338e4
C:0xffc338dc

7. What is the output of the following program?

    1 #include <iostream>
    2 
    3 class A
    4 {
    5 public:
    6     virtual ~A()
    7     {}
    8 };
    9 
   10 class B: protected A
   11 {
   12 public:
   13 };
   14 
   15 int main(int argc, char **argv)
   16 {
   17     B b;
   18     A *a = &b;
   19     return 0;
   20 }
SHOW/HIDE ANSWER
Compilation error. b can be converted in to a within the scope of B only in this case. So the following program will work fine.

    1 #include <iostream>
    2 
    3 class A
    4 {
    5 public:
    6     virtual ~A()
    7     {}
    8 
    9 };
   10 
   11 class B: protected A
   12 {
   13 public:
   14     static A * toA(B * b);
   15 };
   16 
   17 A * B::toA(B * b)
   18 {
   19     return b;
   20 }
   21 
   22 int main(int argc, char **argv)
   23 {
   24     B b;
   25 
   26     A *a = B::toA(&b);
   27 
   28     return 0;
   29 } 
 

8. Is there a way to know if operator[] is used for reading(rhs) or writing(lhs)?

SHOW/HIDE ANSWER
    1 #include <iostream>
    2 #include <algorithm>
    3 
    4 class Test
    5 {
    6     int mData[16];
    7 public:
    8     class Proxy
    9     {
   10         int &mCell;
   11     public:
   12         Proxy(int &cell)
   13          : mCell(cell)
   14         {}
   15 
   16         Proxy& operator=(int i)
   17         {
   18             std::string pass;
   19             std::cout << "password for writting :) ";
   20             std::cin >> pass;
   21             mCell = i;
   22         }
   23 
   24         operator int() const
   25         {
   26             return mCell;
   27         }
   28     };
   29 
   30     class ConstProxy: public Proxy
   31     {
   32     public:
   33         ConstProxy(const int &cell)
   34          : Proxy(const_cast<int &>(cell))
   35         {}
   36     };
   37 
   38 public:
   39     Test()
   40     {
   41         std::fill(mData, mData+16, 0);
   42     }
   43 
   44     ConstProxy operator[](int index) const
   45     {
   46         return ConstProxy(mData[index]);
   47     }
   48 
   49     Proxy operator[](int index)
   50     {
   51         return Proxy(mData[index]);
   52     }
   53 
   54     friend std::ostream& operator<<(std::ostream& os, const Test& t);
   55 };
   56 
   57 std::ostream& operator<<(std::ostream& os, const Test& t)
   58 {
   59     os << "[";
   60     for (int i=0; i<16; ++i) {
   61         os << t[i] << (i<15?", ":"");
   62     }
   63     os << "]";
   64     return os;
   65 }
   66 
   67 int main(int argc, char **argv)
   68 {
   69     Test t;
   70     t[0] = 1;
   71     std::cout << t << std::endl;
   72 }
   73 

9. How to create a non-derivable class?

SHOW/HIDE ANSWER
    1 class Sealed;
    2 
    3 class Lock
    4 {
    5 private:
    6     Lock() {}
    7     Lock(const Lock&) {}
    8     friend class Sealed;
    9 };
   10 
   11 class Sealed: public virtual Lock
   12 {
   13 public:
   14     Sealed() {}
   15 };
 
 
PREVIOUS HOME