Mini project : Send SMS with Flex App and Google Calendar

หัวข้อนี้เราจะมาลองทำ Mini project โดยประยุกต์ใช้ Flex ร่วมกับเทคโนโลยีอื่น ซึ่งในที่นี้เราจะใช้ feature ของ Google calendar ที่สามารถส่ง SMS แจ้งเตือนกิจกรรมที่เราบันทึกไว้ได้ มาใช้ประโยชน์ โดยใช้ Library ใน Zend Frameworks ช่วยในการส่งข้อมูลจาก Flex app ไปยัง Google calendar อีกที

(ซึ่งจริงๆ ผมได้รับอนิสงฆ์จากคนที่มาเรียนแล้วเอาความรู้และการใช้งานของเทคโนโลยีตัวนี้มาให้ผมช่วยดูเพื่อเอาไปประยุกต์ใช้ ต้องขอขอบคุณมากๆ ครับ การถ่ายทอดอะไรให้กับคนอื่นก็สามารถทำให้เราได้รับความรู้ได้เช่นกัน)

ขั้นตอนหลักๆ มีดังนี้

1. Google calendar setting

2. Create PHP client access file

3. Create Flex web app

————————————-

1. Google calendar setting

ก่อนอื่นเราต้องมี gmail account ก่อน จากนั้นให้เข้าไปที่ Your_Gmail_Account > Account Settings ดังภาพ

จากนั้นเลือก My product > Calendar – Settings ดังภาพ

จะมาที่หน้า Calendar Settings ให้เลือก Mobile Setting จากนั้นตั้งค่าต่างๆ ดังนี้

Country : Thailand

Phone number : ใส่เบอร์โทรโดยใส่ระหัสประเทศนำหน้า เช่น เบอร์โทร 0879999999 ให้ใส่เป็น +66879999999

จากนั้นกดปุ่ม Send Verification Code ทาง Google ก็จะส่ง SMS เป็นหมายเลขสำหรับ Verify เพื่อรับข้อมูลการแจ้งเตือนจาก Google calendar

————————————-

2. Create PHP client access file

การติดต่อกับ Google Calendar เราจะใช้ Library ใน Zend Frameworks โดยสามารถ ​Download ได้ที่ http://framework.zend.com/download/latest

เมื่อเรา Download มาแล้ว Unzip แล้วนำ Zend Frameworks ไปวางไว้ที่

LocalServerApp
|-frameworks
|    |-Zend
|    |-…
|-your_web_root_directory_such_as_www_or_htdocs_or_public_html_etc

ตัวอย่างในรูป

จากนั้นสร้างไฟล์ PHP client access file ดังนี้

<?php

// Set up debug
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", "on");

// Set up include path for Zend Framework, this path is assuming a frameworks
// folder contains the Zend package on the same level as your public_html or www folder.
//ini_set("include_path", ini_get("include_path") . ":../frameworks");
//ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR ."../frameworks");
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . $_SERVER['DOCUMENT_ROOT'] . "/../frameworks");

require_once 'Zend/Loader.php';

Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_HttpClient');
Zend_Loader::loadClass('Zend_Gdata_Calendar');

function createEvent($client, $title = 'Title', $desc='Description content', $where = 'Somewhere',
        $startDateTime = '2010-01-20T00:00+07:00', $endDateTime = '2010-01-20T00:00+07:00')
{
    $gdataCal = new Zend_Gdata_Calendar($client);
    $newEvent = $gdataCal->newEventEntry();

    $newEvent->title = $gdataCal->newTitle($title);
    $newEvent->where = array($gdataCal->newWhere($where));
    $newEvent->content = $gdataCal->newContent("$desc");

    $when = $gdataCal->newWhen();
    $when->startTime = "{$startDateTime}";
    $when->endTime = "{$endDateTime}";

    $reminder = $gdataCal->newReminder();
    $reminder->method = "sms";
    $reminder->minutes = "10";

    $when->reminders = array($reminder);

    $newEvent->when = array($when);

    // Upload the event to the calendar server
    // A copy of the event as it is recorded on the server is returned
    $createdEvent = $gdataCal->insertEvent($newEvent);

    return $createdEvent->id->text;
}

$user = $_REQUEST["user"];
$pass = $_REQUEST["pass"];
$service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME; // predefined service name for calendar

$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);

$strStartDateTime = date(DATE_ATOM, mktime(date("H"), date("i") + 14, date("s"), date("m"), date("d"), date("Y")));
$strEndDateTime = date(DATE_ATOM, mktime(date("H"), date("i") + 15, date("s"), date("m"), date("d"), date("Y")));

createEvent($client, $_REQUEST["sms"],
                'Title',
                'Detail',
                $strStartDateTime, $strEndDateTime);

echo $_REQUEST['sms'];

————————————-

3. Create Flex web app

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:views="org.robotlegs.sawasdeesms.views.*">
	<s:layout>
		<s:VerticalLayout paddingLeft="4" paddingRight="4" paddingBottom="4" paddingTop="4"/>
	</s:layout>

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<views:MainView width="{this.width}" height="{this.height}"/>

</s:Application>

MainView.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
		  xmlns:s="library://ns.adobe.com/flex/spark"
		  xmlns:mx="library://ns.adobe.com/flex/mx"
		  horizontalAlign="center"
		  preinitialize="mainViewInit()">

	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.core.FlexGlobals;
			import mx.events.CloseEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.AsyncToken;
			import mx.rpc.events.ResultEvent;
			import mx.rpc.http.HTTPService;

			private var arr:Array = [
				{fullname:'John Doe', email:'johndoe@gmail.com', pwd:'mypassword', selected:true}
			];

			[Bindable] private var ac:ArrayCollection;

			private var mainViewPopUp:MainPopUp;

			private var hs:HTTPService;
			private var urlVar:URLVariables;

			private function mainViewInit():void{
				ac = new ArrayCollection( arr );

				hs = new HTTPService();
				hs.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
				hs.url = '../php_service/sms.php';
				hs.addEventListener(ResultEvent.RESULT, httpServiceResult );

				urlVar = new URLVariables();

			}

			public function onSelect(obj:Object, selected:Boolean):void{
				obj.selected = selected;
				ac.setItemAt( obj, ac.getItemIndex( obj ) );

			}

			protected function btAdd_clickHandler(event:MouseEvent):void
			{
				mainViewPopUp = PopUpManager.createPopUp( FlexGlobals.topLevelApplication as Sprite, MainPopUp, true ) as MainPopUp;
				PopUpManager.centerPopUp( mainViewPopUp );
				mainViewPopUp.addEventListener( 'formSubmit', onFormSubmit );
			}

			private function onFormSubmit( event:Event ):void{
				if( mainViewPopUp.useForm == MainPopUp.FORM_ADD ){
					ac.addItem(
						{
							fullname:mainViewPopUp.txtInName.text,
							email:mainViewPopUp.txtInEmail.text,
							pwd:mainViewPopUp.txtInPwd.text,
							selected:true
						}
					);
				}else{
					ac.setItemAt(
						{
							fullname:mainViewPopUp.txtInName.text,
							email:mainViewPopUp.txtInEmail.text,
							pwd:mainViewPopUp.txtInPwd.text,
							selected:dg.selectedItem.selected
						},
						dg.selectedIndex
					);
				}

			}

			protected function btRemove_clickHandler(event:MouseEvent):void
			{
				Alert.show('ต้องการลบรายชื่อ ใช่หรือไม่?', 'คำเตือน', 3, this, onConfirmRemove );
			}

			private function onConfirmRemove( event:CloseEvent ):void{
				if( event.detail == Alert.YES ){
					ac.removeItemAt( dg.selectedIndex );
				}
			}

			protected function dg_doubleClickHandler(event:MouseEvent):void
			{
				mainViewPopUp = PopUpManager.createPopUp( FlexGlobals.topLevelApplication as Sprite, MainPopUp, true ) as MainPopUp;
				PopUpManager.centerPopUp( mainViewPopUp );
				mainViewPopUp.useForm = MainPopUp.FORM_EDIT;
				mainViewPopUp.txtInName.text = dg.selectedItem.fullname;
				mainViewPopUp.txtInEmail.text = dg.selectedItem.email;
				mainViewPopUp.txtInPwd.text = dg.selectedItem.pwd;
				mainViewPopUp.addEventListener( 'formSubmit', onFormSubmit );
			}

			protected function btSend_clickHandler(event:MouseEvent):void
			{
				urlVar.sms = txtAr.text;
				urlVar.user = ac.getItemAt( 0 ).email;
				urlVar.pass = ac.getItemAt( 0 ).pwd;
				var token:AsyncToken = hs.send(urlVar);
				token.index = 0;
			}

			protected function httpServiceResult( event:ResultEvent ):void{
				var token:AsyncToken;

				trace( event.token.index );
				trace( event.result );
				if( ( event.token.index + 1 ) < ac.length ){
					urlVar.user = ac.getItemAt( event.token.index + 1 ).email;
					urlVar.pass = ac.getItemAt( event.token.index + 1 ).pwd;
					token = hs.send(urlVar);
					token.index = event.token.index + 1;
				}else{
					Alert.show('ส่ง SMS แล้วค่ะ', 'ขอบคุณที่ใช้บริการ');
				}
			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<s:VGroup>
		<s:HGroup width="100%" height="100%">
			<mx:Spacer width="100%"/>
			<mx:Button height="28" id="btAdd" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/add.png')"
					   click="btAdd_clickHandler(event)"/>
			<mx:Button height="28" id="btRemove" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/remove.png')"
					   enabled="{dg.selectedIndex + 1}"
					   click="btRemove_clickHandler(event)"/>
		</s:HGroup>

		<mx:DataGrid id="dg" dataProvider="{ac}"
					 doubleClickEnabled="true" doubleClick="dg_doubleClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn width="200" dataField="fullname" headerText="ชื่อ - นามสกุล"/>
				<mx:DataGridColumn width="200" dataField="email" headerText="อีเมล"/>
				<!--
				<mx:DataGridColumn dataField="selected" headerText="ส่งข้อความ ">
					<mx:itemRenderer>
						<fx:Component>
							<mx:Box horizontalAlign="center">
								<s:CheckBox id="cb" selected="{data.selected}"
											change="outerDocument.onSelect(data, cb.selected)"/>
							</mx:Box>
						</fx:Component>
					</mx:itemRenderer>
				</mx:DataGridColumn>
				-->
			</mx:columns>
		</mx:DataGrid>

		<s:HGroup width="100%">
			<mx:Spacer width="100%"/>
			<s:Label text="*หมายเหตุ Double-click เพื่อแก้ไข"/>
		</s:HGroup>

		<mx:Spacer height="50"/>

	</s:VGroup>

	<s:VGroup>
		<s:TextArea id="txtAr" width="400"/>

		<s:HGroup width="100%">
			<mx:Spacer width="100%"/>
			<mx:Button height="28" id="btSend" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/next.png')"
					   label="ส่ง SMS"
					   click="btSend_clickHandler(event)"/>
		</s:HGroup>
	</s:VGroup>

</s:VGroup>

MainPopUp.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" close="titlewindow1_closeHandler(event)">

	<fx:Metadata>
		[Event(name="formSubmit", type="mx.events.Event")]
	</fx:Metadata>

	<s:layout>
		<s:VerticalLayout/>
	</s:layout>

	<fx:Script>
		<![CDATA[
			import mx.events.CloseEvent;
			import mx.managers.PopUpManager;

			public static const FORM_ADD:String = 'formAdd';
			public static const FORM_EDIT:String = 'formEdit';

			public var useForm:String = FORM_ADD;

			protected function titlewindow1_closeHandler(event:CloseEvent):void
			{
				PopUpManager.removePopUp( this );
			}

			protected function btSubmit_clickHandler(event:MouseEvent):void
			{
				dispatchEvent( new Event( 'formSubmit' ) );
				PopUpManager.removePopUp( this );
			}

		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<s:controlBarContent>
		<mx:Spacer width="100%"/>
		<mx:Button height="28" id="btSubmit"
				    icon="@Embed(source='assets/aesthetica-version-2/png/24x24/accept.png')"
					click="btSubmit_clickHandler(event)"/>
	</s:controlBarContent>
	<mx:Form>
		<mx:FormItem label="ชื่อ">
			<s:TextInput id="txtInName" width="200"/>
		</mx:FormItem>
		<mx:FormItem label="อีเมล">
			<s:TextInput id="txtInEmail" width="200"/>
		</mx:FormItem>
		<mx:FormItem label="รหัสผ่าน">
			<s:TextInput displayAsPassword="true" id="txtInPwd" width="200"/>
		</mx:FormItem>
	</mx:Form>

</s:TitleWindow>

————————————-

งานนี้จะเอาไปใช้อะไรได้บ้าง ก็อย่างเช่น เราไม่สามารถอยู่หน้าคอมฯ ได้ตลอด สมมติเราทำระบบสำหรับสั่งซื้อสินค้าเมื่อมีออเดอร์เข้ามาก็ส่ง SMS ให้แจ้งเตือนให้เรารู้ เพื่อจะได้บริการลูกค้าได้เร็วขึ้น หรือจะทำเป็นระบบแจ้งเตือนการนัดประชุมในองค์กรขนาดเล็ก ช่วยประหยัดค่าใช้จ่ายค่าบริการ SMS หรือจะทำเป็นแจ้งเตือนการนัดหมายกับลูกค้า ฯลฯ แล้วแต่จะเอาไปประยุกต์ แต่ก็นะของฟรี บางทีก็ missing text บ้างไรบ้าง แต่ก็ไม่หนักหนาอะไรเกินไป ;)

Introduce to setTimeOut function and Timer class

แนะนำการจัดการเวลาโดยใช้ setTimeOut function และ Timer


<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">

<s:layout>
<s:VerticalLayout/>
</s:layout>

<fx:Script>

<![CDATA[

import flash.utils.setTimeout;
import mx.controls.Text;

private var timerOnce:Timer;
private var timerRepeat:Timer;

private function onBt1Click():void{

var i1:int = 1;
var i2:int = 2;
trace('BT1 :: i1=' + i1 + ', i2=' + i2 );

setTimeout(  function( i1:int, i2:int ):void{ trace('BT1 passed 1000 ms :: i1=' + i1 + ', i2=' + i2 ); }, 1000, i1, i2 );

i1 = 11;
i2 = 22;
trace('BT1 :: i1=' + i1 + ', i2=' + i2 );
}

private function onBt2Click():void{

var i1:int = 1;
var i2:int = 2;
trace('BT2 :: i1=' + i1 + ', i2=' + i2 );

setTimeout( onSetTimeOut, 1000, i1, i2 );

i1 = 11;
i2 = 22;
trace('BT2 :: i1=' + i1 + ', i2=' + i2 );
}

private function onSetTimeOut(i1:int, i2:int):void{
trace('BT2 passed 1000 ms :: i1=' + i1 + ', i2=' + i2 );
}

private function onTimerOnceClick():void{
if( timerOnce == null ){
timerOnce = new Timer( 1000, 1 ); // 1000 ms round
timerOnce.addEventListener(TimerEvent.TIMER, onTimerOnceRun);
timerOnce.start();
}
}

private function onTimerOnceRun(event:TimerEvent):void{
trace( 'timerOnce.currentCount=' + timerOnce.currentCount );
}

private function onTimerRepeatClick():void{
if( timerRepeat == null ){
timerRepeat = new Timer( 1000 ); // 1000 ms round
timerRepeat.addEventListener( TimerEvent.TIMER, onTimerRepeatRun);
timerRepeat.start();
}
}

private function onTimerRepeatRun(event:TimerEvent):void{
trace( 'timerRepeat.currentCount=' + timerRepeat.currentCount );
}

private function onTimerStartStop():void{
if( timerRepeat != null ){
if( timerRepeat.running ){
timerRepeat.stop();
}else{
timerRepeat.start();
}
}
}

]]>

</fx:Script>

<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>

<s:Button label="set time out 1" click="onBt1Click()"/>

<s:Button label="set time out 2" click="onBt2Click()"/>

<s:Button label="timer once" click="onTimerOnceClick()"/>

<s:HGroup>

<s:Button label="timer repeat" click="onTimerRepeatClick()"/>

<s:Button label="Start or Stop" click="onTimerStartStop()"/>

</s:HGroup>

</s:Application>

Follow

Get every new post delivered to your Inbox.