Blackberry Custom Manager

Posted: 17 Agustus 2011 in Mobile, Programming
Tag:,

Dalam merancang antarmuka aplikasi Blackberry, terdapat berbagai komponen bawaan dari RIM yang dipakai dimana pada dasarnya merupakan turunan dari kelas Field. Kelas-kelas turunan dari kelas Field ini secara umum dibagi ke dalam 2 kelompok yaitu UI Component (turunan langsung dari kelas Field) yang berinteraksi langsung (input / output) dengan pengguna aplikasi serta UI Manager (turunan dari kelas Manager dimana kelas Manager merupakan turunan langsung dari kelas Field) yang mengatur posisi dari UI Component maupun UI Manager (nested manager) pada layar perangkat Blackberry. Jika dibandingkan dengan perancangan UI pada halaman web (menggunakan HTML/CSS) maka tag-tag seperti table, span maupun div termasuk ke dalam UI Manager sedangkan komponen-komponen form (seperti tag input), tag img dan sebagainya termasuk ke dalam UI Component.

Akan tetapi, UI Component dan UI Manager bawaan dari RIM kebanyakan tidak mengakomodasi kebutuhan-kebutuhan dari developer aplikasi BB. Untuk mengatasi ini maka kebanyakan para developer mencoba membuat Custom Field maupun Custom Manager sendiri untuk mengakomodasi kebutuhan aplikasi dengan meng-extend kelaskelas pada UI Component, UI Manager ataupun kelas primitif Field maupun kelas primitif Manager. 

Pada artikel ini, penulis akan berbagi 2 buah contoh custom manager yaitu FlexibleHorizontalFieldManager dan FlexibleVerticalFieldManager yang secara berurutan merupakan turunan dari kelas HorizontalFieldManager dan VerticalFieldManager. Adapun masing-masing custom manager ini memperluas kemampuan dari parent manager dimana mencakup constructor, layout dan navigation. Perluasan behavior dari custom manager ini meliputi  :

  • Dapat memiliki width maupun height yang fleksibel (wrap content) sesuai dengan width terbesar dari komponen-komponen UI yang dikelolanya (untuk vertical manager) ataupun height terbesar dari komponen-komponen UI yang dikelola (untuk horizontal manager). Selain itu dapat memiliki width maupun height yang fix ataupun memenuhi dimensi layar dari perangkat BB.
  • Dapat menempatkan komponen-komponen UI yang dikelolanya secara evenly spaced – width (untuk horizontal manager) maupun evenly spaced – height (untuk vertical manager). Biasanya dibutuhkan untuk layouting toolbar dan kebutuhan lain.
  • Width maupun height dapat menyesuaikan bila komponen/manager sebelumnya maupun sesudahnya (pada parent layout) memiliki width dan height yang fix. Behaviour ini biasanya dibutuhkan untuk mengakomodasi layouting pada perangkat BB yang memiliki multiple orientation screen sehingga constraint layout dapat dijaga.
  • Behaviour tambahan seperti dapat berubah warna/gambar background ketika menerima fokus serta dapat menerima input click / touch. Dengan adanya behaviour tambahan ini maka dapat dijadikan sebagai alternatif lain dari ObjectListField dimana setiap list row memiliki komponen UI yang kompleks (baik dari segi layout maupun multiple event listener / 2 atau lebih komponen memiliki event listener masing-masing).
Adapun kode sumber untuk FlexibleHorizontalFieldManager adalah sebagai berikut :
/**
 * Class  : FlexibleHorizontalFieldManager.java
 * Author : Joel / 144key (joel@hutasoit.net) (2010)
 **/

package org.javan.bb.customui;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.container.AbsoluteFieldManager;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.decor.BackgroundFactory;

/**
 * Extended Class of HorizontalFieldManager that can handle several extended behaviour
 * @author joel
 */
public class FlexibleHorizontalFieldManager extends HorizontalFieldManager {
    /** Extended Attributes **/
        public static final int CLICKNOTIFY = 144;
        public static int FILL_PARENT = -1; // Use maximum width/height that provided by parent manager
        public static int WRAP_CONTENT = -2; // Use maximum/total width/height of the field child
        public static int USE_FIXED_HEADTAIL = -3; // Like fill_parent but substracted with sum of heading/trailing width/height
        public static int NO_COLOR = -4; // Constant for no color initiation
        private int fixedHeadWidth = 0; // Heading Width
        private int fixedHeadHeight = 0; // Heading Height
        private int fixedTailWidth = 0; // Trailing Width
        private int fixedTailHeight = 0; // Trailing Height
        private int flexibleWidth = FILL_PARENT; // Manager Width, default FILL_PARENT
        private int flexibleHeight = FILL_PARENT; // Manager Height, default FILL_PARENT
        private int hoverColor = NO_COLOR; // default NO_HOVER_COLOR to be painted
        private int baseColor = NO_COLOR; // default NO_BASE_COLOR to be painted
        private Bitmap bgImage = null; // default NO_BACKGROUND_IMAGE
        private Bitmap bgHoverImage = null; // default NO_BACKGROUND_HOVER_IMAGE
        private boolean distributedWidth = false; // default FIELD_WIDTH_NOT_DISTRIBUTED
        private boolean enableClick = false; // default NON_CLICKABLE
    /** Extended Methods **/
    public FlexibleHorizontalFieldManager() {
        super(Manager.USE_ALL_WIDTH | Manager.USE_ALL_HEIGHT);
    }
    public FlexibleHorizontalFieldManager(long style) {
        super(Manager.USE_ALL_WIDTH | Manager.USE_ALL_HEIGHT | style);
    }
    public FlexibleHorizontalFieldManager(int w, int h) {
        super((w == FILL_PARENT ? Manager.USE_ALL_WIDTH : ((long) 0)) | (h == FILL_PARENT ? Manager.USE_ALL_HEIGHT : ((long) 0)));
        flexibleWidth = w;
        flexibleHeight = h;
    }
    public FlexibleHorizontalFieldManager(int w, int h, long style) {
        super((w == FILL_PARENT ? Manager.USE_ALL_WIDTH : ((long) 0)) | (h == FILL_PARENT ? Manager.USE_ALL_HEIGHT : ((long) 0)) | style);
        flexibleWidth = w;
        flexibleHeight = h;
    }
    public void setHeadAndTailArea(int _fixedHeadWidth, int _fixedHeadHeight, int _fixedTailWidth, int _fixedTailHeight) {
        fixedHeadWidth = _fixedHeadWidth;
        fixedHeadHeight = _fixedHeadHeight;
        fixedTailWidth = _fixedTailWidth;
        fixedTailHeight = _fixedTailHeight;
    }
    public void setEnableClick(boolean _enableClick) {
        this.enableClick = _enableClick;
    }
    public boolean isAttached() {
        Manager m = getManager();
        return (m != null && m.isValidLayout() && m.isVisible() && isValidLayout() && isVisible());
    }
    public boolean isFocusable() {
        if (isAttached()) {
            return (enableClick ? enableClick : super.isFocusable());
        } else return super.isFocusable();
    }
    protected boolean touchEvent(TouchEvent arg0) {
        if (enableClick) {
            if (arg0.getEvent() == TouchEvent.CLICK) {
                fieldChangeNotify(CLICKNOTIFY);
                return true;
            }
        }
        return super.touchEvent(arg0);
    }
    protected boolean navigationClick(int i, int i1) {
        if (enableClick) {
            fieldChangeNotify(CLICKNOTIFY);
            return true;
        } else return super.navigationClick(i, i1);
    }
    protected boolean keyChar(char c, int i, int i1) {
        if (enableClick && c == Characters.ENTER) {
            fieldChangeNotify(CLICKNOTIFY);
            return true;
        } else return super.keyChar(c, i, i1);
    }
    public void setWitdh(int w) {
        flexibleWidth = w;
    }
    public void setHeight(int h) {
        flexibleHeight = h;
    }
    public void setDimension(int w, int h) {
        flexibleWidth = w;
        flexibleHeight = h;
    }
    public void setDistributedWidth(boolean _newValue) {
        distributedWidth = _newValue;
    }
    public int getPreferredWidth() {
        if (flexibleWidth >= 0) {
            return flexibleWidth;
        } else if (flexibleWidth == USE_FIXED_HEADTAIL) {
            return (Display.getWidth() - fixedHeadWidth - fixedTailWidth);
        } else if (flexibleWidth == WRAP_CONTENT) {
            int totalWidth = 0;
            int numFields = getFieldCount();
            for (int i = 0; i < numFields; i++) {                 Field field = getField(i);                 totalWidth += field.getExtent().width+field.getMarginLeft()+field.getMarginRight();             }             return totalWidth;         } else {             // IF FILL_PARENT             if (getManager() != null && getManager().getClass().equals(AbsoluteFieldManager.class))                 // handle for absolutefieldmanager parent                 return Display.getWidth();              else                 return super.getPreferredWidth();         }     }     public int getPreferredHeight() {         if (flexibleHeight >= 0) {
            return flexibleHeight;
        } else if (flexibleHeight == WRAP_CONTENT) {
            int totalHeight = 0;
            int numFields = getFieldCount();
            for (int i = 0; i < numFields; i++) {                 Field field = getField(i);                 int tmpHeight = field.getExtent().height+field.getMarginTop()+field.getMarginBottom();                 if (tmpHeight > totalHeight) totalHeight = tmpHeight;
            }
            return totalHeight;
        } else if (flexibleHeight == USE_FIXED_HEADTAIL) {
            return (Display.getHeight() - fixedHeadHeight - fixedTailHeight);
        } else {
            // IF FILL_PARENT
            if (getManager() != null && getManager().getClass().equals(AbsoluteFieldManager.class))
                // handle for absolutefieldmanager parent
                return Display.getHeight();
            else
                return super.getPreferredHeight();
        }
    }
    protected void sublayout(int maxWidth, int maxHeight) {
        int totalWidth  = 0;
        int totalHeight = 0;
        if (flexibleWidth == WRAP_CONTENT || flexibleHeight == WRAP_CONTENT) {
            int numFields = getFieldCount();
            int availableWidth = maxWidth;
            int availableHeight = maxHeight;
            for (int i = 0; i < numFields; i++) {                 Field field = getField(i);                 layoutChild(field, availableWidth, maxHeight);                  availableWidth -= field.getExtent().width-field.getMarginLeft()-field.getMarginRight();                 availableHeight  = field.getExtent().height+field.getMarginTop()+field.getMarginBottom();                 totalWidth += field.getExtent().width+field.getMarginLeft()+field.getMarginRight();                 if (availableHeight > totalHeight && availableHeight < maxHeight) totalHeight = availableHeight;
            }
        }
        if (distributedWidth)
        // DistributedWidthHorizontalFieldManager
        {
            super.sublayout(maxWidth, maxHeight);
            int nbFieldForCalculation = ((getFieldCount() != 0) ? getFieldCount() : 1);
            int intervalPosition = (Display.getWidth() / nbFieldForCalculation);
            for (int i = 0; i < getFieldCount(); i++) {
                layoutChild(getField(i), maxWidth, maxHeight);
                setPositionChild(getField(i), (intervalPosition * i), getPaddingTop());
            }
        }
        if (!distributedWidth) super.sublayout((flexibleWidth == FILL_PARENT ? maxWidth : (flexibleWidth == WRAP_CONTENT ? totalWidth : (flexibleWidth == USE_FIXED_HEADTAIL ? (Display.getWidth() - fixedHeadWidth - fixedTailWidth) : flexibleWidth))),
                        (flexibleHeight == FILL_PARENT ? maxHeight : (flexibleHeight == WRAP_CONTENT ? totalHeight : (flexibleHeight == USE_FIXED_HEADTAIL ? (Display.getHeight() - fixedHeadHeight - fixedTailHeight) : flexibleHeight))));
        setExtent(distributedWidth ? Display.getWidth() : (flexibleWidth == FILL_PARENT ? maxWidth : (flexibleWidth == WRAP_CONTENT ? totalWidth : (flexibleWidth == USE_FIXED_HEADTAIL ? (Display.getWidth() - fixedHeadWidth - fixedTailWidth) : flexibleWidth))),
                  (flexibleHeight == FILL_PARENT ? maxHeight : (flexibleHeight == WRAP_CONTENT ? totalHeight : (flexibleHeight == USE_FIXED_HEADTAIL ? (Display.getHeight() - fixedHeadHeight - fixedTailHeight) : flexibleHeight))));
    }
    public void setBaseColor(int baseColor) {
        this.baseColor = baseColor;
        setBackground(BackgroundFactory.createSolidBackground(baseColor));
    }
    public void setHoverColor(int _color) {
        hoverColor = _color;
    }
    public void setBgImages(Bitmap img, Bitmap imgHover) {
        bgImage = img;
        bgHoverImage = imgHover;
        setBackground(BackgroundFactory.createBitmapBackground(bgImage));
    }
    protected void onFocus(int direction) {
        if (bgHoverImage != null) setBackground(BackgroundFactory.createBitmapBackground(bgHoverImage));
        else if (baseColor != NO_COLOR) setBackground(BackgroundFactory.createSolidBackground(hoverColor));
        else if (hoverColor != NO_COLOR) setBackground(BackgroundFactory.createSolidTransparentBackground(hoverColor, 192));
        super.onFocus(direction);
    }
    protected void onUnfocus() {
        if (bgImage != null) setBackground(BackgroundFactory.createBitmapBackground(bgImage));
        else if (baseColor != NO_COLOR) setBackground(BackgroundFactory.createSolidBackground(baseColor));
        else if (hoverColor != NO_COLOR) setBackground(BackgroundFactory.createSolidTransparentBackground(hoverColor, 0));
        super.onUnfocus();
    }
}
Sedangkan kode sumber untuk FlexibleVerticalFieldManager adalah sebagai berikut :
/**
 * Class  : FlexibleVerticalFieldManager.java
 * Author : Joel / 144key (joel@hutasoit.net) (2010)
 **/

package org.javan.bb.customui;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.container.AbsoluteFieldManager;
import net.rim.device.api.ui.container.VerticalFieldManager;
import net.rim.device.api.ui.decor.BackgroundFactory;

/**
 * Extended Class of VerticalFieldManager that can handle several extended behaviour
 * @author joel
 */
public class FlexibleVerticalFieldManager extends VerticalFieldManager {
    /** Extended Attributes **/
        public static final int CLICKNOTIFY = 144;
        public static int FILL_PARENT = -1; // Use maximum width/height that provided by parent manager
        public static int WRAP_CONTENT = -2; // Use maximum/total width/height of the field child
        public static int USE_FIXED_HEADTAIL = -3; // Like fill_parent but substracted with sum of heading/trailing width/height
        public static int NO_COLOR = -4; // Constant for no color initiation
        private int fixedHeadWidth = 0; // Heading Width
        private int fixedHeadHeight = 0; // Heading Height
        private int fixedTailWidth = 0; // Trailing Width
        private int fixedTailHeight = 0; // Trailing Height
        private int flexibleWidth = FILL_PARENT; // Manager Width, default FILL_PARENT
        private int flexibleHeight = FILL_PARENT; // Manager Height, default FILL_PARENT
        private int hoverColor = NO_COLOR; // default NO_HOVER_COLOR to be painted
        private int baseColor = NO_COLOR; // default NO_BASE_COLOR to be painted
        private Bitmap bgImage = null; // default NO_BACKGROUND_IMAGE
        private Bitmap bgHoverImage = null; // default NO_BACKGROUND_HOVER_IMAGE
        private boolean distributedHeight = false; // default FIELD_WIDTH_NOT_DISTRIBUTED
        private boolean enableClick = false; // default NON_CLICKABLE
    /** Extended Methods **/
    public FlexibleVerticalFieldManager() {
        super(Manager.USE_ALL_WIDTH | Manager.USE_ALL_HEIGHT);
    }
    public FlexibleVerticalFieldManager(long style) {
        super(Manager.USE_ALL_WIDTH | Manager.USE_ALL_HEIGHT | style);
    }
    public FlexibleVerticalFieldManager(int w, int h) {
        super((w == FILL_PARENT ? Manager.USE_ALL_WIDTH : ((long) 0)) | (h == FILL_PARENT ? Manager.USE_ALL_HEIGHT : ((long) 0)));
        flexibleWidth = w;
        flexibleHeight = h;
    }
    public FlexibleVerticalFieldManager(int w, int h, long style) {
        super((w == FILL_PARENT ? Manager.USE_ALL_WIDTH : ((long) 0)) | (h == FILL_PARENT ? Manager.USE_ALL_HEIGHT : ((long) 0)) | style);
        flexibleWidth = w;
        flexibleHeight = h;
    }
    public void setHeadAndTailArea(int _fixedHeadWidth, int _fixedHeadHeight, int _fixedTailWidth, int _fixedTailHeight) {
        fixedHeadWidth = _fixedHeadWidth;
        fixedHeadHeight = _fixedHeadHeight;
        fixedTailWidth = _fixedTailWidth;
        fixedTailHeight = _fixedTailHeight;
    }
    public void setEnableClick(boolean _enableClick) {
        this.enableClick = _enableClick;
    }
    public boolean isAttached() {
        Manager m = getManager();
        return (m != null && m.isValidLayout() && m.isVisible() && isValidLayout() && isVisible());
    }
    public boolean isFocusable() {
        if (isAttached()) {
            return (enableClick ? enableClick : super.isFocusable());
        } else return super.isFocusable();
    }
    protected boolean touchEvent(TouchEvent arg0) {
        if (enableClick) {
            if (arg0.getEvent() == TouchEvent.CLICK) {
                fieldChangeNotify(CLICKNOTIFY);
                return true;
            }
        }
        return super.touchEvent(arg0);
    }
    protected boolean navigationClick(int i, int i1) {
        if (enableClick) {
            fieldChangeNotify(CLICKNOTIFY);
            return true;
        } else return super.navigationClick(i, i1);
    }
    protected boolean keyChar(char c, int i, int i1) {
        if (enableClick && c == Characters.ENTER) {
            fieldChangeNotify(CLICKNOTIFY);
            return true;
        } else return super.keyChar(c, i, i1);
    }
    public void setWitdh(int w) {
        flexibleWidth = w;
    }
    public void setHeight(int h) {
        flexibleHeight = h;
    }
    public void setDimension(int w, int h) {
        flexibleWidth = w;
        flexibleHeight = h;
    }
    public void setDistributedHeight(boolean _newValue) {
        distributedHeight = _newValue;
    }
    public int getPreferredWidth() {
        if (flexibleWidth >= 0) {
            return flexibleWidth;
        } else if (flexibleWidth == USE_FIXED_HEADTAIL) {
            return (Display.getWidth() - fixedHeadWidth - fixedTailWidth );
        } else if (flexibleWidth == WRAP_CONTENT) {
            int totalWidth = 0;
            int numFields = getFieldCount();
            for (int i = 0; i < numFields; i++) {                 Field field = getField(i);                 int tmpWidth = field.getExtent().width+field.getMarginLeft()+field.getMarginRight();                 if (tmpWidth > totalWidth) totalWidth = tmpWidth;
            }
            return totalWidth;
        } else {
            // IF FILL_PARENT
            if (getManager() != null && getManager().getClass().equals(AbsoluteFieldManager.class))
                // handle for absolutefieldmanager parent
                return Display.getWidth();
            else
                return super.getPreferredWidth();
        }
    }
    public int getPreferredHeight() {
        if (flexibleHeight >= 0) {
            return flexibleHeight;
        } else if (flexibleHeight == USE_FIXED_HEADTAIL) {
            return (Display.getHeight() - fixedHeadHeight - fixedTailHeight);
        } else if (flexibleHeight == WRAP_CONTENT) {
            int totalHeight = 0;
            int numFields = getFieldCount();
            for (int i = 0; i < numFields; i++) {
                Field field = getField(i);
                totalHeight += field.getExtent().height+field.getMarginTop()+field.getMarginBottom();
            }
            return totalHeight;
        } else {
            // IF FILL_PARENT
            if (getManager() != null && getManager().getClass().equals(AbsoluteFieldManager.class))
                // handle for absolutefieldmanager parent
                return Display.getHeight();
            else
                return super.getPreferredHeight();
        }
    }
    protected void sublayout(int maxWidth, int maxHeight) {
        int totalWidth  = 0;
        int totalHeight = 0;
        if (flexibleWidth == WRAP_CONTENT || flexibleHeight == WRAP_CONTENT) {
            int numFields = getFieldCount();
            int availableWidth = maxWidth;
            int availableHeight = maxHeight;
            for (int i = 0; i < numFields; i++) {                 Field field = getField(i);                 layoutChild(field, maxWidth, availableHeight);                                 availableWidth = field.getExtent().width+field.getMarginLeft()+field.getMarginRight();                 availableHeight -= field.getExtent().height-field.getMarginTop()-field.getMarginBottom();                 if (availableWidth > totalWidth && availableWidth < maxWidth) totalWidth = availableWidth;
                totalHeight += field.getExtent().height+field.getMarginTop()+field.getMarginBottom();
            }
        }
        if (distributedHeight)
        // DistributedHeightVerticalFieldManager
        {
            super.sublayout(maxWidth, maxHeight);
            int nbFieldForCalculation = ((getFieldCount() != 0) ? getFieldCount() : 1);
            int intervalPosition = (Display.getHeight() / nbFieldForCalculation);
            for (int i = 0; i < getFieldCount(); i++) {
                layoutChild(getField(i), maxWidth, maxHeight);
                setPositionChild(getField(i), getPaddingLeft(), (intervalPosition * i));
            }
        }
        if (!distributedHeight) super.sublayout((flexibleWidth == FILL_PARENT ? maxWidth : (flexibleWidth == WRAP_CONTENT ? totalWidth : (flexibleWidth == USE_FIXED_HEADTAIL ? (Display.getWidth() - fixedHeadWidth - fixedTailWidth) : flexibleWidth))),
                        (flexibleHeight == FILL_PARENT ? maxHeight : (flexibleHeight == WRAP_CONTENT ? totalHeight : (flexibleHeight == USE_FIXED_HEADTAIL ? (Display.getHeight() - fixedHeadHeight - fixedTailHeight) : flexibleHeight))));
        setExtent((flexibleWidth == FILL_PARENT ? maxWidth : (flexibleWidth == WRAP_CONTENT ? totalWidth : (flexibleWidth == USE_FIXED_HEADTAIL ? (Display.getWidth() - fixedHeadWidth - fixedTailWidth) : flexibleWidth))),
                  distributedHeight ? Display.getHeight() : (flexibleHeight == FILL_PARENT ? maxHeight : (flexibleHeight == WRAP_CONTENT ? totalHeight : (flexibleHeight == USE_FIXED_HEADTAIL ? (Display.getHeight() - fixedHeadHeight - fixedTailHeight) : flexibleHeight))));
    }
    public void setBaseColor(int baseColor) {
        this.baseColor = baseColor;
        setBackground(BackgroundFactory.createSolidBackground(baseColor));
    }
    public void setHoverColor(int _color) {
        hoverColor = _color;
    }
    public void setBgImages(Bitmap img, Bitmap imgHover) {
        bgImage = img;
        bgHoverImage = imgHover;
        setBackground(BackgroundFactory.createBitmapBackground(bgImage));
    }
    protected void onFocus(int direction) {
        if (bgHoverImage != null) setBackground(BackgroundFactory.createBitmapBackground(bgHoverImage));
        else if (baseColor != NO_COLOR) setBackground(BackgroundFactory.createSolidBackground(hoverColor));
        else if (hoverColor != NO_COLOR) setBackground(BackgroundFactory.createSolidTransparentBackground(hoverColor, 192));
        super.onFocus(direction);
    }
    protected void onUnfocus() {
        if (bgImage != null) setBackground(BackgroundFactory.createBitmapBackground(bgImage));
        else if (baseColor != NO_COLOR) setBackground(BackgroundFactory.createSolidBackground(baseColor));
        else if (hoverColor != NO_COLOR) setBackground(BackgroundFactory.createSolidTransparentBackground(hoverColor, 0));
        super.onUnfocus();
    }
}
Adapun custom manager di atas masih dapat diperluas kemampuannya untuk berbagai keperluan. Selamat memakai dan mengembangkan bagi yang memerlukan, semoga bermanfaat😀
Komentar
  1. setiawand81 mengatakan:

    Trims ya, aku cobain code nya🙂

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s