Saturday, July 12, 2014

Android Custom Components


Creating own view subclasses gives an application precise control over the appearance and function of screen element. Below are the examples of usefulness of creating custom views

- Appplication create a completely custom-rendered view type, for e.g. a Volume control knob rendered using 2D graphics, and which resembles an analog electronic control. 
- Application could combine a group of view components into a new single components, perhaps to make something like a combo box (a combination of popup list and free entry text field), a dual-pane selector control (a left and right pane with a list in each where application can re-assign which item is in which list.) and so on
- Application could override the way that an EditText components is rendered on the screen 
- Application could capture other events like key presses and handle them in some custom way (such as for a game)

The basic approach to do this is like given below

1. Extend an existing View class or subclass with own class
2. Override some of the methods from the superclass. The superclass methods to override starte with on. for e.g. onDraw, onMeasure, onKeyDown, 
3. once above is completed, the new custom view class can be used in place of regular view

Fully customised components can be created like below 
A good example could be a sing-along-text view where a bouncing ball moves along the words so user can sign along with a karaoke machine. 

1. Extend from View. this is the most generic component from which a view can be derived. 
2. Supply a constructor which can take attributes and parameters from the XML
3. Probably create own event listeners, property accessors and modifiers, and possibly more sophisticated behavior
4. Most certainly override the onMeasure() and likely override the onDraw. The default onDraw will do nothing, and the default onMeasure() will always set the z size of 100x100 - Application may require more than this. 
5. Other on… methods are overridden as required. 

The onDraw method delivers application a Canvas upon which the application can draw anything wanted. 2D graphics or standard or custom components, styled text etc. But this can't render the 3D graphics, IF want the 3D graphics, needs to override the SurfaceView instead of View and draw from a separate thread. 

onMeasure is little more involved. This is a critical contract between the application component and its container. 

Below given is the logic that goes into the onMeasure method 

- The overridden onMeasure method is called with width and height measure specifications (widthMeasureSpec and heightMeasureSepc) parameters both representing the dimensions. These should be treated as requirements for the restrictions on the width and height measurements that component should produce. 
- The new Custom component's onMeasure method should calculate a measurement width and height which will be required to render the component. It should try to stay within the specifications passed in, although it can choose to exceed them (in this case, parent can choose what to do such as clipping, scrolling, throwing an exception or asking onMeasure to try again perhaps with a different measurement specifications) 
- Once the width and height are calculated, the setMeasuredDimension(int width, int height) method must be called with the calculated measurements. Failure to do this will result in exception being thrown. 

Rerefences: 

Android - Saving Data

The principal file storage options in Android are the ones below 

- Saving Key value pairs of simple data types in preference file.
- Saving Arbitrary files in Android file system. 
- Using Database managed by SQlite 

Key value pair saving can be via SharedPreferences API. Internally, system keeps a file to store the key value pairs. This shared preference can be made a private or shared. 

Below is a code snippet to invoke the SharePreference call from a Fragment. The context of Fragment is Activity 

Context context = getActivty(); 
SharedPreferences preferences = context.getSharedPreferences(getString(R.strings.preference_file_key),Context.MODE_PRIVATE);

While naming the preference file, it is better to name with the reverse DNS name. For e.g. com.example.PREFERENCE_FILE_KEY 

Alternatively, if application needs only one preference file, then getPreferences cane be called like below 

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

If the Application specifies the preference file name access as MODE_WORLD_READABLE, MODE_WORLD_WRITABLE, it will be accessible by other application if the preference file name is know to those apps. 

Below is the code to write to SharedPreferences = getActivity().getPreferences()

SharedPreferences preferences = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor = preferences.edit();
editor.putInt("high_score",newhighscore);
eidtor.commit();

While reading the preference, we can give an optional value that will get returned if the value for the key we are reading is not present. 

Saving Files
There are two storage areas: "internal" and "external" storage representing built in memory and SD card respectively. 

Internal storage is always accessible. Files stored here is accessible only by the application by default. When the user uninstalls the app, the stored file also get deleted. 
External storage is not always accessible because user can unmount the card. By default the data stored here is world readable. When app is uninstalled, system will remove the apps files only if the saving is done in the getExternalFilesDir()/ 

If the application needs to get installed on to file system, then that can be specified in the android manifest file. 

IF the application would like to write to external storage, a permission needs to be requested in the manifest file via the property android.permission.READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE. 

To get applications root files directory, getFilesDir api can be called. To get the caches directory, getCachesDir api can be called. Caches directory is a temporary directory. When there is any memory constraint situation arises, system will delete files from this directory without any warning. 

When trying to access external storage, always should try to see if it is mounted. There are APIs available such as getExternalStorageState which will return Environment.MEDIA_MOUNTED value. Application can check if the media is read only by checking the value as MEDIA_MOUNTED_READ_ONLY 

The files can be stored in the external directory in two forms 

Public : Files are freely available for other applications, the public directory can be obtained by using the API Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
Private : Even though files are accessible to the user, they don't make much sense to the user. the API getExternalFilesDir can be used for this. Both these APIs accept parameter such as Environment.DIRECTORY_PICTURES so that the system categoryzes the files properly. 

The API getFreeSpace() or getTotalSpace() can be called to know the freespace and the total space. The recommendation is that if the storage is 90% full, then don't write anything. Also, application can write without checking the size and gets into IOEXception, then handle it accordingly. 


When user uninstalls the App, Android system does the following 

- Delete all files in internal storage
- Delete all field on external storage that used getExternalFilesDir() 

Application should delete all the files created with getCacheDir on a regular basis if no longer needed. 

Wednesday, July 9, 2014

Managing Activity Life cycle

Below diagram shows the Activity life cycle 



The main life cycle states are 

Resumed : In this state, activity is in foreground and user can interact with it. (Also referred to as running state)
Paused : In this state, activity is partially obscured by another activity. The other activity that is in the foreground is semi-transparent or does not cover the entire screen. The paused activity does not receive any user input nor execute any code. 
Stopped : In this case, Actvity is completely hidden and not visible to the user; it is considered to be in the background. While stopped, activity information and all its state information such as member variables is retained, but cannot execute any code. 

There are two other states Created and Started, these two are transient state and system moves quickly between these two states. i.e once the system calls onCreate, it quickly calls onStart and immediately onResume. 

An activity should be specified as Launcher activity. The system will use this activity as the starting point of the application. 


if any of the intent type MAIN Or LAUNCHER is not appearing in the  manifest xml, application will not be listed in the HomeScreen's list of apps 

when onCreate finishes the execution, the system calls onStart and onResume in quick succession. The application will never reside in created or started states. Technically the activity comes to the visible state when the onStart call happens, but onResume call comes immediately and it remains on the Resumed state. 

The very last callback is onDestroy. The system calls this method as a final signal that the application's activity instance is being completely removed from the system memory. Most apps should do the cleanup operations in the onPause or onStop method of the activity. 

In normal cases, onDestroy should come after the onPause and onStop methods. However, if the activity calls finish() from the onCreate methods with an intention to launch another activity, the onDestroy will be called without these two life cycle methods are called. 

Pausing and Resuming Activity
During the life cycle of an activity, if there is a semi transparent style of ui is obstructing this activity, then system calls onPause method. However, if the activity is completely invisible, it calls the onStop method. 

If the activity is resuming from the paused state, it will call onResume method. 

It is recommended that the Activity does the below items on the onPause method 

- Stop animations or other ongoing actions that may consume CPU 
- Commit unsaved changes if required. 
- RElease system resources such as broadcast receivers, handles to sensors (like GPS), or any resource that may affect battery life while the activity is paused and the user doesn't need them. 

For e.g. if the application uses camera, this will be the right method to release it 

Generally, onPause should not be used for any CPU intensive operations, instead, they should be done on onStop method. 

When the activity is resumed from the paused state, it calls the onResume method. 

Stopping and Restarting the Activity
Below are the few scenarios where the Activity is stopped and restarted. 

- User opens the recent activity list and switches to the another application
- The user performs an action in your app that starts a new activity. The current activity will be stopped and when user performs the back, it will be restarted. 
- User receives a device interruption such as a phone call. 

When the Activity is getting restarted, onRestart method will be called and immediately the onStart method will be called. 

Similar to paused state, framework keeps the data in memory. application not required to save it. 

Even if the System destroys the activity while it is stopped, it still retains the state of the View objects in a Bundle (a blob of key value pairs)

When the app comes back from Background to foreground, Activities, onRestart get called and followed by that onStart get called. 

Recreating An Activity 
When the Activity is destroyed decease user pressed back or activity finishes itself, system's concept of that Activity is gone, However, if the system destroys an Activity due to system constraints, then although the actual Activity instance is gone, the system remembers that if user navigates back to it, the system creates a new instance of activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the "instance state" and is a collection of key-value pairs stored in Bundle object. 

It is very important to note that the Activity get killed every time when rotate the screen!!! . This is because the screen configuration has changed and the system may need to load different layout files etc. 
In this case also by default system will keep the state in Bundle object. But if the app has to keep track of other states, then it has to have its own mechanism. 

Android uses the android:id attribute in the xml to keep track of the object for restoration purposes. 

System will call onSaveInstanceState method and add key value pairs to the Bundle object. On onCreate method, application should check whether the bundleInstance is null. If not null application can read from it and assign to the instance variables. 

Also application may choose onRestoreInstance method to restore the state of the instance. the onRestoreInstance may get called back only if there is any bundle exist. Also to note, always Application should call super.onREstoreInstancestate and super.onSaveBundleInstanceState methods so that System can do the store/restore operation on the instance state. 

References: 

Monday, July 7, 2014

Android Training - Getting Started - Day 1

Few important items to note in the first application is 

AndroidManifest.xml is having tag which has android:minSdkVersion, android:targetSdkVersion values. The former should be lowest as possible to support various set of devices and the latter should be high as possible to target latest set of devices. 

src/ contains the source which includes the main Activity. res/ directory cottons the below folder 

/drawble- => drawable elements such as bitmaps for the designated screen density 
/layout => contains the layout xml files that defines app user interface 
/values => Contains collection of resources such as strings and colour values 

Running on real devices

Installing the device drives a good documentation is available at http://developer.android.com/tools/extras/oem-usb.html
To enable development mode, the instructions is given at : http://developer.android.com/training/basics/firstapp/running-app.html
to give a short note on this, on devices 3.2 or older, the debug option can be found under Settings -> Applications > Development 
on 4.0 and newer , Settings -> Developer options 
on 4.2 and newer Developer Options is hidden by default. To enable it, Settings -> About Phone, and tap build number seven times and now can return to the previous screen to find Developer Options

To install on to the device, 
1. Change Directories to the root of Android project and execute 

ant debug 

adb install bin/MyFirstApp-debug.apk 

User Interface

User interface is a collection of Views and Viewgroups. Below is the hierarchy. Views are ui widgets such as buttons, Text fields etc. ViewGroups are hidden elements by default and contains these views and defines the layout for these views. 

android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">

LinearLayout is a subclass of ViewGroup which lays out its child subviews in either vertical or horizontal orientation. 

To add a TextField to this, the below needs to be added in the XML file 

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/edit_message">

This above references edit_message property and it is from the strings resource file. The strings resource file can be modified like below so that it picks the value from this place. 

My First App
Enter a message

We can define the width occupied by a view using the layout weight property. The weight value is the amount of remaining space each view should consume. relative to the amount of space consumed by the sibling views. For e.g. if the application gives one view as weight of 2 and another weight of 1, the sum is 3. so the first view will fill the 2/3 of the space and second will fill the rest. now if we add third view with the weight value as 1, then the first view will get 2/4, i.e half of the remaining space while the other two will get 1/4 of the space. 

Default width of each view is 0. so, if we don't specify any weight for a view, then that widget will fill the remaining space after all views are given their requested space. 

With the above in hand, in order to improve the layout efficiency, application can provide the width of the view as 0dp so that the layout engine don't calculate the width and eventually discard it because it not used and overridden by the weight property. 

Inorder to invoke another Activity, application needs to build and intent. the Intent class is in the package android.content.Intent. 
The code to pass the display message to the new activity can be something like below 

Intent intent = new Intent(this, DisplayMessageActivity.class);
intent.putExtra(EXTRA_MESSAGE, message);

References: 
http://developer.android.com/training/basics/firstapp/starting-activity.html

Saturday, July 5, 2014

Android Handling Input Events - Learning Day 3

If application inherit a View class and override the methods like onTouchEvent(), application can intercept these events generated by the Android framework. But since this is tedious, another approach is all of these views contains the nested interfaces with callbacks. These interfaces are Event Listeners.

The common listener methods are: 

onClick()  using View.OnClickListener
This is from View.onClickListener. called back on touch down or corresponding action using a jog wheel or track ball etc. 

onLongClick() using View.OnLongClickListener 
Called by the framework on long touch. 

onFocusChange() using View.OnFocusChangeListener 
Called back when one view is getting unfocused due to user moving away. 

onKey() View.OnKeyListener 
Called back when user focused an item and pressed a hardware key. 

onTouch()  using View.OnTouchListener 
When a touch event happens such as press, release, or any movement gesture within the screen 

onCreateContextMenu Using View.onCreateContextMenuListener 
When a context menu being built. 

It is to be noted that some of the callbacks are having Bool return value, while others don't need a return type. The methods which are having BOOL return type assume the listener handled the event if the listener returned back TRUE. 

When creating a custom Views, some of the common callback method used for event handling are the below 

- onKeyDown(int , KeyEvent)
- onKeyUp
- onTrackballEvent 
- onTouchEvent
- onFocusChanged 

There are few events also bit more important which are below. 

Activity.dispatchTouchEvent(MotionEvent) - This allows view to intercept all events before they are passed to the Window
Activity.onInterceptTouchEvent - This allows ViewGroup to watch the events as they are passed to the child views 
ViewParent.requestDisallowInterceptTouchEevnt- This allows to request to disallow the above. 

There is a touch mode for Views. For e.g. Buttons are focusable in non touch mode, i.e. user is interacting with trackball or keys. In this mode, in order to make actions of a button, first it needs to be focused. But if touch in touch mode, the widget action is directly fired instead of first reporting any focus events. 

Focusable components can be specified in the layout XML file like in same below 


 

references:
http://developer.android.com/guide/topics/ui/ui-events.html

Friday, July 4, 2014

Android Day 2 - Input Controls part 2

CheckBoxes

Application can create a Checkbox using CheckBox class. Like Button, the onClick attribute can specify the method. the code is something like below

public void onCheckbxClicked(View view)
{
boolean checked = ((CheckBox)view).isChecked();
switch (view.getId())
{
case R.id.checkbox_meat:
if(checked)
{
//do something
}
}
}

RadioButton and RadioGroup can be used for creating the RadioButton implementation. RadioButton needs to be placed inside a RadioGroup. The sample is like below

android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:orientation = "vertical">

android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text="@string/pirates"
android:onClick="onRadioButtonClicked"/>

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text = "@string/ninjas"
android:onClick="onRadioButtonClicked"/>

 

ToggleButton class can be used to create a Toggle button which can be used to switch between two states. Android 4.0 API Level 14 has introduced another kind of toggle button called Switch. These two classes are subclass of CompoundButton and function like in same manner. 

Programmatically an application can add the listener like in below code. 

ToggleButton tb = (ToggleButton) findViewById(R.id.togglebutton);
toggle.setOnCheckedChangeListener(new CompoundButton.onCheckedChangeListener())
{
public void onCheckedChanged(CompoindButton buttonView, boolean isChecked)
{
}
}

Spinners allow to select a value from a list of values. Populating values to a Spinner is similar to how it is done for a list, i.e. using ArrayAdapter or a CursorAdapter
sample is like below.

Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter adapter = ArrayAdapter.createFromResource(this,R.array.planets_array,android.R.laout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

Picker 
Android provides picker as a control to provide picking of time or date as a ready-to-use dialogs. Picking date using these picker ensures that the user selects the date/time info correctly adjusted to the user locale and formatted properly. Key classes are DatePickerDialog, TimePickerDialog, DialogFragment

Android recommends to use the DialogFragment class to host the pickers. DialogFragment class provides a way to display the pciekr is different layout and configurations. For e.g. basic dialling on handsets or as an embedded part of the layout on large screens. 

The DialogFragment was added first in Api level 3.0 (API Level 11), even older version can have DialogFragment using the support library. 


Wednesday, July 2, 2014

Android - Day 1 - Android Input Controls Part 1

These are the interactive components. Below are some common controls 

Button :- A push-button that can be pressed or clicked by the user to perform an action. 
Text Field : editable textview. Application can use AutocompleteText widget to create a text entry widget that provides auto-complete suggestions. 
Checkbox : On/Off switch as usual.
RadioButton, ToggleButton, Spinner, 
Pickers -> This displays Dialog for users to select a Single value for a set by using up/down button or swipe gesture. DatePicker or TimePicker widget can be used for acheving this. 

Buttons can have text, icon or both. Application needs to use Button and ImageButton class for this. With the Button, the icon image can be placed on the button using android:leftDrawable property. 

There are two ways application have listeners to the button. Defining in the layout xml file in the Button XML attributes using android:onClick property. Inside the Activity which hosts this Button view need to write the method as public, void and accepting the View argument. 

Programmatically can define the action by using the View.onClickListener object. For e.g. below 

Button b = findViewById(R.id.button_id)
button.setOnClickListener(new View.onClickListener() 
{
public void onClick(View v)
{
}
});

The appearance of the button may vary across the different devices since they are from different manufacturers. However, application can set a theme for the entire application and
For e.g application can set the theme as holo theme using the following 

android:theme="@android:style/Theme.Holo" in the manifest element. The theme is not supported on older devices, so, http://android-developers.blogspot.com/2012/01/holo-everywhere.html can help to do something similar for earlier versions of OS. 

Application can set the border style of a button like style="?android:attr/borderlessButtonStyle"

Application can also set a custom background. Below is what application needs to do for this 

1. Create three bitmpas for the button background that represents the default, pressed and focused button states. Application needs to create these as nine patch images
2. place the bitmapts in /drawable resource. the convention is something like button_pressed.9.png, button_focused.9.png, button_default.9.png 
3. Create a new XML in the /res/drawable directory (the name can be like button_custom.xml)

The below could be the content of this XML file 


Text Fields
Applications can use EditText class to display the text. android:inputType specifies whether we should get the input type as email id, text, texturi, number, phone. The inputType filed is a bit or-ed value and the other values are textCapSentences, text capWords, textAutoCorrect, textPassword, textMultiLine, Applications can specify the subsequent actions using the imeOptions value. The possible values are actionDone, actionSend, actionSearch, or suppress everything by using actionNone. In order to listen to the IME action events, below code can be used 

EditText et = findViewById(R.id.edit_text); 
et.setOnEditActionListener(new OnEditorActionListener()
{
@override
public boolean onEditorAction(TextView v, int actionId, KeyEvent evt)
{
boolean handled = false;
if(actionId == Editor.IME_ACTION_SEND)
{
sendMessage();
handled = true;
}
return handled;
}
}
);

Application can also set a custom IME action label by using the property imeActionLabel property. In addition, many flags can be set using android:imeOptions attribute For e.g. in landscape mode, the text field may turn to a full screen one. And this can be disabled using the flag "flagNoExtractUi"

If application needs to provide an AutoCompleteTextView, The code can be something like below 

AutocompleteTextView tv = (AutocompleteTextView) findViewById(R.id.autocomplete_text);
String countries[] = getResources().getStringArray(R.array.countries_array);
ArrayAdapter adapter = (this, android.R.simple_list_item_1,countries);

tv.setAdapter(adapter);

references: 
http://developer.android.com/guide/topics/ui/controls.html