Friday, April 29, 2016

Problem of BluetoothChat example

Last year (June 26, 2015) I have a post to show how to "Import Android code sample of BluetoothChat to Android Studio". It work perfectly AT THAT TIME. But I re-try it again on update Android Studio 2.1, and test on ASUS Zenfont 2 running Android 5.0 and XiaoMi RedMi 2 running Android 4.4.4; it almost NOT WORK - no problem in pairing and connecting, but fail to send/receive data: sometimes one-way chat, sometimes totally cannot chat!!!

Then I change the Project Structure to target sdk version Android 4.4 (KitKat), it can be improved, but not 100%.


I have no idea is it related to new Android Studio/SDK? or related to new Android version? or related to individual device(s)?


What’s New in Android Studio 2.1

Android Studio 2.1 is required to try out new features and APIs of the Android N developer preview including the new Jack compiler and Java 8 language support. It also includes performance improvements to Instant Run, and a number of bug fixes and stability improvements.

Android Studio 2.1 is now available to download through the Stable release channel.


Tuesday, April 26, 2016

Request Location Updates with LocationListener.onLocationChanged()


This example show how to request location auto updates, by implementing LocationListener.onLocationChanged().


To use Google Play Service in your project, you have to Add Google Play Services to Android Studio project.

MainActivity.java
package com.blogspot.android_er.androidautoupdatelocation;

import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

import java.text.DateFormat;

public class MainActivity extends AppCompatActivity
        implements LocationListener, GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    GoogleApiClient mGoogleApiClient;
    LocationRequest mLocationRequest;
    TextView textAutoUpdateLocation;

    static final int MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textAutoUpdateLocation = (TextView) findViewById(R.id.autoupdatelocation);

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5000);
        mLocationRequest.setFastestInterval(5000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    protected void onStart() {
        mGoogleApiClient.connect();
        super.onStart();
    }

    @Override
    protected void onStop() {
        mGoogleApiClient.disconnect();
        super.onStop();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location != null) {
            String strLocation =
                    DateFormat.getTimeInstance().format(location.getTime()) + "\n" +
                            "Latitude=" + location.getLatitude() + "\n" +
                            "Longitude=" + location.getLongitude();
            textAutoUpdateLocation.setText(strLocation);
        }
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {

        if (ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_COARSE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);

            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, mLocationRequest, this);

    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(MainActivity.this,
                            "permission was granted, :)",
                            Toast.LENGTH_LONG).show();

                    try{
                        LocationServices.FusedLocationApi.requestLocationUpdates(
                                mGoogleApiClient, mLocationRequest, this);
                    }catch(SecurityException e){
                        Toast.makeText(MainActivity.this,
                                "SecurityException:\n" + e.toString(),
                                Toast.LENGTH_LONG).show();
                    }
                } else {
                    Toast.makeText(MainActivity.this,
                            "permission denied, ...:(",
                            Toast.LENGTH_LONG).show();
                }
                return;
            }
        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Toast.makeText(MainActivity.this,
                "onConnectionFailed: \n" + connectionResult.toString(),
                Toast.LENGTH_LONG).show();
    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidautoupdatelocation.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:id="@+id/autoupdatelocation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30dp"
        android:textStyle="italic|bold"/>
</LinearLayout>


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

download filesDownload the files .

It's not complete solution, to know more details, refer Android Developers - Receiving Location Updates, a example of "LocationUpdates" is provided.


Related:
Get my Last Known Location, by calling LocationServices.FusedLocationApi.getLastLocation()

Sunday, April 24, 2016

App Inventor 2 Essentials

A step-by-step introductory guide to mobile app development with App Inventor 2

App Inventor 2 Essentials

About This Book
  • Get an introduction to the functionalities of App Inventor 2 and use it to unleash your creativity
  • Learn to navigate the App Inventor platform, develop basic coding skills and become familiar with a blocks based programming language
  • Build your very first mobile app and feel proud of your accomplishment
  • Follow tutorials to expand your app development skills
Who This Book Is For
App Inventor 2 Essentials is for anyone who wants to learn to make mobile apps for Android devices – no prior coding experience is necessary.

What You Will Learn
  • Perform technical setup and navigate the App Inventor platform
  • Utilize the interactive development environment by pairing a mobile device with a computer using Wi-Fi or USB
  • Build three apps: a game, an event app and a raffle app
  • Create the user interface of the app in the Designer and program the code in the Blocks Editor
  • Integrate basic computer science principles along with more complex elements such fusion tables and lists
  • Test and troubleshoot your applications
  • Publish your apps on Google Play Store to reach a wide audience
  • Unleash your creativity for further app development
In Detail
App Inventor 2 will take you on a journey of mobile app development. We begin by introducing you to the functionalities of App Inventor and giving you an idea about the types of apps you can develop using it. We walk you through the technical set up so you can take advantage of the interactive development environment (live testing). You will get hands-on, practical experience building three different apps using tutorials. Along the way, you will learn computer science principles as well as tips to help you prepare for the creative process of building an app from scratch. By the end of the journey, you will learn how to package an app and deploy it to app markets. App Inventor 2 Essentials prepares you to amass a resource of skills, knowledge and experience to become a mobile app developer

Style and approach
Every topic in this book is explained in step-by-step and easy-to-follow fashion, accompanied with screenshots of the interface that will make it easier for you to understand the processes.

Saturday, April 23, 2016

Fixed for the time being, error of "umake android" on Ubuntu 16.04: A default framework for category Android was requested where there is none

Android Studio 2.0 on Ubuntu 16.04
Refer to the last post "Error in installing Android Studio on Ubuntu 16.04 using umake", when I install Android Studio on Ubuntu 16.04 using umake, error message show "ERROR: A default framework for category Android was requested where there is none".


Android studio and the sdk changed the download pages significantly.
They've been fixed in master, and will be working again in the next release.

If you need you can clone the repository and install from that.
It's less than ideal, but if it can help you for the time being.

git clone https://github.com/ubuntu/ubuntu-make
cd ubuntu-make
bin/umake android

reference: LyzardKing comments in https://github.com/ubuntu/ubuntu-make/issues/302

This video show how:


Onced installed. openjdk version "1.8.0_03-Ubuntu" will also be installed.


Updated@2016-05-24:
Another approach is to add the ppa to get the latest version.
It's updated frequently as it's easier to upload a version to a ppa.

$ sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make
$ sudo apt update
$ sudo apt install ubuntu-make


Friday, April 22, 2016

Error in installing Android Studio on Ubuntu 16.04 using umake

Updated@2016-04-24:
Fixed for the time being, read next post.



I tried to install Android Studio on Ubuntu 16.04 using umake, refer "Install Android Studio 2.0 on 64-bit Ubuntu 15.10 with Ubuntu Make (umake)". It's reported with error of:


$ umake android
ERROR: A default framework for category Android was requested where there is none
usage: umake android [-h] {android-ndk} ...







Install Ubuntu 16.04 LTS on VirtualBox/Windows 10


- Visit Ubuntu web site to download the ISO of Ubuntu 16.04 LTS desktop.


Follow the steps in this video to install Ubuntu 16.04 LTS on VirtualBox/Windows 10.


After Ubuntu installed, it's strongly recommend to install Guest Additions Module, such that your virtual Ubuntu can be re-size and  fit your screen.




Install Ubuntu-MATE 16.04 on VirtualBox/Windows 10

Ubuntu-MATE is another community developed Ubuntu based operating system that beautifully integrates the MATE desktop.


glmark2 test on VirtualBox/Ubuntu-MATE 16.04


glmark2-es2 test on VirtualBox/Ubuntu-MATE 16.04


On ASUS Notbook Aspire E 15, E5-573
CPU: Intel Core i5-5200U
with 3D Acceleration enabled in VirtualBox.


Requesting Permissions of Manifest.permission.ACCESS_FINE_LOCATION at Run Time

Refer to last example of "Get my Last Known Location, by calling LocationServices.FusedLocationApi.getLastLocation()" in Android Studio, you will be prompted with "code should explicitly check to see if permission is available (with 'checkPermission') or explicitly handle a potential 'SecurityException'" on the code "LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient)".


Last example handle 'SecurityException' with try/catch. This example show another approach to check if permission is available with 'checkPermission', then call ActivityCompat.requestPermissions() if need, and handle the user answer in onRequestPermissionsResult().

reference: Android Developers - Requesting Permissions at Run Time




notice for sending location to Android Emulator: I have to open another app, Google Maps, to monitor location, otherwise my example cannot get the updated location.

Edit MainActivity.java from last post.
package com.blogspot.android_er.androidgetlastlocation;

import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;

public class MainActivity extends AppCompatActivity
        implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    Button btnGetLastLocation;
    TextView textLastLocation;

    GoogleApiClient mGoogleApiClient;
    Location mLastLocation;

    static final int MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnGetLastLocation = (Button) findViewById(R.id.getlastlocation);
        btnGetLastLocation.setOnClickListener(btnGetLastLocationOnClickListener);
        textLastLocation = (TextView) findViewById(R.id.lastlocation);

        // Create an instance of GoogleAPIClient.
        if (mGoogleApiClient == null) {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }
    }

    View.OnClickListener btnGetLastLocationOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            if (mGoogleApiClient != null) {
                if (mGoogleApiClient.isConnected()) {
                    getMyLocation();
                } else {
                    Toast.makeText(MainActivity.this,
                            "!mGoogleApiClient.isConnected()", Toast.LENGTH_LONG).show();
                }
            } else {
                Toast.makeText(MainActivity.this,
                        "mGoogleApiClient == null", Toast.LENGTH_LONG).show();
            }
        }
    };

    /*
    // Handle 'SecurityException' with try/catch
    private void getMyLocation(){
        try{
            //code should explicitly check to see if permission is available
            //(with 'checkPermission') or explicitly handle a potential 'SecurityException'
            //
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            if (mLastLocation != null) {
                textLastLocation.setText(
                        String.valueOf(mLastLocation.getLatitude()) + "\n"
                                + String.valueOf(mLastLocation.getLongitude()));
                Toast.makeText(MainActivity.this,
                        String.valueOf(mLastLocation.getLatitude()) + "\n"
                                + String.valueOf(mLastLocation.getLongitude()),
                        Toast.LENGTH_LONG).show();
            }else{
                Toast.makeText(MainActivity.this,
                        "mLastLocation == null",
                        Toast.LENGTH_LONG).show();
            }
        } catch (SecurityException e){
            Toast.makeText(MainActivity.this,
                    "SecurityException:\n" + e.toString(),
                    Toast.LENGTH_LONG).show();
        }
    }
    */


    //------------------------------------------------------------------------------
    //ref: Requesting Permissions at Run Time
    //http://developer.android.com/training/permissions/requesting.html
    //------------------------------------------------------------------------------
    private void getMyLocation() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.


            //------------------------------------------------------------------------------
            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);

            return;
        }
        mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (mLastLocation != null) {
            textLastLocation.setText(
                    String.valueOf(mLastLocation.getLatitude()) + "\n"
                            + String.valueOf(mLastLocation.getLongitude()));
            Toast.makeText(MainActivity.this,
                    String.valueOf(mLastLocation.getLatitude()) + "\n"
                            + String.valueOf(mLastLocation.getLongitude()),
                    Toast.LENGTH_LONG).show();
        }else{
            Toast.makeText(MainActivity.this,
                    "mLastLocation == null",
                    Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(MainActivity.this,
                            "permission was granted, :)",
                            Toast.LENGTH_LONG).show();
                    getMyLocation();

                } else {
                    Toast.makeText(MainActivity.this,
                            "permission denied, ...:(",
                            Toast.LENGTH_LONG).show();
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request
        }
    }

    @Override
    protected void onStart() {
        mGoogleApiClient.connect();
        super.onStart();
    }

    @Override
    protected void onStop() {
        mGoogleApiClient.disconnect();
        super.onStop();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        getMyLocation();
    }

    @Override
    public void onConnectionSuspended(int i) {
        Toast.makeText(MainActivity.this,
                "onConnectionSuspended: " + String.valueOf(i),
                Toast.LENGTH_LONG).show();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Toast.makeText(MainActivity.this,
                "onConnectionFailed: \n" + connectionResult.toString(),
                Toast.LENGTH_LONG).show();
    }
}


Related:
Request Location Updates with LocationListener.onLocationChanged()

Thursday, April 21, 2016

Get my Last Known Location, by calling LocationServices.FusedLocationApi.getLastLocation()

The example show how to get my Last Known Location, by calling LocationServices.FusedLocationApi.getLastLocation() of Google play Service.


reference: Android Developers - Getting the Last Known Location


To use Google Play Service in your project, you have to Add Google Play Services to Android Studio project, refer last post.

MainActivity.java
package com.blogspot.android_er.androidgetlastlocation;

import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;

public class MainActivity extends AppCompatActivity
        implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    Button btnGetLastLocation;
    TextView textLastLocation;

    GoogleApiClient mGoogleApiClient;
    Location mLastLocation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnGetLastLocation = (Button) findViewById(R.id.getlastlocation);
        btnGetLastLocation.setOnClickListener(btnGetLastLocationOnClickListener);
        textLastLocation = (TextView) findViewById(R.id.lastlocation);

        // Create an instance of GoogleAPIClient.
        if (mGoogleApiClient == null) {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }
    }

    View.OnClickListener btnGetLastLocationOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            if(mGoogleApiClient != null){
                if(mGoogleApiClient.isConnected()){
                    getMyLocation();
                }else{
                    Toast.makeText(MainActivity.this,
                            "!mGoogleApiClient.isConnected()", Toast.LENGTH_LONG).show();
                }
            }else{
                Toast.makeText(MainActivity.this,
                        "mGoogleApiClient == null", Toast.LENGTH_LONG).show();
            }
        }
    };

    private void getMyLocation(){
        try{
            /* code should explicitly check to see if permission is available
            (with 'checkPermission') or explicitly handle a potential 'SecurityException'
             */
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            if (mLastLocation != null) {
                textLastLocation.setText(
                        String.valueOf(mLastLocation.getLatitude()) + "\n"
                                + String.valueOf(mLastLocation.getLongitude()));
                Toast.makeText(MainActivity.this,
                        String.valueOf(mLastLocation.getLatitude()) + "\n"
                                + String.valueOf(mLastLocation.getLongitude()),
                        Toast.LENGTH_LONG).show();
            }else{
                Toast.makeText(MainActivity.this,
                        "mLastLocation == null",
                        Toast.LENGTH_LONG).show();
            }
        } catch (SecurityException e){
            Toast.makeText(MainActivity.this,
                    "SecurityException:\n" + e.toString(),
                    Toast.LENGTH_LONG).show();
        }
    }

    @Override
    protected void onStart() {
        mGoogleApiClient.connect();
        super.onStart();
    }

    @Override
    protected void onStop() {
        mGoogleApiClient.disconnect();
        super.onStop();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        getMyLocation();
    }

    @Override
    public void onConnectionSuspended(int i) {
        Toast.makeText(MainActivity.this,
                "onConnectionSuspended: " + String.valueOf(i),
                Toast.LENGTH_LONG).show();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Toast.makeText(MainActivity.this,
                "onConnectionFailed: \n" + connectionResult.toString(),
                Toast.LENGTH_LONG).show();
    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidgetlastlocation.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/getlastlocation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get my last location"/>

    <TextView
        android:id="@+id/lastlocation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>


Permission:
Add uses-permission of ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION in your AndroidManifest.xml. In this example, I use ACCESS_FINE_LOCATION. (Read remark below about getLastLocation() return null)


remark: getLastLocation() return null!
- When run on real device, ASUS Zenfone 2 running Android 5.0:
According to Android Developers - Getting the Last Known Location, you can add permission of ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION. But in my trial, if add permission of ACCESS_COARSE_LOCATION, getLastLocation() return null mostly, around 2 success in more than 30 try. if add permission of ACCESS_FINE_LOCATION, it almost success every time.


Next:
- Requesting Permissions of Manifest.permission.ACCESS_FINE_LOCATION at Run Time

Related:
Request Location Updates with LocationListener.onLocationChanged()

Add Google Play Services to Android Studio project

To add Google Play Services to Android Studio project, make sure Google Play services SDK is installed.


Open Project Structure to add dependencies of compile 'com.google.android.gms:play-services:8.4.0' to app/build.gradle.

To open Project Structure:
- Click File - Project Structure, or
- Right click your App, select Open Module Setting..., or
- With your App selected, press F4.

Follow the video to add 'com.google.android.gms:play-services:x.x.x' to your module.


After finished,  dependencies of compile 'com.google.android.gms:play-services:8.4.0' will be added to app/build.gradle.




App Inventor 2 Graphics, Animation and Charts

App Inventor 2 Graphics, Animation and Charts

Learn to create apps using simplified interactive image sprites and to control movement using a finger on the screen or by tilting the phone or tablet. Learn how to use the "Canvas" features for drawing, including a unique way to implement traditional animation features.

Volume 4 introduces the use of graphics drawing features, including general graphics features, image sprites, animation and charting. Charting refers to the creation of line, column, scatter plot, and strip recorder charts commonly used in business and finance.

This is volume 4 of a 4 volume set. Volume 1 introduces App Inventor programming, Volume 2 introduces advanced features and Volume 3 covers databases and files.

Includes numerous sample apps, detailed explanations, illustrations, app source code downloads and links to video tutorials. A printed edition of this title is available from Amazon's CreateSpace subsidiary at https://www.createspace.com/5801361

Visit the web site at appinventor.pevest.com to learn more about App Inventor and find more tutorials, resources, links to App Inventor books and other App Inventor web sites.

Monday, April 18, 2016

Add MediaController to MediaPlayer


This example show how to add MediaController to simple MediaPlayer in previous example.


MainActivity.java
package com.blogspot.android_er.androidmediaplayer;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.MediaController;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends AppCompatActivity
        implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener,
        MediaController.MediaPlayerControl {

    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private MediaPlayer mediaPlayer;

    private MediaController mediaController;
    private Handler handler = new Handler();

    String videoSource =
            "https://sites.google.com/site/androidexample9/download/RunningClock.mp4";

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

        surfaceView = (SurfaceView)findViewById(R.id.surfaceview);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);

        surfaceView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(mediaController != null){
                    mediaController.show();
                }
                return false;
            }
        });

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        Toast.makeText(MainActivity.this,
                "surfaceCreated()", Toast.LENGTH_LONG).show();
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setDisplay(surfaceHolder);
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.setOnPreparedListener(this);
        try {
            mediaPlayer.setDataSource(videoSource);
            mediaPlayer.prepare();

            mediaController = new MediaController(this);

        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this,
                    "something wrong!\n" + e.toString(),
                    Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder,
                               int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mediaPlayer.start();
        Toast.makeText(MainActivity.this,
                "onPrepared()", Toast.LENGTH_LONG).show();

        mediaController.setMediaPlayer(this);
        mediaController.setAnchorView(surfaceView);
        handler.post(new Runnable() {

            public void run() {
                mediaController.setEnabled(true);
                mediaController.show();
            }
        });
    }

    @Override
    public void start() {
        mediaPlayer.start();
    }

    @Override
    public void pause() {
        mediaPlayer.pause();
    }

    @Override
    public int getDuration() {
        return mediaPlayer.getDuration();
    }

    @Override
    public int getCurrentPosition() {
        return mediaPlayer.getCurrentPosition();
    }

    @Override
    public void seekTo(int pos) {
        mediaPlayer.seekTo(pos);
    }

    @Override
    public boolean isPlaying() {
        return mediaPlayer.isPlaying();
    }

    @Override
    public int getBufferPercentage() {
        return 0;
    }

    @Override
    public boolean canPause() {
        return true;
    }

    @Override
    public boolean canSeekBackward() {
        return true;
    }

    @Override
    public boolean canSeekForward() {
        return true;
    }

    @Override
    public int getAudioSessionId() {
        return mediaPlayer.getAudioSessionId();
    }
}


For layout and permission, refer to the previous post "MediaPlayer example to play video from Internet".


Saturday, April 16, 2016

MediaPlayer example to play video from Internet


Example using MediaPlayer/SurfaceView example to play video from Internet.

MainActivity.java
package com.blogspot.android_er.androidmediaplayer;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends AppCompatActivity
        implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener {

    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private MediaPlayer mediaPlayer;

    String videoSource =
            "https://sites.google.com/site/androidexample9/download/RunningClock.mp4";

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

        surfaceView = (SurfaceView)findViewById(R.id.surfaceview);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        Toast.makeText(MainActivity.this,
                "surfaceCreated()", Toast.LENGTH_LONG).show();
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setDisplay(surfaceHolder);
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.setOnPreparedListener(this);
        try {
            mediaPlayer.setDataSource(videoSource);
            mediaPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this,
                    "something wrong!\n" + e.toString(),
                    Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder,
                               int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mediaPlayer.start();
        Toast.makeText(MainActivity.this,
                "onPrepared()", Toast.LENGTH_LONG).show();
    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidmediaplayer.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" />

    <SurfaceView
        android:id="@+id/surfaceview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>


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



Next:
Add MediaController to MediaPlayer

Related:
- VideoView example to play video from Internet

Friday, April 15, 2016

Android Application Development Cookbook - Second Edition

Over 100 recipes to help you solve the most common problems faced by Android Developers today

Android Application Development Cookbook - Second Edition

About This Book
  • Find the answers to your common Android programming problems, from set up to security, to help you deliver better applications, faster
  • Uncover the latest features of Android Marshmallow to make your applications stand out
  • Get up to speed with Android Studio 1.4 - the first Android Studio based on the IntelliJ IDE from JetBrains
Who This Book Is For
If you are new to Android development and want to take a hands-on approach to learning the framework, or if you are an experienced developer in need of clear working code to solve the many challenges in Android development, you can benefit from this book. Either way, this is a resource you'll want to keep at your desk for a quick reference to solve new problems as you tackle more challenging projects.

What You Will Learn
  • Along with Marshmallow, get hands-on working with Google's new Android Studio IDE
  • Develop applications using the latest Android framework while maintaining backward-compatibility with the support library
  • Master Android programming best practices from the recipes
  • Create exciting and engaging applications using knowledge gained from recipes on graphics, animations, and multimedia
  • Work through succinct steps on specifics that will help you complete your project faster
  • Keep your app responsive (and prevent ANRs) with examples on the AsynchTask class
  • Utilize Google Speech Recognition APIs for your app.
  • Make use of Google Cloud Messaging (GCM) to create Push Notifications for your users
  • Get a better understanding of the Android framework through detailed explanations
In Detail
The Android OS has the largest installation base of any operating system in the world; there has never been a better time to learn Android development to write your own applications, or to make your own contributions to the open source community!

This “cookbook” will make it easy for you to jump to a topic of interest and get what you need to implement the feature in your own application. If you are new to Android and learn best by “doing,” then this book will provide many topics of interest.

Starting with the basics of Android development, we move on to more advanced concepts, and we'll guide you through common tasks developers struggle to solve. The first few chapters cover the basics including Activities, Layouts, Widgets, and the Menu. From there, we cover fragments and data storage (including SQLite), device sensors, the camera, and GPS. Then we move on more advanced topics such as graphics and animation (including OpenGL), multi-threading with AsyncTask, and Internet functionality with Volley. We'll also demonstrate Google Maps and Google Cloud Messaging (also known as Push Notifications) using the Google API Library.

Finally, we'll take a look at several online services designed especially for Android development. Take your application big-time with full Internet web services without having to become a server admin by leveraging the power of Backend as a Service (BaaS) providers.

Style and approach
This book progresses from the fundamentals of Android Development to more advanced concepts, with recipes to solve the most common problems faced by developers. This cookbook makes it easy to jump to specific topics of interest, where you'll find simple steps to implement the solution and get a clear explanation of how it works.

Tuesday, April 12, 2016

Capture frames in VideoView using MediaMetadataRetriever


Previous posts show how to " VideoView example to play video from Internet" and "Add MediaController to VideoView". We can capture frame in the video using MediaMetadataRetriever.

This video show how it run on Android Emulator running Marshmallow.


MainActivity.java
package com.blogspot.android_er.androidvideoview;

import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.VideoView;

import java.util.HashMap;

public class MainActivity extends AppCompatActivity {

    Button btnCapture;
    Spinner spOption;
    VideoView myVideoView;

    String videoSource =
            "https://sites.google.com/site/androidexample9/download/RunningClock.mp4";
    Uri uriVideoSource;

    MediaController myMediaController;
    MediaMetadataRetriever myMediaMetadataRetriever;

    String[] stringOpts = {
            "none",
            "OPTION_CLOSEST",
            "OPTION_CLOSEST_SYNC",
            "OPTION_NEXT_SYNC",
            "OPTION_PREVIOUS_SYNC"};
    int[] valOptions ={
            0,  //will not be used
            MediaMetadataRetriever.OPTION_CLOSEST,
            MediaMetadataRetriever.OPTION_CLOSEST_SYNC,
            MediaMetadataRetriever.OPTION_NEXT_SYNC,
            MediaMetadataRetriever.OPTION_PREVIOUS_SYNC};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myVideoView = (VideoView)findViewById(R.id.vview);

        prepareVideo();

        spOption = (Spinner)findViewById(R.id.option);
        ArrayAdapter<String> adapter
                = new ArrayAdapter<String>(MainActivity.this,
                android.R.layout.simple_list_item_1, stringOpts);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spOption.setAdapter(adapter);

        btnCapture = (Button)findViewById(R.id.capture);
        btnCapture.setOnClickListener(btnCaptureOnClickListener);
    }

    View.OnClickListener btnCaptureOnClickListener = new View.OnClickListener(){
        @Override
        public void onClick(View v) {

            int currentPosition = myVideoView.getCurrentPosition(); //in millisecond
            Toast.makeText(MainActivity.this,
                    "Current Position: " + currentPosition + " (ms)",
                    Toast.LENGTH_LONG).show();

            Bitmap bmFrame;
            int pos = currentPosition * 1000;   //unit in microsecond
            int opt = spOption.getSelectedItemPosition();
            if(opt == 0){
                bmFrame = myMediaMetadataRetriever
                    .getFrameAtTime(pos);
            }else{
                bmFrame = myMediaMetadataRetriever
                        .getFrameAtTime(pos,
                                valOptions[opt]);
            }

            if(bmFrame == null){
                Toast.makeText(MainActivity.this,
                        "bmFrame == null!",
                        Toast.LENGTH_LONG).show();
            }else {
                AlertDialog.Builder myCaptureDialog =
                        new AlertDialog.Builder(MainActivity.this);
                ImageView capturedImageView = new ImageView(MainActivity.this);
                capturedImageView.setImageBitmap(bmFrame);
                LinearLayout.LayoutParams capturedImageViewLayoutParams =
                        new LinearLayout.LayoutParams(
                                LinearLayout.LayoutParams.WRAP_CONTENT,
                                LinearLayout.LayoutParams.WRAP_CONTENT);
                capturedImageView.setLayoutParams(capturedImageViewLayoutParams);

                myCaptureDialog.setView(capturedImageView);
                myCaptureDialog.show();
            }

        }
    };

    private void prepareVideo(){

        myMediaMetadataRetriever = new MediaMetadataRetriever();
        myMediaMetadataRetriever.setDataSource(
                videoSource, new HashMap<String, String>());

        myMediaController = new MediaController(MainActivity.this);
        myVideoView.setMediaController(myMediaController);

        Toast.makeText(MainActivity.this, videoSource, Toast.LENGTH_LONG).show();

        uriVideoSource = Uri.parse(videoSource);

        myVideoView.setVideoURI(uriVideoSource);

        myVideoView.setOnCompletionListener(myVideoViewCompletionListener);
        myVideoView.setOnPreparedListener(MyVideoViewPreparedListener);
        myVideoView.setOnErrorListener(myVideoViewErrorListener);

        myVideoView.requestFocus();
        myVideoView.start();

    }

    MediaPlayer.OnCompletionListener myVideoViewCompletionListener =
            new MediaPlayer.OnCompletionListener() {

                @Override
                public void onCompletion(MediaPlayer arg0) {
                    Toast.makeText(MainActivity.this, "End of Video",
                            Toast.LENGTH_LONG).show();
                }
            };

    MediaPlayer.OnPreparedListener MyVideoViewPreparedListener =
            new MediaPlayer.OnPreparedListener() {

                @Override
                public void onPrepared(MediaPlayer mp) {

                    long duration = myVideoView.getDuration(); //in millisecond
                    Toast.makeText(MainActivity.this,
                            "Duration: " + duration + " (ms)",
                            Toast.LENGTH_LONG).show();

                }
            };

    MediaPlayer.OnErrorListener myVideoViewErrorListener =
            new MediaPlayer.OnErrorListener() {

                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {

                    String errWhat = "";
                    switch (what){
                        case MediaPlayer.MEDIA_ERROR_UNKNOWN:
                            errWhat = "MEDIA_ERROR_UNKNOWN";
                            break;
                        case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
                            errWhat = "MEDIA_ERROR_SERVER_DIED";
                            break;
                        default: errWhat = "unknown what";
                    }

                    String errExtra = "";
                    switch (extra){
                        case MediaPlayer.MEDIA_ERROR_IO:
                            errExtra = "MEDIA_ERROR_IO";
                            break;
                        case MediaPlayer.MEDIA_ERROR_MALFORMED:
                            errExtra = "MEDIA_ERROR_MALFORMED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
                            errExtra = "MEDIA_ERROR_UNSUPPORTED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
                            errExtra = "MEDIA_ERROR_TIMED_OUT";
                            break;
                        default:
                            errExtra = "...others";

                    }

                    Toast.makeText(MainActivity.this,
                            "Error!!!\n" +
                            "what: " + errWhat + "\n" +
                            "extra: " + errExtra,
                            Toast.LENGTH_LONG).show();
                    return true;
                }
            };
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidvideoview.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/capture"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:text="Capture"/>
        <Spinner
            android:id="@+id/option"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
    </LinearLayout>

    <VideoView
        android:id="@+id/vview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>


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

Monday, April 11, 2016

Free book by O'Reilly - Introducing Java 8: A Quick-Start Guide to Lambdas and Streams

Introducing Java 8
A Quick-Start Guide to Lambdas and Streams
By Raoul-Gabriel Urma
Publisher: O'Reilly
Released: August 2015

Java SE 8 is perhaps the largest change to Java in its history, led by its flagship feature—lambda expressions. If you’re an experienced developer looking to adopt Java 8 at work, this short guide will walk you through all of the major changes before taking a deep dive into lambda expressions and Java 8’s other big feature: the Streams API.

Author Raoul-Gabriel Urma explains how improved code readability and support for multicore processors were the prime movers behind Java 8 features. He’ll quickly get you up to speed on new classes including CompleteableFuture and Optional, along with enhanced interfaces and the new Date and Time API. You’ll also:

  • Understand why lambda expressions are considered a kind of anonymous function
  • Learn how lambda expressions and the behavior parameterization pattern let you write flexible and concise code
  • Discover various operations and data processing patterns possible when using the Streams API
  • Use Collector recipes to write queries that are more sophisticated
  • Consider factors such as data size and the number of cores available when using streams in parallel
  • Work with a practical refactoring example to bring lambda expressions and streams into focus

Raoul-Gabriel Urma is co-author of the bestselling book Java 8 in Action (Manning). He has worked as a software engineer for Oracle’s Java Platform Group, as well as for Google’s Python team, eBay and Goldman Sachs. An instructor and frequent conference speaker, he’s currently completing a PhD in Computer Science at the University of Cambridge.

link: http://www.oreilly.com/programming/free/introducing-java-8.csp

Add MediaController to VideoView


Last post show a example of "VideoView example to play video from Internet" without controls. This example show adding MediaController to VideoView, to provide controls of Play/Pause, Forward and Backward.

test on Nexus 9 tablet (Emulator) running Marshmallow:

test on Nexus 7 running Android 5.1.1:

MainActivity.java
package com.blogspot.android_er.androidvideoview;

import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;

public class MainActivity extends AppCompatActivity {

    VideoView myVideoView;

    String videoSource =
            "https://sites.google.com/site/androidexample9/download/RunningClock.mp4";
    Uri uriVideoSource;

    MediaController myMediaController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myVideoView = (VideoView)findViewById(R.id.vview);

        prepareVideo();
    }

    private void prepareVideo(){

        myMediaController = new MediaController(MainActivity.this);
        myVideoView.setMediaController(myMediaController);

        Toast.makeText(MainActivity.this, videoSource, Toast.LENGTH_LONG).show();

        uriVideoSource = Uri.parse(videoSource);

        myVideoView.setVideoURI(uriVideoSource);

        myVideoView.setOnCompletionListener(myVideoViewCompletionListener);
        myVideoView.setOnPreparedListener(MyVideoViewPreparedListener);
        myVideoView.setOnErrorListener(myVideoViewErrorListener);

        myVideoView.requestFocus();
        myVideoView.start();

    }

    MediaPlayer.OnCompletionListener myVideoViewCompletionListener =
            new MediaPlayer.OnCompletionListener() {

                @Override
                public void onCompletion(MediaPlayer arg0) {
                    Toast.makeText(MainActivity.this, "End of Video",
                            Toast.LENGTH_LONG).show();
                }
            };

    MediaPlayer.OnPreparedListener MyVideoViewPreparedListener =
            new MediaPlayer.OnPreparedListener() {

                @Override
                public void onPrepared(MediaPlayer mp) {

                    long duration = myVideoView.getDuration(); //in millisecond
                    Toast.makeText(MainActivity.this,
                            "Duration: " + duration + " (ms)",
                            Toast.LENGTH_LONG).show();

                }
            };

    MediaPlayer.OnErrorListener myVideoViewErrorListener =
            new MediaPlayer.OnErrorListener() {

                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {

                    String errWhat = "";
                    switch (what){
                        case MediaPlayer.MEDIA_ERROR_UNKNOWN:
                            errWhat = "MEDIA_ERROR_UNKNOWN";
                            break;
                        case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
                            errWhat = "MEDIA_ERROR_SERVER_DIED";
                            break;
                        default: errWhat = "unknown what";
                    }

                    String errExtra = "";
                    switch (extra){
                        case MediaPlayer.MEDIA_ERROR_IO:
                            errExtra = "MEDIA_ERROR_IO";
                            break;
                        case MediaPlayer.MEDIA_ERROR_MALFORMED:
                            errExtra = "MEDIA_ERROR_MALFORMED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
                            errExtra = "MEDIA_ERROR_UNSUPPORTED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
                            errExtra = "MEDIA_ERROR_TIMED_OUT";
                            break;
                        default:
                            errExtra = "...others";

                    }

                    Toast.makeText(MainActivity.this,
                            "Error!!!\n" +
                            "what: " + errWhat + "\n" +
                            "extra: " + errExtra,
                            Toast.LENGTH_LONG).show();
                    return true;
                }
            };
}



For layout and uses-permission, refer last post "VideoView example to play video from Internet".

Next:
Capture frames in VideoView using MediaMetadataRetriever

Related:
Open mp4 using Intent.ACTION_OPEN_DOCUMENT, ACTION_GET_CONTENT and ACTION_PICK, and play in VideoView.

VideoView example to play video from Internet

Example of VideoView to play video from Internet:


MainActivity.java
package com.blogspot.android_er.androidvideoview;

import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import android.widget.VideoView;

public class MainActivity extends AppCompatActivity {

    VideoView myVideoView;

    String videoSource =
            "https://sites.google.com/site/androidexample9/download/RunningClock.mp4";
    Uri uriVideoSource;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myVideoView = (VideoView)findViewById(R.id.vview);

        prepareVideo();
    }

    private void prepareVideo(){

        Toast.makeText(MainActivity.this, videoSource, Toast.LENGTH_LONG).show();

        uriVideoSource = Uri.parse(videoSource);

        myVideoView.setVideoURI(uriVideoSource);

        myVideoView.setOnCompletionListener(myVideoViewCompletionListener);
        myVideoView.setOnPreparedListener(MyVideoViewPreparedListener);
        myVideoView.setOnErrorListener(myVideoViewErrorListener);

        myVideoView.requestFocus();
        myVideoView.start();

    }

    MediaPlayer.OnCompletionListener myVideoViewCompletionListener =
            new MediaPlayer.OnCompletionListener() {

                @Override
                public void onCompletion(MediaPlayer arg0) {
                    Toast.makeText(MainActivity.this, "End of Video",
                            Toast.LENGTH_LONG).show();
                }
            };

    MediaPlayer.OnPreparedListener MyVideoViewPreparedListener =
            new MediaPlayer.OnPreparedListener() {

                @Override
                public void onPrepared(MediaPlayer mp) {

                    long duration = myVideoView.getDuration(); //in millisecond
                    Toast.makeText(MainActivity.this,
                            "Duration: " + duration + " (ms)",
                            Toast.LENGTH_LONG).show();

                }
            };

    MediaPlayer.OnErrorListener myVideoViewErrorListener =
            new MediaPlayer.OnErrorListener() {

                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {

                    String errWhat = "";
                    switch (what){
                        case MediaPlayer.MEDIA_ERROR_UNKNOWN:
                            errWhat = "MEDIA_ERROR_UNKNOWN";
                            break;
                        case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
                            errWhat = "MEDIA_ERROR_SERVER_DIED";
                            break;
                        default: errWhat = "unknown what";
                    }

                    String errExtra = "";
                    switch (extra){
                        case MediaPlayer.MEDIA_ERROR_IO:
                            errExtra = "MEDIA_ERROR_IO";
                            break;
                        case MediaPlayer.MEDIA_ERROR_MALFORMED:
                            errExtra = "MEDIA_ERROR_MALFORMED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
                            errExtra = "MEDIA_ERROR_UNSUPPORTED";
                            break;
                        case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
                            errExtra = "MEDIA_ERROR_TIMED_OUT";
                            break;
                        default:
                            errExtra = "...others";

                    }

                    Toast.makeText(MainActivity.this,
                            "Error!!!\n" +
                            "what: " + errWhat + "\n" +
                            "extra: " + errExtra,
                            Toast.LENGTH_LONG).show();
                    return true;
                }
            };
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidvideoview.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" />
    <VideoView
        android:id="@+id/vview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>


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

When I tested it on Nexus 7 running Android 5.1.1, control bar (Play/Pause, Forward and Backward) is shown over the VideoView. But not shown on RedMi 2 running Android 5.0, Android Emulator of Nexus 6P phone, Nexus 7 tablet running Marshmallow, and Nexus 5x phone running Android N.

test on Nexus 7 running Android 5.1.1:

test on RedMi 2 running Android 5.0

test on Nexus 6P phone (Emulator) running Marshmallow

test on Nexus 9 tablet (Emulator) running Marshmallow

test on Nexus 5X (Emulator) running Android N

Remark:
To runing this example on Android Emulator, have to manual select "Hardware = GLES 2.0" in Emulated Performance of Graphics.



Next:
Add MediaController to VideoView, to provide controls of Play/Pause, Forward and Backward.

Related:
- MediaPlayer example to play video from Internet
Open mp4 using Intent.ACTION_OPEN_DOCUMENT, ACTION_GET_CONTENT and ACTION_PICK, and play in VideoView.


Sunday, April 10, 2016

Install Android Studio 2.0 on 64-bit Ubuntu 15.10 with Ubuntu Make (umake)


To install Android Studio (current 2.0) on 64-bit Ubuntu 15.10, it's easy by using Ubuntu Make. It install OpenJDK also.


Enter the command in Terminal:
- Install Ubuntu Make:
$ sudo apt-get install ubuntu-make

- Install android-studio:
$ umake android

if error reported "ERROR: We were expecting to find a license on the download page, we didn't." install android-studio with --accept-license option:
$ umake android --accept-license

(thanks Anonymous's comment in last post "ERROR: We were expecting to find a license on the download page, we didn't."
ref: http://askubuntu.com/questions/755497/error-in-installing-android-studio-using-ubuntu-make)

This video show how to install Ubuntu Make (umake) on Ubuntu 15.10 (running inside VirtualBox), and install Android Studio 2.0 using umake.



About Ubuntu Make
Ubuntu Make is a command line tool which allows you to download the latest version of popular developer tools on your installation, installing it alongside all of the required dependencies (which will only ask for root access if you don't have all the required dependencies installed already), enable multi-arch on your system if you are on a 64 bit machine, integrate it with the Unity launcher. Basically, one command to get your system ready to develop with!

remark@2016-04-23:
BUT error to install on Ubuntu 16.04 LTS, read more "Error in installing Android Studio on Ubuntu 16.04 using umake".



What’s New in Android Studio 2.0

Android Studio 2.0 is focused on making your workflow faster. Faster builds, faster deployment, faster emulators. Everything. Faster.

As developers any time spent waiting for our IDE to do something is time wasted. Time we could be writing code, time that pushes out the end of our day, or a break that risks dropping us out of the zone.

Android Studio 2.0 includes Instant Run to reduce incremental build and deploy times to seconds, improved full build times, a faster and more intuitive emulator, and a new GPU profiler to help make your games and graphics apps performant and error free.


Android Studio 2.0 is now available to download:
http://developer.android.com/sdk/index.html

Find out more about Android Studio 2.0 feature here:
https://medium.com/google-developers/android-studio-2-0-beta-what-s-new-87f8b3946888#.bv3ovscio

Learn more about Android Studio with Android Tool Time:
~ Android Tool Time on Youtube


Saturday, April 9, 2016

Fixed: ERROR: We were expecting to find a license on the download page, we didn't.

Fixed, refer to another post "Install Android Studio 2.0 on 64-bit Ubuntu 15.10 with Ubuntu Make (umake)".

Thx Anonymous's comment:)




I tried to install Android Studio on Ubuntu 15.10 using ubuntu-make (refer Install Android Studio on 64-bit Ubuntu 15.10 with Ubuntu Make (umake)), it show error:
ERROR: We were expecting to find a license on the download page, we didn't.


Anybody know how?
Or may be it's error on the download page, not on me.

Friday, April 8, 2016

Error:Gradle version 2.10 is required. Current version is 2.4. If using the gradle wrapper, try editing the distributionUrl in ...

After update Android Studio, most probably you will be recommended to update the Android Gradle Plugin:


Then, most probably again, "Error:Gradle version 2.10 is required. Current version is 2.4. If using the gradle wrapper, try editing the distributionUrl in ..." will be reported!


Just click the link of "Gradle settings" in Message View, and choice the correct Gradle home.

Alternatively, you can access Gradle home by:
Click File > Settings >
select tabs of Build, Execution, Deployment > Build Tools > Gradle


Android Studio 2.0 and Emulator 25.1.1 are officially now


Android Studio 2.0 and Emulator 25.1.1 are moved to stable release channels.
This release is focused on development productivity by introducing instant run and a much faster emulator. Please see our blog post for more details.

announcement: Android Tools Project Site - Android Studio 2.0 and Emulator 25.1.1 are Available in the Stable, Beta, and Dev Channels

Upgrade to Android Studio 2.0 from 1.5:


Thursday, April 7, 2016

Android BluetoothChat example link with HC-05 Bluetooth

This example show to import and modify Android BluetoothChat example, to link with low-cost Bluetooth HC-05.

reference:
First test HC-05 Bluetooth Module
AT Command mode of HC-05

We need a FTDI USB-to-Serial adapter to connect PC/USB and HC-05 Bluetooth, and use Arduino IDE's Serial Monitor as terminal, to talk with Android running modified BluetoothChat example.


This video show how to import BluetoothChat example in Android Studio, and edit BluetoothChatService.java to change MY_UUID_SECURE to UUID.fromString("00001101-0000-1000-8000-00805F9B34FB").

~ reference http://developer.android.com/reference/android/bluetooth/BluetoothDevice.htmlIf you are connecting to a Bluetooth serial board then try using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.


This video show how it run on Android device, and talk to PC running Arduino IDE's Serial Monitor, via FTDI + HC-05.

In my on-hand HC-05 sample, it set as slave role and 9600, 0, 0, PIN="1234" by default, I have not change any setting.


Cross post with Arduino-er.

Sunday, April 3, 2016

Remote debug web page on Android with Chrome DevTools


This video show how to remote debug web page on Android from PC, with Chrome DevTools.


In order to remote debug on Android device, you have to enable Developer options and USB debugging on your Android device.

details: Google Developers > Chrome DevTools > Remote Debugging Devices


Saturday, April 2, 2016

Open images with Intent.ACTION_OPEN_DOCUMENT, Intent.ACTION_GET_CONTENT and Intent.ACTION_PICK


Example show how to select images by calling startActivityForResult() with Intent.ACTION_OPEN_DOCUMENT (Android 4.4, API level 19, and higher), Intent.ACTION_GET_CONTENT and Intent.ACTION_PICK, then load it using InputStream.

Tested on Nexus 7 running Android 5.1.1, to load photos from local and from cloud of Google Photos.

In my test:
- if select photos with Intent.ACTION_OPEN_DOCUMENT or Intent.ACTION_GET_CONTENT, both photos on local storage and on Google Photos cloud storage can be loaded using InputStream.
- if select photos with Intent.ACTION_PICK, cannot load from Google Photos cloud storage. (or may be by other method, I don't know).


MainActivity.java
package com.blogspot.android_er.androidloadphoto;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import java.io.FileNotFoundException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    Button btnOpen, btnGet, btnPick;
    TextView textInfo1, textInfo2;
    ImageView imageView;

    private static final int RQS_OPEN_IMAGE = 1;
    private static final int RQS_GET_IMAGE = 2;
    private static final int RQS_PICK_IMAGE = 3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnOpen = (Button)findViewById(R.id.open);
        btnGet = (Button)findViewById(R.id.get);
        btnPick = (Button)findViewById(R.id.pick);
        textInfo1 = (TextView)findViewById(R.id.info1);
        textInfo2 = (TextView)findViewById(R.id.info2);
        imageView = (ImageView) findViewById(R.id.image);

        btnOpen.setOnClickListener(btnOpenOnClickListener);
        btnGet.setOnClickListener(btnGetOnClickListener);
        btnPick.setOnClickListener(btnPickOnClickListener);
    }

    View.OnClickListener btnOpenOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("image/*");

            startActivityForResult(intent, RQS_OPEN_IMAGE);
        }
    };

    View.OnClickListener btnGetOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_GET_CONTENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("image/*");

            startActivityForResult(intent, RQS_OPEN_IMAGE);
        }
    };

    View.OnClickListener btnPickOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_PICK,
                    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            startActivityForResult(intent, RQS_PICK_IMAGE);
        }
    };

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {


            if (requestCode == RQS_OPEN_IMAGE ||
                    requestCode == RQS_GET_IMAGE ||
                    requestCode == RQS_PICK_IMAGE) {

                imageView.setImageBitmap(null);
                textInfo1.setText("");
                textInfo2.setText("");

                Uri mediaUri = data.getData();
                textInfo1.setText(mediaUri.toString());
                String mediaPath = mediaUri.getPath();
                textInfo2.setText(mediaPath);

                //display the image
                try {
                    InputStream inputStream = getBaseContext().getContentResolver().openInputStream(mediaUri);
                    Bitmap bm = BitmapFactory.decodeStream(inputStream);
                    imageView.setImageBitmap(bm);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidloadphoto.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/open"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="OPEN Image"/>
    <Button
        android:id="@+id/get"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="GET Image"/>
    <Button
        android:id="@+id/pick"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="PICK Image"/>
    <TextView
        android:id="@+id/info1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"/>
    <TextView
        android:id="@+id/info2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="italic"/>
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>