Setup Titanium Studio environment for developing Titanium module (Android) on Windows

Using Titanium Studio 2.1.2.201208301612 and Titanium Mobile 2.1.4.GA

  1. Install Android SDK on path without space (C:\Android\android-sdk)
  2. Install SDK platform AND Google API – 2.2 to latest (4.2)
  3. Install Android NDK (C:\Android\android-ndk-r8b)
  4. Add titanium.py to PATH
    C:\Users\Username\AppData\Roaming\Titanium\mobilesdk\win32\2.1.4.GA
  5. Add python to PATH (if you don’t have python installed):
    C:\Users\Username\AppData\Local\Titanium Studio\plugins\com.appcelerator.titanium.python.win32_1.0.0.1338515509\python

    (Browse into the folder if want to know exact foldername)

  6. Install Gperf and add to PATH
    C:\Program Files\Gnu\Win32\bin

Create new module

  1. Open Titanium Studio
  2. Right click Project Explorer > New > Titanium Mobile Module Project
  3. You should be able to select Android as platform
  4. Continue until finish creating
  5. Edit build.properties, add
    android.ndk=C:\Android\android-ndk-r8b

Build & package module

  1. Right click build.xml > Run As > Ant Build
  2. Packaged module zip file is in dist folder

Designing web page for Android webview

Android screen built-in resolutions (based on API 8-16):

Skin Resolution (w x h pixel) Orientation
QVGA 240 x 320 Portrait
WQVGA400 240 x 400 Portrait
WQVGA432 240 x 432 Portrait
HVGA 320 x 480 Portrait
WVGA800 480 x 800 Portrait
WVGA854 480 x 854 Portrait
WSVGA 1024 x 600 Landscape
WXGA720 1280 x 720 Landscape
WXGA, WXGA800, WXGA800-7in 1280 x 800 Landscape

Android screen density

Density DPI / Pixel ratio Skin
ldpi 120 / 0.75 QVGA, WQVGA400, WQVGA432
mdpi 160 / 1.0 HVGA, WSVGA, WXGA
tvdpi 213 / 1.33125 WVGA800-7in
hdpi 240 / 1.5 WVGA800, WVGA854
xhdpi 320 / 2.0 WXGA720, WXGA800

Android webview viewport size (100% width and height)

Skin Viewport (w x h pixel, portrait)
QVGA 320 x 427
WQVGA400 320 x 533
WQVGA432 320 x 576
HVGA 320 x 480
WVGA800 320 x 533
WVGA854 320 x 569
WSVGA 600 x 1024
WXGA720 360 x 598
WXGA 800 x 1280
WXGA800 400 x 598
WXGA800-7in 601 x 962

In the HTML page, make sure we lock the webpage from being resize/zoom

<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1" />

To design responsive web page, pay attention to webview viewport size in writing the css media query. From the viewport size table above, if we’re designing for portrait orientation only, we have a set of widths: 320, 360, 400, 600, 800 (601 can use 600 width styles). So our css code must be organized like this:

/* width: 800 */
.container { width: 800px; }

/* width: 600 */
@media (max-width: 799px) {
    .container { width: 600px; }
}

/* width: 400 */
@media (max-width: 599px) {
    .container { width: 400px; }
}

/* width: 360 */
@media (max-width: 399px) {
    .container { width: 360px; }
}

/* width: 320 */
@media (max-width: 359px) {
    .container { width: 320px; }
}

Note: Since landscape width got more, choose widths that can be grouped together, e.g: 420, 480, 530, 960, 1024, 1280

In handling different screen density using css, it’s recommended to display images as background of html elements.
HTML:

<i class="icon ok"></i>

CSS:

.icon {
    width: 50px;
    height: 50px;
    display: block;
    background-size: 50px 50px;
}
.icon.ok {
    background-image: url(img/mdpi/icon_ok.png);
}

That’s for the base density (mdpi). For other densities:

/* density: xhdpi */
@media screen and (-webkit-device-pixel-ratio: 2.0) {
    .icon.ok { background-image: url(img/xhdpi/icon_ok.png); }
}

/* density: hdpi */
@media screen and (-webkit-device-pixel-ratio: 1.5) {
    .icon.ok { background-image: url(img/hdpi/icon_ok.png); }
}

/* density: ldpi */
@media screen and (-webkit-device-pixel-ratio: 0.75) {
    .icon.ok { background-image: url(img/ldpi/icon_ok.png); }
}

Note: for tvdpi, it’ll use hdpi stylesheet & resize accordingly.

Image sizes. To know what image size you need, use pixel ratio to resize the image (base design is using 1.0 pixel ratio). Image DPI (dot per inch) is 72. E.g:

Image file Density / Pixel ratio Size
img/ldpi/icon_ok.png ldpi / 0.75 50 x 0.75 = 38px
img/mdpi/icon_ok.png mdpi / 1.0 50 x 1.0 = 50px
img/hdpi/icon_ok.png hdpi / 1.5 50 x 1.5 = 75px
img/xhdpi/icon_ok.png xhdpi / 2.0 50 x 2.0 = 100px

Git basic

I’m a noob in using Git, and I’m using Github to host some of the code I wrote. Here are some basic commands to host a project with Github (or using Git revision control generally) (sometimes I forgot the command or the order of which command goes first)

Global setup:

Set up git

git config --global user.name "Your Name"
git config --global user.email [email protected]

Next steps:

mkdir project-name
cd project-name
git init
touch README
git add README
git commit -m 'first commit'
git remote add origin [email protected]:username/project-name.git
git push -u origin master

Existing Git Repo?

cd existing_git_repo
git remote add origin [email protected]:username/project-name.git
git push -u origin master

To commit changes (and optionally push changes to Github)

git add .
git commit -m 'commit message'
git push -u origin master

Common SVN users may find it confusing when using Git. Here’s some of the tips:

  • There are no centralized server or repo. You are the server / repo
  • In SVN, what’s in your computer is working copy and the center for committing changes is the repository, In Git, your working copy is your repository.
  • In SVN, project members checked out a working copy and commit changes to a centralized repository. In Git, everyone in a project can be the repository, can checkout (pull) and commit (push) changes to each other
    (Note: In Git, ‘checkout’ is called `pull`, ‘commit’ is saving changes to your own repository, ‘push’ is sending and merging changes to remote repository

Cloudflare & phpBB3

Session handling in phpBB3 requires a user to use one IP address per session, therefore if your IP address changed, you will be prompt to login again.

Cloudflare is a web caching service that cache static contents of a website by acting as the nameserver that manage the website domain name. Therefore, every request to the domain name will pass through Cloudflare system first and any cached contents will be served from Cloudflare servers instead of the web hosting server.

However, when using Cloudflare service, the REMOTE_ADDR value for PHP $_SERVER will follow Cloudflare servers IP address. Therefore, you might face problem when using phpBB (or any other web app that rely on session tied to the IP address) with this service.

So, we need to edit phpBB session.php file to grab the correct variable for the users’ real IP address. Edit includes/session.php

Find:

$this->ip = (!empty($_SERVER['REMOTE_ADDR'])) ? (string) $_SERVER['REMOTE_ADDR'] : '';

Replace with:

$this->ip = (!empty($_SERVER['HTTP_CF_CONNECTING_IP']))
          ? (string) $_SERVER['HTTP_CF_CONNECTING_IP']
          : ((!empty($_SERVER['REMOTE_ADDR'])) ? (string) $_SERVER['REMOTE_ADDR'] : '');

HTTP_CF_CONNECTING_IP is special header value passed by Cloudflare servers containing visitors’ real IP address, and in case this variable not set, we use the good ol REMOTE_ADDR

phpBB SEO Ultimate URL backslash in URL

In case someone stumbled upon this problem:

1. You download and install the new release of phpBB (3.0.9) and immediately integrate SEO Ultimate URL package
2. The setup went well except you found out that the page is looked ugly because the CSS wasn’t loaded – cause by the URL got backslash in it:

http://localhost//style.php …

To fix, you need to edit the data in the config table:
1. Access the phpBB3 config table (phpbb_config)
2. Lookup for config_name = ‘script_path’
3. You’ll see the value is ‘/’. Change this to ‘/’
4. Save the data

Clear the cache, and reload the page. You should see the pretty page back again!

Additional: I’m using localhost with custom port number (82) as testing platform. So in case you’re not getting the image out on the page (caused by the URL generated become http://localhost/style… instead of http://localhost:82/style…, so you need to edit the force server URL settings

1. Go to admin control panel, General > Server Settings
2. Set ‘Force Server URL’ to ‘Yes’

PHP Data Validation

WordPress data validation has some good reference in doing data validation, especially the philosophy part. For best practice, always use format correction first, then use whitelist.

// format correction
$action = (int) $_GET['action'];
// whitelist
switch ($action) {
    case 1:
        do_this();
        break;
    case 2:
        do_that();
        break;
    case 0:
    default:
        die("Don't know this action!");
}

However the above example is only for one input ($_GET[‘action’]). What if your web app (or controller) need  a few inputs, and surely you don’t want to write the same code over again, like this

// correct the format
$id = (int) $_GET['id'];
$name = (string) trim($_POST['name']);
$email = (string) trim($_POST['email');
$about = (string) htmlspecialchars(trim($_POST['about']), ENT_QUOTES, 'UTF-8');
// then do validation
if ($id == 0) {
    $id = 1;
}
if (empty($name)) {
    $msg = 'Name is required';
}
...

Most PHP frameworks I know whether validate the input one by one or using just one input source (GET or POST or COOKIE). It’s better to use $_REQUEST, since some input can be passed in <form> or just query string. E.g. /blog/post/?id=10&do=edit, then inside the page got <input type="hidden" name="do" value="save" />, now you can use both input from $_REQUEST[‘do’], which determine what action need to be done

$do = (string) $_REQUEST['do'];
switch ($do) {
    case 'edit':
        get_post($id);
        break;
    case 'save':
        save_post($id);
        break;
    case 'view':
    default:
        view_post($id);
        break;
}

With that pattern, here comes PHP filter functions, filter_input_array(). It receive an array of arguments, and source type (GET/POST etc.) and validate each of the input. It will return false on failed validation. This function is really convenient when we need to validate a long list of inputs.

However there’s a drawback. Since all the validation can be put in one input key argument (‘input_name’ => array(…list of various validators…), it is difficult to provide accurate error message, of which validation that failed. Therefore we can recreate another filter_input_array(), and put in the previous pattern (format first, then whitelist) into it. So basically the validation function work like this:

$input = validate_input(array(
    'id' => array('filter' => 'int', 'options' => array(
        'range' => array('max' => 1000, 'msg' => 'Max. value for ID is 1000')
    )),
    'name' => array('filter' => 'string', 'options' => array(
        'required' => array('value' => true, 'msg' => 'This input is required'),
        'alphanumeric' => array('value' => true, 'msg' => 'Name need to be alphanumeric')
    ))
));
// now we can use $input
$input['id'];
$input['name'];
// to get error msg
$err_msg['id']; // which may contain the error msg of specific validator
$err_msg['name'];
// to check if the whole form passed the validator or not, simply:
if (empty($err_msg)) {
    // form is valid
}

Additional note: String input.

To sanitize the string input, actually trim() is enough, and can be directly stored in database. You only need to sanitize the value only when outputting it. When echo() the value to HTML, make sure to always encode it first:

<p class="comments"><?php echo htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); ?></p>

This not only can prevent XSS issue, but also display the correct Unicode character. Avoid htmlentities() as it may corrupt the Unicode characters