Feeds:
Posts
Comments

Archive for the ‘Mobile’ Category

It took my quite some time to install the latest PhoneGap (2.9.1 as for now) for Android on Windows 8. I’ve found this Blog particularly useful.

If your target Android version is 4.4.2 (API 19), make sure you have installed “Android SDK Build-tools 19.1”. Otherwise you may get an error something like

The SDK Build Tools revision (19.0.3) is too low for project 'HellowPhoneGap'.
Minimum required is 19.1.0

 

Advertisements

Read Full Post »

MathGrapher (Android app)

I’ve published an Android app, MathGrapher, a while ago. It seems to be attracting more and more users.

With MathGrapher, you can visualize function equations, parametric equations, and/or polar equations. You can also animate an equation by defining the range of one or more parameters: a, b, c, or d.

Here are some examples:

Function equation: y = a*x^2 + b*x + c
Parametric equation: x = a*sin(t); y = b*cos(t)
Polar equation: r = a*cos(b*t + pi/2)

You can select equations from the equation gallery that contains some predefined equations.

You can save the equations for later use.

Read Full Post »

I’ve published a new Android app, SATWordPuzzle, with which you can play CrossWord and WordSearch puzzle games as well as learn SAT vocabulary.

SATWordPuzzle generates CrossWord and WordSearch puzzles from over 5000 SAT vocabulary. You can also learn the definitions of the words, and add them as favorites. The favorite words have more chance to appear in the puzzles.

When you solve a CrossWord puzzle, you can swipe you finger on a word to show the clue of the word, or you can touch the Clue button to show the clues of all words.

You can turn on Instant Feedback to allow the app to change the background color of a letter right after you input one in a cell.

Read Full Post »

In some of my apps, I need to use a pre-existing database, which is very likely to be upgraded in a later release and which could be pretty big (over the roughly 1MB limit of an asset file). After some experiments and searching on the Web (especially on StackOverflow), I’ve settled with the following code. The basic idea is

1. if the database doesn’t exist yet when it is to be opened, combine the split files into a database file, and open it.

2. if the database version is changed, delete the database file in onUpgrade().

//The Android's default system path of your application database.
private static final String DB_PATH = "/data/data/com.mypackage.myapp/databases/";
private static final String DB_NAME = "mydb.db";
private static final int DB_VERSION = 2;
private static final String DB_SPLIT_NAME = "mydb.db.00";
private static final int DB_SPLIT_COUNT = 3;
private SQLiteDatabase m_database;
private final Context m_context;

/**
 * Constructor
 * Takes and keeps a reference of the passed context in order to access to the application assets and resources.
 * @param context
 */
public MyDB(Context context) {
 super(context, DB_NAME, null, DB_VERSION);
 this.m_context = context;
}

public static MyDB openDatabaseReadOnly(Context context) {
 MyDB db = new MyDB(context);

 try {
   db.createDataBase();
 } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
 }

 db.openDataBase(SQLiteDatabase.OPEN_READONLY);
 return db;
}

public static MyDB openDatabaseReadWrite(Context context) {
 MyDB db = new MyDB(context);

 try {
   db.createDataBase();
 } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
 }

 db.openDataBase(SQLiteDatabase.OPEN_READWRITE);
 return db;
}

/**
 * Creates an empty database on the system and rewrites it with your own database.
 */
public void createDataBase() throws IOException {
  boolean dbExist = checkDataBase();
  if (dbExist) {
    /* By calling this method here onUpgrade() will be called on a
    ** writable database, but only if the version number has been bumped.
    */
    SQLiteDatabase db = this.getWritableDatabase();

    if (db != null) {
      db.close();
    }
 }

 dbExist = checkDataBase();

 if (!dbExist) {
   try {
     /* By calling this method an empty database will be created into the 
     * default system path of your application so we are gonna be able 
     * to overwrite that database with our database.
     */
     SQLiteDatabase db = this.getReadableDatabase();

     if (db != null) {
       db.close();
     }
     copyDataBase();
   }
   catch (IOException e) {
     Log.e("DB", e.getMessage());
      throw new Error("Error copying database");
    }
  }
}

/**
 * Check if the database already exist to avoid re-copying the file each time you open the application.
 * @return true if it exists, false if it doesn't
 */
private static boolean checkDataBase(){
   SQLiteDatabase checkDB = null;
   try {
     String path = DB_PATH + DB_NAME;
     checkDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
   }
   catch (SQLiteException e){
     //database does't exist yet.
   }

   if (checkDB != null) {
     checkDB.close();
   }

   return checkDB != null ? true : false;
}

/**
 * Copies your database from your local assets-folder to the just created empty database in the
 * system folder, from where it can be accessed and handled.
 * This is done by transferring byte stream.
 * */
private void copyDataBase() throws IOException {
  // Path to the just created empty db
  String outFileName = DB_PATH + DB_NAME;
  //Open the empty db as the output stream
  OutputStream output = new FileOutputStream(outFileName);
  //transfer bytes from the inputfile to the outputfile
  byte[] buffer = new byte[1024*8];

  AssetManager assetMgr = m_context.getAssets();

  for (int i = 1; i <= DB_SPLIT_COUNT; i++) {
     //Open your local split file as the input stream
     String fn = DB_SPLIT_NAME + String.valueOf(i);
     InputStream input = assetMgr.open(fn);
     //Log.i("DB", "opened " + fn);

     int length;
     while ((length = input.read(buffer)) > 0) {
       //Log.i("DB", "read " + String.valueOf(length));
       output.write(buffer, 0, length);
       //Log.i("DB", "write " + String.valueOf(length));
     }
     input.close();
   }

   //Close the streams
   output.flush();
   output.close();
}

private void openDataBase(int flags) throws SQLException{
  //Open the database
  String myPath = DB_PATH + DB_NAME;
  m_database = SQLiteDatabase.openDatabase(myPath, null, flags);
}

@Override
public synchronized void close() {
  if (m_database != null)
    m_database.close();
    super.close();
  }
}

@Override
public void onCreate(SQLiteDatabase db) {
  // do nothing
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  if (newVersion > oldVersion) {
     m_context.deleteDatabase(DB_NAME);
  }
}

Read Full Post »

iOS app成语纵横1.1版的简体版和繁体版都被approve了。1.1版主要是提高可用性(Usability)。在找成语时通过滑动手指找出成语,并能提供即时反馈。在玩填字游戏时,可沿某成语的方向滑动手指,调出该成语的提示。

Read Full Post »

成语纵横-简体版发布了。在iPhone和iPad上都可运行。你可以玩找成语或填字游戏,也可以学习成语的解释、出处、示例等。

Read Full Post »

有用户反映成語縱橫1.3版似乎变慢了。在1.3版我主要引入了即时反馈,即在一个成语所有的字被找出后,马上自动改变成语的背景色。另一个改动是利用Android的GestureDector检测单点触击和滑动手指。这两项改动都可能影响速度。

在下一版,我将在设置中增加一个选项,允许用户选择是否提供即时反馈。同时改进GestureDector的用法。

在新版推出之前,请尽量通过滑动手指一次选择多个字。这样会大大提高游戏的流畅性。这也同样适合于古诗纵横游戏。

Read Full Post »

Older Posts »