Tutorial Fingerprint - Sử dụng cảm biến vân tay



Kể từ phiên bản 6.0, Android đã hỗ trợ thêm chức năng bảo mật bằng vân tay.
Hôm nay mình xin hướng tạo một ứng dụng đơn giản để xem cảm biến vân tay hoạt đông như thế nào!
- Chuẩn bị một máy Android chạy hệ điều hành 6.0 trở lên và đương nhiên có hỗ trợ cảm biến vân tay.
- Nếu không có thiết bị thật thì ta có thể sử dụng máy ảo ủa Android Studio
 + Tạo một máy ảo API 23, mở nó lên.
 + Vào Settings -> Security -> Fingerprint  

Để setup một fingerprint


Ở màn hình này, đương nhiên ta không thể dùng tay chạm lên đó được :D
ta sẽ dùng lệnh để giả lập hành động chạm vào cảm biến.
adb -e emu finger touch 
finger_id là 1 dãy số id, vd: adb -e emu finger touch 123456

Sau khi đó nhận id,  ta chọn Done. Nếu máy chưa cài password thì nó sẽ bắt cài rồi mới setup fingerprint.

Vậy là chúng ta đã chuẩn bị xong máy Android để test ứng dụng.

- Bây giờ chúng ta tạo mới một project
trong file manifest.xml, ta thêm quyền:
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

MainActivity.java
package com.androidtmc.fingerprint; 
 
import android.annotation.TargetApi; 
import android.app.Activity; 
import android.app.KeyguardManager; 
import android.hardware.fingerprint.FingerprintManager; 
import android.os.Build; 
import android.os.CancellationSignal; 
import android.security.keystore.KeyGenParameterSpec; 
import android.security.keystore.KeyProperties; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.view.View; 
import android.widget.Button; 
import android.widget.TextView; 
import android.widget.Toast; 
 
import java.security.Signature; 
 
@SuppressWarnings("ResourceType") 
public class MainActivity extends AppCompatActivity { 
 
 
    private Signature signature; 
    private KeyguardManager keyguardManager; 
    private FingerprintManager mFingerprintManager; 
    private Button btnListen; 
 
    @TargetApi(Build.VERSION_CODES.M) 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
 
        //create key 
       signature = CreateKeyHelper.create(this); 
 
        //check has ready set fingerprint on device 
         keyguardManager = (KeyguardManager) getSystemService(Activity.KEYGUARD_SERVICE); 
        mFingerprintManager = (FingerprintManager) getSystemService(Activity.FINGERPRINT_SERVICE); 
        TextView tvMessage  = (TextView) findViewById(R.id.tvMessage); 
        btnListen  = (Button) findViewById(R.id.btnListen); 
        if(!mFingerprintManager.isHardwareDetected()) 
            tvMessage.setText("This device is not support!"); 
        if(!keyguardManager.isKeyguardSecure()){ 
 
            tvMessage.setText("Secure lock screen hasn't set up.\n" + "\" + \"Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint"); 
        } 
 
        else if(!mFingerprintManager.hasEnrolledFingerprints()){ 
           tvMessage.setText("Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint"); 
        } 
        else{ 
            tvMessage.setText("Please put your finger on device's finger sensor"); 
            listenTouchSenor(); 
        } 
 
        btnListen.setOnClickListener(new View.OnClickListener() { 
            @Override 
            public void onClick(View v) { 
            listenTouchSenor(); 
            } 
        }); 
 
 
    } 
 
    @TargetApi(Build.VERSION_CODES.M) 
    public void listenTouchSenor(){ 
        //listen action user touch on finger sensor 
        FingerprintManager.CryptoObject cryptObject = new FingerprintManager.CryptoObject(signature); 
        CancellationSignal cancellationSignal = new CancellationSignal(); 
        FingerprintManager fingerprintManager = 
                this.getSystemService(FingerprintManager.class); 
        fingerprintManager.authenticate(cryptObject, cancellationSignal, 0, new FingerprintManager.AuthenticationCallback() { 
            @Override 
            public void onAuthenticationError(int errorCode, CharSequence errString) { 
                super.onAuthenticationError(errorCode, errString); 
                Toast.makeText(MainActivity.this, "Error: "+errString.toString(), Toast.LENGTH_SHORT).show(); 
            } 
 
            @Override 
            public void onAuthenticationHelp(int helpCode, CharSequence helpString) { 
                super.onAuthenticationHelp(helpCode, helpString); 
            } 
 
            @Override 
            public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { 
                super.onAuthenticationSucceeded(result); 
                btnListen.setVisibility(View.VISIBLE); 
                Toast.makeText(MainActivity.this, "Success: "+result.toString(), Toast.LENGTH_SHORT).show(); 
            } 
 
            @Override 
            public void onAuthenticationFailed() { 
                super.onAuthenticationFailed(); 
                Toast.makeText(MainActivity.this, "fail ", Toast.LENGTH_SHORT).show(); 
            } 
        }, null); 
    } 
} 
package com.androidtmc.fingerprint; 
 
import android.annotation.TargetApi; 
import android.content.Context; 
import android.os.Build; 
import android.security.KeyPairGeneratorSpec; 
import android.security.keystore.KeyGenParameterSpec; 
import android.security.keystore.KeyProperties; 
 
import java.io.IOException; 
import java.math.BigInteger; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.KeyPairGenerator; 
import java.security.KeyStore; 
import java.security.KeyStoreException; 
import java.security.NoSuchAlgorithmException; 
import java.security.NoSuchProviderException; 
import java.security.PrivateKey; 
import java.security.Signature; 
import java.security.UnrecoverableKeyException; 
import java.security.cert.CertificateException; 
import java.security.spec.ECGenParameterSpec; 
import java.util.Calendar; 
 
import javax.security.auth.x500.X500Principal; 
 
/** 
 * Created by cuong on 20/01/2016. 
 */ 
public class CreateKeyHelper { 
 
    private static final String KEY_NAME = "my_key"; 
 
    @TargetApi(Build.VERSION_CODES.M) 
    public static Signature create(Context context){ 
 
        KeyPairGenerator keyPairGenerator = null; 
        try { 
            keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); 
        } catch (NoSuchAlgorithmException e) { 
            e.printStackTrace(); 
        } catch (NoSuchProviderException e) { 
            e.printStackTrace(); 
        } 
        try { 
            Calendar start = Calendar.getInstance(); 
            Calendar end = Calendar.getInstance(); 
            end.add(Calendar.YEAR, 1); 
            KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context) 
                    .setAlias(KEY_NAME) 
                    .setSubject(new X500Principal("CN=HealthECard Name, O=Minh Cuong")) 
                    .setSerialNumber(BigInteger.ONE) 
                    .setStartDate(start.getTime()) 
                    .setEndDate(end.getTime()) 
                    .build(); 
            keyPairGenerator.initialize(spec); 
        } catch (InvalidAlgorithmParameterException e) { 
            e.printStackTrace(); 
        } 
        keyPairGenerator.generateKeyPair(); 
 
        Signature signature = null; 
        try { 
            signature = Signature.getInstance("SHA256withRSA");//command for linux: keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android 
 
        } catch (NoSuchAlgorithmException e) { 
            e.printStackTrace(); 
        } 
        KeyStore keyStore = null; 
        try { 
            keyStore = KeyStore.getInstance("AndroidKeyStore"); 
        } catch (KeyStoreException e) { 
            e.printStackTrace(); 
        } 
        try { 
            keyStore.load(null); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } catch (NoSuchAlgorithmException e) { 
            e.printStackTrace(); 
        } catch (CertificateException e) { 
            e.printStackTrace(); 
        } 
        PrivateKey key = null; 
        try { 
            key = (PrivateKey) keyStore.getKey(KEY_NAME, null); 
        } catch (KeyStoreException e) { 
            e.printStackTrace(); 
        } catch (NoSuchAlgorithmException e) { 
            e.printStackTrace(); 
        } catch (UnrecoverableKeyException e) { 
            e.printStackTrace(); 
        } 
        try { 
            signature.initSign(key); 
        } catch (InvalidKeyException e) { 
            e.printStackTrace(); 
        } 
        return signature; 
    } 
} 

Download SourceCode





Lưu trữ dữ liệu với Shared Preferences

Giới thiệu

Hôm nay chúng ta sẽ tạo một ứng dụng. Khi chạy dứng ụng nó sẽ hiện màng hình đăng nhập. Người dùng nhập username và password vào, nếu đến sẽ chuyển đến màng hình khác, thông báo đăng nhập thành công.
Ở màng hình đăng nhập có 1 checkbox, nếu nó được check lần sau, khi mở ứng dụng...nó sẽ load lại username và password đã đăng nhập lần trước.


Android cung cấp rất nhiều các để lưu trữ dữ liệu. Một trong số đó gọi là Shared Preferences, nó cho phép lưu và truy xuất dữ liệu thông qua cặp Key,Value.

Màng hình đăng nhập:

activity_main.xml
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> 
 
    <EditText 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:id="@+id/edtUser" 
        android:layout_marginTop="67dp" 
        android:hint="Username" 
        android:layout_alignParentRight="true" 
        android:layout_alignParentEnd="true" 
        android:layout_alignParentLeft="true" 
        android:layout_alignParentStart="true" /> 
 
    <EditText 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:id="@+id/edtPass" 
        android:layout_below="@+id/edtUser" 
        android:layout_alignParentLeft="true" 
        android:layout_alignParentStart="true" 
        android:layout_alignParentRight="true" 
        android:layout_alignParentEnd="true" 
        android:inputType="textPassword" 
        android:hint="Password" /> 
 
    <Button 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Đăng nhập" 
        android:id="@+id/btnLogin" 
        android:layout_centerHorizontal="true" 
        android:layout_marginTop="50dp" 
        android:layout_below="@+id/edtPass" /> 
 
    <CheckBox 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Ghi nhớ" 
        android:id="@+id/cbRemember" 
        android:layout_below="@+id/edtPass" 
        android:layout_toRightOf="@+id/btnLogin" 
        android:layout_toEndOf="@+id/btnLogin" 
        android:checked="false" /> 
 
</RelativeLayout>
MainActivity.java
package com.androidtmc.sharedpreferencesexample; 
 
import android.content.Context; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.view.View; 
import android.widget.Button; 
import android.widget.CheckBox; 
import android.widget.EditText; 
import android.widget.Toast; 
 
public class MainActivity extends AppCompatActivity { 
 
    EditText edtUserName, edtPass; 
    Button btnLogin; 
    CheckBox cbRemember; 
    public static final String MyPREFERENCES = "MyPrefs"; 
    public static final String USERNAME = "userNameKey"; 
    public static final String PASS = "passKey"; 
    public static final String REMEMBER = "remember"; 
    SharedPreferences sharedpreferences; 
    String userLogin = "androidTMC"; 
    String passLogin = "androidtmc"; 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        //khởi tạo shared preferences 
        sharedpreferences = getSharedPreferences(MyPREFERENCES, Context.MODE_PRIVATE); 
        initWidgets();//khởi tạo các control 
        loadData();//lấy dữ liệu đã lưu nếu có 
        //thiết đặt button đăng nhập 
        btnLogin.setOnClickListener(new View.OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                //nếu người dùng có chọn ghi nhớ 
                if (cbRemember.isChecked()) 
                    //lưu lại thông tin đăng nhập 
                    saveData(edtUserName.getText().toString(), edtPass.getText().toString()); 
                else 
                    clearData();//xóa thông tin đã lưu 
                //nếu thông tin đăng nhập đúng thì đến màng hình home 
                if (edtUserName.getText().toString().equals(userLogin) && edtPass.getText().toString().equals(passLogin)) { 
                    Intent intent = new Intent(MainActivity.this, HomeActivity.class); 
                    startActivity(intent); 
                } else 
                    Toast.makeText(MainActivity.this, "Thông tin đăng nhập không đúng", Toast.LENGTH_SHORT).show(); 
            } 
        }); 
 
    } 
 
    private void clearData() { 
        SharedPreferences.Editor editor = sharedpreferences.edit(); 
        editor.clear(); 
        editor.commit(); 
    } 
 
    private void saveData(String username, String Pass) { 
        SharedPreferences.Editor editor = sharedpreferences.edit(); 
        editor.putString(USERNAME, username); 
        editor.putString(PASS, Pass); 
        editor.putBoolean(REMEMBER,cbRemember.isChecked()); 
        editor.commit(); 
    } 
 
    private void loadData() { 
        if(sharedpreferences.getBoolean(REMEMBER,false)) { 
            edtUserName.setText(sharedpreferences.getString(USERNAME, "")); 
            edtPass.setText(sharedpreferences.getString(PASS, "")); 
            cbRemember.setChecked(true); 
        } 
        else 
            cbRemember.setChecked(false); 
 
    } 
 
    private void initWidgets() { 
        edtUserName = (EditText) findViewById(R.id.edtUser); 
        edtPass = (EditText) findViewById(R.id.edtPass); 
        cbRemember = (CheckBox) findViewById(R.id.cbRemember); 
        btnLogin = (Button) findViewById(R.id.btnLogin); 
    } 
 
 
    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
        // Inflate the menu; this adds items to the action bar if it is present. 
        getMenuInflater().inflate(R.menu.menu_main, menu); 
        return true; 
    } 
 
    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
        // Handle action bar item clicks here. The action bar will 
        // automatically handle clicks on the Home/Up button, so long 
        // as you specify a parent activity in AndroidManifest.xml. 
        int id = item.getItemId(); 
 
        //noinspection SimplifiableIfStatement 
        if (id == R.id.action_settings) { 
            return true; 
        } 
 
        return super.onOptionsItemSelected(item); 
    } 
} 

Màng hình Home (đăng nhập thành công)


<RelativeLayout 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" 
    tools:context="com.androidtmc.sharedpreferencesexample.HomeActivity"> 
 
    <TextView 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Đăng nhập thành công" /> 
</RelativeLayout> 

Chạy ứng dụng để xem kết quả!
Source code

Thêm Xóa Sửa các item trong ListView

Ở 2 bài hôm trước Sử dụng ListView chúng ta đã tạo một ứng dụng hiện thị danh sách các sản phẩm lên ListView.
Hôm này chúng ta thực hiện các thao tác thêm, sửa, xóa các sản phẩm trên ListView.



Khi chúng ta click vào 1 item nó sẽ chuyển đến màng hình thứ 2 cho phép chúng ta xem hay sửa thông tin sản phẩm đó

Layout cho màng hình 2

<?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="vertical" > 
    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content"> 
        <TextView 
            android:layout_width="100dp" 
            android:layout_height="wrap_content" 
            android:text="Product name" 
            android:textSize="20sp" /> 
        <EditText android:id="@+id/txtProductName" 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content" 
            android:ems="10" 
            android:inputType="text" 
            android:textSize="20sp"> 
            <requestFocus /> 
        </EditText> 
    </LinearLayout> 
    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content"> 
        <TextView 
            android:layout_width="100dp" 
            android:layout_height="wrap_content" 
            android:text="Unit" 
            android:textSize="20sp" /> 
        <EditText 
            android:id="@+id/txtUnit" 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content" 
            android:inputType="text" 
            android:textSize="20sp"/> 
    </LinearLayout> 
    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content"> 
        <TextView 
            android:layout_width="100dp" 
            android:layout_height="wrap_content" 
            android:text="Price" 
            android:textSize="20sp" /> 
        <EditText 
            android:id="@+id/txtPrice" 
            android:layout_width="match_parent" 
            android:layout_height="wrap_content" 
            android:inputType="text|numberDecimal" 
            android:textSize="20sp"/> 
    </LinearLayout> 
    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:gravity="center"> 
        <Button android:id="@+id/btnOK" 
            android:layout_width="100dp" 
            android:layout_height="wrap_content" 
            android:textSize="20sp" 
            android:text="OK"/> 
        <Button android:id="@+id/btnCancel" 
            android:layout_width="100dp" 
            android:layout_height="wrap_content" 
            android:textSize="20sp" 
            android:text="Cancel"/> 
    </LinearLayout> 
</LinearLayout>

Class xử lý trên màng hình 2

ProductActivity.java
ProductActivity.java

package com.androidtmc.sales; 
 
import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.widget.Button; 
import android.widget.EditText; 
 
import com.androidtmc.sales.models.Product; 
import com.androidtmc.sales.models.SaleManager; 
 
import java.text.DecimalFormat; 
 
/** 
 * Created by minhc_000 on 13/08/2015. 
 */ 
public class ProductActivity extends Activity { 
    public final static String EXTRA_POSITION = "position"; 
    EditText txtProductName,txtUnit,txtPrice; 
    Product product; 
    int position; 
 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_product); 
        //lấy các control trên layout 
        txtProductName = (EditText)findViewById(R.id.txtProductName); 
        txtUnit = (EditText)findViewById(R.id.txtUnit); 
        txtPrice = (EditText)findViewById(R.id.txtPrice); 
        //hiển thị lên màng hình 
        Intent it = getIntent(); 
        position = it.getExtras().getInt(EXTRA_POSITION); 
        product = (Product) SaleManager.get().getProducts().get(position); 
        txtProductName.setText(product.getProductName()); 
        txtUnit.setText(product.getUnit()); 
        String s = (new DecimalFormat("#,###.##")).format(product.getPrice()); 
        txtPrice.setText(s); 
        //thiết đặt sự kiện khi click vào các button 
        Button btnOK = (Button)findViewById(R.id.btnOK); 
        Button btnCancel = (Button)findViewById(R.id.btnCancel); 
        btnOK.setOnClickListener(new OKClickListener()); 
        btnCancel.setOnClickListener(new CancelClickListener()); 
    } 
 
    class OKClickListener implements View.OnClickListener 
    { 
        @Override 
        public void onClick(View v) { 
            //lấy dữ liệu từ layout để cập nhật lại các sản phẩm trong mảng 
            product.setProductName(txtProductName.getText().toString()); 
            product.setUnit(txtUnit.getText().toString()); 
            String s = txtPrice.getText().toString(); 
            s = s.replace(",", ""); 
            double price = Double.parseDouble(s); 
            product.setPrice(price); 
            Intent returnIntent = new Intent(); 
            setResult(Activity.RESULT_OK, returnIntent); 
            finish(); 
        } 
    } 
    class CancelClickListener implements View.OnClickListener 
    { 
        @Override 
        public void onClick(View v) { 
            // không làm gì cả và trở về màng hình trước 
            Intent returnIntent = new Intent(); 
            setResult(Activity.RESULT_CANCELED, returnIntent); 
            finish(); 
        } 
    } 
} 

Tới đây chúng ta có thể build app và thử sửa thông tin của các sản phẩm trong Listview

Thêm button Add trên Actionbar


Để làm được điều này ta chỉnh sửa ở file menu_main.xml


menu_main.xml
menu_main.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    xmlns:tools="http://schemas.android.com/tools" 
    tools:context=".MainActivity"> 
    <item 
        android:id="@+id/btnAdd" 
        android:orderInCategory="100" 
        android:title="Thêm" 
        android:icon="@android:drawable/ic_menu_add" 
        app:showAsAction="always|withText" /> 
</menu> 

Để sử dụng ta Override lại hàm onOptionsItemSelected(MenuItem item) trong class MainActivity
MainActivity.java
MainActivity.java
@Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
        int id = item.getItemId(); 
        // nếu btnAdd được click 
        if (id == R.id.btnAdd) { 
            //đến màng hình ProductActivity 
            Intent intent = new Intent(this, ProductActivity.class); 
            //tham số -1 tức ta không truyền 1 position của item nào cả 
            //ta mở ProductActivity để thêm sp mới 
            intent.putExtra(ProductActivity.EXTRA_POSITION, -1); 
            startActivityForResult(intent, 0); 
            return true; 
        } 
        return super.onOptionsItemSelected(item); 
    }
 Tiếp theo ta chỉnh trong hàm onCreate và class OKClickListener trong Class ProductActivity.

Xóa 1 item trong Listview 

Khi chúng ta nhấn giữ lâu sẽ xuât hiện một hộp thoại thông báo xác nhận chúng ta có xóa sản phẩm này không.
+ Sự kiện nhấn giữ lâu 1 item trong Listview. Ta thêm dòng này trong OnCreate của class MainActivity
lv.setOnItemLongClickListener(new ItemLongClickRemove());
Class ItemLongClickRemove ta sẽ định nghĩa sau.
+ Để xuất hiện hộp thoại trên ta dùng đối tượng AlertDialog.Builder
+ Ta tạo thêm classItemLongClickRemove trong class MainActivity
MainActivity.java
MainActivity.java
private class ItemLongClickRemove implements AdapterView.OnItemLongClickListener { 
        @Override 
        public boolean onItemLongClick(AdapterView parent, View view, final int position, long id) { 
            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this); 
            alertDialogBuilder.setMessage("Bán có muốn xóa sản phẩm này!"); 
            alertDialogBuilder.setPositiveButton("Có", new DialogInterface.OnClickListener() { 
                @Override 
                public void onClick(DialogInterface dialog, int which) { 
                    // xóa sp đang nhấn giữ 
                    SaleManager.get().getProducts().remove(position); 
                    //cập nhật lại listview 
                    adapter.notifyDataSetChanged(); 
 
                } 
            }); 
            alertDialogBuilder.setNegativeButton("Không", new DialogInterface.OnClickListener() { 
                @Override 
                public void onClick(DialogInterface dialog, int which) { 
                    //không làm gì 
                } 
            }); 
            alertDialogBuilder.show(); 
            return true; 
        } 
    }

Đến đây ứng dụng đã hoàn thành!
Source code
Bài tiếp theo: Sử dụng SQLite tạo Database lưu trữ trong Android
---


----

Hướng dẫn dùng SQLite tạo Database lưu trữ trong Android

Ở bài hôm trước chúng ta đã tạo một ứng dụng hiện thị danh sách các sản phẩm lên ListView và biết thêm các thao tác thêm, xóa, sửa trên ListView.
Hôm nay chúng ta tiếp tục sử dụng lại Project đó, nhưng các sản phẩm chúng ta sẽ được lưu ở dạng Database sử dụng SQLite.(Nhấn vào tên class để xem toàn bộ source class đó)
1. Ta thêm thuộc tính private long ProductIDvào trong class Product
2. Tạo thêm package com.androidtmc.sales.repository. Ta tạo thêm 2 class để xử lý Database SQLite
 - DatabaseHelper
ProductRepository

package com.androidtmc.sales.repository; 
 
import android.content.Context; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteOpenHelper; 
 
public class DatabaseHelper extends SQLiteOpenHelper { 
    static final String DB_NAME = "Sales"; 
    public DatabaseHelper(Context context) { 
        super(context, DB_NAME, null, 1); // 1: version 
    } 
    @Override 
    public void onCreate(SQLiteDatabase db) { 
        String sql = ProductRepository.getCreateTableSQL(); 
        db.execSQL(sql); 
    } 
    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    } 
} 

Gii thích:
Class SQLiteOpenHelper giúp chúng ta định nghĩa tên database (phi truyn tên database cho constructor)
SQLiteOpenHelper cung cp hàm o onCreate để thc hin các lnh to bng lđầu tiên và hàm o onUpgrade để thi hành các lnh bsung, chnh sa bng nếuchúng ta cn thay đổi. Điu này được thc hin thông qua biến version:
• Ln đầu chy vi version là 1, SQLite gi hàm onCreate để thi hành cáclnh to bng ban đầu. Database được ghi nhn là version 1.
Nếu version được sa li là 2, SQLite gi hàm onUpgrade vi oldVersion = 1 và newVersion = 2. Lúc đó ta có ththi hành lnh chnh sa bng hoc bsung bng mi. Database được ghi nhn là version 2.
Nếu version được sa tiếp là 3, SQLite gi hàm onUpgrade voldVersion = 2 và newVersion = 3. Lúc đó ta có ththi hành lnh chnh sa tiếp. Sau đó database được ghi nhn là version 3.
Nếu các ln chy tiếp theo version không được sa thì SQLiteOpenHelper skhông gi làm onCreate cũng như onUpgrade. Cutrúc database vn ginguyên.



Bây gi, để thc hin các thao tác thêm, xóa, sa, truy xut dliu trên bng Product, to class ProductRepository cũng trong package com.androidtmc.repository:
package com.androidtmc.sales.repository; 
 
import android.content.ContentValues; 
import android.content.Context; 
import android.database.Cursor; 
import android.database.sqlite.SQLiteDatabase; 
 
import com.androidtmc.sales.models.Product; 
 
import java.util.ArrayList; 
 
/** 
 * Created by minhc_000 on 13/08/2015. 
 */ 
public class ProductRepository { 
    static final String TAB_PRODUCT = "Product"; 
    static final String COL_PRODUCTID = "ProductID"; 
    static final String COL_PRODUCTNAME = "ProductName"; 
    static final String COL_UNIT = "Unit"; 
    static final String COL_PRICE ="Price"; 
 
    public static String getCreateTableSQL() 
    { 
        String sql = "CREATE TABLE " + TAB_PRODUCT + "(" + 
                COL_PRODUCTID + " integer primary key autoincrement, " + 
                COL_PRODUCTNAME + " text, " + 
                COL_UNIT + " text, " + 
                COL_PRICE + " real)"; 
        return sql; 
    } 
    private DatabaseHelper dbhelper; 
    public ProductRepository(Context context) { 
        dbhelper = new DatabaseHelper(context); 
    } 
    private ContentValues MakeProductContentValues(Product p) 
    { 
        ContentValues cv = new ContentValues(); 
        cv.put(COL_PRODUCTNAME, p.getProductName()); 
        cv.put(COL_UNIT, p.getUnit()); 
        cv.put(COL_PRICE, p.getPrice()); 
        return cv; 
    } 
    public void insertProduct(Product p) 
    { 
        ContentValues cv = MakeProductContentValues(p); 
        SQLiteDatabase db = dbhelper.getWritableDatabase(); 
        long id = db.insert(TAB_PRODUCT, null, cv); 
        if (id!= -1) p.setProductID(id); 
    } 
    public void updateProduct(Product p) 
    { 
        ContentValues cv = MakeProductContentValues(p); 
        SQLiteDatabase db = dbhelper.getWritableDatabase(); 
        db.update(TAB_PRODUCT, cv, COL_PRODUCTID + "=?", 
                new String[] { String.valueOf(p.getProductID()) }); 
    }public void deleteProduct(long id) 
    { 
        SQLiteDatabase db = dbhelper.getWritableDatabase(); 
        db.delete(TAB_PRODUCT,COL_PRODUCTID + " = " + id,null); 
    } 
    public ArrayList<Product> getAllProduct() 
    { 
        ArrayList<Product> products = new ArrayList<Product>(); 
        SQLiteDatabase db = dbhelper.getReadableDatabase(); 
        String sql = "SELECT * FROM " + TAB_PRODUCT; 
        Cursor cursor = db.rawQuery(sql, null); 
        int indexProductID = cursor.getColumnIndex(COL_PRODUCTID); 
        int indexProductName = cursor.getColumnIndex(COL_PRODUCTNAME); 
        int indexUnit = cursor.getColumnIndex(COL_UNIT); 
        int indexPrice = cursor.getColumnIndex(COL_PRICE); 
        if (cursor.moveToFirst()) { 
            while (!cursor.isAfterLast()) { 
                Product p = new Product(); 
                p.setProductID(cursor.getLong(indexProductID)); 
                p.setProductName(cursor.getString(indexProductName)); 
                p.setUnit(cursor.getString(indexUnit)); 
                p.setPrice(cursor.getDouble(indexPrice)); 
                products.add(p); 
                cursor.moveToNext(); 
            } 
        } 
        cursor.close(); 
        return products; 
    } 
} 

Gii thích:
Các hng sTAB_PRODUCT, COL_PRODUCTID, COL_PRODUCTNAME,
COL_UNIT, COL_PRICE xác định tên bng và tên các thuc tính.
Hàm getCreateTableSQL(): to câu lnh to bng dùng cho DatabaseHelper. Câu lnh
to bng này có giá trnhư sau:
CREATE Product (
ProductID integer primary key autoincrement,
ProductName text,
Unit text,
Price real)
primary key nhm xác định khóa chính, autoincrement nhm xác định ProductID là biến tự động tăng khi thêm record vào cơ sdliu.
Khi thêm hay sa record, Android yêu cu cung cp giá trca các ct thông qua biếContentValues. Ta sdng hàm MakeProductContentValues để chuyn giá trtừ đối
tượng Product sang ContentValues.
Hàm insertProduct: thêm sn phm vào database. Ta gi hàm insert cSQLiteDatabase. Hàm insert này cũng trvgiá trca ct ProductID (ttăng), ta cp
nht ngược li vào đối tượng Product.
Hàm updateProduct: sa thông tin mt sn phm. Ta gi hàm update cSQLiteDatabase, sdng ProductID ca sn phm để xác định sn phm nào cupdate.
Hàm getAllProduct: Truy xut tt ccác sn phm có trong database và chuyn thành ArrayList. Ta dùng hàm rawQuery để thc hin mnh đề SELECT và dùng
cursor để truy xut kết qu. Cursor có các phương thc moveToFirst(), moveToNext() và IsAfterLast()


Cui cùng để sdng ProductRepository để truy xut cơ sdliu, ta sa hàm OnCreate trong  MainActivity li như sau:
lv = (ListView) findViewById(R.id.lvProducts); 
        //Khởi tạo các sản phẩm 
        //lấy sản phẩm từ database 
        repo = new ProductRepository(this); 
        if(repo.getAllProduct().size()>0)//nếu đã có 
        //lưu lấy ra mảng 
            SaleManager.get().setProducts(repo.getAllProduct()); 
        else{//nếu chưa có trong database 
            //dùng hàm tạo sẳn để test 
            saleManager.generateProducts(); 
        } 
 
        adapter = new ProductAdapter(this,SaleManager.get().getProducts());//khởi tạo adapter 
        lv.setAdapter(adapter);//hiển lên listview

Ta sửa lại class OKClickListener trong class ProductActivity để lưu sản phẩm mới vào Database khi click OK
class OKClickListener implements View.OnClickListener 
    { 
        @Override 
        public void onClick(View v) { 
            //lấy dữ liệu từ layout để cập nhật lại các sản phẩm trong mảng 
            product.setProductName(txtProductName.getText().toString()); 
            product.setUnit(txtUnit.getText().toString()); 
            String s = txtPrice.getText().toString(); 
            s = s.replace(",", ""); 
            double price = Double.parseDouble(s); 
            product.setPrice(price); 
            //nếu position = -1 ta thực hiện chứ năng thêm sp mới vào mảng 
            if (position == -1){ 
                SaleManager saleManager = new SaleManager(); 
                SaleManager.get().getProducts().add(product); 
                ProductRepository repo = new ProductRepository(ProductActivity.this); 
                repo.insertProduct(product);// lưu vào database 
            } 
 
            Intent returnIntent = new Intent(); 
            setResult(Activity.RESULT_OK, returnIntent); 
            finish(); 
        } 
    }

Để xóa sản phẩm trong Database ta sửa lại class OnItemClickRemve trong MainActivity

 
 
    private class ItemLongClickRemove implements AdapterView.OnItemLongClickListener { 
        @Override 
        public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) { 
            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this); 
            alertDialogBuilder.setMessage("Bán có muốn xóa sản phẩm này!"); 
            alertDialogBuilder.setPositiveButton("Có", new DialogInterface.OnClickListener() { 
                @Override 
                public void onClick(DialogInterface dialog, int which) { 
                    // xóa sp đang nhấn giữ 
 
                    Product pro = (Product)SaleManager.get().getProducts().get(position); 
                    //xóa sản phẩm đang chọn trong Database 
                    repo.deleteProduct(pro.getProductID()); 
                    SaleManager.get().getProducts().remove(position); 
                    //cập nhật lại listview 
                    adapter.notifyDataSetChanged(); 
 
                } 
            }); 
            alertDialogBuilder.setNegativeButton("Không", new DialogInterface.OnClickListener() { 
                @Override 
                public void onClick(DialogInterface dialog, int which) { 
                    //không làm gì 
                } 
            }); 
            alertDialogBuilder.show(); 
            return true; 
        } 
    }


Bây giờ chúng ta có thể chạy ứng dụng và kiểm tra