Jump to content

Android/Printable version

From Wikibooks, open books for an open world


Android

The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/Android

Permission is granted to copy, distribute, and/or modify this document under the terms of the Creative Commons Attribution-ShareAlike 3.0 License.

Introduction

Android speculations

[edit | edit source]

Android's market penetration has extended through Android handset and tablet makers, some do also manufacture other consumer goods.

Android varieties

[edit | edit source]
Android comes in a variety of flavors.

Stock Android

[edit | edit source]

The most widespread flavors of Android are distributed in binary form in numerous smart-phones and tablet computers of companies that are members of the Open Handset Alliance (OHA), founded by Google, and which keeps tight control over 'first-launch' presentation of the operating system and additional software that is required for inclusion.

Because of Google's widespread services ecosystem that includes Google Drive, Google Maps, YouTube, and other Google properties, a hardware manufacturer intending to ship its devices with the Android operating system usually cannot avoid inclusion of built-in Google apps (part of Google Mobile Services) in order to successfully entice prospective buyers to purchase the device. Although Google apps can be separately installed by the user, it may be challenging to the average consumer (most people), who might then seek a competing device which does have the Google apps already installed.

Android trademarks and Google Mobile Services software can only be licensed by hardware manufacturers (OEMs) for devices that meet Google's compatibility standards contained within the Android Compatibility Definition Document. Following that path may also require that the manufacturer be a member of the OHA. Per OHA's rules, inclusion of Google Mobile Services is then mandatory, and the software bundle must be licensed from Google.

OHA members are not allowed to ship their devices with forks of the Android operating system, as doing so may terminate their membership in OHA, the license to use the Android trademark, and the right to pre-install Google apps.

Google Mobile Services is Google's proprietary application stack that includes Google apps, like the Google Play Store and assorted proprietary application programming interfaces (APIs). Many apps published in the Google Play Store directly depend on these proprietary APIs.

The operating system releases of these companies' devices are known as Stock Android.

To counter the mandatory nature of Google Mobile Services, large smartphone companies, like Samsung, almost always bundle their own software and services alongside Google apps that directly compete with Google's offerings, including even their own app stores. The pre-installed competing apps of manufacturers have variable quality, and smart-phone users often resent inclusion of such bundles, as they take up device resources that people wish to use for other purposes. This especially affects users of low-end devices with limited operating memory. In turn, larger mobile phone manufacturers release versions of their phones under the Google Edition label, which devices don't contain manufacturer bundles, but only Google apps.

Forks based on AOSP

[edit | edit source]

There are other device manufacturers, such as Amazon and Nokia, who for various reasons do not want to use the Android flavor promoted by Google and the Open Handset Alliance. Some other manufacturers only market devices to countries where Google is blocked, so they can't use Google's Android by default.

These companies do peruse Android's open source code, but market the operating system under different names, have their own app stores, and develop their own APIs that seek feature parity with Google's APIs, which can be incompatible with one another.

Community forks

[edit | edit source]
LineageOS 16.0

The open source community benefits from regular releases of Android source code, and creates community editions of Android which contain enhanced functionality not included or disabled in stock Android. One of the most popular community distributions is LineageOS.

The benefits of community editions:

  • More up-to-date software, as many manufacturers and mobile operators have a habit of not providing software updates to devices they distribute, especially for low-end devices. While some devices are too under-powered and thus incapable of running major new OS versions, then the more capable devices not offered updates by the manufacturer or operator are effectively orphaned.
  • There are also new versions of applications that don't work on older major versions of Android, as they require newer APIs; thus a major version upgrade extends the lifetime of a device's usefulness and reduces its obsolescence.
  • The forks are perceived to be cleaner, given that they don't contain manufacturer bundles, and on account of that take up less device resources. Neither are Google's apps included in these distributions, as said apps are proprietary, but they can be side-loaded or installed separately.
  • Newer versions are less buggy and are overall better-optimized.

Community distributions are not always fully functional on all phones, given that manufacturers ship their stock Android versions with proprietary hardware drivers, and can release little to no documentation about their devices' innards. If a piece of hardware in a phone is unknown, then this makes it impossible to implement its functionality in a fork. During the development phase, the forks can have various levels of stability and functionality. The LineageOS community maintains lists of devices that are fully functional with particular versions of LineageOS.

App repositories

[edit | edit source]
F-Droid, an app repository offering free and open source applications.

Unlike with Apple's iOS and its walled garden approach, Android allows users to use more than one app repository without having to root the phone.

Google Play Store

[edit | edit source]

Google Play Store is the main application market controlled by Google: entertainment media, such as books, music and rental movies go through the Play Store; if the Play Store app is used, then revenue apart from advertising can be generated there. Developers also provide revenue for Google.

Abuses in revenue generation

[edit | edit source]

An early and infamous example of unethical revenue generation was an anti-virus app that went "viral", but which devoted simple source code merely to change icons, waiting, and then displaying a notification that there was no virus. The ensuing speculation had it that this particular developer wasn't much different from phone makers who depend on Android's activity-per-process and message passing/file IO communication between processes. Nevertheless, Linux's vigorous developer community utilizing the Linus's Law and its given enough eyeballs development and code review method ensures that there are no buffer overrun exploits available in any inter-process kernel functions that would underlie the Dalvik virtual machine.

Google has implemented the ratings and comments system into the Play Store, and also the possibility to report a misbehaving or suspicious app.

Other app stores and repositories

[edit | edit source]

There are also separate app stores provided either by manufacturers and in bigger countries, such as Russia and China, where respectively, Google services are not in favor or completely blocked, local Internet services offer an alternative.

F-Droid is an free and open source app repository, which only contains applications with free/open source software licenses, including a client to browse and install apps. F-Droid offers some apps, like Newpipe, which are not available on the Google Play Store, either by choice or due to restrictions.


Android Applications

Structure of an Android application

[edit | edit source]

An Android application (commonly abbreviated to app) resides in a file with extension .apk. This is an extension of the .jar file format as traditionally used for Java applications. Among the contents of this file are:

  • AndroidManifest.xml—the app manifest, which tells the Android system about the components of the app, what Android versions it will run on, and what hardware features and permissions it needs. The permissions, in particular, are shown to the user when they want to install the app from the Google Play Store, so they can choose whether to accept or reject your app on that basis. If a particular Android system service is controlled by a permission, and your manifest does not list that permission, then your app will be prevented from accessing that service.
  • Compiled Java bytecode. However, instead of using Sun’s JVM .class format, this code is in Dalvik classes.dex format.
  • (Optional) native machine code. Since this has to be architecture-specific, the APK format allows for alternative versions of this to be supplied, under different subdirectories named armeabi, mips etc.
  • Application resources. This can include screen layouts, text strings (to allow localization for different languages/regions), icons, colour specifications, style definitions and various other things, mostly in XML format. Most of these can be created at runtime directly in the code, but keeping them in static form usually allows for easier management. In particular, it allows the Android system to automatically provide selection from alternative resources based on the system configuration, without having to include special cases in your code.

The difference between an app and an activity

[edit | edit source]

The icons the user sees in the app tray correspond, not to applications or .apk files as such, but to activities. Basically, an activity is a single screen that the user interacts with. If an activity is marked as exported in the manifest, then the launcher is able to show the user an icon for it, that can be tapped to launch that activity. An app may contain other activities that are not exported, that are only started internally, perhaps (directly or indirectly) in response to user actions in an exported activity. If an app has more than one exported activity, then corresponding multiple icons will appear in the launcher.

ELF Executables

[edit | edit source]

Android is built on a Linux kernel, and can run executables in the usual Linux format. However, you’ll note that the APK format includes no provision for installing such files, and such executables cannot easily access the usual Android class libraries. There are some standard executables included with every Android system, including Toolbox, which is a very basic command-line shell. You can get access to this from the SDK on a connected PC with the “adb shell” command. It is also possible to implement a terminal window running on the Android device itself; for an example of how to do this, see the source code for ConnectBot.

Security Model

[edit | edit source]

An APK file must be digitally signed by the developer before it can be installed on an Android device. There is no need to officially register your signing key with Google or anybody: the signing key merely allows the device to tell whether two different APK files come from the same developer or not.

Once installed on a device, the app is assigned its own (dynamically-generated) Linux user ID. This allows the system to prevent apps from accessing each other’s data, except in carefully-authorized ways.

Process Concepts

[edit | edit source]

Being based on a Linux kernel, Android supports standard POSIX-style processes and threads. However, the user doesn’t typically see applications in terms of these; instead, the Android UI works in terms of tasks and activities.

Typically, all the activities defined by an app run in a single process, and different apps run in different processes. However, this can be controlled by the programmer in various ways, by specifying the launchMode and taskAffinity for an activity. Also, the application process attribute allows two apps that trust each other (are signed by the same private key) to run in the same process, if the programmer desires.

Note that, in the Android docs where it says that an activity is “stopped” while in the background, all that means is that its onStop method has been called, and its UI is no longer considered to be active. Any processes and threads associated with the activity keep right on running as normal, and can continue performing any actions they like, though of course the only recommended UI-related operations would be the sending of notifications indicating that they would like some user attention.

And of course such background processes/threads are near the top of the priority queue for being killed if system resources run low.

The UI (Main) Thread

[edit | edit source]

Note that your app process initially starts with a single thread. In particular, all UI calls must happen on this thread. With the obvious exception of the relevant Handler methods, and Activity.runonUiThread, the Android UI classes (views, widgets etc) are not thread-safe, and must not be invoked on any other thread. You are free to create additional processes and threads; the easiest way to manage background activities that need to coordinate with the UI (display progress updates, handle cleanup on cancellation etc) is to use an AsyncTask.

The UI thread runs a Looper, which you can also instantiate to run on your own threads. If the UI Looper does not get some CPU time within 5(?) seconds of a user event, you see the dreaded “Application Not Responding” (ANR) alert.

If you need to run short tasks that will not hold up the UI thread for too long, you can queue them to the Looper via Handlers (see the post and postAtTime methods). More time-consuming operations that cannot be split up into short-duration tasks should be relegated to a background thread.

Programmers familiar with Java from other platforms may be accustomed to using a TimerTask to schedule timed events, but these run on their own thread, so they are not safe for directly making UI calls. You can, however, call Activity.runonUiThread from a background thread.

Tasks And The Back Stack

[edit | edit source]

A task is anything started by tapping an icon in the launcher. The launcher’s app tray is automatically populated with appropriate entries (i.e. main activities) from all installed apps, and these entries can be copied to home screens. It is also possible to add shortcuts to home screens. Typically a task is started in a new process, and if such a process is already running, then tapping the icon simply brings the existing process to the front. However, it is possible to alter both these behaviours.

The first (root) activity in a task can launch additional activities as appropriate. Indeed, any application you write other than the very simplest is likely to consist of more than one activity. Tasks can also launch other, entirely separate tasks, the difference being that these show up in the recent-tasks list as separate entries, whereas app-internal activities do not. As each new activity is started, it becomes the topmost one visible to the user, and the one previously topmost has its UI deactivated. These additional activities usually run in the same process as the one that started them, but again, this behaviour can be varied.

Pressing the standard Android Back key normally terminates the topmost activity, and makes the previously-topmost one active. Terminating the root activity of a task terminates the task and returns the user to whatever task was running before (perhaps the launcher).

Activities within a task are never reordered as long as they are running; the only way to bring an activity that is not at the top of the back stack of a task to the top is to terminate all the activities above it in that task. However, tasks can be reordered, since any of the most recent eight tasks launched can be brought to the front at any time simply by leaning on the Home key and tapping on one of the entries that appear.

Activities are launched via some varient of the startActivity calls available to subclasses of a Context (which includes your own Activities). The main argument to each of these calls is an intent.

Memory Usage

[edit | edit source]

On an unrooted Android device, Java code is strictly limited in how much memory it can use. Java objects are restricted to a heap size of about 20MB, while Bitmap objects are limited to their own heap area of a similar size. Oh, and contrary to what it says on that page about the recycle method being “an advanced call, and normally need not be called”, I would recommend you always call recycle on bitmaps when you have finished with them; failure to do this is liable to make your app crash at some point with the dreaded message “java.lang.OutOfMemoryError: bitmap size exceeds VM budget”.

Native code is not arbitrarily limited in its memory usage, and is free to allocate all available memory.

Save/Restore Instance State

[edit | edit source]

Activities and views can implement calls to save and restore their “instance state”. This is not the same as saving/restoring user data; this is purely part of the mechanism to make it look like your process has been running all along, even though the system needed to kill it either because it was running low on memory, or to relaunch it on an orientation change.

If your activity is frontmost, and the user presses the Back button, then that is generally considered to be a request to terminate the activity. The next time the user starts your activity, they will not normally expect it to resume precisely as though it had never terminated. In this situation, onSaveInstanceState and onRestoreInstanceState are not called.

If your activity is frontmost, and the user launches some other activity on top of it, then your onSaveInstanceState methods will be called along with onPause. If the user returns to your activity while it is still running, then onResume will be called as usual, but there is no need to call onRestoreInstanceState.

If, however, the system ran short of memory while your activity was not frontmost, it can be killed without the user noticing. Then, when the user returns to it, it will be relaunched, and onRestoreInstanceState will be called so you can make it look like you were running all along.

Also if the device changes between portrait and landscape orientation while your activity is frontmost, the default action, if your code does not specifically handle this itself, is for the system to kill and relaunch you. Provided your layouts are properly designed to handle both cases, this is often good enough.

Note that what constitutes “instance state” is entirely up to you. For example, a map viewer app might always revert to a fully-zoomed-out view every time the user launches it. So the scroll position and zoom magnification would be saved and restored as part of the instance state. Or you might decide that the app will remember its scroll offset and zoom magnification every time it is quitted and relaunched regardless, in which case save/restore instance state doesn’t have anything to do at all. As the writer of the app, it’s your choice.

You Can’t Kill Threads

[edit | edit source]

Note that it is not possible for one thread to kill or abort another through the Java API: the relevant Thread.stop/destroy methods are not implemented, because of the risk they can leave the Dalvik VM in an inconsistent state.

This also applies to classes that build on Thread, like AsyncTask: once the doInbackground method starts executing on the background thread, it will run to completion, regardless of whether you pass true to cancel or not.

Of course, native code can bypass this restriction, with all the dangers that implies. And also, processes can always be killed in their entirety, because once the entire process state is gone, we don’t care about its consistency.

Writing Android applications in Java

[edit | edit source]

Android applications are primarily written in the Java programming language, and are designed to run in isolation from each other so as to maintain a secure operating environment. Each application runs in its own instance of the Dalvik Virtual Machine and under its own Linux user.

In contrast to most (if not all) other mobile platforms, Android truly supports multitasking/threading; Thus multiple applications can run at the same time and each application can perform multiple operations at the same time. This means that the user can have its mail/news/et.c. application open and get notified when there arrives a new mail instead of constantly having to check for this herself.

Android applications are delivered in form of .apk-files (zip-files signed with jartool, just with another suffix). These packages hold all of the files needed for the application to operate, there is the classes.dex-file which is the file that gets executed inside the Dalvik VM, and the AndroidManifest.xml which is a binary representation of the plain-text XML Manifest. Often there is other resources as well, as drawables (images/sprites/icons) and XML-layouts.

The supported IDE for writing android Application is Eclipse, but it is by no means mandatory to use it.

The Android (Java) applications themselves consists of several classes that subclasses the Intent and Activity classes including Services and content providers.

Android applications are compiled into a .dex file format binary and is then packaged into an apk (zip archive) file.

Alternative programming languages

[edit | edit source]

People write programs for Android in many languages.

We discuss writing programs for Android in JavaScript, HTML5, and CSS3 in another chapter of this book -- Android/PhoneGap.

Scripting Layer for Android (SL4A) [3] supports many scripting languages.

Some people write Python apps for Android using Python For Android [4] together with SL4A.

Other people write Python apps for Android using Kivy [5].

Alternative IDEs

[edit | edit source]

A few people develop Java applications using an editor and compiler that runs on the Android device itself. We discuss the Terminal IDE in a later chapter -- Android/Terminal IDE.

Utility libraries

[edit | edit source]

Some libraries which may help with coding; for testing libraries and systems, see the testing chapter.

  • ActionBarSherlock - supports modern (4.x) action-bar style interfaces on earlier Android releases, allowing one app to serve all versions.
  • AndroidAnnotations - code generator that allows the programmer to avoid having to write much of the boilerplate Android UI and asynchronous code.
  • SlidingMenus - provides for the 'slide panel right to reveal a menu' functionality.
  • android-query - simplifies UI and asynchronous coding.

Tutorials

[edit | edit source]


UserProfile class :-

package com.mock.denethanjanaperera.mymock;

import android.provider.BaseColumns;

public final class UserProfile {

   private UserProfile(){
   }
   public class Users implements BaseColumns{
       public final static String TABLE_NAME = "UserInfo";
       public final static String COLUMN_USERNAME = "username";
       public final static String COLUMN_PASSWORD = "password";
       public final static String COLUMN_GENDER = "gender";
       public final static String COLUMN_DOB = "dateofBirth";
   }


}


DBhelper class :-

package com.mock.denethanjanaperera.mymock.database;

import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;

import com.mock.denethanjanaperera.mymock.User; import com.mock.denethanjanaperera.mymock.UserProfile;

import java.util.ArrayList;

public class DBHelper extends SQLiteOpenHelper {

   private final static String DATABASE_NAME = "UserInfo.db";
   public DBHelper(Context context) {
       super(context, DATABASE_NAME,null, 1);
   }


   @Override
   public void onCreate(SQLiteDatabase sqLiteDatabase) {
       String CREATE_TABLE = "CREATE TABLE " + UserProfile.Users.TABLE_NAME +" (" +
               UserProfile.Users._ID + " INTEGER PRIMARY KEY," +
               UserProfile.Users.COLUMN_USERNAME + " TEXT," +
               UserProfile.Users.COLUMN_DOB + " TEXT," +
               UserProfile.Users.COLUMN_GENDER + " TEXT," +
               UserProfile.Users.COLUMN_PASSWORD + " TEXT )";
       sqLiteDatabase.execSQL(CREATE_TABLE);
   }
   @Override
   public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
   }
   public Long addInfo(String username, String password){
       SQLiteDatabase sqLiteDatabase = getWritableDatabase();
       ContentValues cv = new ContentValues();
       cv.put( UserProfile.Users.COLUMN_USERNAME, username);
       cv.put( UserProfile.Users.COLUMN_PASSWORD, password );
       Long rowId = sqLiteDatabase.insert(UserProfile.Users.TABLE_NAME, null,cv);
       return rowId;
   }


   public int updateInfo(String userId, String username, String password, String dob, String gender ){
       SQLiteDatabase sqLiteDatabase = getWritableDatabase();
       ContentValues cv = new ContentValues();
       cv.put( UserProfile.Users.COLUMN_USERNAME, username);
       cv.put( UserProfile.Users.COLUMN_PASSWORD, password );
       cv.put( UserProfile.Users.COLUMN_GENDER, gender);
       cv.put( UserProfile.Users.COLUMN_DOB, dob);
       String select = UserProfile.Users._ID + " = ?";
       String args[] = {userId};
       int count = sqLiteDatabase.update(UserProfile.Users.TABLE_NAME, cv, select,args);
       return count;
   }
   public ArrayList readAllInfo(){
       SQLiteDatabase sqLiteDatabase = getReadableDatabase();
       String[] projection = {
               UserProfile.Users._ID,
               UserProfile.Users.COLUMN_USERNAME,
               UserProfile.Users.COLUMN_DOB,
               UserProfile.Users.COLUMN_GENDER,
               UserProfile.Users.COLUMN_PASSWORD
       };
       String sortOrder = UserProfile.Users._ID + " DESC";
       Cursor cursor = sqLiteDatabase.query(
               UserProfile.Users.TABLE_NAME,
               projection,
               null,
               null,
               null,
               null,
               sortOrder
       );
       ArrayList<User> list = new ArrayList<>();
       if (cursor.getCount() > 0){
           while(cursor.moveToNext()){
               User newUser = new User();
               int id = cursor.getInt(cursor.getColumnIndexOrThrow(UserProfile.Users._ID));
               String user = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_USERNAME));
               String date = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_DOB));
               String gen = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_GENDER));
               String pass = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_PASSWORD));
               newUser.setUserId(id+"");
               newUser.setUserName(user);
               newUser.setDateOfBirth(date);
               newUser.setGender(gen);
               newUser.setPassword(pass);
               list.add(newUser);
           }
       }
       return list;
   }
   public ArrayList readAllInfo(String userId, String userName){
       String selection;
       String[] args = {""};
       if(userId == null){
           selection = UserProfile.Users.COLUMN_USERNAME + " LIKE ?";
           args[0] = userName;
       }
       else
       {
           selection = UserProfile.Users._ID + " = ?";
           args[0] = userId;
       }
       SQLiteDatabase sqLiteDatabase = getReadableDatabase();
       String[] projection = {
               UserProfile.Users._ID,
               UserProfile.Users.COLUMN_USERNAME,
               UserProfile.Users.COLUMN_DOB,
               UserProfile.Users.COLUMN_GENDER,
               UserProfile.Users.COLUMN_PASSWORD
       };


       String sortOrder = UserProfile.Users._ID + " DESC";
       Cursor cursor = sqLiteDatabase.query(
               UserProfile.Users.TABLE_NAME,
               projection,
               selection,
               args,
               null,
               null,
               sortOrder
       );
       ArrayList<User> list = new ArrayList<>();
       if (cursor.getCount() > 0){
           while(cursor.moveToNext()){
               User newUser = new User();
               int id = cursor.getInt(cursor.getColumnIndexOrThrow(UserProfile.Users._ID));
               String user = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_USERNAME));
               String date = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_DOB));
               String gen = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_GENDER));
               String pass = cursor.getString(cursor.getColumnIndexOrThrow(UserProfile.Users.COLUMN_PASSWORD));
               newUser.setUserId(id+"");
               newUser.setUserName(user);
               newUser.setDateOfBirth(date);
               newUser.setGender(gen);
               newUser.setPassword(pass);
               list.add(newUser);
           }
       }
       return list;
   }
   public int deleteInfo(String username){
       SQLiteDatabase sqLiteDatabase = getReadableDatabase();
       String select = UserProfile.Users._ID + " = ?";
       String [] args = {username};
       int deleteRows = sqLiteDatabase.delete(UserProfile.Users.TABLE_NAME, select, args);
       return deleteRows;
   }
   }



Home class :-

package com.mock.denethanjanaperera.mymock;

import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast;

import com.mock.denethanjanaperera.mymock.database.DBHelper;

import java.util.ArrayList;

public class Home extends AppCompatActivity {

   private Button login, reg;
   private EditText uname, password;
   private DBHelper dbHelper;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_home);
       dbHelper = new DBHelper(this);
       login = findViewById(R.id.btnLogin);
       reg = findViewById(R.id.btnReg);
       uname = findViewById(R.id.etHUname);
       password = findViewById(R.id.etHPassword);


       login.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               ArrayList<User> list = dbHelper.readAllInfo();
               for(User u : list){
                   if(u.getUserName().equals(uname.getText().toString())){
                       if(u.getPassword().equals(password.getText().toString())){
                           Intent intent = new Intent(Home.this, ProfileManagement.class);
                           intent.putExtra("id", u.getUserId());
                           startActivity(intent);
                       }else{
                           Toast.makeText(Home.this, "Invalid Username and Password", Toast.LENGTH_SHORT).show();
                       }
                   }
               }
           }
       });


       reg.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               String user = uname.getText().toString();
               String passwrd = password.getText().toString();
               if ((user.isEmpty() || passwrd.isEmpty())) {
                   Toast.makeText(Home.this, "Enter Registration Info", Toast.LENGTH_SHORT).show();
               } else {
                   dbHelper.addInfo(user, passwrd);
                   Toast.makeText(Home.this, "User Registered!", Toast.LENGTH_SHORT).show();
               }
           }
       });


   }

}


profilManagemant class :-


package com.mock.denethanjanaperera.mymock;

import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioButton;

import com.mock.denethanjanaperera.mymock.database.DBHelper;

import java.util.ArrayList;

public class ProfileManagement extends Activity {

   private Button update;
   private EditText uname, dob, pass;
   private RadioButton male, female;
   private DBHelper dbHelper;
   private String userId;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_profile_management);
       dbHelper = new DBHelper(this);
       Intent intent = getIntent();
       userId = intent.getStringExtra("id");
       uname = findViewById(R.id.etUuser);
       dob = findViewById(R.id.etUdob);
       pass = findViewById(R.id.etUdob);
       update = findViewById(R.id.btnUpdate);
       male = findViewById(R.id.radioMale);
       female = findViewById(R.id.radioFe);
       ArrayList<User> list =  dbHelper.readAllInfo(userId, null);
       for (User u : list){
           uname.setText(u.getUserName());
           pass.setText(u.getPassword());
           dob.setText(u.getDateOfBirth());
           if(u.getGender() != null){
               if(u.getGender().equals("Male")){
                   male.setChecked(true);
               }
               else
               {
                   female.setChecked(true);
               }
           }
       }
       update.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               Intent intent = new Intent(ProfileManagement.this, EditProfile.class);
               intent.putExtra("id", userId);
               startActivity(intent);
           }
       });
   }

}


EditProfile class :-


package com.mock.denethanjanaperera.mymock;

import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast;

import com.mock.denethanjanaperera.mymock.database.DBHelper;

import java.util.ArrayList;

public class EditProfile extends AppCompatActivity {

   private Button edit, delete, search;
   private EditText uname, dob, pass;
   private RadioGroup radioGroup;
   private RadioButton male, female;
   private String gender;
   private DBHelper dbHelper;
   private String userId;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_edit_profile);


       dbHelper = new DBHelper(this);
       Intent intent = getIntent();
       userId = intent.getStringExtra("id");
       Toast.makeText(EditProfile.this, "User Id: " + userId, Toast.LENGTH_SHORT).show();
       uname = findViewById(R.id.etEUser);
       dob = findViewById(R.id.etEdob);
       pass = findViewById(R.id.etEpassword);
       edit = findViewById(R.id.btnEdit);
       delete = findViewById(R.id.btnDelete);
       search = findViewById(R.id.btnSearch);
       radioGroup = findViewById(R.id.radio);
       male = findViewById(R.id.radEmale);
       female = findViewById(R.id.radEfemale);
       ArrayList<User> list = dbHelper.readAllInfo(userId, null);
       if (!list.isEmpty()) {
           for (User u : list) {
               uname.setText(u.getUserName());
               pass.setText(u.getPassword());
               dob.setText(u.getDateOfBirth());
               if (u.getGender() != null) {
                   if (u.getGender().equals("Male")) {
                       male.setChecked(true);
                   } else {
                       female.setChecked(true);
                   }
               }
           }
       }


       radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
           @Override
           public void onCheckedChanged(RadioGroup radioGroup, int view) {
               if(view == R.id.radEfemale){
                   gender = "Female";
               }
               else{
                   gender = "Male";
               }
           }
       });
       search.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               ArrayList<User> urs = dbHelper.readAllInfo(null, uname.getText().toString());
               for (User u : urs){
                   userId = u.getUserId();
                   uname.setText(u.getUserName());
                   pass.setText(u.getPassword());
                   dob.setText(u.getDateOfBirth());
                   if(u.getGender() != null){
                       if(u.getGender().equals("Male")){
                           male.setChecked(true);
                       }
                       else
                       {
                           female.setChecked(true);
                       }
                   }
               }
           }
       });
       edit.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               String userName = uname.getText().toString();
               String date = dob.getText().toString();
               String pwrd = pass.getText().toString();
               if(female.isChecked()){
                   gender = "Female";
               }
               else{
                   gender = "Male";
               }
               int count = dbHelper.updateInfo(userId, userName, pwrd, date, gender);
               if(count > 0){
                   Toast.makeText(EditProfile.this, "Updated!", Toast.LENGTH_SHORT).show();
               }
               else{
                   Toast.makeText(EditProfile.this, "Error in data Sending!", Toast.LENGTH_SHORT).show();
               }
           }
       });


       delete.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               int count = dbHelper.deleteInfo(userId);
               if(count > 0){
                   Toast.makeText(EditProfile.this, "Deleted!", Toast.LENGTH_SHORT).show();
               }
               else{
                   Toast.makeText(EditProfile.this, "Something went wrong!", Toast.LENGTH_SHORT).show();
               }
           }
       });
   }

}


User class :-

package com.mock.denethanjanaperera.mymock;

public class User {

   private String userId;
   private String userName;
   private String dateOfBirth;
   private String gender;
   private String password;
   public User(){
   }
   public String getUserId() {
       return userId;
   }
   public void setUserId(String userId) {
       this.userId = userId;
   }
   public String getUserName() {
       return userName;
   }
   public void setUserName(String userName) {
       this.userName = userName;
   }
   public String getDateOfBirth() {
       return dateOfBirth;
   }
   public void setDateOfBirth(String dateOfBirth) {
       this.dateOfBirth = dateOfBirth;
   }
   public String getGender() {
       return gender;
   }
   public void setGender(String gender) {
       this.gender = gender;
   }
   public String getPassword() {
       return password;
   }
   public void setPassword(String password) {
       this.password = password;
   }



}


Android SDK

The Android Software Developer Kit is what you use to develop Android applications. You get it from here.

The difference between a GUI and a command line

[edit | edit source]

The SDK includes a plugin for use with the Eclipse IDE, as well as command-line tools that can be invoked via an Ant build script.

Most developers seem to prefer GUI tools. However, this makes it difficult to choose a different IDE or text editor. Also, complex builds require the execution of custom scripts at build time, and only the command line offers the flexibility to cope with this.

Native Code

[edit | edit source]

Android apps do not have to be written entirely in Java. It is possible to include C/C++ code and compile it with the Native Development Kit (NDK). This code then interfaces with the Java code through the Java Native Interface (JNI), of which the Dalvik virtual machine includes a (mostly) complete implementation.

When you might want to use the NDK:

  • Port C/C++ code from another platform.
  • Java code is too slow—but beware: “premature optimization is the root of all evil” (variously attributed to Donald Knuth or Tony Hoare). Do actual testing to determine where the slow parts of your code are, before trying to speed them up: the performance bottlenecks are often not where you think they are.
  • Allocate more memory than the 20MB or so permitted for your Java heap.

Reasons not to use the NDK:

  • Your compiled code ends up being architecture-specific. Sure, you can include alternative versions for, say, both ARM and MIPS to cover the likely bases for now, but what happens if Intel’s Atom efforts pay off, and future devices make more use of x86 chips?
  • Debugging is hard. Instead of seeing a nice Java exception traceback in the logcat, pointing to the exact source line where the error occurred, you get a crash register dump. However, it is possible to narrow down the point of the error through the standard expedient of outputting debug messages at strategic points in the code (see Logging, below).

Better JNI glue

[edit | edit source]

The standard Android-provided jni.h makes it slightly awkward to call JNI routines. You can improve this by processing jni.h through JNIGlue, which generates more convenient static inline wrapper routines. For example, instead of

const jclass SystemClass = (**env).FindClass(env, "java/lang/System");, you can do

const jclass SystemClass = JNFindClass(env, "java/lang/System");

Logging

[edit | edit source]

In Java code, you can write messages to the logcat using either the Log class, or simply write to the standard Java System.err diagnostic stream or System.out output stream. In the first case, you can set the logging level and the “tag” string used to prefix your messages; in the second case, the level is always warning (W), and the tag is always “System.err”, and in the last case, the level is info (I) and the tag is “System.out”.

In native code, the usual stdio streams of stdout and stderr are defined, but do not work—certainly trying to write to stdout or stderr not only does not display messages anywhere, it can lead to strange crashes elsewhere in your code.

Instead, you can use the native logging library. There is no public documentation for this, but the routines are in the android/log.h header file that you can include in your C/C++ code. Also in your Android.mk for building the native code, add -llog to the definition of your LOCAL_LDLIBS variable.

Messages written to the logcat in this way look like this:level/tag(pid): text or, like this: W/System.err( 1288): test app successfully started

The log levels are enumerated in android/log.h, and the corresponding single-letter codes that appear in the messages, are:

code char
ANDROID_LOG_UNKNOWN message does
not appear
ANDROID_LOG_DEFAULT
ANDROID_LOG_VERBOSE V
ANDROID_LOG_DEBUG D
ANDROID_LOG_INFO I
ANDROID_LOG_WARN W
ANDROID_LOG_ERROR E
ANDROID_LOG_FATAL F
ANDROID_LOG_SILENT S

GCC Extensions

[edit | edit source]

The C and C++ compilers included with the NDK are built from GCC, so you have access to the usual GCC capabilities. For example, you can do the C99 #include <stdbool.h> and use the bool type, with values true and false, in conditional expressions in your code. You can enable more general C99 capabilities by putting a line like this in your Android.mk:

LOCAL_CFLAGS += -std=c99


A possible example of setting up Android development in Ubuntu

[edit | edit source]

This is a walkthrough of a typical setup of Android development in Ubuntu 18.04 LTS.


Then following the Android Studio for Linux installation instructions ( https://developer.android.com/studio/install#linux ) ( https://linuxize.com/post/how-to-install-android-studio-on-ubuntu-18-04/ ) ( https://askubuntu.com/questions/634082/how-to-install-android-studio-on-ubuntu )

One approach to installing Android Studio: Open a terminal window in Ubuntu 18.04 with Ctrl+Alt+T then run

    sudo add-apt-repository ppa:lyzardking/ubuntu-make
    sudo apt update
    sudo apt install ubuntu-make
    umake android

then add a shortcut to the "~/.local/share/umake/android/android-studio/bin/studio.sh" file to the desktop.


One approach to installing Android Studio: (Alas, doesn't yet work with Ubuntu 18.04) Open a terminal window in Ubuntu with Ctrl+Alt+T then run

    snap install ubuntu-make --classic
    umake android

Another approach to installing Android Studio:

  1. From https://developer.android.com/studio download "Android Studio for Linux".
  2. [FIXME: then what? See https://askubuntu.com/questions/634082/how-to-install-android-studio-on-ubuntu for more details.]
  3. ...



A possible example of setting up Android development

[edit | edit source]

This is a walkthrough of a typical setup of Android development.

The IDE will be Eclipse because it is so flexible. The latest edition is downloaded, it is the one for Java development and plugin development) This is unzipped in a user chosen directory, and we try to execute it. Hopefully it does, e.g. fortunately we didn't choose the 64 bit version of eclipse when the 32 bit of Mint was installed.

A little Hello World java program will be written, to see if the eclipse IDE has a working Java compiler. At least the Java runtime should be working, because Eclipse wouldn't run otherwise. If not, go to synaptic or the mint applications loader and find a java runtime, e.g. openjdk 7 in 2013.

The next step is to get the android SDK. In November 2013, it was the r22 build, found at [6], and this is downloaded and unzipped. It might not work if you have a 64 bit Mint environment though, maybe a Red Hat environment would. To get it working in this case, you have to execute the emulator and see what it says.

If it says something like there is no file or directory called .. tools/emulator , then likely you don't have a 32 bit environment installed, so get the libc 32 bit environment from the Synaptic Package Manager.

If you aren't the administrator, open a terminal first, run su, then type the administrator password (the first user created when Linux Mint was installed). Then type synaptic, and then install libc6-i386.

Keep running the emulator and installing the libraries it needs, e.g. lib32stdc++6.

Something else won't work either, which is the aapt build tool in the build-tools folder from the Android SDK, which requires the 32 bit version of the zlib library, lib32z1. It shows up later, when you find that the generated R class for your android application doesn't generate automatically when editing your project, and everything R.xx is underlined in red.

Run the ./tools/android exec, it should actually work even if there is no 32 bit libs. This is to get your selected Android platform/platforms loaded, e.g. 4.4 kitkat, or 4.3 jelly bean, or 2.2 froyo, and also down in the Extras folder at the bottom, the latest android support package is there, because this gives you backward compatibility of an application written with a set of API's (ex:, using an Android Froyo API in an Android Honeycomb application, if the support library is included in your project. Also you need the ARM system image as a minimum, and maybe the Intel one, but each image makes the download longer, and you can easily end up with 1 GB of development tools (or more) just to get your Hello World application on android.

Install the plugin in Eclipse, by running it from your specified path in Windows or Mac OS X and going to the Help option, and install the required packages, then at the top, add in a new software repository by clicking the "Add.." button, and pasting this URL in the "Location" field, [7]. Then choose the Android Development Plugin (or ADT, for shorthand), and the NDK, if you want to compile native C or C++ code in your Android applications

After this, create a blank activity in your Android project, and run it

This requires setting up by clicking under the "Window" menu item in Eclipse and "Android Virtual Device Manager", and then choose a low grunt virtual device like Nexus One, so that you don't grow too old waiting for the virtual device to start. Click the second tab in the AVD manager, and then select Nexus One from the list on the left, and the "Create AVD" button on the right becomes enabled, and then it creates a virtual device with the ARM system image and whatever level of Android API, but beware of using "Native OpenGL" because the 32 bit libraries for these may not be loaded, and it will still let you create your AVD, but it will not work.

After this, you can try to get a calculator project going, but don't do the programming one from Google, just use the layout editor for the MainActivity.xml file, as you can have 4 buttons in one horizontal layout, copy the layout, and paste it to get the other rows. You can also set the onClick method for all the buttons by Ctrl-clicking to select more than one of the buttons, and then right click in the Outline view over a highlighted button, and choose the menu item "Other property.." and choosing "OnClick", and typing the method name to receive e.g. onClick buttonPressed. Then create a public method in the MainActivity.java file, called public void buttonPressed(View v), and handle any button clicks by identifying the button using the button's label. (Cast the v passed in into a Button object first)

Happy Android programming, and may the Google Play Store success be with you.


API Overview

API Overview

[edit | edit source]

Here is an overview of some of the most important package namespaces and classes in the Android API.

  • android.content.Context is the base class for android.app.Activity and other similar classes. Here you will find important methods for accessing application package data and the application environment.
  • android.view.View is the base class for all widgets. The android.view namespace defines non-widget-specific utility classes.
  • android.widget is the namespace where you will find specific widget implementations and related classes.
  • android.graphics is the namespace for the 2D graphics API; Canvas is the drawing context, Paint specifies settings like what colour, drawing mode, text settings etc to use, and Typeface is how you specify fonts.
  • android.content.Intent is the basic IPC mechanism.
  • android.os contains various useful classes to do with the UI framework: Handler for feeding tasks/messages to a Looper, and AsyncTask for running tasks on background threads that need coordination with the UI. Here you will also find Parcel and Bundle, which are different ways of serializing/deserializing data, mainly meant to be used for saving/restoring UI state.

API Versions

[edit | edit source]

New versions of Android introduce new capabilities, and sometimes drop support for old ones. Each version of Android has an associated API level, and each app in its manifest declares its “minimum” and “target” SDK version (i.e. API level). The minimum API level indicates that it will not work (or at least has not been tested) with any older Android version, and will likely crash. The target API level is used to adjust some automatic behaviour of the Android system, to match with expectations of the app.

Dates and Times

[edit | edit source]

Java (and Android) offer a confusing plethora of different classes to do with dates and times. This section will try to shed some clarity on the thicket.

  • java.util.GregorianCalendar — this is the main class to be used for constructing and manipulating dates and times. Note that many important methods come from its java.util.Calendar superclass.
  • java.util.TimeZone — gives you information about the current system time zone as well as other available time zones that the system knows about.
  • java.util.Date — this remains an essential class, even though most of its constructors and methods are deprecated. You can obtain one of these from a Calendar object by calling its getTime method. The only reason you need objects of this class is because the formatting routines don’t work with Calendar objects!
  • java.text.DateFormat — this is where you will find the functions for doing locale-dependent date formatting. Note they only work on Date objects, not Calendar or GregorianCalendar objects.
  • android.text.format.DateFormat — convenience routines for obtaining suitable java.text.DateFormat objects for the user’s current locale/format settings. Also lets you format dates according to custom formats.
  • java.text.SimpleDateFormat — lets you create custom date formats.
  • java.util.SimpleTimeZone — not sure what the point of this is. Why would you want to define your own time zones?

2D Graphics Concepts

[edit | edit source]

Overview of the Android 2D graphics API.

Canvas

[edit | edit source]

A Canvas provides the basic drawing context. On-screen drawing of most widgets is done in their override of View.onDraw, which gets passed a Canvas into which they render their display. Alternatively, if you want to do drawing at times dictated by your app, rather than by the system, then you can use a SurfaceView.

You can also create your own Canvas objects to do off-screen rendering. You do this by wrapping them around a Bitmap object that you set up.

The normal Canvas coordinate system has the X-coordinate increasing from left to right, and the Y-coordinate increasing from top to bottom, with (0, 0) at the top-left of the drawing area. However, you can alter this by transforming drawing through a Matrix.

Bitmap

[edit | edit source]

A Bitmap is the container for an array of pixels. You can either supply the pixels (in which case the Bitmap is immutable—i.e. the graphics API will not draw into them), or have the graphics API allocate the space for them itself (in which case the Bitmap is mutable—can be drawn into). In the latter case, you must remember to call the recycle method when you have finished with the Bitmap; contrary to the documentation saying “This is an advanced call, and normally need not be called”, you will be quite likely to hit “out of memory” errors if you create lots of mutable bitmaps without doing this.

Colour Values

[edit | edit source]

Pixel colours are specified in many places as a 32-bit integer with alpha, red, blue and green components each taking up 8 bits. Assuming that “alpha”, “red”, “blue” and “green” are each integers in the range 0 .. 255, you can build them into a colour value as follows:

    int color = 
          alpha >> 24 
        | red >> 16 
        | green >> 8
        | blue;

The API doesn’t enforce whether alpha is premultiplied or not. But the Porter-Duff transfer modes will really only give correct results with premultiplied alpha.

Paint

[edit | edit source]

A Paint object contains various settings used to control drawing of some object into a Canvas: the colour to use, what transfer modes and filtering effects to apply, Path-rendering settings, and text settings.

Typeface

[edit | edit source]

A Typeface is how you specify a font. Note that there is only a limited set of preinstalled system fonts, with no (official) provision for adding more. If you want more fonts, you will have to include them with your app, or access them from a user download area, or something.

A Path is the representation of a scalable geometric object. This could be as simple as a single circle, oval or rectangle, or it could be more complicated and consist of multiple segments made out of cubic and quadratic Bézier curves and straight-line segments. A path can be filled or stroked. It can also be used for clipping drawing in a Canvas.

Note that there is no equivalent to the PostScript pathforall operator: there is no way to read back the components of the definition of a Path. If you need to maintain this information, you will need to define your own geometry object, which will convert its contents to a Path on demand.

Shader

[edit | edit source]

A Shader gives you more advanced options for drawing with a Paint. By default a paint renders an area of a single flat colour, but setting the right kind of Shader gives you options for applying gradient fills, filling with a bitmap image, and combinations of these.

Why Do My Graphics Look So Jaggy?

[edit | edit source]

The graphics API includes options for anti-aliasing, but these are not enabled by default (why not? performance issues on older hardware?). To make things look good, use Paint.setAntiAlias for rendering smooth geometry, and Paint.setFilterBitmap for smooth scaling of bitmaps.

See Also

[edit | edit source]

The video Android Graphics and Animations presented by Romain Guy and Chet Haase at the Devoxx 2010 Java conference gives an overview of the graphics API and how to do animations.

"Loading Bitmaps from the Gallery" from Android Code Fragments.

2D Versus 3D Graphics

[edit | edit source]

Android uses quite different APIs for 2D versus 3D graphics. The 2D graphics API is built on the Skia graphics library that Google acquired along with the company that created it. The 3D graphics API is OpenGL-ES, the “embeddable” subset of the well-known cross-platform OpenGL 3D graphics API.

The 2D graphics engine is completely integrated into the UI, and is used for all on-screen drawing of windows, widgets etc. For example, to create your own custom widget, it is easy enough to subclass View, do whatever custom rendering you want in its onDraw method and event handling in onTouchEvent.

3D graphics is not quite so seamlessly integrated. The easiest way to do an on-screen 3D display is to subclass GLSurfaceView, in addition to which you need to provide a custom subclass of GLSurfaceView.Renderer which does the actual setup and drawing.

A GLSurfaceView offers two “render modes”, selected by calling setRenderMode: RENDERMODE_CONTINUOUSLY in which your Renderer is continually called to render the scene (the default), and RENDERMODE_WHEN_DIRTY where your Renderer is only invoked when you do a requestRender. Note this is quite different from normal 2D widgets, which are only (re)drawn in response to an invalidate call.

Note the difference in coordinate systems: in the 2D graphics API, y-coordinates increase downwards, while in OpenGL they increase upwards.

Graphics Primitives

[edit | edit source]

The available graphics primitives are more limited in 3D. Whereas in 2D you have complex entities like text, Paths, Drawables and Pictures, in 3D you just have points, lines and triangles. In particular, all object surfaces must be constructed out of triangles; curved surfaces are approximated to a limited extent by subdividing into smaller triangles, but more importantly by enabling smooth shading, which fools the eye into seeing continuous gradations instead of angular facets.

It is possible to use 2D graphics in 3D, by rendering a 2D image to a Bitmap, which is then used as a texture on an object surface in 3D.

Also note that OpenGL is strictly an on-screen real-time rendering API: it defines no “file format” for storing scenes/models in persistent storage, and it doesn’t support CPU-intensive functions like ray-tracing, radiosity and the like.

OpenGL-ES Versus Regular OpenGL

[edit | edit source]

OpenGL-ES leaves out various capabilities of OpenGL which were deemed to have too high an overhead of implementation in an embedded environment. OpenGL-ES 1.1 is based on OpenGL 1.5, among the differences being:

  • No Begin/End grouping and associated calls for individually specifying vertex info: instead, you must use the xxxPointer calls to pass entire buffers of coordinates at a time, and then draw them with DrawArrays (selecting contiguous subarrays) or DrawElements (selecting individual array elements by index).
  • Only 2D textures, no 3D or 1D.
  • No support for polygons other than triangles.
  • ES adds the option to specify coordinates etc as fixed-point values (calls with an x suffix) instead of floating-point (f suffix).

android.opengl Versus khronos.opengles

[edit | edit source]

Android provides two alternative styles of OpenGL API calls: the khronos.opengles calls are method calls off explicit GL objects, whereas the android.opengl calls are all static, and implicitly refer to a current GL context.

It looks like the latter are now the preferred form, since the addition of support for OpenGL-ES 2.0 in API level 8 (Android 2.2) was done only in the android.opengl style, not the khronos.opengles style.

OpenGL-ES 1.x Versus 2.0

[edit | edit source]

Android 2.2 introduced support for OpenGL-ES 2.0, which is not backward-compatible with OpenGL-ES 1.1 or 1.0. However, the OpenGL-ES 1.x APIs remain available for existing code. When you write new code, you have to decide which one to use.

The main difference is that OpenGL-ES 2.0 drops support for the fixed-function pipeline. That is, all the traditional OpenGL material, lighting and matrix functions are no longer available (these are deprecated in more recent versions of regular OpenGL, too). In their place, you write custom vertex shaders and fragment shaders which give you total control over how your materials, lighting and transformations will work.

EGL is an API for giving direct control over creation of OpenGL contexts that render to on-screen windows, offscreen pixmaps, or additional graphics-card memory. GLSurfaceView provides a simple wrapper to save you using it directly, but you can still do so in cases where this isn’t flexible enough (like when you want to do offscreen rendering).

See Also

[edit | edit source]

khronos.org (Documentation on OpenGL, OpenGL-ES and EGL)

In-app billing

[edit | edit source]

Some developers use the Android in-app billing API to sell photos and other media files, game levels, and premium services.

The Android in-app billing API uses the same backend as buying an app in Google Play.

Google Play handles the checkout details so the application never directly processes any financial transactions—so individual applications don't have to deal with PA-DSS requirements, including PCI DSS requirements.

The Android in-app billing API supports a free trial period for automated billing of recurring subscriptions.

Other developers use Stripe or PayPal for billing.

See also

[edit | edit source]

In no particular order:

"Android In-app billing Phonegap 1.0" [8]

"Android In-app purchases with Phonegap" [9]

"Callback In App Billing Plugin" [10]

"Google Play for Developers: Subscriptions or recurring charges" [11]

"Google Play In-app Billing: Subscriptions" [12]

"Android billing library: in-app billing made simple" [13]

"Android In-App Billing Tutorial" http://stackoverflow.com/questions/8735931/android-in-app-billing-tutorial

"Simple InApp Billing / Payment tutorial" [14]

"Getting Started with Android In-app Billing" [15]

"Android Popular APIs in GitHub" [16]


PhoneGap

What is PhoneGap?

[edit | edit source]

PhoneGap allows programmers to build applications for Android and other mobile devices in JavaScript, HTML5, and CSS3. PhoneGap (also called Apache Cordova) is open source software.


Getting started with PhoneGap for Android on MacOSX

[edit | edit source]
  • brew update
  • brew install node
  • npm install -g cordova

Getting started with PhoneGap for Android on Windows

[edit | edit source]
Clipboard

To do:
describe the "new" approach that uses Android Studio, as described in recent documentation, rather than the "old" approach that uses Eclipse.


... npm install -g cordova ...

[1][2]


As of cordova-android@4.0.0, Apache Cordova has switched to Android Studio and Gradle, and switched away from Eclipse and Ant.

Following the Phonegap documention,[3][4]

These first few steps set up the standard Java development environment for Android:

  • Download and install Eclipse Classic
  • Download and install Android SDK
  • Download and install the ADT plugin

The next steps add Apache Cordova, setting up a standard JavaScript HTML CSS development environment for Android:

  • Download the latest copy of Cordova and extract its contents.
  • Setup your PATH environment variable

Plugins

[edit | edit source]

Many plugins have been developed for PhoneGap.[5][6]

One popular plugin is the Barcode plugin.

Barcode plugin

[edit | edit source]

(This section describes how to scan a barcode inside a PhoneGap app. An alternate approach scans a barcode from a normal HTML/JavaScript Web page.[7])

Several people use the Barcode plugin to work with barcodes in their PhoneGap application.[8] [9] [10] [11] [12] [13]

The standard PhoneGap barcode plugin is often all an Android application needs. If it doesn't yet support the barcode format you want to use, you may need to build your own custom PhoneGap plugin, perhaps starting with Barcode development in Java.[14]

Updating old PhoneGap code

[edit | edit source]

Sometimes old functions of PhoneGap are deprecated, and later removed from PhoneGap. Here's a few tips for application writers who want to update old code to work with the latest version of PhoneGap:

Follow the steps in the PhoneGap Upgrading Guides: "PhoneGap Upgrading Guides" Various other tweaks: "PhoneGap Android Plugins: Sometimes We Screw Up" "Updating PhoneGap"

import org.apache.cordova.api

public class InAppBillingPlugin extends CordovaPlugin {

  this.cordova.getActivity();
  []
}

Further reading

[edit | edit source]


Kivy

Kivy is an open-source library that people can use for rapid development of multi-touch GUI programs for Android and other platforms in Python.[1][2][3][4]

The "Python for Android" project, by default, uses Kivy for its user interface.

(FIXME: put a quick introduction here).

other ways to run Python code on Android

[edit | edit source]

Some Python programmers use Pygame Subset for Android (PGS4A) to build Android apps.[5][6][7][8][9]

Other Python programmers use PySide for Android.[10]

Some Python developers write Python code directly on an Android device, and directly run it on the same device without needing to access any other device to run the program.[11]

Further reading

[edit | edit source]

References

[edit | edit source]


Developing on Android

The software for the vast majority of Android applications is written and edited on a laptop PC or desktop PC.

Some people think trying to write software directly on an Android device or other tablet is so different than writing it on a PC that it requires a new programming language supported by a completely new IDE.[1]

However, there are several ways of writing and editing software directly on the Android device.[2][3] Some of them takes a very traditional approach. It supports software development using a traditional command prompt; traditional text editors such as "vi"; the standard programming language for Android (Java); and the standard compiler for that language (gcc). The only thing non-traditional is that it runs them all directly on the Android device itself. (Even that can be seen as a return to a much older tradition of developing and compiling software on the same machine that runs the software).

Termux

[edit | edit source]

Termux is an open-source Android terminal emulator and Linux environment app that works directly with no rooting or setup required.[4]

Bash script Hello World

[edit | edit source]

Here is a quick "Hello World":

Install Termux by the method you prefer.

Tap the "Termux" icon.

At the command prompt,[5]

type

nano a.bash

(I'm assuming you've done a little programming before, so you'll know when to press the Enter button at the end of the line).

Enter a quick bash script:

#!/bin/bash
echo Hello, world

Then press the Ctrl + X, "y" and Enter to save the program and quit back to the command prompt.

Execute the script:

bash a.bash

You should see the output saying "Hello, world.".

Terminal IDE

[edit | edit source]

Basic system setup

[edit | edit source]

Follow all these tutorials through EXACTLY.

They will help you compile the sample applications in ~/system/src step by step.

The best way to follow these tutorials is by logging in over telnet/ssh and then you can read and follow on the device, whilst using the computer to perform the actions.

Let's Begin..

Installing the basic system.

Click 'Install System' on the main page, and then follow the instructions.

Make sure you have enabled the Terminal IDE Keyboard in Settings>Language and Keyboard on your device. Then choose the Terminal IDE keyboard - click 'Keyboard' on the main page.

Start Terminal IDE. Click the button..

You will now be presented with a nice not-short color prompt. You can change this by typing, export PS1=#

But it does look really nice over telnet/ssh.. :-p

  • Double-tap toggles the keyboard.
  • Swipe left and right to view your 4 Terminals.
  • Long pressing the screen brings up the Terminal Chooser menu.
  • Press 'Menu' for Options

[This part is optional but highly recommended]

Run telnetd. No parameters are required. Defaults setup in ~/.bashrc. You can see what is actually being run by typing: # cat `which telnetd`

(using backquotes, not apostrophes).

Anyway - start telnetd with: # telnetd

You should now see telnetd startup info. Ending in ../system/bin/bash

You can shut telnetd down at any stage by typing [CTRL]-C on the Terminal IDE Keyboard

Now, go to your nice big computer and login via telnet. I have set the escape character to blank, as it is the same as the vim help link follow key.

[Either] $ telnet -e [THE PHONES IP] 8080

[Or - better still with adb, which is part of the Android SDK] Plug your phone into computer via USB first. In you Android's developer options turn on Developer options and check USB debugging, then

$ adb -d forward tcp:8080 tcp:8080
$ telnet -e '' 127.0.0.1 8080

['' is 2 single quotes not a double quote]

Now you should be connected to the system via telnet

[And you can connect multiple times simultaneously]

OK - let's start programming.

Using Termux to ssh to a remote server

[edit | edit source]

You can ssh from your Android device in Termux into your favorite servers. Type your username, at-sign, and domain name like so:

ssh dc@dreamhost.com

The first time you connect from a particular Android device to a particular server, you will see something like:

   Host 'dreamhost.com' is not in the trusted hosts file.
   (fingerprint md5 c4:ad:7f:ba:d8:23:d4:da:53:07:76:a2:7e:24:9d:67)
   Do you want to continue connecting? (y/n)

So type "y" and hit Enter

You must do this directly on the Android device's keyboard the first time you connect to a particular server. If you are telnetted to your phone from some other machine, you will get the error

   ssh: connection to dc@dreamhost.com:22 exited: Didn't validate host key

until you go back to the Android device and type the command directly from the Android device's keyboard.

Synchronization

[edit | edit source]

The Terminal IDE includes "rsync", which is very useful for copying files, making backups, and quickly checking if files in two places are actually the same or not. [6]

Further reading

[edit | edit source]
(Later: add a few words about using dropbearkey to generate a local private key that never leaves the smartphone, and the corresponding public key ... so we never need to type in a password to securely log in to a remote server.)[6][7]
  1. Marc Hesenius, Carlos Dario Orozco Medina, Dominikus Herzberg. "Touching Factor: Software Development on Tablets". 2012. doi: 10.1007/978-3-642-30564-1_10 .
  2. Brian Kracoff. "15 Apps for Programming on Android". 2012. Mentions "Terminal IDE" from Spartacus Rex, "AIDE", "C4droid", "Pocket Code for Android", "Algoid", etc.
  3. Eric Ravenscraft. "AIDE Is An IDE That Lets You Write And Compile Android Apps On Your Android Device".[1]
  4. "Termux". Termux. Retrieved 2024-05-12.
  5. As you can check by typing echo $SHELL; the Spartacus Rex command prompt is a normal "bash" command line. See LPI Linux Certification/Work On The Command Line for a brief introduction.
  6. a b Jason York. "Using Public Keys With Dropbear SSH Client". quote: "Now that ssh works, I can easily perform an automated rsync".
  7. [2]


Testing

Testing is a broad subject, with various methodologies combining various approaches, but it embodies attempts to ensure that the user's experience is satisfactory: intuitive, reliable, responsive, secure, etc. Practices such as Test-Driven Development and Behaviour-Driven Development are popular at the time of writing, using means such as Unit Testing and Integration Testing, but wider testing areas include checking that the user can perceive how to achieve his/her goals without frustration or cultural misunderstandings through to checking that the server against which an application runs can sustain the expected average throughput, gracefully handles peak load, is robust against attacks, and so on.

Unit Testing

[edit | edit source]

Unit tests are tests that exercise a class in isolation from (most of) the rest of the application, and form a significant element of Test Driven Development where failing tests are written to allow the application to be 'debugged into existence'.

Where the code under development is uninvolved with Android, standard Java unit test techniques apply, but where code rubs against the Android system a degree of working around (similar to that required while working with legacy code) is necessary to overcome issues that include:

  • The android.jar on the development machine (against which applications are built) throws exceptions in response to attempts to execute its methods
  • Android declares various of these exception-throwing methods as final, rendering them impossible to include in tested code directly
  • Android makes use of static methods, again impossible to test directly.

The section on Unit Testing gives various approaches that may help with these issues; you will need to evaluate how well they work for you.

The following tools may also prove useful in combination with the aforementioned approaches:

  • Robolectric - provides unit-test friendly simulations of Android classes that run on the hosting PC with a faster compile/execute cycle than running on a phone or an emulator with non-native architecture.[1]
  • Hamcrest - provides useful and readable abstractions for building assertion conditions with useful diagnostics
  • awaitility - helps with asynchronous testing
  • Object mocking libraries provides a powerful way of building mock objects
  • Dependency injection, which allows you to request objects based on interface and a mapping system rather than directly by class, so you can substitute test classes in without disrupting the compiled code. This may simplify your code, with less code to read... or make it harder to track down quite what's calling what, and it can degrade start-up time unacceptably.
    • RoboGuice
    • Spring
    • Related:
      • Android Annotations - Promising as a means of reducing boilerplate (and hence coding error), but not directly testing related. Its unit testing section mentions Robolectric, which is good.
      • Android Binding isn't quite dependency injections, but seeks to separate concerns with MVVM, and may be worth checking out.

Integration Testing

[edit | edit source]

Whereas unit testing tests classes (largely) in isolation from each other, and system testing checks that the entire assembly works in the target platform when operated as a user, integration testing checks that selected groups of parts fit together properly - in controlled conditions that are easier to set up and manipulate with direct access to the program code.

The official Android documentation tends to focus on tests based on the AndroidTestCase and its subclasses, with commentators often calling them 'unit tests'. These tests are emphatically not unit tests: since they integrate with the Android system, they are at least integration tests, but while they are too embedded and slow to execute as unit tests, they are too tightly coupled to the code itself to be system testing. (Which said, on an Intel-based development machine, the emulator boots the Intel Atom Android images in under 15s, as opposed to 40s for ARM-based images, which makes it much more useful.)

System Testing

[edit | edit source]

System tests exercise the system end-to-end: the application will be on the device and will talk to your server if you use one. They are slower to run than unit tests and are based on driving the application through the UI in a 'black box' way. They form part of Behaviour Driven Development.

Because they run on a phone, the compile/execute cycle of the unit test becomes a compile/package/install/execute cycle, but they run against the real implementation and thus don't suffer from the danger of implementation inaccuracy unit test stubs and mocks present.

The following tools may prove useful:

For random, long-running tests:

  • Monkey - a random event generator, part of the Android SDK

Some commercial tools operate on your own equipment:

  • SeeTest, EggPlant (TestPlant), Apphance

Some commercial tools operate on the tool provider's equipment:

  • DeviceAnywhere

Web testing

[edit | edit source]

Some applications are written for the browser, rather than against the SDK. For these, Selenium is supported - particularly under ICS with WebDriver.

Commercial testing

[edit | edit source]

There are commercial companies to which you can submit your applications for testing on a variety of devices, so you don't have to own all the various tablets, phones, and web-enabled refrigerators people might want to run your application on. The good ones will offer implicit consulting by trying to sell you a variety of testing approaches in addition to the ones covered in this book: security, performance, usability, and localisation. The functional testing they will be able to offer will typically be restricted to black box testing, although it's not inconceivable someone might start to offer static checking and architectural advice.

A simple web search for 'Android testing' should bring up several adverts for such companies.

Testing testing

[edit | edit source]

Tests are code, and can have bugs! Tools such as mutation testing, and condition, branch, or line coverage help determine how much of your code is actually and/or only seemingly tested.

Screenshots

[edit | edit source]

In-app screenshots can be a useful debugging tool.[2]

  1. The Intel emulator is quite impressively fast on Intel PCs running Linux, Mac, or Windows, with the proviso that at the time of writing it prevents Mac running any other virtualised machine.
  2. Android Code Fragments. "In App Screenshots".