Mobile Applications at NOCCCD - 3CBG

peruvianwageslaveInternet and Web Development

Feb 5, 2013 (4 years and 10 months ago)

138 views

Mobile Applications at NOCCCD

Brad
Rippe

NOCCCD


Goal

Provide insight into developing and deploying
mobile applications with Sungard’s Mobile
Connection.

Brad
Rippe

IT Project Leader

North Orange County Community College
District

brippe@nocccd.edu


Smartphone Statistics

Types of Smartphones

iPhone 40%
Blackberry 36%
Android 22%
Other 2%
Source: University Colorado Boulder

Source: Google Analytics

Mobile Connection 1.0


1.0 Release


Support multiple
mobile platforms


Built on Open Source Software


Integrates with LDAP and Banner Data


Quick Start


m
-
Apps (Feeds, Grades, Schedule, Restaurant)





What features?

22
College/University iPhone
apps


News

-

21

Directory

-

18

Maps

-

18

Events

-

13

Videos

-

9

Courses

-

6

Sports


-

6

Photos

-

5

Library

-

5

Twitter

-

4



8 vendor solutions

University Texas at Austin; Stanford; Harvard; Boston College; Saddleback; Irvine Valley College; Mt. Hood College; Rice
University; more





1.0 Planned Features


m
-
Apps


Student’s Daily Schedule


Grades


Restaurants


News Feed


Staff Directory


Class Schedule


Campus Map



Mobile Connection 1.0.1


Java Development Kit (JDK) 1.6.21+


Grails 1.3.6
-

http://www.grails.org


Spring


Hibernate


Groovy


Rhodes Framework 2.2.5

-

http://rhomobile.com/projects/rhodes


Ruby/Rails


Android SDK and NDK


Blackberry JDE


iOS

SDK/
Xcode




Android Requirements


Android SDK

Android 2.2, API
8

http://developer.android.com/sdk/index.html



Android NDK r4b

Windows
-

http://dl.google.com/android/ndk/android
-
ndk
-
r4b
-
windows.zip

Mac
-

http
://
dl.google.com/android/ndk/android
-
ndk
-
r4b
-
darwin
-
x86.zip

Linux
-

http
://
dl.google.com/android/ndk/android
-
ndk
-
r4b
-
linux
-
x86.zip




API

MC Architecture

request

Luminis

IV LDAP

response


Banner

Mobile Server

NOCCCD Basic Configuration

“The Plan”



3 institutions

3 platforms

~ 4 months

Setting up the “Team”



District/SCE

Cypress

Fullerton

TEST

Collaborating



Collaborating



Repository Structure



MC Directory Structure



Android Emulator



#
Android
config

-

android doesn't respond to
localhost

security_service_url

=







'http
://
10.0.2.2:8001
/
mobileserver
/rest/security'

grade_service_url

=

'http
://
10.0.2.2:8001
/
mobileserver
/rest/grade'

schedule_service_url

=

'http
://
10.0.2.2:8001
/
mobileserver
/rest/schedule'

feed_service_url

=

'http
://
10.0.2.2:8001
/
mobileserver
/rest/feed/'

http://bit.ly/psZ8G7

News Feeds



Updated Feed



Feed Client



2.

Feed


show.erb

<form action="<%=
url_for

:action => :index
%>">


<
select name="id"
onchange
="submit()"
>


<%
if
::
Rho.get_app.feedId

&&
::
Rho.get_app.feedId

== 'cypress'%>


<
option selected value="cypress">Cypress College News</option>


<%
else %>


<
option value="cypress">Cypress College News</option>


<%
end%>


<%
if ::
Rho.get_app.feedId

&& ::
Rho.get_app.feedId

== '
fullerton
'%>


<
option selected value="
fullerton
">Fullerton College News</option>


<%
else %>


<
option value="
fullerton
">Fullerton College News</option>


<%
end
%></
select
></
form><
hr

/>

… More …

Feed


application.rb

require 'rho/
rhoapplication
'


class
AppApplication

< Rho::
RhoApplication


attr_accessor

:
feedId



# more code in here


def

initialize


super


end

end


Feed


feed_controller.rb

def

index



::
Rho.get_app.feedId

= @
params
['id']


url

= "#{@
serviceUrl
}"


url

<<
"/#{::
Rho.get_app.feedId
}.
json
"




#More code


result = Rho::
AsyncHttp.get
( :
url

=>
url
,



:
callback => (
url_for

:



action
=> :
feed_callback
))


redirect :action => :
wait


end

Feed


Config.groovy

feed.location

=

[

cypress
:"http
://
cypresscollege.edu/rss.xml
",
fullerton
:"http://www.fullcoll.edu/rss.xml",

sce
:"http
://
sce.edu/rss.xml

]

sghe

{


plugins {


banner {


jdbc

{ … }



}



}

}

Back Issue

Back Issue


feed_controller.rb

def

index



::
Rho.get_app.feedId

= @
params
['id']


url

= "#{@
serviceUrl
}"


url

<<
"/#{::
Rho.get_app.feedId
}.
json
"




#More code



result = Rho::
AsyncHttp.get
(:
url

=>
url
,



:
callback => (
url_for

:



action
=> :
feed_callback
))


redirect :action => :wait


end

Back Issue


feed_controller.rb

# Original Method

def

feed_callback


app_info

"
feed_callback
: #{@
params
}“ # log
feedId


# parse
json

response



# send browser to show view but still have wait in history??


WebView
.navigate
(
url_for

:action => :show)

end

Fix Back Issue

def

feed_callback


app_info

"
feed_callback
: #{@
params
}“


# parse
json

response




# send browser to
show_feed

method


WebView.navigate
(
url_for

:action => :
show_feed
)

end


def

show_feed


render :action => :show,
:back => '/app/
Mshell
'

end

http://bit.ly/p44ghq

MC 1.1.1


Back Issue

def

index





feedId

= @
params
[
'id‘]


Rho
::
AsyncHttp.get
( :
url

=>
url
,



:
callback => (
url_for

:action => :
feed_callback
))






@
response["headers"]["Wait
-
Page"] = "true
"



redirect
:action => :wait

end

app_info

or
app_error


Android


rake
emulator:android:getlog



BlackBerry


C
:
\
Users
\
<username>
\
net
\
rim
\
fledge
-
2
\
3.0.0.118
\
sdcard
\
Rho
\
<application_name
>
\
RhoLog.tx
t


iPhone


/Users/<username>/Library/Application
Support/iPhone
Simulator/4.3/Applications/<GUID>/Documents/RhoLog.
txt



Server Side Of Things



Class Schedule Plugin



ClassScheduleUrlMappings




class
ClassScheduleUrlMappings

{


static
mappings = {


"/
rest/
getCampuses
"(
controller:"
search
", action:"
getCampuses
")


"/
rest/
getAllTerms
"(
controller:"search
", action:"
getAllTerms
")


// continued…


}

}


SearchController

class
SearchController

{


def

classScheduleService


def

getCampuses

= {



if
(
classScheduleService

== null ) {


// code here, handle null
classScheduleService


}
else
{


def

camps =
classScheduleService.getCampuses
()



if
(camps != null &&
camps.
size
() > 0) {



response.setContentType
("text/
json
")



render
camps as JSON



}
else {



// send no information 404 error



}



}


} …


ClassScheduleService

class
ClassScheduleService

{


def

dataSourceBanner


//defined in the server’s
Config.groovy

file


def

getCampuses
()
{


def

sql

=
Sql.newInstance

(
dataSourceBanner
)


def

query = " "
"select
stvcamp_code
,
stvcamp_desc

from
stvcamp

where




stvcamp_code

in ('1', '2', '3
')
order by
stvcamp_desc
"

"

"


def

campuses =
[]
// list of Campus objects


try
{


sql.eachRow
(query
)
{
def

campus = new Campus()


campus.
campusCode

=
it.stvcamp_code


campus.
campusDesc

=
it.stvcamp_desc


campuses.add
(campus)
}


}
catch(Exception e)
{
log.error

"
Exception ${
query}
-

${e
}“ }


return
campuses


} // end
getCampuses

ClassSchedule

Request



GrailsDispatcherServlet

/service/rest/
getCampuses

SearchController

Campus

ClassScheduleService

response

request

Automatic
Marshalling

import grails.converters.*

class
SearchController

{



def

getCampuses

= {



if
(
classScheduleService

== null ) {



//
code here, handle null
classScheduleService


} else {



def

camps =
classScheduleService.getCampuses
()



if
(camps != null &&
camps.
size
() > 0) {



response.setContentType
("text/
json
")


render camps as JSON



} else {



// send no information 404 error


}



}


} …


1

2

JSON output?

[

{
"
class":"edu.nocccd.Campus"
,
"id":null
,"campusCode":
"1","campusDesc":"Cypress College
"},


{
"
class":"edu.nocccd.Campus"
,
"id":null
,"campusCode":
"2","campusDesc":"Fullerton College
"},


{
"
class":"edu.nocccd.Campus"
,
"id":null
,"campusCode":
"3","campusDesc":"School of Continuing Education
"}

]


Server
BootStrap.groovy

import
grails.converters.JSON

import
org.codehaus.groovy.grails.plugins.GrailsPluginUtils

import
edu.nocccd.Campus

class
BootStrap

{



def

init

= {


}


def

destroy
= {


}

}



Init



Override Converters

class
BootStrap

{



def

init

= {
servletContext

-
>


JSON.registerObjectMarshaller
(Campus
) {


def

returnArray

=
[:]


returnArray
['
campusCode
'] =
it.campusCode


returnArray
['
campusDesc
'] =
it.campusDesc


return
returnArray


}


// continued for each domain object


} …

}


Result JSON

[

{"
campusCode":"1","campusDesc":"Cypress College
"},

{"
campusCode":"2","campusDesc":"
Fullerton College"},

{"
campusCode":"3","campusDesc":"School of
Continuing Education
"}

]


Objects with in Objects


Sections


Multiple buildings


Multiple meeting times



Default converters


Skip internal data


render
sections
as JSON

MATH 030 F

Bldg

1

Bldg

2

Meet 2

Meet 1

MATH 100 F

SearchController

def

sections =
classScheduleService.getSections
(
cCode
,
tCode
,
subCode
,
courNum
)

if (sections != null &&
sections.size
() > 0) {


def

sectionList

= new
ArrayList
()


sections.each

{ section
-
>
def

sectionMap

= new
HashMap
()


sectionMap
.put

"
crn
",
section.crn


def
bldgs

= new
ArrayList
()

// Done for meetings too!


section.buildings.each

{
bldg

-
>
def

bldgMap

= new
HashMap
()


bldgMap.put

"
bldgCode
",
bldg.bldgCode


bldgMap.put

"
bldgDesc
",
bldg.bldgDesc


bldgMap.put

"
roomCode
",
bldg.roomCode


bldgs
.add

bldgMap


}


sectionMap.
put

"
bldgs
",
bldgs


}


sectionList
.add

sectionMap

JSON Response

[{"instructor":"
Danufsky
,
Joshua","wDropDate":"11/20/2011","waitAvailable":10
,"bldgs":
[{"roomCode":"624","bldgDesc":"Math/Comp Science
-

FC","bldgCode":"600"}]
,"meetings":[{"startDate":"08/15/2011","wednesday":null,"thur
sday":null,"monday":null,"sunday":null,"beginTime":null,"satur
day":null,"endDate":"12/16/2011","friday":null,"tuesday":null,"
endTime":null,"arrangedHrs":"Plus arranged
hours"},{"startDate":"08/15/2011","wednesday":" Wed
","
thursday
":null,"
monday
":" Mon
","sunday":null,"beginTime":"1:00","saturday":null,"endDate":"
12/16/2011","friday":null,"tuesday":null,"endTime":"2:50
PM","
arrangedHrs
":null}]]


Render Results



Mobile Server


Apache Tomcat 6.0.32


Run Non
-
Root User


Apache
JMeter


JVM Options


VisualVM


http://visualvm.java.net/





PSI
-
Probe



http://code.google.com/p/psi
-
probe/

Tips
-

Build for Devices


Rhoconfig.txt


MaxLogFileSize
=0

to 50


MinSeverity
=1

to 3


local_server_port
=8080



build.yml



v
ersion
:
1.0
to 1.0.0



rake
device:android:production


version: 1.0

AndroidManifest.xml


<
manifest
xmlns:android
='http://schemas.android.com/
apk
/res/an
droid'
package='com.north_orange_county_community_college_d
istrict.cypresscollege'
android:installLocation
='auto'
android:versionCode
='26'
android:versionName
='2.2.5
'>



</
manifest>


v
ersion: 1.0.0

AndroidManifest.xml


<
manifest
xmlns:android
='http://schemas.android.com/
apk
/res/an
droid'
package='com.north_orange_county_community_college_d
istrict.cypresscollege'
android:installLocation
='auto'

android:versionCode
='10000'

android:versionName
='1.0.0'>



</
manifest>


Demo



Mobile Connection


Version 1.1.1


mApps


Campus Directory


Event Calendar


Campus Map


iPad

Support


JQuery

Mobile


Active Directory Support

1.1 Release Notes

-

http
://
bit.ly/oq90m6




Things to Take Away


Collaborative Environment


Team Commitment


Developer Environment


JQuery

Mobile


Steering Committee


Limited Feature Set


Questions



Resources

Grails Tutorial

http://www.ibm.com/developerworks/java/library/j
-
grails02128/

Ruby Tutorial

http://www.ruby
-
lang.org/en/documentation/quickstart
/

RhoMobile

Docs

http://docs.rhomobile.com/

JMeter

http://jakarta.apache.org/jmeter/

PSI
-
Probe

http://code.google.com/p/psi
-
probe/

VisualVM

http://visualvm.java.net
/

NOCCCD Mobile Apps

http://www.nocccd.edu/mobile