Friday, July 29, 2016

Hacking Android

Explore every nook and cranny of the Android OS to modify your device and guard it against security threats

Hacking Android

About This Book
  • Understand and counteract against offensive security threats to your applications
  • Maximize your device's power and potential to suit your needs and curiosity
  • See exactly how your smartphone's OS is put together (and where the seams are)
Who This Book Is For
This book is for anyone who wants to learn about Android security. Software developers, QA professionals, and beginner- to intermediate-level security professionals will find this book helpful. Basic knowledge of Android programming would be a plus.

What You Will Learn
  • Acquaint yourself with the fundamental building blocks of Android Apps in the right way
  • Pentest Android apps and perform various attacks in the real world using real case studies
  • Take a look at how your personal data can be stolen by malicious attackers
  • Understand the offensive maneuvers that hackers use
  • Discover how to defend against threats
  • Get to know the basic concepts of Android rooting
  • See how developers make mistakes that allow attackers to steal data from phones
  • Grasp ways to secure your Android apps and devices
  • Find out how remote attacks are possible on Android devices
In Detail
With the mass explosion of Android mobile phones in the world, mobile devices have become an integral part of our everyday lives. Security of Android devices is a broad subject that should be part of our everyday lives to defend against ever-growing smartphone attacks. Everyone, starting with end users all the way up to developers and security professionals should care about android security.

Hacking Android is a step-by-step guide that will get you started with Android security. You'll begin your journey at the absolute basics, and then will slowly gear up to the concepts of Android rooting, application security assessments, malware, infecting APK files, and fuzzing. On this journey you'll get to grips with various tools and techniques that can be used in your everyday pentests. You'll gain the skills necessary to perform Android application vulnerability assessment and penetration testing and will create an Android pentesting lab.

Style and approach
This comprehensive guide takes a step-by-step approach and is explained in a conversational and easy-to-follow style. Each topic is explained sequentially in the process of performing a successful penetration test. We also include detailed explanations as well as screenshots of the basic and advanced concepts.

Thursday, July 28, 2016

Apply Material Theme to Activity


This video show how to apply Android provided Material Theme to your Activity.


- Edit values/styles.xml to add a new style inherits from Android provided Material Theme: android:Theme.Material, android:Theme.Material.Light or android:Theme.Material.Light.DarkActionBar.

- Edit AndroidManifest.xml to use the new style.

- You have to change your MainActivity extends Activity. Otherwise the following error will happen:
You need to use a Theme.AppCompat theme (or descendant) with this activity.

The video also show how it display on Multi-Window Mode.


Next:
- Customize theme's colors

Wednesday, July 27, 2016

Get display information using DisplayMetrics, in Multi-Window Mode


The class android.util.DisplayMetrics is a structure describing general information about a display, such as its size, density, and font scaling. Here show how to get display information (specially heightPixels and widthPixels) in Multi-Window Mode.


package com.blogspot.android_er.androidmultiwindow;

        import android.content.res.Configuration;
        import android.os.Bundle;
        import android.support.v7.app.AppCompatActivity;
        import android.util.DisplayMetrics;
        import android.widget.TextView;
        import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    TextView textPrompt;
    TextView textInfo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textPrompt = (TextView)findViewById(R.id.prompt);
        textInfo = (TextView)findViewById(R.id.info);

        if(isInMultiWindowMode()){
            textPrompt.setText("onCreate run In Multi Window Mode ");
        }else{
            textPrompt.setText("onCreate run NOT In Multi Window Mode ");
        }

        Toast.makeText(MainActivity.this,
                "onCreate() called", Toast.LENGTH_LONG).show();

        showDisplayInfo();
    }

    @Override
    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
        super.onMultiWindowModeChanged(isInMultiWindowMode);

        if(isInMultiWindowMode){
            textPrompt.setText("It is In Multi Window Mode ");
        }else{
            textPrompt.setText("It is NOT In Multi Window Mode ");
        }

        Toast.makeText(MainActivity.this,
                "onMultiWindowModeChanged() called", Toast.LENGTH_LONG).show();

        showDisplayInfo();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        Toast.makeText(MainActivity.this,
                "onConfigurationChanged() called", Toast.LENGTH_LONG).show();

        showDisplayInfo();
    }

    private void showDisplayInfo(){
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);

        String strScreenDIP = "";
        strScreenDIP += "The logical density of the display: " + dm.density + "\n";
        strScreenDIP += "The screen density expressed as dots-per-inch: " + dm.densityDpi +"\n";
        strScreenDIP += "The absolute height of the display in pixels: " + dm.heightPixels +"\n";
        strScreenDIP += "The absolute width of the display in pixels: " + dm.widthPixels + "\n";
        strScreenDIP += "A scaling factor for fonts displayed on the display: " 
                + dm.scaledDensity + "\n";
        strScreenDIP += "The exact physical pixels per inch of the screen in the X dimension: " 
                + dm.xdpi + "\n";
        strScreenDIP += "The exact physical pixels per inch of the screen in the Y dimension: " 
                + dm.ydpi + "\n";

        textInfo.setText(strScreenDIP);
    }
}


<?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.androidmultiwindow.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold"/>
    <TextView
        android:id="@+id/prompt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textStyle="bold"/>
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20dp"/>
    </ScrollView>

</LinearLayout>


Tuesday, July 26, 2016

onConfigurationChanged() called when window size changed in Multi-Window Mode

Last post show that onMultiWindowModeChanged() will be called when your app change from Full screen mode to Multi-Window Mode, or reverse. How about size changed in Multi-Window Mode?


This example show that onConfigurationChanged() will be called in case of size changed in Multi-Window Mode.


Modify MainActivity to override onConfigurationChanged() to show a Toast.
package com.blogspot.android_er.androidmultiwindow;

import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    TextView textPrompt;

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

        if(isInMultiWindowMode()){
            textPrompt.setText("onCreate run In Multi Window Mode ");
        }else{
            textPrompt.setText("onCreate run NOT In Multi Window Mode ");
        }

        Toast.makeText(MainActivity.this,
                "onCreate() called", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
        super.onMultiWindowModeChanged(isInMultiWindowMode);

        if(isInMultiWindowMode){
            textPrompt.setText("It is In Multi Window Mode ");
        }else{
            textPrompt.setText("It is NOT In Multi Window Mode ");
        }

        Toast.makeText(MainActivity.this,
                "onMultiWindowModeChanged() called", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        Toast.makeText(MainActivity.this,
                "onConfigurationChanged() called", Toast.LENGTH_LONG).show();
    }
}


The layout file keep using that in last post.

The AndroidManifest.xml in this example use the default setting by Android Studio without any changed.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidmultiwindow">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>

</manifest>

Next:
Get display information using DisplayMetrics, in Multi-Window Mode

Monday, July 25, 2016

Detect and check if your app run in multi window mode


This example show how to detect and check if your app run in multi window mode, target Android N with Multi-Window Support, by calling isInMultiWindowMode() and override onMultiWindowModeChanged() methods.


package com.blogspot.android_er.androidmultiwindow;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView textPrompt;

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

        if(isInMultiWindowMode()){
            textPrompt.setText("onCreate run In Multi Window Mode ");
        }else{
            textPrompt.setText("onCreate run NOT In Multi Window Mode ");
        }
    }

    @Override
    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
        super.onMultiWindowModeChanged(isInMultiWindowMode);

        if(isInMultiWindowMode){
            textPrompt.setText("It is In Multi Window Mode ");
        }else{
            textPrompt.setText("It is NOT In Multi Window Mode ");
        }
    }
}


<?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.androidmultiwindow.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold"/>
    <TextView
        android:id="@+id/prompt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="28dp"
        android:textStyle="bold"/>
</LinearLayout>


Next:
onConfigurationChanged() called when window size changed in Multi-Window Mode

Wednesday, July 20, 2016

Create styling string from resources

This example show how to create partial styling string from resources:


Edit values/strings.xml to add string resources with styling:
<resources>
    <string name="app_name">AndroidStylingStringResources</string>
    <string name="normal_name">It is Normal String</string>
    <string name="italic_name">Partial <i>Italic String</i> from resources</string>
    <string name="bold_name">Partial <b>Bold String</b> from resources</string>
    <string name="underline_name">Partial <u>Underline String</u> from resources</string>
</resources>


Edit layout/activity_main.xml to use the string resources:
<?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.androidstylingstringresources.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="28dp"
        android:text="@string/normal_name"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="28dp"
        android:textStyle="italic"
        android:text="Whole TextView in italic style"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="28dp"
        android:text="@string/italic_name"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="28dp"
        android:text="@string/bold_name"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="28dp"
        android:text="@string/underline_name"/>

</LinearLayout>



Tuesday, July 19, 2016

android:gravity vs android:layout_gravity


This simple example show how android:gravity and android:layout_gravity affect the placement.


<?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.androidgravity.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        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="match_parent"
        android:layout_margin="10dp"
        android:background="#D00000">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="200sp"
            android:layout_margin="10dp"
            android:gravity="center"
            android:layout_gravity="center"
            android:background="#0000C0"
            android:textSize="28sp"
            android:textStyle="bold"
            android:text="android-er"/>
    </LinearLayout>
</LinearLayout>


Sunday, July 17, 2016

Wednesday, July 13, 2016

Bluetooth LE example - connect to Bluetooth LE device and display GATT Services


Last post of Bluetooth LE example show how to "Scan specified BLE devices with ScanFilter". This post show how to connect to the device and display the supported service by the device.


Modify from last "Scan specified BLE devices with ScanFilter". Basically this part copy from "example code of Bluetooth Le Gatt", refer to "Bluetooth Le Gatt example to link with Arduino/Genuino 101".

Create a new activity ControlActivity.java
package com.blogspot.android_er.androidblegatt;

import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView;

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

public class ControlActivity extends AppCompatActivity {

    private final static String TAG = ControlActivity.class.getSimpleName();

    public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
    public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";

    private String mDeviceName;
    private String mDeviceAddress;

    private boolean mConnected = false;
    private BluetoothGattCharacteristic mNotifyCharacteristic;
    private BluetoothLeService mBluetoothLeService;

    TextView textViewState;
    private ExpandableListView mGattServicesList;

    private final String LIST_NAME = "NAME";
    private final String LIST_UUID = "UUID";

    private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics =
            new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

    // Code to manage Service lifecycle.
    private final ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
            if (!mBluetoothLeService.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            // Automatically connects to the device upon successful start-up initialization.
            mBluetoothLeService.connect(mDeviceAddress);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBluetoothLeService = null;
        }
    };

    // Handles various events fired by the Service.
    // ACTION_GATT_CONNECTED: connected to a GATT server.
    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
    // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
    //                        or notification operations.
    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                mConnected = true;
                updateConnectionState("GATT_CONNECTED");
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                mConnected = false;
                updateConnectionState("GATT_DISCONNECTED");
                clearUI();
            } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                // Show all the supported services and characteristics on the user interface.
                displayGattServices(mBluetoothLeService.getSupportedGattServices());
            } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));

            }
        }
    };

    private void clearUI() {
        mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
    }

    private void updateConnectionState(final String st) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textViewState.setText(st);
            }
        });
    }

    private void displayData(String data) {
        if (data != null) {
            textViewState.setText(data);
        }
    }

    // Demonstrates how to iterate through the supported GATT Services/Characteristics.
    // In this sample, we populate the data structure that is bound to the ExpandableListView
    // on the UI.
    private void displayGattServices(List<BluetoothGattService> gattServices) {

        if (gattServices == null) return;
        String uuid = null;
        String unknownServiceString = "Unknown Service";
        String unknownCharaString = "Unknown Characteristic";
        ArrayList<HashMap<String, String>> gattServiceData =
                new ArrayList<HashMap<String, String>>();
        ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                = new ArrayList<ArrayList<HashMap<String, String>>>();
        mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

        // Loops through available GATT Services.
        for (BluetoothGattService gattService : gattServices) {
            HashMap<String, String> currentServiceData = new HashMap<String, String>();
            uuid = gattService.getUuid().toString();
            currentServiceData.put(
                    LIST_NAME, lookup(uuid, unknownServiceString));
            currentServiceData.put(LIST_UUID, uuid);
            gattServiceData.add(currentServiceData);

            ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                    new ArrayList<HashMap<String, String>>();
            List<BluetoothGattCharacteristic> gattCharacteristics =
                    gattService.getCharacteristics();
            ArrayList<BluetoothGattCharacteristic> charas =
                    new ArrayList<BluetoothGattCharacteristic>();

            // Loops through available Characteristics.
            for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                charas.add(gattCharacteristic);
                HashMap<String, String> currentCharaData = new HashMap<String, String>();
                uuid = gattCharacteristic.getUuid().toString();
                currentCharaData.put(
                        LIST_NAME, lookup(uuid, unknownCharaString));
                currentCharaData.put(LIST_UUID, uuid);
                gattCharacteristicGroupData.add(currentCharaData);

            }
            mGattCharacteristics.add(charas);
            gattCharacteristicData.add(gattCharacteristicGroupData);
        }

        SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
                this,
                gattServiceData,
                android.R.layout.simple_expandable_list_item_2,
                new String[] {LIST_NAME, LIST_UUID},
                new int[] { android.R.id.text1, android.R.id.text2 },
                gattCharacteristicData,
                android.R.layout.simple_expandable_list_item_2,
                new String[] {LIST_NAME, LIST_UUID},
                new int[] { android.R.id.text1, android.R.id.text2 }
        );
        mGattServicesList.setAdapter(gattServiceAdapter);
    }

    // If a given GATT characteristic is selected, check for supported features.  This sample
    // demonstrates 'Read' and 'Notify' features.  See
    // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete
    // list of supported characteristic features.
    private final ExpandableListView.OnChildClickListener servicesListClickListner =
            new ExpandableListView.OnChildClickListener() {
                @Override
                public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
                                            int childPosition, long id) {
                    if (mGattCharacteristics != null) {
                        final BluetoothGattCharacteristic characteristic =
                                mGattCharacteristics.get(groupPosition).get(childPosition);
                        final int charaProp = characteristic.getProperties();
                        if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                            // If there is an active notification on a characteristic, clear
                            // it first so it doesn't update the data field on the user interface.
                            if (mNotifyCharacteristic != null) {
                                mBluetoothLeService.setCharacteristicNotification(
                                        mNotifyCharacteristic, false);
                                mNotifyCharacteristic = null;
                            }
                            mBluetoothLeService.readCharacteristic(characteristic);
                        }
                        if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                            mNotifyCharacteristic = characteristic;
                            mBluetoothLeService.setCharacteristicNotification(
                                    characteristic, true);
                        }
                        return true;
                    }
                    return false;
                }
            };

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

        final Intent intent = getIntent();
        mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
        mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);

        TextView textViewDeviceName = (TextView)findViewById(R.id.textDeviceName);
        TextView textViewDeviceAddr = (TextView)findViewById(R.id.textDeviceAddress);
        textViewState = (TextView)findViewById(R.id.textState);

        textViewDeviceName.setText(mDeviceName);
        textViewDeviceAddr.setText(mDeviceAddress);

        mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
        mGattServicesList.setOnChildClickListener(servicesListClickListner);

        Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
        if (mBluetoothLeService != null) {
            final boolean result = mBluetoothLeService.connect(mDeviceAddress);
            Log.d(TAG, "Connect request result=" + result);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mGattUpdateReceiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
        mBluetoothLeService = null;
    }

    private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
        return intentFilter;
    }

    private static HashMap<String, String> attributes = new HashMap();

    public static String lookup(String uuid, String defaultName) {
        String name = attributes.get(uuid);
        return name == null ? defaultName : name;
    }
}


And associated layout, activity_control.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.androidblegatt.ControlActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

    <TextView
        android:id="@+id/textDeviceName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textStyle="bold"/>
    <TextView
        android:id="@+id/textDeviceAddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textStyle="bold" />
    <TextView
        android:id="@+id/textState"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textStyle="bold" />
    <ExpandableListView android:id="@+id/gatt_services_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>


Modify MainActivity.java to add a button to start ControlActivity.
package com.blogspot.android_er.androidblegatt;

import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

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

public class MainActivity extends AppCompatActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothLeScanner mBluetoothLeScanner;

    private boolean mScanning;

    private static final int RQS_ENABLE_BLUETOOTH = 1;

    Button btnScan;
    ListView listViewLE;

    List<BluetoothDevice> listBluetoothDevice;
    ListAdapter adapterLeScanResult;

    private Handler mHandler;
    private static final long SCAN_PERIOD = 10000;

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

        // Check if BLE is supported on the device.
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this,
                    "BLUETOOTH_LE not supported in this device!",
                    Toast.LENGTH_SHORT).show();
            finish();
        }

        getBluetoothAdapterAndLeScanner();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            Toast.makeText(this,
                    "bluetoothManager.getAdapter()==null",
                    Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        btnScan = (Button)findViewById(R.id.scan);
        btnScan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                scanLeDevice(true);
            }
        });
        listViewLE = (ListView)findViewById(R.id.lelist);

        listBluetoothDevice = new ArrayList<>();
        adapterLeScanResult = new ArrayAdapter<BluetoothDevice>(
                this, android.R.layout.simple_list_item_1, listBluetoothDevice);
        listViewLE.setAdapter(adapterLeScanResult);
        listViewLE.setOnItemClickListener(scanResultOnItemClickListener);

        mHandler = new Handler();

    }

    AdapterView.OnItemClickListener scanResultOnItemClickListener =
            new AdapterView.OnItemClickListener(){

                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    final BluetoothDevice device =
                            (BluetoothDevice) parent.getItemAtPosition(position);

                    String msg = device.getAddress() + "\n"
                            + device.getBluetoothClass().toString() + "\n"
                            + getBTDevieType(device);

                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle(device.getName())
                            .setMessage(msg)
                            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {

                                }
                            })
                            .setNeutralButton("CONNECT", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    final Intent intent = new Intent(MainActivity.this,
                                            ControlActivity.class);
                                    intent.putExtra(ControlActivity.EXTRAS_DEVICE_NAME,
                                            device.getName());
                                    intent.putExtra(ControlActivity.EXTRAS_DEVICE_ADDRESS,
                                            device.getAddress());

                                    if (mScanning) {
                                        mBluetoothLeScanner.stopScan(scanCallback);
                                        mScanning = false;
                                        btnScan.setEnabled(true);
                                    }
                                    startActivity(intent);
                                }
                            })
                            .show();

                }
            };

    private String getBTDevieType(BluetoothDevice d){
        String type = "";

        switch (d.getType()){
            case BluetoothDevice.DEVICE_TYPE_CLASSIC:
                type = "DEVICE_TYPE_CLASSIC";
                break;
            case BluetoothDevice.DEVICE_TYPE_DUAL:
                type = "DEVICE_TYPE_DUAL";
                break;
            case BluetoothDevice.DEVICE_TYPE_LE:
                type = "DEVICE_TYPE_LE";
                break;
            case BluetoothDevice.DEVICE_TYPE_UNKNOWN:
                type = "DEVICE_TYPE_UNKNOWN";
                break;
            default:
                type = "unknown...";
        }

        return type;
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (!mBluetoothAdapter.isEnabled()) {
            if (!mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, RQS_ENABLE_BLUETOOTH);
            }
        }
    }

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

        if (requestCode == RQS_ENABLE_BLUETOOTH && resultCode == Activity.RESULT_CANCELED) {
            finish();
            return;
        }

        getBluetoothAdapterAndLeScanner();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            Toast.makeText(this,
                    "bluetoothManager.getAdapter()==null",
                    Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

    private void getBluetoothAdapterAndLeScanner(){
        // Get BluetoothAdapter and BluetoothLeScanner.
        final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
        mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

        mScanning = false;
    }

    /*
    to call startScan (ScanCallback callback),
    Requires BLUETOOTH_ADMIN permission.
    Must hold ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get results.
     */
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            listBluetoothDevice.clear();
            listViewLE.invalidateViews();

            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mBluetoothLeScanner.stopScan(scanCallback);
                    listViewLE.invalidateViews();

                    Toast.makeText(MainActivity.this,
                            "Scan timeout",
                            Toast.LENGTH_LONG).show();

                    mScanning = false;
                    btnScan.setEnabled(true);
                }
            }, SCAN_PERIOD);

            //mBluetoothLeScanner.startScan(scanCallback);

            //scan specified devices only with ScanFilter
            ScanFilter scanFilter =
                    new ScanFilter.Builder()
                            .setServiceUuid(BluetoothLeService.ParcelUuid_GENUINO101_ledService)
                            .build();
            List<ScanFilter> scanFilters = new ArrayList<ScanFilter>();
            scanFilters.add(scanFilter);

            ScanSettings scanSettings =
                    new ScanSettings.Builder().build();

            mBluetoothLeScanner.startScan(scanFilters, scanSettings, scanCallback);

            mScanning = true;
            btnScan.setEnabled(false);
        } else {
            mBluetoothLeScanner.stopScan(scanCallback);
            mScanning = false;
            btnScan.setEnabled(true);
        }
    }

    private ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);

            addBluetoothDevice(result.getDevice());
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            for(ScanResult result : results){
                addBluetoothDevice(result.getDevice());
            }
        }

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

        private void addBluetoothDevice(BluetoothDevice device){
            if(!listBluetoothDevice.contains(device)){
                listBluetoothDevice.add(device);
                listViewLE.invalidateViews();
            }
        }
    };
}


Modify AndroidManifest.xml to add activity of ".ControlActivity".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidblegatt">

    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>
        <activity android:name=".ControlActivity"/>
        <service android:name=".BluetoothLeService" android:enabled="true"/>
    </application>

</manifest>



~ Bluetooth LE Gatt Example, step-by-step

Friday, July 8, 2016

elevation effect of overlapped view


This example show how elevation effect of overlapped view.


package com.blogspot.android_er.androidzelevation;

import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    SeekBar seekBarZ1, seekBarZ2;
    ImageView image1;
    TextView text2;

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

        seekBarZ1 = (SeekBar)findViewById(R.id.z1);
        seekBarZ2 = (SeekBar)findViewById(R.id.z2);
        image1 = (ImageView)findViewById(R.id.image1);
        text2 = (TextView)findViewById(R.id.text2);

        seekBarZ1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    image1.setZ(i);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

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

        seekBarZ2.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    text2.setZ(i);
                    text2.setText("elevation: " + String.valueOf(i) + "px");
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

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


<?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.androidzelevation.MainActivity">

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

    <SeekBar
        android:id="@+id/z1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="10"
        android:max="50"/>
    <SeekBar
        android:id="@+id/z2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="5"
        android:max="50"/>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:id="@+id/image1"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:elevation="10px"
            android:background="#90F0F0F0"
            android:src="@mipmap/ic_launcher"/>
        <TextView
            android:id="@+id/text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:elevation="5px"
            android:textSize="40dp"
            android:background="#90F0F0F0"
            android:text="elevation: 5px"/>
    </FrameLayout>

</LinearLayout>


Android for the BeagleBone Black

Design and implement Android apps that interface with your own custom hardware circuits and the BeagleBone Black

Android for the BeagleBone Black

About This Book
  • Design custom apps that interact with the outside world via BeagleBone Black
  • Modify Android to recognize, configure, and communicate with sensors, LEDs, memory, and more
  • A step-by-step guide full of practical Android app examples that will help the users to create Android controlled devices that will use BeagleBone as hardware
Who This Book Is For
If you are an Android app developer who wants to experiment with the hardware capabilities of the BeagleBone Black platform, then this book is ideal for you. You are expected to have basic knowledge of developing Android apps but no prior hardware experience is required.

What You Will Learn
  • Install Android on your BeagleBone Black
  • Explore the three primary hardware interfaces of the BeagleBone Black—GPIO, SPI, and I2C
  • Construct circuits that interface the BeagleBone Black with high-speed sensors, external memory chips, and more
  • Discover the advantages and disadvantages of using GPIO, I2C, and SPI components in your interfacing projects
  • Modify Android to recognize and interface with your own custom and prototype hardware
  • Develop multithreaded apps that communicate directly with custom circuitry
In Detail
This book explores using the Android OS on the BeagleBone Black hardware platform and provides an introduction to Android's unique approach to hardware interfacing. You'll be walked through the process of installing and configuring Android on your BeagleBone Black, as well as preparing your PC development environment to create Android applications that directly interface with hardware devices. Several example projects within this book introduce you to using the GPIO, SPI, and I2C hardware interfaces of the BeagleBone Black.

You'll create Android apps that communicate directly with actual hardware components such as sensors, memory chips, switches, and LEDs. Step-by-step guidance through both the software and hardware portions of these projects is provided. Combining all of the previous projects into a single project that uses GPIO, SPI, and I2C together, you will explore the details of creating an advanced hardware interfacing app. Finally, you'll be provided with information on transitioning prototype code into code suitable for deployment on an Android-based device. With a variety of example apps that demonstrate key hardware communication concepts, this book will help you become an Android hardware interfacing pro in no time.

Wednesday, July 6, 2016

Creating Dynamic UIs with Android Fragments - Second Edition

Create engaging apps with fragments to provide a rich user interface that dynamically adapts to the individual characteristics of your customers' tablets and smartphones

Creating Dynamic UIs with Android Fragments - Second Edition

About This Book
  • From an eminent author comes a book that will help you create engaging apps that dynamically adapt to individual device characteristics
  • The only book that includes the latest fragment-oriented features and their role in Material design
  • This book provides code-intensive discussions and detailed examples that help you understand better and learn faster.
Who This Book Is For
This book is for developers with a basic understanding of Android programming who would like to improve the appearance and usability of their applications by creating a more interactive user experience and dynamically adaptive UIs; providing better support for tablets and smartphones in a single app; and reducing the complexity of managing app UIs.

What You Will Learn
  • Learn the role and capabilities of fragments
  • Use Android Studio's fragment-oriented features
  • Create an app UI that works effectively on smartphones and tablets
  • Manage the creation and life cycle of fragments
  • Dynamically manage fragments using the FragmentTransaction class
  • Learn the application design for communicating between fragments
  • Leverage fragments when implementing applications that take advantage of the latest features of Material Design
In Detail
Today's users expect mobile apps to be dynamic and highly interactive, with rich navigation features. These same apps must look fantastic whether running on a medium-resolution smartphone or high-resolution tablet. Fragments provide the toolset we need to meet these user expectations by enabling us to build our applications out of adaptable components that take advantage of the rich capabilities of each individual device and automatically adapt to their differences.

This book looks at the impact fragments have on Android UI design and their role in both simplifying many common UI challenges and in providing best practices for incorporating rich UI behaviors. We look closely at the roll of fragment transactions and how to work with the Android back stack. Leveraging this understanding, we explore several specialized fragment-related classes such as ListFragment and DialogFragment. We then go on to discuss how to implement rich navigation features such as swipe-based screen browsing, and the role of fragments when developing applications that take advantage of the latest aspects of Material Design.

You will learn everything you need to provide dynamic, multi-screen UIs within a single activity, and the rich UI features demanded by today's mobile users.

Style and approach
A fast-paced learning guide that gives a hands-on, code-intensive approach with a focus on real-world applications.

Stream video from Raspberry Pi 3 + Camera V2, play in Android/VideoView

This video show how to stream Camera Module NoIR V2 video from Raspberry Pi 3/Raspbian Jessie using vlc, play in Android App with VideoView. Details, refer my another blogspot.


Tuesday, July 5, 2016

Change elevation at run-time, by calling setZ()

This example show how to change elevation of ImageView programmatically, by calling setZ().


<?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.androidzelevation.MainActivity">

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

    <SeekBar
        android:id="@+id/z"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="20"
        android:max="100"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center">
        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:elevation="20dp"
            android:background="@android:color/background_dark"
            android:src="@mipmap/ic_launcher"/>
    </LinearLayout>

</LinearLayout>



package com.blogspot.android_er.androidzelevation;

import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.SeekBar;

public class MainActivity extends AppCompatActivity {

    SeekBar seekBarZ;
    ImageView image;

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

        seekBarZ = (SeekBar)findViewById(R.id.z);
        image = (ImageView)findViewById(R.id.image);
        
        seekBarZ.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    image.setZ(i);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }
}


more:
elevation effect of overlapped view

Example of applying android:elevation on ImageView

Example to apply android:elevation on ImageView.

<?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.androidzelevation.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        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="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:elevation="20dp"
            android:src="@mipmap/ic_launcher"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:elevation="20dp"
            android:background="@android:color/background_dark"
            android:src="@mipmap/ic_launcher"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:gravity="center">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:elevation="20dp"
            android:background="#FF0000"
            android:src="@mipmap/ic_launcher"/>
    </LinearLayout>

</LinearLayout>


Running on ASUS Zenfone 2 running Android 5.0


How it shown on Emulator running Android API 23 and API 19, and also Android Studio Design View:


Friday, July 1, 2016

MPAndroidChart, a powerful Android chart view / graph view library.


MPAndroidChart is a powerful Android chart view / graph view library, supporting line- bar- pie- radar- bubble- and candlestick charts as well as scaling, dragging and animations. This video show how to download and run its example in Android Studio/Emulator.


Locate the libs folder in Android Studio


This video show how to where the libs folder is in Android Studio: