Chào các bạn, mình là Vũ.
Hôm nay nhân dịp tí nữa đo đường vì tội vừa đi xe máy vừa gọi điện thoại, mà bản chất cũng chỉ vì sự rắc rối và lóng ngóng khi tìm contact để call. Mình sẽ xây dựng 1 loạt bài tutorial về ứng dụng quay số nhanh trên Android.
Chức năng chủ yếu của ứng dụng này là cho phép tạo ra 1 widget ngoài màn hình home của điện thoại chứa những contact mà ta hay liên lạc. Đồng thời widget này cũng chứa lịch sử cuộc gọi và bàn phím số. Tóm lại nó giúp người dùng đơn giản hóa tối đa thao tác để liên lạc với người khác. Sau đây là video demo của ứng dụng.
[toc]
Chức năng và luồng chạy của ứng dụng
Chức năng chi tiết
Các chức năng của ứng dụng mà mình sẽ làm bao gồm:
- Thêm, bớt 1 contact vào danh sách quay số nhanh (trên app)
- Xem danh sách quay số nhanh (trên app), cho phép gọi, nhắn tin (đến 1 hoặc nhiều người).
- Tạo widget ngoài màn hình home, widget này chứa
- Danh sách quay số nhanh, cho phép gọi điện thoại khi click vào contact
- Lịch sử cuộc gọi, cho phép gọi điện thoại khi click vào 1 lịch sử
- Bàn phím số, cho phép nhập số điện thoại để gọi
Khá đơn giản, đúng không nào.
Luồng chạy của ứng dụng
- Ban đầu bạn cần vào app và định nghĩa ra danh sách quay số nhanh. Bằng cách chọn lựa các contact từ danh bạ điện thoại. Danh sách quay số nhanh này có thể thêm bớt, tùy chỉnh về sau.
- Sau khi có danh sách quay số nhanh, bạn có thể kéo widget của ứng dụng ra ngoài màn hình home. Widget này có chứa danh sách bạn đã tạo trên app, lịch sử cuộc gọi, bàn phím, cho phép make a phone call 1 cách dễ dàng nhất.
Luồng chạy cũng không có gì phức tạp, nhỉ?
Khởi tạo dự án
Các thư viện sử dụng
Mình sẽ sử dụng những thư viện sau đây để code:
//BUtter Knife dùng để bind view compile "com.jakewharton:butterknife:$rootProject.butterKnifeVersion" //SDP android, thư viện chứa các dimensions, hỗ trợ làm layout đa màn hình compile "com.intuit.sdp:sdp-android:$rootProject.ext.sdpAndroidVersion" //Material dialog, giao diện dialog material cho android đời thấp compile 'com.afollestad.material-dialogs:core:0.9.4.4' //Thư viện log compile "com.orhanobut:logger:$rootProject.loggerVersion" //ImageView bo góc, mình lười nên dùng lib luôn compile 'com.makeramen:roundedimageview:2.3.0' //HIệu ứng ripple khi click vào các view compile 'com.balysv:material-ripple:1.0.2' //Gson dùng để parse json compile 'com.google.code.gson:gson:2.4' //Thư viện danh bạ điện thoại compile 'com.github.tamir7.contacts:contacts:1.1.7' //Thư viện expandable recyclerview, thư viện này mình import từ 1 module trong project compile project(':libs:expandablerecyclerview')
Tạo base cho dự án
Permission
Ứng dụng cần quyền truy cập danh bạ, lịch sử cuộc gọi, gọi điện thoại và gửi tin nhắn. Vì vậy ta cần thêm các dòng sau vào file AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.CALL_PHONE"></uses-permission> <uses-permission android:name="android.permission.READ_CALL_LOG"></uses-permission> <uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
BaseActivity
Ở ứng dụng này, trên App ta cần xây dựng 2 màn hình:
- Danh sách quay số nhanh
- Màn hình Chọn lựa contact từ danh bạ để thêm vào danh sách quay số nhanh.
Mình sẽ sử dụng 2 Activity để xây dựng 2 màn hình này. Để code được ngắn gọn và tường minh, mình sẽ viết 1 lớp BaseActivity.java. Lớp này chứa 1 số phương thức chung có thể sử dụng cho tất cả các Activity trong dự án. Mọi Activity sẽ kế thừa từ lớp BaseActivity này:
public abstract class BaseActivity extends AppCompatActivity { protected Gson gson; protected DBHelper dbHelper; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); gson = new Gson(); dbHelper = QuickDialApplication.getInstance().getDbHelper(); ButterKnife.bind(this); createView(); } //Hàm abstract trả về layout của activity protected abstract int getLayoutId(); //Hàm set sự kiện cho các view trong activity protected abstract void createView(); //Hàm show thông báo toast public void showToast(String msg) { if (msg != null) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "null", Toast.LENGTH_SHORT).show(); } } //Hàm chuyển activity public void showActivity(Class t) { Intent intent = new Intent(this, t); startActivity(intent); } ////Hàm chuyển activity kèm theo bundle public void showActivity(Class t, Bundle bundle) { Intent intent = new Intent(this, t); intent.putExtra(Constant.KEY_EXTRA, bundle); startActivity(intent); } }
Màn hình danh sách quay số nhanh QuickDialActivity
Khai báo layout
Sau khi tạo xong base dự án ta sẽ bắt tay vào màn hình đầu tiên, là màn hình Danh sách quay số nhanh. Mình đặt tên Activity tương ứng là QuickDialActivity. Mình tạo 1 file activity_quick_dial.xml làm layout cho activity này:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:contentInsetEnd="0dp" android:contentInsetLeft="0dp" android:contentInsetRight="0dp" android:contentInsetStart="0dp" app:contentInsetEnd="0dp" app:contentInsetLeft="0dp" app:contentInsetRight="0dp" app:contentInsetStart="0dp" app:popupTheme="@style/AppTheme.PopupOverlay"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/iv_icon" android:layout_width="@dimen/_27sdp" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="@dimen/_10sdp" android:adjustViewBounds="true" android:src="@drawable/ic_icon" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="@dimen/_10sdp" android:layout_toRightOf="@id/iv_icon" android:text="@string/app_name" android:textColor="@android:color/white" android:textSize="16sp" android:textStyle="bold" /> <ImageView android:id="@+id/bt_add" android:layout_width="@dimen/_26sdp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="@dimen/_10sdp" android:adjustViewBounds="true" android:padding="@dimen/_5sdp" android:src="@drawable/ic_add_people" /> <ImageView android:id="@+id/bt_remove" android:layout_width="@dimen/_26sdp" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="@dimen/_10sdp" android:layout_toLeftOf="@+id/bt_add" android:adjustViewBounds="true" android:padding="@dimen/_5sdp" android:src="@drawable/ic_edit" /> <TextView android:id="@+id/bt_confirm_remove" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="@dimen/_10sdp" android:text="@string/action_remove" android:textColor="@android:color/white" android:textSize="15sp" android:textStyle="bold" android:visibility="gone" /> <TextView android:id="@+id/bt_send_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="@dimen/_10sdp" android:layout_toLeftOf="@+id/bt_confirm_remove" android:text="@string/action_message" android:textColor="@android:color/white" android:textSize="15sp" android:textStyle="bold" android:visibility="gone" /> </RelativeLayout> </android.support.v7.widget.Toolbar> </android.support.design.widget.AppBarLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/activity_quick_dial"> <GridView android:id="@+id/gv_quick_dial" android:layout_width="match_parent" android:layout_height="match_parent" android:columnWidth="@dimen/_54sdp" android:horizontalSpacing="@dimen/_10sdp" android:numColumns="auto_fit" android:verticalSpacing="@dimen/_10sdp"></GridView> <LinearLayout android:id="@+id/ll_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:orientation="vertical" android:visibility="gone"> <ImageView android:layout_width="@dimen/_40sdp" android:layout_height="wrap_content" android:adjustViewBounds="true" android:src="@drawable/ic_layout_add" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/_10sdp" android:gravity="center" android:text="@string/no_contact" android:textSize="16sp" /> </LinearLayout> <android.support.design.widget.FloatingActionButton xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/bt_setting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_margin="@dimen/_10sdp" android:src="@drawable/ic_setting" app:backgroundTint="@color/colorPrimaryDark" /> </RelativeLayout> </android.support.design.widget.CoordinatorLayout>
Layout khi danh sách quay số nhanh đã có contact trông sẽ thế này:
Về cơ bản, layout này chứa:
- GridView để hiển thị danh sách quay số nhanh. (gv_quick_dial)
- Layout thông báo chưa có liên lạc nào trong danh sách, click vào layout này sẽ chuyển sang màn hình chọn liên lạc (ll_add)
- 1 vài button chức năng khác (ta chưa cần quan tâm)
Vậy 2 chức năng đầu tiên mà ta cần xây dựng là:
- Lấy được danh sách quay số nhanh
- HIển thị được danh bạ điện thoại để chọn liên lạc.
Danh sách quay số nhanh
Các liên hệ trong danh sách quay số nhanh cần được lưu trữ trong 1 database để ta có thể đem ra sử dụng. Ở đây mình dùng luôn SQLite. Vậy công việc đầu tiên của ta là xây dựng 1 database bằng SQLite cho phép thêm, xóa 1 contact vào trong database. Mình sẽ tạo 1 package db và tạo file DBHelper.java. Lớp DBHelper này sẽ quản lý việc thêm, xóa dữ liệu về contact vào database. Cụ thể:
- Lưu dữ liệu từ đối tượng Contact vào bảng QContact (thuộc thư viện danh bạ mà mình đã nêu ra ban đầu, đối tượng này chứa các thông tin của 1 contact trong danh bạ điện thoại)
- Lấy các dữ liệu đã lưu theo dạng List các đối tượng WidgetContactModel để hiển thị ra ở trên Widget ngoài màn hình home.
Ok, vậy giờ mình sẽ định nghĩa đối tượng WidgetContactModel trước. Mình tạo 1 package models, và file WidgetContactModel.class:
public class WidgetContactModel implements Parcelable { int id; String displayName; String numbers; String avatar; byte[] avatarBitmap; public WidgetContactModel(int id, String displayName, String numbers, String avatar) { this.id = id; this.displayName = displayName; this.numbers = numbers; this.avatar = avatar; } public WidgetContactModel(int id, String displayName, String numbers, String avatar, byte[] avatarBitmap) { this.id = id; this.displayName = displayName; this.numbers = numbers; this.avatar = avatar; this.avatarBitmap = avatarBitmap; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getNumbers() { return numbers; } public void setNumbers(String numbers) { this.numbers = numbers; } public String getAvatar() { return avatar; } public void setAvatar(String avatar) { this.avatar = avatar; } public byte[] getAvatarBitmap() { return avatarBitmap; } public void setAvatarBitmap(byte[] avatarBitmap) { this.avatarBitmap = avatarBitmap; } public WidgetContactModel(Parcel in) { id = in.readInt(); displayName = in.readString(); numbers = in.readString(); avatar = in.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(id); parcel.writeString(displayName); parcel.writeString(numbers); parcel.writeString(avatar); } public static final Creator CREATOR = new Creator() { @Override public Object createFromParcel(Parcel parcel) { return new WidgetContactModel(parcel); } @Override public WidgetContactModel[] newArray(int i) { return new WidgetContactModel[i]; } }; }
Xong, giờ mình sẽ quay lại viết nội dung cho DBHelper thực hiện các tác vụ của 1 database:
public class DBHelper extends SQLiteOpenHelper { private static final String DB_NAME = "quick-dial-db"; private static final int DB_VERSION = 3; private static final String CREATE_TABLE_CONTACT = "create table QContact(id integer primary key autoincrement not null, display_name text, numbers text, avatar text, avatar_bitmap blob);"; public DBHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_CONTACT); } @Override public void onUpgrade(SQLiteDatabase db, int i, int i1) { db.execSQL("drop table if exists QContact"); onCreate(db); } public ArrayList<WidgetContactModel> getListContact() { String query = "select * from QContact"; Cursor cursor = getReadableDatabase().rawQuery(query, null); ArrayList<WidgetContactModel> listContact = new ArrayList<>(); if (cursor.getCount() > 0) { cursor.moveToFirst(); do { WidgetContactModel contact; contact = new WidgetContactModel(cursor.getInt(0), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getBlob(4)); listContact.add(contact); } while (cursor.moveToNext()); } cursor.close(); return listContact; } public boolean checkContactSaved(ContactModel contact, int position) { Cursor cursor = getReadableDatabase().query("QContact", new String[]{"id", "display_name", "numbers", "avatar"}, "display_name = ? and numbers = ?", new String[]{contact.getTitle(), contact.getItems().get(position).getPhoneNumber()}, null, null, null); boolean saved = false; if (cursor.getCount() > 0) { cursor.moveToFirst(); do { boolean firstCondition = cursor.getString(cursor.getColumnIndex("display_name")).equalsIgnoreCase(contact.getTitle()); if (!firstCondition) { continue; } boolean secondCondition = false; String numbers = cursor.getString(cursor.getColumnIndex("numbers")); secondCondition = numbers.equalsIgnoreCase(contact.getItems().get(position).getPhoneNumber()); saved = firstCondition && secondCondition; if (saved) { break; } } while (cursor.moveToNext()); cursor.close(); return saved; } else { cursor.close(); return saved; } } public boolean checkContactSaved(Contact contact, int position) { Cursor cursor = getReadableDatabase().query("QContact", new String[]{"id", "display_name", "numbers", "avatar"}, "display_name = ? and numbers = ?", new String[]{contact.getDisplayName(), contact.getPhoneNumbers().get(position).getNumber()}, null, null, null); boolean saved = false; if (cursor.getCount() > 0) { cursor.moveToFirst(); do { boolean firstCondition = cursor.getString(cursor.getColumnIndex("display_name")).equalsIgnoreCase(contact.getDisplayName()); if (!firstCondition) { continue; } boolean secondCondition = false; String numbers = cursor.getString(cursor.getColumnIndex("numbers")); secondCondition = numbers.equalsIgnoreCase(contact.getPhoneNumbers().get(position).getNumber()); saved = firstCondition && secondCondition; if (saved) { break; } } while (cursor.moveToNext()); cursor.close(); return saved; } else { cursor.close(); return saved; } } public long saveContact(ContactModel contact, int numberPosition, byte[] avatarBitmap) { if (!checkContactSaved(contact, numberPosition)) { LogUtils.d("contact save true"); ContentValues contentValues = new ContentValues(); contentValues.put("display_name", contact.getTitle()); StringBuffer numbersBuffer = new StringBuffer(); numbersBuffer.append(contact.getItems().get(numberPosition).getPhoneNumber()); contentValues.put("numbers", numbersBuffer.toString()); contentValues.put("avatar", (contact.getUriAvatar() != null && !contact.getUriAvatar().isEmpty()) ? contact.getUriAvatar() : ""); contentValues.put("avatar_bitmap", avatarBitmap); return getWritableDatabase().insert("QContact", null, contentValues); } else { LogUtils.d("contact save false"); return -1; } } public long removeContact(ContactModel contact, int numberPosition) { return getWritableDatabase().delete("QContact", "display_name = ? and numbers = ?", new String[]{contact.getTitle(), contact.getItems().get(numberPosition).getPhoneNumber()}); } private long removeContact(WidgetContactModel contact) { return getWritableDatabase().delete("QContact", "display_name = ? and numbers = ?", new String[]{contact.getDisplayName(), contact.getNumbers()}); } public List<Boolean> removeContacts(List<WidgetContactModel> listContact) { List<Boolean> listResult = new ArrayList<>(); for (WidgetContactModel contactModel : listContact) { listResult.add(removeContact(contactModel) > 0); } return listResult; } }
Như vậy là đã xong các phương thức quản lý dữ liệu. Tiếp theo ta sẽ làm đến bước hiển thị chúng ra màn hình.
Hiển thị danh sách quay số nhanh
Ta sẽ xây dựng adapter hiển thị danh sách quay số nhanh. adapter này sẽ được sử dụng sau khi lấy được danh sách quay số nhanh từ cơ sở dữ liệu. Mình tạo 1 package adapter và tạo file ListQuickDialAdapter.java.
Adapter này sẽ có 2 dạng item:
- Item hiển thị contact
- Item hiển thị button Add Contact
Vậy mình sẽ phải xây dựng layout cho 2 dạng item này. Với item hiển thị contact, mình tạo 1 file item_quick_dial.xml như sau:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="@dimen/_54sdp" android:layout_height="wrap_content"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_item_widget" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:padding="@dimen/_7sdp"> <com.makeramen.roundedimageview.RoundedImageView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/iv_avatar" android:layout_width="@dimen/_40sdp" android:layout_height="@dimen/_40sdp" android:layout_gravity="center_horizontal" android:scaleType="fitXY" app:riv_mutate_background="true" app:riv_oval="true" /> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_gravity="center_horizontal" android:layout_marginTop="@dimen/_3sdp" android:gravity="center" android:lines="2" android:maxLines="2" android:text="BH" android:textSize="11sp" android:textStyle="bold" /> </LinearLayout> <CheckBox android:layout_alignParentRight="true" android:id="@+id/cb_remove" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
Đơn giản là chứa ImageView hiển thị avatar, TextView hiển thị tên và 1 CheckBox để đánh dấu chọn contact (chức năng này mình sẽ nói sau)
Đối với item hiển thị Button add thêm contact, mình tạo 1 file item_add_more.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/layout_item_widget" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:padding="@dimen/_7sdp"> <com.makeramen.roundedimageview.RoundedImageView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/iv_avatar" android:layout_width="@dimen/_40sdp" android:layout_height="@dimen/_40sdp" android:layout_gravity="center_horizontal" android:scaleType="fitXY" android:src="@drawable/ic_add" app:riv_mutate_background="true" app:riv_oval="true" /> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_gravity="center_horizontal" android:layout_marginTop="@dimen/_3sdp" android:gravity="center" android:lines="2" android:maxLines="2" android:text="BH" android:textSize="11sp" android:textStyle="bold" android:visibility="invisible" /> </LinearLayout> </RelativeLayout>
Đã xong phần layout, giờ ta sẽ viết nội dung cho ListQuickDialAdapter.java.
public class ListQuickDialAdapter extends BaseAdapter { Context context; List<WidgetContactModel> listContact; LayoutInflater layoutInflater; boolean selectionMode = false; List<WidgetContactModel> listToRemove; public ListQuickDialAdapter(Context context, List<WidgetContactModel> listContact) { this.context = context; this.listContact = listContact; layoutInflater = LayoutInflater.from(context); listToRemove = new ArrayList<>(); } @Override public int getCount() { return listContact.size() + 1; } @Override public WidgetContactModel getItem(int i) { return listContact.get(i); } @Override public long getItemId(int i) { return i; } @Override public int getItemViewType(int position) { if (position < listContact.size()) { return 0; } return 1; } @Override public int getViewTypeCount() { return 2; } @Override public View getView(int position, View convertView, ViewGroup parent) { QuickDialViewHolder quickDialViewHolder = null; AddMoreViewHolder addMoreViewHolder = null; if (convertView == null) { if (getItemViewType(position) == 0) { convertView = layoutInflater.inflate(R.layout.item_quick_dial, parent, false); quickDialViewHolder = new QuickDialViewHolder(convertView); convertView.setTag(quickDialViewHolder); } else { convertView = layoutInflater.inflate(R.layout.item_add_more, parent, false); addMoreViewHolder = new AddMoreViewHolder(convertView); convertView.setTag(addMoreViewHolder); } } else { if (getItemViewType(position) == 0) { quickDialViewHolder = (QuickDialViewHolder) convertView.getTag(); } else { addMoreViewHolder = (AddMoreViewHolder) convertView.getTag(); } } if (getItemViewType(position) == 0) { quickDialViewHolder.setData(listContact.get(position), position); } return convertView; } class QuickDialViewHolder { @Bind(R.id.iv_avatar) RoundedImageView ivAvatar; @Bind(R.id.tv_name) TextView tvName; @Bind(R.id.cb_remove) CheckBox cbRemove; public QuickDialViewHolder(View itemView) { ButterKnife.bind(this, itemView); } public void setData(WidgetContactModel widgetContactModel, int position) { if (widgetContactModel.getAvatar() != null && !widgetContactModel.getAvatar().isEmpty()) { ivAvatar.setImageURI(Uri.parse(widgetContactModel.getAvatar())); } else { ivAvatar.setImageResource(R.drawable.default_avatar); } tvName.setText(widgetContactModel.getDisplayName()); if (selectionMode) { cbRemove.setVisibility(View.VISIBLE); if (listToRemove.contains(widgetContactModel)) { cbRemove.setChecked(true); } else { cbRemove.setChecked(false); } } else { cbRemove.setVisibility(View.GONE); cbRemove.setChecked(false); } cbRemove.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (listToRemove.contains(widgetContactModel)) { cbRemove.setChecked(false); listToRemove.remove(widgetContactModel); } else { cbRemove.setChecked(true); listToRemove.add(widgetContactModel); } } }); } } public void resetMode(boolean selectionMode) { this.selectionMode = selectionMode; if (selectionMode) { listToRemove.clear(); } } class AddMoreViewHolder { public AddMoreViewHolder(View itemView) { ButterKnife.bind(this, itemView); } } public List<WidgetContactModel> getListContact() { return listContact; } public void setListContact(List<WidgetContactModel> listContact) { this.listContact = listContact; } public List<WidgetContactModel> getListToRemove() { return listToRemove; } public void setListToRemove(List<WidgetContactModel> listToRemove) { this.listToRemove = listToRemove; } public boolean isSelectionMode() { return selectionMode; } public void setSelectionMode(boolean selectionMode) { this.selectionMode = selectionMode; } }
Lớp này sẽ lấy list dữ liệu từ
List<WidgetContactModel> listContact
để hiển thị lên App.
Ở đây, mình có định nghĩa thêm 1 list nữa:
List<WidgetContactModel> listToRemove
List này là để sau này mình làm chức năng remove 1 contact từ danh sách quay số nhanh, mình sẽ dùng nó để lưu trữ các contact cần remove.
Ok, quay lại QuickDialActivity, ta viết hàm lấy dữ liệu từ database và đổ lên gridView:
@Override protected void onResume() { super.onResume(); if (getQuickDialTask != null) { getQuickDialTask.cancel(true); getQuickDialTask = null; } getQuickDialTask = new AsyncTask<Void, Void, List<WidgetContactModel>>() { @Override protected List<WidgetContactModel> doInBackground(Void... voids) { return dbHelper.getListContact(); } @Override protected void onPreExecute() { super.onPreExecute(); showLoadingDialog(R.string.app_name, R.string.action_search); } @Override protected void onPostExecute(List<WidgetContactModel> widgetContactModels) { super.onPostExecute(widgetContactModels); for (WidgetContactModel widgetContactModel : widgetContactModels) { LogUtils.d("[" + getClass().getSimpleName() + "]" + widgetContactModel.getDisplayName() + " with " + widgetContactModel.getNumbers()); } if (adapter == null) { adapter = new ListQuickDialAdapter(QuickDialActivity.this, widgetContactModels); gvQuickDial.setAdapter(adapter); } else { adapter.setListContact(widgetContactModels); adapter.notifyDataSetChanged(); } if (!widgetContactModels.isEmpty()) { gvQuickDial.setVisibility(View.VISIBLE); llAdd.setVisibility(View.GONE); } else { gvQuickDial.setVisibility(View.GONE); llAdd.setVisibility(View.VISIBLE); } hideLoadingDialog(); } }.execute(); }
Tuy nhiên, từ đầu tới giờ ta mới xây dựng module hiển thị danh sách quay số nhanh mà chưa làm module thêm contact vào danh sách quay số nhanh. Chính vì vậy cho tới hiện tại thì database của chúng ta vẫn là database rỗng. Vậy mình sẽ đi tiếp sang chức năng add 1 contact từ danh bạ vào danh sách quay số nhanh.
List contact từ danh bạ mình sẽ hiển thị trong lớp MainActivity. Để cho đầy đủ thì ở QuickDialActivity, mình sẽ set sự kiện click cho ll_add để chuyển qua lớp MainActivity. Đây sẽ là nơi chúng ta implement các phương thức thêm, xóa contact vào database.
@OnClick(R.id.bt_add) public void onClickAdd() { showActivity(MainActivity.class); }
Hết bài 1
Như vậy bài này mình đã trình bày xong về:
- Xây dựng database để lưu trữ contact
- Xây dựng màn hình hiển thị danh sách quay số nhanh
Bài tiếp theo mình sẽ trình bày về cách lấy danh sách liên lạc từ danh bạ và đổ vào cơ sở dữ liệu. Đồng thời hiển thị lên màn hình danh sách quay số nhanh.
Phần widget ngoài màn hình home khá phức tạp, mình sẽ dành ra 2 bài cuối để trình bày. Các bạn theo dõi nhé!