Thursday, 23 June 2016

Install Android Studio 2.1.2 on Linux Mint 17.3 Rosa


This video show how to install Android Studio (currently 2.1.2) on Linux Mint 17.3 Rosa run on VirtualBox/Windows 10.

Before install Android Studio, have to install jdk. Refer to Install Oracle Java8 on Linux Mint 17.3 Rosa.


Enter in Terminal:
$ sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make
$ sudo apt update
$ sudo apt install ubuntu-make
$ umake android

After reboot, launcher icon of Android Studio will appear in Linux Mint Menu.



Install Oracle Java8 on Linux Mint 17.3 Rosa

This video show how to Install Oracle Java8 on Linux Mint 17.3 Rosa (run on VirtualBox/Windows 10).


Enter the command in Terminal:
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer

Install Linux Mint 17.3 Rosa on VirtualBox/Windows 10


This video show how to install Install Linux Mint 17.3 Rosa on Oracle VirtualBox/Windows 10.

Download Linux Mint iso file from https://www.linuxmint.com/.


The video also show how to enable 3D Acceleration in VirtualBox. (Please note: In my experience of using Ubuntu on VirtualBox/Windows 10, sometimes will have problem when 3D Acceleration enabled.)


Sunday, 19 June 2016

Think Java: How to Think Like a Computer Scientist

Think Java: How to Think Like a Computer Scientist

Currently used at many colleges, universities, and high schools, this hands-on introduction to computer science is ideal for people with little or no programming experience. The goal of this concise book is not just to teach you Java, but to help you think like a computer scientist. You�ll learn how to program�a useful skill by itself�but you�ll also discover how to use programming as a means to an end.

Authors Allen Downey and Chris Mayfield start with the most basic concepts and gradually move into topics that are more complex, such as recursion and object-oriented programming. Each brief chapter covers the material for one week of a college course and includes exercises to help you practice what you�ve learned.

  • Learn one concept at a time: tackle complex topics in a series of small steps with examples
  • Understand how to formulate problems, think creatively about solutions, and write programs clearly and accurately
  • Determine which development techniques work best for you, and practice the important skill of debugging
  • Learn relationships among input and output, decisions and loops, classes and methods, strings and arrays
  • Work on exercises involving word games, graphics, puzzles, and playing cards

Tuesday, 14 June 2016

BottomSheetDialog example


The former post show a example of "Implement BottomSheet of Android Design Support Library". This post show a BottomSheetDialog example.


To use Design Support Library in your app, you have to "Add Android Design Support Library to Android Studio Project", currently version 'com.android.support:design:23.4.0'.


Create a file layout/bottomsheetdialog_layout.xml, to define the layout of the BottomSheetDialog.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Bottom Sheet Dialog Example"
android:textSize="26dp"
android:textStyle="bold"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>

</LinearLayout>
</LinearLayout>

layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
tools:context="com.blogspot.android_er.androidbottomsheetdialog.MainActivity">

<LinearLayout
android:id="@+id/backgroundlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://android-er.blogspot.com/"
android:textStyle="bold" />
<TextView
android:id="@+id/textSDK"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/prompt1"
android:textSize="28dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/prompt2"
android:textSize="28dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>


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

import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.BottomSheetDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

LinearLayout backgroundLayout;
View bottomSheetView;
TextView textPrompt1, textPrompt2;
TextView textSDK;

BottomSheetDialog bottomSheetDialog;
BottomSheetBehavior bottomSheetBehavior;

int sdk_int = Build.VERSION.SDK_INT;

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

textSDK = (TextView)findViewById(R.id.textSDK);
textSDK.setText("Running SDK_INT: " + sdk_int);

textPrompt1 = (TextView)findViewById(R.id.prompt1);
textPrompt2 = (TextView)findViewById(R.id.prompt2);
backgroundLayout = (LinearLayout)findViewById(R.id.backgroundlayout);

bottomSheetView = getLayoutInflater().inflate(R.layout.bottomsheetdialog_layout, null);
bottomSheetDialog = new BottomSheetDialog(MainActivity.this);
bottomSheetDialog.setContentView(bottomSheetView);
bottomSheetBehavior = BottomSheetBehavior.from((View) bottomSheetView.getParent());
bottomSheetBehavior.setBottomSheetCallback(bottomSheetCallback);

bottomSheetDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
textPrompt1.setText("OnShow");
}
});

bottomSheetDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
textPrompt1.setText("OnDismiss");
}
});

backgroundLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
bottomSheetDialog.show();
}
});

}

BottomSheetBehavior.BottomSheetCallback bottomSheetCallback =
new BottomSheetBehavior.BottomSheetCallback(){
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
switch (newState){
case BottomSheetBehavior.STATE_COLLAPSED:
textPrompt2.setText("COLLAPSED");
break;
case BottomSheetBehavior.STATE_DRAGGING:
textPrompt2.setText("DRAGGING");
break;
case BottomSheetBehavior.STATE_EXPANDED:
textPrompt2.setText("EXPANDED");
break;
case BottomSheetBehavior.STATE_HIDDEN:
textPrompt2.setText("HIDDEN");
bottomSheetDialog.dismiss();
break;
case BottomSheetBehavior.STATE_SETTLING:
textPrompt2.setText("SETTLING");
break;
default:
textPrompt2.setText("unknown...");
}
}

@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {

}
};
}




~ More example of using Android Design Support Library, Snackbar, FloatingActionButton.

Friday, 10 June 2016

Sets the height of collapsed bottom sheet, by calling setPeekHeight() method


In lasp example of BottomSheet, I implement a OnClickListener of background to expand and collapse the bottom sheet. Without this, user cannot expand the bottom sheet if collapsed, because bottom sheet have height of 0 by default. If you want you can set the height of collapsed bottom sheet, by calling setPeekHeight() method.

This video show how:


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

import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomSheetBehavior;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

LinearLayout backgroundLayout;
View bottomSheet;
private BottomSheetBehavior bottomSheetBehavior;
TextView textPrompt;
TextView textSDK;

/*
Build.VERSION.SDK_INT:
The user-visible SDK version of the framework;
its possible values are defined in Build.VERSION_CODES.
https://developer.android.com/reference/android/os/Build.VERSION_CODES.html
*/
int sdk_int = Build.VERSION.SDK_INT;

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

textSDK = (TextView)findViewById(R.id.textSDK);
textSDK.setText("Running SDK_INT: " + sdk_int);

textPrompt = (TextView)findViewById(R.id.prompt);
backgroundLayout = (LinearLayout)findViewById(R.id.backgroundlayout);

bottomSheet = findViewById(R.id.bottomsheet);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setBottomSheetCallback(bottomSheetCallback);

bottomSheetBehavior.setPeekHeight(150);

/*
backgroundLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (bottomSheetBehavior.getState()){
case BottomSheetBehavior.STATE_COLLAPSED:
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
break;
case BottomSheetBehavior.STATE_EXPANDED:
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
break;
}
}
});
*/

}

BottomSheetBehavior.BottomSheetCallback bottomSheetCallback =
new BottomSheetBehavior.BottomSheetCallback(){
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
switch (newState){
case BottomSheetBehavior.STATE_COLLAPSED:
textPrompt.setText("COLLAPSED");
break;
case BottomSheetBehavior.STATE_DRAGGING:
textPrompt.setText("DRAGGING");
break;
case BottomSheetBehavior.STATE_EXPANDED:
textPrompt.setText("EXPANDED");
break;
case BottomSheetBehavior.STATE_HIDDEN:
textPrompt.setText("HIDDEN");
break;
case BottomSheetBehavior.STATE_SETTLING:
textPrompt.setText("SETTLING");
break;
default:
textPrompt.setText("unknown...");
}
}

@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {

}
};
}



Thursday, 9 June 2016

Scan specified BLE devices with ScanFilter


The former post show how to scan Bluetooth LE device with BluetoothLeScanner, accept all BLE devices. We can use ScanFilter to limit to specified BLE deviecs only.


Edit scanLeDevice() method of MainActivity.java in last post.

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) {

}
})
.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();
}
}
};
}


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


~ Bluetooth LE Gatt Example, step-by-step