Just wanted to show you how to create a phpinfo page.
This page will give you a lot of usefull information about the webserver your webpage is running on.
To start open your favorite text editor. (I like to use notepad++)
You only need to use call one methode: phpinfo()
<?php
phpinfo();
?>
Save your file, make sure it has the php extension. For example “serverinfo.php”
Upload your file to your server with your favorite FTP tool (I like to use Filezilla)
Note: I would highly recommend to upload the file to user/password protected folders only. For more information on secure folders see: http://httpd.apache.org/docs/1.3/howto/htaccess.html
Now you navigate to the page with your webbrowser.
Last time I posted an article on how to fill a table with the help of a string array in a resource file: Android Filling a table from resource file using string array (SQLite)
The drawback of that technique was that you could only import simple records. So this time I want to show you how to fill a table with complex records (multiple fields).
I will use plain xml as a resource file. This gives me the option to enter records and multiple field values for each record.
The xml will be formatted by using one tag for each record and I will use the tag attributes for field values.
As you can see I made a xml with records of animals. For every animal I specified the title and color, so thats two fields. You can add as many fields as you want.
We will be using a SQLiteOpenHelper to create/update the database.
/**
* This class helps open, create, and upgrade the database file.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
private final Context fContext;
DatabaseHelper(Context context) {
super(context, "sampledb", null, 1);
fContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE animals ("
+ "_id INTEGER PRIMARY KEY,"
+ "title TEXT,"
+ "color TEXT"
+ ");");
//Add default records to animals
ContentValues _Values = new ContentValues();
//Get xml resource file
Resources res = fContext.getResources();
//Open xml file
XmlResourceParser _xml = res.getXml(R.xml.animals_records);
try
{
//Check for end of document
int eventType = _xml.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
//Search for record tags
if ((eventType == XmlPullParser.START_TAG) &&(_xml.getName().equals("record"))){
//Record tag found, now get values and insert record
String _Title = _xml.getAttributeValue(null, AnimalColumns.TITLE);
String _Color = _xml.getAttributeValue(null, AnimalColumns.COLOR, 0);
_Values.put(AnimalColumns.TITLE, _Title);
_Values.put(AnimalColumns.COLOR, _Color);
db.insert(AnimalColumns.TABLENAME, null, _Values);
}
eventType = _xml.next();
}
}
//Catch errors
catch (XmlPullParserException e)
{
Log.e(TAG, e.getMessage(), e);
}
catch (IOException e)
{
Log.e(TAG, e.getMessage(), e);
}
finally
{
//Close the xml file
_xml.close();
}
}
/* Update database to latest version */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//Crude update, make sure to implement a correct one when needed.
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS animals");
onCreate(db);
}
}
Let’s look at some of the code.
Here we get the xml file and open it
//Get xml resource file
Resources res = fContext.getResources();
//Open xml file
XmlResourceParser _xml = res.getXml(R.xml.animals_records);
Next we loop trough the xml file and check for record tags
//Check for end of document
int eventType = _xml.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
//Search for record tags
if ((eventType == XmlPullParser.START_TAG) &&(_xml.getName().equals("record"))){
Last we get the values from the attributes and insert a record into our table.
//Record tag found, now get values and insert record
String _Title = _xml.getAttributeValue(null, AnimalColumns.TITLE);
String _Color = _xml.getAttributeValue(null, AnimalColumns.COLOR, 0);
_Values.put(AnimalColumns.TITLE, _Title);
_Values.put(AnimalColumns.COLOR, _Color);
db.insert(AnimalColumns.TABLENAME, null, _Values);
This was the first code example of 2012, I hope it will be of some use to my readers.
Till next time, happy coding!
The last weeks were filled with christmas tree’s and fireworks. After the hollidays I was working on building a new pc. I’m eager to test how fast it can compile and the performance of the Android virtual device.
Stay tuned because I have some great stuff to post.
Well 2011 is almost over. I would like to conclude this year with the words of great Merlin:
You must set your sights upon the heights Don’t be a mediocrity Don’t just wait and trust to fate And say, that’s how it’s meant to be It’s up to you how far you go If you don’t try you’ll never know And so my lad as I’ve explained Nothing ventured, nothing gained
In this post we will be filling a table in our database with values from a resource xml file.
You can use this technique to create default rows in your table on creation/updating of the database.
As you can see I have named the array mytable_records_v1. Put the version in the id so you can make a new string array for future versions. For example in the future you want to add “Fourth row” in your table, you will then name the array “mytable_records_v2″ and only add the new records in your table in the update method.
We will be using a SQLiteOpenHelper to create/update the database.
/**
* This class helps open, create, and upgrade the database file.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
private final Context fContext;
DatabaseHelper(Context context) {
super(context, "sampledb", null, 1);
fContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE mytable ("
+ "_id INTEGER PRIMARY KEY,"
+ "title TEXT"
+ ");");
//Add default record to mytable
ContentValues _Values = new ContentValues();
//Get string array from resource file
Resources res = fContext.getResources();
String[] _mytable_Records = res.getStringArray(R.array.mytable_records_v1);
//Loop trough array and insert records into table
int _Length = _mytable_Records .length;
for (int i = 0; i < _Length; i++) {
_Values.put("title", _mytable_Records [i]);
db.insert("mytable", null, _Values);
}
}
/* Update database to latest version */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//Crude update, make sure to implement a correct one when needed.
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS _mytable_Records ");
onCreate(db);
}
}
This sample will be very useful when dealing with simple lookup tables. However the downside is that there is only room for one field in the string array, so it won’t work for more complex records.
Here is a second template. This template features a grid for quick drawing and boxes for menu options.
Scaled at A4 format the template has an accurate screen size, so sketches can be made at 1:1 ratio.
Click on the image to download the full size version.
As you can see I have created a button called “btn_feedback”, it’s text is specified by a resource string and its OnClick event is set to “btnFeedbackOnClick”.
Now we will specify our resource strings, these are allocated in the file “res/values/strings.xml”.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Feedback sample</string>
<string name="button_feedback">Send feedback</string>
<string name="title_send_feedback">Send feedback</string>
<string name="mail_feedback_email">feedback@example.com</string>
<string name="mail_feedback_subject">Feedback on your app</string>
<string name="mail_feedback_message">Hi,
\n\nYour Feedback sample app rocks! I would like to give you some feedback:</string>
</resources>
Strings:
“app_name”: Your application title.
“button_feedback”: Text on feedback button.
“title_send_feedback”: Title of chooser dialog.
“mail_feedback_email”: Email address where the feedback is going.
“mail_feedback_subject”: Subject of the email.
“mail_feedback_message”: Default mail message.
Last we will create our activity “sample_feedback_activity.java”
package com.example.feedback;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class sample_feedback_activity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.feedback_sample);
}
//On click event for button feedback
public void btnFeedbackOnClick(View v) {
final Intent _Intent = new Intent(android.content.Intent.ACTION_SEND);
_Intent.setType("text/html");
_Intent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{ getString(R.string.mail_feedback_email) });
_Intent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.mail_feedback_subject));
_Intent.putExtra(android.content.Intent.EXTRA_TEXT, getString(R.string.mail_feedback_message));
startActivity(Intent.createChooser(_Intent, getString(R.string.title_send_feedback)));
}
}
I am using type “text/html” you can also use “text/plain” but when testing the code on my Desire Z the default HTC mail app is not set for “text/plain” and it will only start GMail. By using “text/html” the user can use the HTC mail app.
I hope this post will help you develop your Android app.
Till next post.
In this post I will show how to use a left outer join in your provider.
Now note that you can also create a view in your database and query on the view. I might do a post on that subject in the future.
For this example I will use a SQLite database with two tables: book
_id – Id field
title – Title of our book
category – Id references to table categories.
categories
_id – Id field
title – Caption of our category
Note that I’m using title in both tables, I’ve used these field names so I can show you how to deal with fields that have the same name.
First I have created a helper class for our table:
/**
* Books table
*/
public static final class BookColumns implements BaseColumns {
// This class cannot be instantiated
private BookColumns () {}
/**
* The table name of books = "books"
*/
public static final String TABLENAME = "books";
/**
* The title of the book
* <P>Type: TEXT</P>
*/
public static final String TITLE = "title";
/**
* The category of the book (ref to table categories)
* <P>Type: INT</P>
*/
public static final String CATEGORY = "category";
}
/**
* Categories table
*/
public static final class CategoriesColumns implements BaseColumns {
// This class cannot be instantiated
private CategoriesColumns () {}
/**
* The table name of categories = "categories"
*/
public static final String TABLENAME = "categories";
/**
* The caption of the category
* <P>Type: TEXT</P>
*/
public static final String TITLE = "title";
}
Next I have created a provider, nothing special just your basic provider.
I want to get the title of the category as a field when getting our books or one book.
First I will need to define the projection maps. But here a problem occurs. I want to get the book title and our category title in one query, but both field names are named the same.
Luckily In SQL you can specify your exact field name with a prefix of the table name. I will use this to map the category title to a more suitable field name. But in the helper class I only specified the field names. To keep my code clean I will modify the helper classes.
/**
* Books table
*/
public static final class BookColumns implements BaseColumns {
// This class cannot be instantiated
private BookColumns () {}
/**
* The table name of books = "books"
*/
public static final String TABLENAME = "books";
/**
* The id of the book, includes tablename prefix
* <P>Type: INT</P>
*/
public static final String FULL_ID = TABLENAME + "." + _ID;
/**
* The title of the book
* <P>Type: TEXT</P>
*/
public static final String TITLE = "title";
/**
* The title of the book, includes tablename prefix
* <P>Type: TEXT</P>
*/
public static final String FULL_TITLE = TABLENAME + "." + TITLE;
/**
* The category of the book (ref to table categories)
* <P>Type: INT</P>
*/
public static final String CATEGORY = "category";
/**
* The category of the book, includes tablename prefix (ref to table categories)
* <P>Type: INT</P>
*/
public static final String FULL_CATEGORY = TABLENAME + "." + CATEGORY;
}
/**
* Categories table
*/
public static final class CategoriesColumns implements BaseColumns {
// This class cannot be instantiated
private CategoriesColumns () {}
/**
* The table name of categories = "categories"
*/
public static final String TABLENAME = "categories";
/**
* The id of the category, includes tablename prefix
* <P>Type: INT</P>
*/
public static final String FULL_ID = TABLENAME + "." + _ID;
/**
* The caption of the category
* <P>Type: TEXT</P>
*/
public static final String TITLE = "title";
/**
* The caption of the category, includes tablename prefix
* <P>Type: TEXT</P>
*/
public static final String FULL_TITLE = TABLENAME + "." + TITLE;
}
Here’s a component I made a long time ago. This is a label that scroll’s the text.
You can set the scroll speed with the property: ScrollSpeed
You can specify if the text should scroll once or repeat in a loop with the property: RepeatScroll
And you have two events that tell you when the scrolling starts and stops: OnStartScroll, OnEndScroll
unit caScrollLabel;
interface
uses
StdCtrls, Messages, Windows, ExtCtrls, Controls, Classes;
type
TcaScrollLabel = class(TLabel)
private
FScrollTimer: TTimer;
FScrollPos: Integer;
FTextWidth: Integer;
FOnEndScroll: TNotifyEvent;
FOnStartScroll: TNotifyEvent;
FScrolling: Boolean;
FRepeatScroll: Boolean;
FScrollSpeed: Cardinal;
procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
procedure ScrollTimer(Sender: TObject);
procedure SetOnEndScroll(const Value: TNotifyEvent);
procedure SetOnStartScroll(const Value: TNotifyEvent);
procedure SetRepeatScroll(const Value: Boolean);
procedure SetScrollSpeed(const Value: Cardinal);
protected
procedure Paint; override;
procedure SetAutoSize(Value: Boolean); override;
procedure DoStartScroll;
procedure DoEndScroll;
procedure StartScrolling();
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Scrolling: Boolean read FScrolling;
published
property OnStartScroll: TNotifyEvent read FOnStartScroll write SetOnStartScroll;
property OnEndScroll: TNotifyEvent read FOnEndScroll write SetOnEndScroll;
property RepeatScroll: Boolean read FRepeatScroll write SetRepeatScroll;
property ScrollSpeed: Cardinal read FScrollSpeed write SetScrollSpeed;
end;
implementation
uses
SysUtils, Graphics, Math;
{ TcaScrollLabel }
procedure TcaScrollLabel.CMTextChanged(var Message: TMessage);
begin
inherited;
StartScrolling();
end;
constructor TcaScrollLabel.Create(AOwner: TComponent);
begin
inherited;
AutoSize := False;
FScrollTimer := TTimer.Create(nil);
FScrollTimer.OnTimer := ScrollTimer;
FScrollTimer.Interval := 100;
FScrollSpeed := 100;
FScrollPos := 0;
FTextWidth := 0;
end;
destructor TcaScrollLabel.Destroy;
begin
FreeAndNil(FScrollTimer);
inherited;
end;
procedure TcaScrollLabel.DoEndScroll;
begin
FScrolling := False;
if Assigned(OnEndScroll) then
OnEndScroll(Self);
if RepeatScroll then
StartScrolling();
end;
procedure TcaScrollLabel.DoStartScroll;
begin
FScrolling := True;
if Assigned(OnStartScroll) then
OnStartScroll(Self);
end;
procedure TcaScrollLabel.Paint;
var
lRect: TRect;
begin
if not Assigned(Parent) then
Exit;
lRect := ClientRect;
if not (csDesigning in ComponentState) then
lRect.Left := (lRect.Right - FScrollPos);
Canvas.Font := Font;
if not Transparent then
begin
Canvas.Brush.Color := Color;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(lRect);
end else
Canvas.Brush.Style := bsClear;
TextOut(Canvas.Handle, lRect.Left, lRect.Top, PChar(Caption), Length(Caption));
end;
procedure TcaScrollLabel.ScrollTimer(Sender: TObject);
begin
if FScrollPos >= (FTextWidth + Width) then
begin
FScrollTimer.Enabled := False;
DoEndScroll;
end
else
Inc(FScrollPos, 3);
if Assigned(Parent) then
Repaint;
end;
procedure TcaScrollLabel.SetAutoSize(Value: Boolean);
begin
inherited SetAutoSize(False);
end;
procedure TcaScrollLabel.SetOnEndScroll(const Value: TNotifyEvent);
begin
FOnEndScroll := Value;
end;
procedure TcaScrollLabel.SetOnStartScroll(const Value: TNotifyEvent);
begin
FOnStartScroll := Value;
end;
procedure TcaScrollLabel.SetRepeatScroll(const Value: Boolean);
begin
FRepeatScroll := Value;
end;
procedure TcaScrollLabel.SetScrollSpeed(const Value: Cardinal);
begin
FScrollSpeed := Value;
FScrollTimer.Interval := Value;
end;
procedure TcaScrollLabel.StartScrolling;
var
lRect: TRect;
lFlags: Cardinal;
begin
if not Assigned(Parent) then
Exit;
if Assigned(FScrollTimer) then
begin
DoStartScroll;
FScrollPos := 0;
FScrollTimer.Enabled := not (csDesigning in ComponentState);
lRect := Rect(0,0,0,0);
Canvas.Font := Font;
lFlags := DT_LEFT or DT_CALCRECT or DT_SINGLELINE or DT_NOPREFIX;
DrawText(Canvas.Handle, PChar(Caption), Length(Caption), lRect, lFlags);
FTextWidth := lRect.Right - lRect.Left;
end;
end;
end.