Saturday, May 17, 2014

Pager Tabs with Icon and text which appears to have fixed width.

We are going to create pager tabs, which unlike default library will be able to show more than 3 tabs in a screen. All the tabs will close to each other.


PagerTab
 
To achieve this purpose we shall need a library located at http://viewpagerindicator.com/. Download the library project, named as JakeWharton-Android-ViewPagerIndicator-2.4.1-0-g8cd549f.zip. It includes a library and sample project. We need library for our app. If you are curious, you may check the sample project as well. Import the library project in eclipse.
 
Create a new Android project and open its properties. Add library project in this project using Android section in properties. Clean library and android project. Add the following in your activity_layout.xml.

<com.viewpagerindicator.TabPageIndicator

                android:id="@+id/indicator"

                style="@style/CustomTabPageIndicator"

                android:layout_width="fill_parent"

                android:layout_height="30dp"

                 android:visibility="gone"/>

<android.support.v4.view.ViewPager

            xmlns:tools="http://schemas.android.com/tools"

            android:id="@+id/pager"

            android:layout_width="match_parent"

            android:layout_height="0dp"

            android:layout_weight="1"

            tools:context=".VMEBaseClass" >

        </android.support.v4.view.ViewPager>


Whereas style.xml will contain:

<style name="CustomTabPageIndicator" parent="Widget.TabPageIndicator">

        <!-- <item name="android:background">@drawable/custom_tab_indicator</item> -->

        <item name="android:textAppearance">@style/CustomTabPageIndicator.Text</item>

        <item name="android:textColor">#cccccc</item>

        <item name="android:textSize">12sp</item>

        <!-- <item name="android:divider">@drawable/vertical_line</item> -->

        <!-- @drawable/custom_tab_indicator_divider</item> -->

        <!-- <item name="android:dividerPadding">10dp</item>

        <item name="android:showDividers">middle</item> -->

        <item name="android:paddingLeft">0dp</item>

        <item name="android:paddingRight">0dp</item>

        <item name="android:fadingEdge">horizontal</item>

        <item name="android:paddingTop">1dp</item>

        <item name="android:paddingBottom">0dp</item>

        <item name="android:fadingEdgeLength">8dp</item>

        <item name="android:layout_marginTop">5dp</item>

        <item name="android:layout_marginBottom">5dp</item>

    </style>


In your FragmentActivity's onCreate(), add the following code.

BasePagerAdapter mSectionsPagerAdapter = new BasePagerAdapter( getSupportFragmentManager( ) );

        

        // Set up the ViewPager with the sections adapter.

        mViewPager = (ViewPager) findViewById( R.id.pager );

        mViewPager.setAdapter( mSectionsPagerAdapter );

        

        TabPageIndicator indicator = (TabPageIndicator) findViewById( R.id.indicator );

        indicator.setVisibility( View.VISIBLE );

        indicator.setViewPager( mViewPager );


Implement the IconPagerAdapter for showing icons and background in both normal and selected states. In this sample we will support 4 pages.

public class BasePagerAdapter extends FragmentPagerAdapter implements IconPagerAdapter {

        

        public VMEBasePagerAdapter( FragmentManager fm ) {

            super( fm );

        }

        

        @Override

        public Fragment getItem( int position ) {

            // getItem is called to instantiate the fragment for the given page.

                       

            if ( position == 0 ) {

                Fragment fragment = new DownloadFragment();

                return fragment;

            } else if ( position == 1 ) {

                Fragment fragment = new FavoriteFragment( favoriteData);

                return fragment;

            } else if ( position == 2 ) {

                Fragment fragment = new FlashFragment();

                return fragment;

            } else if ( position == 3 ) {

                Fragment fragment = null;

                fragment = new DummyFragment();

                return fragment;

            } else {

                return null;

            }

        }

        

        @Override

        public int getCount( ) {

//Return the count of pages.

            return 4;

        }

        

        @Override

        public CharSequence getPageTitle( int position ) {

            switch ( position ) {

                case 0:

                    return getString( R.string.title_download );

                    

                case 1:

                    return getString( R.string.title_favorite );

                    

                case 2:

                    return getString( R.string.title_flash );

                    

                case 3:

                    return getString( R.string.title_dummy );

            }

            return null;

        }

        

        @Override

        public int getIconResIdNormal( int index ) {

switch ( index ) {

                case 0:

                    return R.drawable.ic_normal_download;

                    

                case 1:

                    return R.drawable.ic_normal_favorite;

                    

                case 2:

                    return R.drawable.ic_normal_flash;

                    

                case 3:

                    return R.drawable.ic_normal_dummy;

            }

            return 0;

        }

        

        @Override

        public int getIconResIdSelected( int index ) {

            switch ( index ) {

                case 0:

                    return R.drawable.ic_selected_download;

                    

                case 1:

                    return R.drawable.ic_selected_favorite;

                    

                case 2:

                    return R.drawable.ic_selected_flash;

                    

                case 3:

                    return R.drawable.ic_selected_dummy;

            }

            return 0;

        }

        

        @Override

        public int getBackgroundResIdSelected( int index ) {

            switch ( index ) {

                case 0:

                    return R.drawable.tab_left_pressed;

                case 1:

                case 2:

                    return R.drawable.tab_center_pressed;

                case 3:

                    return R.drawable.tab_right_pressed;

            }

            return 0;

        }

        

        @Override

        public int getBackgroundResIdNormal( int index ) {

            switch ( index ) {

                case 0:

                    return R.drawable.tab_left_normal;

                case 1:

                case 2:

                    return R.drawable.tab_center_normal;

                case 3:

                    return R.drawable.tab_right_normal;

            }

            return 0;

        }

    }


Now its time to modify the library project's TabPageIndicator.java like this.

public class TabPageIndicator extends HorizontalScrollView implements PageIndicator {
    /** Title text used when no title is provided by the adapter. */
    private static final CharSequence EMPTY_TITLE = "";
    //private Vector< TabView > tabViews = new Vector< TabPageIndicator.TabView >( );
    
    /**
     * Interface for a callback when the selected tab has been reselected.
     */
    public interface OnTabReselectedListener {
        /**
         * Callback when the selected tab has been reselected.
         * 
         * @param position
         *            Position of the current center item.
         */
        void onTabReselected( int position );
    }
    
    private Runnable mTabSelector;
    
    private final OnClickListener mTabClickListener = new OnClickListener( ) {
        public void onClick( View view ) {
            //TabView tabView = (TabView) view;
            final int oldSelected = mViewPager.getCurrentItem( );
            final int newSelected = (Integer)view.getTag( );
            mViewPager.setCurrentItem( newSelected );
            if ( oldSelected == newSelected && mTabReselectedListener != null ) {
                mTabReselectedListener.onTabReselected( newSelected );
            }
        }
    };
    
    private final IcsLinearLayout mTabLayout;
    
    private ViewPager mViewPager;
    private ViewPager.OnPageChangeListener mListener;
    
    private int mMaxTabWidth;
    private int mSelectedTabIndex;
    
    private OnTabReselectedListener mTabReselectedListener;
    
    public TabPageIndicator( Context context ) {
        this( context, null );
    }
    
    public TabPageIndicator( Context context, AttributeSet attrs ) {
        super( context, attrs );
        setHorizontalScrollBarEnabled( false );
        
        mTabLayout = new IcsLinearLayout( context, R.attr.vpiTabPageIndicatorStyle );
        addView( mTabLayout, new ViewGroup.LayoutParams( WRAP_CONTENT, MATCH_PARENT ) );
    }
    
    public void setOnTabReselectedListener( OnTabReselectedListener listener ) {
        mTabReselectedListener = listener;
    }
    
    @Override
    public void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
        final int widthMode = MeasureSpec.getMode( widthMeasureSpec );
        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
        setFillViewport( lockedExpanded );
        
        final int childCount = mTabLayout.getChildCount( );
        if ( childCount > 1 && ( widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST ) ) {
            if ( childCount > 2 ) {
                mMaxTabWidth = (int) ( MeasureSpec.getSize( widthMeasureSpec ) * 0.4f );
            } else {
                mMaxTabWidth = MeasureSpec.getSize( widthMeasureSpec ) / 2;
            }
        } else {
            mMaxTabWidth = -1;
        }
        
        final int oldWidth = getMeasuredWidth( );
        super.onMeasure( widthMeasureSpec, heightMeasureSpec );
        final int newWidth = getMeasuredWidth( );
        
        if ( lockedExpanded && oldWidth != newWidth ) {
            // Recenter the tab display if we're at a new (scrollable) size.
            setCurrentItem( mSelectedTabIndex );
        }
    }
    
    private void animateToTab( final int position ) {
        final View tabView = mTabLayout.getChildAt( position );
        if ( mTabSelector != null ) {
            removeCallbacks( mTabSelector );
        }
        mTabSelector = new Runnable( ) {
            public void run( ) {
                final int scrollPos = tabView.getLeft( ) - ( getWidth( ) - tabView.getWidth( ) ) / 2;
                smoothScrollTo( scrollPos, 0 );
                mTabSelector = null;
            }
        };
        post( mTabSelector );
    }
    
    @Override
    public void onAttachedToWindow( ) {
        super.onAttachedToWindow( );
        if ( mTabSelector != null ) {
            // Re-post the selector we saved
            post( mTabSelector );
        }
    }
    
    @Override
    public void onDetachedFromWindow( ) {
        super.onDetachedFromWindow( );
        if ( mTabSelector != null ) {
            removeCallbacks( mTabSelector );
        }
    }
    
    private void addTab( int index, CharSequence text, int iconResId ) {
        LayoutInflater inflater = (LayoutInflater)getContext( ).getSystemService( Context.LAYOUT_INFLATER_SERVICE );
        View view = inflater.inflate( R.layout.tab_layout, null);
        ImageView imageView = (ImageView)view.findViewById( R.id.ivIcon );
        TextView textView = (TextView)view.findViewById( R.id.tvTabText );
        imageView.setImageResource( iconResId );
        textView.setText( text );
        view.setOnClickListener( mTabClickListener );
        view.setTag( index );
        mTabLayout.addView( view, new LinearLayout.LayoutParams( 0, MATCH_PARENT, 1 ) );
        
        /*final TabView tabView = new TabView( getContext( ) );
        tabView.mIndex = index;
        tabView.setFocusable( true );
        tabView.setOnClickListener( mTabClickListener );
        tabView.setText( text );
        
        if ( iconResId != 0 ) {
            tabView.setCompoundDrawablesWithIntrinsicBounds( iconResId, 0, 0, 0 );
        }
        tabViews.add( tabView );
        tabView.setCompoundDrawablePadding (-30 );
        mTabLayout.addView( tabView, new LinearLayout.LayoutParams( 0, MATCH_PARENT, 1 ) );*/
    }
    
    /*public TabView getTabAt( int position ) {
        if ( tabViews != null && tabViews.size( ) > position )
            return tabViews.get( position );
        return null;
    }*/
    
    @Override
    public void onPageScrollStateChanged( int arg0 ) {
        if ( mListener != null ) {
            mListener.onPageScrollStateChanged( arg0 );
        }
    }
    
    @Override
    public void onPageScrolled( int arg0, float arg1, int arg2 ) {
        if ( mListener != null ) {
            mListener.onPageScrolled( arg0, arg1, arg2 );
        }
    }
    
    @Override
    public void onPageSelected( int arg0 ) {
        setCurrentItem( arg0 );
        if ( mListener != null ) {
            mListener.onPageSelected( arg0 );
        }
    }
    
    @Override
    public void setViewPager( ViewPager view ) {
        if ( mViewPager == view ) {
            return;
        }
        if ( mViewPager != null ) {
            mViewPager.setOnPageChangeListener( null );
        }
        final PagerAdapter adapter = view.getAdapter( );
        if ( adapter == null ) {
            throw new IllegalStateException( "ViewPager does not have adapter instance." );
        }
        mViewPager = view;
        view.setOnPageChangeListener( this );
        notifyDataSetChanged( );
    }
    
    public void notifyDataSetChanged( ) {
        mTabLayout.removeAllViews( );
        PagerAdapter adapter = mViewPager.getAdapter( );
        IconPagerAdapter iconAdapter = null;
        if ( adapter instanceof IconPagerAdapter ) {
            iconAdapter = (IconPagerAdapter) adapter;
        }
        final int count = adapter.getCount( );
        for ( int i = 0; i < count; i++ ) {
            CharSequence title = adapter.getPageTitle( i );
            if ( title == null ) {
                title = EMPTY_TITLE;
            }
            int iconResId = 0;
            if ( iconAdapter != null ) {
                iconResId = iconAdapter.getIconResIdNormal( i );
            }
            addTab( i, title, iconResId );
        }
        if ( mSelectedTabIndex > count ) {
            mSelectedTabIndex = count - 1;
        }
        setCurrentItem( mSelectedTabIndex );
        requestLayout( );
    }
    
    @Override
    public void setViewPager( ViewPager view, int initialPosition ) {
        setViewPager( view );
        setCurrentItem( initialPosition );
    }
    
    @Override
    public void setCurrentItem( int item ) {
        if ( mViewPager == null ) {
            throw new IllegalStateException( "ViewPager has not been bound." );
        }
        mSelectedTabIndex = item;
        mViewPager.setCurrentItem( item );
        
        final int tabCount = mTabLayout.getChildCount( );
        for ( int i = 0; i < tabCount; i++ ) {
            final LinearLayout child = (LinearLayout) mTabLayout.getChildAt( i );
            //final TabView child = (TabView) mTabLayout.getChildAt( i );
            final boolean isSelected = ( i == item );
            child.setSelected( isSelected );
            PagerAdapter adapter = mViewPager.getAdapter( );
            IconPagerAdapter iconAdapter = null;
            if ( adapter instanceof IconPagerAdapter ) {
                iconAdapter = (IconPagerAdapter) adapter;
            }
            int iconResId;
            int bgResId;
            if ( iconAdapter != null ) {
                if ( isSelected ) {
                    iconResId = iconAdapter.getIconResIdSelected( i );
                    bgResId = iconAdapter.getBackgroundResIdSelected( i );
                } else {
                    iconResId = iconAdapter.getIconResIdNormal( i );
                    bgResId = iconAdapter.getBackgroundResIdNormal( i );
                }
                
                if ( bgResId != 0 )
                    child.setBackgroundResource( bgResId );
                
                ImageView iv = (ImageView)child.findViewById( R.id.ivIcon );
                if ( iconResId != 0 ) {
                    iv.setVisibility( View.VISIBLE );
                    iv.setImageResource( iconResId );
                }else{
                    iv.setVisibility( View.GONE );
                }
            }
            
            if ( isSelected ) {
                animateToTab( item );
            }
        }
    }
    
    @Override
    public void setOnPageChangeListener( OnPageChangeListener listener ) {
        mListener = listener;
    }    
}


Finally update library's  IconPagerAdapter .java

public interface IconPagerAdapter {
    /**
     * Get icon representing the page at {@code index} in the adapter.
     */
    int getIconResIdNormal(int index);
    /**
     * Get icon representing the page at {@code index} in the adapter This will be used when the tab is selected.
     */
    int getIconResIdSelected(int index);
    // From PagerAdapter
    int getCount();
    /**
     * Get background representing the page at {@code index} in the adapter This will be used when the tab is selected.
     */
    int getBackgroundResIdSelected(int index);
    /**
     * Get background representing the page at {@code index} in the adapter..
     */
    int getBackgroundResIdNormal(int index);
}

Sunday, May 4, 2014

Downloading image blob from Google's App Engine

Last time we have seen how to upload an image to app engine. Now its time to fetch it back. There are 2 ways to do that.

1. Using serve()
2. Using urls generate for each blob.

I tried using 1st method, but it did not work. So tried out second method which did work. Following are the details of that implementation.

First get the url for download for each blob. To do that you need blob key of your blobs. In my case I have saved blob keys in a separate database entity. Blob key is returned when you create new blob.
        
String[] array = getBlobKeyByProblemId(problemId);
if (array != null && array.length > 0) {
    String[] urls = new String[array.length];
    for (int i = 0; array != null && i < array.length; i++) {
        ImagesService imagesService = ImagesServiceFactory.getImagesService();
        BlobKey key = new BlobKey(array[i]);
        ServingUrlOptions options = ServingUrlOptions.Builder.withBlobKey(key).secureUrl(true);
        urls[i] = imagesService.getServingUrl(options);
    }
}

Send this key to client, so that whenever it needs to download image it will use this url.
 
Now time to get the image. Call the following method as:
returnValue = httpPostBinary( entry.url, null );

private byte[] httpPostBinary( String url, String data ) {
        if ( url == null )
            return null;
        
        int bufferSize = 1024;
        int loopCounter = 3;
        int timeout = 1 * 60 * 1000;
        byte[] retVal = null;
        HttpURLConnection urlConnection = null;
        while ( loopCounter > 0 ) {
            try {
                retVal = null;
                Log.d( Utils.AppName, "CommunicationManager.httpPostBinary(): Communication attempt#" + loopCounter );
                byte[] buffer = new byte[bufferSize];
                URL httpUrl = new URL( url );
                // handleTrustedConnection();
                urlConnection = (HttpURLConnection) httpUrl.openConnection( );
                urlConnection.setRequestMethod( "POST" );
                urlConnection.setRequestProperty( "User-Agent", "eRecommendation" );
                urlConnection.setRequestProperty( "Accept", "*/*" );
                urlConnection.setRequestProperty( "Content-Type", "application/xml" );
                if ( data != null )
                    urlConnection.setDoOutput( true );
                else
                    urlConnection.setDoOutput( false );
                urlConnection.setConnectTimeout( timeout );
                if ( data != null ) {
                    OutputStream outputStream = urlConnection.getOutputStream( );
                    if ( outputStream != null ) {
                        DataOutputStream dataOutputStream = new DataOutputStream( outputStream );
                        dataOutputStream.writeUTF( data );
                        dataOutputStream.close( );
                        outputStream.close( );
                    }
                }
                InputStream inputStream = urlConnection.getInputStream( );
                if ( inputStream != null ) {
                    ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream( );
                    int count;
                    while ( true ) {
                        count = inputStream.read( buffer );
                        if ( count == -1 )
                            break;
                        arrayOutputStream.write( buffer, 0, count );
                    }
                    retVal = arrayOutputStream.toByteArray( );
                    arrayOutputStream.close( );
                    inputStream.close( );
                }
                if ( retVal != null && retVal.length > 0 )
                    break;
            } catch ( Exception e ) {
                Log.d( Utils.AppName, "CommunicationManager.httpPost():" + e.toString( ) );
                retVal = null;
            } finally {
                if ( urlConnection != null )
                    urlConnection.disconnect( );
            }
            try {
                Thread.sleep( 1000 );
            } catch ( InterruptedException e ) {
            }
            loopCounter--;
        }
        return retVal;
    }

retVal contains array bytes which forms the image. You can save this image in file system and use it.

Android aar deployment in Maven - 2022

Introduction If you are working on android library project, you might be wondering how to publish it on Maven like this . Earl...