Tuesday, May 23, 2017

Programming Kotlin

Familiarize yourself with all of Kotlin's features with this in-depth guide

Programming Kotlin

About This Book
  • Get a thorough introduction to Kotlin
  • Learn to use Java code alongside Kotlin without any hiccups
  • Get a complete overview of null safety, Generics, and many more interesting features
Who This Book Is For
The book is for existing Java developers who want to learn more about an alternative JVM language. If you want to see what Kotlin has to offer, this book is ideal for you.

What You Will Learn
  • Use new features to write structured and readable object-oriented code
  • Find out how to use lambdas and higher order functions to write clean, reusable, and simple code
  • Write unit tests and integrate Kotlin tests with Java code in a transitioning code base
  • Write real-world production code in Kotlin in the style of microservices
  • Leverage Kotlin's extensions to the Java collections library
  • Use destructuring expressions and find out how to write your own
  • Write code that avoids null pointer errors and see how Java-nullable code can integrate with features in a Kotlin codebase
  • Discover how to write functions in Kotlin, see the new features available, and extend existing libraries
  • Learn to write an algebraic data types and figure out when they should be used
In Detail
Kotlin has been making waves ever since it was open sourced by JetBrains in 2011; it has been praised by developers across the world and is already being adopted by companies. This book provides a detailed introduction to Kotlin that shows you all its features and will enable you to write Kotlin code to production.

We start with the basics: get you familiar with running Kotlin code, setting up, tools, and instructions that you can use to write basic programs. Next, we cover object oriented code: functions, lambdas, and properties - all while using Kotlin's new features.

Then, we move on to null safety aspects and type parameterization. We show you how to destructure expressions and even write your own. We also take you through important topics like testing, concurrency, microservices, and a whole lot more. By the end of this book you will be able to compose different services and build your own applications.

Style and approach
An easy to follow guide that covers the full set of features in Kotlin programming.

Kotlin for Android Developers: Learn Kotlin the easy way while developing an Android App

Kotlin for Android Developers: Learn Kotlin the easy way while developing an Android App

Are you tired of using an ancient, inexpressive and unsafe language to develop your Android apps? Do you cry out loud every time you see a Null Pointer Exception in your bug tracker? Then Kotlin is your solution! A language specifically created for Java developers, easy to learn, expressive, null safe and really intuitive. Your productivity will boost and your apps will become more robust. Learn Kotlin the easy way by example and discover the tricks that will make coding easier. In this book, I'll be creating an Android app from ground using Kotlin as the main language. The idea is to learn the language by example, instead of following a typical structure. I'll be stopping to explain the most interesting concepts and ideas about Kotlin, comparing it with Java 7. This way, you can see what the differences are and which parts of the language will help you speed up your work. This book is not meant to be a language reference, but a tool for Android developers to learn Kotlin and be able to continue with their own projects by themselves. I'll be solving many of the typical problems we have to face in our daily lives by making use of the language expressiveness and some other really interesting tools and libraries. The book is very practical, so it is recommended to follow the examples and the code in front of a computer and try everything it's suggested. You could, however, take a first read to get a broad idea and then dive into practice.

Monday, May 22, 2017

Download and install Android Studio 3.0 Canary 1 on Ubuntu 17.04

This post show how to Download and install Android Studio 3.0 Canary 1 on 64-bit Ubuntu 17.04, over Windows 10/Oracle VirtualBox,


Firstly, Install Ubuntu 17.04 on Windows 10/Oracle VirtualBox.

Before install Android Studio on 64-bit Ubuntu 17.04, install 32-bit libraries:
$ sudo dpkg --add-architecture i386
$ sudo apt-get update
$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386

(details refer to Fix "Unable to run mksdcard SDK tool" when install Android Studio on Linux)

Download Android Studio 3.0 Canary, visit https://developer.android.com/studio/preview/index.html
Unpack the ZIP file, to any directory you want.
Open a terminal, navigate into <android-studio>/bin/ and execute studio.sh

This video show how:


Setup for debugging on real device:

On Ubuntu/Linux, to debug on real device, you have to set up your system to detect your device.
(referencec: https://developer.android.com/studio/run/device.html#setting-up)

Edit/Create /etc/udev/rules.d/51-android.rules
$ sudo nano /etc/udev/rules.d/51-android.rules

Add the code:
SUBSYSTEM=="usb", ATTR{idVendor}=="0bb4", MODE="0666", GROUP="plugdev"

Where 00b4 is the vendor ID, replace it to match your device. For my Samsung device, replace with 04e8.

You can check your USB vendor ID here: https://developer.android.com/studio/run/device.html#VendorIds.

or try to find it using lsusb command.



Kotlin programming language, Now official on Android. It's a dummy Hello World of Kotlin language, created in Android Studio 3.0 Canary 1.



Get USB devices' vendor ID and product ID on Linux, using lsusb command

To get the vendor ID/product ID of attached USB devices in Linux, you can use the command lsusb.

This video show how to, tested on Ubuntu 17.05 over Windows 10/Oracle VirtualBox 5.1




reference: Ubuntu Manual - lsusb



Saturday, May 20, 2017

Install Ubuntu 17.04 on Windows 10/Oracle VirtualBox


This video show how to install Install Ubuntu 17.04 on Windows 10 with Oracle VirtualBox 5.1. Basically, the steps is very straightforward.


But...after setup, I cannot google in the browser. And it was found some sites can be reached, some sites cannot. Even cannot run apt-get update. After some searching, I found it's related to DNSSEC in /etc/systemd/resolved.conf.

To fix it, edit the file /etc/systemd/resolved.conf:
$ sudo nano /etc/systemd/resolved.conf

uncomment #DNSSEC=allow-downgrade and replace allow-downgrade with off .


Save and Exit
Reboot

This video show how:


reference:
This page have a good explanation: Hectic Geek - Websites Not Loading (dns issue) After Installing Ubuntu 17.04? [fix]

Friday, May 19, 2017

Fix "Unable to run mksdcard SDK tool" when install Android Studio on Linux

If you install Android Studio on 64-bit Linux and reported with error:
Unable to run mksdcard SDK tool



Most probably some 32-bit libraries are missed.

If you are running a 64-bit version of Ubuntu, you need to install some 32-bit libraries with the following command:
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 lib32bz2-1.0

If you are running 64-bit Fedora, the command is:
sudo yum install zlib.i686 ncurses-libs.i686 bzip2-libs.i686

reference: Troubleshoot Android Studio - Linux libraries


When I try to install the 32-bit libraries on Ubuntu 16.04.2 LTS (64-bit) on VirtualBox with the command:

$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 lib32bz2-1.0

The errors reported:
E: Unable to locate package lib32bz2-1.0
E: Couldn't find any package by glob 'lib32bz2-1.0'
E: Couldn't find any package by regex 'lib32bz2-1.0'

To fixed it, add 32 bit architecture with the command:
$ sudo dpkg --add-architecture i386
$ sudo apt-get update

Re-install with the command 
$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386


Done:


Android Studio 3.0 available on canary release channel

Just in time for Google I/O 2017, Android Studio 3.0 is available to download on  canary release channel.

To accelerate your development flow, Android Studio 3.0 includes three major features: a new suite of app performance profiling tools to quickly diagnose performance issues, support for the Kotlin programming language, and increased Gradle build speeds for large sized app projects. Android Studio 3.0 also tightly integrates with Android platform development with these additional key features: support for Instant App development, inclusion of the Google Play Store in the Android O emulator system images, and new wizards for Android O development. Overall, this first canary release of Android Studio 3.0 has 20+ new features.



Please refer to the announcement blogpost "Android Studio 3.0 Canary 1" for more details.

Thursday, May 18, 2017

Kotlin programming language, Now official on Android

At Google I/O 2017 keynote, the Android team announced first-class support for Kotlin. Kotlin is a statically typed programming language for modern multiplatform applications

Starting now, Android Studio 3.0 ships with Kotlin out of the box, meaning Android developers no longer need to install any extras or worry about compatibility. It also means that moving forward, you can rest assured that both JetBrains and Google will be supporting Android development in Kotlin.

~ read more

This tutorial, Getting started with Android and Kotlin, walks us through creating a simple Kotlin application for Android using Android Studio.


Related:
Download and install Android Studio 3.0 Canary 1 on Ubuntu 17.04, and create Hello World of Kotlin language.

Video playlist of Google I/O 2017 - All Sessions

If you missed Google I/O 2017 live videos, you can review here:

Tuesday, May 16, 2017

Developer Preview 4 of Android Things released

Developer Preview 4 of Android Things released, bringing new supported hardware, features, and bug fixes to the platform. This version support Google Assistant SDK on all Android Things certified development boards.

know more: Android Developers Blog - Android Things Developer Preview 4

It's time to install Google I/O 2017 app on your phone

The official Google I/O 2017 conference app is your co-pilot to navigate the conference, whether you're attending in-person or remotely. With the app, you can:

• Explore the conference schedule, with details on topics and speakers
• Save events to My I/O, your personalized schedule
• Get reminders before events you’ve saved in My I/O start
• Sync your custom schedule between all of your devices and the I/O website
• Watch the keynote and sessions in real time via the livestream
• Guide yourself using the vector-based conference map
• Opt-in to receive important notifications about the event, schedule, travel, after hours, session recordings and more
• Read about the latest event details in the Feed

Exclusive for onsite attendees:
• Take advantage of facilitated pre-event WiFi configuration
• Reserve seats for events ahead of schedule

Source code for the app will be available soon after I/O.

Saturday, May 13, 2017

Microsoft's Customer Guidance for WannaCrypt attacks and security update, including Windows XP, Windows 8, and Windows Server 2003

In these two day, many Windows users affected by the malicious “WannaCrypt” software. Microsoft posted "Customer Guidance for WannaCrypt attacks", the steps to stay protected. Additionally, Microsoft are taking the highly unusual step of providing a security update for all customers to protect Windows platforms that are in custom support only, including Windows XP, Windows 8, and Windows Server 2003. Windows 10 were not targeted by the attack up to now.

Link ~ Microsoft TechNet - Customer Guidance for WannaCrypt attacks


Treble: A modular base for Android

With Treble in coming Android O, device-maker will easier to update devices to a new version of Android.

know more: Android Developers Blog - Here comes Treble: A modular base for Android


How to disable "SMB 1.0/CIFS File Sharing Support" in Windows 10


Open Control Panel
- Program

- Programs and Features > Turn Windows features on or off

- Uncheck "SMB 1.0/CIFS File Sharing Support"

- And restart your computer


Reference:
~ Microsoft Support: How to enable and disable SMBv1, SMBv2, and SMBv3 in Windows and Windows Server

Friday, May 12, 2017

Add button inside Toolbar

This example modify from my old post "Implement checkable items in OptionsMenu of Toolbar" to add button to Toolbar.


Edit activity_main.xml to add Button inside android.support.v7.widget.Toolbar:
<?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: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.blogspot.android_er.androidtoolbar.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="#FFA000">
        <Button
            android:id="@+id/toolbarbtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            android:layout_gravity="right"/>
    </android.support.v7.widget.Toolbar>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</LinearLayout>

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

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    Button toolBarBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        getSupportActionBar().setTitle("Toolbar example");
        toolbar.setSubtitle("Android-er.blogspot.com");
        toolbar.setLogo(android.R.drawable.ic_menu_info_details);

        toolBarBtn = (Button)findViewById(R.id.toolbarbtn);
        toolBarBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getBaseContext(),
                        "Button in ToolBar clicked",
                        Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        Toast.makeText(this, item.getTitle(), Toast.LENGTH_LONG).show();

        int item_id = item.getItemId();
        switch(item_id){
            case R.id.item_checkable1:
                if(item.isChecked()){
                    item.setChecked(false);
                }else{
                    item.setChecked(true);
                }
                break;
            case R.id.item_checkable2:
                if(item.isChecked()){
                    item.setChecked(false);
                }else{
                    item.setChecked(true);
                }
                break;
        }

        return super.onOptionsItemSelected(item);
    }
}



Learn about Places and the Google Maps Places API

Google Maps Places API make your app stand out with detailed information about 100 million places across a wide range of categories, from the same database as Google Maps and Google+.

Learn about Places and the Google Maps Places API over Coffee with Fontaine Foxworth

Learn more about the Places API: https://developers.google.com/places/
Watch a Geocast on how to use it in a game: https://goo.gl/oRMKWh

Coffee with a Googler meets with Fontaine Foxworth to talk about the Places API in Google Maps. Places give locations in the world semantic meaning. Your favorite coffee shop isn't just an address or a latitude and longitude, it's a place. With the API, you can locate and get metadata about a place, giving you lots of fun applications you can build!

Watch more episodes of Coffee with a Googler here: https://goo.gl/5l123N
Subscribe to the Google Developers Channel: http://goo.gl/mQyv5L


Wednesday, May 10, 2017

I/O Live Widget



I/O Live Widget is a widget to embed on your website that allows you to deliver highlights and live announcements directly from Google I/O. It features the following elements:
  • The complete I/O live video stream (this includes the keynote and all livestreamed sessions).
  • Social feed from @GOOGLEDEVS.

Customize the widget at: https://events.google.com/io/widget/


Monday, May 1, 2017

Learning Embedded Android N Programming

Learning Embedded Android N Programming

Key Features
  • Understand the system architecture and how the source code is organized
  • Explore the power of Android and customize the build system
  • Build a fully customized Android version as per your requirements
Book Description
Take a deep dive into the Android build system and its customization with Learning Embedded Android Programming, written to help you master the steep learning curve of working with embedded Android. Start by exploring the basics of Android OS, discover Google's “repo” system, and discover how to retrieve AOSP source code. You'll then find out to set up the build environment and the first AOSP system. Next, learn how to customize the boot sequence with a new animation, and use an Android “kitchen” to “cook” your custom ROM. By the end of the book, you'll be able to build customized Android open source projects by developing your own set of features.

What you will learn
  • Master Android architecture and system design
  • Obtain source code and understand the modular organization
  • Customize and build your first system image for the Android emulator
  • Level up and build your own Android system for a real-world device
  • Use Android as a home automation and entertainment system
  • Tailor your system with optimizations and add-ons
  • Reach for the stars: look at the Internet of Things, entertainment, and domotics
About the Author
Ivan Morgillo is a computer engineer, a conference speaker, and a community organizer. He is passionate about programming and embedded systems—from DIY domotics to Android devices.

He is cofounder of Alter Ego Solutions, a mobile development consulting company.

He is also the author of RxJava Essentials, by Packt Publishing and Grokking Rx, by Manning Publications.

Stefano Viola is an embedded software developer with proved experience with Linux embedded devices and microcontrollers. He is an Android platform expert and application developer. He is passionate about programming and embedded systems, from DIY domotics and robots to customized Android devices.

He is currently working at SECO as an embedded software engineer. He is part of AXIOM project, an R&D project by the European Community, and the UDOO team.

Table of Contents
  1. Understanding the Architecture
  2. Obtaining the Source Code – Structure and Philosophy
  3. Set up and Build – the Emulator Way
  4. Moving to Real-World Hardware
  5. Customizing Kernel and Boot Sequence
  6. "Cooking" Your First ROM
  7. Tailoring Your Personal Android System
  8. Beyond the Smartphone

Thursday, April 27, 2017

Read Exif tag of JPG using ExifInterface(String filename), with Requesting Permissions at Run Time for Android 6.0 (API level 23) or higher.


Last post show how to Read Exif tag of JPG using ExifInterface(String filename), with Target Sdk Version to API 22. As mentioned, beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. It need extra handle of Requesting Permissions at Run Time.

This example add handle Requesting Permissions at Run Time, to make it Target Sdk Version API 25.



uses-permission of "android.permission.READ_EXTERNAL_STORAGE" is needed in AndroidManifest.xml, refer last post.

For the layout, refer to the example in Read Exif tag of JPG using ExifInterface(FileDescriptor).

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

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final int RQS_OPEN_IMAGE = 1;
    private static final int RQS_READ_EXTERNAL_STORAGE = 2;

    Button buttonOpen;
    TextView textUri;
    ImageView imageView;

    Uri targetUri = null;

    View.OnClickListener buttonOpenOnClickListener =
            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/jpeg");
                    startActivityForResult(intent, RQS_OPEN_IMAGE);
                }

            };
    View.OnClickListener textUriOnClickListener =
            new View.OnClickListener(){

                @Override
                public void onClick(View v) {
                    if (targetUri != null){
                        Bitmap bm;
                        try {
                            bm = BitmapFactory.decodeStream(
                                    getContentResolver()
                                            .openInputStream(targetUri));
                            imageView.setImageBitmap(bm);
                        } catch (FileNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }

            };

    View.OnClickListener imageOnClickListener =
            new View.OnClickListener(){

                @Override
                public void onClick(View view) {

                    if(CheckPermission_READ_EXTERNAL_STORAGE()){
                        showExif(targetUri);
                    }

                }
            };

    private boolean CheckPermission_READ_EXTERNAL_STORAGE() {
        // return true: have permission
        // return false: no permission
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    RQS_READ_EXTERNAL_STORAGE);

            return false;
        }else{
            return true;
        }
    }

    void showExif(Uri photoUri){

        if(photoUri != null){

            String photoPath = getRealPathFromURI(photoUri);

            try {
                /*
                ExifInterface (String filename) added in API level 5
                 */
                ExifInterface exifInterface = new ExifInterface(photoPath);

                String exif="Exif: ";
                exif += "\nIMAGE_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
                exif += "\nIMAGE_WIDTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
                exif += "\n DATETIME: " +
                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
                exif += "\n TAG_MAKE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MAKE);
                exif += "\n TAG_MODEL: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MODEL);
                exif += "\n TAG_ORIENTATION: " +
                        exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION);
                exif += "\n TAG_WHITE_BALANCE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
                exif += "\n TAG_FOCAL_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
                exif += "\n TAG_FLASH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FLASH);
                exif += "\nGPS related:";
                exif += "\n TAG_GPS_DATESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
                exif += "\n TAG_GPS_TIMESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
                exif += "\n TAG_GPS_LATITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
                exif += "\n TAG_GPS_LATITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
                exif += "\n TAG_GPS_LONGITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
                exif += "\n TAG_GPS_LONGITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
                exif += "\n TAG_GPS_PROCESSING_METHOD: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);

                Toast.makeText(getApplicationContext(),
                        exif,
                        Toast.LENGTH_LONG).show();


            } catch (FileNotFoundException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            }

        }else{
            Toast.makeText(getApplicationContext(),
                    "photoUri == null",
                    Toast.LENGTH_LONG).show();
        }
    };

    /*
    This method getRealPathFromURI() is not coded by me,
    but I forgot where I copy it from.
     */
    private String getRealPathFromURI(Uri uri){
        String filePath = "";
        String wholeID = DocumentsContract.getDocumentId(uri);

        // Split at colon, use second item in the array
        String id = wholeID.split(":")[1];

        String[] column = { MediaStore.Images.Media.DATA };

        // where id is equal to
        String sel = MediaStore.Images.Media._ID + "=?";

        Cursor cursor = getApplicationContext().getContentResolver()
                .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        column, sel, new String[]{ id }, null);

        int columnIndex = cursor.getColumnIndex(column[0]);

        if (cursor.moveToFirst()) {
            filePath = cursor.getString(columnIndex);
        }
        cursor.close();
        return filePath;
    }

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

        buttonOpen = (Button) findViewById(R.id.opendocument);
        buttonOpen.setOnClickListener(buttonOpenOnClickListener);

        textUri = (TextView) findViewById(R.id.texturi);
        textUri.setOnClickListener(textUriOnClickListener);

        imageView = (ImageView)findViewById(R.id.image);
        imageView.setOnClickListener(imageOnClickListener);
    }

    protected void onActivityResult(int requestCode,
                                    int resultCode, Intent data) {

        if (resultCode == Activity.RESULT_OK) {

            Uri dataUri = data.getData();

            if (requestCode == RQS_OPEN_IMAGE) {
                targetUri = dataUri;
                textUri.setText(dataUri.toString());
                imageView.setImageBitmap(null);
            }
        }

    }

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

        switch (requestCode) {
            case RQS_READ_EXTERNAL_STORAGE: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    showExif(targetUri);
                } else {
                    Toast.makeText(this,
                            "permission denied!",
                            Toast.LENGTH_SHORT).show();
                }
                return;
            }
        }
    }
}



Remark:
When I prepare the screencast, it seem not work on Xiaomi Redmi 2 running Android 4.4.4. Refer to the video above.


Read Exif tag of JPG using ExifInterface(String filename)


Last posts show how to Read Exif tag of JPG using ExifInterface(FileDescriptor) and using ExifInterface(InputStream). But both ExifInterface(FileDescriptor) and ExifInterface(InputStream) added in API level 24, Android 7.0. In this post, I will show how to use ExifInterface(String filename), added in API level 5. Such that we can set Min Sdk Version to API 19.

To use ExifInterface(String filename), uses-permission of "android.permission.READ_EXTERNAL_STORAGE" is needed.

Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. It need extra handle of Requesting Permissions at Run Time. To make it simple, we can set Target Sdk Version to API 22.


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidexif">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

</manifest>

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

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final int RQS_OPEN_IMAGE = 1;

    Button buttonOpen;
    TextView textUri;
    ImageView imageView;

    Uri targetUri = null;

    View.OnClickListener buttonOpenOnClickListener =
            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/jpeg");
                    startActivityForResult(intent, RQS_OPEN_IMAGE);
                }

            };
    View.OnClickListener textUriOnClickListener =
            new View.OnClickListener(){

                @Override
                public void onClick(View v) {
                    if (targetUri != null){
                        Bitmap bm;
                        try {
                            bm = BitmapFactory.decodeStream(
                                    getContentResolver()
                                            .openInputStream(targetUri));
                            imageView.setImageBitmap(bm);
                        } catch (FileNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }

            };

    View.OnClickListener imageOnClickListener =
            new View.OnClickListener(){

                @Override
                public void onClick(View view) {
                    showExif(targetUri);
                }
            };

    void showExif(Uri photoUri){
        if(photoUri != null){

            String photoPath = getRealPathFromURI(photoUri);

            try {
                /*
                ExifInterface (String filename) added in API level 5
                 */
                ExifInterface exifInterface = new ExifInterface(photoPath);

                String exif="Exif: ";
                exif += "\nIMAGE_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
                exif += "\nIMAGE_WIDTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
                exif += "\n DATETIME: " +
                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
                exif += "\n TAG_MAKE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MAKE);
                exif += "\n TAG_MODEL: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MODEL);
                exif += "\n TAG_ORIENTATION: " +
                        exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION);
                exif += "\n TAG_WHITE_BALANCE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
                exif += "\n TAG_FOCAL_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
                exif += "\n TAG_FLASH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FLASH);
                exif += "\nGPS related:";
                exif += "\n TAG_GPS_DATESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
                exif += "\n TAG_GPS_TIMESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
                exif += "\n TAG_GPS_LATITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
                exif += "\n TAG_GPS_LATITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
                exif += "\n TAG_GPS_LONGITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
                exif += "\n TAG_GPS_LONGITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
                exif += "\n TAG_GPS_PROCESSING_METHOD: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);

                Toast.makeText(getApplicationContext(),
                        exif,
                        Toast.LENGTH_LONG).show();


            } catch (FileNotFoundException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            }

        }else{
            Toast.makeText(getApplicationContext(),
                    "photoUri == null",
                    Toast.LENGTH_LONG).show();
        }
    };

    /*
    This method getRealPathFromURI() is not coded by me,
    but I forgot where I copy it from.
     */
    private String getRealPathFromURI(Uri uri){
        String filePath = "";
        String wholeID = DocumentsContract.getDocumentId(uri);

        // Split at colon, use second item in the array
        String id = wholeID.split(":")[1];

        String[] column = { MediaStore.Images.Media.DATA };

        // where id is equal to
        String sel = MediaStore.Images.Media._ID + "=?";

        Cursor cursor = getApplicationContext().getContentResolver()
                .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        column, sel, new String[]{ id }, null);

        int columnIndex = cursor.getColumnIndex(column[0]);

        if (cursor.moveToFirst()) {
            filePath = cursor.getString(columnIndex);
        }
        cursor.close();
        return filePath;
    }

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

        buttonOpen = (Button) findViewById(R.id.opendocument);
        buttonOpen.setOnClickListener(buttonOpenOnClickListener);

        textUri = (TextView) findViewById(R.id.texturi);
        textUri.setOnClickListener(textUriOnClickListener);

        imageView = (ImageView)findViewById(R.id.image);
        imageView.setOnClickListener(imageOnClickListener);
    }

    protected void onActivityResult(int requestCode,
                                    int resultCode, Intent data) {

        if (resultCode == Activity.RESULT_OK) {

            Uri dataUri = data.getData();

            if (requestCode == RQS_OPEN_IMAGE) {
                targetUri = dataUri;
                textUri.setText(dataUri.toString());
                imageView.setImageBitmap(null);
            }
        }

    }
}


For the layout, refer to the example in Read Exif tag of JPG using ExifInterface(FileDescriptor).

Next:
Read Exif tag of JPG using ExifInterface(String filename), with Requesting Permissions at Run Time for Android 6.0 (API level 23) or higher.

Tuesday, April 25, 2017

Read Exif tag of JPG using ExifInterface(InputStream)

Last post show how to open image of jpeg using Intent.ACTION_OPEN_DOCUMENT (added in API level 19), then display the image and read the Exif tag using ExifInterface (FileDescriptor)(added in API level 24). Here is another alternative using ExifInterface(InputStream) (added in API level 24) to read Exif.

Simple modify the method showExif(Uri photoUri). All other follow the last example. It have the same result:



    void showExif(Uri photoUri){
        if(photoUri != null){

            /*
            How to convert the Uri to InputStream, refer to the example in the document:
            https://developer.android.com/guide/topics/providers/document-provider.html
             */

            try {
                InputStream inputStream = getContentResolver().openInputStream(photoUri);

                /*
                ExifInterface (InputStream inputStream) added in API level 24
                 */
                ExifInterface exifInterface = new ExifInterface(inputStream);

                String exif="Exif: ";
                exif += "\nIMAGE_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
                exif += "\nIMAGE_WIDTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
                exif += "\n DATETIME: " +
                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
                exif += "\n TAG_MAKE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MAKE);
                exif += "\n TAG_MODEL: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MODEL);
                exif += "\n TAG_ORIENTATION: " +
                        exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION);
                exif += "\n TAG_WHITE_BALANCE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
                exif += "\n TAG_FOCAL_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
                exif += "\n TAG_FLASH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FLASH);
                exif += "\nGPS related:";
                exif += "\n TAG_GPS_DATESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
                exif += "\n TAG_GPS_TIMESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
                exif += "\n TAG_GPS_LATITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
                exif += "\n TAG_GPS_LATITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
                exif += "\n TAG_GPS_LONGITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
                exif += "\n TAG_GPS_LONGITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
                exif += "\n TAG_GPS_PROCESSING_METHOD: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);

                inputStream.close();

                Toast.makeText(getApplicationContext(),
                        exif,
                        Toast.LENGTH_LONG).show();


            } catch (FileNotFoundException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            }

        }else{
            Toast.makeText(getApplicationContext(),
                    "photoUri == null",
                    Toast.LENGTH_LONG).show();
        }
    };


Next:
Read Exif tag of JPG using ExifInterface(String filename)

Read Exif tag of JPG using ExifInterface(FileDescriptor)

android.media.ExifInterface is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF.
Attribute mutation is supported for JPEG image files.


This example show how to open image of jpeg using Intent.ACTION_OPEN_DOCUMENT (added in API level 19), then display the image and read the Exif tag using ExifInterface (FileDescriptor)(added in API level 24).

Please notice:
Because ExifInterface (FileDescriptor) is used here, so the min Sdk Version have to be set API 24.
To convert the Uri return by Storage Access Framework to FileDescriptor, refer to the example in the document "Open Files using Storage Access Framework".


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

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

import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final int RQS_OPEN_IMAGE = 1;

    Button buttonOpen;
    TextView textUri;
    ImageView imageView;

    Uri targetUri = null;

    View.OnClickListener buttonOpenOnClickListener =
            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/jpeg");
                    startActivityForResult(intent, RQS_OPEN_IMAGE);
                }

            };
    View.OnClickListener textUriOnClickListener =
            new View.OnClickListener(){

                @Override
                public void onClick(View v) {
                    if (targetUri != null){
                        Bitmap bm;
                        try {
                            bm = BitmapFactory.decodeStream(
                                    getContentResolver()
                                            .openInputStream(targetUri));
                            imageView.setImageBitmap(bm);
                        } catch (FileNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }

            };

    View.OnClickListener imageOnClickListener =
            new View.OnClickListener(){

                @Override
                public void onClick(View view) {
                    showExif(targetUri);
                }
            };

    void showExif(Uri photoUri){
        if(photoUri != null){

            ParcelFileDescriptor parcelFileDescriptor = null;

            /*
            How to convert the Uri to FileDescriptor, refer to the example in the document:
            https://developer.android.com/guide/topics/providers/document-provider.html
             */
            try {
                parcelFileDescriptor = getContentResolver().openFileDescriptor(photoUri, "r");
                FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();

                /*
                ExifInterface (FileDescriptor fileDescriptor) added in API level 24
                 */
                ExifInterface exifInterface = new ExifInterface(fileDescriptor);
                String exif="Exif: " + fileDescriptor.toString();
                exif += "\nIMAGE_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
                exif += "\nIMAGE_WIDTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
                exif += "\n DATETIME: " +
                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
                exif += "\n TAG_MAKE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MAKE);
                exif += "\n TAG_MODEL: " +
                        exifInterface.getAttribute(ExifInterface.TAG_MODEL);
                exif += "\n TAG_ORIENTATION: " +
                        exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION);
                exif += "\n TAG_WHITE_BALANCE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
                exif += "\n TAG_FOCAL_LENGTH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
                exif += "\n TAG_FLASH: " +
                        exifInterface.getAttribute(ExifInterface.TAG_FLASH);
                exif += "\nGPS related:";
                exif += "\n TAG_GPS_DATESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
                exif += "\n TAG_GPS_TIMESTAMP: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
                exif += "\n TAG_GPS_LATITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
                exif += "\n TAG_GPS_LATITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
                exif += "\n TAG_GPS_LONGITUDE: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
                exif += "\n TAG_GPS_LONGITUDE_REF: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
                exif += "\n TAG_GPS_PROCESSING_METHOD: " +
                        exifInterface.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);

                parcelFileDescriptor.close();

                Toast.makeText(getApplicationContext(),
                        exif,
                        Toast.LENGTH_LONG).show();

            } catch (FileNotFoundException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(getApplicationContext(),
                        "Something wrong:\n" + e.toString(),
                        Toast.LENGTH_LONG).show();
            }

            String strPhotoPath = photoUri.getPath();

        }else{
            Toast.makeText(getApplicationContext(),
                    "photoUri == null",
                    Toast.LENGTH_LONG).show();
        }
    };

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

        buttonOpen = (Button) findViewById(R.id.opendocument);
        buttonOpen.setOnClickListener(buttonOpenOnClickListener);

        textUri = (TextView) findViewById(R.id.texturi);
        textUri.setOnClickListener(textUriOnClickListener);

        imageView = (ImageView)findViewById(R.id.image);
        imageView.setOnClickListener(imageOnClickListener);
    }

    protected void onActivityResult(int requestCode,
                                    int resultCode, Intent data) {

        if (resultCode == Activity.RESULT_OK) {

            Uri dataUri = data.getData();

            if (requestCode == RQS_OPEN_IMAGE) {
                targetUri = dataUri;
                textUri.setText(dataUri.toString());
                imageView.setImageBitmap(null);
            }
        }

    }
}


Layout:
<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.blogspot.android_er.androidexif.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/opendocument"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Open Document of Image" />

    <TextView
        android:id="@+id/texturi"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ImageView
        android:id="@+id/image"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

Next:
Read Exif tag of JPG using ExifInterface(InputStream)
Read Exif tag of JPG using ExifInterface(String filename)

Thursday, April 6, 2017

Introduction videos for Android Things

I found two Youtube videos good for Android Things, published by The Linux Foundation.


Android Things: High Level Introduction - Anisha Dattatraya & Geeta Krishna, Intel Corporation


An overview of the basic concepts behind Android things and its structure and components is presented. Upon completion of this session, you should have a good overview of how Android Things brings simplicity to IoT software and hardware development by providing a simple and secure deployment and update model. This presentation provides the context needed for the Android Things Tutorial and other deep dive sessions for Android Things.

About Geeta Krishna
I've been involved in Unix based OS development since the 1990s. Most of my career has been spent in system level development of High Availability and Distributed Systems software technology. More recently, I have taken on Engineering Management roles for Tizen In Vehicle Infotainment and Intel's Reference Design for Android. I currently work for Open Source Technology Center at Intel Corporation and am Engineering Manager for Android Things.


Android Things - Karim Yaghmour, Opersys Inc.


While Android has shipped in more than a billion phones and has made its way into countless hmi systems, its uses have remained user-centric; that, despite some community attempts over the years to show that it can be used in headless systems. Google changed all that by introducing Brillo and Weave, and later announcing Android Things as the successor to Brillo. By using an Android-based system to venture head on into the world of IOT, Google has chosen to leverage its success in one ecosystem to help shape a completely different one. This talk will look at how Android Things builds on what Brillo was and how Android Things can be used to create Android-based IOT devices.

About Karim Yaghmour
Karim is part serial entrepreneur, part unrepentant geek. He's most widely know for his O'Reilly books: "Building Embedded Linux Systems" and "Embedded Android". As an active member of the open source community since the mid-90's, he pioneered the world of Linux tracing with the Linux Trace Toolkit and introduced Adeos, one of the first nanokernels/hypervisors for Linux. As part of his work, he's had the privilege of working with teams from a wide range of fields, from enterprise to embedded.

Thursday, March 30, 2017

Android ListView with CheckBox

This example show how to implement ListView with CheckBox, modified from the post "Custom ListView with ImageView".



At the beginning, I tried to implement OnCheckedChangeListener for the CheckBox; to handle the check state. But, it will be called when the ListView item scroll-out from the screen, and clear the check state unexpectedly.

Finally, I implement OnClickListener. It work as expected.


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

    <CheckBox
        android:id="@+id/rowCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <ImageView
        android:id="@+id/rowImageView"
        android:layout_gravity="center"
        android:layout_width="48dp"
        android:layout_height="48dp" />

    <TextView
        android:id="@+id/rowTextView"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>


/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"
    tools:context="com.blogspot.android_er.androidcheckboxlistview.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/lookup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Look up"/>
    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>



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

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    public class Item {
        boolean checked;
        Drawable ItemDrawable;
        String ItemString;
        Item(Drawable drawable, String t, boolean b){
            ItemDrawable = drawable;
            ItemString = t;
            checked = b;
        }

        public boolean isChecked(){
            return checked;
        }
    }

    static class ViewHolder {
        CheckBox checkBox;
        ImageView icon;
        TextView text;
    }

    public class ItemsListAdapter extends BaseAdapter {

        private Context context;
        private List<Item> list;

        ItemsListAdapter(Context c, List<Item> l) {
            context = c;
            list = l;
        }

        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        public boolean isChecked(int position) {
            return list.get(position).checked;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            View rowView = convertView;

            // reuse views
            ViewHolder viewHolder = new ViewHolder();
            if (rowView == null) {
                LayoutInflater inflater = ((Activity) context).getLayoutInflater();
                rowView = inflater.inflate(R.layout.row, null);

                viewHolder.checkBox = (CheckBox) rowView.findViewById(R.id.rowCheckBox);
                viewHolder.icon = (ImageView) rowView.findViewById(R.id.rowImageView);
                viewHolder.text = (TextView) rowView.findViewById(R.id.rowTextView);
                rowView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) rowView.getTag();
            }

            viewHolder.icon.setImageDrawable(list.get(position).ItemDrawable);
            viewHolder.checkBox.setChecked(list.get(position).checked);

            final String itemStr = list.get(position).ItemString;
            viewHolder.text.setText(itemStr);

            viewHolder.checkBox.setTag(position);

            /*
            viewHolder.checkBox.setOnCheckedChangeListener(
                    new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                    list.get(position).checked = b;

                    Toast.makeText(getApplicationContext(),
                            itemStr + "onCheckedChanged\nchecked: " + b,
                            Toast.LENGTH_LONG).show();
                }
            });
            */

            viewHolder.checkBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    boolean newState = !list.get(position).isChecked();
                    list.get(position).checked = newState;
                    Toast.makeText(getApplicationContext(),
                            itemStr + "setOnClickListener\nchecked: " + newState,
                            Toast.LENGTH_LONG).show();
                }
            });

            viewHolder.checkBox.setChecked(isChecked(position));

            return rowView;
        }
    }

    Button btnLookup;
    List<Item> items;
    ListView listView;
    ItemsListAdapter myItemsListAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView)findViewById(R.id.listview);
        btnLookup = (Button)findViewById(R.id.lookup);

        initItems();
        myItemsListAdapter = new ItemsListAdapter(this, items);
        listView.setAdapter(myItemsListAdapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                Toast.makeText(MainActivity.this,
                        ((Item)(parent.getItemAtPosition(position))).ItemString,
                        Toast.LENGTH_LONG).show();
            }});

        btnLookup.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String str = "Check items:\n";

                for (int i=0; i<items.size(); i++){
                    if (items.get(i).isChecked()){
                        str += i + "\n";
                    }
                }

                /*
                int cnt = myItemsListAdapter.getCount();
                for (int i=0; i<cnt; i++){
                    if(myItemsListAdapter.isChecked(i)){
                        str += i + "\n";
                    }
                }
                */

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

            }
        });
    }

    private void initItems(){
        items = new ArrayList<Item>();

        TypedArray arrayDrawable = getResources().obtainTypedArray(R.array.resicon);
        TypedArray arrayText = getResources().obtainTypedArray(R.array.restext);

        for(int i=0; i<arrayDrawable.length(); i++){
            Drawable d = arrayDrawable.getDrawable(i);
            String s = arrayText.getString(i);
            boolean b = false;
            Item item = new Item(d, s, b);
            items.add(item);
        }

        arrayDrawable.recycle();
        arrayText.recycle();
    }

}



/res/values/arrays.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="resicon">
        <item>@android:drawable/ic_dialog_alert</item>
        <item>@android:drawable/ic_dialog_dialer</item>
        <item>@android:drawable/ic_dialog_email</item>
        <item>@android:drawable/ic_dialog_info</item>
        <item>@android:drawable/ic_dialog_map</item>
        <item>@android:drawable/ic_menu_camera</item>
        <item>@android:drawable/ic_menu_compass</item>
        <item>@android:drawable/ic_menu_gallery</item>
        <item>@android:drawable/ic_menu_my_calendar</item>
        <item>@android:drawable/ic_menu_myplaces</item>
        <item>@android:drawable/ic_menu_slideshow</item>
        <item>@android:drawable/ic_menu_today</item>
    </array>
    <array name="restext">
        <item>"alert"</item>
        <item>"dialer"</item>
        <item>"email"</item>
        <item>"info"</item>
        <item>"map"</item>
        <item>"camera"</item>
        <item>"compass"</item>
        <item>"gallery"</item>
        <item>"calendar"</item>
        <item>"myplaces"</item>
        <item>"slideshow"</item>
        <item>"today"</item>
    </array>

</resources>



Wednesday, March 22, 2017

Tuesday, March 21, 2017

Developer Preview of Android O

Google share a first developer preview of the next version of Android OS: Android O. Android O introduces a number of new features and APIs to use in your apps.

read more: Android Developers Blog - O-MG, the Developer Preview of Android O is here!


Sunday, March 19, 2017

Android Apps for Absolute Beginners: Covering Android 7 - 4th Edition

Android Apps for Absolute Beginners: Covering Android 7

Get your first Android apps up and running with the help of plain English and practical examples. If you have a great idea for an Android app, but have never programmed before, then this book is for you. Android Apps for Absolute Beginners cuts through the fog of jargon and mystery that surrounds Android app development, and gives you simple, step-by-step instructions to get you started.

This book teaches Android application development in language anyone can understand, giving you the best possible start in Android development. It provides clean, straightforward examples that make learning easy, allowing you to pick up the concepts without fuss. It offers clear code descriptions and layout so that you can get your apps running as soon as possible

Although this book covers what's new in Android 7, it is also backwards compatible to cover some of the previous Android releases.

What You'll Learn
  • Download, install, and configure the latest software needed for Android app development
  • Work efficiently using an integrated development environment (IDE)
  • Build useful, attractive applications and get them working immediately
  • Create apps with ease using XML markup and drag-and-drop graphical layout editors
  • Use new media and graphics to skin your app so that it has maximum appeal
  • Create advanced apps combining XML, Java and new media content
Who This Book Is For

If you have a great idea for an Android app, but have never programmed before, then this book is for you. You don’t need to have any previous computer programming skills ― as long as you have a desire to learn and you know which end of the mouse is which, the world of Android apps development awaits.

Monday, March 6, 2017

Download NASA Software for Free!



The NASA Software Catalog offers an extensive portfolio of software products for a wide variety of technical applications!

You can access the full catalog for FREE at https://software.nasa.gov/



Sunday, February 19, 2017

Implement transparent status bar for android.support.v7.widget.Toolbar

Implement transparent status bar for android.support.v7.widget.Toolbar:

- Follow the steps in "Replace ActionBar with android.support.v7.widget.Toolbar" to implement a simplest toolbar.


- Edit styles.xml to add the item of "android:windowTranslucentStatus".
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowTranslucentStatus">true</item>
    </style>

</resources>


Edit MainActivity.java to set the padding (with the height of status bar) of the status.
package com.blogspot.android_er.androidtoolbarex2;

import android.content.res.Resources;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        toolbar.setPadding(0, getStatusBarHeight(), 0, 0);

    }

    private int getStatusBarHeight() {
        int height;

        Resources myResources = getResources();
        int idStatusBarHeight = myResources.getIdentifier(
                "status_bar_height", "dimen", "android");
        if (idStatusBarHeight > 0) {
            height = getResources().getDimensionPixelSize(idStatusBarHeight);
            Toast.makeText(this,
                    "Status Bar Height = " + height,
                    Toast.LENGTH_LONG).show();
        }else{
            height = 0;
            Toast.makeText(this,
                    "Resources NOT found",
                    Toast.LENGTH_LONG).show();
        }

        return height;
    }
}


After finished: