Date post: | 03-Jul-2015 |
Category: |
Technology |
Upload: | ondraz |
View: | 234 times |
Download: | 5 times |
Android 2Ondra Zahradník
MUNI - 18.5.2012
Tuesday, March 19, 13
Agenda
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
Agenda
1.Rekapitulace z minula
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
Agenda
1.Rekapitulace z minula2.BroadcastReceiver
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
Agenda
1.Rekapitulace z minula2.BroadcastReceiver3.Podpora různých zařízení
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
Agenda
1.Rekapitulace z minula2.BroadcastReceiver3.Podpora různých zařízení4.Dynamické UI pomocí fragmentů
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
Agenda
1.Rekapitulace z minula2.BroadcastReceiver3.Podpora různých zařízení4.Dynamické UI pomocí fragmentů5.Přestávka
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
Agenda
1.Rekapitulace z minula2.BroadcastReceiver3.Podpora různých zařízení4.Dynamické UI pomocí fragmentů5.Přestávka6.Architektura typické aplikace
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
Agenda
1.Rekapitulace z minula2.BroadcastReceiver3.Podpora různých zařízení4.Dynamické UI pomocí fragmentů5.Přestávka6.Architektura typické aplikace7.Otázky a odpovědi
Tuesday, March 19, 13
nejdrive trochu teorie, potom prakticky prikladprvni dva priklady se tykaji UI, druhe dva pak programovanibehem prikladu budu obchazet a pomahat
House-keeping notes
•slajdy - http://goo.gl/QiFDy
•zdroje - http://goo.gl/QH5J7
•vaše otázky
Tuesday, March 19, 13
Rekapitulace z minula
1.Android SDK2.Eclipse + ADT3.Emulátor / device4.Založení nového projektu5.Struktura Android projektu6.Activity, layout, Intent = UI7.Kompilace a spuštění8.Debugging
Tuesday, March 19, 13
Broadcasty
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
•Boot, SMS, Call, připojení, čas...
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
•Boot, SMS, Call, připojení, čas...
•BroadcastReceiver
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
•Boot, SMS, Call, připojení, čas...
•BroadcastReceiver<receiver android:name=".SMSReceiver">
<intent-filter > <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter></receiver>
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
•Boot, SMS, Call, připojení, čas...
•BroadcastReceiver<receiver android:name=".SMSReceiver">
<intent-filter > <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter></receiver>
public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context ctx, Intent i) { //TODO do something }}
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
•Boot, SMS, Call, připojení, čas...
•BroadcastReceiver
•Možno definovat vlastní
<receiver android:name=".SMSReceiver"><intent-filter > <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter></receiver> public class SMSReceiver extends BroadcastReceiver {
@Override public void onReceive(Context ctx, Intent i) { //TODO do something }}
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
•Boot, SMS, Call, připojení, čas...
•BroadcastReceiver
•Možno definovat vlastní
•globální
<receiver android:name=".SMSReceiver"><intent-filter > <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter></receiver> public class SMSReceiver extends BroadcastReceiver {
@Override public void onReceive(Context ctx, Intent i) { //TODO do something }}
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Broadcasty
•Aplikace reaguje na důležité události
•Boot, SMS, Call, připojení, čas...
•BroadcastReceiver
•Možno definovat vlastní
•globální•lokální - notifikační systém uvnitř appky,
levné
<receiver android:name=".SMSReceiver"><intent-filter > <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter></receiver> public class SMSReceiver extends BroadcastReceiver {
@Override public void onReceive(Context ctx, Intent i) { //TODO do something }}
Tuesday, March 19, 13
aplikaci to i probudívykonat rychlou akci (limit 10s)mozno broadcasty radit i stopnout
Příklad
Tuesday, March 19, 13
Příklad
•Vypište číslo a text příchozí sms v toastu
Tuesday, March 19, 13
Příklad
•Vypište číslo a text příchozí sms v toastu
•Toast - http://goo.gl/WoTk
Tuesday, March 19, 13
Příklad
•Vypište číslo a text příchozí sms v toastu
•Toast - http://goo.gl/WoTk•Oprávnění android.permission.RECEIVE_SMS
Tuesday, March 19, 13
Příklad
•Vypište číslo a text příchozí sms v toastu
•Toast - http://goo.gl/WoTk•Oprávnění android.permission.RECEIVE_SMS•SMS Intent - extra “pdus” seznam smsek
Tuesday, March 19, 13
Příklad
•Vypište číslo a text příchozí sms v toastu
•Toast - http://goo.gl/WoTk•Oprávnění android.permission.RECEIVE_SMS•SMS Intent - extra “pdus” seznam smsek•SmsMessage.createFromPdu
Tuesday, March 19, 13
Příklad
•Vypište číslo a text příchozí sms v toastu
•Toast - http://goo.gl/WoTk•Oprávnění android.permission.RECEIVE_SMS•SMS Intent - extra “pdus” seznam smsek•SmsMessage.createFromPdu
•SMS lze posílat z DDMS do emulátoru
Tuesday, March 19, 13
Příklad
•Vypište číslo a text příchozí sms v toastu
•Toast - http://goo.gl/WoTk•Oprávnění android.permission.RECEIVE_SMS•SMS Intent - extra “pdus” seznam smsek•SmsMessage.createFromPdu
•SMS lze posílat z DDMS do emulátoru
•zdrojáky - muni-android-hello
Tuesday, March 19, 13
Různá zařízení
Tuesday, March 19, 13
Různá zařízení
•deklarativně podadresáři v /res
Tuesday, March 19, 13
Různá zařízení
•deklarativně podadresáři v /res
•defaultní a alternativní
Tuesday, March 19, 13
Různá zařízení
•deklarativně podadresáři v /res
•defaultní a alternativní
dvě zařízeníjeden layout
Tuesday, March 19, 13
Různá zařízení
•deklarativně podadresáři v /res
•defaultní a alternativní
dvě zařízeníjeden layout
jedno zařízenídva layouty
Tuesday, March 19, 13
Zdroje a kvalifikátory
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
MyProject/ src/ MyActivity.java res/ drawable/ icon.png layout/ main.xml info.xml values/ strings.xml
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
•Kvalifikátory• jazyk (en_rUS, cs, ...)
MyProject/ src/ MyActivity.java res/ drawable/ icon.png layout/ main.xml info.xml values/ strings.xml
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
•Kvalifikátory• jazyk (en_rUS, cs, ...)
• nejmenší šířka (sw720dp)
MyProject/ src/ MyActivity.java res/ drawable/ icon.png layout/ main.xml info.xml values/ strings.xml
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
•Kvalifikátory• jazyk (en_rUS, cs, ...)
• nejmenší šířka (sw720dp)
• velikost obrazovky (small, normal, large, xlarge)
MyProject/ src/ MyActivity.java res/ drawable/ icon.png layout/ main.xml info.xml values/ strings.xml
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
•Kvalifikátory• jazyk (en_rUS, cs, ...)
• nejmenší šířka (sw720dp)
• velikost obrazovky (small, normal, large, xlarge)
• orientace (land, port)
MyProject/ src/ MyActivity.java res/ drawable/ icon.png layout/ main.xml info.xml values/ strings.xml
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
•Kvalifikátory• jazyk (en_rUS, cs, ...)
• nejmenší šířka (sw720dp)
• velikost obrazovky (small, normal, large, xlarge)
• orientace (land, port)
• hustota (ldpi, mdpi, hdpi, xhdpi)
MyProject/ src/ MyActivity.java res/ drawable/ icon.png layout/ main.xml info.xml values/ strings.xml
Tuesday, March 19, 13
Zdroje a kvalifikátory
•Zdroje
•texty, obrázky, layout, barvy,...
•Kvalifikátory• jazyk (en_rUS, cs, ...)
• nejmenší šířka (sw720dp)
• velikost obrazovky (small, normal, large, xlarge)
• orientace (land, port)
• hustota (ldpi, mdpi, hdpi, xhdpi)
• layout, layout-land, layout-land-v13
• drawable, drawable-hdpi, drawable-xhdpi
MyProject/ src/ MyActivity.java res/ drawable/ icon.png layout/ main.xml info.xml values/ strings.xml
res/ drawable/ icon.png background.png drawable-hdpi/ icon.png background.png
Tuesday, March 19, 13
Resource resolving
Tuesday, March 19, 13
Resource resolving
Tuesday, March 19, 13
Resource resolving
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
Tuesday, March 19, 13
Resource resolving
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
Locale = en-GB Screen orientation = port Screen pixel density = hdpi Touchscreen type = notouch Primary text input method = 12key
Tuesday, March 19, 13
Resource resolving
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
Locale = en-GB Screen orientation = port Screen pixel density = hdpi Touchscreen type = notouch Primary text input method = 12key
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
Tuesday, March 19, 13
Resource resolving
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
Locale = en-GB Screen orientation = port Screen pixel density = hdpi Touchscreen type = notouch Primary text input method = 12key
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
drawable/drawable-en/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
Tuesday, March 19, 13
Resource resolving
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
Locale = en-GB Screen orientation = port Screen pixel density = hdpi Touchscreen type = notouch Primary text input method = 12key
drawable/drawable-en/drawable-fr-rCA/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
drawable/drawable-en/drawable-en-port/drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/
drawable-en/drawable-en-port/drawable-en-notouch-12key/
Tuesday, March 19, 13
Různé obrazovky
Tuesday, March 19, 13
•obrazovky small, normal, large, xlarge
Různé obrazovky
Tuesday, March 19, 13
•obrazovky small, normal, large, xlarge
•wrap_content, match_parent •RelativeLayout
Různé obrazovky
Tuesday, March 19, 13
•obrazovky small, normal, large, xlarge
•wrap_content, match_parent •RelativeLayout•9-patch
Různé obrazovky
Tuesday, March 19, 13
•obrazovky small, normal, large, xlarge
•wrap_content, match_parent •RelativeLayout•9-patch
Různé obrazovky
Tuesday, March 19, 13
•obrazovky small, normal, large, xlarge
•wrap_content, match_parent •RelativeLayout•9-patch
Různé obrazovky
Tuesday, March 19, 13
Různé hustoty
Tuesday, March 19, 13
•hustoty ldpi, mdpi, hdpi, xhdpi
Různé hustoty
Tuesday, March 19, 13
•hustoty ldpi, mdpi, hdpi, xhdpi
Různé hustoty
Tuesday, March 19, 13
•hustoty ldpi, mdpi, hdpi, xhdpi
Různé hustoty
• xhdpi: 2.0• hdpi: 1.5• mdpi: 1.0 (baseline)• ldpi: 0.75
Tuesday, March 19, 13
•hustoty ldpi, mdpi, hdpi, xhdpi
Různé hustoty
• xhdpi: 2.0• hdpi: 1.5• mdpi: 1.0 (baseline)• ldpi: 0.75
MyProject/ res/ drawable-xhdpi/ awesomeimage.png drawable-hdpi/ awesomeimage.png drawable-mdpi/ awesomeimage.png drawable-ldpi/ awesomeimage.png
Tuesday, March 19, 13
•hustoty ldpi, mdpi, hdpi, xhdpi
•dp = density independent pixel
Různé hustoty
• xhdpi: 2.0• hdpi: 1.5• mdpi: 1.0 (baseline)• ldpi: 0.75
MyProject/ res/ drawable-xhdpi/ awesomeimage.png drawable-hdpi/ awesomeimage.png drawable-mdpi/ awesomeimage.png drawable-ldpi/ awesomeimage.png
Tuesday, March 19, 13
Podpora různých verzí
Tuesday, March 19, 13
Podpora různých verzí
•Na trhu Android v2.1 - v4.0.3
Tuesday, March 19, 13
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... > <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" /> ...</manifest>
Podpora různých verzí
•Na trhu Android v2.1 - v4.0.3
•Nastavit minSdkVersion a targetSdkVersion
Tuesday, March 19, 13
Podpora různých verzí
•Na trhu Android v2.1 - v4.0.3
•Nastavit minSdkVersion a targetSdkVersion
•Kontrolovat verzi APIprivate void setUpActionBar() { // Make sure we're running on Honeycomb or higher to use ActionBar APIs if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); }}
Tuesday, March 19, 13
Příklad
•Rozšiřte příklad kalkulačky z minulé přednášky o podporu češtiny a landscape režimu
•zdrojáky, ze kterých lze vyjít
•muni-android-hello
•dokumentace
•http://goo.gl/RKihT
Tuesday, March 19, 13
Dynamické UI pomocí fragmentů
•Fragment je kus funkčního UI
•Aktivita hostí fragmenty
•Jedna aplikace pro telefon i tablet
Tuesday, March 19, 13
Fragment
Tuesday, March 19, 13
Fragment
Tuesday, March 19, 13
Fragment
Fragment a jeho UIpublic class ArticleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.article_view, container, false); }}
Tuesday, March 19, 13
Fragment
Fragment a jeho UI
Layout
public class ArticleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.article_view, container, false); }}
<fragment android:name="com.example.android.fragments.ArticleFragment" android:id="@+id/article_fragment" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" />
Tuesday, March 19, 13
Fragment
Fragment a jeho UI
Layout
Instance
public class ArticleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.article_view, container, false); }}
<fragment android:name="com.example.android.fragments.ArticleFragment" android:id="@+id/article_fragment" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" />
public class MainActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_articles); }}
Tuesday, March 19, 13
Fragmenty na telefonu a tabletu
Tuesday, March 19, 13
Fragmenty na telefonu a tabletu
Tuesday, March 19, 13
Fragmenty na telefonu a tabletu
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/frags"> <!-- "Fragment A" --> <fragment class="com.example.android.TitlesFragment" android:id="@+id/list_frag" android:layout_width="@dimen/titles_size" android:layout_height="match_parent"/> <!-- "Fragment B" --> <fragment class="com.example.android.DetailsFragment" android:id="@+id/details_frag" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
Tuesday, March 19, 13
Fragmenty na telefonu a tabletu
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- "Fragment A" --> <fragment class="com.example.android.TitlesFragment" android:id="@+id/list_frag" android:layout_width="match_parent" android:layout_height="match_parent"/></FrameLayout>
Tuesday, March 19, 13
Fragmenty na telefonu a tabletu
public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener { public void onItemSelected(int position) { DisplayFragment displayFrag = (DisplayFragment) getFragmentManager() .findFragmentById(R.id.display_frag); if (displayFrag == null) { // DisplayFragment (Fragment B) is not in the layout (handset layout), // so start DisplayActivity (Activity B) // and pass it the info about the selected item Intent intent = new Intent(this, DisplayActivity.class); intent.putExtra("position", position); startActivity(intent); } else { // DisplayFragment (Fragment B) is in the layout (tablet layout), // so tell the fragment to update displayFrag.updateContent(position); } }}
Tuesday, March 19, 13
Komunikace mezi fragmenty
Tuesday, March 19, 13
Komunikace mezi fragmenty
Tuesday, March 19, 13
Komunikace mezi fragmenty
•FragmentA definuje Listener interface
Tuesday, March 19, 13
Komunikace mezi fragmenty
•FragmentA definuje Listener interface
•Activity interface implementuje
Tuesday, March 19, 13
Komunikace mezi fragmenty
•FragmentA definuje Listener interface
•Activity interface implementuje
•FragmentA volá callback onItemSelected(...)
Tuesday, March 19, 13
Nové API na starých Androidech
•support lib. zpřístupňuje nové api pro staré devices
•Fragment, Loader
•ViewPager, accessibility, NotificationBuilder,...
•ActionBarSherlock
•Animations
import android.support.v4.app.Fragment;import android.support.v4.app.FragmentManager;...
Tuesday, March 19, 13
Příklad
•do seznamu přidejte možnost změnit položku
•změnu potvrďte FragmentDialogem
•zdrojáky
•http://goo.gl/NmK4o
•dokumentace
•http://goo.gl/tuZwX•http://goo.gl/bK5GQ
Tuesday, March 19, 13
Přestávka
Tuesday, March 19, 13
Architektura typické aplikace 1
•Zobraz seznam položek z databáze
Tuesday, March 19, 13
Architektura typické aplikace 1
•Zobraz seznam položek z databáze
ListFragment
Tuesday, March 19, 13
Architektura typické aplikace 1
•Zobraz seznam položek z databáze
ListFragment DBčte data
Tuesday, March 19, 13
Architektura typické aplikace 1
•Zobraz seznam položek z databáze
ListFragment DBčte data
sql
Tuesday, March 19, 13
Architektura typické aplikace 1
•Zobraz seznam položek z databáze
ListFragment DBčte data
sql
UI
Tuesday, March 19, 13
Architektura typické aplikace 1
•Zobraz seznam položek z databáze
ListFragment DBčte data
sql
UI Model
Tuesday, March 19, 13
Databáze
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
•stará se o life-cycle databáze
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
•stará se o life-cycle databáze•callbacky pro vytvoření a upgrade
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
•stará se o life-cycle databáze•callbacky pro vytvoření a upgrade
• onCreate() a onUpgrade()
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
•stará se o life-cycle databáze•callbacky pro vytvoření a upgrade
• onCreate() a onUpgrade()•zpřístupňuje interface databáze
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
•stará se o life-cycle databáze•callbacky pro vytvoření a upgrade
• onCreate() a onUpgrade()•zpřístupňuje interface databáze• getWritableDatabase()
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
•stará se o life-cycle databáze•callbacky pro vytvoření a upgrade
• onCreate() a onUpgrade()•zpřístupňuje interface databáze• getWritableDatabase()
• db.query(), db.insert (), db.update (), db.delete ()
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Databáze
•SQLite v3 databáze
•SQLiteOpenHelper
•stará se o life-cycle databáze•callbacky pro vytvoření a upgrade
• onCreate() a onUpgrade()•zpřístupňuje interface databáze• getWritableDatabase()
• db.query(), db.insert (), db.update (), db.delete ()
• db.beginTransaction(), db.setTransactionSuccessful(), db.endTransaction()
Tuesday, March 19, 13
nefunguji cizí klíčecasto implementovan jako singleton
Cursor
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
•metody pro pohyb
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
•metody pro pohyb• moveToFirst(), moveToNext(), getCount()
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
•metody pro pohyb• moveToFirst(), moveToNext(), getCount()
•metody pro čtení
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
•metody pro pohyb• moveToFirst(), moveToNext(), getCount()
•metody pro čtení• getColumnIndex(String columnName)
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
•metody pro pohyb• moveToFirst(), moveToNext(), getCount()
•metody pro čtení• getColumnIndex(String columnName)
• getString(int columnIndex), ...
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
•metody pro pohyb• moveToFirst(), moveToNext(), getCount()
•metody pro čtení• getColumnIndex(String columnName)
• getString(int columnIndex), ...
• db.query(table, columns, selection, selectionArgs,........)
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Cursor
•zpřístupňuje řádky výsledku db.query()
•metody pro pohyb• moveToFirst(), moveToNext(), getCount()
•metody pro čtení• getColumnIndex(String columnName)
• getString(int columnIndex), ...
• db.query(table, columns, selection, selectionArgs,........)
• SimpleCursorAdapter, CursorAdapter
Tuesday, March 19, 13
optimalizovane dotazy do databaze
Příklad
1.prozkoumejte DatabaseHelper2.v MainFragment zobrazte seznam knizek v DB
1.hint: použijte Cursor a SimpleCursorAdapter
•zdrojáky
•muni-android-1
•dokumentace
•http://goo.gl/eme2a
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
ListFragment
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data uri
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
DBčte data
uri
sql
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
DBčte data
uri
sql
UI
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
DBčte data
uri
sql
UI
Model
Tuesday, March 19, 13
Architektura typické aplikace 2
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
DBčte data
uri
sql
UI
Model
Boilerplate
Tuesday, March 19, 13
ContentProvider
Tuesday, March 19, 13
ContentProvider
•Jednotné API pro přístup k modelu
Tuesday, March 19, 13
ContentProvider
•Jednotné API pro přístup k modelu
•Zapouzdřuje CRUD operace nad databází
Tuesday, March 19, 13
ContentProvider
•Jednotné API pro přístup k modelu
•Zapouzdřuje CRUD operace nad databází
•REST-like api a systém URI
Tuesday, March 19, 13
ContentProvider
•Jednotné API pro přístup k modelu
•Zapouzdřuje CRUD operace nad databází
•REST-like api a systém URI
•Řeší synchronizaci
Tuesday, March 19, 13
ContentProvider
•Jednotné API pro přístup k modelu
•Zapouzdřuje CRUD operace nad databází
•REST-like api a systém URI
•Řeší synchronizaci
•Poskytuje data jiným aplikacím
Tuesday, March 19, 13
Příklad
1.prozkoumejte komunikaci provideru a databáze
2.v Provider naimplementujte query operaci3.v MainFragment načtěte data přes provider
•zdrojáky
•muni-android-2
•dokumentace
•http://goo.gl/EFKK7
Tuesday, March 19, 13
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
CursorLoader
načítání dat
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
CursorLoader
načítání dat callbacky
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat
DBčte data
callbacky
uri
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat
DBčte data
callbacky
uri
sql
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat
DBčte data
callbacky
uri
sql
UI
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat
DBčte data
callbacky
uri
sql
UI
Model
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 3
•Zobraz seznam položek z databáze
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat
DBčte data
callbacky
uri
sql
UI
Model
Boilerplate
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Asynchroní operace
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking•aktualizace UI pouze v ui threadu
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking•aktualizace UI pouze v ui threadu•Application not responding aka. ANR
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking•aktualizace UI pouze v ui threadu•Application not responding aka. ANR•UI žije jenom chvíli
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking•aktualizace UI pouze v ui threadu•Application not responding aka. ANR•UI žije jenom chvíli
•Vše, co může dlouho trvat, mimo ui thread
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking•aktualizace UI pouze v ui threadu•Application not responding aka. ANR•UI žije jenom chvíli
•Vše, co může dlouho trvat, mimo ui thread•síťová komunikace
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking•aktualizace UI pouze v ui threadu•Application not responding aka. ANR•UI žije jenom chvíli
•Vše, co může dlouho trvat, mimo ui thread•síťová komunikace•čtení z databáze
Tuesday, March 19, 13
Asynchroní operace
•Omezený programovací model•slabý procesor•multi-tasking•aktualizace UI pouze v ui threadu•Application not responding aka. ANR•UI žije jenom chvíli
•Vše, co může dlouho trvat, mimo ui thread•síťová komunikace•čtení z databáze•čtení z filesystému
Tuesday, March 19, 13
CursorLoader
Tuesday, March 19, 13
CursorLoader
•Čte z ContentProvideru na pozadí
Tuesday, March 19, 13
CursorLoader
•Čte z ContentProvideru na pozadí
•Activity/Fragment notifikováno callbackem• onCreateLoader()
• onLoadFinished()
• onLoadReset()
Tuesday, March 19, 13
CursorLoader
•Čte z ContentProvideru na pozadí
•Activity/Fragment notifikováno callbackem• onCreateLoader()
• onLoadFinished()
• onLoadReset()
•Umí reusovat jednou nahraný Cursor
Tuesday, March 19, 13
CursorLoader
•Čte z ContentProvideru na pozadí
•Activity/Fragment notifikováno callbackem• onCreateLoader()
• onLoadFinished()
• onLoadReset()
•Umí reusovat jednou nahraný Cursor
•Obnoví Cursor při updatu modelu
Tuesday, March 19, 13
CursorLoader
•Čte z ContentProvideru na pozadí
•Activity/Fragment notifikováno callbackem• onCreateLoader()
• onLoadFinished()
• onLoadReset()
•Umí reusovat jednou nahraný Cursor
•Obnoví Cursor při updatu modelu
•Je třeba volat v onActivityCreated() jako getActivity().getSupportLoaderManager().initLoader()
Tuesday, March 19, 13
Příklad
1.nahraďte volání provideru CursorLoaderem1.hint: fragment nechť implementuje
LoaderManager.LoaderCallbacks
2.hint: použijte getActivity().getContentResolver()
•zdrojáky
•muni-android-3
•dokumentace
•http://goo.gl/BBb2N
Tuesday, March 19, 13
Architektura typické aplikace 4
•Aktualizace z internetu
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 4
•Aktualizace z internetu
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 4
•Aktualizace z internetu
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
UI
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 4
•Aktualizace z internetu
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
UI
Model
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 4
•Aktualizace z internetu
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
UI
ModelČtení
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 4
•Aktualizace z internetu
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
IntentServiceIntent
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 4
•Aktualizace z internetu
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
provede síťování
sql, CVzapisuje
IntentServiceIntent
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 4
•Aktualizace z internetu
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
provede síťování
sql, CVzapisuje
IntentServiceIntent
Aktualizace
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
IntentService
Tuesday, March 19, 13
parametry mozno predat bundlemkonci, pokud neni co zpracovavat,
IntentService
•Implementuje frontu tasků za zpracovaní
Tuesday, March 19, 13
parametry mozno predat bundlemkonci, pokud neni co zpracovavat,
IntentService
•Implementuje frontu tasků za zpracovaní• onHandleIntent(Intent i)
Tuesday, March 19, 13
parametry mozno predat bundlemkonci, pokud neni co zpracovavat,
IntentService
•Implementuje frontu tasků za zpracovaní• onHandleIntent(Intent i)
•Impl. pomocí HandlerThread a Handler
Tuesday, March 19, 13
parametry mozno predat bundlemkonci, pokud neni co zpracovavat,
IntentService
•Implementuje frontu tasků za zpracovaní• onHandleIntent(Intent i)
•Impl. pomocí HandlerThread a Handler
•Třeba definovat v manifestu
Tuesday, March 19, 13
parametry mozno predat bundlemkonci, pokud neni co zpracovavat,
IntentService
•Implementuje frontu tasků za zpracovaní• onHandleIntent(Intent i)
•Impl. pomocí HandlerThread a Handler
•Třeba definovat v manifestu
•Volání přes Context.startService()
Tuesday, March 19, 13
parametry mozno predat bundlemkonci, pokud neni co zpracovavat,
IntentService
•Implementuje frontu tasků za zpracovaní• onHandleIntent(Intent i)
•Impl. pomocí HandlerThread a Handler
•Třeba definovat v manifestu
•Volání přes Context.startService()final Intent i = new Intent(getActivity(), MyService.class);getActivity().startService(i);
Tuesday, March 19, 13
parametry mozno predat bundlemkonci, pokud neni co zpracovavat,
Síťování
Tuesday, March 19, 13
Síťování
•Http Client, UrlConnection
Tuesday, March 19, 13
Síťování
•Http Client, UrlConnection try { final URL url = new URL("http://...."); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if (connection.getResponseCode() == 200) { final InputStream is = connection.getInputStream(); try { //TODO networking } finally { is.close(); } } } catch (IOException e) { // TODO: handle exception }
Tuesday, March 19, 13
Síťování
•Http Client, UrlConnection try { final URL url = new URL("http://...."); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if (connection.getResponseCode() == 200) { final InputStream is = connection.getInputStream(); try { //TODO networking } finally { is.close(); } } } catch (IOException e) { // TODO: handle exception }
Tuesday, March 19, 13
Síťování
•Http Client, UrlConnection
•Oprávnění
•android.permission.INTERNET
try { final URL url = new URL("http://...."); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if (connection.getResponseCode() == 200) { final InputStream is = connection.getInputStream(); try { //TODO networking } finally { is.close(); } } } catch (IOException e) { // TODO: handle exception }
Tuesday, March 19, 13
Parsování a db insert
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray• getString(), getInt(), getDouble()
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray• getString(), getInt(), getDouble()
•XML
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray• getString(), getInt(), getDouble()
•XML
•android.util.XML
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray• getString(), getInt(), getDouble()
•XML
•android.util.XML•android.sax.RootElement
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray• getString(), getInt(), getDouble()
•XML
•android.util.XML•android.sax.RootElement
•ContentValues
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray• getString(), getInt(), getDouble()
•XML
•android.util.XML•android.sax.RootElement
•ContentValues• db.insert()
Tuesday, March 19, 13
Parsování a db insert
•JSON - JSONObject, JSONArray• getString(), getInt(), getDouble()
•XML
•android.util.XML•android.sax.RootElement
•ContentValues• db.insert()
• getContentResolver.notify(Uri,...)
Tuesday, March 19, 13
Příklad
1.Přes RefreshService aktualizujte tabulku Books
2.http://dl.dropbox.com/u/5296640/books.json
•zdrojáky
•muni-android-4
•dokumentace
•http://goo.gl/3EeeTuesday, March 19, 13
Architektura typické aplikace 5
•Operace create, update, delete
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
UI
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
UI
Model
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
UI
ModelČtení
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
AsyncQueryHandluri,CV
čte data
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
ContentProvider
provede c, u, d uri, CV
AsyncQueryHandluri,CV
čte data
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
ContentProvider
provede c, u, d uri, CV
sql, CVzapisuje
AsyncQueryHandluri,CV
čte data
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
Architektura typické aplikace 5
•Operace create, update, delete
ListFragment
ContentProvider
poskytuje data
CursorLoader
načítání dat callbacky
uri
DBčte data
sql
ContentProvider
provede c, u, d uri, CV
sql, CVzapisuje
AsyncQueryHandluri,CV
čte data
Zápis
Tuesday, March 19, 13
můžeme vyřešit update modeluaktivita je hloupe ui
AsyncQueryHandler
Tuesday, March 19, 13
AsyncQueryHandler
•Volá crud operace v threadu na pozadí
Tuesday, March 19, 13
AsyncQueryHandler
•Volá crud operace v threadu na pozadí
•Zpětně notifikuje volajícího
Tuesday, March 19, 13
AsyncQueryHandler
•Volá crud operace v threadu na pozadí
•Zpětně notifikuje volajícího
•Problém - volající nemusí již existovat
Tuesday, March 19, 13
AsyncQueryHandler
•Volá crud operace v threadu na pozadí
•Zpětně notifikuje volajícího
•Problém - volající nemusí již existovat
•Řešení
Tuesday, March 19, 13
AsyncQueryHandler
•Volá crud operace v threadu na pozadí
•Zpětně notifikuje volajícího
•Problém - volající nemusí již existovat
•Řešení•Implementovat jako inner-static nebo top-
level
Tuesday, March 19, 13
AsyncQueryHandler
•Volá crud operace v threadu na pozadí
•Zpětně notifikuje volajícího
•Problém - volající nemusí již existovat
•Řešení•Implementovat jako inner-static nebo top-
level•WeakReference pro odkaz na volajícího
Tuesday, March 19, 13
AsyncQueryHandler
•Volá crud operace v threadu na pozadí
•Zpětně notifikuje volajícího
•Problém - volající nemusí již existovat
•Řešení•Implementovat jako inner-static nebo top-
level•WeakReference pro odkaz na volajícíhoprivate class MyQueryHandler extends AsyncQueryHandler {
// Use weak reference to avoid memoey leakprivate WeakReference<MyActivity> mMyActivity;public MyQueryHandler(Context context) {
super(context.getContentResolver()); mMyActivity = new WeakReference<MyActivity>((MyActivity) context); }
protected void onQueryComplete(int token, Object cookie, Cursor cursor) { MyActivity activity = mMyActivity.get();
if (activity != null && !activity.isFinishing()) {...
}}
Tuesday, March 19, 13
Příklad
1.Doplňte c, u, d operace do provideru a fragmentu
•zdrojáky
•muni-android-5
•dokumentace
•http://goo.gl/BS1hr
Tuesday, March 19, 13
Děkuji@ondraz
ukázky kódu a obrázky pocházejí z http://developer.android.com pod licencemi Apache v2.0 a Creative Commons Attribution v2.5
Tuesday, March 19, 13