Pages

Strategic Planning for Enterprise Mobility



Strategic Planning for Enterprise Mobility

             Plan Strategi untuk masalah mobility adalah topic yang sangat menarik, dimana banyak elemen-elemen bisnis yang harus beralih dari it yang bersifat statis atau web based kepada it atau aplikasi yang bersifat mobility atau mobile enterprise, oleh karena itu artikel ini akan membahas bagaimana memplanning strategi untuk mobile enterprise. Menurut Rahul C empat strategi yang harus di perhatikan untuk membangun strategi planning for enterprise mobility adalah sebagai berikut:


Gambar 1: area kritikal dari mobility enterprise (Basole, 2007)

1.      Business Value
             Untuk enterprise mobility berbagai macam solusi menarik bermunculan, kemampuan untuk mengakse network dan resource perusahaan dari manapun dan kapanpun menjadi salah satu benefit dan kunci utama dalam mengadopsi mobility ICT (Basole, 2007). Pekerja lapangna tidak lagi terikat pada desktop untuk mengakses data-data kritikal. Pekerja juga dapat menerima jawaban yang di butuhkan saat itu juga sehingga dapat mengambil keputusan dengan cepat. Enterprise mobility juga menawarkan solusi untuk memotong biaya operasional. Alat-alat yang memerlukan biaya yang besar dapat diganti dengan alat-alat yang lebih portable dengan sekala yang lebih kecil misalnya smartphones, dan yang lebih luas lagi adalah mengurangi penggunaan kertas yang ada, dengan aplikasi mobile mengurangi potensial error dalam mengirimkan report, membawa ke tingkat yang lebih tinggi dari integritas dan akurasi data, hal itu dapat di aplikasikan ke dalam berbagai macam bisnis intelligence, seperti order shipping yang mengurangi cost dengan mengirimkan data secara real-time, memberikan efisiensi dalam (Basole, 2007).

2.      Cost/Economic
            Element kedua yang harus di perhatikan adalah aspek yang selanjutnya harus diperhatikan. Organisasi yang akan mengadopsi ICT tidak hanya harus memperhatikan tanglible dan intangible benefit darimengadopsi ICT tetapi juga harus memperhatikan cost dari berhasilnya implementasi ICT(). Lebih besar dan complex-nya suatu scope dari ICT initiativemaka akan membengkak pula resource yang di perlukan hal ini akan membawa pada cost yang lebih tinggi pula. Banyak organisasi yang salah mengkankulasi cost ketika mengadopsi dan mengimplementasikan ICT, mereka hanya menghitung inlai dari pengeluaran hardware, infrastructure dan instalasi dan configurasi software tetapi tidak memperhitungkan cost yang berasal dari transformasi ICT lama ke ICT yang baru. Jika organisasi tidak mengiplementasikan ICT yang baru mereka harus menghitung berapa biaya kehilangan. Penghitugan cost dan ekonomi dapat mengantarkan kepada implementasi mobility ICT dengan sukses karena hal ini berkaitan dengan proses strategi planning. 

3.      Strategic Aligment
            Elemenet ketiga adalah trategic alignment dari mobile ICT dengan bisnis objective. Organisasi harus mengevaluasi apa impact dari mengadopsi dan mengimplementasikan mobile ICT terhadap keseluruhan strategi bisnis. Jika penggabungan ICT dengan tujuan jangka panjang dari bisnis tidak terpenuhi maka keuntungan dari mengadopsi dan mengimplementasikan ICT tidak akan terlalu member manfaat.

4.      Enterprise Readiness
            Enterprise readiness merepresentasikan bagaimana kesiapan suatu organisasi untuk mengadopsi dan mengimplementasikaan new mobility ICT, meskipun cost sudah terpenuhi, implementasi mobile ICT sudah berjalan seiring dengan bisnis strategi keseluruhan tetapi jika enterprise belum siap maka,

Untuk meminimalisir resiko dan meamkasimalkan keuntungan enterprise mobility solution, organisasi tidak hanya harus mengetahui nilai danfaktor ekonomi dari enterprise mobility tetapi juga harus dapat mengevaluasi dengan hati-hati bagaimana kesiapan dari penerapan mobility enterprise.

Referensi:
 Basole, R. (2007). Strategic Planning for Enterprise Mobility: A Readiness-Centric Approach. Amcis, (Zetie). Retrieved from http://www.ti.gatech.edu/basole/docs/Basole.AMCIS2007.StrategicPlanningEnterpriseMobility.pdf


Strategic Planning Enterprise Mobility 

Paper Summary - Evaluation of Ethical Issues in the Knowledge Age: An Exploratory Study. Volume 14, Issue 1, pp.106-112, 2013

Summary

Paper ini membahas mengenai isu-isu etika yang terkait dengan orang-orang yang bekerja pada bidang ilmu pengetahuan contohnya seperti software engineers, doctors, architects, engineers, scientists, public accountants, lawyers, and academics. Dengan adanya perkembangat teknologi informasi yang sangat cepat dan berbeda-beda membuat perubahan pada bisnis dan ekonomi

Dalam 2 dekade banyak disaksikan berbedaan perkembangat computer dan teknologi informasi yang sangat cepat dalam bidang bisnis diseluruh temapt. Masyarakat pun telah memasuki era teknologi informasi yang sering disebut sebagai "knowlege age", Managers, researcher, dan akademisi menjadi aset kritikal yang sangat penting bagi tenologi dan informasi. Mayarakat telah berubah dari era industri ke arah era setelah industri dimana sumber informasi teknologi menjadi tidak terbatas, teknologi informasi menajdi sumber utama dari keuntungan dan kelebihan bagi sebuah organisasi, Mengingat pengtingnya infomasi dan teknologi dalam sebuah bisnis ekonomi yang baru membuar para pekerja dalam bidang keilmuan memanfaatkan IT dan manager mereka untuk lebih mengetahui hubungan antara menggunakan informasi teknologi dan bekerja pada bidang mereka. oleh karena itu "knowlege era" ini menjadikan etika menjadi sangat vital dalam perkembangan teknologi dan informasi. Dengan adanya "knowledge era" ini membuat penerapan etika yang didalamnya termasuk privacy, property, accuracy, accessibility, nilai dan tanggung jawab pada setiap individu harus didefinisikan ulang agar menjadikan teknologi dan informasi sangat bermanfaat dan berkembang dalam semua bidang 

Meskipun investigasi dari isu-isu etika dengan komputer berkembang sejak tahun 1980, hal itu telah berkembang dalam beberapa dekade berikutnya. awalnya isu-itu itu terkait dengan pertanyaan bagaimana mengembangkan sebuah sistem yang kemudian bergeser ke arah internet, penggunaan komputer, dan teknologi komunikasi. dimana bisnis dan personal informasi berlanjut berkembang dengan pesat untuk mengakses teknologi informasi, menjadikan pelanggaran nilai dan etika oleh IT profesional dan pekerja dalam bilang keilmuan menjadi sangat penting

Didalam paper ini mereview literature yang terkait dengan etika teknologi dan system informasi. kemudian kita memaparkan fakta empiris dari investigasi dari perkerja bidang keilmuan dan IT profesional. kemudian kita akan memaparkan juga model perilaku etis yang komprehensif

Insight

Tujuan utama dari studi ini adalah untuk menginvestigasi isu-isu etika apa saja yang sudah di alami oleh pekerja dalam bidang ilmu pengetahuan dan IT profesionals dan bagaimana mengatasi dan mengendalikan masalah tersebut

Terdapat empat pertanyaan utama dalam penelitian ini diantaranya adalah

1. Apakah pencegahan etika sudah efektif dalam mengurangi kemungkinan perilaku tidak etis?

2.Dapatkah menjelaskan, termasuk code etik, education dan pelatihan yagn menjelaskan kejadian kecil dari perilaku tidak etis?

3.Strategi penanganan apa yang dapat mengatasi isu-isu etika dalam era pengetahuan?

4.Apakah ada perbedaan gender dalam indentifikasi dari perilakku tidak etis dalam era pengetahuan?


Paper ini menghadirkan solusi dengan membuat model untuk menjelaskan keterkaitan perilaku ketidak-etisan dengan beberapa faktor penyebabnya diantaranya adalah faktor indifidual, pencegahan, teleogical dan faktor external seperti IT guideline.

Model tersebut menampilkan pencegahan sebagai summative unit dari perilaku tidak etis.





Application  to Research

Ditegakkannya Perilaku Etika dalam IT sangat penting bagi seorang pembisnis maupun seorang peneliti. banyak penelitian yang sudah selesai untuk mengevaluasi efek dari beberapa faktor ketika membuat keputusan dalam bidang teknologi informasi. Dampak yang terjadi pada finansial yang ditimbulkan dari perilaku tidak etis sangat besar. Dengan pengungkapan bencana yang melekat dalam skandal korporasi dalam dekade terakhir, pembelajaran maupun pendekatan untuk mempelajari etika yang baik menjadi sangat dibutuhkan

Hasil dari penelitian ini akan memiliki dampak terhadap partisipan dan peneliti. untuk IS peneliti akan ada dua dampak
1.memahami pengaruh perilaku tidak etis teknologi informasi diamana perilaku tidak etis ini akan memilki manfaat yang sangat besar ketika dapat ditangani


2.untuk bisnis dan ekonomi akan menjadi lebih global dan digital, hal ini akan membuat para pekerja dalam bidang keilmuan harus belajar bagaimana berperilaku etis terhadap teknologi informasi


Paper ini belum memaparkan secara lengkap dan jelas bagaimana mencegah dan pengaruh faktor-faktor yang ada terhadap perilaku etika dalam IT, akan tetapi paper ini sudah cukup menggambarkan apa saja faktor yang mempengaruhi perilaku tidak etis dengan adanya model yang telah dibuat



Paper Summary - Evaluation of Ethical Issues in the Knowledge Age: An Exploratory Study. Volume 14, Issue 1, pp.106-112, 2013


Curl Page - harism modify left to right

I have modify code from harism to fit my requirement curl page from left to right like read arabic book. thanks to harism, its cool...
package fi.harism.curl; 

import android.annotation.SuppressLint; 
import android.content.Context; 
import android.graphics.PointF; 
import android.graphics.RectF; 
import android.opengl.GLSurfaceView; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.View; 

@SuppressLint("FloatMath") 
public class CurlView extends GLSurfaceView implements
        View.OnTouchListener, CurlRenderer.Observer { 

    // Curl state. We are flipping none, left or right page. 
    private static final int CURL_LEFT = 1;
    private static final int CURL_NONE = 0;
    private static final int CURL_RIGHT = 2;

    // Constants for mAnimationTargetEvent. 
    private static final int SET_CURL_TO_LEFT = 1;
    private static final int SET_CURL_TO_RIGHT = 2;

    // Shows one page at the center of view. 
    public static final int SHOW_ONE_PAGE = 1;
    // Shows two pages side by side. 
    public static final int SHOW_TWO_PAGES = 2;

    private boolean mAllowLastPageCurl = true;

    private boolean mAnimate = false;
    private long mAnimationDurationTime = 300;
    private PointF mAnimationSource = new PointF();
    private long mAnimationStartTime;
    private PointF mAnimationTarget = new PointF();
    private int mAnimationTargetEvent;

    private PointF mCurlDir = new PointF();

    private PointF mCurlPos = new PointF();
    private int mCurlState = CURL_NONE;
    // Current bitmap index. This is always showed as front of right page. 
    private int mCurrentIndex = 0;

    // Start position for dragging. 
    private PointF mDragStartPos = new PointF();

    private boolean mEnableTouchPressure = false;
    // Bitmap size. These are updated from renderer once it's initialized. 
    private int mPageBitmapHeight = -1; 

    private int mPageBitmapWidth = -1; 
    // Page meshes. Left and right meshes are 'static' while curl is used to
    // show page flipping. 
    private CurlMesh mPageCurl; 

    private CurlMesh mPageLeft; 
    private PageProvider mPageProvider; 
    private CurlMesh mPageRight; 

    private PointerPosition mPointerPos = new PointerPosition();

    private CurlRenderer mRenderer; 
    private boolean mRenderLeftPage = true;
    private SizeChangedObserver mSizeChangedObserver; 

    // One page is the default. 
    private int mViewMode = SHOW_ONE_PAGE;
     
    private boolean RIGHT_TO_LEFT = true;

    /** 
     * Default constructor. 
     */ 
    public CurlView(Context ctx) { 
        super(ctx); 
        init(ctx); 
    } 

    /** 
     * Default constructor. 
     */ 
    public CurlView(Context ctx, AttributeSet attrs) {
        super(ctx, attrs); 
        init(ctx); 
    } 

    /** 
     * Default constructor. 
     */ 
    public CurlView(Context ctx, AttributeSet attrs, int defStyle) {
        this(ctx, attrs); 
    } 

    /** 
     * Get current page index. Page indices are zero based values presenting 
     * page being shown on right side of the book. 
     */ 
    public int getCurrentIndex() {
        return mCurrentIndex; 
    } 

    /** 
     * Initialize method. 
     */ 
    private void init(Context ctx) {
        mRenderer = new CurlRenderer(this);
        setRenderer(mRenderer); 
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 
        setOnTouchListener(this); 

        // Even though left and right pages are static we have to allocate room
        // for curl on them too as we are switching meshes. Another way would be
        // to swap texture ids only. 
        mPageLeft = new CurlMesh(10);
        mPageRight = new CurlMesh(10);
        mPageCurl = new CurlMesh(10);
        mPageLeft.setFlipTexture(true); 
        mPageRight.setFlipTexture(false); 
    } 

    @Override 
    public void onDrawFrame() {
        //TODO:on draw frame 
        // We are not animating. 
        if (mAnimate == false) {
            return; 
        } 
        long currentTime = System.currentTimeMillis(); 
        // If animation is done. 
        if (currentTime >= mAnimationStartTime + mAnimationDurationTime) {
            if (mAnimationTargetEvent == SET_CURL_TO_RIGHT) { 
                // Switch curled page to right. 
                CurlMesh right = mPageCurl; 
                CurlMesh curl = mPageRight; 
                right.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT)); 
                right.setFlipTexture(false); 
                right.reset(); 
                mRenderer.removeCurlMesh(curl); 
                mPageCurl = curl; 
                mPageRight = right; 
                // If we were curling left page update current index. 
                if (mCurlState == CURL_LEFT) { 
                    --mCurrentIndex; 
                } 
            } else if (mAnimationTargetEvent == SET_CURL_TO_LEFT) {
                // Switch curled page to left. 
                CurlMesh left = mPageCurl; 
                CurlMesh curl = mPageLeft; 
                left.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); 
                left.setFlipTexture(true); 
                left.reset(); 
                mRenderer.removeCurlMesh(curl); 
                if (!mRenderLeftPage) { 
                    mRenderer.removeCurlMesh(left); 
                } 
                mPageCurl = curl; 
                mPageLeft = left; 
                // If we were curling right page update current index. 
                if (mCurlState == CURL_RIGHT) { 
                    ++mCurrentIndex; 
                } 
            } 
            mCurlState = CURL_NONE; 
            mAnimate = false; 
            requestRender(); 
        } else { 
            mPointerPos.mPos.set(mAnimationSource); 
            float t = 1f - ((float) (currentTime - mAnimationStartTime) / mAnimationDurationTime);
            t = 1f - (t * t * t * (3 - 2 * t));
            mPointerPos.mPos.x += (mAnimationTarget.x - mAnimationSource.x) * t; 
            mPointerPos.mPos.y += (mAnimationTarget.y - mAnimationSource.y) * t; 
            updateCurlPos(mPointerPos); 
        } 
    } 

    @Override 
    public void onPageSizeChanged(int width, int height) {
        mPageBitmapWidth = width; 
        mPageBitmapHeight = height; 
        updatePages(); 
        requestRender(); 
    } 

    @Override 
    public void onSizeChanged(int w, int h, int ow, int oh) {
        super.onSizeChanged(w, h, ow, oh); 
        requestRender(); 
        if (mSizeChangedObserver != null) {
            mSizeChangedObserver.onSizeChanged(w, h); 
        } 
    } 

    @Override 
    public void onSurfaceCreated() {
        // In case surface is recreated, let page meshes drop allocated texture
        // ids and ask for new ones. There's no need to set textures here as
        // onPageSizeChanged should be called later on. 
        mPageLeft.resetTexture(); 
        mPageRight.resetTexture(); 
        mPageCurl.resetTexture(); 
    } 

    @Override 
    public boolean onTouch(View view, MotionEvent me) {
        // No dragging during animation at the moment. 
        // TODO: Stop animation on touch event and return to drag mode. 
        if (mAnimate || mPageProvider == null) {
            return false;
        } 

        // We need page rects quite extensively so get them for later use. 
        RectF rightRect = mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT); 
        RectF leftRect = mRenderer.getPageRect(CurlRenderer.PAGE_LEFT); 

        // Store pointer position. 
        mPointerPos.mPos.set(me.getX(), me.getY()); 
        mRenderer.translate(mPointerPos.mPos); 
         
        if (mEnableTouchPressure) { 
            mPointerPos.mPressure = me.getPressure(); 
        } else { 
            mPointerPos.mPressure = 0.8f; 
        } 

        switch (me.getAction()) { 
        case MotionEvent.ACTION_DOWN: { 

            // Once we receive pointer down event its position is mapped to
            // right or left edge of page and that'll be the position from where
            // user is holding the paper to make curl happen.
            mDragStartPos.set(mPointerPos.mPos); 
            //mDragStartPos.y = 0.0f; 
            // First we make sure it's not over or below page. Pages are
            // supposed to be same height so it really doesn't matter do we use
            // left or right one. 
            if (mDragStartPos.y > rightRect.top) { 
                mDragStartPos.y = rightRect.top; 
            } else if (mDragStartPos.y < rightRect.bottom) {
                mDragStartPos.y = rightRect.bottom; 
            } 

            // Then we have to make decisions for the user whether curl is going
            // to happen from left or right, and on which page. 
            if (mViewMode == SHOW_TWO_PAGES) { 
                // If we have an open book and pointer is on the left from right
                // page we'll mark drag position to left edge of left page. 
                // Additionally checking mCurrentIndex is higher than zero tells
                // us there is a visible page at all. 
                if (mDragStartPos.x < rightRect.left && mCurrentIndex > 0) {
                    mDragStartPos.x = leftRect.left; 
                    startCurl(CURL_LEFT); 
                } 
                // Otherwise check pointer is on right page's side. 
                else if (mDragStartPos.x >= rightRect.left
                        && mCurrentIndex < mPageProvider.getPageCount()) { 
                    mDragStartPos.x = rightRect.right; 
                    if (!mAllowLastPageCurl 
                            && mCurrentIndex >= mPageProvider.getPageCount() - 1) {
                        return false;
                    } 
                    startCurl(CURL_RIGHT); 
                } 
            } else if (mViewMode == SHOW_ONE_PAGE) {
                float halfX = (leftRect.right + leftRect.left) / 2;
                if (mDragStartPos.x < halfX && mCurrentIndex > 0) {
                    mDragStartPos.x = leftRect.left; 
                    startCurl(CURL_LEFT); 
                } else if (mDragStartPos.x >= halfX
                        && mCurrentIndex < mPageProvider.getPageCount()) { 
                    mDragStartPos.x = rightRect.left; 
                    if (!mAllowLastPageCurl 
                            && mCurrentIndex >= mPageProvider.getPageCount() - 1) {
                        return false;
                    } 
                    startCurl(CURL_RIGHT); 
                } 
            } 
            // If we have are in curl state, let this case clause flow through
            // to next one. We have pointer position and drag position defined
            // and this will create first render request given these points. 
            if (mCurlState == CURL_NONE) { 
                return false;
            } 
        } 
        case MotionEvent.ACTION_MOVE: { 
            updateCurlPos(mPointerPos); 
            break; 
        } 
        case MotionEvent.ACTION_CANCEL: 
        case MotionEvent.ACTION_UP: { 
            if (mCurlState == CURL_LEFT || mCurlState == CURL_RIGHT) {
                // Animation source is the point from where animation starts. 
                // Also it's handled in a way we actually simulate touch events
                // meaning the output is exactly the same as if user drags the
                // page to other side. While not producing the best looking
                // result (which is easier done by altering curl position and/or
                // direction directly), this is done in a hope it made code a
                // bit more readable and easier to maintain. 
                mAnimationSource.set(mPointerPos.mPos); 
                mAnimationStartTime = System.currentTimeMillis(); 

                // Given the explanation, here we decide whether to simulate
                // drag to left or right end. 
                if ((mViewMode == SHOW_ONE_PAGE && mPointerPos.mPos.x < (leftRect.left + leftRect.right) / 2)
                        || mViewMode == SHOW_TWO_PAGES && mPointerPos.mPos.x < rightRect.left) {
                    // On right side target is always right page's right border. 
                    mAnimationTarget.set(mDragStartPos); 
                    mAnimationTarget.x = mRenderer.getPageRect(CurlRenderer.PAGE_LEFT).left; 
                    mAnimationTargetEvent = SET_CURL_TO_LEFT; 
                } else { 
                    // On left side target depends on visible pages. 
                    mAnimationTarget.set(mDragStartPos); 
                    if (mCurlState == CURL_RIGHT && mViewMode == SHOW_TWO_PAGES || mCurlState == CURL_LEFT && mPointerPos.mPos.x > rightRect.left) {
                        mAnimationTarget.x = rightRect.right; 
                    } else { 
                        mAnimationTarget.x = leftRect.right; 
                    } 
                    mAnimationTargetEvent = SET_CURL_TO_RIGHT; 
                } 
                mAnimate = true; 
                requestRender(); 
            } 
            break; 
        } 
        } 

        return true; 
    } 

    /** 
     * Allow the last page to curl. 
     */ 
    public void setAllowLastPageCurl(boolean allowLastPageCurl) {
        mAllowLastPageCurl = allowLastPageCurl; 
    } 

    /** 
     * Sets background color - or OpenGL clear color to be more precise. Color 
     * is a 32bit value consisting of 0xAARRGGBB and is extracted using 
     * android.graphics.Color eventually. 
     */ 
    @Override 
    public void setBackgroundColor(int color) {
        mRenderer.setBackgroundColor(color); 
        requestRender(); 
    } 

    /** 
     * Sets mPageCurl curl position. 
     */ 
    //TODO:startCurlPost 
    private void setCurlPos(PointF curlPos, PointF curlDir, double radius) {
        if (mCurlState == CURL_LEFT || (mCurlState == CURL_RIGHT && mViewMode == SHOW_ONE_PAGE)) {
            RectF pageRect = mRenderer.getPageRect(CurlRenderer.PAGE_LEFT); 
            if (curlPos.x <= pageRect.left) { 
                mPageCurl.reset(); 
                requestRender(); 
                return; 
            } 
            if (curlPos.x > pageRect.right) { 
                curlPos.x = pageRect.right; 
            } 
            if (curlDir.y != 0) {
                float diffX = curlPos.x - pageRect.right; 
                float rightY = curlPos.y + (diffX * curlDir.x / curlDir.y);
                if (curlDir.y < 0 && rightY < pageRect.top) {
                    curlDir.x = pageRect.top - curlPos.y; 
                    curlDir.y = curlPos.x - pageRect.right; 
                } else if (curlDir.y > 0 && rightY > pageRect.bottom) {
                    curlDir.x = curlPos.y - pageRect.bottom; 
                    curlDir.y = pageRect.right - curlPos.x; 
                } 
            } 
        }else if(mCurlState == CURL_RIGHT){ 
            RectF pageRect = mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT); 
            if (curlPos.x >= pageRect.right) { 
                mPageCurl.reset(); 
                requestRender(); 
                return; 
            } 
            if (curlPos.x < pageRect.left) { 
                curlPos.x = pageRect.left; 
            } 
            if (curlDir.y != 0) {
                float diffX = curlPos.x - pageRect.left; 
                float leftY = curlPos.y + (diffX * curlDir.x / curlDir.y);
                if (curlDir.y < 0 && leftY < pageRect.top) {
                    curlDir.x = curlPos.y - pageRect.top; 
                    curlDir.y = pageRect.left - curlPos.x; 
                } else if (curlDir.y > 0 && leftY > pageRect.bottom) {
                    curlDir.x = pageRect.bottom - curlPos.y; 
                    curlDir.y = curlPos.x - pageRect.left; 
                } 
            } 
        } 

        // Finally normalize direction vector and do rendering. 
        double dist = Math.sqrt(curlDir.x * curlDir.x + curlDir.y * curlDir.y);
        if (dist != 0) { 
            curlDir.x /= dist; 
            curlDir.y /= dist; 
            mPageCurl.curl(curlPos, curlDir, radius); 
        } else { 
            mPageCurl.reset(); 
        } 

        requestRender(); 
    } 

    /** 
     * Set current page index. Page indices are zero based values presenting 
     * page being shown on right side of the book. E.g if you set value to 4; 
     * right side front facing bitmap will be with index 4, back facing 5 and 
     * for left side page index 3 is front facing, and index 2 back facing (once 
     * page is on left side it's flipped over). 
     *  
     * Current index is rounded to closest value divisible with 2. 
     */ 
    public void setCurrentIndex(int index) {
        if (mPageProvider == null || index < 0) {
            mCurrentIndex = 0; 
        } else { 
            if (mAllowLastPageCurl) { 
                mCurrentIndex = Math.min(index, mPageProvider.getPageCount()); 
            } else { 
                mCurrentIndex = Math.min(index, 
                        mPageProvider.getPageCount() - 1); 
            } 
        } 
        updatePages(); 
        requestRender(); 
    } 

    /** 
     * If set to true, touch event pressure information is used to adjust curl 
     * radius. The more you press, the flatter the curl becomes. This is 
     * somewhat experimental and results may vary significantly between devices. 
     * On emulator pressure information seems to be flat 1.0f which is maximum 
     * value and therefore not very much of use. 
     */ 
    public void setEnableTouchPressure(boolean enableTouchPressure) {
        mEnableTouchPressure = enableTouchPressure; 
    } 

    /** 
     * Set margins (or padding). Note: margins are proportional. Meaning a value 
     * of .1f will produce a 10% margin. 
     */ 
    public void setMargins(float left, float top, float right, float bottom) {
        mRenderer.setMargins(left, top, right, bottom); 
    } 

    /** 
     * Update/set page provider. 
     */ 
    public void setPageProvider(PageProvider pageProvider) {
        mPageProvider = pageProvider; 
        mCurrentIndex = 0; 
        updatePages(); 
        requestRender(); 
    } 

    /** 
     * Setter for whether left side page is rendered. This is useful mostly for 
     * situations where right (main) page is aligned to left side of screen and 
     * left page is not visible anyway. 
     */ 
    public void setRenderLeftPage(boolean renderLeftPage) {
        mRenderLeftPage = renderLeftPage; 
    } 

    /** 
     * Sets SizeChangedObserver for this View. Call back method is called from 
     * this View's onSizeChanged method. 
     */ 
    public void setSizeChangedObserver(SizeChangedObserver observer) {
        mSizeChangedObserver = observer; 
    } 

    /** 
     * Sets view mode. Value can be either SHOW_ONE_PAGE or SHOW_TWO_PAGES. In 
     * former case right page is made size of display, and in latter case two 
     * pages are laid on visible area. 
     */ 
    public void setViewMode(int viewMode) {
        switch (viewMode) { 
        case SHOW_ONE_PAGE: 
            mViewMode = viewMode; 
            mPageLeft.setFlipTexture(false); 
            mRenderer.setViewMode(CurlRenderer.SHOW_ONE_PAGE); 
            mRenderer.setRightToLeft(RIGHT_TO_LEFT); 
            break; 
        case SHOW_TWO_PAGES: 
            mViewMode = viewMode; 
            mPageLeft.setFlipTexture(false); 
            mRenderer.setViewMode(CurlRenderer.SHOW_TWO_PAGES); 
            break; 
        } 
    } 

    /** 
     * Switches meshes and loads new bitmaps if available. Updated to support 2 
     * pages in landscape 
     */ 
    private void startCurl(int page) {
        switch (page) { 
        //TODO:starcurl 
        // Once right side page is curled, first right page is assigned into
        // curled page. And if there are more bitmaps available new bitmap is
        // loaded into right side mesh. 
        case CURL_RIGHT: { 
            // Remove meshes from renderer. 
            mRenderer.removeCurlMesh(mPageLeft); 
            mRenderer.removeCurlMesh(mPageRight); 
            mRenderer.removeCurlMesh(mPageCurl); 

            // We are curling right page. 
            CurlMesh curl = mPageRight; 
            mPageRight = mPageCurl; 
            mPageCurl = curl; 

            if (mCurrentIndex > 0) {
                mPageLeft.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); 
                mPageLeft.setFlipTexture(true); 
                mPageLeft.reset(); 
                if (mRenderLeftPage) { 
                    mRenderer.addCurlMesh(mPageLeft); 
                } 
            } 
            if (mCurrentIndex < mPageProvider.getPageCount() - 1) {
                updatePage(mPageRight.getTexturePage(), mCurrentIndex + 1);
                mPageRight.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT)); 
                mPageRight.setFlipTexture(false); 
                mPageRight.reset(); 
                mRenderer.addCurlMesh(mPageRight); 
            } 
             
            // How dragging previous page happens depends on view mode. 
            if (mViewMode == SHOW_ONE_PAGE || (mCurlState == CURL_LEFT && mViewMode == SHOW_TWO_PAGES)) {
                mPageCurl.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); 
                mPageCurl.setFlipTexture(true); 
            } else { 
                mPageCurl.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT)); 
                mPageCurl.setFlipTexture(false); 
            } 
             
            mPageCurl.reset(); 
            mRenderer.addCurlMesh(mPageCurl); 

            mCurlState = CURL_RIGHT; 
            break; 
        } 

        // On left side curl, left page is assigned to curled page. And if
        // there are more bitmaps available before currentIndex, new bitmap
        // is loaded into left page. 
        case CURL_LEFT: { 
            // Remove meshes from renderer. 
            mRenderer.removeCurlMesh(mPageLeft); 
            mRenderer.removeCurlMesh(mPageRight); 
            mRenderer.removeCurlMesh(mPageCurl); 

            // We are curling left page. 
            CurlMesh curl = mPageLeft; 
            mPageLeft = mPageCurl; 
            mPageCurl = curl; 

            if (mCurrentIndex > 1) {
                updatePage(mPageLeft.getTexturePage(), mCurrentIndex - 2);
                mPageLeft.setFlipTexture(true); 
                mPageLeft.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); 
                mPageLeft.reset(); 
                if (mRenderLeftPage) { 
                    mRenderer.addCurlMesh(mPageLeft); 
                } 
            } 

            // If there is something to show on right page add it to renderer. 
            if (mCurrentIndex < mPageProvider.getPageCount()) {
                mPageRight.setFlipTexture(false); 
                mPageRight.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT)); 
                mPageRight.reset(); 
                mRenderer.addCurlMesh(mPageRight); 
            } 

            mPageCurl.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); 
            mPageCurl.setFlipTexture(true); 
            mPageCurl.reset(); 
            mRenderer.addCurlMesh(mPageCurl); 

            mCurlState = CURL_LEFT; 
            break; 
        } 

        } 
    } 

    /** 
     * Updates curl position. 
     */ 
    @SuppressLint("FloatMath") 
    private void updateCurlPos(PointerPosition pointerPos) {

        // Default curl radius. 
        double radius = mRenderer.getPageRect(CURL_RIGHT).width() / 3;
        // TODO: This is not an optimal solution. Based on feedback received so
        // far; pressure is not very accurate, it may be better not to map
        // coefficient to range [0f, 1f] but something like [.2f, 1f] instead. 
        // Leaving it as is until get my hands on a real device. On emulator
        // this doesn't work anyway. 
        radius *= Math.max(1f - pointerPos.mPressure, 0f);
        // NOTE: Here we set pointerPos to mCurlPos. It might be a bit confusing
        // later to see e.g "mCurlPos.x - mDragStartPos.x" used. But it's
        // actually pointerPos we are doing calculations against. Why? Simply to
        // optimize code a bit with the cost of making it unreadable. Otherwise
        // we had to this in both of the next if-else branches. 
        mCurlPos.set(pointerPos.mPos); 
        //mCurlPos.y = 0.0f; 
        // If curl happens on right page, or on left page on two page mode, 
        // we'll calculate curl position from pointerPos. 
        if (mCurlState == CURL_LEFT || (mCurlState == CURL_RIGHT && mViewMode == SHOW_TWO_PAGES)) {

            mCurlDir.x = mCurlPos.x - mDragStartPos.x; 
            mCurlDir.y = mCurlPos.y - mDragStartPos.y; 
            float dist = (float) Math.sqrt(mCurlDir.x * mCurlDir.x + mCurlDir.y * mCurlDir.y);

            // Adjust curl radius so that if page is dragged far enough on
            // opposite side, radius gets closer to zero. 
            float pageWidth = mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT)
                    .width(); 
            double curlLen = radius * Math.PI; 
            if (dist > (pageWidth * 2) - curlLen) {
                curlLen = Math.max((pageWidth * 2) - dist, 0f);
                radius = curlLen / Math.PI; 
            } 

            // Actual curl position calculation. 
            if (dist >= curlLen) { 
                double translate = (dist - curlLen) / 2;
                if (mViewMode == SHOW_TWO_PAGES) { 
                    mCurlPos.x -= mCurlDir.x * translate / dist; 
                } else { 
                    float pageLeftX = mRenderer.getPageRect(CurlRenderer.PAGE_LEFT).left;
                    radius = Math.max(Math.min(mCurlPos.x - pageLeftX, radius), 0f);
                } 
                mCurlPos.y -= mCurlDir.y * translate / dist; 
            } else { 
                double angle = Math.PI * Math.sqrt(dist / curlLen);
                double translate = radius * Math.sin(angle); 
                mCurlPos.x += mCurlDir.x * translate / dist; 
                mCurlPos.y += mCurlDir.y * translate / dist; 
            } 
        } 
        // Otherwise we'll let curl follow pointer position. 
        else if (mCurlState == CURL_RIGHT) {

            // Adjust radius regarding how close to page edge we are. 
            float pageLeftX = mRenderer.getPageRect(CurlRenderer.PAGE_LEFT).left;
            radius = Math.max(Math.min(mCurlPos.x - pageLeftX, radius), 0f);

            float pageRightX = mRenderer.getPageRect(CurlRenderer.PAGE_LEFT).right;
            mCurlPos.x -= Math.min(pageRightX - mCurlPos.x, radius); 
            mCurlDir.x -= mCurlPos.x - mDragStartPos.x; 
            mCurlDir.y = mCurlPos.y - mDragStartPos.y; 
        } 

        setCurlPos(mCurlPos, mCurlDir, radius); 
    } 

    /** 
     * Updates given CurlPage via PageProvider for page located at index. 
     */ 
    private void updatePage(CurlPage page, int index) {
        // First reset page to initial state. 
        page.reset(); 
        // Ask page provider to fill it up with bitmaps and colors. 
        mPageProvider.updatePage(page, mPageBitmapWidth, mPageBitmapHeight, 
                index); 
    } 

    /** 
     * Updates bitmaps for page meshes. 
     */ 
    private void updatePages() {
        if (mPageProvider == null || mPageBitmapWidth <= 0
                || mPageBitmapHeight <= 0) { 
            return; 
        } 

        // Remove meshes from renderer. 
        mRenderer.removeCurlMesh(mPageLeft); 
        mRenderer.removeCurlMesh(mPageRight); 
        mRenderer.removeCurlMesh(mPageCurl); 

        int leftIdx = mCurrentIndex - 1;
        int rightIdx = mCurrentIndex; 
        int curlIdx = -1;
        if (mCurlState == CURL_LEFT) { 
            curlIdx = leftIdx; 
            --leftIdx; 
        } else if (mCurlState == CURL_RIGHT) {
            curlIdx = rightIdx; 
            ++rightIdx; 
        } 

        if (rightIdx >= 0 && rightIdx < mPageProvider.getPageCount()) {
            updatePage(mPageRight.getTexturePage(), rightIdx); 
            mPageRight.setFlipTexture(false); 
            mPageRight.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_RIGHT)); 
            mPageRight.reset(); 
            mRenderer.addCurlMesh(mPageRight); 
        } 
        if (leftIdx >= 0 && leftIdx < mPageProvider.getPageCount()) {
            updatePage(mPageLeft.getTexturePage(), leftIdx); 
            mPageLeft.setFlipTexture(true); 
            mPageLeft.setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); 
            mPageLeft.reset(); 
            if (mRenderLeftPage) { 
                mRenderer.addCurlMesh(mPageLeft); 
            } 
        } 
        if (curlIdx >= 0 && curlIdx < mPageProvider.getPageCount()) {
            updatePage(mPageCurl.getTexturePage(), curlIdx); 

            if (mCurlState == CURL_RIGHT) { 
                mPageCurl.setFlipTexture(true); 
                mPageCurl.setRect(mRenderer 
                        .getPageRect(CurlRenderer.PAGE_RIGHT)); 
            } else { 
                mPageCurl.setFlipTexture(false); 
                mPageCurl 
                        .setRect(mRenderer.getPageRect(CurlRenderer.PAGE_LEFT)); 
            } 

            mPageCurl.reset(); 
            mRenderer.addCurlMesh(mPageCurl); 
        } 
    } 

    /** 
     * Provider for feeding 'book' with bitmaps which are used for rendering 
     * pages. 
     */ 
    public interface PageProvider {

        /** 
         * Return number of pages available. 
         */ 
        public int getPageCount();

        /** 
         * Called once new bitmaps/textures are needed. Width and height are in 
         * pixels telling the size it will be drawn on screen and following them 
         * ensures that aspect ratio remains. But it's possible to return bitmap 
         * of any size though. You should use provided CurlPage for storing page 
         * information for requested page number.<br/> 
         * <br/> 
         * Index is a number between 0 and getBitmapCount() - 1. 
         */ 
        public void updatePage(CurlPage page, int width, int height, int index);
    } 

    /** 
     * Simple holder for pointer position. 
     */ 
    private class PointerPosition {
        PointF mPos = new PointF(); 
        float mPressure; 
    } 

    /** 
     * Observer interface for handling CurlView size changes. 
     */ 
    public interface SizeChangedObserver {

        /** 
         * Called once CurlView size changes. 
         */ 
        public void onSizeChanged(int width, int height);
    } 

}