Monday, June 30, 2014

Cardboard: Virtual Reality (VR) for Android

Cardboard is a simple DIY enclosure that transforms a phone into a basic virtual reality headset and the accompanying open software toolkit. Cardboard makes it easy for both users and developers to experiment with VR.
This session in Google I/O 2014, Cardboard: VR for Android, discuss how to build immersive 3D experiences for Android. Give a deep dive into 3D side-by-side rendering, head tracking, and user interaction principles. Introduce a new open source VR toolkit and walk through using it to create a sample immersive app.

Sunday, June 29, 2014

The ART runtime

ART is an evolution of the Android runtime, and was first made available as an option on Android 4.4, KitKat. It comes with improvements in the garbage collector, threading and locking model, compiler and runtime performance. This session in Google I/O 2014, The ART runtime, focus on all of the improvements have been made to the Android runtime.

Thursday, June 26, 2014

The L Developer Preview is available for download now

The Android L Developer Preview is available for download. It lets developers explore features and capabilities of the upcoming Android L release and get started developing and testing on the new platform. You can take a look at the developer features and APIs in the API Overview page.



What's new in Android and Android Development Tools@Google I/O 2014

Join for a thrilling, guided tour of all the latest developments in Android technologies and APIs. Cover everything that's new and improved in the Android platform.



Provide an in depth tour of the Android development tools and take a closer look at everything new - along with tips and tricks for getting the most out of them!

Wednesday, June 25, 2014

Nexus 7 receive Android 4.4.4 System Update

My Nexus 7 (1st generation) just received Android 4.4.4 System Update :)


Cardboard, Virtual reality on your smartphone

Cardboard is a no-frills enclosure that transforms a phone into a basic VR headset, and the accompanying open software toolkit that makes writing VR software as simple as building a web or mobile app.


Install Cardboard app puts virtual reality on your smartphone. Try a variety of immersive demos on Android, and get inspired to build your own using the VR Toolkit at http://g.co/cardboard.

• Earth: Fly where your fancy takes you on Google Earth.
• Tour Guide: Visit Versailles with a local guide.
• YouTube: Watch popular YouTube videos on a massive screen.
• Exhibit: Examine cultural artifacts from every angle.
• Photo Sphere: Look around the photo spheres you've captured.
• Street Vue: Drive through Paris on a summer day.
• Windy Day: Follow the story (and the hat) in this interactive animated short from Spotlight Stories.

To fully enjoy this app you'll need a Cardboard viewer. You can make your own using the instructions at http://g.co/cardboard to build your viewer.


more: Cardboard: Virtual Reality (VR) for Android

Google I/O 2014 Keynote

Google I/O 2014 Keynote is available now.

Detect AnimationDrawable.isRunning() to toggle start/stop animation

This example base on the old example "Start and Stop frame animation with AnimationDrawable". When button clicked, it read AnimationDrawable.isRunning() and call AnimationDrawable.start() or AnimationDrawable.stop() to toggle the animation.


Download the png files and copy /res/anim/anim_android.xml here.

/res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:id="@+id/startstopanimation"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Start/Stop Animation" />

    <ImageView 
        android:id="@+id/myanimation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@anim/anim_android"
        />

</LinearLayout>

AndroidAnimationActivity.java
package com.exercise.AndroidAnimation;

import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class AndroidAnimationActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ImageView myAnimation = (ImageView)findViewById(R.id.myanimation);
        final AnimationDrawable myAnimationDrawable 
         = (AnimationDrawable)myAnimation.getDrawable();
        
        Button startStopAnimation = (Button)findViewById(R.id.startstopanimation);
        
        startStopAnimation.setOnClickListener(new Button.OnClickListener(){

   @Override
   public void onClick(View arg0) {
    
    if(myAnimationDrawable.isRunning()){
     myAnimationDrawable.stop(); 
    }else{
     myAnimationDrawable.start(); 
    }

   }});

    }
}


download filesDownload the files.

Next:
Determine end of animation to start another animation

Thursday, June 19, 2014

Wednesday, June 18, 2014

Implement arrow-like PathDashPathEffect

Last example "Implement running dash path, using PathDashPathEffect" with path of circle. It's modified to create custom path to show running arrow-like PathDashPathEffect.


Modify MyView.java in last example.
package com.example.androiddrawpath;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;

public class MyView extends View {
 
 MyShape myShape;
 float ratioRadius, ratioInnerRadius;
 int numberOfPoint = 3; //default
 
 float rotate;
 
 //corresponding to UI element
 TextView textLayerInfo;

 Path dashPath;
 float phase;
 float advance;

 public MyView(Context context) {
  super(context);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  initMyView();
 }
 
 public void initMyView(){
  myShape = new MyShape();
  
  dashPath = new Path();
  dashPath.moveTo(5, -5);
  dashPath.lineTo(45, -5);
  dashPath.lineTo(40, 0);
  dashPath.lineTo(45, 5);
  dashPath.lineTo(5, 5);
  dashPath.lineTo(0, 0);
  dashPath.close();
  
  phase = 0.0f;
  advance = 60.0f;
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  
  long starting = System.nanoTime();
  
  int w = getWidth();
  int h = getHeight();
  
  if((w==0) || (h==0)){
   return;
  }
  
  float x = (float)w/2.0f;
  float y = (float)h/2.0f;
  float radius, innerRadius;
  if(w > h){
   radius = h * ratioRadius;
   innerRadius = h * ratioInnerRadius;
  }else{
   radius = w * ratioRadius;
   innerRadius = w * ratioInnerRadius;
  }
  
  myShape.setStar(x, y, radius, innerRadius, numberOfPoint);
  
  //Save and rotate canvas 
  canvas.save();
  canvas.rotate(rotate, x, y);
  
  phase++;
  PathDashPathEffect pathDashPathEffect = 
   new PathDashPathEffect(dashPath, advance, phase, PathDashPathEffect.Style.MORPH);
  
  Paint paintDash = myShape.getPaint();
  paintDash.setPathEffect(pathDashPathEffect);
  
  canvas.drawPath(myShape.getPath(), myShape.getPaint());
  
  //restore canvas
  canvas.restore();
  
  long end = System.nanoTime();
  
  String info = "myView.isHardwareAccelerated() = " + isHardwareAccelerated() + "\n"
    + "canvas.isHardwareAccelerated() = " + canvas.isHardwareAccelerated() + "\n"
    + "processing time (reference only) : " + String.valueOf(end - starting) + " (ns)";
  textLayerInfo.setText(info);
  
  invalidate();
  
 }
 
 public void setShapeRadiusRatio(float ratio){
  ratioRadius = ratio;
 }
 
 public void setShapeInnerRadiusRatio(float ratio){
  ratioInnerRadius = ratio;
 }
 
 public void setNumberOfPoint(int pt){
  numberOfPoint = pt;
 }
 
 public void passElements(TextView textLayerInfo){
  this.textLayerInfo = textLayerInfo;
 }

 public void setShapeRotate(int rot){
  rotate = (float)rot;
 }

}


Other files have no changed, re-list here:

MyShape.java
package com.example.androiddrawpath;

import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;

public class MyShape {

 private Paint paint;
 private Path path;

 public MyShape() {
  paint = new Paint();
  paint.setColor(Color.BLUE);
  paint.setStrokeWidth(3);
  paint.setStyle(Paint.Style.STROKE);
  //paint.setStyle(Paint.Style.FILL);
  //paint.setStyle(Paint.Style.FILL_AND_STROKE);
  
  path = new Path();
 }

 public void setCircle(float x, float y, float radius, Path.Direction dir){
  path.reset();
  path.addCircle(x, y, radius, dir);
 }
 
 public void setStar(float x, float y, float radius, float innerRadius, int numOfPt){
  
  double section = 2.0 * Math.PI/numOfPt;
  
  path.reset();
  path.moveTo(
   (float)(x + radius * Math.cos(0)), 
   (float)(y + radius * Math.sin(0)));
  path.lineTo(
   (float)(x + innerRadius * Math.cos(0 + section/2.0)), 
   (float)(y + innerRadius * Math.sin(0 + section/2.0)));
  
  for(int i=1; i<numOfPt; i++){
   path.lineTo(
    (float)(x + radius * Math.cos(section * i)), 
    (float)(y + radius * Math.sin(section * i)));
   path.lineTo(
     (float)(x + innerRadius * Math.cos(section * i + section/2.0)), 
     (float)(y + innerRadius * Math.sin(section * i + section/2.0)));
  }
  
  path.close();
  
 }
 
 public Path getPath(){
  return path;
 }
 
 public Paint getPaint(){
  return paint;
 }

}

MainActivity.java
package com.example.androiddrawpath;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.RadioButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity {

 SeekBar rotateBar;
 TextView rotateText;
 
 SeekBar radiusBar, innerRadiusBar;
 MyView myView;
 
 SeekBar ptBar;
 TextView textPt;
 final static int MIN_PT = 3;
 
 RadioButton optLayerTypeNone, optLayerTypeSoftware, optLayerTypeHardware;
 TextView textLayerInfo;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  radiusBar = (SeekBar) findViewById(R.id.radiusbar);
  innerRadiusBar = (SeekBar)findViewById(R.id.innerradiusbar);
  myView = (MyView) findViewById(R.id.myview);
  float defaultRatio = (float) (radiusBar.getProgress())
    / (float) (radiusBar.getMax());
  myView.setShapeRadiusRatio(defaultRatio);
  float defaultInnerRatio = (float) (innerRadiusBar.getProgress())
    / (float) (innerRadiusBar.getMax());
  myView.setShapeInnerRadiusRatio(defaultInnerRatio);

  radiusBar.setOnSeekBarChangeListener(radiusBarOnSeekBarChangeListener);
  innerRadiusBar.setOnSeekBarChangeListener(innerRadiusBarOnSeekBarChangeListener);
  
  textPt = (TextView)findViewById(R.id.pttext);
  ptBar = (SeekBar)findViewById(R.id.ptbar);
  ptBar.setOnSeekBarChangeListener(ptBarOnSeekBarChangeListener);
  
  optLayerTypeNone = (RadioButton)findViewById(R.id.typeNone);
  optLayerTypeSoftware = (RadioButton)findViewById(R.id.typeSoftware);
  optLayerTypeHardware = (RadioButton)findViewById(R.id.typeHardware);
  textLayerInfo = (TextView)findViewById(R.id.typeinfo);
  
  myView.passElements(textLayerInfo);
  myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

  optLayerTypeNone.setOnCheckedChangeListener(optLayerTypeOnCheckedChangeListener);
  optLayerTypeSoftware.setOnCheckedChangeListener(optLayerTypeOnCheckedChangeListener);
  optLayerTypeHardware.setOnCheckedChangeListener(optLayerTypeOnCheckedChangeListener);

  rotateText = (TextView)findViewById(R.id.rottext);
  rotateBar = (SeekBar)findViewById(R.id.rotatebar);
  rotateBar.setOnSeekBarChangeListener(rotateBarOnSeekBarChangeListener);
  myView.setRotation(0);  //set default rotate degree
 };
 
 OnCheckedChangeListener optLayerTypeOnCheckedChangeListener = 
  new OnCheckedChangeListener(){

   @Override
   public void onCheckedChanged(CompoundButton buttonView,
     boolean isChecked) {
    if(optLayerTypeNone.isChecked()){
     myView.setLayerType(View.LAYER_TYPE_NONE, null);
    }else if(optLayerTypeSoftware.isChecked()){
     myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }else{
     myView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    }
    
    myView.invalidate();
   }};

 OnSeekBarChangeListener radiusBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener() {

  @Override
  public void onProgressChanged(SeekBar seekBar, int progress,
    boolean fromUser) {
   float ratio = (float) (radiusBar.getProgress())
     / (float) (radiusBar.getMax());
   myView.setShapeRadiusRatio(ratio);
   myView.invalidate();
  }

  @Override
  public void onStartTrackingTouch(SeekBar seekBar) {}

  @Override
  public void onStopTrackingTouch(SeekBar seekBar) {}

 };
 
 OnSeekBarChangeListener innerRadiusBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener() {

  @Override
  public void onProgressChanged(SeekBar seekBar, int progress,
    boolean fromUser) {
   float ratio = (float) (innerRadiusBar.getProgress())
     / (float) (innerRadiusBar.getMax());
   myView.setShapeInnerRadiusRatio(ratio);
   myView.invalidate();
  }

  @Override
  public void onStartTrackingTouch(SeekBar seekBar) {}

  @Override
  public void onStopTrackingTouch(SeekBar seekBar) {}

 };
 
 OnSeekBarChangeListener ptBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener() {

  @Override
  public void onProgressChanged(SeekBar seekBar, int progress,
    boolean fromUser) {
   int pt = progress + MIN_PT;
   textPt.setText("number of point in polygon: " + String.valueOf(pt));
   myView.setNumberOfPoint(pt);
   myView.invalidate();
  }

  @Override
  public void onStartTrackingTouch(SeekBar seekBar) {}

  @Override
  public void onStopTrackingTouch(SeekBar seekBar) {}

 };
 
 OnSeekBarChangeListener rotateBarOnSeekBarChangeListener = 
   new OnSeekBarChangeListener() {
  
  @Override
  public void onProgressChanged(SeekBar seekBar, int progress,
    boolean fromUser) {
   int degree = progress-180;
   rotateText.setText("rotate : " + degree + " degree");
   myView.setShapeRotate(degree);
   myView.invalidate(); 
  }
  
  @Override
  public void onStartTrackingTouch(SeekBar seekBar) {}
  
  @Override
  public void onStopTrackingTouch(SeekBar seekBar) {} 
 };

}

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.androiddrawpath.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="radius(%)"/>
    <SeekBar 
        android:id="@+id/radiusbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="50" />
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="inner radius(%)"/>
    <SeekBar 
        android:id="@+id/innerradiusbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="25" />
    <TextView 
        android:id="@+id/pttext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="number of point in polygon: 3"/>
    <SeekBar 
        android:id="@+id/ptbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10"
        android:progress="0" />

    <TextView 
        android:id="@+id/rottext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="rotate :"/>
    <SeekBar 
        android:id="@+id/rotatebar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="360"
        android:progress="180" />
    
    <RadioGroup
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
     <RadioButton android:id="@+id/typeNone"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="wrap_content"
         android:text="LAYER_TYPE_NONE"/>
     <RadioButton android:id="@+id/typeSoftware"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="wrap_content"
         android:text="LAYER_TYPE_SOFTWARE"/>
     <RadioButton android:id="@+id/typeHardware"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="wrap_content"
         android:text="LAYER_TYPE_HARDWARE"/>
    </RadioGroup>
    
    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="match_parent">
     <TextView 
         android:id="@+id/typeinfo"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true" />
     <com.example.androiddrawpath.MyView 
         android:id="@+id/myview"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
    </RelativeLayout>

</LinearLayout>


download filesDownload the files.

More example of Drawing Path on canvas of custom View.

Monday, June 16, 2014

Implement running dash path, using PathDashPathEffect

Example to implement running dash path, using PathDashPathEffect.

Modify from previous example "DashPathEffect, apply dash effect on path", implement PathDashPathEffect. In order to make it running, advance phase of PathDashPathEffect, and call invalidate() in onDraw() method.

MyView.java
package com.example.androiddrawpath;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.graphics.Path.Direction;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;

public class MyView extends View {
 
 MyShape myShape;
 float ratioRadius, ratioInnerRadius;
 int numberOfPoint = 3; //default
 
 float rotate;
 
 //corresponding to UI element
 TextView textLayerInfo;

 Path dashPath;
 float phase;

 public MyView(Context context) {
  super(context);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  initMyView();
 }
 
 public void initMyView(){
  myShape = new MyShape();
  
  dashPath = new Path();
  dashPath.addCircle(0, 0, 3, Direction.CCW);
  
  phase = 0.0f;
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  
  long starting = System.nanoTime();
  
  int w = getWidth();
  int h = getHeight();
  
  if((w==0) || (h==0)){
   return;
  }
  
  float x = (float)w/2.0f;
  float y = (float)h/2.0f;
  float radius, innerRadius;
  if(w > h){
   radius = h * ratioRadius;
   innerRadius = h * ratioInnerRadius;
  }else{
   radius = w * ratioRadius;
   innerRadius = w * ratioInnerRadius;
  }
  
  myShape.setStar(x, y, radius, innerRadius, numberOfPoint);
  
  //Save and rotate canvas 
  canvas.save();
  canvas.rotate(rotate, x, y);
  
  phase++;
  PathDashPathEffect pathDashPathEffect = 
   new PathDashPathEffect(dashPath, 15.0f, phase, PathDashPathEffect.Style.MORPH);
  
  Paint paintDash = myShape.getPaint();
  paintDash.setPathEffect(pathDashPathEffect);
  
  canvas.drawPath(myShape.getPath(), myShape.getPaint());
  
  //restore canvas
  canvas.restore();
  
  long end = System.nanoTime();
  
  String info = "myView.isHardwareAccelerated() = " + isHardwareAccelerated() + "\n"
    + "canvas.isHardwareAccelerated() = " + canvas.isHardwareAccelerated() + "\n"
    + "processing time (reference only) : " + String.valueOf(end - starting) + " (ns)";
  textLayerInfo.setText(info);
  
  invalidate();
  
 }
 
 public void setShapeRadiusRatio(float ratio){
  ratioRadius = ratio;
 }
 
 public void setShapeInnerRadiusRatio(float ratio){
  ratioInnerRadius = ratio;
 }
 
 public void setNumberOfPoint(int pt){
  numberOfPoint = pt;
 }
 
 public void passElements(TextView textLayerInfo){
  this.textLayerInfo = textLayerInfo;
 }

 public void setShapeRotate(int rot){
  rotate = (float)rot;
 }

}


Other files, refer to last example.

download filesDownload the files.

More example of Drawing Path on canvas of custom View.


Sunday, June 15, 2014

Implement WebChromeClient to retrieve downloaded favicon and title in WebView.

In the post of "Create your own App for FIFA World Cup", we use our default icon and title. Now I want to use the downloaded favicon and title from the target page as our icon and title.


To get the downloaded icon and title, implement our custom WebChromeClient, MyWebChromeClient, override onReceivedIcon() and onReceivedTitle() methods.

To update icon and title, call getSupportActionBar().setIcon(iconDrawable) and getSupportActionBar().setTitle(title). In order to call getSupportActionBar(), change our MainActivity extends ActionBarActivity, instead of Activity.

Call myBrowser.setWebChromeClient(new MyWebChromeClient()) in MainActivity to set our custom WebChromeClient.

MainActivity.java
package com.example.myfifa;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {


 public class MyWebChromeClient extends WebChromeClient {

  @Override
  public void onReceivedTitle(WebView view, String title) {
   Toast.makeText(getApplicationContext(), 
    "Title received: " + title, 
    Toast.LENGTH_LONG).show();
   getSupportActionBar().setTitle(title); 
   super.onReceivedTitle(view, title);
  }

  @Override
  public void onReceivedIcon(WebView view, Bitmap icon) {
   Toast.makeText(getApplicationContext(), 
    "icon received", 
    Toast.LENGTH_LONG).show();
   
   BitmapDrawable iconDrawable = 
    new BitmapDrawable(getResources(), icon);
   
   getSupportActionBar().setIcon(iconDrawable);
   super.onReceivedIcon(view, icon);
  }

 }

 WebView myBrowser;
 private final static String FIFA_HOME = "http://www.fifa.com/";
 
 private final static String KEY_URL = "KEY_URL";

 @SuppressLint("SetJavaScriptEnabled")
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  //restore url after config/orientation changed
  String openUrl = FIFA_HOME;
  if (savedInstanceState != null) {
   openUrl = savedInstanceState.getString(KEY_URL);
  }

  myBrowser = (WebView)findViewById(R.id.mybrowser);
  myBrowser.getSettings().setJavaScriptEnabled(true);
  myBrowser.setWebViewClient(new WebViewClient());
  myBrowser.setWebChromeClient(new MyWebChromeClient());
  myBrowser.loadUrl(openUrl);

 }

 @Override
 protected void onSaveInstanceState(Bundle outState) {
  //save url before config/orientation changed
  outState.putString(KEY_URL, myBrowser.getUrl());
  super.onSaveInstanceState(outState);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {

  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  int id = item.getItemId();
  if (id == R.id.action_openinbrowser) {
   
   Uri openUri = Uri.parse(myBrowser.getUrl());
   Intent myIntent = new Intent(Intent.ACTION_VIEW, openUri);
   startActivity(myIntent);
   
   return true;
  }
  return super.onOptionsItemSelected(item);
 }

}

Other files, activity_main.xml, /res/menu/main.xml, /res/values/strings.xml and AndroidManifest.xml, refer to the last post.



download filesDownload the files.

Saturday, June 14, 2014

Unboxing Android USB: A hands on approach with real world examples

If you are a multimedia developer on Android platform you need USB for media transfer or playback audio. This book explores MTP and USB Audio in both USB device and USB host mode.

If you are an core developer who work on charging you need to understand USB charging specification which is explained in the book,

If you are a networking developer interested in tethering, USB plays a role using RNDIS specification which is explained in the book

If you are an application developer interested in managing USB devices in from an Android platform, this book explores Android USB Service framework which manages USB functionalities

Last not the least, Android Debug Bridge (ADB) the debugging tool of Android is over USB and knowledge of its internals is a define value add for any application or platform developer.  This book details the internal of ABD till the kernel level.

Thus this book covers everything about USB on Android from different USB Classes supported in device mode to the USB host framework that manages USB device connected to an Android platform. Each chapter will explain USB class specification before exploring how the functionality (class) is implemented in Android. This gives a clean perspective for you as a reader on what the USB specification demands and how it implemented in Android.

Unboxing Android USB: A hands on approach with real world examples

What you'll learn
  • Understand Android USB framework from APIs to the kernel layer and enable advance USB application development.
  • Learn all major USB functionalities by exploring the USB Class specification not covered in any of the USB books
  • Learn the newly introduced Android Open Accessory (AOA) Protocol and explore developing NFC reader using AOA protocol.
  • Learn critical changes in the Android USB framework between different Android versions.
  • Learn how USB charging works with explanation from the USB Battery Specification.
  • Learn how to switch between MTP to Mass Storage and vice versa to share storage to host PC.
Who this book is for
  • The primary audiences of this book are application developers and engineers who do hands on work with Android.  This book is for an application developer who has an APP idea with USB and wonders how to implement it. This book will be a definite guide for the developer and help him/her to manage USB on Android.
  • With the book covering from APIs to the Linux kernel, core platform developers finds it easy to put data point to debug. Thus core Android platform developers working on USB, Audio, media and others are the next primary audiences of the book.
  • Technical Managers or Architects or senior managers who look for eagle eye view of a system, are the secondary audiences of the book. The book will enable them to understand the different blocks of the Android USB subsystem and would help plan and estimate complexity involved.
  • Student and engineers can use this book as a do it yourself reference book as the book explains different blocks the Android USB framework from application level to the kernel. Students can use similar study approach to similar Android framework.

Friday, June 13, 2014

Add uses-permission of "android.permission.INTERNET" in AndroidManifest.xml.

To grant permission of accessing Internet to your app, edit AndroidManifest.xml and add the code:

    <uses-permission android:name="android.permission.INTERNET"/>


Thursday, June 12, 2014

Create your own App for FIFA World Cup

Just a funny exercise to load The Official Website of the FIFA World Cup (http://www.fifa.com/) in a webview.


MainActivity.java
package com.example.myfifa;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends Activity {
 
 WebView myBrowser;
 private final static String FIFA_HOME = "http://www.fifa.com/";
 
 private final static String KEY_URL = "KEY_URL";

 @SuppressLint("SetJavaScriptEnabled")
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  //restore url after config/orientation changed
  String openUrl = FIFA_HOME;
  if (savedInstanceState != null) {
   openUrl = savedInstanceState.getString(KEY_URL);
  }

  myBrowser = (WebView)findViewById(R.id.mybrowser);
  myBrowser.getSettings().setJavaScriptEnabled(true);
  myBrowser.setWebViewClient(new WebViewClient());
  myBrowser.loadUrl(openUrl);

 }

 @Override
 protected void onSaveInstanceState(Bundle outState) {
  //save url before config/orientation changed
  outState.putString(KEY_URL, myBrowser.getUrl());
  super.onSaveInstanceState(outState);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {

  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  int id = item.getItemId();
  if (id == R.id.action_openinbrowser) {
   
   Uri openUri = Uri.parse(myBrowser.getUrl());
   Intent myIntent = new Intent(Intent.ACTION_VIEW, openUri);
   startActivity(myIntent);
   
   return true;
  }
  return super.onOptionsItemSelected(item);
 }

}

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.myfifa.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <WebView
        android:id="@+id/mybrowser"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Modify /res/menu/main.xml to replace add option item of action_openinbrowser.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.myfifa.MainActivity" >

    <item
        android:id="@+id/action_openinbrowser"
        android:orderInCategory="100"
        android:title="@string/action_openinbrowser"
        app:showAsAction="ifRoom|withText"/>

</menu>

Modify /res/values/strings.xml to add string resource of "action_openinbrowser".
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">MyFIFA</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="action_openinbrowser">Open in Browser</string>

</resources>

uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml.

download filesDownload the files.

Download and try the APK.

Next:
Implement WebChromeClient to retrieve downloaded favicon and title in WebView.

The Official FIFA World Cup App on Android

The Official FIFA World Cup App
https://play.google.com/store/apps/details?id=com.fifa.fifaapp.android

Download the Official App of the 2014 FIFA World Cup Brazil™, unlocking all the exclusive coverage from the planet's biggest single-sporting event. With the official FIFA World Cup App, you can take the World Cup with you everywhere you go.


Wednesday, June 11, 2014

Set opacity/alpha of Button or drawable

Example to change opacity/alpha of Button or its CompoundDrawables, also show how to display CompoundDrawables on Button programmatically.


Example code:

MainActivity.java
package com.example.androidsetimageopacity;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;

public class MainActivity extends Activity {
 
 SeekBar barOpacityButton;
 SeekBar barOpacityDrawableLeft, barOpacityDrawableTop, 
   barOpacityDrawableRight, barOpacityDrawableBottom;
 Button button;
 
 Drawable drawableLeft, drawableTop, drawableRight, drawableBottom;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  drawableLeft = getResources()
    .getDrawable(android.R.drawable.ic_menu_call);
  drawableTop = getResources()
    .getDrawable(android.R.drawable.ic_menu_agenda);
  drawableRight = getResources()
    .getDrawable(android.R.drawable.ic_menu_compass);
  drawableBottom = getResources()
    .getDrawable(android.R.drawable.ic_menu_camera);

  button = (Button)findViewById(R.id.button);
  
  //display CompoundDrawables on Button programmatically
  button.setCompoundDrawablesWithIntrinsicBounds(
   drawableLeft, drawableTop, drawableRight, drawableBottom);
  
  barOpacityButton = 
    (SeekBar)findViewById(R.id.opacitybutton);
  barOpacityDrawableLeft = 
    (SeekBar)findViewById(R.id.opacitydrawableleft);
  barOpacityDrawableTop = 
    (SeekBar)findViewById(R.id.opacitydrawabletop);
  barOpacityDrawableRight = 
    (SeekBar)findViewById(R.id.opacitydrawableright);
  barOpacityDrawableBottom = 
    (SeekBar)findViewById(R.id.opacitydrawablebottom);

  barOpacityButton.setOnSeekBarChangeListener(
    barOpacityButtonOnSeekBarChangeListener);
  
  barOpacityDrawableLeft.setOnSeekBarChangeListener(
    barOpacityDrawableOnSeekBarChangeListener);
  barOpacityDrawableTop.setOnSeekBarChangeListener(
    barOpacityDrawableOnSeekBarChangeListener);
  barOpacityDrawableRight.setOnSeekBarChangeListener(
    barOpacityDrawableOnSeekBarChangeListener);
  barOpacityDrawableBottom.setOnSeekBarChangeListener(
    barOpacityDrawableOnSeekBarChangeListener);
 }
 
 OnSeekBarChangeListener barOpacityButtonOnSeekBarChangeListener =
  new OnSeekBarChangeListener(){

   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
     boolean fromUser) {
    float alpha = (float)progress/(float)seekBar.getMax();
    button.setAlpha(alpha); //for API Level 11+
   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {}

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {}
   
 };
 
 OnSeekBarChangeListener barOpacityDrawableOnSeekBarChangeListener =
  new OnSeekBarChangeListener(){
  
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
     boolean fromUser) {

    drawableLeft.setAlpha(
      barOpacityDrawableLeft.getProgress());
    drawableTop.setAlpha(
      barOpacityDrawableTop.getProgress());
    drawableRight.setAlpha(
      barOpacityDrawableRight.getProgress());
    drawableBottom.setAlpha(
      barOpacityDrawableBottom.getProgress());

   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {}

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {}
    
 };

}

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidsetimageopacity.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <SeekBar
        android:id="@+id/opacitybutton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="100"/>
    <SeekBar
        android:id="@+id/opacitydrawableleft"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="255"/>
    <SeekBar
        android:id="@+id/opacitydrawabletop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="255"/>
    <SeekBar
        android:id="@+id/opacitydrawableright"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="255"/>
    <SeekBar
        android:id="@+id/opacitydrawablebottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"
        android:progress="255"/>
    <TextView
        android:id="@+id/opacitysetting"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Opacity Button"/>

</LinearLayout>


Tuesday, June 10, 2014

Change Opacity of ImageView programmatically

Example to change opacity/alpha of ImageView programmatically, by calling deprecated setAlpha(alpha) or setImageAlpha(alpha) for APL Level 16 or higher.


MainActivity.java
package com.example.androidsetimageopacity;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 SeekBar barOpacity;
 ImageView image;
 TextView textOpacitySetting;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  image = (ImageView)findViewById(R.id.image);
  textOpacitySetting = (TextView)findViewById(R.id.opacitysetting);
  barOpacity = (SeekBar)findViewById(R.id.opacity);
  
  int alpha = barOpacity.getProgress();
  textOpacitySetting.setText(String.valueOf(alpha));
  image.setAlpha(alpha);   //deprecated
  //image.setImageAlpha(alpha); //for API Level 16+

  barOpacity.setOnSeekBarChangeListener(barOpacityOnSeekBarChangeListener);
 }
 
 OnSeekBarChangeListener barOpacityOnSeekBarChangeListener =
  new OnSeekBarChangeListener(){

   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
     boolean fromUser) {
    int alpha = barOpacity.getProgress();
    textOpacitySetting.setText(String.valueOf(alpha));
    image.setAlpha(alpha);   //deprecated
    //image.setImageAlpha(alpha); //for API Level 16+
   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {}

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {}
   
 };

}

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidsetimageopacity.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <SeekBar
        android:id="@+id/opacity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="511"
        android:progress="100"/>
    <TextView
        android:id="@+id/opacitysetting"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ic_launcher" />

</LinearLayout>


Use CountDownTimer to do something repeatly, second example.

It is another approach to do the same job as the previous example "Use CountDownTimer to do something repeatly, updating custom view". Instead of implementing CountDownTimer inside custom view as self running function of custom view, this example implement CountDownTimer in MainActivity, and handle all the timing functions. The custom view simple display the graphic only.


MainActivity.java
package com.example.androidcountdowntimer;

import android.app.Activity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity{
 
 CountDownTimer countDownTimer;
  
 int state;
 final static int STATE_IDLE = 0;
 final static int STATE_RED = 1;
 final static int STATE_GREEN = 2;
 final static int STATE_BLUE = 3;

 TextView myState, myCounter;
 Button btnStart;
 
 SpotView mySpotView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mySpotView = (SpotView)findViewById(R.id.myspotview);

  myState = (TextView) findViewById(R.id.mystate);
  
  myCounter = (TextView) findViewById(R.id.mycounter);
  btnStart = (Button) findViewById(R.id.start);
  btnStart.setOnClickListener(btnStartOnClickListener);
  
  state = STATE_IDLE;

 }
 
 OnClickListener btnStartOnClickListener = 
  new OnClickListener(){

   @Override
   public void onClick(View v) {
    
    if(countDownTimer!=null){
       countDownTimer.cancel();   
    }
    
    state = STATE_RED;
    
    countDownTimer = new CountDownTimer(3000, 500) {

     @Override
     public void onTick(long millisUntilFinished) {
      myCounter.setText("onTick: " + millisUntilFinished);
     }

     @Override
     public void onFinish() {
      if(state == STATE_RED){
       state = STATE_GREEN;
       mySpotView.setSpots(false, true, false);
       myState.setText("GREEN");
      }else if(state == STATE_GREEN){
       state = STATE_BLUE;
       mySpotView.setSpots(false, false, true);
       myState.setText("BLUE");
      }else if(state == STATE_BLUE){
       state = STATE_RED;
       mySpotView.setSpots(true, false, false);
       myState.setText("RED");
      }
      
      countDownTimer.start(); 
     }
    };
    
    mySpotView.setSpots(true, false, false);
    myState.setText("RED");
    countDownTimer.start();
   }  
 };

}

SpotView.java
package com.example.androidcountdowntimer;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class SpotView extends View{
 
 boolean spotRedOn, spotGreenOn, spotBlueOn;
 
 Paint paintRed, paintGreen, paintBlue;

 public SpotView(Context context) {
  super(context);
  init();
 }

 public SpotView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public SpotView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 
 private void init(){

  paintRed = new Paint();
  paintRed.setColor(Color.RED);
  paintRed.setStrokeWidth(1);
  paintRed.setStyle(Paint.Style.FILL);
  
  paintGreen = new Paint();
  paintGreen.setColor(Color.GREEN);
  paintGreen.setStrokeWidth(1);
  paintGreen.setStyle(Paint.Style.FILL);
  
  paintBlue = new Paint();
  paintBlue.setColor(Color.BLUE);
  paintBlue.setStrokeWidth(1);
  paintBlue.setStyle(Paint.Style.FILL);
  
  spotRedOn = spotGreenOn = spotBlueOn = false;
 }

 
 public void setSpots(boolean r, boolean g, boolean b){
  spotRedOn = r;
  spotGreenOn = g;
  spotBlueOn = b;
  invalidate();
 }

 @Override
 protected void onDraw(Canvas canvas) {
  
  float w = getWidth();
  float h = getHeight();
  
  if(spotRedOn){
   canvas.drawCircle(w/4, h/2, 100, paintRed);
  }
  
  if(spotGreenOn){
   canvas.drawCircle(w/2, h/2, 100, paintGreen);
  }
  
  if(spotBlueOn){
   canvas.drawCircle(3*w/4, h/2, 100, paintBlue);
  }
 }

}

Keep using the same layout in previous example.

Monday, June 9, 2014

Use CountDownTimer to do something repeatly, updating custom view.

This example show how to change color spots in custom view repeatly with CountDownTimer. We also implement callback interface on MainActivity.java, such that our custom view can callback MainActivity to pass something.

May be it is not a good practice to achieve such a job, just a example to use CountDownTimer.


MainActivity.java
package com.example.androidcountdowntimer;

import com.example.androidcountdowntimer.SpotView.SpotCallBack;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity 
  implements SpotCallBack{

 TextView myState, myCounter;
 Button btnStart;
 
 SpotView mySpotView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mySpotView = (SpotView)findViewById(R.id.myspotview);
  mySpotView.setCallback(this);

  myState = (TextView) findViewById(R.id.mystate);
  
  myCounter = (TextView) findViewById(R.id.mycounter);
  btnStart = (Button) findViewById(R.id.start);
  btnStart.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {
    mySpotView.startRun();
   }
  });

 }

 @Override
 public void cb_onTick(String msg) {
  myCounter.setText("onTick: " + msg);
 }

 @Override
 public void cb_onFinish(String msg) {
  myState.setText(msg);
 }

}

SpotView.java
package com.example.androidcountdowntimer;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.view.View;

public class SpotView extends View{
 
 //used to pass back something to MainActivity
 interface SpotCallBack {
  void cb_onTick(String msg);
  void cb_onFinish(String msg); 
 }
 
 SpotCallBack spotCallback;
 
 CountDownTimer countDownTimer;
 
 int state;
 final static int STATE_IDLE = 0;
 final static int STATE_RED = 1;
 final static int STATE_GREEN = 2;
 final static int STATE_BLUE = 3;
 
 Paint paintRed, paintGreen, paintBlue;

 public SpotView(Context context) {
  super(context);
  init();
 }

 public SpotView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public SpotView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 
 private void init(){
  state = STATE_IDLE;
  
  paintRed = new Paint();
  paintRed.setColor(Color.RED);
  paintRed.setStrokeWidth(1);
  paintRed.setStyle(Paint.Style.FILL);
  
  paintGreen = new Paint();
  paintGreen.setColor(Color.GREEN);
  paintGreen.setStrokeWidth(1);
  paintGreen.setStyle(Paint.Style.FILL);
  
  paintBlue = new Paint();
  paintBlue.setColor(Color.BLUE);
  paintBlue.setStrokeWidth(1);
  paintBlue.setStyle(Paint.Style.FILL);
 }
 
 public void setCallback(SpotCallBack cb){
  spotCallback = cb;
 }
 
 public void startRun(){
  state = STATE_RED;
  
  if(countDownTimer!=null){
   countDownTimer.cancel();
  }
  
  countDownTimer = new CountDownTimer(3000, 500) {
   
   @Override
   public void onTick(long millisUntilFinished) {
    spotCallback.cb_onTick(String.valueOf(millisUntilFinished));
   }
   
   @Override
   public void onFinish() {
    if(state == STATE_RED){
     state = STATE_GREEN;
     spotCallback.cb_onFinish("GREEN");
    }else if(state == STATE_GREEN){
     state = STATE_BLUE;
     spotCallback.cb_onFinish("BLUE");
    }else if(state == STATE_BLUE){
     state = STATE_RED;
     spotCallback.cb_onFinish("RED");
    }
    countDownTimer.start();
    invalidate();
   }
  };
  
  countDownTimer.start();
  spotCallback.cb_onFinish("RED");
  invalidate();
 }

 @Override
 protected void onDraw(Canvas canvas) {
  
  float w = getWidth();
  float h = getHeight();
  
  if(state == STATE_RED){
   canvas.drawCircle(w/4, h/2, 100, paintRed);
  }else if(state == STATE_GREEN){
   canvas.drawCircle(w/2, h/2, 100, paintGreen);
  }else if(state == STATE_BLUE){
   canvas.drawCircle(3*w/4, h/2, 100, paintBlue);
  }
 }

}

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.androidcountdowntimer.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Button
        android:id="@+id/start"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Start CountDownTimer" />
    <TextView
        android:id="@+id/mystate"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/mycounter"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <com.example.androidcountdowntimer.SpotView 
        android:id="@+id/myspotview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

Next example:
- Another approach to handle CountDownTimer in MainActivity, and the custom view handle display only.

Saturday, June 7, 2014

Restart CountDownTimer

Refer to my old exercise of "Count Down Timer", it is a one-shot CountDownTimer. In some case you want to reset/restart the CountDownTimer, you have to cancel the old one if exist, otherwise both old and new CountDownTimer will run together.

Example:


MainActivity.java
package com.example.androidcountdowntimer;

import android.app.Activity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

 TextView myCounter;
 Button btnStart;
 CountDownTimer countDownTimer;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  myCounter = (TextView) findViewById(R.id.mycounter);
  btnStart = (Button) findViewById(R.id.start);
  btnStart.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {

    //cancel the old countDownTimer 
    if(countDownTimer!=null){
     countDownTimer.cancel();
    }
    
    countDownTimer = new CountDownTimer(10000, 1000) {

     @Override
     public void onFinish() {
      myCounter.setText("Finished!");
     }

     @Override
     public void onTick(long millisUntilFinished) {
      myCounter.setText("Millisecond Until Finished: "
        + String.valueOf(millisUntilFinished));
     }

    };
    
    countDownTimer.start();
   }
  });

 }

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.androidcountdowntimer.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Button
        android:id="@+id/start"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Start CountDownTimer" />
    <TextView
        android:id="@+id/mycounter"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Next:
- Use CountDownTimer to do something repeatly, updating custom view; handle CountDownTimer in custom view.
- Another approach to use CountDownTimer to do something repeatly; handle CountDownTimer in MainActivity, custom view handle display only.

Animation follow touch path forward and backward

Further modify the example of "Change speed of Animation follow touch path" to add feature of animation direction, forward and backward.


AnimationView.java
package com.example.androidanimationalongpath;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class AnimationView extends View {
 
 Paint paint;
 
 Bitmap bm;
 int bm_offsetX, bm_offsetY;
 
 Path animPath;
 PathMeasure pathMeasure;
 float pathLength;
 
 float step;   //distance each step
 float distance;  //distance moved
 float curX, curY;
  
 float curAngle;  //current angle
 float targetAngle; //target angle
 float stepAngle; //angle each step

 float[] pos;
 float[] tan;
 
 Matrix matrix;
 
 Path touchPath;
 
 static int FORWARD = 1;
 static int BACKWARD = -1;
 int direction;

 public AnimationView(Context context) {
  super(context);
  initMyView();
 }

 public AnimationView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initMyView();
 }

 public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  initMyView();
 }
 
 public void initMyView(){
  paint = new Paint();
  paint.setColor(Color.BLUE);
  paint.setStrokeWidth(1);
  paint.setStyle(Paint.Style.STROKE);
    
  bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
  bm_offsetX = bm.getWidth()/2;
  bm_offsetY = bm.getHeight()/2;
  
  animPath = new Path();
  
  pos = new float[2];
  tan = new float[2];
  
  matrix = new Matrix();
  
  touchPath = new Path();
  
  step = 1;  //default
  stepAngle = 1; //default
  
  direction = FORWARD; //default
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if(animPath.isEmpty()){
   return;
  }
  
  canvas.drawPath(animPath, paint);
  
  matrix.reset();
  
  if((targetAngle-curAngle)>stepAngle){
   curAngle += stepAngle;
   matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
   matrix.postTranslate(curX, curY);
   canvas.drawBitmap(bm, matrix, null);
   
   invalidate();
  }else if((curAngle-targetAngle)>stepAngle){
   curAngle -= stepAngle;
   matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
   matrix.postTranslate(curX, curY);
   canvas.drawBitmap(bm, matrix, null);
   
   invalidate();
  }else{
   curAngle=targetAngle;
   
   if((direction==FORWARD && distance < pathLength)
    ||(direction==BACKWARD && distance > 0)){
    
    pathMeasure.getPosTan(distance, pos, tan);

    targetAngle = (float)(Math.atan2(tan[1], tan[0])*180.0/Math.PI);
    matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
    
    curX = pos[0]-bm_offsetX;
    curY = pos[1]-bm_offsetY;
    matrix.postTranslate(curX, curY);
    
    canvas.drawBitmap(bm, matrix, null);
    
    distance += step * direction;
    
    invalidate();
   }else{
    matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
    matrix.postTranslate(curX, curY);
    canvas.drawBitmap(bm, matrix, null);
   }
  }

 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  
  int action = event.getAction();
  
  switch(action){
  case MotionEvent.ACTION_DOWN:
   touchPath.reset();
   touchPath.moveTo(event.getX(), event.getY());
   break;
  case MotionEvent.ACTION_MOVE:
   touchPath.lineTo(event.getX(), event.getY());
   break;
  case MotionEvent.ACTION_UP:
   touchPath.lineTo(event.getX(), event.getY());
   animPath = new Path(touchPath);
   
   pathMeasure = new PathMeasure(animPath, false);
   pathLength = pathMeasure.getLength();
   
   //step = 1;
   distance = 0;
   curX = 0;
   curY = 0;
   
   //stepAngle = 1; 
   curAngle = 0;
   targetAngle = 0;
   
   direction = FORWARD;
   
   invalidate();
   
   break;
    
  }
  
  return true;
 }
 
 public void setSpeed(int sp){
  step = sp;
  stepAngle = sp;
 }
 
 public void replayForward(){
  direction = FORWARD;
  invalidate();
 }
 
 public void replayBackward(){
  direction = BACKWARD;
  invalidate();
 }

}

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidanimationalongpath.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        
        <Button 
            android:id="@+id/replayfor"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            android:text="Replay/Forward" />
        <Button 
            android:id="@+id/replayback"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            android:text="Replay/Backward" />
        
    </LinearLayout>

    <SeekBar
        android:id="@+id/speedbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10"
        android:progress="1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >
        
        <com.example.androidanimationalongpath.AnimationView
            android:id="@+id/animationview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="10dp"
            android:background="@android:color/darker_gray" />

    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidanimationalongpath;

import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.os.Bundle;

public class MainActivity extends ActionBarActivity {
 
 AnimationView animationView;
 SeekBar speedBar;
 Button btnForward, btnbackward;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  animationView = (AnimationView)findViewById(R.id.animationview);
  speedBar = (SeekBar)findViewById(R.id.speedbar);
  speedBar.setOnSeekBarChangeListener(speedBarOnSeekBarChangeListener);
  animationView.setSpeed(speedBar.getProgress()); //set default speed
  
  btnForward = (Button)findViewById(R.id.replayfor);
  btnForward.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    animationView.replayForward();
   }});
  
  btnbackward = (Button)findViewById(R.id.replayback);
  btnbackward.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    animationView.replayBackward();
   }});
 }
 
 OnSeekBarChangeListener speedBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener(){

   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
     boolean fromUser) {
    //offset from SeekBar 0~19 to step 1~10
    animationView.setSpeed(progress);
   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {}

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {}
  };

}


download filesDownload the files.

More example of Drawing Path on canvas of custom View.


Friday, June 6, 2014

Learning Android Intents

Explore and apply the power of intents in Android application development

Learning Android Intents

Overview
  • Understand Android Intents to make application development quicker and easier
  • Categorize and implement various kinds of Intents in your application
  • Perform data manipulation within Android applications
In Detail

Android is an emerging technology with loads of apps in the Google Play Market. Till date, it is the biggest marvel in Smartphone technology, propelling a larger number of developers into Android application development. Intent is an essential part of any Android Application and no Android application is complete without using them. Features such as listening broadcasts, sending messages, sharing via social networks, notifications, hardware components including camera, sensors, Wi-Fi, and more, can be used in your applications by using Intents.

This practical guide focuses on using intents to make the best use of various features of Android platform. It is ideal for those developers who want to understand the backbone and the domain of Android Intents, its power, and the need for it inside an Android application. Practical, in-depth examples are used throughout the book, to help you understand the key concepts.

The book starts with introducing the very basic concepts of Android, and its various facts and figures such as different Android versions, their release dates, evolution of Android phones and so on. While covering the basic technical concepts, it proceeds from the easiest route of introducing Android Intents towards the more practical view of Android Intents in terms of components and features.

You will learn how to use different components and features such as transfer data between activities, invoke various features and components of Android, execute different in-built and custom-made services, use hardware and software components of Android device, and start Pending Intents & notifications. You will gain better theoretical knowledge of what is running behind the concepts of Android Intents, and practical knowledge of the mobile-efficient ways to perform a certain task using Android Intents.

Towards the end, you will have a clear vision and a practical grip on Android intents and its features. Learning Android Intents is a proper guide to give you the best knowledge of Intents.

What you will learn from this book

  • Understand Android Intents and their importance in Android apps
  • Get to grips with the different types of Intents and their implementation
  • Discover data transfer methods in Android Intents along with their optimization and performance comparisons
  • Explore the implementation of Intents while invoking Android Features in an application.
  • Use Intent Filters and their sub-domains in order to perform various actions and sorting categories in Android Intents
  • Catch different events while working with Broadcast Receiver and perform various actions
  • Implement pending Intents and Intent Service, sending text to the Notification Panel, and much more
Approach

The book will take an easy-to-follow and engaging tutorial approach, providing a practical and comprehensive way to learn Android intents.

Change speed of Animation follow touch path

This exercise change the speed of the former example of "Animation follow touch path".


AnimationView.java
package com.example.androidanimationalongpath;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class AnimationView extends View {
 
 Paint paint;
 
 Bitmap bm;
 int bm_offsetX, bm_offsetY;
 
 Path animPath;
 PathMeasure pathMeasure;
 float pathLength;
 
 float step;   //distance each step
 float distance;  //distance moved
 float curX, curY;
  
 float curAngle;  //current angle
 float targetAngle; //target angle
 float stepAngle; //angle each step

 float[] pos;
 float[] tan;
 
 Matrix matrix;
 
 Path touchPath;

 public AnimationView(Context context) {
  super(context);
  initMyView();
 }

 public AnimationView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initMyView();
 }

 public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  initMyView();
 }
 
 public void initMyView(){
  paint = new Paint();
  paint.setColor(Color.BLUE);
  paint.setStrokeWidth(1);
  paint.setStyle(Paint.Style.STROKE);
    
  bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
  bm_offsetX = bm.getWidth()/2;
  bm_offsetY = bm.getHeight()/2;
  
  animPath = new Path();
  
  pos = new float[2];
  tan = new float[2];
  
  matrix = new Matrix();
  
  touchPath = new Path();
  
  step = 1;  //default
  stepAngle = 1; //default
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if(animPath.isEmpty()){
   return;
  }
  
  canvas.drawPath(animPath, paint);
  
  matrix.reset();
  
  if((targetAngle-curAngle)>stepAngle){
   curAngle += stepAngle;
   matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
   matrix.postTranslate(curX, curY);
   canvas.drawBitmap(bm, matrix, null);
   
   invalidate();
  }else if((curAngle-targetAngle)>stepAngle){
   curAngle -= stepAngle;
   matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
   matrix.postTranslate(curX, curY);
   canvas.drawBitmap(bm, matrix, null);
   
   invalidate();
  }else{
   curAngle=targetAngle;
   if(distance < pathLength){
    pathMeasure.getPosTan(distance, pos, tan);

    targetAngle = (float)(Math.atan2(tan[1], tan[0])*180.0/Math.PI);
    matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
    
    curX = pos[0]-bm_offsetX;
    curY = pos[1]-bm_offsetY;
    matrix.postTranslate(curX, curY);
    
    canvas.drawBitmap(bm, matrix, null);
    
    distance += step;
    
    invalidate();
   }else{
    matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
    matrix.postTranslate(curX, curY);
    canvas.drawBitmap(bm, matrix, null);
   }
  }

 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  
  int action = event.getAction();
  
  switch(action){
  case MotionEvent.ACTION_DOWN:
   touchPath.reset();
   touchPath.moveTo(event.getX(), event.getY());
   break;
  case MotionEvent.ACTION_MOVE:
   touchPath.lineTo(event.getX(), event.getY());
   break;
  case MotionEvent.ACTION_UP:
   touchPath.lineTo(event.getX(), event.getY());
   animPath = new Path(touchPath);
   
   pathMeasure = new PathMeasure(animPath, false);
   pathLength = pathMeasure.getLength();
   
   //step = 1;
   distance = 0;
   curX = 0;
   curY = 0;
   
   //stepAngle = 1; 
   curAngle = 0;
   targetAngle = 0;
   
   invalidate();
   
   break;
    
  }
  
  return true;
 }
 
 public void setSpeed(int sp){
  step = sp;
  stepAngle = sp;
 }

}

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidanimationalongpath.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <SeekBar
        android:id="@+id/speedbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10"
        android:progress="1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >
        
        <com.example.androidanimationalongpath.AnimationView
            android:id="@+id/animationview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="10dp"
            android:background="@android:color/darker_gray" />

    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidanimationalongpath;

import android.support.v7.app.ActionBarActivity;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.os.Bundle;

public class MainActivity extends ActionBarActivity {
 
 AnimationView animationView;
 SeekBar speedBar;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  animationView = (AnimationView)findViewById(R.id.animationview);
  speedBar = (SeekBar)findViewById(R.id.speedbar);
  speedBar.setOnSeekBarChangeListener(speedBarOnSeekBarChangeListener);
  animationView.setSpeed(speedBar.getProgress()); //set default speed
 }
 
 OnSeekBarChangeListener speedBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener(){

   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
     boolean fromUser) {
    //offset from SeekBar 0~19 to step 1~10
    animationView.setSpeed(progress);
   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {}

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {}};
   

}


download filesDownload the files.

More example of Drawing Path on canvas of custom View.

Xamarin Mobile Application Development for Android

Learn to develop full featured Android apps using your existing C# skills with Xamarin.Android

Xamarin Mobile Application Development for Android

Overview
  • Gain an understanding of both the Android and Xamarin platforms
  • Build a working multi-view Android app incrementally throughout the book
  • Work with device capabilities such as location sensors and the camera
In Detail

Technology trends come and go, but few have generated the excitement, momentum, or long-term impact that mobile computing has. Mobile computing impacts people's lives at work and at home on a daily basis. Many companies and individual developers are looking to become a part of the movement but are unsure how to best utilize their existing skills and assets. The Xamarin suite of products provides new opportunities to those who already have a significant investment in C# development skills and .NET code bases, and would like to enter into this new, exciting world.

This example-oriented guide provides a practical approach to quickly learning the fundamentals of Android app development using C# and Xamarin.Android. It will lead the readers through building an Android app step-by-step with steadily increasing complexity.

This book begins with an overview of the Android and Xamarin platforms to provide you with a solid understanding of the environment you will be working in. You will then be gradually walked through building and testing an Android app using C# and the Xamarin.Android product. You will learn the basics of interacting with some of the more interesting aspects of Android devices including location services, the camera, and maps. You will also be given the opportunity to work with three different layout managers to gain an understanding of the various options available for arranging controls and content. The book ends with a discussion on the final steps involved in preparing apps for deployment to the various Android app stores.

In a relatively short period of time, developers familiar with C# and rich client technologies such as WPF and Silverlight will be effectively developing, testing, and delivering Android apps.

What you will learn from this book
  • Build a multi-view Android application with navigation
  • Utilize the ActionBar for app actions
  • Create a simple JSON-based persistent service to save data locally on the device
  • Lay out content using the LinearLayout, RelativeLayout, and TableLayout layout managers
  • Use a ListView (AdapterView) and Adapter to build a view that is populated by dynamic data
  • Capture the current location of a device and determine the street address
  • Integrate with the map app to display a point of interest
  • Capture and save a photo
  • Test, debug, and deploy an Android app
Approach

A step-by-step tutorial that follows the development of a simple Android app from end to end, through troubleshooting, and then distribution. The language used assumes a knowledge of basic C#.

Who this book is written for

If you are a C# developer with a desire to develop Android apps and want to enhance your existing skill set, then this book is for you. It is assumed that you have a good working knowledge of C#, .NET, and object-oriented software development. Familiarity with rich client technologies such as WPF or Silverlight is also helpful, but not required.

Wednesday, June 4, 2014

DashPathEffect, apply dash effect on path

To apply DashPathEffect on path:
  //init DashPathEffect
  dashPathEffect = new DashPathEffect(
    new float[]{10.0f, 5.0f}, //interval 
    0);       //phase

  paintDash.setPathEffect(dashPathEffect);

Example:

Modify from last example "Draw rotated path by rotating canvas".

MyView.java
package com.example.androiddrawpath;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;

public class MyView extends View {
 
 MyShape myShape;
 float ratioRadius, ratioInnerRadius;
 int numberOfPoint = 3; //default
 
 float rotate;
 
 //corresponding to UI element
 TextView textLayerInfo;
 
 DashPathEffect dashPathEffect;

 public MyView(Context context) {
  super(context);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  initMyView();
 }
 
 public void initMyView(){
  myShape = new MyShape();
  
  //init DashPathEffect
  dashPathEffect = new DashPathEffect(
    new float[]{10.0f, 5.0f}, //interval 
    0);       //phase
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  
  long starting = System.nanoTime();
  
  int w = getWidth();
  int h = getHeight();
  
  if((w==0) || (h==0)){
   return;
  }
  
  float x = (float)w/2.0f;
  float y = (float)h/2.0f;
  float radius, innerRadius;
  if(w > h){
   radius = h * ratioRadius;
   innerRadius = h * ratioInnerRadius;
  }else{
   radius = w * ratioRadius;
   innerRadius = w * ratioInnerRadius;
  }
  
  myShape.setStar(x, y, radius, innerRadius, numberOfPoint);
  
  //Save and rotate canvas 
  canvas.save();
  canvas.rotate(rotate, x, y);

  Paint paintDash = myShape.getPaint();
  paintDash.setPathEffect(dashPathEffect);
  
  canvas.drawPath(myShape.getPath(), myShape.getPaint());
  
  //restore canvas
  canvas.restore();
  
  long end = System.nanoTime();
  
  String info = "myView.isHardwareAccelerated() = " + isHardwareAccelerated() + "\n"
    + "canvas.isHardwareAccelerated() = " + canvas.isHardwareAccelerated() + "\n"
    + "processing time (reference only) : " + String.valueOf(end - starting) + " (ns)";
  textLayerInfo.setText(info);
  
 }
 
 public void setShapeRadiusRatio(float ratio){
  ratioRadius = ratio;
 }
 
 public void setShapeInnerRadiusRatio(float ratio){
  ratioInnerRadius = ratio;
 }
 
 public void setNumberOfPoint(int pt){
  numberOfPoint = pt;
 }
 
 public void passElements(TextView textLayerInfo){
  this.textLayerInfo = textLayerInfo;
 }

 public void setShapeRotate(int rot){
  rotate = (float)rot;
 }

}

Modify MyShape.java to call paint.setStyle(Paint.Style.STROKE).
package com.example.androiddrawpath;

import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;

public class MyShape {

 private Paint paint;
 private Path path;

 public MyShape() {
  paint = new Paint();
  paint.setColor(Color.BLUE);
  paint.setStrokeWidth(3);
  paint.setStyle(Paint.Style.STROKE);
  //paint.setStyle(Paint.Style.FILL);
  //paint.setStyle(Paint.Style.FILL_AND_STROKE);
  
  path = new Path();
 }

 public void setCircle(float x, float y, float radius, Path.Direction dir){
  path.reset();
  path.addCircle(x, y, radius, dir);
 }
 
 public void setStar(float x, float y, float radius, float innerRadius, int numOfPt){
  
  double section = 2.0 * Math.PI/numOfPt;
  
  path.reset();
  path.moveTo(
   (float)(x + radius * Math.cos(0)), 
   (float)(y + radius * Math.sin(0)));
  path.lineTo(
   (float)(x + innerRadius * Math.cos(0 + section/2.0)), 
   (float)(y + innerRadius * Math.sin(0 + section/2.0)));
  
  for(int i=1; i<numOfPt; i++){
   path.lineTo(
    (float)(x + radius * Math.cos(section * i)), 
    (float)(y + radius * Math.sin(section * i)));
   path.lineTo(
     (float)(x + innerRadius * Math.cos(section * i + section/2.0)), 
     (float)(y + innerRadius * Math.sin(section * i + section/2.0)));
  }
  
  path.close();
  
 }
 
 public Path getPath(){
  return path;
 }
 
 public Paint getPaint(){
  return paint;
 }

}


download filesDownload the files.

More example of Drawing Path on canvas of custom View.